refactor: заменить Caddy на Traefik v3

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shop
2026-05-16 20:27:54 +03:00
parent 11633fbe6e
commit 3419d90e61
11 changed files with 135 additions and 128 deletions
+71 -77
View File
@@ -10,17 +10,17 @@ import (
)
type Config struct {
SiteDomain string
CaddyEmail string
HTTPPort string
HTTPSPort string
UseDockerDB bool
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
DBSSLMode string
SiteDomain string
AcmeEmail string
HTTPPort string
HTTPSPort string
UseDockerDB bool
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
DBSSLMode string
}
func RunInteractive(root string) (Config, error) {
@@ -31,7 +31,7 @@ func RunInteractive(root string) (Config, error) {
cfg := Config{}
cfg.SiteDomain = ask(in, "Домен сайта (например shop.example.com, Enter = localhost)", "localhost")
cfg.CaddyEmail = ask(in, "Email для Let's Encrypt (Caddy)", "admin@localhost")
cfg.AcmeEmail = ask(in, "Email для Let's Encrypt (Traefik)", "admin@localhost")
cfg.HTTPPort = ask(in, "HTTP порт", "80")
cfg.HTTPSPort = ask(in, "HTTPS порт", "443")
@@ -58,7 +58,7 @@ func RunInteractive(root string) (Config, error) {
return cfg, err
}
fmt.Println("\n✓ Созданы файлы: .env, caddy/Caddyfile")
fmt.Println("\n✓ Созданы файлы: .env, traefik/dynamic/shop.yml")
fmt.Println("\nДальше (на сервере):")
fmt.Println(" docker compose up --build -d")
fmt.Println(" ./check.sh --after-start")
@@ -72,26 +72,30 @@ func RunInteractive(root string) (Config, error) {
func WriteFiles(root string, cfg Config) error {
envPath := filepath.Join(root, ".env")
caddyPath := filepath.Join(root, "caddy", "Caddyfile")
traefikPath := filepath.Join(root, "traefik", "dynamic", "shop.yml")
if err := os.WriteFile(envPath, []byte(buildEnv(cfg)), 0o600); err != nil {
return fmt.Errorf("write .env: %w", err)
}
if err := os.MkdirAll(filepath.Dir(caddyPath), 0o755); err != nil {
if err := os.MkdirAll(filepath.Dir(traefikPath), 0o755); err != nil {
return err
}
if err := os.WriteFile(caddyPath, []byte(buildCaddyfile(cfg)), 0o644); err != nil {
return fmt.Errorf("write Caddyfile: %w", err)
if err := os.WriteFile(traefikPath, []byte(buildTraefikDynamic(cfg)), 0o644); err != nil {
return fmt.Errorf("write traefik config: %w", err)
}
return nil
}
func buildEnv(cfg Config) string {
dbURL := DatabaseURL(cfg)
cookieSecure := "false"
if !useLocalDomain(cfg.SiteDomain) {
cookieSecure = "true"
}
lines := []string{
"# Сгенерировано установщиком ShopNova",
fmt.Sprintf("SITE_DOMAIN=%s", cfg.SiteDomain),
fmt.Sprintf("CADDY_EMAIL=%s", cfg.CaddyEmail),
fmt.Sprintf("ACME_EMAIL=%s", cfg.AcmeEmail),
fmt.Sprintf("HTTP_PORT=%s", cfg.HTTPPort),
fmt.Sprintf("HTTPS_PORT=%s", cfg.HTTPSPort),
"",
@@ -101,6 +105,8 @@ func buildEnv(cfg Config) string {
"",
fmt.Sprintf("DATABASE_URL=%s", dbURL),
"APP_PORT=8080",
fmt.Sprintf("COOKIE_SECURE=%s", cookieSecure),
"SESSION_TTL_HOURS=168",
"",
fmt.Sprintf("DB_HOST=%s", cfg.DBHost),
fmt.Sprintf("DB_PORT=%s", cfg.DBPort),
@@ -122,68 +128,56 @@ func DatabaseURL(cfg Config) string {
return u.String()
}
func buildCaddyfile(cfg Config) string {
email := cfg.CaddyEmail
if useLocalDomain(cfg.SiteDomain) {
return fmt.Sprintf(`{
email %s
}
:80 {
encode gzip zstd
@api path /health /version
handle @api {
reverse_proxy app:8080
}
handle /static/* {
reverse_proxy app:8080
}
handle {
reverse_proxy app:8080
}
log {
output stdout
format console
}
}
`, email)
}
func buildTraefikDynamic(cfg Config) string {
domain := strings.TrimSpace(cfg.SiteDomain)
return fmt.Sprintf(`{
email %s
}
%s {
encode gzip zstd
@api path /health /version
handle @api {
reverse_proxy app:8080
if useLocalDomain(domain) {
return fmt.Sprintf(`# Сгенерировано установщиком ShopNova (localhost)
http:
routers:
shop:
rule: "Host(`+"`%s`"+`)"
entryPoints: [web]
middlewares: [gzip]
service: shop
middlewares:
gzip:
compress: {}
services:
shop:
loadBalancer:
servers:
- url: "http://app:8080"
`, domain)
}
handle /static/* {
reverse_proxy app:8080
}
handle {
reverse_proxy app:8080
}
log {
output stdout
format console
}
}
http://%s {
redir https://{host}{uri} permanent
}
`, email, domain, domain)
return fmt.Sprintf(`# Сгенерировано установщиком ShopNova
http:
routers:
shop-http:
rule: "Host(`+"`%s`"+`)"
entryPoints: [web]
middlewares: [redirect-https]
service: shop
shop:
rule: "Host(`+"`%s`"+`)"
entryPoints: [websecure]
middlewares: [gzip]
service: shop
tls:
certResolver: letsencrypt
middlewares:
redirect-https:
redirectScheme:
scheme: https
permanent: true
gzip:
compress: {}
services:
shop:
loadBalancer:
servers:
- url: "http://app:8080"
`, domain, domain)
}
func useLocalDomain(d string) bool {
+1 -1
View File
@@ -42,7 +42,7 @@
<footer class="site-footer">
<div class="container footer-inner">
<p class="footer-brand">ShopNova</p>
<p class="footer-copy">© 2026 Интернет-магазин. Go + PostgreSQL + Caddy.</p>
<p class="footer-copy">© 2026 Интернет-магазин. Go + PostgreSQL + Traefik.</p>
</div>
</footer>
</body>