diff --git a/README.md b/README.md index 4a9a4d1..e320ce9 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,13 @@ --- -## 🧩 支持组件(42 个) +## 🧩 支持组件(44 个) | 分类 | 组件 | 输出格式 | 核心配置项 | |------|------|----------|------------| | 代理与 Web | **NGINX** | `.conf` | HTTP/HTTPS 端口、域名、SSL 证书、反向代理、Gzip、CORS | +| 代理与 Web | **Apache** | `.conf` | MPM 模型(event/worker/prefork)、SSL、反向代理、模块管理 | +| 代理与 Web | **Tomcat** | `.xml` | 连接器、线程池、AJP、SSL、JVM 内存、GC 策略 | | 缓存 | **Redis** | `.conf` | 单机/哨兵/集群模式、端口、内存限制、RDB/AOF 持久化、密码 | | 数据库 | **MySQL** | `.cnf` | 版本(5.7/8.0)、Buffer Pool 自动计算、字符集、慢查询、主从复制 | | 数据库 | **PostgreSQL** | `.conf` | 共享缓冲区、WAL 配置、SSL、连接数、慢查询阈值 | diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index 0f910f2..c71adca 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -60,7 +60,7 @@ diff --git a/src/schemas/apache.js b/src/schemas/apache.js new file mode 100644 index 0000000..93f66cd --- /dev/null +++ b/src/schemas/apache.js @@ -0,0 +1,432 @@ +export const apacheSchema = { + id: 'apache', + name: 'Apache', + icon: 'Monitor', + category: '代理与Web', + description: '世界上使用最广泛的 HTTP Web 服务器', + format: 'conf', + fileName: 'httpd.conf', + groups: [ + { + title: '基础配置', + fields: [ + { + key: 'serverRoot', + label: '服务器根目录', + type: 'text', + default: '/etc/httpd', + }, + { + key: 'listen', + label: '监听端口', + type: 'number', + min: 1, + max: 65535, + default: 80, + }, + { + key: 'serverName', + label: '服务器域名', + type: 'text', + placeholder: 'www.example.com:80', + default: 'localhost:80', + required: true, + }, + { + key: 'serverAdmin', + label: '管理员邮箱', + type: 'text', + placeholder: 'admin@example.com', + default: 'admin@localhost', + }, + { + key: 'documentRoot', + label: '网站根目录', + type: 'text', + default: '/var/www/html', + }, + ], + }, + { + title: 'MPM 模块', + fields: [ + { + key: 'mpmModule', + label: 'MPM 模型', + type: 'select', + options: [ + { label: 'event - 高并发首选 (推荐)', value: 'event' }, + { label: 'worker - 线程池模型', value: 'worker' }, + { label: 'prefork - 进程模型 (兼容性最好)', value: 'prefork' }, + ], + default: 'event', + tip: 'event 适合高并发,prefork 兼容 mod_php', + }, + { + key: 'startServers', + label: '启动进程数', + type: 'number', + min: 1, + max: 64, + default: 4, + }, + { + key: 'minSpareThreads', + label: '最小空闲线程', + type: 'number', + min: 1, + max: 256, + default: 25, + dependsOn: { key: 'mpmModule', valueNotIn: ['prefork'] }, + }, + { + key: 'maxSpareThreads', + label: '最大空闲线程', + type: 'number', + min: 1, + max: 1024, + default: 75, + dependsOn: { key: 'mpmModule', valueNotIn: ['prefork'] }, + }, + { + key: 'maxRequestWorkers', + label: '最大并发数', + type: 'number', + min: 1, + max: 10000, + default: 256, + }, + { + key: 'threadsPerChild', + label: '每进程线程数', + type: 'number', + min: 1, + max: 128, + default: 25, + dependsOn: { key: 'mpmModule', valueNotIn: ['prefork'] }, + }, + { + key: 'minSpareServers', + label: '最小空闲进程', + type: 'number', + min: 1, + max: 64, + default: 5, + dependsOn: { key: 'mpmModule', value: 'prefork' }, + }, + { + key: 'maxSpareServers', + label: '最大空闲进程', + type: 'number', + min: 1, + max: 256, + default: 10, + dependsOn: { key: 'mpmModule', value: 'prefork' }, + }, + ], + }, + { + title: 'HTTPS / SSL', + fields: [ + { + key: 'enableSsl', + label: '开启 HTTPS', + type: 'switch', + default: false, + }, + { + key: 'sslPort', + label: 'HTTPS 端口', + type: 'number', + min: 1, + max: 65535, + default: 443, + dependsOn: { key: 'enableSsl', value: true }, + }, + { + key: 'sslCertificate', + label: 'SSL 证书路径', + type: 'text', + default: '/etc/pki/tls/certs/localhost.crt', + dependsOn: { key: 'enableSsl', value: true }, + }, + { + key: 'sslCertificateKey', + label: 'SSL 私钥路径', + type: 'text', + default: '/etc/pki/tls/private/localhost.key', + dependsOn: { key: 'enableSsl', value: true }, + }, + { + key: 'sslProtocols', + label: 'TLS 版本', + type: 'select', + options: [ + { label: 'TLSv1.2 + TLSv1.3 (推荐)', value: 'all -SSLv3 -TLSv1 -TLSv1.1' }, + { label: 'TLSv1.1 + TLSv1.2 + TLSv1.3', value: 'all -SSLv3 -TLSv1' }, + { label: '仅 TLSv1.3', value: 'TLSv1.3' }, + ], + default: 'all -SSLv3 -TLSv1 -TLSv1.1', + dependsOn: { key: 'enableSsl', value: true }, + }, + ], + }, + { + title: '模块管理', + fields: [ + { + key: 'enableModRewrite', + label: '加载 mod_rewrite', + type: 'switch', + default: true, + tip: 'URL 重写模块', + }, + { + key: 'enableModProxy', + label: '加载 mod_proxy', + type: 'switch', + default: false, + tip: '反向代理模块', + }, + { + key: 'enableModDeflate', + label: '加载 mod_deflate', + type: 'switch', + default: true, + tip: '压缩传输模块', + }, + { + key: 'enableModExpires', + label: '加载 mod_expires', + type: 'switch', + default: true, + tip: '浏览器缓存控制', + }, + { + key: 'enableModSecurity', + label: '加载 mod_security', + type: 'switch', + default: false, + tip: 'Web 应用防火墙', + }, + ], + }, + { + title: '反向代理', + fields: [ + { + key: 'enableProxy', + label: '开启反向代理', + type: 'switch', + default: false, + }, + { + key: 'proxyTarget', + label: '后端地址', + type: 'text', + placeholder: 'http://127.0.0.1:8080', + default: 'http://127.0.0.1:8080', + dependsOn: { key: 'enableProxy', value: true }, + required: true, + }, + { + key: 'proxyPath', + label: '代理路径前缀', + type: 'text', + default: '/', + dependsOn: { key: 'enableProxy', value: true }, + }, + ], + }, + { + title: '日志与性能', + fields: [ + { + key: 'logLevel', + label: '日志级别', + type: 'select', + options: [ + { label: 'warn (推荐生产)', value: 'warn' }, + { label: 'error', value: 'error' }, + { label: 'info', value: 'info' }, + { label: 'debug', value: 'debug' }, + ], + default: 'warn', + }, + { + key: 'enableGzip', + label: '开启 Gzip 压缩', + type: 'switch', + default: true, + }, + { + key: 'keepAlive', + label: '开启 Keep-Alive', + type: 'switch', + default: true, + }, + { + key: 'maxKeepAliveRequests', + label: 'Keep-Alive 最大请求数', + type: 'number', + min: 0, + max: 10000, + default: 100, + dependsOn: { key: 'keepAlive', value: true }, + }, + { + key: 'keepAliveTimeout', + label: 'Keep-Alive 超时 (秒)', + type: 'number', + min: 1, + max: 300, + default: 5, + dependsOn: { key: 'keepAlive', value: true }, + }, + { + key: 'timeout', + label: '请求超时 (秒)', + type: 'number', + min: 10, + max: 600, + default: 60, + }, + { + key: 'serverTokens', + label: '隐藏版本信息', + type: 'switch', + default: true, + tip: '不在响应头暴露 Apache 版本', + }, + ], + }, + ], +} + +export function generateApacheConf(config) { + const lines = [] + lines.push(`# Apache HTTP Server 配置文件 - 由 ConfTemplate 生成`) + lines.push(`# 生成时间: ${new Date().toLocaleString('zh-CN')}`) + lines.push(``) + lines.push(`ServerRoot "${config.serverRoot}"`) + lines.push(`Listen ${config.listen}`) + lines.push(`ServerName ${config.serverName}`) + lines.push(`ServerAdmin ${config.serverAdmin}`) + lines.push(`DocumentRoot "${config.documentRoot}"`) + lines.push(``) + + if (config.serverTokens) { + lines.push(`# 隐藏版本信息`) + lines.push(`ServerTokens Prod`) + lines.push(`ServerSignature Off`) + lines.push(``) + } + + // MPM + lines.push(`# ======================== MPM 模块 ========================`) + lines.push(``) + + if (config.mpmModule === 'prefork') { + lines.push(` StartServers ${config.startServers}`) + lines.push(` MinSpareServers ${config.minSpareServers}`) + lines.push(` MaxSpareServers ${config.maxSpareServers}`) + lines.push(` MaxRequestWorkers ${config.maxRequestWorkers}`) + lines.push(` MaxConnectionsPerChild 0`) + } else { + lines.push(` StartServers ${config.startServers}`) + lines.push(` MinSpareThreads ${config.minSpareThreads}`) + lines.push(` MaxSpareThreads ${config.maxSpareThreads}`) + lines.push(` ThreadsPerChild ${config.threadsPerChild}`) + lines.push(` MaxRequestWorkers ${config.maxRequestWorkers}`) + lines.push(` MaxConnectionsPerChild 0`) + } + + lines.push(``) + lines.push(``) + + // 模块 + lines.push(`# ======================== 模块加载 ========================`) + if (config.enableModRewrite) lines.push(`LoadModule rewrite_module modules/mod_rewrite.so`) + if (config.enableModProxy) { + lines.push(`LoadModule proxy_module modules/mod_proxy.so`) + lines.push(`LoadModule proxy_http_module modules/mod_proxy_http.so`) + } + if (config.enableModDeflate) lines.push(`LoadModule deflate_module modules/mod_deflate.so`) + if (config.enableModExpires) lines.push(`LoadModule expires_module modules/mod_expires.so`) + if (config.enableModSecurity) lines.push(`LoadModule security2_module modules/mod_security2.so`) + lines.push(`LoadModule dir_module modules/mod_dir.so`) + lines.push(`LoadModule mime_module modules/mod_mime.so`) + lines.push(`LoadModule log_config_module modules/mod_log_config.so`) + lines.push(`LoadModule headers_module modules/mod_headers.so`) + lines.push(``) + + // HTTPS + if (config.enableSsl) { + lines.push(`# ======================== HTTPS / SSL ========================`) + lines.push(`LoadModule ssl_module modules/mod_ssl.so`) + lines.push(``) + lines.push(``) + lines.push(` SSLProtocol ${config.sslProtocols}`) + lines.push(` SSLCipherSuite HIGH:!aNULL:!MD5`) + lines.push(` SSLHonorCipherOrder on`) + lines.push(``) + lines.push(``) + lines.push(``) + lines.push(` ServerName ${config.serverName}`) + lines.push(` SSLEngine on`) + lines.push(` SSLCertificateFile "${config.sslCertificate}"`) + lines.push(` SSLCertificateKeyFile "${config.sslCertificateKey}"`) + lines.push(` DocumentRoot "${config.documentRoot}"`) + lines.push(``) + lines.push(``) + } + + // Gzip + if (config.enableGzip) { + lines.push(`# ======================== Gzip 压缩 ========================`) + lines.push(``) + lines.push(` AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css`) + lines.push(` AddOutputFilterByType DEFLATE application/javascript application/json application/xml`) + lines.push(` DeflateCompressionLevel 6`) + lines.push(``) + lines.push(``) + } + + // 反向代理 + if (config.enableProxy) { + lines.push(`# ======================== 反向代理 ========================`) + lines.push(``) + lines.push(` ProxyPreserveHost On`) + lines.push(` ProxyPass ${config.proxyPath} ${config.proxyTarget}/`) + lines.push(` ProxyPassReverse ${config.proxyPath} ${config.proxyTarget}/`) + lines.push(``) + lines.push(``) + } + + // 日志 + lines.push(`# ======================== 日志配置 ========================`) + lines.push(`LogLevel ${config.logLevel}`) + lines.push(`ErrorLog "logs/error_log"`) + lines.push(`CustomLog "logs/access_log" combined`) + lines.push(``) + + // Keep-Alive + lines.push(`# ======================== Keep-Alive ========================`) + lines.push(`KeepAlive ${config.keepAlive ? 'On' : 'Off'}`) + if (config.keepAlive) { + lines.push(`MaxKeepAliveRequests ${config.maxKeepAliveRequests}`) + lines.push(`KeepAliveTimeout ${config.keepAliveTimeout}`) + } + lines.push(`Timeout ${config.timeout}`) + lines.push(``) + + // 目录权限 + lines.push(`# ======================== 目录权限 ========================`) + lines.push(``) + lines.push(` Options FollowSymLinks`) + lines.push(` AllowOverride All`) + lines.push(` Require all granted`) + lines.push(``) + + return lines.join('\n') +} diff --git a/src/schemas/index.js b/src/schemas/index.js index 27ef2fb..5e819e2 100644 --- a/src/schemas/index.js +++ b/src/schemas/index.js @@ -1,5 +1,7 @@ // ============ 代理与Web ============ import { nginxSchema, generateNginxConf } from './nginx' +import { apacheSchema, generateApacheConf } from './apache' +import { tomcatSchema, generateTomcatServerXml } from './tomcat' // ============ 缓存 ============ import { redisSchema, generateRedisConf } from './redis' @@ -79,6 +81,8 @@ import { ansiblePlaybookSchema, generateAnsiblePlaybookYaml } from './ansible-pl export const schemas = { // 代理与Web nginx: nginxSchema, + apache: apacheSchema, + tomcat: tomcatSchema, // 缓存 redis: redisSchema, // 数据库 @@ -143,6 +147,8 @@ export const schemas = { export const generators = { nginx: generateNginxConf, + apache: generateApacheConf, + tomcat: generateTomcatServerXml, redis: generateRedisConf, mysql: generateMysqlConf, mongodb: generateMongodbConf, @@ -194,7 +200,7 @@ export const categories = [ { name: '代理与Web', icon: 'Monitor', - items: ['nginx'], + items: ['nginx', 'apache', 'tomcat'], }, { name: '缓存', @@ -299,6 +305,7 @@ export function getLanguage(format) { toml: 'toml', properties: 'properties', ini: 'ini', + xml: 'xml', } return map[format] || 'plaintext' } diff --git a/src/schemas/tomcat.js b/src/schemas/tomcat.js new file mode 100644 index 0000000..094d479 --- /dev/null +++ b/src/schemas/tomcat.js @@ -0,0 +1,417 @@ +export const tomcatSchema = { + id: 'tomcat', + name: 'Tomcat', + icon: 'Monitor', + category: '代理与Web', + description: 'Apache Tomcat Java 应用服务器', + format: 'xml', + fileName: 'server.xml', + groups: [ + { + title: '基础配置', + fields: [ + { + key: 'port', + label: 'Server 关闭端口', + type: 'number', + min: 1, + max: 65535, + default: 8005, + tip: '用于接收关闭命令的端口', + }, + { + key: 'shutdown', + label: '关闭命令', + type: 'text', + default: 'SHUTDOWN', + }, + { + key: 'httpPort', + label: 'HTTP 连接端口', + type: 'number', + min: 1, + max: 65535, + default: 8080, + }, + { + key: 'ajpPort', + label: 'AJP 连接端口', + type: 'number', + min: 1, + max: 65535, + default: 8009, + tip: 'Apache 通过 AJP 协议连接 Tomcat', + }, + { + key: 'enableAjp', + label: '启用 AJP 连接器', + type: 'switch', + default: false, + }, + ], + }, + { + title: '连接器配置', + fields: [ + { + key: 'maxThreads', + label: '最大线程数', + type: 'number', + min: 10, + max: 4096, + default: 200, + tip: '处理请求的最大线程数', + }, + { + key: 'minSpareThreads', + label: '最小空闲线程', + type: 'number', + min: 1, + max: 200, + default: 10, + }, + { + key: 'connectionTimeout', + label: '连接超时 (毫秒)', + type: 'number', + min: 0, + max: 120000, + default: 20000, + }, + { + key: 'maxConnections', + label: '最大连接数', + type: 'number', + min: 1, + max: 100000, + default: 10000, + }, + { + key: 'enableCompression', + label: '开启压缩', + type: 'switch', + default: false, + }, + { + key: 'compressionMinSize', + label: '压缩最小体积 (字节)', + type: 'number', + min: 0, + max: 10240, + default: 2048, + dependsOn: { key: 'enableCompression', value: true }, + }, + { + key: 'compressibleMimeType', + label: '压缩 MIME 类型', + type: 'text', + default: 'text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json', + dependsOn: { key: 'enableCompression', value: true }, + }, + ], + }, + { + title: 'HTTPS / SSL', + fields: [ + { + key: 'enableSsl', + label: '开启 HTTPS', + type: 'switch', + default: false, + }, + { + key: 'httpsPort', + label: 'HTTPS 端口', + type: 'number', + min: 1, + max: 65535, + default: 8443, + dependsOn: { key: 'enableSsl', value: true }, + }, + { + key: 'keystoreFile', + label: 'Keystore 文件路径', + type: 'text', + placeholder: '/etc/tomcat/keystore.jks', + default: '/etc/tomcat/keystore.jks', + dependsOn: { key: 'enableSsl', value: true }, + required: true, + }, + { + key: 'keystorePass', + label: 'Keystore 密码', + type: 'text', + placeholder: 'changeit', + default: 'changeit', + dependsOn: { key: 'enableSsl', value: true }, + }, + { + key: 'sslProtocol', + label: 'SSL 协议', + type: 'select', + options: [ + { label: 'TLS (推荐)', value: 'TLS' }, + { label: 'TLSv1.2', value: 'TLSv1.2' }, + { label: 'TLSv1.2 + TLSv1.3', value: 'TLSv1.2,TLSv1.3' }, + ], + default: 'TLS', + dependsOn: { key: 'enableSsl', value: true }, + }, + ], + }, + { + title: 'Engine 与 Host', + fields: [ + { + key: 'defaultHost', + label: '默认主机名', + type: 'text', + default: 'localhost', + }, + { + key: 'appBase', + label: '应用部署目录', + type: 'text', + default: 'webapps', + tip: '相对于 CATALINA_BASE', + }, + { + key: 'unpackWARs', + label: '自动解压 WAR', + type: 'switch', + default: true, + }, + { + key: 'autoDeploy', + label: '自动部署', + type: 'switch', + default: true, + tip: '检测到新 WAR 自动部署', + }, + { + key: 'deployOnStartup', + label: '启动时自动部署', + type: 'switch', + default: true, + }, + ], + }, + { + title: 'JVM 内存', + fields: [ + { + key: 'enableJvmArgs', + label: '自定义 JVM 参数', + type: 'switch', + default: false, + tip: '设置 JAVA_OPTS 环境变量', + }, + { + key: 'xms', + label: '初始堆内存 (-Xms)', + type: 'select', + options: [ + { label: '256m', value: '256m' }, + { label: '512m', value: '512m' }, + { label: '1g', value: '1g' }, + { label: '2g', value: '2g' }, + { label: '4g', value: '4g' }, + ], + default: '512m', + dependsOn: { key: 'enableJvmArgs', value: true }, + }, + { + key: 'xmx', + label: '最大堆内存 (-Xmx)', + type: 'select', + options: [ + { label: '512m', value: '512m' }, + { label: '1g', value: '1g' }, + { label: '2g', value: '2g' }, + { label: '4g', value: '4g' }, + { label: '8g', value: '8g' }, + ], + default: '1g', + dependsOn: { key: 'enableJvmArgs', value: true }, + }, + { + key: 'metaspaceSize', + label: 'Metaspace 大小', + type: 'select', + options: [ + { label: '128m', value: '128m' }, + { label: '256m', value: '256m' }, + { label: '512m', value: '512m' }, + ], + default: '256m', + dependsOn: { key: 'enableJvmArgs', value: true }, + }, + { + key: 'gcType', + label: 'GC 算法', + type: 'select', + options: [ + { label: 'G1GC (推荐 JDK9+)', value: '-XX:+UseG1GC' }, + { label: 'ZGC (推荐 JDK15+)', value: '-XX:+UseZGC' }, + { label: 'ParallelGC', value: '-XX:+UseParallelGC' }, + { label: 'CMS (JDK8)', value: '-XX:+UseConcMarkSweepGC' }, + ], + default: '-XX:+UseG1GC', + dependsOn: { key: 'enableJvmArgs', value: true }, + }, + ], + }, + { + title: '日志与安全', + fields: [ + { + key: 'accessLogEnabled', + label: '开启访问日志', + type: 'switch', + default: true, + }, + { + key: 'accessLogPattern', + label: '日志格式', + type: 'select', + options: [ + { label: 'common - 通用日志格式', value: 'common' }, + { label: 'combined - 组合日志格式 (推荐)', value: 'combined' }, + { label: '详细格式 (含响应时间)', value: '%h %l %u %t "%r" %s %b %D "%{Referer}i" "%{User-Agent}i"' }, + ], + default: 'combined', + dependsOn: { key: 'accessLogEnabled', value: true }, + }, + { + key: 'enableManager', + label: '启用 Manager 应用', + type: 'switch', + default: false, + tip: '生产环境建议通过 CI/CD 部署,禁用 Manager', + }, + { + key: 'enableHostManager', + label: '启用 Host Manager', + type: 'switch', + default: false, + }, + ], + }, + ], +} + +export function generateTomcatServerXml(config) { + const lines = [] + lines.push(``) + lines.push(``) + lines.push(``) + lines.push(``) + lines.push(``) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(``) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(``) + lines.push(` `) + lines.push(``) + + // HTTP Connector + lines.push(` `) + lines.push(` `) + lines.push(``) + + // AJP Connector + if (config.enableAjp) { + lines.push(` `) + lines.push(` `) + lines.push(``) + } + + // HTTPS Connector + if (config.enableSsl) { + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(``) + } + + lines.push(` `) + lines.push(``) + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(``) + lines.push(` `) + lines.push(``) + + if (config.accessLogEnabled) { + lines.push(` `) + } + + lines.push(` `) + lines.push(` `) + lines.push(` `) + lines.push(``) + + // JVM args as comment + if (config.enableJvmArgs) { + lines.push(``) + lines.push(``) + lines.push(``) + } + + return lines.join('\n') +}