2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 06:47:20 +08:00

feat: 格式化配置文件

This commit is contained in:
2025-12-03 22:30:34 +08:00
parent 62bd8927e3
commit 9319226fd8
4 changed files with 388 additions and 6 deletions

View File

@@ -33,3 +33,149 @@ const DefaultVhostConf = `<VirtualHost *:80>
</Directory>
</VirtualHost>
`
// order 定义 Apache 指令的排序优先级
var order = map[string]int{
"Listen": 0,
"ServerName": 1,
"ServerAlias": 10,
"ServerAdmin": 11,
"DocumentRoot": 100,
"DirectoryIndex": 101,
"Options": 102,
"AllowOverride": 103,
"Require": 104,
"Order": 105,
"Allow": 106,
"Deny": 107,
"LimitRequestBody": 200,
"LimitRequestFields": 201,
"LimitRequestFieldSize": 202,
"LimitRequestLine": 203,
"LimitXMLRequestBody": 204,
"AuthType": 300,
"AuthName": 301,
"AuthUserFile": 302,
"AuthGroupFile": 303,
"AuthBasicProvider": 304,
"SSLEngine": 400,
"SSLCertificateFile": 401,
"SSLCertificateKeyFile": 402,
"SSLCertificateChainFile": 403,
"SSLCACertificateFile": 404,
"SSLCACertificatePath": 405,
"SSLProtocol": 406,
"SSLCipherSuite": 407,
"SSLHonorCipherOrder": 408,
"SSLCompression": 409,
"SSLSessionCache": 410,
"SSLSessionCacheTimeout": 411,
"SSLSessionTickets": 412,
"SSLUseStapling": 413,
"SSLStaplingCache": 414,
"SSLStaplingResponderTimeout": 415,
"SSLStaplingReturnResponderErrors": 416,
"SSLInsecureRenegotiation": 417,
"SSLVerifyClient": 418,
"SSLVerifyDepth": 419,
"SSLOptions": 420,
"Header": 500,
"RequestHeader": 501,
"SetEnvIf": 502,
"SetEnvIfNoCase": 503,
"SetEnv": 504,
"UnsetEnv": 505,
"PassEnv": 506,
"SetOutputFilter": 507,
"SetInputFilter": 508,
"AddOutputFilter": 509,
"AddInputFilter": 510,
"AddType": 511,
"AddHandler": 512,
"AddCharset": 513,
"AddEncoding": 514,
"AddLanguage": 515,
"DefaultType": 516,
"ForceType": 517,
"RemoveType": 518,
"RemoveHandler": 519,
"RemoveCharset": 520,
"RemoveEncoding": 521,
"RemoveLanguage": 522,
"ProxyPass": 600,
"ProxyPassReverse": 601,
"ProxyPassMatch": 602,
"ProxyPassReverseCookieDomain": 603,
"ProxyPassReverseCookiePath": 604,
"ProxyPreserveHost": 605,
"ProxyRequests": 606,
"ProxyVia": 607,
"ProxyTimeout": 608,
"ProxyAddHeaders": 609,
"ProxySet": 610,
"BalancerMember": 611,
"ProxyPassInherit": 612,
"ProxyPassInterpolateEnv": 613,
"RewriteEngine": 700,
"RewriteBase": 701,
"RewriteCond": 702,
"RewriteRule": 703,
"RewriteMap": 704,
"RewriteOptions": 705,
"Redirect": 800,
"RedirectMatch": 801,
"RedirectTemp": 802,
"RedirectPermanent": 803,
"Alias": 900,
"AliasMatch": 901,
"ScriptAlias": 902,
"ScriptAliasMatch": 903,
"ErrorDocument": 1000,
"ExpiresActive": 1100,
"ExpiresDefault": 1101,
"ExpiresByType": 1102,
"DeflateCompressionLevel": 1103,
"DeflateMemLevel": 1104,
"DeflateWindowSize": 1105,
"DeflateBufferSize": 1106,
"DeflateFilterNote": 1107,
"AddOutputFilterByType": 1108,
"PHPIniDir": 1200,
"SetHandler": 1201,
"Directory": 1300,
"DirectoryMatch": 1301,
"Files": 1302,
"FilesMatch": 1303,
"Location": 1304,
"LocationMatch": 1305,
"If": 1306,
"IfDefine": 1307,
"IfModule": 1308,
"Else": 1309,
"ElseIf": 1310,
"Proxy": 1311,
"ProxyMatch": 1312,
"Include": 1400,
"IncludeOptional": 1401,
"ErrorLog": 1500,
"CustomLog": 1501,
"LogLevel": 1502,
"LogFormat": 1503,
"TransferLog": 1504,
}

