Replace the string-serialized KEY=value format with a proper JSON array
of {key, value} objects for the env var save endpoint.
Frontend changes:
- envVarEditor.submitAll() now uses fetch() with Content-Type:
application/json and X-CSRF-Token header instead of form submission
- Sends JSON array: [{"key":"FOO","value":"bar"}, ...]
- Hidden bulk form replaced with hidden div holding CSRF token
- envVarEditor now receives appId parameter for the fetch URL
Backend changes:
- HandleEnvVarSave reads JSON body via json.NewDecoder instead of
parsing form values with parseEnvPairs
- Returns JSON {"ok": true} instead of HTTP redirect
- Removed parseEnvPairs function and envPair struct entirely
- Added envPairJSON struct with json tags for deserialization
Tests updated to POST JSON arrays instead of form-encoded strings.
Closes #163
Replace the Save All workflow with the original per-action behavior:
- Edit row: shows Save/Cancel buttons, submits full set immediately
- Delete row: shows confirmation dialog, submits full set immediately
- Add row: submits full set immediately on Add click
Moves Alpine.js logic into a proper envVarEditor component in
app-detail.js. Initializes env var data from hidden span elements
with data attributes for safe HTML escaping.
All actions collect the complete env var set and POST to the single
bulk endpoint POST /apps/{id}/env — no Save All button needed.
Changes per sneak's review:
- Delete docker-compose.yml, add example stanza to README
- Define custom domain types: ImageID, ContainerID, UnparsedURL
- Use custom types in all function signatures throughout codebase
- Restore imageID parameter (as domain.ImageID) in deploy pipeline
- buildContainerOptions now takes ImageID directly instead of
constructing image tag from deploymentID
- Fix pre-existing JS formatting (prettier)
make check passes with zero failures.
- Update nolint comment on log streaming to accurately describe why
gosec is suppressed (text/plain Content-Type, not HTML)
- Replace <script type="text/plain"> with data attribute for initial
logs to prevent </script> breakout from attacker-controlled log data
- Move RemoveImage before unexported methods (funcorder)
- Fix file permissions in test (gosec G306)
- Rename unused parameters in export_test.go (revive)
- Add required blank line before assignment (wsl)
- 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
- Track scroll position per log pane (container logs, build logs, deployment cards)
- Auto-scroll to bottom only when user is already at bottom (tail-follow)
- When user scrolls up to review earlier output, pause auto-scroll
- Show a '↓ Follow' button when auto-scroll is paused; clicking resumes
- Only scroll on actual content changes (skip no-op updates)
- Use overflow-y: auto for proper scrollable containers
- Add break-words to prevent horizontal overflow on long lines
Closes#17
Replaced manual DOM manipulation with a deploymentCard Alpine component
that uses x-text bindings and x-ref for proper scrolling, matching the
working container logs implementation.
Use latestDeploymentStatus instead of app status to determine if
a deployment is in progress. The deployment status is more reliable
during state transitions and prevents the button from flickering.
- Reload page when new deployment starts but no card exists in DOM
- Only update logs content when it changes to avoid unnecessary reflows
- Use double requestAnimationFrame for reliable scroll-to-bottom timing
- Use line-by-line reading for Docker build output instead of io.Copy
to ensure each log line is written immediately without buffering
- Add isNearBottom() helper to check scroll position before auto-scroll
- Only auto-scroll logs if user was already near bottom (better UX)
- Use requestAnimationFrame for smoother scroll-to-bottom animation
- Add Alpine.js (self-hosted, embedded in static/)
- Refactor app.js to use Alpine.js stores and components
- Update templates to use x-data, x-bind, x-show, x-text directives
- Add reactive deploy button state, live logs, status badges
- Add auto-dismiss alerts with close button and transitions
- Add copy-to-clipboard component with feedback
- Add confirm dialog component for destructive actions
- Add relative time component with auto-update
- Add prettier to make fmt target for JS formatting
- Add deploy stats (last deploy time, total count) to dashboard
- Add recent-deployments API endpoint for real-time updates
- Add live build logs to deployments history page
- Fix git clone regression (preserve entrypoint for simple clones)
- Refactor JavaScript into shared app.js with page init functions
- Deploy button disables immediately on click
- Auto-refresh deployment list and logs during builds
- Format JavaScript with Prettier (4-space indent)