Explorar o código

fix: prevent book create and import from silently overwriting data

book create: check if books/{bookId}/ already exists before creating.
import chapters: check existing chapter count, require --resume-from
if chapters already exist.
Ma hai 7 meses
pai
achega
b35dcf9fae
Modificáronse 2 ficheiros con 21 adicións e 1 borrados
  1. 11 0
      packages/cli/src/commands/book.ts
  2. 10 1
      packages/cli/src/commands/import.ts

+ 11 - 0
packages/cli/src/commands/book.ts

@@ -1,4 +1,6 @@
 import { Command } from "commander";
+import { access } from "node:fs/promises";
+import { join } from "node:path";
 import { PipelineRunner, StateManager, type BookConfig } from "@actalk/inkos-core";
 import { loadConfig, buildPipelineConfig, findProjectRoot, resolveContext, resolveBookId, log, logError } from "../utils.js";
 
@@ -40,6 +42,15 @@ bookCommand
         updatedAt: now,
       };
 
+      const bookDir = join(root, "books", bookId);
+      try {
+        await access(bookDir);
+        throw new Error(`Book "${bookId}" already exists at books/${bookId}/. Use a different title or delete the existing book first.`);
+      } catch (e) {
+        if (e instanceof Error && e.message.includes("already exists")) throw e;
+        // Directory doesn't exist, good
+      }
+
       if (!opts.json) log(`Creating book "${book.title}" (${book.genre} / ${book.platform})...`);
 
       const context = await resolveContext(opts);

+ 10 - 1
packages/cli/src/commands/import.ts

@@ -1,5 +1,5 @@
 import { Command } from "commander";
-import { PipelineRunner, splitChapters } from "@actalk/inkos-core";
+import { PipelineRunner, StateManager, splitChapters } from "@actalk/inkos-core";
 import { readFile, readdir, stat } from "node:fs/promises";
 import { join, resolve } from "node:path";
 import { loadConfig, buildPipelineConfig, findProjectRoot, resolveBookId, log, logError } from "../utils.js";
@@ -59,6 +59,15 @@ importCommand
       const bookId = await resolveBookId(bookIdArg, root);
       const config = await loadConfig();
 
+      const state = new StateManager(root);
+      const existingChapterCount = (await state.getNextChapterNumber(bookId)) - 1;
+      if (existingChapterCount > 0 && !opts.resumeFrom) {
+        throw new Error(
+          `Book "${bookId}" already has ${existingChapterCount} chapter(s). ` +
+          `Use --resume-from <n> to append, or delete existing chapters first.`
+        );
+      }
+
       const fromPath = resolve(opts.from);
       const fromStat = await stat(fromPath);