Skip to content

feat(orchestrator): add support for Docker Compose project deployments - #32

Open
Lftobs wants to merge 15 commits into
devfrom
feat/managed-db-and-compose
Open

feat(orchestrator): add support for Docker Compose project deployments#32
Lftobs wants to merge 15 commits into
devfrom
feat/managed-db-and-compose

Conversation

@Lftobs

@Lftobs Lftobs commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Description

  • Update database schema to store compose-specific metadata
  • Enhance Caddy dynamic ingress to route traffic to specific services
  • Extend project/domain APIs to support service target configuration
  • Refactor documentation landing page and UI components

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor (no functional changes)
  • CI / Build / Tooling
  • Other (please describe):

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.

  • Existing tests pass (bun test in apps/api/)
  • New tests added (if applicable)
  • Manual testing performed (describe steps)

Checklist

  • My code follows the project's code style (no comments, named exports, functional components, etc.)
  • I have read the contributing guidelines
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation (if applicable)
  • My changes generate no new warnings or lint errors
  • I have run bun test in apps/api/ and all tests pass
  • I have synced the VERSION file if needed (bun run sync-versions)

Screenshots (if applicable)

Before After
(insert here) (insert here)

Additional Context

Add any other context about the PR here (e.g., migration notes, deployment considerations, rollback strategy).

Summary by CodeRabbit

  • New Features

    • Added Docker Compose project support, including build selection, service mappings, ports, subdomains, and multi-step project creation.
    • Added custom domain routing to selected Compose services and ports.
    • Added environment-variable imports and optional database provisioning during project setup.
    • Added automatic session refresh when authentication expires.
  • Bug Fixes

    • Improved database provisioning recovery and failure reporting.
    • Added validation for Compose services, ports, domains, and routing settings.
  • Documentation

    • Redesigned the documentation landing page with updated deployment guidance, feature previews, comparisons, and installation messaging.

Lftobs and others added 4 commits June 15, 2026 12:47
project deployments

- Update database schema to store compose-specific
  metadata
- Enhance Caddy dynamic ingress to route traffic
  to specific services
- Extend project/domain APIs to support service
  target configuration
- Refactor documentation landing page and UI
  components
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds Docker Compose project configuration, validation, deployment orchestration, service-specific domain routing, database provisioning recovery, a multi-step project creation page, session refresh handling, and a redesigned documentation landing page.

Changes

Compose platform

