feat(docker-watcher): phase 12 - hardening

Blue-green zero-downtime deploys, promote flow validation.
Dual auth: local (bcrypt + JWT) and OAuth2/OIDC (any provider).
Auth middleware, login page, auth settings UI.
Structured logging (slog JSON), config export to YAML.
Graceful shutdown with deploy draining.
Multi-stage Dockerfile and production docker-compose.yml.
Swap phase order: Volumes & Env before UI Polish.
This commit is contained in:
2026-03-27 23:20:56 +03:00
parent 5558396bb7
commit 32de5b26a8
30 changed files with 2134 additions and 143 deletions
+8 -8
View File
@@ -3,7 +3,7 @@ package deployer
import (
"context"
"fmt"
"log"
"log/slog"
)
// rollback cleans up a failed deployment by removing the container,
@@ -15,7 +15,7 @@ func (d *Deployer) rollback(ctx context.Context, deployID string, containerID st
// Remove the container if it was created.
if containerID != "" {
if err := d.docker.RemoveContainer(ctx, containerID, true); err != nil {
log.Printf("rollback: remove container %s: %v", containerID, err)
slog.Warn("rollback: remove container", "container_id", containerID, "error", err)
d.logDeploy(deployID, fmt.Sprintf("Rollback: failed to remove container: %v", err), "error")
} else {
d.logDeploy(deployID, "Rollback: container removed", "info")
@@ -26,16 +26,16 @@ func (d *Deployer) rollback(ctx context.Context, deployID string, containerID st
if npmProxyID > 0 {
settings, err := d.store.GetSettings()
if err != nil {
log.Printf("rollback: get settings for npm auth: %v", err)
slog.Warn("rollback: get settings for npm auth", "error", err)
d.logDeploy(deployID, fmt.Sprintf("Rollback: failed to get settings for proxy cleanup: %v", err), "error")
} else if npmPassword, err := d.decryptNpmPassword(settings.NpmPassword); err != nil {
log.Printf("rollback: decrypt npm password: %v", err)
slog.Warn("rollback: decrypt npm password", "error", err)
d.logDeploy(deployID, "Rollback: failed to decrypt NPM password for proxy cleanup", "error")
} else if err := d.npm.Authenticate(ctx, settings.NpmEmail, npmPassword); err != nil {
log.Printf("rollback: authenticate npm: %v", err)
slog.Warn("rollback: authenticate npm", "error", err)
d.logDeploy(deployID, "Rollback: failed to authenticate NPM for proxy cleanup", "error")
} else if err := d.npm.DeleteProxyHost(ctx, npmProxyID); err != nil {
log.Printf("rollback: delete proxy host %d: %v", npmProxyID, err)
slog.Warn("rollback: delete proxy host", "proxy_id", npmProxyID, "error", err)
d.logDeploy(deployID, fmt.Sprintf("Rollback: failed to delete proxy host: %v", err), "error")
} else {
d.logDeploy(deployID, "Rollback: proxy host deleted", "info")
@@ -45,13 +45,13 @@ func (d *Deployer) rollback(ctx context.Context, deployID string, containerID st
// Update instance status to failed if it was created.
if instanceID != "" {
if err := d.store.UpdateInstanceStatus(instanceID, "failed"); err != nil {
log.Printf("rollback: update instance %s status: %v", instanceID, err)
slog.Warn("rollback: update instance status", "instance_id", instanceID, "error", err)
}
}
// Mark deploy as rolled back.
if err := d.store.UpdateDeployStatus(deployID, "rolled_back", "deployment failed, rolled back"); err != nil {
log.Printf("rollback: update deploy %s status: %v", deployID, err)
slog.Warn("rollback: update deploy status", "deploy_id", deployID, "error", err)
}
d.logDeploy(deployID, "Rollback complete", "info")