"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { getDb, users, organizations, eq } from "@fetcha/db"; import { getWorkspace, requestMeta, requireUser } from "@/lib/session"; import { writeAudit } from "@/lib/audit"; import type { ActionResult } from "./projects"; export async function completeOnboarding(): Promise { const user = await requireUser(); await getDb().update(users).set({ onboardingCompletedAt: new Date(), updatedAt: new Date() }).where(eq(users.id, user.id)); revalidatePath("/dashboard", "layout"); return { ok: true }; } const orgSchema = z.object({ name: z.string().trim().min(2).max(80), softLimitUsd: z.coerce.number().min(0).optional().or(z.literal("")), hardLimitUsd: z.coerce.number().min(0).optional().or(z.literal("")), }); export async function updateOrganization(formData: FormData): Promise { const ws = await getWorkspace(); if (!["owner", "admin", "billing"].includes(ws.role)) return { ok: false, error: "Only owners and admins can change organization settings." }; const parsed = orgSchema.safeParse(Object.fromEntries(formData)); if (!parsed.success) return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid input" }; const d = parsed.data; const soft = d.softLimitUsd === "" || d.softLimitUsd === undefined ? null : d.softLimitUsd; const hard = d.hardLimitUsd === "" || d.hardLimitUsd === undefined ? null : d.hardLimitUsd; if (soft !== null && hard !== null && soft > hard) return { ok: false, error: "The soft limit must be lower than the hard limit." }; await getDb().update(organizations).set({ name: d.name, softLimitUsd: soft, hardLimitUsd: hard, updatedAt: new Date() }).where(eq(organizations.id, ws.organization.id)); const meta = await requestMeta(); await writeAudit({ userId: ws.user.id, organizationId: ws.organization.id, action: "limits.updated", metadata: { soft, hard }, ipAddress: meta.ip, userAgent: meta.userAgent }); revalidatePath("/dashboard", "layout"); return { ok: true }; } export async function updateProfileName(name: string): Promise { const user = await requireUser(); const clean = name.trim().slice(0, 80); if (clean.length < 1) return { ok: false, error: "Name cannot be empty" }; await getDb().update(users).set({ name: clean, updatedAt: new Date() }).where(eq(users.id, user.id)); revalidatePath("/dashboard", "layout"); return { ok: true }; } export async function recordAuditFromClient(action: "password.changed" | "email.changed" | "session.revoked" | "logout", metadata?: Record): Promise { const user = await requireUser(); const meta = await requestMeta(); await writeAudit({ userId: user.id, action, metadata, ipAddress: meta.ip, userAgent: meta.userAgent }); }