Layer / File(s) Summary
Compose contracts and persistence
apps/api/src/types.ts, apps/api/src/db/schema.ts, apps/api/src/db/repo/projects.ts, apps/api/src/db/migrations/*
Projects now store build strategy and Compose service configuration.
Compose validation and project API
apps/api/src/utils/validate.ts, apps/api/src/api/projects/index.ts, apps/api/src/utils/__tests__/validate.test.ts
Project creation and updates validate Compose names, ports, subdomains, and service lists before persistence.
Compose build and deployment orchestration
apps/api/src/orchestrator/compose.ts, apps/api/src/orchestrator/pipeline.ts, apps/api/src/orchestrator/__tests__/compose.test.ts
The API parses Compose files, builds and deploys stacks, discovers containers, generates Caddy routes, and cleans up Compose deployments.
Compose project creation and settings
apps/web/src/routes/CreateProjectPage.tsx, apps/web/src/components/project/settings/ProjectSettingsTab.tsx, apps/web/src/routes/index.tsx, apps/web/src/routes/Dashboard.tsx
The web app adds multi-step project creation and Compose service configuration controls.

Domain service routing

Layer / File(s) Summary
Domain routing contracts and persistence
apps/api/src/types.ts, apps/api/src/db/schema.ts, apps/api/src/db/repo/domains.ts, apps/api/src/db/migrations/0005_famous_grandmaster.sql
Domains now store optional target service and port values.
Domain target validation and Caddy routes
apps/api/src/api/domains/index.ts, apps/api/src/utils/validate.ts, apps/api/src/utils/domain-verifier.ts
Domain creation validates routing targets, and Caddy creates custom proxy blocks for selected Compose services.
Domain routing controls
apps/web/src/components/project/domains/DomainsTab.tsx, apps/web/src/api/client.ts
Compose projects expose target service and port fields in the domain form.

Database provisioning recovery

Layer / File(s) Summary
Provisioning cleanup and failure status
apps/api/src/databases/manager.ts
Provisioning removes an existing container, records failures on errors or timeouts, logs errors, and rethrows the original error.

Session refresh

Layer / File(s) Summary
Authentication refresh flow
apps/web/src/api/client.ts
getMe refreshes unauthenticated sessions and retries the current-user request.

Documentation refresh

Layer / File(s) Summary
Landing page content and interactions
apps/docs/src/pages/index.astro, apps/docs/src/components/*, apps/docs/.astro/*
The documentation landing page adds comparison content, revised platform sections, updated terminal behavior, and refreshed metadata.
Landing page styling
apps/docs/src/styles/global.css
Global styles add hero cards, pill buttons, capability tabs, comparison cards, and updated selection colors.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CreateProjectPage
  participant ProjectsAPI
  participant ComposeOrchestrator
  participant Caddy
  User->>CreateProjectPage: Configure Compose services
  CreateProjectPage->>ProjectsAPI: Create project and deployment
  ProjectsAPI->>ComposeOrchestrator: Build and deploy Compose stack
  ComposeOrchestrator->>Caddy: Generate and reload service routes
  Caddy-->>CreateProjectPage: Return deployed runtime URL
Loading

Possibly related PRs

  • Lftobs/dequel#17: Shares deployment orchestration changes in apps/api/src/orchestrator/pipeline.ts.
  • Lftobs/dequel#28: Shares project API changes in apps/api/src/api/projects/index.ts.
  • Lftobs/dequel#29: Directly overlaps Compose validation, schema, orchestration, routing, and web UI changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Docker Compose project deployment support to the orchestrator.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/managed-db-and-compose

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/src/utils/domain-verifier.ts (1)

127-155: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Resolve the fallback port before generating custom blocks.

The loop formats customBlocks before the PORT environment variable can update port. If a domain has targetService but no targetPort, its custom block uses config.appInternalPort while the primary block can use PORT.

Read and validate PORT before the domain loop, or defer custom-block formatting until after port resolution. Add a regression test for a target service without targetPort.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/utils/domain-verifier.ts` around lines 127 - 155, The domain
loop currently builds custom blocks before the PORT environment variable can
update the fallback port. In the domain-verification flow around
listEnvironmentVariablesForDeploy and customBlocks generation, resolve and
validate PORT before iterating verified domains, or defer block formatting until
afterward, so targetService entries without targetPort use the resolved port
consistently with the primary block. Add a regression test covering a target
service with no targetPort.
🧹 Nitpick comments (3)
apps/web/src/components/project/settings/ProjectSettingsTab.tsx (1)

143-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the nonessential source comment.

The JSX structure already identifies this section.

As per coding guidelines, **/*.{ts,tsx,js,jsx} files must have no comments in source code unless absolutely necessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/project/settings/ProjectSettingsTab.tsx` at line 143,
Remove the nonessential JSX comment `{/* Build Strategy */}` from the project
settings component, leaving the surrounding JSX structure and behavior
unchanged.

Source: Coding guidelines

apps/web/src/routes/CreateProjectPage.tsx (1)

44-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the new section comments.

The state grouping and JSX hierarchy already identify these sections.

As per coding guidelines, **/*.{ts,tsx,js,jsx} files must have no comments in source code unless absolutely necessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/routes/CreateProjectPage.tsx` around lines 44 - 96, Remove the
section comments around the state declarations in the component, including “Form
State,” “Git Settings,” “ZIP Settings,” “Build & Runtime Options (Railpack),”
“Docker Compose Settings,” and “Environment Variables State & Tabs.” Leave the
existing state declarations and JSX hierarchy unchanged.

Source: Coding guidelines

apps/api/src/orchestrator/compose.ts (1)

46-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove comments that restate parser control flow.

These comments only describe the adjacent conditions and regexes. Use clear helper names when context needs explanation.

