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>
86 lines
2.4 KiB
TypeScript
86 lines
2.4 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import puppeteer from "puppeteer";
|
|
|
|
export async function GET(request: Request) {
|
|
const { searchParams } = new URL(request.url);
|
|
const groupName = searchParams.get("group");
|
|
|
|
if (!groupName) {
|
|
return NextResponse.json({ error: "Missing group name" }, { status: 400 });
|
|
}
|
|
|
|
const schemaPageUrl = `http://localhost/schema?group=${encodeURIComponent(
|
|
groupName
|
|
)}`;
|
|
|
|
let browser;
|
|
try {
|
|
browser = await puppeteer.launch({
|
|
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
|
|
headless: true,
|
|
args: [
|
|
"--no-sandbox",
|
|
"--disable-setuid-sandbox",
|
|
"--disable-dev-shm-usage",
|
|
"--disable-gpu",
|
|
"--disable-web-security",
|
|
"--disable-background-timer-throttling",
|
|
"--disable-backgrounding-occluded-windows",
|
|
"--disable-renderer-backgrounding",
|
|
"--disable-features=TranslateUI",
|
|
"--disable-ipc-flooding-protection",
|
|
],
|
|
});
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1280, height: 720 });
|
|
page.setDefaultNavigationTimeout(300000);
|
|
page.setDefaultTimeout(300000);
|
|
await page.goto(schemaPageUrl, {
|
|
waitUntil: "networkidle0",
|
|
timeout: 300000,
|
|
});
|
|
|
|
await page.waitForSelector("[data-layouts]", { timeout: 300000 });
|
|
await page.waitForSelector("[data-settings]", { timeout: 300000 });
|
|
|
|
const { dataLayouts, dataGroupSettings } = await page.$eval(
|
|
"[data-layouts]",
|
|
(el) => ({
|
|
dataLayouts: el.getAttribute("data-layouts"),
|
|
dataGroupSettings: el.getAttribute("data-settings"),
|
|
})
|
|
);
|
|
|
|
let slides, groupSettings;
|
|
try {
|
|
slides = JSON.parse(dataLayouts || "[]");
|
|
} catch (e) {
|
|
slides = [];
|
|
}
|
|
try {
|
|
groupSettings = JSON.parse(dataGroupSettings || "null");
|
|
} catch (e) {
|
|
groupSettings = null;
|
|
}
|
|
|
|
const response = {
|
|
name: groupName,
|
|
ordered: groupSettings?.ordered ?? false,
|
|
slides: slides.map((slide: any) => ({
|
|
id: slide.id,
|
|
name: slide.name,
|
|
description: slide.description,
|
|
json_schema: slide.json_schema,
|
|
})),
|
|
};
|
|
|
|
return NextResponse.json(response);
|
|
} catch (err) {
|
|
return NextResponse.json(
|
|
{ error: "Failed to fetch or parse client page" },
|
|
{ status: 500 }
|
|
);
|
|
} finally {
|
|
if (browser) await browser.close();
|
|
}
|
|
}
|