All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m57s
136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
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 });
|
|
}
|
|
};
|