feat: customer contacts table, branded mail-from on root domain
Some checks failed
Build & Release / build (push) Has been cancelled
Some checks failed
Build & Release / build (push) Has been cancelled
- Add customer_contacts table for storing contacts per customer - Save initial user as primary contact during provisioning - Use "Store Name via LunarFront <noreply@lunarfront.tech>" as mail-from (root domain is verified with Resend, no per-customer DNS needed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,18 @@ export async function migrate() {
|
|||||||
`;
|
`;
|
||||||
await db`CREATE INDEX IF NOT EXISTS customer_size_snapshots_slug_date ON customer_size_snapshots (slug, recorded_at DESC)`;
|
await db`CREATE INDEX IF NOT EXISTS customer_size_snapshots_slug_date ON customer_size_snapshots (slug, recorded_at DESC)`;
|
||||||
await db`ALTER TABLE users ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`;
|
await db`ALTER TABLE users ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`;
|
||||||
|
await db`
|
||||||
|
CREATE TABLE IF NOT EXISTS customer_contacts (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
slug TEXT NOT NULL REFERENCES customers(slug) ON DELETE CASCADE,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
first_name TEXT NOT NULL,
|
||||||
|
last_name TEXT NOT NULL,
|
||||||
|
is_primary BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
await db`CREATE INDEX IF NOT EXISTS customer_contacts_slug ON customer_contacts (slug)`;
|
||||||
await db`
|
await db`
|
||||||
ALTER TABLE customers
|
ALTER TABLE customers
|
||||||
ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT '',
|
ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT '',
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export async function customerRoutes(app: FastifyInstance) {
|
|||||||
"spaces-prefix": `${slug}/`,
|
"spaces-prefix": `${slug}/`,
|
||||||
"encryption-key": encryptionKey,
|
"encryption-key": encryptionKey,
|
||||||
"resend-api-key": config.resendApiKey,
|
"resend-api-key": config.resendApiKey,
|
||||||
"mail-from": `noreply@${slug}.lunarfront.tech`,
|
"mail-from": `${body.name} via LunarFront <noreply@lunarfront.tech>`,
|
||||||
"business-name": body.name,
|
"business-name": body.name,
|
||||||
...(body.initialUser ? {
|
...(body.initialUser ? {
|
||||||
"initial-user-email": body.initialUser.email,
|
"initial-user-email": body.initialUser.email,
|
||||||
@@ -159,6 +159,14 @@ export async function customerRoutes(app: FastifyInstance) {
|
|||||||
await setStep("chart", "done");
|
await setStep("chart", "done");
|
||||||
|
|
||||||
await db`UPDATE customers SET status = 'provisioned', updated_at = NOW() WHERE slug = ${slug}`;
|
await db`UPDATE customers SET status = 'provisioned', updated_at = NOW() WHERE slug = ${slug}`;
|
||||||
|
|
||||||
|
// Save initial user as primary contact
|
||||||
|
if (body.initialUser) {
|
||||||
|
await db`
|
||||||
|
INSERT INTO customer_contacts (slug, email, first_name, last_name, is_primary)
|
||||||
|
VALUES (${slug}, ${body.initialUser.email}, ${body.initialUser.firstName}, ${body.initialUser.lastName}, true)
|
||||||
|
`;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const failedStep = Object.entries(steps).find(([, v]) => v === "pending")?.[0];
|
const failedStep = Object.entries(steps).find(([, v]) => v === "pending")?.[0];
|
||||||
if (failedStep) await setStep(failedStep, "failed");
|
if (failedStep) await setStep(failedStep, "failed");
|
||||||
|
|||||||
Reference in New Issue
Block a user