View File

@@ -2,6 +2,7 @@ package apache
import (
"fmt"
"slices"
"sort"
"strings"
)
@@ -32,7 +33,7 @@ func DefaultExportOptions() *ExportOptions {
return &ExportOptions{
IndentStyle: "spaces",
IndentSize: 4,
SortDirectives: false,
SortDirectives: true,
IncludeComments: true,
PreserveEmptyLines: true,
FormatStyle: "standard",
@@ -79,8 +80,10 @@ func (c *Config) ExportWithOptions(options *ExportOptions) string {
})
}
// 如果需要保持原始顺序,按行号排序
if !options.SortDirectives {
// 排序:如果需要语义排序则按 order 排序,否则按行号排序
if options.SortDirectives {
sortDirectivesSlice(items, order)
} else {
sort.Slice(items, func(i, j int) bool {
return items[i].line < items[j].line
})
@@ -186,8 +189,10 @@ func (v *VirtualHost) ExportWithOptions(options *ExportOptions, indent int) stri
})
}
// 排序
if !options.SortDirectives {
// 排序:如果需要语义排序则按 order 排序,否则按行号排序
if options.SortDirectives {
sortDirectivesSlice(items, order)
} else {
sort.Slice(items, func(i, j int) bool {
return items[i].line < items[j].line
})
@@ -268,8 +273,10 @@ func (b *Block) ExportWithOptions(options *ExportOptions, indent int) string {
}
}
// 按行号排序
// 排序:如果需要语义排序则按 order 排序,否则按行号排序
if options.SortDirectives {
sortDirectivesSlice(allItems, order)
} else {
sort.Slice(allItems, func(i, j int) bool {
return allItems[i].line < allItems[j].line
})
@@ -345,3 +352,67 @@ func shouldAddEmptyLine(current, next exportItem, options *ExportOptions) bool {
return false
}
// sortDirectivesSlice 对指令切片进行语义排序
func sortDirectivesSlice(items []exportItem, orderIndex map[string]int) {
slices.SortFunc(items, func(a, b exportItem) int {
// 跳过注释,注释保持原有位置
if a.typ == "comment" || b.typ == "comment" {
return a.line - b.line
}
var aName, bName string
switch a.typ {
case "directive":
if dir, ok := a.item.(*Directive); ok {
aName = dir.Name
}
case "virtualhost":
if vhost, ok := a.item.(*VirtualHost); ok {
aName = vhost.Name
}
}
switch b.typ {
case "directive":
if dir, ok := b.item.(*Directive); ok {
bName = dir.Name
}
case "virtualhost":
if vhost, ok := b.item.(*VirtualHost); ok {
bName = vhost.Name
}
}
// 按照 order 优先级排序
if orderIndex[aName] != orderIndex[bName] {
return orderIndex[aName] - orderIndex[bName]
}
// 优先级相同时,按照参数排序
var aArgs, bArgs []string
switch a.typ {
case "directive":
if dir, ok := a.item.(*Directive); ok {
aArgs = dir.Args
}
case "virtualhost":
if vhost, ok := a.item.(*VirtualHost); ok {
aArgs = vhost.Args
}
}
switch b.typ {
case "directive":
if dir, ok := b.item.(*Directive); ok {
bArgs = dir.Args
}
case "virtualhost":
if vhost, ok := b.item.(*VirtualHost); ok {
bArgs = vhost.Args
}
}
return slices.Compare(aArgs, bArgs)
})
}

View File

@@ -44,3 +44,151 @@ server {
error_log /opt/ace/sites/default/log/error.log;
}
`
// order 定义 Nginx 指令的排序优先级
var order = map[string]int{
"listen": 0,
"server_name": 1,
"root": 10,
"index": 11,
"try_files": 12,
"charset": 13,
"autoindex": 14,
"client_max_body_size": 100,
"client_body_buffer_size": 101,
"limit_except": 102,
"limit_req_zone": 103,
"limit_req": 104,
"limit_conn_zone": 105,
"limit_conn": 106,
"allow": 107,
"deny": 108,
"auth_basic": 109,
"auth_basic_user_file": 110,
"ssl": 200,
"ssl_certificate": 201,
"ssl_certificate_key": 202,
"ssl_session_timeout": 203,
"ssl_session_cache": 204,
"ssl_session_tickets": 205,
"ssl_protocols": 206,
"ssl_ciphers": 207,
"ssl_prefer_server_ciphers": 208,
"ssl_early_data": 209,
"ssl_dhparam": 210,
"ssl_stapling": 211,
"ssl_stapling_verify": 212,
"ssl_trusted_certificate": 213,
"resolver": 300,
"resolver_timeout": 301,
"proxy_pass": 400,
"proxy_redirect": 401,
"proxy_set_header": 402,
"proxy_hide_header": 403,
"proxy_pass_header": 404,
"proxy_http_version": 405,
"proxy_method": 406,
"proxy_headers_hash_max_size": 407,
"proxy_headers_hash_bucket_size": 408,
"proxy_buffering": 409,
"proxy_buffer_size": 410,
"proxy_buffers": 411,
"proxy_busy_buffers_size": 412,
"proxy_max_temp_file_size": 413,
"proxy_read_timeout": 414,
"proxy_send_timeout": 415,
"proxy_connect_timeout": 416,
"proxy_next_upstream": 417,
"proxy_next_upstream_tries": 418,
"proxy_next_upstream_timeout": 419,
"proxy_no_cache": 420,
"proxy_cache": 421,
"proxy_cache_key": 422,
"proxy_cache_valid": 423,
"proxy_cache_bypass": 424,
"proxy_cache_use_stale": 425,
"proxy_cache_lock": 426,
"proxy_cache_lock_timeout": 427,
"proxy_cache_background_update": 428,
"proxy_cache_min_uses": 429,
"proxy_ignore_client_abort": 430,
"proxy_intercept_errors": 431,
"proxy_ssl_server_name": 432,
"proxy_ssl_name": 433,
"proxy_ssl_protocols": 434,
"proxy_ssl_ciphers": 435,
"proxy_ssl_verify": 436,
"proxy_ssl_verify_depth": 437,
"proxy_ssl_trusted_certificate": 438,
"fastcgi_pass": 500,
"fastcgi_index": 501,
"fastcgi_param": 502,
"fastcgi_split_path_info": 503,
"fastcgi_buffers": 504,
"fastcgi_buffer_size": 505,
"fastcgi_busy_buffers_size": 506,
"fastcgi_temp_file_write_size": 507,
"fastcgi_read_timeout": 508,
"fastcgi_send_timeout": 509,
"fastcgi_connect_timeout": 510,
"fastcgi_intercept_errors": 511,
"uwsgi_pass": 600,
"uwsgi_param": 601,
"uwsgi_read_timeout": 602,
"uwsgi_send_timeout": 603,
"uwsgi_connect_timeout": 604,
"grpc_pass": 700,
"grpc_read_timeout": 701,
"grpc_send_timeout": 702,
"proxy_cache_path": 800,
"fastcgi_cache_path": 801,
"uwsgi_cache_path": 802,
"proxy_temp_path": 803,
"fastcgi_temp_path": 804,
"uwsgi_temp_path": 805,
"gzip": 900,
"gzip_comp_level": 901,
"gzip_min_length": 902,
"gzip_types": 903,
"gzip_buffers": 904,
"gzip_proxied": 905,
"gzip_disable": 906,
"gzip_vary": 907,
"brotli": 910,
"brotli_comp_level": 911,
"brotli_min_length": 912,
"brotli_types": 913,
"zstd": 920,
"zstd_comp_level": 921,
"zstd_min_length": 922,
"zstd_types": 923,
"zstd_static": 924,
"add_header": 1000,
"expires": 1001,
"rewrite": 1100,
"return": 1101,
"error_page": 1200,
"if": 1300,
"location": 1301,
"include": 1400,
"access_log": 1500,
"error_log": 1501,
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"slices"
"strings"
"github.com/tufanbarisyildirim/gonginx/config"
@@ -198,6 +199,7 @@ func (p *Parser) Dump() string {
// Save 保存配置到文件
func (p *Parser) Save() error {
p.sortDirectives(p.cfg.Directives, order)
content := p.Dump()
if err := os.WriteFile(p.cfgPath, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to save config file: %w", err)
@@ -211,6 +213,21 @@ func (p *Parser) SetConfigPath(path string) {
p.cfgPath = path
}
func (p *Parser) sortDirectives(directives []config.IDirective, orderIndex map[string]int) {
slices.SortFunc(directives, func(a config.IDirective, b config.IDirective) int {
if orderIndex[a.GetName()] != orderIndex[b.GetName()] {
return orderIndex[a.GetName()] - orderIndex[b.GetName()]
}
return slices.Compare(p.parameters2Slices(a.GetParameters()), p.parameters2Slices(b.GetParameters()))
})
for _, directive := range directives {
if block, ok := directive.GetBlock().(*config.Block); ok {
p.sortDirectives(block.Directives, orderIndex)
}
}
}
func (p *Parser) slices2Parameters(slices []string) []config.Parameter {
var parameters []config.Parameter
for _, slice := range slices {