Edit Business flow
CI / ci (push) Canceled after 0s

New files:

manager/src/lib/env-file.ts — a shared, surgical .env reader/updater. It only ever touches the specific keys you tell it to (never regenerates the file), so anything hand-edited into a business's .env since provisioning is preserved exactly.
manager/src/app/dashboard/[slug]/edit/ — the edit page, its form component, and its server action.

Modified:

provisioning.ts gained updateBusiness() alongside the existing provisionBusiness(), reusing the same Caddy-site-writing and container-recreation logic (refactored into a shared writeCaddySiteAndReload helper).
stacks.ts/compose-template.ts — extended to track a BUSINESS_NAME field (previously collected on the Add Business form but silently discarded — that gap is now fixed too, so newly created businesses get their name persisted from the start).
The detail page and dashboard list now show the business name, and the detail page has an Edit button.

Per the requirements:

Slug is read-only in the edit form (disabled input, never accepted by the server action — it's bound in from the route, not form data).
Domain uniqueness is validated against every other business (and correctly self-excludes the business being edited).
A domain change rewrites the Caddy site file, reloads Caddy, and force-recreates the container; a name-only change does neither.
No secrets (DATABASE_PASSWORD, AUTH_SECRET, etc.) are ever read into the edit UI — only name/domain/databaseName/status are exposed.
Clear success banners ("Changes saved" vs. "...the container was restarted...") and inline error display, matching the existing Add Business pattern.
This commit is contained in:
Stefan Lazic committed 2026-09-05 17:05:33 +01:00
1 parent c92ee46d76
commit 3b07124d7e
11 files changed
+393 -40

No files matched your search

+2 -1
View File
@@ -8,12 +8,13 @@ export interface AddBusinessState {
}
export async function addBusinessAction(_prevState: AddBusinessState, formData: FormData): Promise<AddBusinessState> {
const name = String(formData.get("name") ?? "");
const slug = String(formData.get("slug") ?? "");
const domain = String(formData.get("domain") ?? "");
let result: { slug: string };
try {
result = await provisionBusiness({ slug, domain });
result = await provisionBusiness({ name, slug, domain });
} catch (error) {
return { error: error instanceof Error ? error.message : "Failed to provision this business." };
}