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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-headless-lms",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-headless-lms": "dist/index.js"
@@ -11,5 +11,6 @@ CREDENTIAL_STORE_KEY={{CREDENTIAL_STORE_KEY}}
11
11
 
12
12
  DELIVERY_URL_EXPIRY=86400
13
13
  MEMBER_IMAGE_MAX_BYTES=10485760
14
+ MEMBER_FILE_MAX_BYTES=104857600
14
15
  REQUEST_LOGGING=true
15
16
  {{STORAGE_ENV}}
@@ -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
  }
@@ -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", {