feat: update presentation export version and enhance export functionality with cookie handling
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
4fd1a84f59
commit
46d2f02da4
5 changed files with 58 additions and 26 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "presenton",
|
||||
"version": "1.0.0",
|
||||
"presentationExportVersion": "v0.2.2",
|
||||
"presentationExportVersion": "v0.2.6",
|
||||
"type": "module",
|
||||
"description": "Open-source AI presentation generator",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -120,13 +120,56 @@ function chmodIfPossible(filePath) {
|
|||
}
|
||||
}
|
||||
|
||||
function getConverterCandidates() {
|
||||
function getConverterCandidates(baseDir = targetPyDir) {
|
||||
return [
|
||||
path.join(targetPyDir, "convert-linux-x64"),
|
||||
path.join(targetPyDir, "convert-linux-amd64"),
|
||||
path.join(baseDir, "convert-linux-x64"),
|
||||
path.join(baseDir, "convert-linux-amd64"),
|
||||
path.join(baseDir, "convert"),
|
||||
];
|
||||
}
|
||||
|
||||
function hasRuntimeBundle(baseDir) {
|
||||
const indexPath = path.join(baseDir, "index.js");
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pyCandidates = getConverterCandidates(path.join(baseDir, "py"));
|
||||
const rootCandidates = getConverterCandidates(baseDir);
|
||||
return [...pyCandidates, ...rootCandidates].some((candidate) =>
|
||||
fs.existsSync(candidate)
|
||||
);
|
||||
}
|
||||
|
||||
function moveFileAtomic(src, dest) {
|
||||
try {
|
||||
fs.renameSync(src, dest);
|
||||
} catch {
|
||||
fs.copyFileSync(src, dest);
|
||||
fs.rmSync(src, { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRuntimeLayout() {
|
||||
if (!fs.existsSync(targetRoot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ensureDir(targetPyDir);
|
||||
|
||||
const rootCandidates = getConverterCandidates(targetRoot);
|
||||
for (const sourcePath of rootCandidates) {
|
||||
if (!fs.existsSync(sourcePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const destinationPath = path.join(targetPyDir, path.basename(sourcePath));
|
||||
if (!fs.existsSync(destinationPath)) {
|
||||
moveFileAtomic(sourcePath, destinationPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureCommonJsEntrypoint() {
|
||||
if (!fs.existsSync(targetIndexJs)) {
|
||||
return { ok: false, reason: `Missing runtime bundle: ${targetIndexJs}` };
|
||||
|
|
@ -148,6 +191,8 @@ function ensureCommonJsEntrypoint() {
|
|||
}
|
||||
|
||||
function validateExistingRuntime() {
|
||||
normalizeRuntimeLayout();
|
||||
|
||||
const entrypoint = ensureCommonJsEntrypoint();
|
||||
if (!entrypoint.ok) {
|
||||
return { ok: false, reason: entrypoint.reason };
|
||||
|
|
@ -158,7 +203,7 @@ function validateExistingRuntime() {
|
|||
if (!converterPath) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: `No Linux converter binary under ${targetPyDir}.`,
|
||||
reason: `No Linux converter binary under ${targetPyDir} or ${targetRoot}.`,
|
||||
};
|
||||
}
|
||||
chmodIfPossible(converterPath);
|
||||
|
|
@ -210,18 +255,15 @@ function unzipArchive(zipPath, destDir) {
|
|||
}
|
||||
|
||||
function resolveExtractedRoot(extractDir) {
|
||||
const directIndex = path.join(extractDir, "index.js");
|
||||
const directPy = path.join(extractDir, "py");
|
||||
if (fs.existsSync(directIndex) && fs.existsSync(directPy)) {
|
||||
if (hasRuntimeBundle(extractDir)) {
|
||||
return extractDir;
|
||||
}
|
||||
|
||||
const children = fs.readdirSync(extractDir, { withFileTypes: true });
|
||||
for (const entry of children) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const candidate = path.join(extractDir, entry.name);
|
||||
const candidateIndex = path.join(candidate, "index.js");
|
||||
const candidatePy = path.join(candidate, "py");
|
||||
if (fs.existsSync(candidateIndex) && fs.existsSync(candidatePy)) {
|
||||
if (hasRuntimeBundle(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import re
|
||||
|
||||
from fastapi import Request
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
|
@ -25,12 +23,6 @@ class SessionAuthMiddleware(BaseHTTPMiddleware):
|
|||
_EXEMPT_PREFIXES = (
|
||||
"/api/v1/auth/",
|
||||
)
|
||||
# PPTX/PDF export loads /pdf-maker in a headless browser with no session cookie; it
|
||||
# only needs a single-deck read by id. (UUID is not a secret; this matches prior behavior
|
||||
# when auth middleware did not protect these routes during export.)
|
||||
_PRESENTATION_GET_BY_ID = re.compile(
|
||||
r"^/api/v1/ppt/presentation/[0-9a-fA-F-]{36}/?$"
|
||||
)
|
||||
_PROTECTED_NON_API_PATHS = {
|
||||
"/docs",
|
||||
"/openapi.json",
|
||||
|
|
@ -40,11 +32,6 @@ class SessionAuthMiddleware(BaseHTTPMiddleware):
|
|||
def _is_exempt(self, path: str) -> bool:
|
||||
return any(path.startswith(prefix) for prefix in self._EXEMPT_PREFIXES)
|
||||
|
||||
def _is_presentation_get_by_id(self, request: Request, path: str) -> bool:
|
||||
if request.method != "GET":
|
||||
return False
|
||||
return bool(self._PRESENTATION_GET_BY_ID.match(path))
|
||||
|
||||
def _requires_auth(self, path: str) -> bool:
|
||||
if path.startswith("/api/"):
|
||||
return True
|
||||
|
|
@ -59,7 +46,6 @@ class SessionAuthMiddleware(BaseHTTPMiddleware):
|
|||
request.method == "OPTIONS"
|
||||
or not self._requires_auth(path)
|
||||
or self._is_exempt(path)
|
||||
or self._is_presentation_get_by_id(request, path)
|
||||
):
|
||||
return await call_next(request)
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ function isValidFormat(value: unknown): value is BundledPresentationExportFormat
|
|||
|
||||
export async function POST(req: NextRequest) {
|
||||
const { format, id, title } = await req.json();
|
||||
const cookieHeader = req.headers.get("cookie") ?? "";
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json(
|
||||
|
|
@ -38,6 +39,7 @@ export async function POST(req: NextRequest) {
|
|||
format,
|
||||
presentationId: id,
|
||||
title,
|
||||
cookieHeader,
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
|
|
|
|||
|
|
@ -116,8 +116,9 @@ export async function runBundledPresentationExport(params: {
|
|||
presentationId: string;
|
||||
title: string | undefined;
|
||||
format: BundledPresentationExportFormat;
|
||||
cookieHeader?: string;
|
||||
}): Promise<BundledPresentationExportResult> {
|
||||
const { presentationId, title, format } = params;
|
||||
const { presentationId, title, format, cookieHeader } = params;
|
||||
const exportRoot = getExportPackageRoot();
|
||||
const entrypoint = await resolveExportEntrypoint(exportRoot);
|
||||
const converter = bundledConverterPath(exportRoot);
|
||||
|
|
@ -146,6 +147,7 @@ export async function runBundledPresentationExport(params: {
|
|||
format,
|
||||
title: sanitizeFilename(title ?? "presentation"),
|
||||
fastapiUrl: fastapiUrl || undefined,
|
||||
cookieHeader: cookieHeader || undefined,
|
||||
};
|
||||
|
||||
await fs.writeFile(exportTaskPath, JSON.stringify(exportTask), "utf8");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue