diff --git a/README.md b/README.md index 9eca24b..cd08e03 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,11 @@ | 代理与 Web | **NGINX** | `.conf` | HTTP/HTTPS 端口、域名、SSL 证书、反向代理、Gzip、CORS | | 缓存 | **Redis** | `.conf` | 单机/哨兵/集群模式、端口、内存限制、RDB/AOF 持久化、密码 | | 数据库 | **MySQL** | `.cnf` | 版本(5.7/8.0)、Buffer Pool 自动计算、字符集、慢查询、主从复制 | +| 数据库 | **MongoDB** | `.conf` | 端口、WiredTiger 引擎、认证、TLS、副本集、Oplog、压缩算法 | | 云原生 | **K8s Deployment** | `.yaml` | 镜像、副本数、资源限制、健康检查、滚动更新策略 | | 云原生 | **K8s Service** | `.yaml` | ClusterIP/NodePort/LoadBalancer、端口映射、会话保持 | +| 自动化运维 | **Ansible** | `.cfg` | SSH 连接、Pipelining、Become 提权、Forks 并发、日志、事实缓存 | +| 自动化运维 | **Ansible Playbook** | `.yaml` | 变量、包管理、服务管理、文件复制、模板渲染、Shell 命令、Handlers | --- @@ -105,8 +108,11 @@ conf-template/ │ ├── nginx.js # NGINX Schema 定义 + 模板生成 │ ├── redis.js # Redis Schema 定义 + 模板生成 │ ├── mysql.js # MySQL Schema 定义 + 模板生成 +│ ├── mongodb.js # MongoDB Schema 定义 + 模板生成 │ ├── k8s-deployment.js # K8s Deployment Schema + YAML 生成 -│ └── k8s-service.js # K8s Service Schema + YAML 生成 +│ ├── k8s-service.js # K8s Service Schema + YAML 生成 +│ ├── ansible.js # Ansible Schema + 配置生成 +│ └── ansible-playbook.js # Ansible Playbook Schema + YAML 生成 └── dist/ # 生产构建产物(gitignore) ``` diff --git a/src/schemas/ansible-playbook.js b/src/schemas/ansible-playbook.js new file mode 100644 index 0000000..f5e72e5 --- /dev/null +++ b/src/schemas/ansible-playbook.js @@ -0,0 +1,468 @@ +export const ansiblePlaybookSchema = { + id: 'ansible-playbook', + name: 'Ansible Playbook', + icon: 'List', + category: '自动化运维', + description: 'Ansible 任务编排与自动化剧本', + format: 'yaml', + fileName: 'playbook.yaml', + groups: [ + { + title: 'Play 基本信息', + fields: [ + { + key: 'playName', + label: 'Play 名称', + type: 'text', + placeholder: 'Configure web servers', + default: 'My Playbook', + required: true, + }, + { + key: 'hosts', + label: '目标主机/组', + type: 'text', + placeholder: 'webservers 或 all', + default: 'all', + required: true, + }, + { + key: 'become', + label: '使用 sudo 提权', + type: 'switch', + default: true, + }, + { + key: 'gatherFacts', + label: '收集主机信息', + type: 'switch', + default: true, + }, + ], + }, + { + title: '变量与环境', + fields: [ + { + key: 'enableVars', + label: '定义变量', + type: 'switch', + default: false, + }, + { + key: 'varAppPort', + label: '应用端口 (app_port)', + type: 'number', + min: 1, + max: 65535, + default: 8080, + dependsOn: { key: 'enableVars', value: true }, + }, + { + key: 'varAppName', + label: '应用名称 (app_name)', + type: 'text', + default: 'myapp', + dependsOn: { key: 'enableVars', value: true }, + }, + { + key: 'enableEnvironment', + label: '设置环境变量', + type: 'switch', + default: false, + }, + { + key: 'envHttpProxy', + label: 'HTTP_PROXY', + type: 'text', + placeholder: 'http://proxy.example.com:8080', + default: 'http://proxy.example.com:8080', + dependsOn: { key: 'enableEnvironment', value: true }, + }, + { + key: 'envHttpsProxy', + label: 'HTTPS_PROXY', + type: 'text', + placeholder: 'http://proxy.example.com:8080', + default: 'http://proxy.example.com:8080', + dependsOn: { key: 'enableEnvironment', value: true }, + }, + ], + }, + { + title: '前置任务 (Pre Tasks)', + fields: [ + { + key: 'enablePretasks', + label: '添加前置任务', + type: 'switch', + default: false, + }, + { + key: 'pretaskUpdateCache', + label: '更新包管理器缓存', + type: 'switch', + default: true, + dependsOn: { key: 'enablePretasks', value: true }, + }, + ], + }, + { + title: 'Tasks — 包管理', + fields: [ + { + key: 'taskInstallPackages', + label: '安装软件包', + type: 'switch', + default: false, + }, + { + key: 'packagesToInstall', + label: '包名列表', + type: 'text', + placeholder: 'nginx, curl, vim (逗号分隔)', + default: 'nginx, curl', + dependsOn: { key: 'taskInstallPackages', value: true }, + tip: '多个包用逗号分隔', + }, + { + key: 'packageState', + label: '安装状态', + type: 'select', + options: [ + { label: 'present - 确保已安装', value: 'present' }, + { label: 'latest - 更新到最新版', value: 'latest' }, + { label: 'absent - 卸载', value: 'absent' }, + ], + default: 'present', + dependsOn: { key: 'taskInstallPackages', value: true }, + }, + ], + }, + { + title: 'Tasks — 服务管理', + fields: [ + { + key: 'taskManageService', + label: '管理服务', + type: 'switch', + default: false, + }, + { + key: 'serviceName', + label: '服务名称', + type: 'text', + placeholder: 'nginx', + default: 'nginx', + dependsOn: { key: 'taskManageService', value: true }, + }, + { + key: 'serviceState', + label: '服务状态', + type: 'select', + options: [ + { label: 'started - 启动', value: 'started' }, + { label: 'stopped - 停止', value: 'stopped' }, + { label: 'restarted - 重启', value: 'restarted' }, + { label: 'reloaded - 重载配置', value: 'reloaded' }, + ], + default: 'started', + dependsOn: { key: 'taskManageService', value: true }, + }, + { + key: 'serviceEnabled', + label: '开机自启', + type: 'switch', + default: true, + dependsOn: { key: 'taskManageService', value: true }, + }, + ], + }, + { + title: 'Tasks — 文件管理', + fields: [ + { + key: 'taskCopyFile', + label: '复制文件到远程', + type: 'switch', + default: false, + }, + { + key: 'copySrc', + label: '本地源文件路径', + type: 'text', + placeholder: './files/index.html', + default: './files/index.html', + dependsOn: { key: 'taskCopyFile', value: true }, + }, + { + key: 'copyDest', + label: '远程目标路径', + type: 'text', + placeholder: '/var/www/html/index.html', + default: '/var/www/html/index.html', + dependsOn: { key: 'taskCopyFile', value: true }, + }, + { + key: 'taskCreateDir', + label: '创建目录', + type: 'switch', + default: false, + }, + { + key: 'dirPath', + label: '目录路径', + type: 'text', + placeholder: '/var/www/myapp', + default: '/var/www/myapp', + dependsOn: { key: 'taskCreateDir', value: true }, + }, + { + key: 'dirOwner', + label: '目录所有者', + type: 'text', + default: 'www-data', + dependsOn: { key: 'taskCreateDir', value: true }, + }, + { + key: 'dirMode', + label: '目录权限', + type: 'text', + default: '0755', + dependsOn: { key: 'taskCreateDir', value: true }, + }, + ], + }, + { + title: 'Tasks — 模板渲染', + fields: [ + { + key: 'taskTemplate', + label: '渲染 Jinja2 模板', + type: 'switch', + default: false, + }, + { + key: 'templateSrc', + label: '模板文件路径', + type: 'text', + placeholder: './templates/nginx.conf.j2', + default: './templates/nginx.conf.j2', + dependsOn: { key: 'taskTemplate', value: true }, + }, + { + key: 'templateDest', + label: '远程目标路径', + type: 'text', + placeholder: '/etc/nginx/nginx.conf', + default: '/etc/nginx/nginx.conf', + dependsOn: { key: 'taskTemplate', value: true }, + }, + { + key: 'templateOwner', + label: '文件所有者', + type: 'text', + default: 'root', + dependsOn: { key: 'taskTemplate', value: true }, + }, + { + key: 'templateMode', + label: '文件权限', + type: 'text', + default: '0644', + dependsOn: { key: 'taskTemplate', value: true }, + }, + ], + }, + { + title: 'Tasks — 执行命令', + fields: [ + { + key: 'taskShell', + label: '执行 Shell 命令', + type: 'switch', + default: false, + }, + { + key: 'shellCommand', + label: '命令内容', + type: 'text', + placeholder: 'systemctl daemon-reload', + default: 'systemctl daemon-reload', + dependsOn: { key: 'taskShell', value: true }, + }, + ], + }, + { + title: '后置任务 (Post Tasks) 与处理器', + fields: [ + { + key: 'enableHandlers', + label: '定义处理器 (Handlers)', + type: 'switch', + default: false, + tip: '由 notify 触发的任务,如重启服务', + }, + { + key: 'handlerName', + label: '处理器名称', + type: 'text', + default: 'restart nginx', + dependsOn: { key: 'enableHandlers', value: true }, + }, + { + key: 'handlerService', + label: '重启的服务', + type: 'text', + default: 'nginx', + dependsOn: { key: 'enableHandlers', value: true }, + }, + ], + }, + ], +} + +export function generateAnsiblePlaybookYaml(config) { + const indent = (str, n) => str.split('\n').map(l => ' '.repeat(n) + l).join('\n') + const lines = [] + + lines.push(`# Ansible Playbook - 由 ConfTemplate 生成`) + lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`) + lines.push(`# 执行命令: ansible-playbook ${config.fileName || 'playbook.yaml'}`) + lines.push(``) + lines.push(`- name: ${config.playName}`) + lines.push(` hosts: ${config.hosts}`) + + if (config.become) { + lines.push(` become: true`) + } + + lines.push(` gather_facts: ${config.gatherFacts ? 'true' : 'false'}`) + + // 变量 + if (config.enableVars) { + lines.push(``) + lines.push(` vars:`) + lines.push(` app_port: ${config.varAppPort}`) + lines.push(` app_name: ${config.varAppName}`) + } + + // 环境变量 + if (config.enableEnvironment) { + lines.push(``) + lines.push(` environment:`) + lines.push(` HTTP_PROXY: ${config.envHttpProxy}`) + lines.push(` HTTPS_PROXY: ${config.envHttpsProxy}`) + } + + // 前置任务 + if (config.enablePretasks) { + lines.push(``) + lines.push(` pre_tasks:`) + if (config.pretaskUpdateCache) { + lines.push(` - name: Update apt cache`) + lines.push(` apt:`) + lines.push(` update_cache: yes`) + lines.push(` cache_valid_time: 3600`) + lines.push(` when: ansible_os_family == "Debian"`) + lines.push(``) + lines.push(` - name: Update yum cache`) + lines.push(` yum:`) + lines.push(` update_cache: yes`) + lines.push(` when: ansible_os_family == "RedHat"`) + } + } + + // 主任务 + lines.push(``) + lines.push(` tasks:`) + let taskNum = 0 + + // 安装包 + if (config.taskInstallPackages) { + taskNum++ + const pkgs = config.packagesToInstall.split(',').map(s => s.trim()).filter(Boolean) + lines.push(` - name: Install packages`) + lines.push(` ansible.builtin.package:`) + lines.push(` name:`) + pkgs.forEach(p => lines.push(` - ${p}`)) + lines.push(` state: ${config.packageState}`) + lines.push(``) + } + + // 文件复制 + if (config.taskCopyFile) { + taskNum++ + lines.push(` - name: Copy file to remote host`) + lines.push(` ansible.builtin.copy:`) + lines.push(` src: ${config.copySrc}`) + lines.push(` dest: ${config.copyDest}`) + lines.push(` owner: root`) + lines.push(` mode: '0644'`) + + if (config.enableHandlers && config.handlerName) { + lines.push(` notify: ${config.handlerName}`) + } + lines.push(``) + } + + // 创建目录 + if (config.taskCreateDir) { + taskNum++ + lines.push(` - name: Create application directory`) + lines.push(` ansible.builtin.file:`) + lines.push(` path: ${config.dirPath}`) + lines.push(` state: directory`) + lines.push(` owner: ${config.dirOwner}`) + lines.push(` mode: '${config.dirMode}'`) + lines.push(``) + } + + // 模板渲染 + if (config.taskTemplate) { + taskNum++ + lines.push(` - name: Deploy configuration from template`) + lines.push(` ansible.builtin.template:`) + lines.push(` src: ${config.templateSrc}`) + lines.push(` dest: ${config.templateDest}`) + lines.push(` owner: ${config.templateOwner}`) + lines.push(` mode: '${config.templateMode}'`) + + if (config.enableHandlers && config.handlerName) { + lines.push(` notify: ${config.handlerName}`) + } + lines.push(``) + } + + // Shell 命令 + if (config.taskShell) { + taskNum++ + lines.push(` - name: Execute shell command`) + lines.push(` ansible.builtin.shell:`) + lines.push(` cmd: ${config.shellCommand}`) + lines.push(` changed_when: true`) + lines.push(``) + } + + // 服务管理 + if (config.taskManageService) { + taskNum++ + lines.push(` - name: Manage service ${config.serviceName}`) + lines.push(` ansible.builtin.service:`) + lines.push(` name: ${config.serviceName}`) + lines.push(` state: ${config.serviceState}`) + lines.push(` enabled: ${config.serviceEnabled ? 'true' : 'false'}`) + lines.push(``) + } + + // 处理器 + if (config.enableHandlers && config.handlerName) { + lines.push(` handlers:`) + lines.push(` - name: ${config.handlerName}`) + lines.push(` ansible.builtin.service:`) + lines.push(` name: ${config.handlerService}`) + lines.push(` state: restarted`) + } + + return lines.join('\n') +} diff --git a/src/schemas/ansible.js b/src/schemas/ansible.js new file mode 100644 index 0000000..43b2e3a --- /dev/null +++ b/src/schemas/ansible.js @@ -0,0 +1,284 @@ +export const ansibleSchema = { + id: 'ansible', + name: 'Ansible', + icon: 'Files', + category: '自动化运维', + description: '自动化配置管理与应用部署工具', + format: 'cfg', + fileName: 'ansible.cfg', + groups: [ + { + title: '基础配置', + fields: [ + { + key: 'inventory', + label: 'Inventory 文件路径', + type: 'text', + default: '/etc/ansible/hosts', + tip: '主机清单文件路径', + }, + { + key: 'remoteUser', + label: '远程用户', + type: 'text', + placeholder: 'root', + default: 'root', + }, + { + key: 'hostKeyChecking', + label: 'SSH 主机密钥检查', + type: 'switch', + default: false, + tip: '首次连接是否检查主机密钥,测试环境建议关闭', + }, + { + key: 'retryFilesEnabled', + label: '启用重试文件', + type: 'switch', + default: false, + tip: '失败时生成 .retry 文件', + }, + ], + }, + { + title: 'SSH 连接', + fields: [ + { + key: 'privateKeyFile', + label: 'SSH 私钥路径', + type: 'text', + placeholder: '~/.ssh/id_rsa', + default: '~/.ssh/id_rsa', + }, + { + key: 'sshPort', + label: 'SSH 默认端口', + type: 'number', + min: 1, + max: 65535, + default: 22, + }, + { + key: 'timeout', + label: '连接超时 (秒)', + type: 'number', + min: 5, + max: 300, + default: 30, + }, + { + key: 'pipelining', + label: '开启 Pipelining', + type: 'switch', + default: true, + tip: '减少 SSH 连接次数,提升执行效率', + }, + { + key: 'sshArgs', + label: 'SSH 额外参数', + type: 'text', + placeholder: '-o ControlMaster=auto -o ControlPersist=60s', + default: '-o ControlMaster=auto -o ControlPersist=60s', + tip: '控制 SSH 连接复用', + }, + ], + }, + { + title: '权限提升 (Become)', + fields: [ + { + key: 'become', + label: '默认开启提权', + type: 'switch', + default: false, + tip: '是否默认使用 sudo 提权', + }, + { + key: 'becomeMethod', + label: '提权方式', + type: 'select', + options: [ + { label: 'sudo', value: 'sudo' }, + { label: 'su', value: 'su' }, + { label: 'pbrun', value: 'pbrun' }, + { label: 'pfexec', value: 'pfexec' }, + ], + default: 'sudo', + dependsOn: { key: 'become', value: true }, + }, + { + key: 'becomeUser', + label: '提权目标用户', + type: 'text', + default: 'root', + dependsOn: { key: 'become', value: true }, + }, + { + key: 'becomeAskPass', + label: '询问提权密码', + type: 'switch', + default: false, + dependsOn: { key: 'become', value: true }, + }, + ], + }, + { + title: '执行控制', + fields: [ + { + key: 'forks', + label: '并发进程数 (forks)', + type: 'number', + min: 1, + max: 200, + default: 20, + tip: '同时操作的主机数量', + }, + { + key: 'pollInterval', + label: '轮询间隔 (秒)', + type: 'number', + min: 1, + max: 60, + default: 15, + }, + { + key: 'timeoutTask', + label: '任务超时 (秒)', + type: 'number', + min: 10, + max: 3600, + default: 300, + }, + ], + }, + { + title: '日志与输出', + fields: [ + { + key: 'logPath', + label: '日志文件路径', + type: 'text', + placeholder: '/var/log/ansible.log', + default: '', + tip: '留空表示不记录到文件', + }, + { + key: 'stdoutCallback', + label: '输出回调插件', + type: 'select', + options: [ + { label: 'default - 默认格式', value: 'default' }, + { label: 'yaml - YAML 格式 (推荐)', value: 'yaml' }, + { label: 'json - JSON 格式', value: 'json' }, + { label: 'minimal - 最小化输出', value: 'minimal' }, + { label: 'dense - 密集输出', value: 'dense' }, + ], + default: 'yaml', + }, + { + key: 'displaySkippedHosts', + label: '显示跳过的主机', + type: 'switch', + default: false, + }, + { + key: 'displayOkHosts', + label: '显示成功的主机', + type: 'switch', + default: true, + }, + ], + }, + { + title: '事实收集', + fields: [ + { + key: 'gathering', + label: '事实收集策略', + type: 'select', + options: [ + { label: 'smart - 智能缓存 (推荐)', value: 'smart' }, + { label: 'implicit - 每次收集', value: 'implicit' }, + { label: 'explicit - 仅显式收集', value: 'explicit' }, + ], + default: 'smart', + }, + { + key: 'factCaching', + label: '事实缓存方式', + type: 'select', + options: [ + { label: 'memory - 不缓存 (默认)', value: 'memory' }, + { label: 'jsonfile - JSON 文件缓存', value: 'jsonfile' }, + { label: 'redis - Redis 缓存', value: 'redis' }, + ], + default: 'memory', + }, + { + key: 'factCachingConnection', + label: '缓存路径/连接', + type: 'text', + placeholder: '/tmp/ansible_facts_cache', + default: '/tmp/ansible_facts_cache', + dependsOn: { key: 'factCaching', value: 'jsonfile' }, + }, + ], + }, + ], +} + +export function generateAnsibleCfg(config) { + const lines = [] + lines.push(`# Ansible 配置文件 - 由 ConfTemplate 生成`) + lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`) + lines.push(``) + lines.push(`[defaults]`) + lines.push(`inventory = ${config.inventory}`) + lines.push(`remote_user = ${config.remoteUser}`) + lines.push(`host_key_checking = ${config.hostKeyChecking ? 'True' : 'False'}`) + lines.push(`retry_files_enabled = ${config.retryFilesEnabled ? 'True' : 'False'}`) + lines.push(`forks = ${config.forks}`) + lines.push(`poll_interval = ${config.pollInterval}`) + lines.push(`timeout = ${config.timeoutTask}`) + lines.push(`stdout_callback = ${config.stdoutCallback}`) + lines.push(`display_skipped_hosts = ${config.displaySkippedHosts ? 'True' : 'False'}`) + lines.push(`display_ok_hosts = ${config.displayOkHosts ? 'True' : 'False'}`) + lines.push(`gathering = ${config.gathering}`) + + if (config.factCaching !== 'memory') { + lines.push(`fact_caching = ${config.factCaching}`) + lines.push(`fact_caching_connection = ${config.factCachingConnection}`) + lines.push(`fact_caching_timeout = 86400`) + } + + if (config.logPath) { + lines.push(`log_path = ${config.logPath}`) + } + + lines.push(``) + lines.push(`[ssh_connection]`) + lines.push(`ssh_args = ${config.sshArgs}`) + lines.push(`pipelining = ${config.pipelining ? 'True' : 'False'}`) + lines.push(`control_path = /tmp/ansible-%%h-%%r`) + lines.push(`timeout = ${config.timeout}`) + + if (config.privateKeyFile) { + lines.push(`private_key_file = ${config.privateKeyFile}`) + } + + if (config.become) { + lines.push(``) + lines.push(`[privilege_escalation]`) + lines.push(`become = True`) + lines.push(`become_method = ${config.becomeMethod}`) + lines.push(`become_user = ${config.becomeUser}`) + lines.push(`become_ask_pass = ${config.becomeAskPass ? 'True' : 'False'}`) + } + + lines.push(``) + lines.push(`[inventory]`) + lines.push(`enable_plugins = host_list, script, yaml, ini, auto`) + + return lines.join('\n') +} diff --git a/src/schemas/index.js b/src/schemas/index.js index 113418c..7f0f0c9 100644 --- a/src/schemas/index.js +++ b/src/schemas/index.js @@ -1,23 +1,32 @@ import { nginxSchema, generateNginxConf } from './nginx' import { redisSchema, generateRedisConf } from './redis' import { mysqlSchema, generateMysqlConf } from './mysql' +import { mongodbSchema, generateMongodbConf } from './mongodb' import { k8sDeploymentSchema, generateK8sDeploymentYaml } from './k8s-deployment' import { k8sServiceSchema, generateK8sServiceYaml } from './k8s-service' +import { ansibleSchema, generateAnsibleCfg } from './ansible' +import { ansiblePlaybookSchema, generateAnsiblePlaybookYaml } from './ansible-playbook' export const schemas = { nginx: nginxSchema, redis: redisSchema, mysql: mysqlSchema, + mongodb: mongodbSchema, 'k8s-deployment': k8sDeploymentSchema, 'k8s-service': k8sServiceSchema, + ansible: ansibleSchema, + 'ansible-playbook': ansiblePlaybookSchema, } export const generators = { nginx: generateNginxConf, redis: generateRedisConf, mysql: generateMysqlConf, + mongodb: generateMongodbConf, 'k8s-deployment': generateK8sDeploymentYaml, 'k8s-service': generateK8sServiceYaml, + ansible: generateAnsibleCfg, + 'ansible-playbook': generateAnsiblePlaybookYaml, } export const categories = [ @@ -34,13 +43,18 @@ export const categories = [ { name: '数据库', icon: 'Coin', - items: ['mysql'], + items: ['mysql', 'mongodb'], }, { name: '云原生', icon: 'Box', items: ['k8s-deployment', 'k8s-service'], }, + { + name: '自动化运维', + icon: 'Files', + items: ['ansible', 'ansible-playbook'], + }, ] export function getDefaultValues(schema) { @@ -57,6 +71,7 @@ export function getLanguage(format) { const map = { conf: 'ini', cnf: 'ini', + cfg: 'ini', yaml: 'yaml', json: 'json', toml: 'toml', diff --git a/src/schemas/mongodb.js b/src/schemas/mongodb.js new file mode 100644 index 0000000..f7f7048 --- /dev/null +++ b/src/schemas/mongodb.js @@ -0,0 +1,317 @@ +export const mongodbSchema = { + id: 'mongodb', + name: 'MongoDB', + icon: 'Coin', + category: '数据库', + description: '面向文档的 NoSQL 数据库', + format: 'conf', + fileName: 'mongod.conf', + groups: [ + { + title: '基础配置', + fields: [ + { + key: 'port', + label: '监听端口', + type: 'number', + min: 1, + max: 65535, + default: 27017, + }, + { + key: 'bindIp', + label: '绑定地址', + type: 'text', + default: '0.0.0.0', + tip: '0.0.0.0 允许所有地址连接', + }, + { + key: 'dbPath', + label: '数据目录', + type: 'text', + default: '/var/lib/mongodb', + }, + { + key: 'logPath', + label: '日志文件路径', + type: 'text', + default: '/var/log/mongodb/mongod.log', + }, + { + key: 'fork', + label: '守护进程模式', + type: 'switch', + default: true, + }, + ], + }, + { + title: '安全认证', + fields: [ + { + key: 'auth', + label: '开启身份认证', + type: 'switch', + default: true, + tip: '生产环境必须开启', + }, + { + key: 'authorization', + label: '授权模式', + type: 'select', + options: [ + { label: 'enabled - 基于角色的访问控制 (推荐)', value: 'enabled' }, + { label: 'disabled - 无访问控制', value: 'disabled' }, + ], + default: 'enabled', + dependsOn: { key: 'auth', value: true }, + }, + { + key: 'enableTls', + label: '开启 TLS/SSL', + type: 'switch', + default: false, + }, + { + key: 'tlsCertFile', + label: 'TLS 证书文件', + type: 'text', + placeholder: '/etc/ssl/mongodb.pem', + default: '/etc/ssl/mongodb.pem', + dependsOn: { key: 'enableTls', value: true }, + }, + { + key: 'tlsKeyFile', + label: 'TLS 私钥文件', + type: 'text', + placeholder: '/etc/ssl/mongodb-key.pem', + default: '/etc/ssl/mongodb-key.pem', + dependsOn: { key: 'enableTls', value: true }, + }, + ], + }, + { + title: '存储引擎', + fields: [ + { + key: 'engine', + label: '存储引擎', + type: 'select', + options: [ + { label: 'WiredTiger (默认,推荐)', value: 'wiredTiger' }, + ], + default: 'wiredTiger', + }, + { + key: 'cacheSizeGB', + label: 'WiredTiger 缓存大小 (GB)', + type: 'select', + options: [ + { label: '自动 (物理内存的 50%)', value: '' }, + { label: '1 GB', value: '1' }, + { label: '2 GB', value: '2' }, + { label: '4 GB', value: '4' }, + { label: '8 GB', value: '8' }, + { label: '16 GB', value: '16' }, + ], + default: '', + tip: '默认为 (物理内存 - 1GB) 的 50%', + }, + { + key: 'journalEnabled', + label: '开启 Journal 日志', + type: 'switch', + default: true, + tip: '保证数据持久性,单节点必须开启', + }, + { + key: 'directoryPerDb', + label: '每个数据库独立目录', + type: 'switch', + default: false, + }, + ], + }, + { + title: '网络与连接', + fields: [ + { + key: 'maxConns', + label: '最大连接数', + type: 'number', + min: 10, + max: 65536, + default: 65536, + }, + { + key: 'ipv6', + label: '启用 IPv6', + type: 'switch', + default: false, + }, + { + key: 'httpInterface', + label: '开启 HTTP 接口', + type: 'switch', + default: false, + tip: '生产环境建议关闭', + }, + ], + }, + { + title: '操作日志 (Oplog)', + fields: [ + { + key: 'oplogSizeMB', + label: 'Oplog 大小 (MB)', + type: 'number', + min: 0, + max: 102400, + step: 512, + default: 0, + tip: '0 表示使用默认值(磁盘的 5%)', + }, + { + key: 'replSetName', + label: '副本集名称', + type: 'text', + placeholder: '留空表示不启用副本集', + default: '', + tip: '设置后启用副本集模式', + }, + ], + }, + { + title: '性能调优', + fields: [ + { + key: 'wiredTigerCollectionBlockCompressor', + label: '压缩算法', + type: 'select', + options: [ + { label: 'snappy (默认,平衡)', value: 'snappy' }, + { label: 'zlib (高压缩率)', value: 'zlib' }, + { label: 'zstd (最佳压缩)', value: 'zstd' }, + { label: 'none (不压缩)', value: 'none' }, + ], + default: 'snappy', + }, + { + key: 'profileLevel', + label: 'Profiling 级别', + type: 'select', + options: [ + { label: '0 - 关闭 (推荐生产)', value: '0' }, + { label: '1 - 慢操作', value: '1' }, + { label: '2 - 所有操作', value: '2' }, + ], + default: '0', + tip: '性能分析,调试时开启', + }, + { + key: 'slowOpThresholdMs', + label: '慢操作阈值 (毫秒)', + type: 'number', + min: 0, + max: 60000, + default: 100, + dependsOn: { key: 'profileLevel', valueNotIn: ['0'] }, + }, + ], + }, + ], +} + +export function generateMongodbConf(config) { + const lines = [] + lines.push(`# MongoDB 配置文件 - 由 ConfTemplate 生成`) + lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`) + lines.push(``) + lines.push(`# ======================== 系统日志 ========================`) + lines.push(`systemLog:`) + lines.push(` destination: file`) + lines.push(` path: ${config.logPath}`) + lines.push(` logAppend: true`) + lines.push(``) + lines.push(`# ======================== 存储 ========================`) + lines.push(`storage:`) + lines.push(` dbPath: ${config.dbPath}`) + lines.push(` engine: ${config.engine}`) + lines.push(` directoryPerDB: ${config.directoryPerDb}`) + + if (config.journalEnabled) { + lines.push(` journal:`) + lines.push(` enabled: true`) + } + + if (config.cacheSizeGB) { + lines.push(` wiredTiger:`) + lines.push(` engineConfig:`) + lines.push(` cacheSizeGB: ${config.cacheSizeGB}`) + lines.push(` collectionConfig:`) + lines.push(` blockCompressor: ${config.wiredTigerCollectionBlockCompressor}`) + } else { + lines.push(` wiredTiger:`) + lines.push(` collectionConfig:`) + lines.push(` blockCompressor: ${config.wiredTigerCollectionBlockCompressor}`) + } + + lines.push(``) + lines.push(`# ======================== 网络 ========================`) + lines.push(`net:`) + lines.push(` port: ${config.port}`) + lines.push(` bindIp: ${config.bindIp}`) + lines.push(` maxIncomingConnections: ${config.maxConns}`) + + if (config.ipv6) { + lines.push(` ipv6: true`) + } + + if (config.httpInterface) { + lines.push(` http:`) + lines.push(` enabled: true`) + } + + if (config.enableTls) { + lines.push(` tls:`) + lines.push(` mode: requireTLS`) + lines.push(` certificateKeyFile: ${config.tlsCertFile}`) + } + + lines.push(``) + lines.push(`# ======================== 安全 ========================`) + lines.push(`security:`) + + if (config.auth) { + lines.push(` authorization: ${config.authorization}`) + } else { + lines.push(` authorization: disabled`) + } + + if (config.replSetName) { + lines.push(``) + lines.push(`# ======================== 副本集 ========================`) + lines.push(`replication:`) + lines.push(` replSetName: ${config.replSetName}`) + if (config.oplogSizeMB > 0) { + lines.push(` oplogSizeMB: ${config.oplogSizeMB}`) + } + } + + if (config.profileLevel !== '0') { + lines.push(``) + lines.push(`# ======================== 操作日志 ========================`) + lines.push(`operationProfiling:`) + lines.push(` mode: ${parseInt(config.profileLevel) === 1 ? 'slowOp' : 'all'}`) + lines.push(` slowOpThresholdMs: ${config.slowOpThresholdMs}`) + } + + lines.push(``) + lines.push(`# ======================== 进程管理 ========================`) + lines.push(`processManagement:`) + lines.push(` fork: ${config.fork}`) + lines.push(` pidFilePath: /var/run/mongodb/mongod.pid`) + lines.push(` timeZoneInfo: /usr/share/zoneinfo`) + + return lines.join('\n') +}