@musnows/scriverse 0.5.6 → 0.5.8
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/app.js +30 -3
- package/dist/app.js.map +1 -1
- package/dist/database.js +14 -0
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +127 -18
- package/dist/public/index.html +14 -6
- package/dist/public/relationship-graph.js +80 -22
- package/dist/public/styles.css +42 -16
- package/dist/store.js +30 -1
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
+
import JSZip from "jszip";
|
|
2
3
|
import multer from "multer";
|
|
3
4
|
import mammoth from "mammoth";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -6,6 +7,7 @@ import { dirname, extname, join } from "node:path";
|
|
|
6
7
|
import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
7
8
|
import { rm } from "node:fs/promises";
|
|
8
9
|
import { tmpdir } from "node:os";
|
|
10
|
+
import { pipeline } from "node:stream/promises";
|
|
9
11
|
import { z, ZodError } from "zod";
|
|
10
12
|
import { AttachmentStorage } from "./attachment-storage.js";
|
|
11
13
|
import { AiManager } from "./ai.js";
|
|
@@ -1011,6 +1013,11 @@ export function createRuntime(options) {
|
|
|
1011
1013
|
store.deleteChapter(request.params.chapterId, input.expectedVersionNo);
|
|
1012
1014
|
noContent(response);
|
|
1013
1015
|
});
|
|
1016
|
+
app.delete("/api/chapters/:chapterId/permanent", (request, response) => {
|
|
1017
|
+
const input = parse(z.object({ expectedVersionNo: expectedVersionNoSchema }).strict(), request.body ?? {});
|
|
1018
|
+
store.permanentlyDeleteChapter(request.params.chapterId, input.expectedVersionNo);
|
|
1019
|
+
noContent(response);
|
|
1020
|
+
});
|
|
1014
1021
|
app.get("/api/chapters/:chapterId/versions", (request, response) => {
|
|
1015
1022
|
const pagination = parsePagination(request.query);
|
|
1016
1023
|
data(response, pagination ? store.listChapterVersionsPage(request.params.chapterId, pagination) : store.listChapterVersions(request.params.chapterId));
|
|
@@ -1855,15 +1862,29 @@ export function createRuntime(options) {
|
|
|
1855
1862
|
const query = parse(z.string().trim().min(1).max(500), request.query.q);
|
|
1856
1863
|
data(response, store.search(request.params.workId, query));
|
|
1857
1864
|
});
|
|
1858
|
-
app.get("/api/works/:workId/export", (request, response) => {
|
|
1865
|
+
app.get("/api/works/:workId/export", async (request, response) => {
|
|
1859
1866
|
const format = parse(z.enum(["json", "txt", "markdown"]), request.query.format ?? "json");
|
|
1860
1867
|
if (format === "json") {
|
|
1861
1868
|
response.setHeader("Content-Disposition", `attachment; filename=novel-${request.params.workId}.json`);
|
|
1862
1869
|
data(response, store.exportWork(request.params.workId));
|
|
1863
1870
|
return;
|
|
1864
1871
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1872
|
+
if (format === "markdown") {
|
|
1873
|
+
const exportName = `novel-${request.params.workId}`;
|
|
1874
|
+
const archive = new JSZip();
|
|
1875
|
+
archive.file(`${exportName}.md`, store.exportText(request.params.workId, format));
|
|
1876
|
+
response.type("application/zip");
|
|
1877
|
+
response.setHeader("Content-Disposition", `attachment; filename=${exportName}.zip`);
|
|
1878
|
+
await pipeline(archive.generateNodeStream({
|
|
1879
|
+
type: "nodebuffer",
|
|
1880
|
+
streamFiles: true,
|
|
1881
|
+
compression: "DEFLATE",
|
|
1882
|
+
compressionOptions: { level: 6 }
|
|
1883
|
+
}), response);
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
response.type("text/plain");
|
|
1887
|
+
response.setHeader("Content-Disposition", `attachment; filename=novel-${request.params.workId}.txt`);
|
|
1867
1888
|
response.send(store.exportText(request.params.workId, format));
|
|
1868
1889
|
});
|
|
1869
1890
|
app.get("/api/works/:workId/audit-logs", (request, response) => {
|
|
@@ -1929,6 +1950,12 @@ export function createRuntime(options) {
|
|
|
1929
1950
|
app.use((_request, _response, next) => next(new AppError(404, "ROUTE_NOT_FOUND", "请求的接口不存在")));
|
|
1930
1951
|
app.use((error, request, response, _next) => {
|
|
1931
1952
|
const commonFields = { method: request.method, path: sanitizeRequestPath(request.path), error: sanitizeError(error) };
|
|
1953
|
+
if (response.headersSent || response.destroyed) {
|
|
1954
|
+
logger.warn("http.request.response_stream_failed", commonFields);
|
|
1955
|
+
if (!response.destroyed)
|
|
1956
|
+
response.destroy(error instanceof Error ? error : undefined);
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1932
1959
|
if (error instanceof ZodError) {
|
|
1933
1960
|
logger.warn("http.request.validation_failed", { ...commonFields, issuePaths: error.issues.map((issue) => issue.path.join(".")) });
|
|
1934
1961
|
response.status(400).json({
|