package proxy import ( "context" "fmt" "os" "path/filepath" "github.com/yourusername/buque/internal/docker" "github.com/yourusername/buque/internal/models" ) const ( nginxProxyImage = "nginxproxy/nginx-proxy:latest" nginxProxyCompanionImage = "nginxproxy/acme-companion:latest" ) // NginxManager manages nginx-proxy deployment and configuration type NginxManager struct { config models.NginxProxyConfig dockerClient *docker.Client composeManager *docker.ComposeManager } // NewNginxManager creates a new nginx-proxy manager func NewNginxManager(config models.NginxProxyConfig) (*NginxManager, error) { dockerClient, err := docker.NewClient() if err != nil { return nil, err } composeManager, err := docker.NewComposeManager() if err != nil { return nil, err } return &NginxManager{ config: config, dockerClient: dockerClient, composeManager: composeManager, }, nil } // Close closes the nginx manager func (nm *NginxManager) Close() error { return nm.dockerClient.Close() } // Deploy deploys the nginx-proxy environment func (nm *NginxManager) Deploy(ctx context.Context) error { // Create nginx-proxy directory if it doesn't exist if err := os.MkdirAll(nm.config.Path, 0755); err != nil { return fmt.Errorf("failed to create nginx-proxy directory: %w", err) } // Create docker-compose.yml composeContent := nm.generateComposeFile() composePath := filepath.Join(nm.config.Path, "docker-compose.yml") if err := os.WriteFile(composePath, []byte(composeContent), 0644); err != nil { return fmt.Errorf("failed to write compose file: %w", err) } // Create network if it doesn't exist exists, err := nm.dockerClient.NetworkExists(ctx, nm.config.NetworkName) if err != nil { return fmt.Errorf("failed to check network: %w", err) } if !exists { if err := nm.dockerClient.CreateNetwork(ctx, nm.config.NetworkName); err != nil { return fmt.Errorf("failed to create network: %w", err) } fmt.Printf("Created network: %s\n", nm.config.NetworkName) } // Deploy using docker-compose env := models.Environment{ Name: "nginx-proxy", Path: nm.config.Path, ComposeFile: "docker-compose.yml", Enabled: true, } fmt.Println("Deploying nginx-proxy...") if err := nm.composeManager.Up(ctx, env, true); err != nil { return fmt.Errorf("failed to deploy nginx-proxy: %w", err) } fmt.Println("Nginx-proxy deployed successfully!") return nil } // Remove removes the nginx-proxy environment func (nm *NginxManager) Remove(ctx context.Context) error { env := models.Environment{ Name: "nginx-proxy", Path: nm.config.Path, ComposeFile: "docker-compose.yml", Enabled: true, } fmt.Println("Removing nginx-proxy...") if err := nm.composeManager.Down(ctx, env, true); err != nil { return fmt.Errorf("failed to remove nginx-proxy: %w", err) } fmt.Println("Nginx-proxy removed successfully!") return nil } // Status returns the status of nginx-proxy func (nm *NginxManager) Status(ctx context.Context) (string, error) { env := models.Environment{ Name: "nginx-proxy", Path: nm.config.Path, ComposeFile: "docker-compose.yml", Enabled: true, } return nm.composeManager.PS(ctx, env) } // generateComposeFile generates the docker-compose.yml content for nginx-proxy func (nm *NginxManager) generateComposeFile() string { content := fmt.Sprintf(`version: '3.8' services: nginx-proxy: image: %s container_name: %s restart: unless-stopped ports: - "%d:80"`, nginxProxyImage, nm.config.ContainerName, nm.config.HTTPPort) if nm.config.SSLEnabled { content += fmt.Sprintf(` - "%d:443"`, nm.config.HTTPSPort) } content += ` volumes: - /var/run/docker.sock:/tmp/docker.sock:ro` if nm.config.SSLEnabled { content += ` - certs:/etc/nginx/certs:ro - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html` } content += ` environment: - DEFAULT_HOST=localhost networks: - ` + nm.config.NetworkName content += ` labels: - "buque.managed=true" - "buque.service=nginx-proxy"` if nm.config.SSLEnabled { content += fmt.Sprintf(` acme-companion: image: %s container_name: nginx-proxy-acme restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - certs:/etc/nginx/certs - vhost:/etc/nginx/vhost.d - html:/usr/share/nginx/html - acme:/etc/acme.sh environment: - DEFAULT_EMAIL=admin@localhost - NGINX_PROXY_CONTAINER=%s networks: - %s depends_on: - nginx-proxy labels: - "buque.managed=true" - "buque.service=nginx-proxy-acme"`, nginxProxyCompanionImage, nm.config.ContainerName, nm.config.NetworkName) } content += ` networks: ` + nm.config.NetworkName + `: external: true` if nm.config.SSLEnabled { content += ` volumes: certs: vhost: html: acme:` } return content } // GenerateServiceLabels generates labels for a service to work with nginx-proxy func (nm *NginxManager) GenerateServiceLabels(virtualHost string, virtualPort int, letsencryptHost string, letsencryptEmail string) map[string]string { labels := map[string]string{ "VIRTUAL_HOST": virtualHost, } if virtualPort > 0 { labels["VIRTUAL_PORT"] = fmt.Sprintf("%d", virtualPort) } if nm.config.SSLEnabled && letsencryptHost != "" { labels["LETSENCRYPT_HOST"] = letsencryptHost if letsencryptEmail != "" { labels["LETSENCRYPT_EMAIL"] = letsencryptEmail } } return labels } // GetExampleServiceCompose returns an example docker-compose.yml for a service behind nginx-proxy func (nm *NginxManager) GetExampleServiceCompose(serviceName, virtualHost string) string { return fmt.Sprintf(`version: '3.8' services: %s: image: your-image:latest container_name: %s restart: unless-stopped expose: - "80" environment: - VIRTUAL_HOST=%s - VIRTUAL_PORT=80 - LETSENCRYPT_HOST=%s - LETSENCRYPT_EMAIL=admin@%s networks: - %s labels: - "buque.environment=%s" - "buque.managed=true" networks: %s: external: true `, serviceName, serviceName, virtualHost, virtualHost, virtualHost, nm.config.NetworkName, serviceName, nm.config.NetworkName) }