As per coding guidelines, **/*.{ts,tsx,js,jsx}: “No comments in source code unless absolutely necessary.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/orchestrator/compose.ts` around lines 46 - 94, Remove the
redundant control-flow comments in the parser block around the services,
top-level block, service declaration, ports block, inline port syntax, and
port-item handling; preserve the existing parsing logic unchanged and rely on
the conditions, regexes, and helper names for context.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/api/domains/index.ts`:
- Around line 33-38: Update addToCaddyRoute and both domain-verification call
sites so verified domains use their persisted Domain targetService and
targetPort values when generating Caddy routing. Generate a dedicated proxy
block for targeted domains instead of always appending to the primary Caddy
block, while preserving existing routing for domains without targets.

In `@apps/api/src/databases/manager.ts`:
- Around line 67-78: Update the provisioning flow after the readiness loop to
throw a timeout error instead of resolving normally after updateDatabaseStatus
records the database as failed. Ensure the existing catch handling captures and
propagates this error while preserving the failed status update.

In `@apps/api/src/orchestrator/compose.ts`:
- Around line 223-263: Propagate the deployment controller’s abort signal
through buildWithCompose and deployWithCompose into spawnComposeCommand,
extending its options as needed. In spawnComposeCommand, listen for signal
abortion and terminate the spawned Compose child process, while preserving
normal completion and error handling.
- Around line 11-17: Update findComposeFilePath to resolve and realpath the
workspace and sourceDir-derived candidate directory, rejecting any candidate
outside the resolved workspace, including paths escaping through ../ segments.
Use the validated candidate directory when locating docker-compose.yml or
docker-compose.yaml and ensure the same validated directory is used as the
deployment cwd.
- Around line 41-96: Replace the regex-based Compose parsing in
parseComposeTarget and parseAllComposeServices with normalized Compose data from
a structured YAML parser or Docker Compose output. Implement one shared
service-extraction function used by both callers, handling host-bound mappings
such as 127.0.0.1:8080:80 and long-form ports while selecting the container
target port so servicePorts is populated instead of falling back to 3000.

In `@apps/api/src/orchestrator/pipeline.ts`:
- Around line 575-700: Extract the compose-specific branch from the deployment
orchestration around deployWithCompose into a dedicated, feature-grouped Compose
deployment module. Move stack replacement, network attachment, service/container
resolution, Caddy route generation and reload, and runtime URL/name assignment
there, exposing a narrow result contract containing the runtime container name
and live URL; update the parent orchestration to call this module while
preserving the existing non-compose path and behavior.
- Around line 412-427: Update the Compose deployment and rollback flow around
buildWithCompose, rollbackTo, and deployContainer so Compose rollback does not
attempt to start the unavailable imageTag. Either persist or recreate the prior
Compose source revision and restore it through the Compose deployment path,
including its services and Caddy routes; otherwise reject rollback requests for
Compose deployments before invoking single-container rollback logic.

In `@apps/api/src/utils/validate.ts`:
- Around line 27-31: Reject non-number and non-decimal-string values before
conversion in validate.ts's port validation, preserving the existing 1–65535
integer rule. Add boolean and array failure assertions in
apps/api/src/utils/__tests__/validate.test.ts lines 22-27. In
apps/api/src/api/projects/index.ts lines 22-26, detect composePort by presence
rather than truthiness, reuse the strict port parser, and treat only null as an
explicit clear operation.

In `@apps/docs/src/components/Hero.astro`:
- Around line 10-13: Update the Hero component’s props and defaults so accepted
values are not ignored: either render tag, titleNormal, and titleItalic in the
markup around the hardcoded title and commented tag usage, or remove these props
and their default values from the Props definition and component setup. Keep the
chosen approach consistent with the component’s rendered output.

In `@apps/docs/src/components/HowItWorks.astro`:
- Around line 44-47: Update the descriptive text in the “Instant Auto Routing”
section to say the internal Caddy engine reloads dynamic routes “with zero
downtime,” preserving the existing claim and surrounding content.

In `@apps/docs/src/pages/index.astro`:
- Around line 203-204: Replace the inline style on the “dequel” span in the page
markup with equivalent static Tailwind utility classes for font weight, font
size, letter spacing, and muted text color. Keep the rendered appearance
unchanged and remove the style attribute.

In `@apps/docs/src/styles/global.css`:
- Around line 895-917: Remove the quotation marks around the single-word Play
font family in the affected .cf-pill-btn-white and .cf-pill-btn-glass
declarations, preserving the existing sans-serif fallback.

In `@apps/web/src/components/project/domains/DomainsTab.tsx`:
- Around line 495-522: Refactor DomainsTab into feature-local components so the
file is under 500 lines: extract the add-domain dialog and domain-list UI into
dedicated components within the domains feature folder, while keeping domain
state and API mutations in a focused container or hook used by DomainsTab.
Preserve the existing compose-specific fields, behavior, and component
interactions.

In `@apps/web/src/components/project/settings/ProjectSettingsTab.tsx`:
- Around line 47-58: Update the composeServices parsing flow in
ProjectSettingsTab to validate and normalize every parsed service row before
calling setComposeServicesList. Require each row to be a non-null object with
string id, serviceName, port, and subdomain fields; if any row fails validation,
use the existing default service entry instead.
- Around line 83-96: Only include composeService, composePort, and
composeServices in the updateProjectMutation payload within
ProjectSettingsTab.tsx when buildType === "compose"; omit them for Railpack
builds. Apply the same conditional payload handling in CreateProjectPage.tsx at
lines 243-261, while preserving the existing Compose values and validation
behavior for Compose builds.

In `@apps/web/src/routes/CreateProjectPage.tsx`:
- Around line 263-297: Reorder the submission flow so the staged environment
variables and managed database are created before the deployment request. Move
the `api.createDeployment` block below the `stagedEnvs` and `provisionDb`
handling in the surrounding submit function, while preserving the existing
status updates and payloads.
- Around line 72-76: Update the step-four and sidebar summary displays in
CreateProjectPage to derive the primary service and port from
composeServicesList[0] instead of the stale composeService and composePort
state. Reuse the first entry’s serviceName and port values, preserving the
existing Auto-detect fallback when they are empty, and remove the duplicate
state only if it is no longer used elsewhere.

---

Outside diff comments:
In `@apps/api/src/utils/domain-verifier.ts`:
- Around line 127-155: The domain loop currently builds custom blocks before the
PORT environment variable can update the fallback port. In the
domain-verification flow around listEnvironmentVariablesForDeploy and
customBlocks generation, resolve and validate PORT before iterating verified
domains, or defer block formatting until afterward, so targetService entries
without targetPort use the resolved port consistently with the primary block.
Add a regression test covering a target service with no targetPort.

---

Nitpick comments:
In `@apps/api/src/orchestrator/compose.ts`:
- Around line 46-94: Remove the redundant control-flow comments in the parser
block around the services, top-level block, service declaration, ports block,
inline port syntax, and port-item handling; preserve the existing parsing logic
unchanged and rely on the conditions, regexes, and helper names for context.

In `@apps/web/src/components/project/settings/ProjectSettingsTab.tsx`:
- Line 143: Remove the nonessential JSX comment `{/* Build Strategy */}` from
the project settings component, leaving the surrounding JSX structure and
behavior unchanged.

In `@apps/web/src/routes/CreateProjectPage.tsx`:
- Around line 44-96: Remove the section comments around the state declarations
in the component, including “Form State,” “Git Settings,” “ZIP Settings,” “Build
& Runtime Options (Railpack),” “Docker Compose Settings,” and “Environment
Variables State & Tabs.” Leave the existing state declarations and JSX hierarchy
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bd325b6-a68e-4470-aad9-4021eb7def87

📥 Commits

Reviewing files that changed from the base of the PR and between 13fc38a and 027e32d.

📒 Files selected for processing (39)
  • apps/api/src/api/domains/index.ts
  • apps/api/src/api/projects/index.ts
  • apps/api/src/databases/manager.ts
  • apps/api/src/db/migrations/0004_early_sunfire.sql
  • apps/api/src/db/migrations/0005_famous_grandmaster.sql
  • apps/api/src/db/migrations/0006_cheerful_vance_astro.sql
  • apps/api/src/db/migrations/meta/0004_snapshot.json
  • apps/api/src/db/migrations/meta/0005_snapshot.json
  • apps/api/src/db/migrations/meta/0006_snapshot.json
  • apps/api/src/db/migrations/meta/_journal.json
  • apps/api/src/db/repo/domains.ts
  • apps/api/src/db/repo/projects.ts
  • apps/api/src/db/schema.ts
  • apps/api/src/orchestrator/__tests__/compose.test.ts
  • apps/api/src/orchestrator/compose.ts
  • apps/api/src/orchestrator/pipeline.ts
  • apps/api/src/types.ts
  • apps/api/src/utils/__tests__/domain-verifier.test.ts
  • apps/api/src/utils/__tests__/validate.test.ts
  • apps/api/src/utils/domain-verifier.ts
  • apps/api/src/utils/validate.ts
  • apps/docs/.astro/astro/content.d.ts
  • apps/docs/.astro/settings.json
  • apps/docs/src/components/CTA.astro
  • apps/docs/src/components/Comparison.astro
  • apps/docs/src/components/Features.astro
  • apps/docs/src/components/Hero.astro
  • apps/docs/src/components/HowItWorks.astro
  • apps/docs/src/components/Stats.astro
  • apps/docs/src/components/TerminalDemo.astro
  • apps/docs/src/pages/index.astro
  • apps/docs/src/styles/global.css
  • apps/web/src/api/client.ts
  • apps/web/src/components/project/domains/DomainsTab.tsx
  • apps/web/src/components/project/settings/ProjectSettingsTab.tsx
  • apps/web/src/routes/CreateProjectPage.tsx
  • apps/web/src/routes/Dashboard.tsx
  • apps/web/src/routes/index.tsx
  • apps/web/src/types/index.ts

Comment on lines 33 to +38
const domain = await createDomain({
projectId: params.id,
domain: body.domain,
type: body.type ?? "custom",
targetService: body.targetService || null,
targetPort,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve target routing for domains verified after deployment.

This route persists targetService and targetPort, but the verification flow calls addToCaddyRoute without either value. addToCaddyRoute only appends the domain to the primary Caddy block. A target domain added after deployment therefore routes to the default container and port until a later redeploy regenerates the snippet.

Update addToCaddyRoute and both verification call sites to generate a dedicated proxy block from the persisted Domain, or regenerate the project snippet from persisted domain records.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/api/domains/index.ts` around lines 33 - 38, Update
addToCaddyRoute and both domain-verification call sites so verified domains use
their persisted Domain targetService and targetPort values when generating Caddy
routing. Generate a dedicated proxy block for targeted domains instead of always
appending to the primary Caddy block, while preserving existing routing for
domains without targets.

