@musnows/scriverse 0.3.7 → 0.3.9
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.
- package/dist/ai.js +59 -0
- package/dist/ai.js.map +1 -1
- package/dist/app.js +253 -100
- package/dist/app.js.map +1 -1
- package/dist/cli-contract.js +4 -4
- package/dist/cli-contract.js.map +1 -1
- package/dist/cli-core.js +37 -6
- package/dist/cli-core.js.map +1 -1
- package/dist/database.js +41 -0
- package/dist/database.js.map +1 -1
- package/dist/http-logging.js +2 -2
- package/dist/http-logging.js.map +1 -1
- package/dist/logger.js +2 -2
- package/dist/logger.js.map +1 -1
- package/dist/pagination.js +42 -0
- package/dist/pagination.js.map +1 -0
- package/dist/public/app.js +483 -115
- package/dist/public/index.html +61 -9
- package/dist/public/markdown.js +35 -1
- package/dist/public/page-route.js +12 -0
- package/dist/public/styles.css +98 -22
- package/dist/security.js +41 -4
- package/dist/security.js.map +1 -1
- package/dist/store.js +720 -109
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +75 -11
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
package/dist/app.js
CHANGED
|
@@ -16,6 +16,7 @@ import { TASK_TYPES } from "./domain.js";
|
|
|
16
16
|
import { AppError } from "./errors.js";
|
|
17
17
|
import { applyImportFileHints, parseNovelText } from "./parser.js";
|
|
18
18
|
import { Store, versionedEntityTypes } from "./store.js";
|
|
19
|
+
import { parsePagination } from "./pagination.js";
|
|
19
20
|
import { normalizeUploadFileName } from "./utils.js";
|
|
20
21
|
import { assertSafeAiEndpoint, createApiRateLimitMiddleware, createAuthenticationRateLimitMiddleware, createBasicAuthMiddleware, createSameOriginMiddleware, createSecurityHeadersMiddleware } from "./security.js";
|
|
21
22
|
import { ImageCaptchaService } from "./image-captcha.js";
|
|
@@ -54,11 +55,12 @@ const loginSchema = z.object({
|
|
|
54
55
|
...captchaFields
|
|
55
56
|
}).strict();
|
|
56
57
|
const userUpdateSchema = z.object({ role: z.enum(["admin", "user"]).optional(), status: z.enum(["active", "disabled"]).optional() }).strict();
|
|
57
|
-
const memberSchema = z.object({ userId: identifier, role: z.enum(["editor", "viewer"]) }).strict();
|
|
58
|
-
const memberRoleSchema = z.object({ role: z.enum(["editor", "viewer"]) }).strict();
|
|
58
|
+
const memberSchema = z.object({ userId: identifier, role: z.enum(["editor", "settings-editor", "viewer"]) }).strict();
|
|
59
|
+
const memberRoleSchema = z.object({ role: z.enum(["editor", "settings-editor", "viewer"]) }).strict();
|
|
59
60
|
const profileSchema = z.object({ displayName: z.string().trim().min(1).max(80) }).strict();
|
|
60
61
|
const passwordChangeSchema = z.object({ currentPassword: z.string().max(200), newPassword: passwordSchema }).strict();
|
|
61
62
|
const changeNoteSchema = z.string().trim().max(500).optional();
|
|
63
|
+
const expectedVersionNoSchema = z.coerce.number().int().positive().optional();
|
|
62
64
|
function validateImportedText(text) {
|
|
63
65
|
if (text.length > maximumImportedTextLength)
|
|
64
66
|
throw new AppError(413, "IMPORT_TEXT_TOO_LARGE", "导入文件解压后的文本超过 2000 万字符限制");
|
|
@@ -173,7 +175,7 @@ const organizationSchema = z.object({
|
|
|
173
175
|
description: z.string().max(100_000).optional(),
|
|
174
176
|
settings: z.array(z.string().trim().min(1).max(20_000)).max(200).optional(),
|
|
175
177
|
memberIds: z.array(identifier).max(1000).optional()
|
|
176
|
-
});
|
|
178
|
+
}).strict();
|
|
177
179
|
const raceSchema = z.object({
|
|
178
180
|
name: nonEmpty.max(200),
|
|
179
181
|
parentRaceId: identifier.nullable().optional(),
|
|
@@ -474,8 +476,15 @@ export function createRuntime(options) {
|
|
|
474
476
|
});
|
|
475
477
|
data(response, result);
|
|
476
478
|
});
|
|
477
|
-
app.get("/api/users", (
|
|
478
|
-
|
|
479
|
+
app.get("/api/users", (request, response) => {
|
|
480
|
+
const pagination = parsePagination(request.query);
|
|
481
|
+
data(response, pagination ? auth.listUsersPage(pagination) : auth.listUsers());
|
|
482
|
+
});
|
|
483
|
+
app.get("/api/users/directory", (request, response) => {
|
|
484
|
+
const pagination = parsePagination(request.query);
|
|
485
|
+
const query = String(request.query.q ?? "");
|
|
486
|
+
data(response, pagination ? auth.directoryPage(query, pagination) : auth.directory(query));
|
|
487
|
+
});
|
|
479
488
|
app.get("/api/user-avatars/:userId", (request, response) => {
|
|
480
489
|
const avatar = auth.getAvatar(request.params.userId);
|
|
481
490
|
response.setHeader("Content-Type", avatar.mimeType);
|
|
@@ -491,7 +500,10 @@ export function createRuntime(options) {
|
|
|
491
500
|
store.audit(null, "user.updated", "user", updated.userId, { role: updated.role, status: updated.status });
|
|
492
501
|
data(response, updated);
|
|
493
502
|
});
|
|
494
|
-
app.get("/api/works", (
|
|
503
|
+
app.get("/api/works", (request, response) => {
|
|
504
|
+
const pagination = parsePagination(request.query);
|
|
505
|
+
data(response, pagination ? store.listWorksPage(pagination) : store.listWorks());
|
|
506
|
+
});
|
|
495
507
|
app.post("/api/works", (request, response) => data(response, store.createWork(parse(workSchema, request.body)), 201));
|
|
496
508
|
app.post("/api/works/import", upload.single("file"), async (request, response) => {
|
|
497
509
|
if (!request.file)
|
|
@@ -512,8 +524,14 @@ export function createRuntime(options) {
|
|
|
512
524
|
});
|
|
513
525
|
data(response, store.createImportedWork(input, originalFileName, extension.slice(1), parsedNovel), 201);
|
|
514
526
|
});
|
|
515
|
-
app.get("/api/works/:workId", (request, response) =>
|
|
516
|
-
|
|
527
|
+
app.get("/api/works/:workId", (request, response) => {
|
|
528
|
+
const pagination = parsePagination(request.query);
|
|
529
|
+
data(response, pagination ? store.getWorkDirectoryPage(request.params.workId, pagination) : store.getWorkDirectory(request.params.workId));
|
|
530
|
+
});
|
|
531
|
+
app.get("/api/works/:workId/members", (request, response) => {
|
|
532
|
+
const pagination = parsePagination(request.query);
|
|
533
|
+
data(response, pagination ? auth.listMembersPage(request.params.workId, pagination) : auth.listMembers(request.params.workId));
|
|
534
|
+
});
|
|
517
535
|
app.post("/api/works/:workId/members", (request, response) => {
|
|
518
536
|
if (!request.authUser)
|
|
519
537
|
throw new AppError(401, "AUTH_REQUIRED", "请先登录");
|
|
@@ -533,9 +551,13 @@ export function createRuntime(options) {
|
|
|
533
551
|
store.audit(request.params.workId, "work.member-removed", "user", request.params.userId);
|
|
534
552
|
data(response, members);
|
|
535
553
|
});
|
|
536
|
-
app.patch("/api/works/:workId", (request, response) =>
|
|
554
|
+
app.patch("/api/works/:workId", (request, response) => {
|
|
555
|
+
const { expectedVersionNo, changeNote, ...input } = parse(workSchema.partial().extend({ expectedVersionNo: expectedVersionNoSchema, changeNote: changeNoteSchema }).strict(), request.body);
|
|
556
|
+
data(response, store.updateWork(request.params.workId, input, expectedVersionNo, "manual", null, changeNote));
|
|
557
|
+
});
|
|
537
558
|
app.delete("/api/works/:workId", async (request, response) => {
|
|
538
|
-
const
|
|
559
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
560
|
+
const removableStorageKeys = store.deleteWork(request.params.workId, input.expectedVersionNo);
|
|
539
561
|
await Promise.all(removableStorageKeys.map((storageKey) => attachmentStorage.remove(storageKey)));
|
|
540
562
|
noContent(response);
|
|
541
563
|
});
|
|
@@ -557,15 +579,21 @@ export function createRuntime(options) {
|
|
|
557
579
|
const mimeType = isPng ? "image/png" : isJpeg ? "image/jpeg" : isWebp ? "image/webp" : null;
|
|
558
580
|
if (!mimeType)
|
|
559
581
|
throw new AppError(415, "INVALID_COVER", "封面文件内容不是有效的 PNG、JPEG 或 WebP 图片");
|
|
560
|
-
|
|
582
|
+
const expectedVersionNo = parse(expectedVersionNoSchema, request.body.expectedVersionNo);
|
|
583
|
+
data(response, store.setWorkCover(String(request.params.workId), mimeType, bytes, expectedVersionNo));
|
|
561
584
|
});
|
|
562
585
|
app.delete("/api/works/:workId/cover", (request, response) => {
|
|
563
|
-
|
|
586
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
587
|
+
store.deleteWorkCover(request.params.workId, input.expectedVersionNo);
|
|
564
588
|
noContent(response);
|
|
565
589
|
});
|
|
566
|
-
app.get("/api/works/:workId/file-versions", (request, response) =>
|
|
590
|
+
app.get("/api/works/:workId/file-versions", (request, response) => {
|
|
591
|
+
const pagination = parsePagination(request.query);
|
|
592
|
+
data(response, pagination ? store.listFileVersionsPage(request.params.workId, pagination) : store.listFileVersions(request.params.workId));
|
|
593
|
+
});
|
|
567
594
|
app.post("/api/works/:workId/file-versions/:fileVersionId/restore", (request, response) => {
|
|
568
|
-
|
|
595
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
596
|
+
data(response, store.restoreFileVersion(request.params.workId, request.params.fileVersionId, input.expectedVersionNo));
|
|
569
597
|
});
|
|
570
598
|
app.post("/api/works/:workId/import", upload.single("file"), async (request, response) => {
|
|
571
599
|
if (!request.file)
|
|
@@ -579,19 +607,23 @@ export function createRuntime(options) {
|
|
|
579
607
|
? await extractDocxText(request.file.buffer)
|
|
580
608
|
: decodeUtf8ImportedText(request.file.buffer));
|
|
581
609
|
const parsed = applyImportFileHints(parseNovelText(text), originalFileName);
|
|
582
|
-
|
|
610
|
+
const mode = parse(z.enum(["append", "overwrite"]), request.body.mode ?? "overwrite");
|
|
611
|
+
const expectedVersionNo = parse(expectedVersionNoSchema, request.body.expectedVersionNo);
|
|
612
|
+
data(response, store.importNovel(String(request.params.workId), originalFileName, extension.slice(1), parsed, mode, expectedVersionNo), 201);
|
|
583
613
|
});
|
|
584
614
|
app.post("/api/works/:workId/volumes", (request, response) => {
|
|
585
615
|
const input = parse(z.object({ title: nonEmpty.max(200), kind: z.enum(["main", "prequel", "extra", "epilogue", "appendix"]).optional(), description: z.string().max(5_000).optional(), keywords: z.array(nonEmpty.max(100)).max(100).optional() }), request.body);
|
|
586
616
|
data(response, store.createVolume(request.params.workId, input), 201);
|
|
587
617
|
});
|
|
588
618
|
app.patch("/api/volumes/:volumeId", (request, response) => {
|
|
589
|
-
const input = parse(z.object({ title: nonEmpty.max(200).optional(), kind: z.enum(["main", "prequel", "extra", "epilogue", "appendix"]).optional(), description: z.string().max(5_000).optional(), keywords: z.array(nonEmpty.max(100)).max(100).optional(), sortOrder: z.number().int().min(0).optional() }), request.body);
|
|
590
|
-
|
|
619
|
+
const input = parse(z.object({ title: nonEmpty.max(200).optional(), kind: z.enum(["main", "prequel", "extra", "epilogue", "appendix"]).optional(), description: z.string().max(5_000).optional(), keywords: z.array(nonEmpty.max(100)).max(100).optional(), sortOrder: z.number().int().min(0).optional(), expectedVersionNo: expectedVersionNoSchema, changeNote: changeNoteSchema }).strict(), request.body);
|
|
620
|
+
const { expectedVersionNo, changeNote, ...volumeInput } = input;
|
|
621
|
+
data(response, store.updateVolume(request.params.volumeId, volumeInput, expectedVersionNo, "manual", null, changeNote));
|
|
591
622
|
});
|
|
592
623
|
app.get("/api/volumes/:volumeId", (request, response) => data(response, store.getVolume(request.params.volumeId)));
|
|
593
624
|
app.delete("/api/volumes/:volumeId", (request, response) => {
|
|
594
|
-
|
|
625
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
626
|
+
store.deleteVolume(request.params.volumeId, input.expectedVersionNo);
|
|
595
627
|
noContent(response);
|
|
596
628
|
});
|
|
597
629
|
app.post("/api/works/:workId/chapters", (request, response) => {
|
|
@@ -600,32 +632,44 @@ export function createRuntime(options) {
|
|
|
600
632
|
});
|
|
601
633
|
app.get("/api/chapters/:chapterId", (request, response) => data(response, store.getChapter(request.params.chapterId)));
|
|
602
634
|
app.patch("/api/chapters/:chapterId", (request, response) => {
|
|
603
|
-
const input = parse(z.object({ title: nonEmpty.max(300).optional(), content: z.string().max(2_000_000).optional(), excludedFromAnalysis: z.boolean().optional(), chapterType: chapterTypeSchema.optional(), source: z.enum(["manual", "auto"]).optional(), changeNote: changeNoteSchema }).strict(), request.body);
|
|
604
|
-
const { source, changeNote, ...chapterInput } = input;
|
|
605
|
-
data(response, store.saveChapter(request.params.chapterId, chapterInput, source ?? "manual", null, changeNote));
|
|
635
|
+
const input = parse(z.object({ title: nonEmpty.max(300).optional(), content: z.string().max(2_000_000).optional(), excludedFromAnalysis: z.boolean().optional(), chapterType: chapterTypeSchema.optional(), source: z.enum(["manual", "auto"]).optional(), changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
636
|
+
const { source, changeNote, expectedVersionNo, ...chapterInput } = input;
|
|
637
|
+
data(response, store.saveChapter(request.params.chapterId, chapterInput, source ?? "manual", null, changeNote, expectedVersionNo));
|
|
606
638
|
});
|
|
607
639
|
app.delete("/api/chapters/:chapterId", (request, response) => {
|
|
608
|
-
|
|
640
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
641
|
+
store.deleteChapter(request.params.chapterId, input.expectedVersionNo);
|
|
609
642
|
noContent(response);
|
|
610
643
|
});
|
|
611
|
-
app.get("/api/chapters/:chapterId/versions", (request, response) =>
|
|
612
|
-
|
|
644
|
+
app.get("/api/chapters/:chapterId/versions", (request, response) => {
|
|
645
|
+
const pagination = parsePagination(request.query);
|
|
646
|
+
data(response, pagination ? store.listChapterVersionsPage(request.params.chapterId, pagination) : store.listChapterVersions(request.params.chapterId));
|
|
647
|
+
});
|
|
648
|
+
app.get("/api/chapters/:chapterId/insights", (request, response) => {
|
|
649
|
+
const pagination = parsePagination(request.query);
|
|
650
|
+
data(response, pagination ? store.listChapterInsightsPage(request.params.chapterId, pagination) : store.listChapterInsights(request.params.chapterId));
|
|
651
|
+
});
|
|
613
652
|
app.post("/api/chapters/:chapterId/restore", (request, response) => {
|
|
614
|
-
const input = parse(z.object({ versionNo: z.number().int().positive() }), request.body);
|
|
615
|
-
data(response, store.restoreChapter(request.params.chapterId, input.versionNo));
|
|
653
|
+
const input = parse(z.object({ versionNo: z.number().int().positive(), expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
654
|
+
data(response, store.restoreChapter(request.params.chapterId, input.versionNo, input.expectedVersionNo));
|
|
616
655
|
});
|
|
617
656
|
app.post("/api/chapters/:chapterId/move", (request, response) => {
|
|
618
|
-
const input = parse(z.object({ volumeId: identifier, sortOrder: z.number().int().min(0) }), request.body);
|
|
619
|
-
|
|
657
|
+
const input = parse(z.object({ volumeId: identifier, sortOrder: z.number().int().min(0), expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
658
|
+
const { expectedVersionNo, ...moveInput } = input;
|
|
659
|
+
data(response, store.moveChapter(request.params.chapterId, moveInput, expectedVersionNo));
|
|
660
|
+
});
|
|
661
|
+
app.get("/api/works/:workId/outlines", (request, response) => {
|
|
662
|
+
const pagination = parsePagination(request.query);
|
|
663
|
+
data(response, pagination ? store.listChapterOutlinesPage(request.params.workId, pagination) : store.listChapterOutlines(request.params.workId));
|
|
620
664
|
});
|
|
621
|
-
app.get("/api/works/:workId/outlines", (request, response) => data(response, store.listChapterOutlines(request.params.workId)));
|
|
622
665
|
app.get("/api/chapters/:chapterId/outline", (request, response) => data(response, store.getChapterOutline(request.params.chapterId)));
|
|
623
666
|
app.put("/api/chapters/:chapterId/outline", (request, response) => {
|
|
624
|
-
const { changeNote, ...input } = parse(chapterOutlineSchema.extend({ changeNote: changeNoteSchema }), request.body);
|
|
625
|
-
data(response, store.upsertChapterOutline(request.params.chapterId, input, "manual", null, changeNote));
|
|
667
|
+
const { changeNote, expectedVersionNo, ...input } = parse(chapterOutlineSchema.extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
668
|
+
data(response, store.upsertChapterOutline(request.params.chapterId, input, "manual", null, changeNote, expectedVersionNo));
|
|
626
669
|
});
|
|
627
670
|
app.delete("/api/chapters/:chapterId/outline", (request, response) => {
|
|
628
|
-
|
|
671
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
672
|
+
store.deleteChapterOutline(request.params.chapterId, input.expectedVersionNo);
|
|
629
673
|
noContent(response);
|
|
630
674
|
});
|
|
631
675
|
app.get("/api/works/:workId/foreshadows", (request, response) => {
|
|
@@ -633,39 +677,52 @@ export function createRuntime(options) {
|
|
|
633
677
|
status: z.enum(["all", "unresolved", "resolved"]).default("all"),
|
|
634
678
|
currentChapterId: identifier.optional()
|
|
635
679
|
}), request.query);
|
|
636
|
-
|
|
680
|
+
const pagination = parsePagination(request.query);
|
|
681
|
+
data(response, pagination
|
|
682
|
+
? store.listForeshadowsPage(request.params.workId, pagination, query.status, query.currentChapterId)
|
|
683
|
+
: store.listForeshadows(request.params.workId, query.status, query.currentChapterId));
|
|
637
684
|
});
|
|
638
685
|
app.post("/api/works/:workId/foreshadows", (request, response) => {
|
|
639
686
|
data(response, store.createForeshadow(request.params.workId, parse(foreshadowSchema, request.body)), 201);
|
|
640
687
|
});
|
|
641
688
|
app.get("/api/foreshadows/:foreshadowId", (request, response) => data(response, store.getForeshadow(request.params.foreshadowId)));
|
|
642
689
|
app.patch("/api/foreshadows/:foreshadowId", (request, response) => {
|
|
643
|
-
const { changeNote, ...input } = parse(foreshadowSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
644
|
-
data(response, store.updateForeshadow(request.params.foreshadowId, input, "manual", null, changeNote));
|
|
690
|
+
const { changeNote, expectedVersionNo, ...input } = parse(foreshadowSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
691
|
+
data(response, store.updateForeshadow(request.params.foreshadowId, input, "manual", null, changeNote, expectedVersionNo));
|
|
645
692
|
});
|
|
646
693
|
app.delete("/api/foreshadows/:foreshadowId", (request, response) => {
|
|
647
|
-
|
|
694
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
695
|
+
store.deleteForeshadow(request.params.foreshadowId, input.expectedVersionNo);
|
|
648
696
|
noContent(response);
|
|
649
697
|
});
|
|
650
698
|
app.post("/api/foreshadows/:foreshadowId/occurrences", (request, response) => {
|
|
651
|
-
|
|
699
|
+
const input = parse(foreshadowOccurrenceSchema.extend({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
700
|
+
const { expectedVersionNo, ...occurrenceInput } = input;
|
|
701
|
+
data(response, store.createForeshadowOccurrence(request.params.foreshadowId, occurrenceInput, expectedVersionNo), 201);
|
|
652
702
|
});
|
|
653
703
|
app.patch("/api/foreshadow-occurrences/:occurrenceId", (request, response) => {
|
|
654
|
-
|
|
704
|
+
const input = parse(foreshadowOccurrenceSchema.partial().extend({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
705
|
+
const { expectedVersionNo, ...occurrenceInput } = input;
|
|
706
|
+
data(response, store.updateForeshadowOccurrence(request.params.occurrenceId, occurrenceInput, expectedVersionNo));
|
|
655
707
|
});
|
|
656
708
|
app.delete("/api/foreshadow-occurrences/:occurrenceId", (request, response) => {
|
|
657
|
-
|
|
709
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
710
|
+
store.deleteForeshadowOccurrence(request.params.occurrenceId, input.expectedVersionNo);
|
|
658
711
|
noContent(response);
|
|
659
712
|
});
|
|
660
|
-
app.get("/api/works/:workId/settings", (request, response) =>
|
|
713
|
+
app.get("/api/works/:workId/settings", (request, response) => {
|
|
714
|
+
const pagination = parsePagination(request.query);
|
|
715
|
+
data(response, pagination ? store.listSettingsPage(request.params.workId, pagination) : store.listSettings(request.params.workId));
|
|
716
|
+
});
|
|
661
717
|
app.post("/api/works/:workId/settings", (request, response) => data(response, store.createSetting(request.params.workId, parse(settingSchema, request.body)), 201));
|
|
662
718
|
app.get("/api/settings/:settingId", (request, response) => data(response, store.getSetting(request.params.settingId)));
|
|
663
719
|
app.patch("/api/settings/:settingId", (request, response) => {
|
|
664
|
-
const { changeNote, ...input } = parse(settingSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
665
|
-
data(response, store.updateSetting(request.params.settingId, input, "manual", null, changeNote));
|
|
720
|
+
const { changeNote, expectedVersionNo, ...input } = parse(settingSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
721
|
+
data(response, store.updateSetting(request.params.settingId, input, "manual", null, changeNote, expectedVersionNo));
|
|
666
722
|
});
|
|
667
723
|
app.delete("/api/settings/:settingId", (request, response) => {
|
|
668
|
-
|
|
724
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
725
|
+
store.deleteSetting(request.params.settingId, input.expectedVersionNo);
|
|
669
726
|
noContent(response);
|
|
670
727
|
});
|
|
671
728
|
app.get("/api/works/:workId/characters", (request, response) => {
|
|
@@ -673,25 +730,47 @@ export function createRuntime(options) {
|
|
|
673
730
|
includeSections: z.enum(["true", "false"]).default("false"),
|
|
674
731
|
includeMerged: z.enum(["0", "1"]).default("0")
|
|
675
732
|
}), request.query);
|
|
676
|
-
|
|
733
|
+
const pagination = parsePagination(request.query);
|
|
734
|
+
data(response, pagination
|
|
735
|
+
? store.listCharactersPage(request.params.workId, pagination, includeSections === "true", includeMerged === "1")
|
|
736
|
+
: store.listCharacters(request.params.workId, includeSections === "true", includeMerged === "1"));
|
|
677
737
|
});
|
|
678
738
|
app.post("/api/works/:workId/characters", (request, response) => data(response, store.createCharacter(request.params.workId, parse(characterSchema, request.body)), 201));
|
|
679
739
|
app.get("/api/characters/:characterId", (request, response) => data(response, store.getCharacter(request.params.characterId)));
|
|
680
740
|
app.patch("/api/characters/:characterId", (request, response) => {
|
|
681
|
-
const { changeNote, ...input } = parse(characterUpdateSchema, request.body);
|
|
682
|
-
data(response, store.updateCharacter(request.params.characterId, input, "manual", null, changeNote));
|
|
741
|
+
const { changeNote, expectedVersionNo, ...input } = parse(characterUpdateSchema.extend({ expectedVersionNo: expectedVersionNoSchema }), request.body);
|
|
742
|
+
data(response, store.updateCharacter(request.params.characterId, input, "manual", null, changeNote, expectedVersionNo));
|
|
743
|
+
});
|
|
744
|
+
app.get("/api/characters/:characterId/versions", (request, response) => {
|
|
745
|
+
const pagination = parsePagination(request.query);
|
|
746
|
+
data(response, pagination ? store.listCharacterVersionsPage(request.params.characterId, pagination) : store.listCharacterVersions(request.params.characterId));
|
|
683
747
|
});
|
|
684
|
-
app.get("/api/characters/:characterId/versions", (request, response) => data(response, store.listCharacterVersions(request.params.characterId)));
|
|
685
748
|
app.post("/api/characters/:characterId/restore", (request, response) => {
|
|
686
|
-
const input = parse(z.object({ versionNo: z.number().int().positive() }), request.body);
|
|
687
|
-
data(response, store.restoreCharacter(request.params.characterId, input.versionNo));
|
|
749
|
+
const input = parse(z.object({ versionNo: z.number().int().positive(), expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
750
|
+
data(response, store.restoreCharacter(request.params.characterId, input.versionNo, input.expectedVersionNo));
|
|
688
751
|
});
|
|
689
752
|
app.delete("/api/characters/:characterId", (request, response) => {
|
|
690
|
-
|
|
753
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
754
|
+
store.deleteCharacter(request.params.characterId, input.expectedVersionNo);
|
|
691
755
|
noContent(response);
|
|
692
756
|
});
|
|
757
|
+
app.post("/api/characters/:characterId/merge", (request, response) => {
|
|
758
|
+
const input = parse(z.object({
|
|
759
|
+
targetCharacterId: identifier,
|
|
760
|
+
expectedTargetVersionNo: z.number().int().positive(),
|
|
761
|
+
expectedSourceVersionNo: z.number().int().positive()
|
|
762
|
+
}).strict(), request.body);
|
|
763
|
+
data(response, store.mergeCharacters({
|
|
764
|
+
reviewId: null,
|
|
765
|
+
sourceCharacterId: request.params.characterId,
|
|
766
|
+
...input
|
|
767
|
+
}));
|
|
768
|
+
});
|
|
693
769
|
app.get("/api/characters/:characterId/sections", (request, response) => {
|
|
694
|
-
|
|
770
|
+
const pagination = parsePagination(request.query);
|
|
771
|
+
data(response, pagination
|
|
772
|
+
? store.listCharacterProfileSectionsPage(request.params.characterId, pagination)
|
|
773
|
+
: store.listCharacterProfileSections(request.params.characterId));
|
|
695
774
|
});
|
|
696
775
|
app.post("/api/characters/:characterId/sections", (request, response) => {
|
|
697
776
|
data(response, store.createCharacterProfileSection(request.params.characterId, parse(characterProfileSectionSchema, request.body)), 201);
|
|
@@ -700,22 +779,27 @@ export function createRuntime(options) {
|
|
|
700
779
|
data(response, store.getCharacterProfileSection(request.params.sectionId));
|
|
701
780
|
});
|
|
702
781
|
app.patch("/api/character-sections/:sectionId", (request, response) => {
|
|
703
|
-
const { changeNote, ...input } = parse(characterProfileSectionSchema.partial().extend({ changeNote: changeNoteSchema }).strict(), request.body);
|
|
704
|
-
data(response, store.updateCharacterProfileSection(request.params.sectionId, input, "manual", null, changeNote));
|
|
782
|
+
const { changeNote, expectedVersionNo, ...input } = parse(characterProfileSectionSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
783
|
+
data(response, store.updateCharacterProfileSection(request.params.sectionId, input, "manual", null, changeNote, expectedVersionNo));
|
|
705
784
|
});
|
|
706
785
|
app.delete("/api/character-sections/:sectionId", (request, response) => {
|
|
707
|
-
|
|
786
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
787
|
+
store.deleteCharacterProfileSection(request.params.sectionId, input.expectedVersionNo);
|
|
708
788
|
noContent(response);
|
|
709
789
|
});
|
|
710
790
|
app.get("/api/character-sections/:sectionId/versions", (request, response) => {
|
|
711
|
-
|
|
791
|
+
const pagination = parsePagination(request.query);
|
|
792
|
+
data(response, pagination
|
|
793
|
+
? store.listCharacterProfileSectionVersionsPage(request.params.sectionId, pagination)
|
|
794
|
+
: store.listCharacterProfileSectionVersions(request.params.sectionId));
|
|
712
795
|
});
|
|
713
796
|
app.post("/api/character-sections/:sectionId/restore", (request, response) => {
|
|
714
|
-
const input = parse(z.object({ versionNo: z.number().int().positive() }).strict(), request.body);
|
|
715
|
-
data(response, store.restoreCharacterProfileSection(request.params.sectionId, input.versionNo));
|
|
797
|
+
const input = parse(z.object({ versionNo: z.number().int().positive(), expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
798
|
+
data(response, store.restoreCharacterProfileSection(request.params.sectionId, input.versionNo, input.expectedVersionNo));
|
|
716
799
|
});
|
|
717
800
|
app.get("/api/works/:workId/attachments", (request, response) => {
|
|
718
|
-
|
|
801
|
+
const pagination = parsePagination(request.query);
|
|
802
|
+
data(response, pagination ? store.listAttachmentsPage(request.params.workId, pagination) : store.listAttachments(request.params.workId));
|
|
719
803
|
});
|
|
720
804
|
app.post("/api/works/:workId/attachments", attachmentUpload.single("file"), async (request, response) => {
|
|
721
805
|
if (!request.file)
|
|
@@ -758,44 +842,67 @@ export function createRuntime(options) {
|
|
|
758
842
|
await attachmentStorage.remove(deleted.storageKey);
|
|
759
843
|
noContent(response);
|
|
760
844
|
});
|
|
761
|
-
app.get("/api/works/:workId/races", (request, response) =>
|
|
845
|
+
app.get("/api/works/:workId/races", (request, response) => {
|
|
846
|
+
const pagination = parsePagination(request.query);
|
|
847
|
+
data(response, pagination ? store.listRacesPage(request.params.workId, pagination) : store.listRaces(request.params.workId));
|
|
848
|
+
});
|
|
762
849
|
app.post("/api/works/:workId/races", (request, response) => {
|
|
763
850
|
data(response, store.createRace(request.params.workId, parse(raceSchema, request.body)), 201);
|
|
764
851
|
});
|
|
765
852
|
app.get("/api/races/:raceId", (request, response) => data(response, store.getRace(request.params.raceId)));
|
|
766
853
|
app.patch("/api/races/:raceId", (request, response) => {
|
|
767
|
-
const { changeNote, ...input } = parse(raceSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
768
|
-
data(response, store.updateRace(request.params.raceId, input, "manual", null, changeNote));
|
|
854
|
+
const { changeNote, expectedVersionNo, ...input } = parse(raceSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
855
|
+
data(response, store.updateRace(request.params.raceId, input, "manual", null, changeNote, expectedVersionNo));
|
|
769
856
|
});
|
|
770
857
|
app.delete("/api/races/:raceId", (request, response) => {
|
|
771
|
-
|
|
858
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
859
|
+
store.deleteRace(request.params.raceId, input.expectedVersionNo);
|
|
772
860
|
noContent(response);
|
|
773
861
|
});
|
|
774
|
-
app.
|
|
862
|
+
app.post("/api/races/:raceId/merge", (request, response) => {
|
|
863
|
+
const input = parse(z.object({ targetRaceId: identifier }).strict(), request.body);
|
|
864
|
+
data(response, store.mergeRaces(request.params.raceId, input.targetRaceId));
|
|
865
|
+
});
|
|
866
|
+
app.get("/api/works/:workId/organizations", (request, response) => {
|
|
867
|
+
const pagination = parsePagination(request.query);
|
|
868
|
+
data(response, pagination ? store.listOrganizationsPage(request.params.workId, pagination) : store.listOrganizations(request.params.workId));
|
|
869
|
+
});
|
|
775
870
|
app.post("/api/works/:workId/organizations", (request, response) => {
|
|
776
871
|
data(response, store.createOrganization(request.params.workId, parse(organizationSchema, request.body)), 201);
|
|
777
872
|
});
|
|
778
873
|
app.get("/api/organizations/:organizationId", (request, response) => data(response, store.getOrganization(request.params.organizationId)));
|
|
779
874
|
app.patch("/api/organizations/:organizationId", (request, response) => {
|
|
780
|
-
const { changeNote, ...input } = parse(organizationSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
781
|
-
data(response, store.updateOrganization(request.params.organizationId, input, "manual", null, changeNote));
|
|
875
|
+
const { changeNote, expectedVersionNo, ...input } = parse(organizationSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
876
|
+
data(response, store.updateOrganization(request.params.organizationId, input, "manual", null, changeNote, expectedVersionNo));
|
|
782
877
|
});
|
|
783
878
|
app.delete("/api/organizations/:organizationId", (request, response) => {
|
|
784
|
-
|
|
879
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
880
|
+
store.deleteOrganization(request.params.organizationId, input.expectedVersionNo);
|
|
785
881
|
noContent(response);
|
|
786
882
|
});
|
|
787
|
-
app.
|
|
883
|
+
app.post("/api/organizations/:organizationId/merge", (request, response) => {
|
|
884
|
+
const input = parse(z.object({ targetOrganizationId: identifier }).strict(), request.body);
|
|
885
|
+
data(response, store.mergeOrganizations(request.params.organizationId, input.targetOrganizationId));
|
|
886
|
+
});
|
|
887
|
+
app.get("/api/works/:workId/timeline-tracks", (request, response) => {
|
|
888
|
+
const pagination = parsePagination(request.query);
|
|
889
|
+
data(response, pagination ? store.listTimelineTracksPage(request.params.workId, pagination) : store.listTimelineTracks(request.params.workId));
|
|
890
|
+
});
|
|
788
891
|
app.post("/api/works/:workId/timeline-tracks", (request, response) => data(response, store.createTimelineTrack(request.params.workId, parse(timelineTrackSchema, request.body)), 201));
|
|
789
892
|
app.get("/api/timeline-tracks/:trackId", (request, response) => data(response, store.getTimelineTrack(request.params.trackId)));
|
|
790
893
|
app.patch("/api/timeline-tracks/:trackId", (request, response) => {
|
|
791
|
-
const { changeNote, ...input } = parse(timelineTrackSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
792
|
-
data(response, store.updateTimelineTrack(request.params.trackId, input, "manual", null, changeNote));
|
|
894
|
+
const { changeNote, expectedVersionNo, ...input } = parse(timelineTrackSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
895
|
+
data(response, store.updateTimelineTrack(request.params.trackId, input, "manual", null, changeNote, expectedVersionNo));
|
|
793
896
|
});
|
|
794
897
|
app.delete("/api/timeline-tracks/:trackId", (request, response) => {
|
|
795
|
-
|
|
898
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
899
|
+
store.deleteTimelineTrack(request.params.trackId, input.expectedVersionNo);
|
|
796
900
|
noContent(response);
|
|
797
901
|
});
|
|
798
|
-
app.get("/api/works/:workId/timeline", (request, response) =>
|
|
902
|
+
app.get("/api/works/:workId/timeline", (request, response) => {
|
|
903
|
+
const pagination = parsePagination(request.query);
|
|
904
|
+
data(response, pagination ? store.listTimelineEventsPage(request.params.workId, pagination) : store.listTimelineEvents(request.params.workId));
|
|
905
|
+
});
|
|
799
906
|
app.post("/api/works/:workId/timeline", (request, response) => data(response, store.createTimelineEvent(request.params.workId, parse(timelineSchema, request.body)), 201));
|
|
800
907
|
app.post("/api/works/:workId/timeline/merge", (request, response) => {
|
|
801
908
|
const input = parse(z.object({
|
|
@@ -803,14 +910,16 @@ export function createRuntime(options) {
|
|
|
803
910
|
name: nonEmpty.max(300),
|
|
804
911
|
description: z.string().max(100_000).optional(),
|
|
805
912
|
timeLabel: z.string().max(300).optional(),
|
|
806
|
-
timeSort: z.number().finite().nullable().optional()
|
|
807
|
-
|
|
808
|
-
|
|
913
|
+
timeSort: z.number().finite().nullable().optional(),
|
|
914
|
+
expectedVersionNos: z.record(identifier, z.number().int().positive()).optional()
|
|
915
|
+
}).strict(), request.body);
|
|
916
|
+
const { expectedVersionNos, ...mergeInput } = input;
|
|
917
|
+
data(response, store.mergeTimelineEvents(request.params.workId, input.eventIds, mergeInput, expectedVersionNos), 201);
|
|
809
918
|
});
|
|
810
919
|
app.get("/api/timeline/:eventId", (request, response) => data(response, store.getTimelineEvent(request.params.eventId)));
|
|
811
920
|
app.patch("/api/timeline/:eventId", (request, response) => {
|
|
812
|
-
const { changeNote, ...input } = parse(timelineSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
813
|
-
data(response, store.updateTimelineEvent(request.params.eventId, input, "manual", null, changeNote));
|
|
921
|
+
const { changeNote, expectedVersionNo, ...input } = parse(timelineSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
922
|
+
data(response, store.updateTimelineEvent(request.params.eventId, input, "manual", null, changeNote, expectedVersionNo));
|
|
814
923
|
});
|
|
815
924
|
app.post("/api/timeline/:eventId/split", (request, response) => {
|
|
816
925
|
const input = parse(z.object({
|
|
@@ -819,42 +928,52 @@ export function createRuntime(options) {
|
|
|
819
928
|
description: z.string().max(100_000).optional(),
|
|
820
929
|
timeLabel: z.string().max(300).optional(),
|
|
821
930
|
timeSort: z.number().finite().nullable().optional()
|
|
822
|
-
})).min(2)
|
|
823
|
-
|
|
824
|
-
|
|
931
|
+
})).min(2),
|
|
932
|
+
expectedVersionNo: expectedVersionNoSchema
|
|
933
|
+
}).strict(), request.body);
|
|
934
|
+
data(response, store.splitTimelineEvent(request.params.eventId, input.parts, input.expectedVersionNo), 201);
|
|
825
935
|
});
|
|
826
936
|
app.delete("/api/timeline/:eventId", (request, response) => {
|
|
827
|
-
|
|
937
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
938
|
+
store.deleteTimelineEvent(request.params.eventId, input.expectedVersionNo);
|
|
828
939
|
noContent(response);
|
|
829
940
|
});
|
|
830
941
|
app.get("/api/works/:workId/relationships", (request, response) => {
|
|
831
942
|
const confidence = request.query.minimumConfidence ? Number(request.query.minimumConfidence) : 0;
|
|
832
943
|
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1)
|
|
833
944
|
throw new AppError(400, "INVALID_CONFIDENCE", "置信度必须在 0 到 1 之间");
|
|
834
|
-
|
|
945
|
+
const pagination = parsePagination(request.query);
|
|
946
|
+
data(response, pagination
|
|
947
|
+
? store.listRelationshipsPage(request.params.workId, pagination, confidence)
|
|
948
|
+
: store.listRelationships(request.params.workId, confidence));
|
|
835
949
|
});
|
|
836
950
|
app.post("/api/works/:workId/relationships", (request, response) => data(response, store.createRelationship(request.params.workId, parse(relationshipSchema, request.body)), 201));
|
|
837
951
|
app.get("/api/relationships/:relationshipId", (request, response) => data(response, store.getRelationship(request.params.relationshipId)));
|
|
838
952
|
app.patch("/api/relationships/:relationshipId", (request, response) => {
|
|
839
|
-
const { changeNote, ...input } = parse(relationshipSchema.partial().extend({ changeNote: changeNoteSchema }), request.body);
|
|
840
|
-
data(response, store.updateRelationship(request.params.relationshipId, input, "manual", null, changeNote));
|
|
953
|
+
const { changeNote, expectedVersionNo, ...input } = parse(relationshipSchema.partial().extend({ changeNote: changeNoteSchema, expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
954
|
+
data(response, store.updateRelationship(request.params.relationshipId, input, "manual", null, changeNote, expectedVersionNo));
|
|
841
955
|
});
|
|
842
956
|
app.delete("/api/relationships/:relationshipId", (request, response) => {
|
|
843
|
-
|
|
957
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
958
|
+
store.deleteRelationship(request.params.relationshipId, input.expectedVersionNo);
|
|
844
959
|
noContent(response);
|
|
845
960
|
});
|
|
846
961
|
app.get("/api/entity-versions/:entityType/:entityId", (request, response) => {
|
|
847
962
|
const input = parse(z.object({ entityType: versionedEntityTypeSchema, entityId: identifier }), request.params);
|
|
848
|
-
|
|
963
|
+
const pagination = parsePagination(request.query);
|
|
964
|
+
data(response, pagination
|
|
965
|
+
? store.listEntityVersionsPage(input.entityType, input.entityId, pagination)
|
|
966
|
+
: store.listEntityVersions(input.entityType, input.entityId));
|
|
849
967
|
});
|
|
850
968
|
app.post("/api/entity-versions/:entityType/:entityId/restore", (request, response) => {
|
|
851
969
|
const params = parse(z.object({ entityType: versionedEntityTypeSchema, entityId: identifier }), request.params);
|
|
852
|
-
const input = parse(z.object({ versionNo: z.number().int().positive() }), request.body);
|
|
853
|
-
data(response, store.restoreEntityVersion(params.entityType, params.entityId, input.versionNo));
|
|
970
|
+
const input = parse(z.object({ versionNo: z.number().int().positive(), expectedVersionNo: expectedVersionNoSchema }).strict(), request.body);
|
|
971
|
+
data(response, store.restoreEntityVersion(params.entityType, params.entityId, input.versionNo, input.expectedVersionNo));
|
|
854
972
|
});
|
|
855
973
|
app.get("/api/works/:workId/reviews", (request, response) => {
|
|
856
974
|
const status = typeof request.query.status === "string" ? request.query.status : undefined;
|
|
857
|
-
|
|
975
|
+
const pagination = parsePagination(request.query);
|
|
976
|
+
data(response, pagination ? store.listReviewItemsPage(request.params.workId, pagination, status) : store.listReviewItems(request.params.workId, status));
|
|
858
977
|
});
|
|
859
978
|
app.post("/api/works/:workId/reviews", (request, response) => data(response, store.createReviewItem(request.params.workId, parse(reviewSchema, request.body)), 201));
|
|
860
979
|
app.patch("/api/reviews/:reviewId", (request, response) => data(response, store.updateReviewItem(request.params.reviewId, parse(reviewSchema.partial(), request.body))));
|
|
@@ -875,7 +994,10 @@ export function createRuntime(options) {
|
|
|
875
994
|
}
|
|
876
995
|
data(response, store.mergeCharacters({ reviewId: request.params.reviewId, ...input }));
|
|
877
996
|
});
|
|
878
|
-
app.get("/api/works/:workId/tasks", (request, response) =>
|
|
997
|
+
app.get("/api/works/:workId/tasks", (request, response) => {
|
|
998
|
+
const pagination = parsePagination(request.query);
|
|
999
|
+
data(response, pagination ? store.listTasksPage(request.params.workId, pagination) : store.listTasks(request.params.workId));
|
|
1000
|
+
});
|
|
879
1001
|
app.post("/api/works/:workId/tasks", (request, response) => {
|
|
880
1002
|
const input = parse(z.object({ taskType: z.enum(["structure", "chapter-analysis", "character-extraction", "character-summary", "character-identity-audit", "timeline-analysis", "relationship-analysis", "worldview-analysis", "setting-extraction", "consistency-check", "report-update", "book-analysis"]), scope: jsonObject.optional() }), request.body);
|
|
881
1003
|
data(response, store.createTask(request.params.workId, input), 201);
|
|
@@ -889,9 +1011,15 @@ export function createRuntime(options) {
|
|
|
889
1011
|
data(response, await ai.runTask(request.params.taskId, input.modelId));
|
|
890
1012
|
});
|
|
891
1013
|
app.post("/api/tasks/:taskId/cancel", (request, response) => data(response, ai.cancelTask(request.params.taskId)));
|
|
892
|
-
app.get("/api/platform/ai/providers", (
|
|
1014
|
+
app.get("/api/platform/ai/providers", (request, response) => {
|
|
1015
|
+
const pagination = parsePagination(request.query);
|
|
1016
|
+
data(response, pagination ? ai.listProvidersPage(pagination) : ai.listProviders());
|
|
1017
|
+
});
|
|
893
1018
|
app.post("/api/platform/ai/providers", (request, response) => data(response, ai.createProvider(parse(providerSchema, request.body)), 201));
|
|
894
|
-
app.get("/api/platform/ai/models", (
|
|
1019
|
+
app.get("/api/platform/ai/models", (request, response) => {
|
|
1020
|
+
const pagination = parsePagination(request.query);
|
|
1021
|
+
data(response, pagination ? ai.listPlatformModelsPage(pagination) : ai.listPlatformModels());
|
|
1022
|
+
});
|
|
895
1023
|
app.get("/api/platform/ai/settings", (_request, response) => data(response, store.getPlatformAiSettings()));
|
|
896
1024
|
app.patch("/api/platform/ai/settings", (request, response) => data(response, store.updatePlatformAiSettings(parse(aiPromptSchema, request.body))));
|
|
897
1025
|
app.get("/api/ui-settings", (_request, response) => data(response, store.getPlatformUiSettings()));
|
|
@@ -911,12 +1039,18 @@ export function createRuntime(options) {
|
|
|
911
1039
|
}
|
|
912
1040
|
data(response, updated);
|
|
913
1041
|
});
|
|
914
|
-
app.get("/api/works/:workId/ai-conversations", (request, response) =>
|
|
1042
|
+
app.get("/api/works/:workId/ai-conversations", (request, response) => {
|
|
1043
|
+
const pagination = parsePagination(request.query);
|
|
1044
|
+
data(response, pagination ? store.listAiConversationsPage(request.params.workId, pagination) : store.listAiConversations(request.params.workId));
|
|
1045
|
+
});
|
|
915
1046
|
app.post("/api/works/:workId/ai-conversations", (request, response) => {
|
|
916
1047
|
const input = parse(z.object({ title: z.string().max(200).optional() }), request.body ?? {});
|
|
917
1048
|
data(response, store.createAiConversation(request.params.workId, input.title), 201);
|
|
918
1049
|
});
|
|
919
|
-
app.get("/api/ai-conversations/:conversationId", (request, response) =>
|
|
1050
|
+
app.get("/api/ai-conversations/:conversationId", (request, response) => {
|
|
1051
|
+
const pagination = parsePagination(request.query);
|
|
1052
|
+
data(response, pagination ? store.getAiConversationPage(request.params.conversationId, pagination) : store.getAiConversation(request.params.conversationId));
|
|
1053
|
+
});
|
|
920
1054
|
app.post("/api/ai-conversations/:conversationId/fork", (request, response) => {
|
|
921
1055
|
const input = parse(z.object({ messageId: identifier, title: z.string().max(200).optional() }), request.body);
|
|
922
1056
|
data(response, store.forkAiConversation(request.params.conversationId, input.messageId, input.title), 201);
|
|
@@ -997,7 +1131,10 @@ export function createRuntime(options) {
|
|
|
997
1131
|
noContent(response);
|
|
998
1132
|
});
|
|
999
1133
|
app.post("/api/providers/:providerId/test", async (request, response) => data(response, await ai.testProvider(request.params.providerId)));
|
|
1000
|
-
app.get("/api/providers/:providerId/models", (request, response) =>
|
|
1134
|
+
app.get("/api/providers/:providerId/models", (request, response) => {
|
|
1135
|
+
const pagination = parsePagination(request.query);
|
|
1136
|
+
data(response, pagination ? ai.listModelsPage(request.params.providerId, pagination) : ai.listModels(request.params.providerId));
|
|
1137
|
+
});
|
|
1001
1138
|
app.post("/api/providers/:providerId/models", (request, response) => data(response, ai.createModel(request.params.providerId, parse(modelSchema, request.body)), 201));
|
|
1002
1139
|
app.get("/api/models/:modelId", (request, response) => data(response, ai.getModel(request.params.modelId)));
|
|
1003
1140
|
app.patch("/api/models/:modelId", (request, response) => data(response, ai.updateModel(request.params.modelId, parse(modelSchema.partial(), request.body))));
|
|
@@ -1005,8 +1142,14 @@ export function createRuntime(options) {
|
|
|
1005
1142
|
ai.deleteModel(request.params.modelId);
|
|
1006
1143
|
noContent(response);
|
|
1007
1144
|
});
|
|
1008
|
-
app.get("/api/works/:workId/models", (request, response) =>
|
|
1009
|
-
|
|
1145
|
+
app.get("/api/works/:workId/models", (request, response) => {
|
|
1146
|
+
const pagination = parsePagination(request.query);
|
|
1147
|
+
data(response, pagination ? ai.listWorkModelsPage(request.params.workId, pagination) : ai.listWorkModels(request.params.workId));
|
|
1148
|
+
});
|
|
1149
|
+
app.get("/api/works/:workId/task-defaults", (request, response) => {
|
|
1150
|
+
const pagination = parsePagination(request.query);
|
|
1151
|
+
data(response, pagination ? ai.listTaskDefaultsPage(request.params.workId, pagination) : ai.listTaskDefaults(request.params.workId));
|
|
1152
|
+
});
|
|
1010
1153
|
app.put("/api/works/:workId/task-defaults/:taskType", (request, response) => {
|
|
1011
1154
|
const taskType = parse(z.enum(TASK_TYPES), request.params.taskType);
|
|
1012
1155
|
const input = parse(z.object({ modelId: identifier }), request.body);
|
|
@@ -1014,7 +1157,8 @@ export function createRuntime(options) {
|
|
|
1014
1157
|
});
|
|
1015
1158
|
app.get("/api/works/:workId/suggestions", (request, response) => {
|
|
1016
1159
|
const status = typeof request.query.status === "string" ? request.query.status : undefined;
|
|
1017
|
-
|
|
1160
|
+
const pagination = parsePagination(request.query);
|
|
1161
|
+
data(response, pagination ? ai.listSuggestionsPage(request.params.workId, pagination, status) : ai.listSuggestions(request.params.workId, status));
|
|
1018
1162
|
});
|
|
1019
1163
|
app.post("/api/works/:workId/suggestions", async (request, response) => {
|
|
1020
1164
|
const input = parse(z.object({
|
|
@@ -1109,7 +1253,10 @@ export function createRuntime(options) {
|
|
|
1109
1253
|
});
|
|
1110
1254
|
app.get("/api/suggestions/:suggestionId", (request, response) => data(response, ai.getSuggestion(request.params.suggestionId)));
|
|
1111
1255
|
app.get("/api/suggestions/:suggestionId/guards", (request, response) => {
|
|
1112
|
-
|
|
1256
|
+
const pagination = parsePagination(request.query);
|
|
1257
|
+
data(response, pagination
|
|
1258
|
+
? store.listContinuationGuardsPage(request.params.suggestionId, pagination)
|
|
1259
|
+
: store.listContinuationGuards(request.params.suggestionId));
|
|
1113
1260
|
});
|
|
1114
1261
|
app.post("/api/suggestions/:suggestionId/guard", async (request, response) => {
|
|
1115
1262
|
const input = parse(z.object({ content: z.string().max(2_000_000).optional() }), request.body ?? {});
|
|
@@ -1120,7 +1267,10 @@ export function createRuntime(options) {
|
|
|
1120
1267
|
data(response, ai.acceptSuggestion(request.params.suggestionId, input.content));
|
|
1121
1268
|
});
|
|
1122
1269
|
app.post("/api/suggestions/:suggestionId/reject", (request, response) => data(response, ai.rejectSuggestion(request.params.suggestionId)));
|
|
1123
|
-
app.get("/api/works/:workId/ai-calls", (request, response) =>
|
|
1270
|
+
app.get("/api/works/:workId/ai-calls", (request, response) => {
|
|
1271
|
+
const pagination = parsePagination(request.query);
|
|
1272
|
+
data(response, pagination ? ai.listCallsPage(request.params.workId, pagination) : ai.listCalls(request.params.workId));
|
|
1273
|
+
});
|
|
1124
1274
|
app.get("/api/works/:workId/search", (request, response) => {
|
|
1125
1275
|
const query = parse(z.string().trim().min(1).max(500), request.query.q);
|
|
1126
1276
|
data(response, store.search(request.params.workId, query));
|
|
@@ -1136,7 +1286,10 @@ export function createRuntime(options) {
|
|
|
1136
1286
|
response.setHeader("Content-Disposition", `attachment; filename=novel-${request.params.workId}.${format === "markdown" ? "md" : "txt"}`);
|
|
1137
1287
|
response.send(store.exportText(request.params.workId, format));
|
|
1138
1288
|
});
|
|
1139
|
-
app.get("/api/works/:workId/audit-logs", (request, response) =>
|
|
1289
|
+
app.get("/api/works/:workId/audit-logs", (request, response) => {
|
|
1290
|
+
const pagination = parsePagination(request.query);
|
|
1291
|
+
data(response, pagination ? store.listAuditLogsPage(request.params.workId, pagination) : store.listAuditLogs(request.params.workId));
|
|
1292
|
+
});
|
|
1140
1293
|
if (options.serveUi ?? true) {
|
|
1141
1294
|
const publicPath = options.publicPath ?? join(process.cwd(), "src", "public");
|
|
1142
1295
|
// index.html 按登录态动态下发:未登录时注入 login-route 类,首帧直接渲染登录页;
|