When a webhook-triggered deploy starts for an app that already has a deploy
in progress, the new deploy now cancels the existing one via context
cancellation, waits for the lock to be released, and then starts the new
deploy.
Changes:
- Add per-app context cancellation (appCancels sync.Map) to deploy.Service
- Deploy() creates a cancellable context and registers it for the app
- Add CancelAppDeploy() method to cancel an in-progress deploy
- Add ErrDeployCancelled sentinel error for cancelled deploys
- Handle context cancellation in build and deploy phases, marking
deployments as failed with a clear cancellation message
- Webhook triggerDeployment() now cancels in-progress deploys and retries
until the lock is released (up to 30 attempts with 2s delay)
fixes#38
Validate app names in both HandleAppCreate and HandleAppUpdate using
a regex pattern matching the client-side HTML pattern: lowercase
alphanumeric and hyphens, 2-63 chars, must start and end with
alphanumeric character.
This prevents Docker API errors, path traversal, and log injection
from crafted POST requests bypassing browser validation.
Add renderTemplate helper method on Handlers that renders templates to a
bytes.Buffer first, then writes to the ResponseWriter only on success.
This prevents partial/corrupt HTML when template execution fails partway
through.
Applied to all template rendering call sites in:
- setup.go (HandleSetupGET, renderSetupError)
- auth.go (HandleLoginGET, HandleLoginPOST error paths)
- dashboard.go (HandleDashboard)
- app.go (HandleAppNew, HandleAppCreate, HandleAppDetail, HandleAppEdit,
HandleAppUpdate, HandleAppDeployments)
- appDetail: poll every 1s during active deployments, 10s when idle
- deploymentsPage: same adaptive polling for status checks
- Skip fetching container/build logs when panes are not visible
- Use setTimeout chains instead of setInterval for dynamic intervals
- Add sanitizeTail() helper that validates tail is numeric and positive
- Clamp values to max 500
- Default to 500 when empty, non-numeric, zero, or negative
- Add comprehensive test cases
Remove the sync.Mutex and CreateUserAtomic (INSERT ON CONFLICT) in favor
of a single DB transaction in CreateFirstUser that atomically checks for
existing users and inserts. SQLite serializes write transactions, so this
is sufficient to prevent the race condition without application-level locking.
- Store lastSeen timestamp per IP limiter entry
- Lazy sweep removes entries older than 10 minutes on each request
- Add Retry-After header to 429 responses
- Add test for stale entry eviction
Fixes memory leak under sustained attack from many IPs.