import type { APIRoute } from "astro"; import { promises as fs } from "fs"; import path from "path"; import { db } from "../../../db"; import { organizations, members } from "../../../db/schema"; import { eq, and } from "drizzle-orm"; import { MAX_LENGTHS, exceedsLength } from "../../../lib/validation"; export const POST: APIRoute = async ({ request, locals, redirect }) => { const user = locals.user; if (!user) { return redirect("/login"); } const formData = await request.formData(); const organizationId = formData.get("organizationId") as string; const name = formData.get("name") as string; const street = formData.get("street") as string | null; const city = formData.get("city") as string | null; const state = formData.get("state") as string | null; const zip = formData.get("zip") as string | null; const country = formData.get("country") as string | null; const defaultTaxRate = formData.get("defaultTaxRate") as string | null; const defaultCurrency = formData.get("defaultCurrency") as string | null; const logo = formData.get("logo") as File | null; if (!organizationId || !name || name.trim().length === 0) { return new Response("Organization ID and name are required", { status: 400, }); } const lengthError = exceedsLength("Name", name, MAX_LENGTHS.name) || exceedsLength("Street", street, MAX_LENGTHS.address) || exceedsLength("City", city, MAX_LENGTHS.address) || exceedsLength("State", state, MAX_LENGTHS.address) || exceedsLength("ZIP", zip, MAX_LENGTHS.address) || exceedsLength("Country", country, MAX_LENGTHS.address) || exceedsLength("Currency", defaultCurrency, MAX_LENGTHS.currency); if (lengthError) { return new Response(lengthError, { status: 400 }); } try { // Verify user is admin/owner of this organization const membership = await db .select() .from(members) .where( and( eq(members.userId, user.id), eq(members.organizationId, organizationId), ), ) .get(); if (!membership) { return new Response("Not authorized", { status: 403 }); } const isAdmin = membership.role === "owner" || membership.role === "admin"; if (!isAdmin) { return new Response( "Only owners and admins can update organization settings", { status: 403 }, ); } let logoUrl: string | undefined; if (logo && logo.size > 0) { const allowedTypes = ["image/png", "image/jpeg"]; if (!allowedTypes.includes(logo.type)) { return new Response( "Invalid file type. Only PNG and JPG are allowed.", { status: 400, }, ); } const rawExt = (logo.name.split(".").pop() || "png").toLowerCase().replace(/[^a-z]/g, ""); const allowedExtensions = ["png", "jpg", "jpeg"]; const ext = allowedExtensions.includes(rawExt) ? rawExt : "png"; const filename = `${organizationId}-${Date.now()}.${ext}`; const dataDir = process.env.DATA_DIR ? process.env.DATA_DIR : import.meta.env.DATA_DIR; if (!dataDir) { throw new Error("DATA_DIR environment variable is not set"); } const uploadDir = path.join(dataDir, "uploads"); try { await fs.access(uploadDir); } catch { await fs.mkdir(uploadDir, { recursive: true }); } const buffer = Buffer.from(await logo.arrayBuffer()); await fs.writeFile(path.join(uploadDir, filename), buffer); logoUrl = `/uploads/${filename}`; } // Update organization information const updateData: any = { name: name.trim(), street: street?.trim() || null, city: city?.trim() || null, state: state?.trim() || null, zip: zip?.trim() || null, country: country?.trim() || null, defaultTaxRate: defaultTaxRate ? parseFloat(defaultTaxRate) : 0, defaultCurrency: defaultCurrency || "USD", }; if (logoUrl) { updateData.logoUrl = logoUrl; } await db .update(organizations) .set(updateData) .where(eq(organizations.id, organizationId)) .run(); return redirect("/dashboard/team/settings?success=org-name"); } catch (error) { console.error("Error updating organization:", error); return new Response("Failed to update organization", { status: 500 }); } };