@json-to-office/jto 0.33.2 → 0.35.0
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/cli.js +214 -516
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/HomePage-DgVVosCp.js +106 -0
- package/dist/client/assets/HomePage-DgVVosCp.js.map +1 -0
- package/dist/client/assets/{JsonEditorPage-GZV7oJD2.js → JsonEditorPage-13oxEHfY.js} +3 -3
- package/dist/client/assets/{JsonEditorPage-GZV7oJD2.js.map → JsonEditorPage-13oxEHfY.js.map} +1 -1
- package/dist/client/assets/{MonacoPluginProvider-btNzJ41o.js → MonacoPluginProvider-DiUyU9px.js} +3 -3
- package/dist/client/assets/{MonacoPluginProvider-btNzJ41o.js.map → MonacoPluginProvider-DiUyU9px.js.map} +1 -1
- package/dist/client/assets/editor-monaco-json-CTrPZKuA.js +24 -0
- package/dist/client/assets/{editor-monaco-json-rhvIaBDI.js.map → editor-monaco-json-CTrPZKuA.js.map} +1 -1
- package/dist/client/assets/{editor-Cugq4RzH.js → editor-ouhvdGgK.js} +2 -2
- package/dist/client/assets/{editor-Cugq4RzH.js.map → editor-ouhvdGgK.js.map} +1 -1
- package/dist/client/assets/{editor-refs-store-DeJZ8FB9.js → editor-refs-store-DNQzN0_z.js} +2 -2
- package/dist/client/assets/{editor-refs-store-DeJZ8FB9.js.map → editor-refs-store-DNQzN0_z.js.map} +1 -1
- package/dist/client/assets/index-CRuLxurM.js +5 -0
- package/dist/client/assets/index-CRuLxurM.js.map +1 -0
- package/dist/client/assets/index-CnUTzeor.css +1 -0
- package/dist/client/assets/preview-D3QYaLPp.js +4 -0
- package/dist/client/assets/preview-D3QYaLPp.js.map +1 -0
- package/dist/client/assets/{ui-vendor-Cs3OXpi7.js → ui-vendor-DyQLnWB8.js} +16 -11
- package/dist/client/assets/ui-vendor-DyQLnWB8.js.map +1 -0
- package/dist/client/index.html +3 -3
- package/dist/client/templates/Alternative deck 16_9.pptx.json +91 -49
- package/dist/client/templates/Company deck 16_9.pptx.json +134 -70
- package/dist/client/templates/Company deck 4_3.pptx.json +84 -44
- package/dist/client/templates/data-report-presentation.pptx.json +100 -52
- package/dist/client/templates/modern-annual-report-1.docx.json +207 -116
- package/dist/client/templates/modern-annual-report-2.docx.json +52 -37
- package/dist/client/templates/modern-annual-report-3.docx.json +40 -20
- package/dist/client/templates/tech-report.docx.json +96 -48
- package/dist/render-server.js +168 -106
- package/dist/render-server.js.map +1 -1
- package/package.json +7 -8
- package/dist/client/assets/HomePage-Cq6woGMG.js +0 -106
- package/dist/client/assets/HomePage-Cq6woGMG.js.map +0 -1
- package/dist/client/assets/editor-monaco-json-rhvIaBDI.js +0 -24
- package/dist/client/assets/index-C9ply7vX.css +0 -1
- package/dist/client/assets/index-C9uVroFQ.js +0 -5
- package/dist/client/assets/index-C9uVroFQ.js.map +0 -1
- package/dist/client/assets/preview-B_B09gpA.js +0 -3
- package/dist/client/assets/preview-B_B09gpA.js.map +0 -1
- package/dist/client/assets/ui-vendor-Cs3OXpi7.js.map +0 -1
package/dist/render-server.js
CHANGED
|
@@ -16,13 +16,111 @@ import {
|
|
|
16
16
|
DEFAULT_VISUAL_DPI,
|
|
17
17
|
MIN_VISUAL_DPI,
|
|
18
18
|
MAX_VISUAL_DPI,
|
|
19
|
-
MAX_RASTERIZE_BATCH_SLIDES
|
|
19
|
+
MAX_RASTERIZE_BATCH_SLIDES,
|
|
20
|
+
MAX_RASTERIZE_FONTS,
|
|
21
|
+
MAX_RASTERIZE_FONT_BYTES
|
|
20
22
|
} from "@json-to-office/shared";
|
|
21
23
|
import {
|
|
22
24
|
createLibreOfficePptxRasterizer,
|
|
23
25
|
createLibreOfficePptxBatchRasterizer
|
|
24
26
|
} from "@json-to-office/jto-cli";
|
|
25
27
|
|
|
28
|
+
// src/server/config/index.ts
|
|
29
|
+
import dotenv from "dotenv";
|
|
30
|
+
dotenv.config();
|
|
31
|
+
function positiveInteger(value, fallback) {
|
|
32
|
+
const parsed = Number.parseInt(value ?? "", 10);
|
|
33
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
34
|
+
}
|
|
35
|
+
function parseAuthMode(value, nodeEnv) {
|
|
36
|
+
if (value === "auto" || value === "required" || value === "disabled") {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
return nodeEnv === "production" ? "required" : "auto";
|
|
40
|
+
}
|
|
41
|
+
function parseOutboundSourceMode(value, nodeEnv) {
|
|
42
|
+
if (value === "development" || value === "safe") return value;
|
|
43
|
+
return nodeEnv === "production" ? "safe" : "development";
|
|
44
|
+
}
|
|
45
|
+
function normalizeNodeEnv(value) {
|
|
46
|
+
return value === "development" || value === "test" ? value : "production";
|
|
47
|
+
}
|
|
48
|
+
function parseEnv(env) {
|
|
49
|
+
const nodeEnv = normalizeNodeEnv(env.NODE_ENV || "development");
|
|
50
|
+
return {
|
|
51
|
+
NODE_ENV: nodeEnv,
|
|
52
|
+
// No PORT here: the listener is opened by the dev server from the CLI
|
|
53
|
+
// config (`-p` > `server.port` > `PORT` env > format default), so a second
|
|
54
|
+
// copy of it in this module would be silently ignored.
|
|
55
|
+
CORS_ORIGIN: env.CORS_ORIGIN || "*",
|
|
56
|
+
API_KEY: env.API_KEY,
|
|
57
|
+
API_KEY_HEADER: env.API_KEY_HEADER || "x-api-key",
|
|
58
|
+
API_AUTH_MODE: parseAuthMode(env.API_AUTH_MODE, nodeEnv),
|
|
59
|
+
RATE_LIMIT_WINDOW_MS: positiveInteger(env.RATE_LIMIT_WINDOW_MS, 9e5),
|
|
60
|
+
RATE_LIMIT_MAX: positiveInteger(
|
|
61
|
+
env.RATE_LIMIT_MAX,
|
|
62
|
+
nodeEnv === "production" ? 100 : 1e3
|
|
63
|
+
),
|
|
64
|
+
TRUST_PROXY_HEADERS: env.TRUST_PROXY_HEADERS === "true",
|
|
65
|
+
MAX_FILE_SIZE: positiveInteger(env.MAX_FILE_SIZE, 10 * 1024 * 1024),
|
|
66
|
+
MAX_REQUEST_BODY_SIZE: positiveInteger(
|
|
67
|
+
env.MAX_REQUEST_BODY_SIZE,
|
|
68
|
+
32 * 1024 * 1024
|
|
69
|
+
),
|
|
70
|
+
MAX_CONCURRENT_REQUESTS: positiveInteger(
|
|
71
|
+
env.MAX_CONCURRENT_REQUESTS,
|
|
72
|
+
nodeEnv === "production" ? 8 : 64
|
|
73
|
+
),
|
|
74
|
+
OUTBOUND_SOURCE_MODE: parseOutboundSourceMode(
|
|
75
|
+
env.OUTBOUND_SOURCE_MODE,
|
|
76
|
+
nodeEnv
|
|
77
|
+
),
|
|
78
|
+
OUTBOUND_HOST_ALLOWLIST: (env.OUTBOUND_HOST_ALLOWLIST || "").split(",").map((host) => host.trim().toLowerCase()).filter(Boolean),
|
|
79
|
+
LIBREOFFICE_PATH: env.LIBREOFFICE_PATH,
|
|
80
|
+
LIBREOFFICE_TIMEOUT_MS: env.LIBREOFFICE_TIMEOUT_MS ? positiveInteger(env.LIBREOFFICE_TIMEOUT_MS, 3e4) : 3e4,
|
|
81
|
+
LOG_LEVEL: env.LOG_LEVEL || "info",
|
|
82
|
+
CACHE_ENABLED: env.CACHE_ENABLED !== "false",
|
|
83
|
+
CACHE_MAX_SIZE_MB: positiveInteger(env.CACHE_MAX_SIZE_MB, 100),
|
|
84
|
+
CACHE_TTL_SECONDS: positiveInteger(env.CACHE_TTL_SECONDS, 3600),
|
|
85
|
+
CACHE_MAX_ITEMS: positiveInteger(env.CACHE_MAX_ITEMS, 1e3)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var parsedEnv = parseEnv(process.env);
|
|
89
|
+
var config = {
|
|
90
|
+
...parsedEnv,
|
|
91
|
+
isDevelopment: parsedEnv.NODE_ENV === "development",
|
|
92
|
+
isProduction: parsedEnv.NODE_ENV === "production",
|
|
93
|
+
isTest: parsedEnv.NODE_ENV === "test",
|
|
94
|
+
features: {
|
|
95
|
+
apiKey: parsedEnv.API_AUTH_MODE !== "disabled",
|
|
96
|
+
cache: parsedEnv.CACHE_ENABLED
|
|
97
|
+
},
|
|
98
|
+
cors: {
|
|
99
|
+
origin: parsedEnv.CORS_ORIGIN === "*" ? parsedEnv.CORS_ORIGIN : parsedEnv.CORS_ORIGIN.split(",").map((o) => o.trim()),
|
|
100
|
+
credentials: true
|
|
101
|
+
},
|
|
102
|
+
rateLimit: {
|
|
103
|
+
windowMs: parsedEnv.RATE_LIMIT_WINDOW_MS,
|
|
104
|
+
max: parsedEnv.RATE_LIMIT_MAX,
|
|
105
|
+
trustProxy: parsedEnv.TRUST_PROXY_HEADERS
|
|
106
|
+
},
|
|
107
|
+
requestLimits: {
|
|
108
|
+
maxBodySize: parsedEnv.MAX_REQUEST_BODY_SIZE,
|
|
109
|
+
maxConcurrent: parsedEnv.MAX_CONCURRENT_REQUESTS,
|
|
110
|
+
maxFileSize: parsedEnv.MAX_FILE_SIZE
|
|
111
|
+
},
|
|
112
|
+
outboundSources: {
|
|
113
|
+
mode: parsedEnv.OUTBOUND_SOURCE_MODE,
|
|
114
|
+
allowedHosts: parsedEnv.OUTBOUND_HOST_ALLOWLIST
|
|
115
|
+
},
|
|
116
|
+
cache: {
|
|
117
|
+
enabled: parsedEnv.CACHE_ENABLED,
|
|
118
|
+
maxSizeMB: parsedEnv.CACHE_MAX_SIZE_MB,
|
|
119
|
+
ttlSeconds: parsedEnv.CACHE_TTL_SECONDS,
|
|
120
|
+
maxItems: parsedEnv.CACHE_MAX_ITEMS
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
26
124
|
// src/server/lib/typebox-validator.ts
|
|
27
125
|
import { Value } from "@sinclair/typebox/value";
|
|
28
126
|
import { HTTPException } from "hono/http-exception";
|
|
@@ -386,13 +484,39 @@ function assertSafeRendererPayload(value, policy) {
|
|
|
386
484
|
}
|
|
387
485
|
|
|
388
486
|
// src/server/rasterize-route.ts
|
|
487
|
+
var BASE64_PATTERN = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$";
|
|
488
|
+
var RasterizeFontFaceSchema = Type.Object(
|
|
489
|
+
{
|
|
490
|
+
family: Type.String({ minLength: 1, maxLength: 128 }),
|
|
491
|
+
weight: Type.Integer({ minimum: 1, maximum: 1e3 }),
|
|
492
|
+
italic: Type.Boolean(),
|
|
493
|
+
data: Type.String({
|
|
494
|
+
minLength: 1,
|
|
495
|
+
maxLength: 4 * 1024 * 1024,
|
|
496
|
+
pattern: BASE64_PATTERN
|
|
497
|
+
}),
|
|
498
|
+
format: Type.Optional(
|
|
499
|
+
Type.Union([
|
|
500
|
+
Type.Literal("ttf"),
|
|
501
|
+
Type.Literal("otf"),
|
|
502
|
+
Type.Literal("woff"),
|
|
503
|
+
Type.Literal("woff2")
|
|
504
|
+
])
|
|
505
|
+
)
|
|
506
|
+
},
|
|
507
|
+
{ additionalProperties: false }
|
|
508
|
+
);
|
|
509
|
+
var FontsSchema = Type.Optional(
|
|
510
|
+
Type.Array(RasterizeFontFaceSchema, { maxItems: MAX_RASTERIZE_FONTS })
|
|
511
|
+
);
|
|
389
512
|
var RasterizeRequestSchema = Type.Object(
|
|
390
513
|
{
|
|
391
514
|
presentation: Type.Object({}, { additionalProperties: true }),
|
|
392
515
|
dpi: Type.Optional(
|
|
393
516
|
Type.Number({ minimum: MIN_VISUAL_DPI, maximum: MAX_VISUAL_DPI })
|
|
394
517
|
),
|
|
395
|
-
baseDir: Type.Optional(Type.String())
|
|
518
|
+
baseDir: Type.Optional(Type.String()),
|
|
519
|
+
fonts: FontsSchema
|
|
396
520
|
},
|
|
397
521
|
{ additionalProperties: false }
|
|
398
522
|
);
|
|
@@ -410,7 +534,13 @@ var RasterizeBatchRequestSchema = Type.Object(
|
|
|
410
534
|
),
|
|
411
535
|
{ minItems: 1, maxItems: MAX_RASTERIZE_BATCH_SLIDES }
|
|
412
536
|
),
|
|
413
|
-
baseDir: Type.Optional(Type.String())
|
|
537
|
+
baseDir: Type.Optional(Type.String()),
|
|
538
|
+
// Request-level, shared by every slide, exactly like `baseDir`. NOT
|
|
539
|
+
// inside the per-slide object above, whose `additionalProperties: false`
|
|
540
|
+
// must keep rejecting per-slide fonts: a uniform slide shape is what
|
|
541
|
+
// lets batch dedupe and the disk-cache key stay identical to the
|
|
542
|
+
// single-slide path.
|
|
543
|
+
fonts: FontsSchema
|
|
414
544
|
},
|
|
415
545
|
{ additionalProperties: false }
|
|
416
546
|
);
|
|
@@ -465,6 +595,18 @@ function assertPixelBudget(slides) {
|
|
|
465
595
|
});
|
|
466
596
|
}
|
|
467
597
|
}
|
|
598
|
+
function assertFontBudget(fonts) {
|
|
599
|
+
if (!fonts?.length) return;
|
|
600
|
+
const bytes = fonts.reduce(
|
|
601
|
+
(sum, f) => sum + Math.ceil(f.data.length * 0.75),
|
|
602
|
+
0
|
|
603
|
+
);
|
|
604
|
+
if (bytes > MAX_RASTERIZE_FONT_BYTES) {
|
|
605
|
+
throw new HTTPException2(400, {
|
|
606
|
+
message: "Requested font payload is too large"
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
468
610
|
function resolveSafeBaseDir(baseDir) {
|
|
469
611
|
if (baseDir === void 0) return void 0;
|
|
470
612
|
const resolved = path.resolve(baseDir);
|
|
@@ -492,13 +634,23 @@ function toHttpException(error) {
|
|
|
492
634
|
message: "Internal server error during rasterization"
|
|
493
635
|
});
|
|
494
636
|
}
|
|
637
|
+
var MAX_RASTERIZE_BODY_CEILING = 64 * 1024 * 1024;
|
|
638
|
+
function resolveMaxBodyBytes(explicit) {
|
|
639
|
+
const fromEnv = Number.parseInt(
|
|
640
|
+
process.env.MAX_RASTERIZE_BODY_SIZE ?? "",
|
|
641
|
+
10
|
|
642
|
+
);
|
|
643
|
+
const configured = explicit ?? (Number.isFinite(fromEnv) && fromEnv > 0 ? fromEnv : config.requestLimits.maxBodySize);
|
|
644
|
+
return Math.min(configured, MAX_RASTERIZE_BODY_CEILING);
|
|
645
|
+
}
|
|
495
646
|
function registerRasterizeRoute(router, options = {}) {
|
|
496
647
|
const getRasterizer = options.getRasterizer ?? getSharedRasterizer;
|
|
497
648
|
const getBatchRasterizer = options.getBatchRasterizer ?? getSharedBatchRasterizer;
|
|
649
|
+
const maxBodyBytes = resolveMaxBodyBytes(options.maxBodyBytes);
|
|
498
650
|
const shared = [
|
|
499
651
|
...options.preMiddleware ?? [],
|
|
500
652
|
bodyLimit({
|
|
501
|
-
maxSize:
|
|
653
|
+
maxSize: maxBodyBytes,
|
|
502
654
|
onError: () => {
|
|
503
655
|
throw new HTTPException2(413, { message: "Request body too large" });
|
|
504
656
|
}
|
|
@@ -518,10 +670,11 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
518
670
|
...shared,
|
|
519
671
|
tbValidator(RasterizeRequestSchema),
|
|
520
672
|
async (c) => {
|
|
521
|
-
const { presentation, dpi, baseDir } = getValidated(c, "json");
|
|
673
|
+
const { presentation, dpi, baseDir, fonts } = getValidated(c, "json");
|
|
522
674
|
const safeBaseDir = resolveSafeBaseDir(baseDir);
|
|
523
675
|
const effectiveDpi = clampVisualDpi(dpi ?? DEFAULT_VISUAL_DPI);
|
|
524
676
|
assertPixelBudget([{ presentation, dpi: effectiveDpi }]);
|
|
677
|
+
assertFontBudget(fonts);
|
|
525
678
|
const result = await guard(async () => {
|
|
526
679
|
if (options.sourcePolicy) {
|
|
527
680
|
assertSafeOutboundSources(
|
|
@@ -533,7 +686,12 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
533
686
|
return getRasterizer()({
|
|
534
687
|
presentation,
|
|
535
688
|
dpi: effectiveDpi,
|
|
536
|
-
baseDir: safeBaseDir
|
|
689
|
+
baseDir: safeBaseDir,
|
|
690
|
+
// Conditional so a fontless request reaches the engine with no
|
|
691
|
+
// `fonts` key at all — an `undefined` would still be an absent
|
|
692
|
+
// digest, but keeping the object identical to the pre-change shape
|
|
693
|
+
// makes the cache-key equivalence obvious.
|
|
694
|
+
...fonts?.length && { fonts }
|
|
537
695
|
});
|
|
538
696
|
});
|
|
539
697
|
return c.json(result);
|
|
@@ -544,13 +702,14 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
544
702
|
...shared,
|
|
545
703
|
tbValidator(RasterizeBatchRequestSchema),
|
|
546
704
|
async (c) => {
|
|
547
|
-
const { slides, baseDir } = getValidated(c, "json");
|
|
705
|
+
const { slides, baseDir, fonts } = getValidated(c, "json");
|
|
548
706
|
const safeBaseDir = resolveSafeBaseDir(baseDir);
|
|
549
707
|
const effectiveSlides = slides.map((slide) => ({
|
|
550
708
|
presentation: slide.presentation,
|
|
551
709
|
dpi: clampVisualDpi(slide.dpi ?? DEFAULT_VISUAL_DPI)
|
|
552
710
|
}));
|
|
553
711
|
assertPixelBudget(effectiveSlides);
|
|
712
|
+
assertFontBudget(fonts);
|
|
554
713
|
const result = await guard(async () => {
|
|
555
714
|
if (options.sourcePolicy) {
|
|
556
715
|
slides.forEach(
|
|
@@ -563,7 +722,8 @@ function registerRasterizeRoute(router, options = {}) {
|
|
|
563
722
|
}
|
|
564
723
|
return getBatchRasterizer()({
|
|
565
724
|
slides: effectiveSlides,
|
|
566
|
-
baseDir: safeBaseDir
|
|
725
|
+
baseDir: safeBaseDir,
|
|
726
|
+
...fonts?.length && { fonts }
|
|
567
727
|
});
|
|
568
728
|
});
|
|
569
729
|
return c.json({
|
|
@@ -673,104 +833,6 @@ function concurrencyLimiter(options) {
|
|
|
673
833
|
|
|
674
834
|
// src/server/middleware/hono/auth.ts
|
|
675
835
|
import { createHash, timingSafeEqual } from "crypto";
|
|
676
|
-
|
|
677
|
-
// src/server/config/index.ts
|
|
678
|
-
import dotenv from "dotenv";
|
|
679
|
-
dotenv.config();
|
|
680
|
-
function positiveInteger(value, fallback) {
|
|
681
|
-
const parsed = Number.parseInt(value ?? "", 10);
|
|
682
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
683
|
-
}
|
|
684
|
-
function parseAuthMode(value, nodeEnv) {
|
|
685
|
-
if (value === "auto" || value === "required" || value === "disabled") {
|
|
686
|
-
return value;
|
|
687
|
-
}
|
|
688
|
-
return nodeEnv === "production" ? "required" : "auto";
|
|
689
|
-
}
|
|
690
|
-
function parseOutboundSourceMode(value, nodeEnv) {
|
|
691
|
-
if (value === "development" || value === "safe") return value;
|
|
692
|
-
return nodeEnv === "production" ? "safe" : "development";
|
|
693
|
-
}
|
|
694
|
-
function normalizeNodeEnv(value) {
|
|
695
|
-
return value === "development" || value === "test" ? value : "production";
|
|
696
|
-
}
|
|
697
|
-
function parseEnv(env) {
|
|
698
|
-
const nodeEnv = normalizeNodeEnv(env.NODE_ENV || "development");
|
|
699
|
-
return {
|
|
700
|
-
NODE_ENV: nodeEnv,
|
|
701
|
-
// No PORT here: the listener is opened by the dev server from the CLI
|
|
702
|
-
// config (`-p` > `server.port` > `PORT` env > format default), so a second
|
|
703
|
-
// copy of it in this module would be silently ignored.
|
|
704
|
-
CORS_ORIGIN: env.CORS_ORIGIN || "*",
|
|
705
|
-
API_KEY: env.API_KEY,
|
|
706
|
-
API_KEY_HEADER: env.API_KEY_HEADER || "x-api-key",
|
|
707
|
-
API_AUTH_MODE: parseAuthMode(env.API_AUTH_MODE, nodeEnv),
|
|
708
|
-
RATE_LIMIT_WINDOW_MS: positiveInteger(env.RATE_LIMIT_WINDOW_MS, 9e5),
|
|
709
|
-
RATE_LIMIT_MAX: positiveInteger(
|
|
710
|
-
env.RATE_LIMIT_MAX,
|
|
711
|
-
nodeEnv === "production" ? 100 : 1e3
|
|
712
|
-
),
|
|
713
|
-
TRUST_PROXY_HEADERS: env.TRUST_PROXY_HEADERS === "true",
|
|
714
|
-
MAX_FILE_SIZE: positiveInteger(env.MAX_FILE_SIZE, 10 * 1024 * 1024),
|
|
715
|
-
MAX_REQUEST_BODY_SIZE: positiveInteger(
|
|
716
|
-
env.MAX_REQUEST_BODY_SIZE,
|
|
717
|
-
32 * 1024 * 1024
|
|
718
|
-
),
|
|
719
|
-
MAX_CONCURRENT_REQUESTS: positiveInteger(
|
|
720
|
-
env.MAX_CONCURRENT_REQUESTS,
|
|
721
|
-
nodeEnv === "production" ? 8 : 64
|
|
722
|
-
),
|
|
723
|
-
OUTBOUND_SOURCE_MODE: parseOutboundSourceMode(
|
|
724
|
-
env.OUTBOUND_SOURCE_MODE,
|
|
725
|
-
nodeEnv
|
|
726
|
-
),
|
|
727
|
-
OUTBOUND_HOST_ALLOWLIST: (env.OUTBOUND_HOST_ALLOWLIST || "").split(",").map((host) => host.trim().toLowerCase()).filter(Boolean),
|
|
728
|
-
LIBREOFFICE_PATH: env.LIBREOFFICE_PATH,
|
|
729
|
-
LIBREOFFICE_TIMEOUT_MS: env.LIBREOFFICE_TIMEOUT_MS ? positiveInteger(env.LIBREOFFICE_TIMEOUT_MS, 3e4) : 3e4,
|
|
730
|
-
LOG_LEVEL: env.LOG_LEVEL || "info",
|
|
731
|
-
CACHE_ENABLED: env.CACHE_ENABLED !== "false",
|
|
732
|
-
CACHE_MAX_SIZE_MB: positiveInteger(env.CACHE_MAX_SIZE_MB, 100),
|
|
733
|
-
CACHE_TTL_SECONDS: positiveInteger(env.CACHE_TTL_SECONDS, 3600),
|
|
734
|
-
CACHE_MAX_ITEMS: positiveInteger(env.CACHE_MAX_ITEMS, 1e3)
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
var parsedEnv = parseEnv(process.env);
|
|
738
|
-
var config = {
|
|
739
|
-
...parsedEnv,
|
|
740
|
-
isDevelopment: parsedEnv.NODE_ENV === "development",
|
|
741
|
-
isProduction: parsedEnv.NODE_ENV === "production",
|
|
742
|
-
isTest: parsedEnv.NODE_ENV === "test",
|
|
743
|
-
features: {
|
|
744
|
-
apiKey: parsedEnv.API_AUTH_MODE !== "disabled",
|
|
745
|
-
cache: parsedEnv.CACHE_ENABLED
|
|
746
|
-
},
|
|
747
|
-
cors: {
|
|
748
|
-
origin: parsedEnv.CORS_ORIGIN === "*" ? parsedEnv.CORS_ORIGIN : parsedEnv.CORS_ORIGIN.split(",").map((o) => o.trim()),
|
|
749
|
-
credentials: true
|
|
750
|
-
},
|
|
751
|
-
rateLimit: {
|
|
752
|
-
windowMs: parsedEnv.RATE_LIMIT_WINDOW_MS,
|
|
753
|
-
max: parsedEnv.RATE_LIMIT_MAX,
|
|
754
|
-
trustProxy: parsedEnv.TRUST_PROXY_HEADERS
|
|
755
|
-
},
|
|
756
|
-
requestLimits: {
|
|
757
|
-
maxBodySize: parsedEnv.MAX_REQUEST_BODY_SIZE,
|
|
758
|
-
maxConcurrent: parsedEnv.MAX_CONCURRENT_REQUESTS,
|
|
759
|
-
maxFileSize: parsedEnv.MAX_FILE_SIZE
|
|
760
|
-
},
|
|
761
|
-
outboundSources: {
|
|
762
|
-
mode: parsedEnv.OUTBOUND_SOURCE_MODE,
|
|
763
|
-
allowedHosts: parsedEnv.OUTBOUND_HOST_ALLOWLIST
|
|
764
|
-
},
|
|
765
|
-
cache: {
|
|
766
|
-
enabled: parsedEnv.CACHE_ENABLED,
|
|
767
|
-
maxSizeMB: parsedEnv.CACHE_MAX_SIZE_MB,
|
|
768
|
-
ttlSeconds: parsedEnv.CACHE_TTL_SECONDS,
|
|
769
|
-
maxItems: parsedEnv.CACHE_MAX_ITEMS
|
|
770
|
-
}
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
// src/server/middleware/hono/auth.ts
|
|
774
836
|
function keysEqual(received, expected) {
|
|
775
837
|
const receivedDigest = createHash("sha256").update(received).digest();
|
|
776
838
|
const expectedDigest = createHash("sha256").update(expected).digest();
|