@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 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
- response.type(format === "txt" ? "text/plain" : "text/markdown");
1866
- response.setHeader("Content-Disposition", `attachment; filename=novel-${request.params.workId}.${format === "markdown" ? "md" : "txt"}`);
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({