160 lines
3.7 KiB
Go
160 lines
3.7 KiB
Go
package caddy
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/models"
|
|
)
|
|
|
|
// GenerateConfig creates a Caddy JSON configuration from proxy hosts.
|
|
// This is the core transformation layer from our database model to Caddy config.
|
|
func GenerateConfig(hosts []models.ProxyHost, storageDir string, acmeEmail string) (*Config, error) {
|
|
// Define log file paths
|
|
// We assume storageDir is like ".../data/caddy/data", so we go up to ".../data/logs"
|
|
// Or we can just use a relative path if Caddy's working directory is set correctly.
|
|
// In Docker, WORKDIR is /app, and storageDir passed here is usually /app/data/caddy.
|
|
// Let's put logs in /app/data/logs/access.log
|
|
logFile := "/app/data/logs/access.log"
|
|
|
|
config := &Config{
|
|
Logging: &LoggingConfig{
|
|
Logs: map[string]*LogConfig{
|
|
"access": {
|
|
Level: "INFO",
|
|
Writer: &WriterConfig{
|
|
Output: "file",
|
|
Filename: logFile,
|
|
Roll: true,
|
|
RollSize: 10, // 10 MB
|
|
RollKeep: 5, // Keep 5 files
|
|
RollKeepDays: 7, // Keep for 7 days
|
|
},
|
|
Encoder: &EncoderConfig{
|
|
Format: "json",
|
|
},
|
|
Include: []string{"http.log.access.access_log"},
|
|
},
|
|
},
|
|
},
|
|
Apps: Apps{
|
|
HTTP: &HTTPApp{
|
|
Servers: map[string]*Server{},
|
|
},
|
|
},
|
|
Storage: Storage{
|
|
System: "file_system",
|
|
Root: storageDir,
|
|
},
|
|
}
|
|
|
|
if acmeEmail != "" {
|
|
config.Apps.TLS = &TLSApp{
|
|
Automation: &AutomationConfig{
|
|
Policies: []*AutomationPolicy{
|
|
{
|
|
IssuersRaw: []interface{}{
|
|
map[string]interface{}{
|
|
"module": "acme",
|
|
"email": acmeEmail,
|
|
},
|
|
map[string]interface{}{
|
|
"module": "zerossl",
|
|
"email": acmeEmail,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
if len(hosts) == 0 {
|
|
return config, nil
|
|
}
|
|
|
|
// We already initialized srv0 above, so we just append routes to it
|
|
routes := make([]*Route, 0)
|
|
|
|
for _, host := range hosts {
|
|
if !host.Enabled {
|
|
continue
|
|
}
|
|
|
|
if host.DomainNames == "" {
|
|
return nil, fmt.Errorf("proxy host %s has empty domain names", host.UUID)
|
|
}
|
|
|
|
// Parse comma-separated domains
|
|
domains := strings.Split(host.DomainNames, ",")
|
|
for i := range domains {
|
|
domains[i] = strings.TrimSpace(domains[i])
|
|
}
|
|
|
|
// Build handlers for this host
|
|
handlers := make([]Handler, 0)
|
|
|
|
// Add HSTS header if enabled
|
|
if host.HSTSEnabled {
|
|
hstsValue := "max-age=31536000"
|
|
if host.HSTSSubdomains {
|
|
hstsValue += "; includeSubDomains"
|
|
}
|
|
handlers = append(handlers, HeaderHandler(map[string][]string{
|
|
"Strict-Transport-Security": {hstsValue},
|
|
}))
|
|
}
|
|
|
|
// Add exploit blocking if enabled
|
|
if host.BlockExploits {
|
|
handlers = append(handlers, BlockExploitsHandler())
|
|
}
|
|
|
|
// Handle custom locations first (more specific routes)
|
|
for _, loc := range host.Locations {
|
|
dial := fmt.Sprintf("%s:%d", loc.ForwardHost, loc.ForwardPort)
|
|
locRoute := &Route{
|
|
Match: []Match{
|
|
{
|
|
Host: domains,
|
|
Path: []string{loc.Path, loc.Path + "/*"},
|
|
},
|
|
},
|
|
Handle: []Handler{
|
|
ReverseProxyHandler(dial, host.WebsocketSupport),
|
|
},
|
|
Terminal: true,
|
|
}
|
|
routes = append(routes, locRoute)
|
|
}
|
|
|
|
// Main proxy handler
|
|
dial := fmt.Sprintf("%s:%d", host.ForwardHost, host.ForwardPort)
|
|
mainHandlers := append(handlers, ReverseProxyHandler(dial, host.WebsocketSupport))
|
|
|
|
route := &Route{
|
|
Match: []Match{
|
|
{Host: domains},
|
|
},
|
|
Handle: mainHandlers,
|
|
Terminal: true,
|
|
}
|
|
|
|
routes = append(routes, route)
|
|
}
|
|
|
|
config.Apps.HTTP.Servers["cpm_server"] = &Server{
|
|
Listen: []string{":80", ":443"},
|
|
Routes: routes,
|
|
AutoHTTPS: &AutoHTTPSConfig{
|
|
Disable: false,
|
|
DisableRedir: false,
|
|
},
|
|
Logs: &ServerLogs{
|
|
DefaultLoggerName: "access_log",
|
|
},
|
|
}
|
|
|
|
return config, nil
|
|
}
|