afterbefore 0.2.15 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -283,24 +283,48 @@ async function handlePush(req) {
283
283
  // src/server/open.ts
284
284
  import { NextResponse as NextResponse4 } from "next/server";
285
285
  import { execFile as execFile4 } from "child_process";
286
- async function handleOpen(_req) {
287
- const desktop = await getSaveDir();
286
+ import { mkdir as mkdir4 } from "fs/promises";
287
+ import { join as join4 } from "path";
288
+ import { homedir as homedir3 } from "os";
289
+ function isSafeName(name) {
290
+ return /^[a-zA-Z0-9._\-]+$/.test(name);
291
+ }
292
+ function isSafeBranchName(name) {
293
+ return /^[a-zA-Z0-9._\-/]+$/.test(name) && !name.includes("..") && !name.startsWith("/") && !name.endsWith("/");
294
+ }
295
+ async function handleOpen(req) {
296
+ let body = null;
297
+ try {
298
+ body = await req.json();
299
+ } catch {
300
+ body = null;
301
+ }
302
+ let dir;
303
+ if (body?.repo || body?.branch) {
304
+ if (typeof body.repo !== "string" || typeof body.branch !== "string" || !isSafeName(body.repo) || !isSafeBranchName(body.branch)) {
305
+ return NextResponse4.json({ error: "Invalid parameter" }, { status: 400 });
306
+ }
307
+ dir = join4(homedir3(), ".afterbefore", body.repo, body.branch);
308
+ await mkdir4(dir, { recursive: true });
309
+ } else {
310
+ dir = await getSaveDir();
311
+ }
288
312
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
289
- execFile4(cmd, [desktop]);
313
+ execFile4(cmd, [dir]);
290
314
  return NextResponse4.json({ success: true });
291
315
  }
292
316
 
293
317
  // src/server/history.ts
294
318
  import { NextResponse as NextResponse5 } from "next/server";
295
- import { readdir, readFile as readFile2 } from "fs/promises";
296
- import { join as join4, resolve } from "path";
297
- import { homedir as homedir3 } from "os";
298
- var ARCHIVE_DIR = join4(homedir3(), ".afterbefore");
319
+ import { access as access2, readdir, readFile as readFile2, rename, unlink } from "fs/promises";
320
+ import { join as join5, resolve } from "path";
321
+ import { homedir as homedir4 } from "os";
322
+ var ARCHIVE_DIR = join5(homedir4(), ".afterbefore");
299
323
  var MAX_SCREENSHOTS = 50;
300
- function isSafeName(name) {
324
+ function isSafeName2(name) {
301
325
  return /^[a-zA-Z0-9._\-]+$/.test(name);
302
326
  }
303
- function isSafeBranchName(name) {
327
+ function isSafeBranchName2(name) {
304
328
  return /^[a-zA-Z0-9._\-/]+$/.test(name) && !name.includes("..") && !name.startsWith("/") && !name.endsWith("/");
305
329
  }
306
330
  async function listDirs(dir) {
@@ -322,7 +346,7 @@ async function listBranches(repoDir) {
322
346
  }
323
347
  for (const entry of entries) {
324
348
  if (entry.isDirectory()) {
325
- await walk(join4(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
349
+ await walk(join5(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
326
350
  }
327
351
  }
328
352
  } catch {
@@ -354,11 +378,11 @@ async function handleHistoryList(req) {
354
378
  let branches = [];
355
379
  let screenshots = [];
356
380
  const selectedRepo = repoParam || currentRepo;
357
- if (selectedRepo && isSafeName(selectedRepo)) {
358
- branches = await listBranches(join4(ARCHIVE_DIR, selectedRepo));
381
+ if (selectedRepo && isSafeName2(selectedRepo)) {
382
+ branches = await listBranches(join5(ARCHIVE_DIR, selectedRepo));
359
383
  const selectedBranch = branchParam || currentBranch;
360
- if (selectedBranch && isSafeBranchName(selectedBranch)) {
361
- screenshots = await listPngs(join4(ARCHIVE_DIR, selectedRepo, selectedBranch));
384
+ if (selectedBranch && isSafeBranchName2(selectedBranch)) {
385
+ screenshots = await listPngs(join5(ARCHIVE_DIR, selectedRepo, selectedBranch));
362
386
  }
363
387
  }
364
388
  return NextResponse5.json({
@@ -369,6 +393,66 @@ async function handleHistoryList(req) {
369
393
  screenshots
370
394
  });
371
395
  }
396
+ async function handleHistoryRename(req) {
397
+ let body;
398
+ try {
399
+ body = await req.json();
400
+ } catch {
401
+ return NextResponse5.json({ error: "Invalid JSON" }, { status: 400 });
402
+ }
403
+ const { repo, branch, oldName, newName } = body;
404
+ if (!repo || !branch || !oldName || !newName) {
405
+ return NextResponse5.json({ error: "Missing fields" }, { status: 400 });
406
+ }
407
+ const safeName = newName.endsWith(".png") ? newName : `${newName}.png`;
408
+ if (!isSafeName2(repo) || !isSafeBranchName2(branch) || !isSafeName2(oldName) || !isSafeName2(safeName)) {
409
+ return NextResponse5.json({ error: "Invalid parameter" }, { status: 400 });
410
+ }
411
+ const dir = resolve(ARCHIVE_DIR, repo, branch);
412
+ const oldPath = join5(dir, oldName);
413
+ const newPath = join5(dir, safeName);
414
+ if (!oldPath.startsWith(ARCHIVE_DIR) || !newPath.startsWith(ARCHIVE_DIR)) {
415
+ return NextResponse5.json({ error: "Invalid parameter" }, { status: 400 });
416
+ }
417
+ if (safeName !== oldName) {
418
+ try {
419
+ await access2(newPath);
420
+ return NextResponse5.json({ error: "File already exists" }, { status: 409 });
421
+ } catch {
422
+ }
423
+ }
424
+ try {
425
+ await rename(oldPath, newPath);
426
+ return NextResponse5.json({ success: true, filename: safeName });
427
+ } catch {
428
+ return NextResponse5.json({ error: "Rename failed" }, { status: 500 });
429
+ }
430
+ }
431
+ async function handleHistoryDelete(req) {
432
+ let body;
433
+ try {
434
+ body = await req.json();
435
+ } catch {
436
+ return NextResponse5.json({ error: "Invalid JSON" }, { status: 400 });
437
+ }
438
+ const { repo, branch, file } = body;
439
+ if (!repo || !branch || !file) {
440
+ return NextResponse5.json({ error: "Missing fields" }, { status: 400 });
441
+ }
442
+ if (!isSafeName2(repo) || !isSafeBranchName2(branch) || !isSafeName2(file)) {
443
+ return NextResponse5.json({ error: "Invalid parameter" }, { status: 400 });
444
+ }
445
+ const filepath = resolve(ARCHIVE_DIR, repo, branch, file);
446
+ if (!filepath.startsWith(ARCHIVE_DIR)) {
447
+ return NextResponse5.json({ error: "Invalid parameter" }, { status: 400 });
448
+ }
449
+ try {
450
+ await unlink(filepath);
451
+ return NextResponse5.json({ success: true });
452
+ } catch {
453
+ return NextResponse5.json({ error: "Delete failed" }, { status: 500 });
454
+ }
455
+ }
372
456
  async function handleHistoryImage(req) {
373
457
  const url = req.nextUrl;
374
458
  const repo = url.searchParams.get("repo");
@@ -377,7 +461,7 @@ async function handleHistoryImage(req) {
377
461
  if (!repo || !branch || !file) {
378
462
  return NextResponse5.json({ error: "Missing repo, branch, or file param" }, { status: 400 });
379
463
  }
380
- if (!isSafeName(repo) || !isSafeBranchName(branch) || !isSafeName(file)) {
464
+ if (!isSafeName2(repo) || !isSafeBranchName2(branch) || !isSafeName2(file)) {
381
465
  return NextResponse5.json({ error: "Invalid parameter" }, { status: 400 });
382
466
  }
383
467
  const filepath = resolve(ARCHIVE_DIR, repo, branch, file);
@@ -405,6 +489,8 @@ export {
405
489
  handlePush,
406
490
  handleOpen,
407
491
  handleHistoryList,
492
+ handleHistoryRename,
493
+ handleHistoryDelete,
408
494
  handleHistoryImage
409
495
  };
410
- //# sourceMappingURL=chunk-XSDO6N5Q.js.map
496
+ //# sourceMappingURL=chunk-CC6QEACR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/config.ts","../src/server/save.ts","../src/server/git.ts","../src/server/push.ts","../src/server/open.ts","../src/server/history.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\ninterface Config {\n saveDir: string;\n}\n\nconst CONFIG_DIR = join(process.cwd(), \".afterbefore\");\nconst CONFIG_PATH = join(CONFIG_DIR, \"config.json\");\nconst DEFAULT_SAVE_DIR = join(homedir(), \"Desktop\");\n\nasync function readConfig(): Promise<Config> {\n try {\n const raw = await readFile(CONFIG_PATH, \"utf-8\");\n const parsed = JSON.parse(raw);\n return { saveDir: parsed.saveDir || DEFAULT_SAVE_DIR };\n } catch {\n return { saveDir: DEFAULT_SAVE_DIR };\n }\n}\n\nasync function writeConfig(config: Config): Promise<void> {\n await mkdir(CONFIG_DIR, { recursive: true });\n await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nexport async function getSaveDir(): Promise<string> {\n const config = await readConfig();\n return config.saveDir;\n}\n\nexport async function handleGetConfig(): Promise<NextResponse> {\n const config = await readConfig();\n return NextResponse.json(config);\n}\n\nexport async function handleSetConfig(req: NextRequest): Promise<NextResponse> {\n let body: Partial<Config>;\n try {\n body = await req.json();\n } catch {\n return NextResponse.json({ error: \"Invalid JSON\" }, { status: 400 });\n }\n\n if (typeof body.saveDir !== \"string\" || body.saveDir.trim() === \"\") {\n return NextResponse.json({ error: \"Invalid saveDir\" }, { status: 400 });\n }\n\n const config: Config = { saveDir: body.saveDir.trim() };\n await writeConfig(config);\n return NextResponse.json(config);\n}\n\nexport async function handlePickFolder(): Promise<NextResponse> {\n if (process.platform !== \"darwin\") {\n return NextResponse.json(\n { error: \"Folder picker is only supported on macOS\" },\n { status: 501 },\n );\n }\n\n try {\n const { stdout } = await execFileAsync(\"osascript\", [\n \"-e\",\n 'POSIX path of (choose folder with prompt \"Select screenshot save location\")',\n ]);\n const folder = stdout.trim().replace(/\\/$/, \"\");\n if (!folder) {\n return NextResponse.json({ cancelled: true });\n }\n return NextResponse.json({ folder });\n } catch {\n // User cancelled the dialog\n return NextResponse.json({ cancelled: true });\n }\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getSaveDir } from \"./config\";\nimport { getBranch, getRepoName } from \"./git\";\n\nasync function saveGlobalCopy(buffer: Buffer, repoName: string, branch: string): Promise<void> {\n const globalDir = join(homedir(), \".afterbefore\", repoName, branch);\n await mkdir(globalDir, { recursive: true });\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const filepath = join(globalDir, `${timestamp}.png`);\n await writeFile(filepath, buffer);\n}\n\nconst VALID_MODES = [\"fullpage\", \"viewport\", \"component\"] as const;\n\nconst DATA_URL_PREFIX = \"data:image/png;base64,\";\n\ninterface SaveRequestBody {\n mode: string;\n image: string;\n}\n\nexport async function handleSave(req: NextRequest): Promise<NextResponse> {\n let body: SaveRequestBody;\n try {\n body = await req.json();\n } catch {\n return NextResponse.json(\n { error: \"Invalid JSON body\" },\n { status: 400 },\n );\n }\n\n const { mode, image } = body;\n\n if (!VALID_MODES.includes(mode as (typeof VALID_MODES)[number])) {\n return NextResponse.json(\n { error: `Invalid mode: must be \"fullpage\", \"viewport\", or \"component\"` },\n { status: 400 },\n );\n }\n\n if (typeof image !== \"string\" || !image.startsWith(DATA_URL_PREFIX)) {\n return NextResponse.json(\n { error: \"Invalid image: must be a data:image/png;base64 data URL\" },\n { status: 400 },\n );\n }\n\n const base64 = image.slice(DATA_URL_PREFIX.length);\n const buffer = Buffer.from(base64, \"base64\");\n\n const [branch, repoName, saveDir] = await Promise.all([\n getBranch(),\n getRepoName(),\n getSaveDir(),\n ]);\n const filename = `${branch}.png`;\n const filepath = join(saveDir, filename);\n\n try {\n await writeFile(filepath, buffer);\n } catch (err) {\n return NextResponse.json(\n { error: \"Failed to write screenshot\", detail: String(err) },\n { status: 500 },\n );\n }\n\n // Always keep a copy in the global ~/.afterbefore/<repo>/<branch>/ archive\n try {\n await saveGlobalCopy(buffer, repoName, branch);\n } catch {\n // Non-fatal: don't fail the save if the global copy fails\n }\n\n return NextResponse.json({ success: true, path: filepath });\n}\n","import { basename } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nexport async function getBranch(): Promise<string> {\n const { stdout } = await execFileAsync(\"git\", [\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], {\n cwd: process.cwd(),\n });\n return stdout.trim();\n}\n\nexport async function getRepoName(): Promise<string> {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n cwd: process.cwd(),\n });\n return basename(stdout.trim());\n } catch {\n return basename(process.cwd());\n }\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { execFile } from \"node:child_process\";\nimport { access, mkdir, copyFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { getSaveDir } from \"./config\";\n\nconst execFileAsync = promisify(execFile);\n\ninterface PrInfo {\n number: number;\n url: string;\n headRepository: { owner: { login: string }; name: string };\n headRefName: string;\n}\n\nasync function run(\n cmd: string,\n args: string[],\n): Promise<{ stdout: string; stderr: string }> {\n return execFileAsync(cmd, args, { cwd: process.cwd() });\n}\n\nasync function ghAvailable(): Promise<boolean> {\n try {\n await run(\"gh\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function getPrInfo(): Promise<PrInfo | null> {\n try {\n const { stdout } = await run(\"gh\", [\n \"pr\",\n \"view\",\n \"--json\",\n \"number,url,headRepository,headRefName\",\n ]);\n return JSON.parse(stdout) as PrInfo;\n } catch {\n return null;\n }\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function handlePush(req: NextRequest): Promise<NextResponse> {\n // 1. Check gh CLI availability\n if (!(await ghAvailable())) {\n return NextResponse.json(\n { success: false, error: \"GitHub CLI (gh) is not installed or not in PATH\" },\n { status: 500 },\n );\n }\n\n // 2. Check for active PR\n const pr = await getPrInfo();\n if (!pr) {\n return NextResponse.json(\n { success: false, error: \"No PR found for current branch\" },\n { status: 404 },\n );\n }\n\n // 3. Check that screenshot file exists in save directory\n const saveDir = await getSaveDir();\n const branch = pr.headRefName;\n const screenshotPath = join(saveDir, `${branch}.png`);\n\n if (!(await fileExists(screenshotPath))) {\n return NextResponse.json(\n { success: false, error: `Missing screenshot: ${branch}.png` },\n { status: 400 },\n );\n }\n\n // 4. Copy into repo, stage, commit, and push\n const repoDir = join(process.cwd(), \".afterbefore\");\n const repoFile = join(repoDir, `${branch}.png`);\n\n try {\n await mkdir(repoDir, { recursive: true });\n await copyFile(screenshotPath, repoFile);\n } catch (err) {\n return NextResponse.json(\n { success: false, error: \"Failed to copy screenshot into repo\", detail: String(err) },\n { status: 500 },\n );\n }\n\n try {\n await run(\"git\", [\"add\", repoFile]);\n await run(\"git\", [\n \"commit\",\n \"-m\",\n \"chore: add screenshot\",\n ]);\n } catch (err) {\n const msg = String(err);\n if (!msg.includes(\"nothing to commit\")) {\n return NextResponse.json(\n { success: false, error: \"Failed to commit screenshot\", detail: msg },\n { status: 500 },\n );\n }\n }\n\n try {\n await run(\"git\", [\"push\"]);\n } catch (err) {\n return NextResponse.json(\n { success: false, error: \"Failed to push to remote\", detail: String(err) },\n { status: 500 },\n );\n }\n\n // 5. Build raw GitHub URL for the image\n const owner = pr.headRepository.owner.login;\n const repo = pr.headRepository.name;\n const rawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;\n const imageUrl = `${rawBase}/.afterbefore/${branch}.png`;\n\n // Cache-bust with timestamp so GitHub doesn't serve stale images\n const ts = Date.now();\n const commentBody = `## Screenshot\\n\\n![screenshot](${imageUrl}?t=${ts})`;\n\n // 6. Post PR comment\n let commentUrl: string | undefined;\n try {\n const { stdout } = await run(\"gh\", [\n \"pr\",\n \"comment\",\n String(pr.number),\n \"--body\",\n commentBody,\n ]);\n commentUrl = stdout.trim() || undefined;\n } catch (err) {\n return NextResponse.json(\n { success: false, error: \"Failed to post PR comment\", detail: String(err) },\n { status: 500 },\n );\n }\n\n return NextResponse.json({\n success: true,\n pr: pr.number,\n prUrl: pr.url,\n commentUrl,\n });\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { execFile } from \"node:child_process\";\nimport { mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getSaveDir } from \"./config\";\n\ninterface OpenRequestBody {\n repo?: string;\n branch?: string;\n}\n\nfunction isSafeName(name: string): boolean {\n return /^[a-zA-Z0-9._\\-]+$/.test(name);\n}\n\nfunction isSafeBranchName(name: string): boolean {\n return /^[a-zA-Z0-9._\\-/]+$/.test(name) && !name.includes(\"..\") && !name.startsWith(\"/\") && !name.endsWith(\"/\");\n}\n\nexport async function handleOpen(req: NextRequest): Promise<NextResponse> {\n let body: OpenRequestBody | null = null;\n try {\n body = (await req.json()) as OpenRequestBody;\n } catch {\n body = null;\n }\n\n let dir: string;\n if (body?.repo || body?.branch) {\n if (\n typeof body.repo !== \"string\" ||\n typeof body.branch !== \"string\" ||\n !isSafeName(body.repo) ||\n !isSafeBranchName(body.branch)\n ) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n dir = join(homedir(), \".afterbefore\", body.repo, body.branch);\n await mkdir(dir, { recursive: true });\n } else {\n dir = await getSaveDir();\n }\n\n const cmd =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"explorer\"\n : \"xdg-open\";\n\n execFile(cmd, [dir]);\n\n return NextResponse.json({ success: true });\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport { access, readdir, readFile, rename, unlink } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { getBranch, getRepoName } from \"./git\";\n\nconst ARCHIVE_DIR = join(homedir(), \".afterbefore\");\nconst MAX_SCREENSHOTS = 50;\n\nfunction isSafeName(name: string): boolean {\n return /^[a-zA-Z0-9._\\-]+$/.test(name);\n}\n\n/** Branch names can contain slashes (e.g. \"fix/something\"). Reject \"..\" and leading/trailing slashes. */\nfunction isSafeBranchName(name: string): boolean {\n return /^[a-zA-Z0-9._\\-/]+$/.test(name) && !name.includes(\"..\") && !name.startsWith(\"/\") && !name.endsWith(\"/\");\n}\n\nasync function listDirs(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();\n } catch {\n return [];\n }\n}\n\n/**\n * Recursively find branch paths under a repo directory.\n * A branch path is any relative path that contains .png files.\n * e.g. \"main\", \"fix/capture-mode-toolbar\"\n */\nasync function listBranches(repoDir: string): Promise<string[]> {\n const branches: string[] = [];\n\n async function walk(dir: string, prefix: string) {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n const hasPngs = entries.some((e) => e.isFile() && e.name.endsWith(\".png\"));\n if (hasPngs && prefix) {\n branches.push(prefix);\n }\n for (const entry of entries) {\n if (entry.isDirectory()) {\n await walk(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);\n }\n }\n } catch {\n // directory doesn't exist or can't be read\n }\n }\n\n await walk(repoDir, \"\");\n return branches.sort();\n}\n\nasync function listPngs(dir: string): Promise<{ filename: string; timestamp: string }[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries\n .filter((e) => e.isFile() && e.name.endsWith(\".png\"))\n .map((e) => ({\n filename: e.name,\n timestamp: e.name.replace(/\\.png$/, \"\"),\n }))\n .sort((a, b) => b.filename.localeCompare(a.filename))\n .slice(0, MAX_SCREENSHOTS);\n } catch {\n return [];\n }\n}\n\nexport async function handleHistoryList(req: NextRequest): Promise<NextResponse> {\n const url = req.nextUrl;\n const repoParam = url.searchParams.get(\"repo\");\n const branchParam = url.searchParams.get(\"branch\");\n\n const [currentRepo, currentBranch] = await Promise.all([\n getRepoName(),\n getBranch(),\n ]);\n\n const repos = await listDirs(ARCHIVE_DIR);\n\n let branches: string[] = [];\n let screenshots: { filename: string; timestamp: string }[] = [];\n\n const selectedRepo = repoParam || currentRepo;\n if (selectedRepo && isSafeName(selectedRepo)) {\n branches = await listBranches(join(ARCHIVE_DIR, selectedRepo));\n\n const selectedBranch = branchParam || currentBranch;\n if (selectedBranch && isSafeBranchName(selectedBranch)) {\n screenshots = await listPngs(join(ARCHIVE_DIR, selectedRepo, selectedBranch));\n }\n }\n\n return NextResponse.json({\n repos,\n currentRepo,\n branches,\n currentBranch,\n screenshots,\n });\n}\n\nexport async function handleHistoryRename(req: NextRequest): Promise<NextResponse> {\n let body: { repo: string; branch: string; oldName: string; newName: string };\n try {\n body = await req.json();\n } catch {\n return NextResponse.json({ error: \"Invalid JSON\" }, { status: 400 });\n }\n\n const { repo, branch, oldName, newName } = body;\n if (!repo || !branch || !oldName || !newName) {\n return NextResponse.json({ error: \"Missing fields\" }, { status: 400 });\n }\n\n // Ensure newName ends with .png\n const safeName = newName.endsWith(\".png\") ? newName : `${newName}.png`;\n\n if (!isSafeName(repo) || !isSafeBranchName(branch) || !isSafeName(oldName) || !isSafeName(safeName)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n const dir = resolve(ARCHIVE_DIR, repo, branch);\n const oldPath = join(dir, oldName);\n const newPath = join(dir, safeName);\n\n if (!oldPath.startsWith(ARCHIVE_DIR) || !newPath.startsWith(ARCHIVE_DIR)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n if (safeName !== oldName) {\n try {\n await access(newPath);\n return NextResponse.json({ error: \"File already exists\" }, { status: 409 });\n } catch {\n // Target does not exist, continue.\n }\n }\n\n try {\n await rename(oldPath, newPath);\n return NextResponse.json({ success: true, filename: safeName });\n } catch {\n return NextResponse.json({ error: \"Rename failed\" }, { status: 500 });\n }\n}\n\nexport async function handleHistoryDelete(req: NextRequest): Promise<NextResponse> {\n let body: { repo: string; branch: string; file: string };\n try {\n body = await req.json();\n } catch {\n return NextResponse.json({ error: \"Invalid JSON\" }, { status: 400 });\n }\n\n const { repo, branch, file } = body;\n if (!repo || !branch || !file) {\n return NextResponse.json({ error: \"Missing fields\" }, { status: 400 });\n }\n\n if (!isSafeName(repo) || !isSafeBranchName(branch) || !isSafeName(file)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n const filepath = resolve(ARCHIVE_DIR, repo, branch, file);\n if (!filepath.startsWith(ARCHIVE_DIR)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n try {\n await unlink(filepath);\n return NextResponse.json({ success: true });\n } catch {\n return NextResponse.json({ error: \"Delete failed\" }, { status: 500 });\n }\n}\n\nexport async function handleHistoryImage(req: NextRequest): Promise<NextResponse> {\n const url = req.nextUrl;\n const repo = url.searchParams.get(\"repo\");\n const branch = url.searchParams.get(\"branch\");\n const file = url.searchParams.get(\"file\");\n\n if (!repo || !branch || !file) {\n return NextResponse.json({ error: \"Missing repo, branch, or file param\" }, { status: 400 });\n }\n\n if (!isSafeName(repo) || !isSafeBranchName(branch) || !isSafeName(file)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n // Extra safety: ensure resolved path stays within the archive directory\n const filepath = resolve(ARCHIVE_DIR, repo, branch, file);\n if (!filepath.startsWith(ARCHIVE_DIR)) {\n return NextResponse.json({ error: \"Invalid parameter\" }, { status: 400 });\n }\n\n try {\n const buffer = await readFile(filepath);\n return new NextResponse(buffer, {\n headers: {\n \"Content-Type\": \"image/png\",\n \"Cache-Control\": \"private, max-age=3600\",\n },\n });\n } catch {\n return NextResponse.json({ error: \"Not found\" }, { status: 404 });\n }\n}\n"],"mappings":"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","names":["NextResponse","writeFile","mkdir","join","homedir","execFile","promisify","execFileAsync","join","homedir","mkdir","writeFile","NextResponse","NextResponse","execFile","mkdir","join","promisify","execFileAsync","promisify","execFile","NextResponse","join","mkdir","NextResponse","execFile","mkdir","join","homedir","NextResponse","join","homedir","mkdir","execFile","NextResponse","access","readFile","join","homedir","join","homedir","isSafeName","isSafeBranchName","NextResponse","access","readFile"]}