create-headless-lms 0.2.0 → 0.3.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/package.json +1 -1
- package/templates/env.tmpl +1 -0
- package/templates/src/config.ts +34 -0
- package/templates/src/main.ts +4 -1
package/package.json
CHANGED
package/templates/env.tmpl
CHANGED
package/templates/src/config.ts
CHANGED
|
@@ -18,6 +18,7 @@ export function loadStorageConfig(): MinioStorageConfig | undefined {
|
|
|
18
18
|
secretKey: process.env.STORAGE_SECRET_KEY ?? "",
|
|
19
19
|
region: process.env.STORAGE_REGION ?? "us-east-1",
|
|
20
20
|
bucket: process.env.STORAGE_BUCKET ?? "headless-lms",
|
|
21
|
+
...(process.env.STORAGE_PUBLIC_URL ? { publicUrl: process.env.STORAGE_PUBLIC_URL } : {}),
|
|
21
22
|
uploadExpirySeconds: Number(process.env.STORAGE_UPLOAD_EXPIRY ?? 300),
|
|
22
23
|
downloadExpirySeconds: Number(process.env.STORAGE_DOWNLOAD_EXPIRY ?? 86400),
|
|
23
24
|
};
|
|
@@ -35,6 +36,18 @@ export function parseMemberImageMaxBytes(): number {
|
|
|
35
36
|
return value;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
export function parseMemberFileMaxBytes(): number {
|
|
40
|
+
const raw = process.env.MEMBER_FILE_MAX_BYTES;
|
|
41
|
+
if (raw === undefined || raw === "") {
|
|
42
|
+
return 100 * 1024 * 1024;
|
|
43
|
+
}
|
|
44
|
+
const value = Number(raw);
|
|
45
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
46
|
+
throw new Error(`invalid MEMBER_FILE_MAX_BYTES "${raw}" (expected a positive integer)`);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
/** Browser app origins from CLIENT_ORIGIN (comma-separated). */
|
|
39
52
|
export function parseClientOrigins(): string[] {
|
|
40
53
|
return (process.env.CLIENT_ORIGIN ?? "http://localhost:8001,http://localhost:8002")
|
|
@@ -43,6 +56,22 @@ export function parseClientOrigins(): string[] {
|
|
|
43
56
|
.filter(Boolean);
|
|
44
57
|
}
|
|
45
58
|
|
|
59
|
+
function loadPlatformConfig(): ContainerConfig["platform"] {
|
|
60
|
+
const key = process.env.PLATFORM_KEY;
|
|
61
|
+
if (!key) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const accountEmail = process.env.PLATFORM_ACCOUNT_EMAIL;
|
|
65
|
+
if (!accountEmail) {
|
|
66
|
+
throw new Error("PLATFORM_KEY is set: set PLATFORM_ACCOUNT_EMAIL too");
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
key,
|
|
70
|
+
accountEmail,
|
|
71
|
+
...(process.env.PLATFORM_ACCOUNT_NAME && { accountName: process.env.PLATFORM_ACCOUNT_NAME }),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
function loadContainerConfig(): ContainerConfig {
|
|
47
76
|
const clientOrigins = parseClientOrigins();
|
|
48
77
|
const apiOrigin = process.env.BETTER_AUTH_URL ?? "http://localhost:8000";
|
|
@@ -57,6 +86,7 @@ function loadContainerConfig(): ContainerConfig {
|
|
|
57
86
|
secureCookies: process.env.NODE_ENV === "production",
|
|
58
87
|
studentPortalUrl: process.env.STUDENT_PORTAL_URL ?? "http://localhost:8002",
|
|
59
88
|
adminAppUrl: process.env.ADMIN_APP_URL ?? "http://localhost:8001",
|
|
89
|
+
platform: loadPlatformConfig(),
|
|
60
90
|
};
|
|
61
91
|
}
|
|
62
92
|
|
|
@@ -73,6 +103,10 @@ export function loadServerConfig(): ServerConfig {
|
|
|
73
103
|
// default must never silently extend entitled/paywalled link lifetimes.
|
|
74
104
|
deliveryExpirySeconds: Number(process.env.DELIVERY_URL_EXPIRY ?? 86400),
|
|
75
105
|
memberImageMaxBytes: parseMemberImageMaxBytes(),
|
|
106
|
+
memberFileMaxBytes: parseMemberFileMaxBytes(),
|
|
107
|
+
// LISTEN needs a session connection; behind a transaction-mode pooler
|
|
108
|
+
// point PUBSUB_DATABASE_URL at Postgres directly.
|
|
109
|
+
pubsubDatabaseUrl: process.env.PUBSUB_DATABASE_URL || container.databaseUrl,
|
|
76
110
|
container,
|
|
77
111
|
};
|
|
78
112
|
}
|
package/templates/src/main.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Process entry point: env → config → container → server → listen.
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { createContainer, buildServer } from "@headless-lms/server";
|
|
3
|
+
import { createContainer, buildServer, closeOnSignals } from "@headless-lms/server";
|
|
4
4
|
import { MinioStorageAdapter } from "@headless-lms/adapter-storage-minio";
|
|
5
5
|
import { loadServerConfig, loadStorageConfig } from "./config.js";
|
|
6
6
|
|
|
@@ -22,7 +22,10 @@ try {
|
|
|
22
22
|
app.log.error(err);
|
|
23
23
|
process.exit(1);
|
|
24
24
|
}
|
|
25
|
+
closeOnSignals(app);
|
|
25
26
|
|
|
27
|
+
await container.pubsub?.start();
|
|
28
|
+
await container.chatHub?.start();
|
|
26
29
|
container.outboxRelay.start();
|
|
27
30
|
container.automationEngine.start().catch((err) => {
|
|
28
31
|
container.logger.error("automation engine failed to start", {
|