Comment on lines +67 to +78
for (let i = 0; i < 30; i++) {
try {
const status = await run(dockerBin, ['inspect', '-f', '{{.State.Status}}', containerName]);
if (status.trim() === 'running') {
await updateDatabaseStatus(dbRecord.id, 'running', containerName);
return;
}
} catch {}
await new Promise(r => setTimeout(r, 2000));
}

await updateDatabaseStatus(dbRecord.id, 'failed', containerName);
await updateDatabaseStatus(dbRecord.id, 'failed', containerName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail the provisioning operation after the readiness timeout.

At Line 78, the function records failed and then resolves normally. Callers can treat the provisioning operation as successful and continue with a database that never reached running.

Throw a timeout error here. The existing catch block will record the failed status, log the error, and propagate the failure.

Proposed fix
-    await updateDatabaseStatus(dbRecord.id, 'failed', containerName);
+    throw new Error(`Database ${dbRecord.id} did not reach running state before the readiness timeout`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (let i = 0; i < 30; i++) {
try {
const status = await run(dockerBin, ['inspect', '-f', '{{.State.Status}}', containerName]);
if (status.trim() === 'running') {
await updateDatabaseStatus(dbRecord.id, 'running', containerName);
return;
}
} catch {}
await new Promise(r => setTimeout(r, 2000));
}
await updateDatabaseStatus(dbRecord.id, 'failed', containerName);
await updateDatabaseStatus(dbRecord.id, 'failed', containerName);
for (let i = 0; i < 30; i++) {
try {
const status = await run(dockerBin, ['inspect', '-f', '{{.State.Status}}', containerName]);
if (status.trim() === 'running') {
await updateDatabaseStatus(dbRecord.id, 'running', containerName);
return;
}
} catch {}
await new Promise(r => setTimeout(r, 2000));
}
throw new Error(`Database ${dbRecord.id} did not reach running state before the readiness timeout`);
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from 'node:child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/databases/manager.ts` around lines 67 - 78, Update the
provisioning flow after the readiness loop to throw a timeout error instead of
resolving normally after updateDatabaseStatus records the database as failed.
Ensure the existing catch handling captures and propagates this error while
preserving the failed status update.

Comment on lines +11 to +17
export const findComposeFilePath = (workspacePath: string, sourceDir?: string | null): string | null => {
const base = sourceDir ? join(workspacePath, sourceDir.replace(/^\//, "")) : workspacePath;
const ymlPath = join(base, "docker-compose.yml");
if (existsSync(ymlPath)) return ymlPath;
const yamlPath = join(base, "docker-compose.yaml");
if (existsSync(yamlPath)) return yamlPath;
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Block traversal outside the deployment workspace.

sourceDir reaches this function from the project API. Removing only the first / leaves ../ segments intact. join() can then select a Compose file and working directory outside workspacePath.

Resolve and realpath the workspace and candidate directory. Reject the candidate when it is outside the workspace. Use the validated directory for both the Compose file and cwd.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/orchestrator/compose.ts` around lines 11 - 17, Update
findComposeFilePath to resolve and realpath the workspace and sourceDir-derived
candidate directory, rejecting any candidate outside the resolved workspace,
including paths escaping through ../ segments. Use the validated candidate
directory when locating docker-compose.yml or docker-compose.yaml and ensure the
same validated directory is used as the deployment cwd.

Comment thread apps/api/src/orchestrator/compose.ts Outdated
Comment on lines +41 to +96
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) continue;

// Check if entering services block
if (line.match(/^services:/)) {
inServicesBlock = true;
continue;
}

// Top level block exit
if (line.match(/^[a-zA-Z0-9_-]+:/) && !line.startsWith("services:")) {
inServicesBlock = false;
currentService = "";
}

if (!inServicesBlock) continue;

// Service declaration under services: (2 spaces indentation)
const serviceMatch = line.match(/^ ([a-zA-Z0-9_-]+):/);
if (serviceMatch) {
currentService = serviceMatch[1];
servicesList.push(currentService);
inPortsBlock = false;
continue;
}

if (currentService) {
// Check if entering ports block
if (line.match(/^\s+ports:/)) {
inPortsBlock = true;
// Inline array syntax: ports: ["8080:80"] or ports: [3000]
const inlineMatch = trimmed.match(/ports:\s*\[\s*["']?(\d+)(?::(\d+))?["']?\s*\]/i);
if (inlineMatch) {
const target = inlineMatch[2] || inlineMatch[1];
servicePorts[currentService] = Number(target);
inPortsBlock = false;
}
continue;
}

// Port item under ports block
if (inPortsBlock && line.match(/^\s+-\s*/)) {
const portMatch = trimmed.match(/-\s*["']?(\d+)(?::(\d+))?["']?/);
if (portMatch) {
const targetPort = portMatch[2] || portMatch[1];
if (!servicePorts[currentService]) {
servicePorts[currentService] = Number(targetPort);
}
}
} else if (line.match(/^\s+[a-zA-Z0-9_-]+:/)) {
inPortsBlock = false;
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Replace the regex-based Compose parser.

The parser accepts only limited short port syntax. Valid mappings such as "127.0.0.1:8080:80" and long-form ports entries leave servicePorts unset. The deployment then routes Caddy to the fallback port 3000.

Parse normalized Compose data with a structured parser or Docker Compose output. Share one service-extraction function between parseComposeTarget and parseAllComposeServices.

Also applies to: 154-203

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/orchestrator/compose.ts` around lines 41 - 96, Replace the
regex-based Compose parsing in parseComposeTarget and parseAllComposeServices
with normalized Compose data from a structured YAML parser or Docker Compose
output. Implement one shared service-extraction function used by both callers,
handling host-bound mappings such as 127.0.0.1:8080:80 and long-form ports while
selecting the container target port so servicePorts is populated instead of
falling back to 3000.

Comment on lines +223 to +263
const spawnComposeCommand = (
args: string[],
cwd: string,
envVars?: Record<string, string>,
onLog?: (line: string) => Promise<void>,
): Promise<{ code: number; stdout: string; stderr: string }> => {
return new Promise((resolve, reject) => {
const child = spawn(dockerBin, ["compose", ...args], {
cwd,
env: { ...process.env, ...(envVars || {}) },
stdio: ["ignore", "pipe", "pipe"],
});

let stdout = "";
let stderr = "";

child.stdout.on("data", (chunk) => {
const str = String(chunk);
stdout += str;
if (onLog) {
for (const line of str.split("\n").map((l) => l.trim()).filter(Boolean)) {
void onLog(line);
}
}
});

child.stderr.on("data", (chunk) => {
const str = String(chunk);
stderr += str;
if (onLog) {
for (const line of str.split("\n").map((l) => l.trim()).filter(Boolean)) {
void onLog(line);
}
}
});

child.on("error", reject);
child.on("close", (code) => {
resolve({ code: code ?? 1, stdout: stdout.trim(), stderr: stderr.trim() });
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Terminate Compose builds when deployment cancellation occurs.

cancelDeployment aborts its controller, but this process does not receive an abort signal. A canceled Compose build continues to use Docker and host resources until it exits.

Pass the controller signal through buildWithCompose to spawnComposeCommand. Stop the child process when that signal aborts. Apply the same mechanism to deployWithCompose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/src/orchestrator/compose.ts` around lines 223 - 263, Propagate the
deployment controller’s abort signal through buildWithCompose and
deployWithCompose into spawnComposeCommand, extending its options as needed. In
spawnComposeCommand, listen for signal abortion and terminate the spawned
Compose child process, while preserving normal completion and error handling.

Comment thread apps/web/src/components/project/domains/DomainsTab.tsx
Comment thread apps/web/src/components/project/settings/ProjectSettingsTab.tsx
Comment thread apps/web/src/components/project/settings/ProjectSettingsTab.tsx Outdated
Comment thread apps/web/src/routes/CreateProjectPage.tsx Outdated
Comment thread apps/web/src/routes/CreateProjectPage.tsx
@Lftobs
Lftobs changed the base branch from dev to main August 4, 2026 00:32
Lftobs added 8 commits August 4, 2026 01:43
time

- Add `finished_at` column to deployments table
- Implement status-based timestamping for
  deployment completion
- Update runtime reconciliation to skip
  non-existent containers
- Fix Ko-fi popup mounting in dashboard sidebar
- Update docker-compose configuration for local
  builds
customization

- Add `project_type`, `buildCommand`, and
  `startCommand` fields to projects.
- Implement dynamic `railpack.json` generation for
  better build/runtime compatibility.
- Add `ProjectSettingsTab` to the dashboard for
  managing build settings.
- Update Caddy reverse proxy to correctly pass the
  Host header to upstream containers.
project deployments

- Update database schema to store compose-specific
  metadata
- Enhance Caddy dynamic ingress to route traffic
  to specific services
- Extend project/domain APIs to support service
  target configuration
- Refactor documentation landing page and UI
  components
@Lftobs
Lftobs force-pushed the feat/managed-db-and-compose branch from 027e32d to 5b37f3a Compare August 4, 2026 00:43
Lftobs added 3 commits August 4, 2026 02:37
deployment support

- Refactor compose service parsing to support
  long-form port syntax.
- Extract compose deployment logic into dedicated
  module.
- Add validation to block rollbacks for compose
  deployments.
- Add error handling for database provisioning
  timeouts.
- Include `yaml` package to improve parsing
  reliability.
@Lftobs
Lftobs changed the base branch from main to dev August 4, 2026 02:07
@coderabbitai coderabbitai Bot mentioned this pull request Aug 4, 2026
17 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant