Phase 1 (Foundation): - Project restructure (presenton-main → backend/ + frontend/) - Database schema (8 new models, Alembic config, seed script) - Auth (Azure AD SSO + dev bypass, JWT sessions, AuthMiddleware) - RBAC (access_service, rbac_middleware, admin routers) - Audit logging (fire-and-forget, AuditMiddleware, admin router) - i18n (react-i18next with 5 namespace files) Phase 2 (Admin Panel & Client Management): - Admin panel shell (sidebar layout, role guard, 12 pages) - Redux admin slice with 18 async thunks - User management (role changes, deactivation) - Client management (CRUD, brand config, team management) - Brand config editor (colors, fonts, logos, voice rules) - Master deck upload & parser (PPTX → HTML → React pipeline) - Audit log viewer with filters and CSV/JSON export Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
54 lines
1.4 KiB
TypeScript
54 lines
1.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import path from "path";
|
|
import fs from "fs";
|
|
import crypto from "crypto";
|
|
|
|
|
|
const userDataDir = process.env.APP_DATA_DIRECTORY!;
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const formData = await request.formData();
|
|
const file = formData.get("file") as File;
|
|
|
|
if (!file) {
|
|
return NextResponse.json(
|
|
{ error: "No file provided" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const bytes = await file.arrayBuffer();
|
|
const buffer = Buffer.from(bytes);
|
|
|
|
if (!userDataDir) {
|
|
return NextResponse.json(
|
|
{ error: "User data directory not found" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
// Create uploads directory if it doesn't exist
|
|
const uploadsDir = path.join(userDataDir, "uploads");
|
|
fs.mkdirSync(uploadsDir, { recursive: true });
|
|
|
|
|
|
// Generate unique filename
|
|
const filename = `${crypto.randomBytes(16).toString("hex")}.png`;
|
|
const filePath = path.join(uploadsDir, filename);
|
|
|
|
// Write file to disk
|
|
fs.writeFileSync(filePath, buffer);
|
|
|
|
// Return the relative path that can be used in the frontend
|
|
return NextResponse.json({
|
|
success: true,
|
|
filePath: `${uploadsDir}/${filename}`
|
|
});
|
|
} catch (error) {
|
|
console.error("Error saving image:", error);
|
|
return NextResponse.json(
|
|
{ error: "Failed to save image" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|