@nuraly/lumenjs 0.1.2 → 0.1.4
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/README.md +76 -235
- package/dist/auth/config.d.ts +23 -0
- package/dist/auth/config.js +115 -0
- package/dist/auth/guard.d.ts +12 -0
- package/dist/auth/guard.js +28 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/middleware.d.ts +23 -0
- package/dist/auth/middleware.js +89 -0
- package/dist/auth/native-auth.d.ts +73 -0
- package/dist/auth/native-auth.js +293 -0
- package/dist/auth/oidc-client.d.ts +17 -0
- package/dist/auth/oidc-client.js +123 -0
- package/dist/auth/providers/google.d.ts +23 -0
- package/dist/auth/providers/google.js +25 -0
- package/dist/auth/providers/index.d.ts +2 -0
- package/dist/auth/providers/index.js +1 -0
- package/dist/auth/routes/login.d.ts +8 -0
- package/dist/auth/routes/login.js +98 -0
- package/dist/auth/routes/logout.d.ts +4 -0
- package/dist/auth/routes/logout.js +79 -0
- package/dist/auth/routes/oidc-callback.d.ts +3 -0
- package/dist/auth/routes/oidc-callback.js +70 -0
- package/dist/auth/routes/password.d.ts +5 -0
- package/dist/auth/routes/password.js +149 -0
- package/dist/auth/routes/signup.d.ts +3 -0
- package/dist/auth/routes/signup.js +81 -0
- package/dist/auth/routes/token.d.ts +4 -0
- package/dist/auth/routes/token.js +70 -0
- package/dist/auth/routes/utils.d.ts +7 -0
- package/dist/auth/routes/utils.js +35 -0
- package/dist/auth/routes/verify.d.ts +3 -0
- package/dist/auth/routes/verify.js +26 -0
- package/dist/auth/routes.d.ts +8 -0
- package/dist/auth/routes.js +110 -0
- package/dist/auth/session.d.ts +8 -0
- package/dist/auth/session.js +54 -0
- package/dist/auth/token.d.ts +33 -0
- package/dist/auth/token.js +90 -0
- package/dist/auth/types.d.ts +156 -0
- package/dist/auth/types.js +2 -0
- package/dist/build/build-client.d.ts +15 -0
- package/dist/build/build-client.js +45 -0
- package/dist/build/build-prerender.d.ts +11 -0
- package/dist/build/build-prerender.js +159 -0
- package/dist/build/build-server.d.ts +17 -0
- package/dist/build/build-server.js +98 -0
- package/dist/build/build.js +52 -120
- package/dist/build/scan.d.ts +19 -0
- package/dist/build/scan.js +77 -6
- package/dist/build/serve-api.js +8 -2
- package/dist/build/serve-loaders.d.ts +4 -2
- package/dist/build/serve-loaders.js +128 -10
- package/dist/build/serve-ssr.js +38 -11
- package/dist/build/serve-static.js +3 -3
- package/dist/build/serve.js +229 -14
- package/dist/cli.js +37 -6
- package/dist/communication/encryption.d.ts +35 -0
- package/dist/communication/encryption.js +90 -0
- package/dist/communication/handlers/context.d.ts +27 -0
- package/dist/communication/handlers/context.js +1 -0
- package/dist/communication/handlers/conversation.d.ts +24 -0
- package/dist/communication/handlers/conversation.js +113 -0
- package/dist/communication/handlers/file-upload.d.ts +17 -0
- package/dist/communication/handlers/file-upload.js +62 -0
- package/dist/communication/handlers/messaging.d.ts +30 -0
- package/dist/communication/handlers/messaging.js +237 -0
- package/dist/communication/handlers/presence.d.ts +15 -0
- package/dist/communication/handlers/presence.js +76 -0
- package/dist/communication/handlers.d.ts +5 -0
- package/dist/communication/handlers.js +5 -0
- package/dist/communication/index.d.ts +9 -0
- package/dist/communication/index.js +7 -0
- package/dist/communication/link-preview.d.ts +18 -0
- package/dist/communication/link-preview.js +115 -0
- package/dist/communication/schema.d.ts +10 -0
- package/dist/communication/schema.js +101 -0
- package/dist/communication/server.d.ts +86 -0
- package/dist/communication/server.js +212 -0
- package/dist/communication/signaling.d.ts +43 -0
- package/dist/communication/signaling.js +271 -0
- package/dist/communication/store.d.ts +71 -0
- package/dist/communication/store.js +289 -0
- package/dist/communication/types.d.ts +454 -0
- package/dist/communication/types.js +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +55 -0
- package/dist/db/auto-migrate.d.ts +3 -0
- package/dist/db/auto-migrate.js +100 -0
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.js +18 -0
- package/dist/db/context.d.ts +2 -0
- package/dist/db/context.js +9 -0
- package/dist/db/index.d.ts +23 -0
- package/dist/db/index.js +258 -0
- package/dist/db/seed.d.ts +12 -0
- package/dist/db/seed.js +88 -0
- package/dist/db/table.d.ts +10 -0
- package/dist/db/table.js +12 -0
- package/dist/dev-server/config.d.ts +14 -0
- package/dist/dev-server/config.js +26 -9
- package/dist/dev-server/index-html.d.ts +3 -0
- package/dist/dev-server/index-html.js +18 -6
- package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
- package/dist/dev-server/nuralyui-aliases.js +115 -94
- package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
- package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
- package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
- package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
- package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
- package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
- package/dist/dev-server/plugins/vite-plugin-loaders.d.ts +0 -1
- package/dist/dev-server/plugins/vite-plugin-loaders.js +311 -42
- package/dist/dev-server/plugins/vite-plugin-routes.js +18 -6
- package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
- package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
- package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
- package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +111 -2
- package/dist/dev-server/server.js +128 -12
- package/dist/dev-server/ssr-render.d.ts +2 -1
- package/dist/dev-server/ssr-render.js +107 -48
- package/dist/editor/ai/backend.d.ts +20 -0
- package/dist/editor/ai/backend.js +104 -0
- package/dist/editor/ai/claude-code-client.d.ts +20 -0
- package/dist/editor/ai/claude-code-client.js +145 -0
- package/dist/editor/ai/opencode-client.d.ts +14 -0
- package/dist/editor/ai/opencode-client.js +125 -0
- package/dist/editor/ai/snapshot-store.d.ts +22 -0
- package/dist/editor/ai/snapshot-store.js +35 -0
- package/dist/editor/ai/types.d.ts +30 -0
- package/dist/editor/ai/types.js +136 -0
- package/dist/editor/ai-chat-panel.d.ts +13 -0
- package/dist/editor/ai-chat-panel.js +587 -0
- package/dist/editor/ai-markdown.d.ts +10 -0
- package/dist/editor/ai-markdown.js +70 -0
- package/dist/editor/ai-project-panel.d.ts +11 -0
- package/dist/editor/ai-project-panel.js +332 -0
- package/dist/editor/ast-modification.d.ts +11 -0
- package/dist/editor/ast-modification.js +1 -0
- package/dist/editor/ast-service.d.ts +30 -0
- package/dist/editor/ast-service.js +180 -0
- package/dist/editor/css-rules.d.ts +54 -0
- package/dist/editor/css-rules.js +423 -0
- package/dist/editor/editor-api-client.d.ts +51 -0
- package/dist/editor/editor-api-client.js +162 -0
- package/dist/editor/editor-bridge.d.ts +1 -0
- package/dist/editor/editor-bridge.js +17 -8
- package/dist/editor/editor-toolbar.d.ts +14 -0
- package/dist/editor/editor-toolbar.js +115 -0
- package/dist/editor/file-editor.d.ts +9 -0
- package/dist/editor/file-editor.js +236 -0
- package/dist/editor/file-service.d.ts +16 -0
- package/dist/editor/file-service.js +52 -0
- package/dist/editor/i18n-key-gen.d.ts +1 -0
- package/dist/editor/i18n-key-gen.js +7 -0
- package/dist/editor/inline-text-edit.d.ts +5 -0
- package/dist/editor/inline-text-edit.js +173 -92
- package/dist/editor/overlay-events.d.ts +5 -0
- package/dist/editor/overlay-events.js +364 -0
- package/dist/editor/overlay-hmr.d.ts +2 -0
- package/dist/editor/overlay-hmr.js +75 -0
- package/dist/editor/overlay-selection.d.ts +29 -0
- package/dist/editor/overlay-selection.js +148 -0
- package/dist/editor/overlay-utils.d.ts +12 -0
- package/dist/editor/overlay-utils.js +59 -0
- package/dist/editor/properties-panel-persist.d.ts +14 -0
- package/dist/editor/properties-panel-persist.js +70 -0
- package/dist/editor/properties-panel-rows.d.ts +10 -0
- package/dist/editor/properties-panel-rows.js +349 -0
- package/dist/editor/properties-panel-styles.d.ts +4 -0
- package/dist/editor/properties-panel-styles.js +174 -0
- package/dist/editor/properties-panel.d.ts +4 -0
- package/dist/editor/properties-panel.js +148 -0
- package/dist/editor/property-registry.d.ts +16 -0
- package/dist/editor/property-registry.js +303 -0
- package/dist/editor/standalone-file-panel.d.ts +0 -0
- package/dist/editor/standalone-file-panel.js +1 -0
- package/dist/editor/standalone-overlay-dom.d.ts +0 -0
- package/dist/editor/standalone-overlay-dom.js +1 -0
- package/dist/editor/standalone-overlay-styles.d.ts +0 -0
- package/dist/editor/standalone-overlay-styles.js +1 -0
- package/dist/editor/standalone-overlay.d.ts +1 -0
- package/dist/editor/standalone-overlay.js +76 -0
- package/dist/editor/syntax-highlighter.d.ts +4 -0
- package/dist/editor/syntax-highlighter.js +81 -0
- package/dist/editor/text-toolbar.d.ts +11 -0
- package/dist/editor/text-toolbar.js +327 -0
- package/dist/editor/toolbar-styles.d.ts +4 -0
- package/dist/editor/toolbar-styles.js +198 -0
- package/dist/email/index.d.ts +32 -0
- package/dist/email/index.js +154 -0
- package/dist/email/providers/resend.d.ts +2 -0
- package/dist/email/providers/resend.js +24 -0
- package/dist/email/providers/sendgrid.d.ts +2 -0
- package/dist/email/providers/sendgrid.js +31 -0
- package/dist/email/providers/smtp.d.ts +13 -0
- package/dist/email/providers/smtp.js +125 -0
- package/dist/email/template-engine.d.ts +18 -0
- package/dist/email/template-engine.js +116 -0
- package/dist/email/templates/base.d.ts +9 -0
- package/dist/email/templates/base.js +65 -0
- package/dist/email/templates/password-reset.d.ts +5 -0
- package/dist/email/templates/password-reset.js +15 -0
- package/dist/email/templates/verify-email.d.ts +5 -0
- package/dist/email/templates/verify-email.js +15 -0
- package/dist/email/templates/welcome.d.ts +5 -0
- package/dist/email/templates/welcome.js +13 -0
- package/dist/email/types.d.ts +49 -0
- package/dist/email/types.js +1 -0
- package/dist/llms/generate.d.ts +46 -0
- package/dist/llms/generate.js +185 -0
- package/dist/permissions/guard.d.ts +28 -0
- package/dist/permissions/guard.js +30 -0
- package/dist/permissions/index.d.ts +6 -0
- package/dist/permissions/index.js +3 -0
- package/dist/permissions/service.d.ts +80 -0
- package/dist/permissions/service.js +210 -0
- package/dist/permissions/tables.d.ts +5 -0
- package/dist/permissions/tables.js +68 -0
- package/dist/permissions/types.d.ts +33 -0
- package/dist/permissions/types.js +1 -0
- package/dist/runtime/app-shell.js +163 -0
- package/dist/runtime/auth.d.ts +10 -0
- package/dist/runtime/auth.js +30 -0
- package/dist/runtime/communication.d.ts +137 -0
- package/dist/runtime/communication.js +228 -0
- package/dist/runtime/error-boundary.d.ts +23 -0
- package/dist/runtime/error-boundary.js +120 -0
- package/dist/runtime/i18n.d.ts +6 -1
- package/dist/runtime/i18n.js +42 -21
- package/dist/runtime/router-data.d.ts +5 -0
- package/dist/runtime/router-data.js +121 -16
- package/dist/runtime/router-hydration.js +25 -0
- package/dist/runtime/router.d.ts +21 -1
- package/dist/runtime/router.js +221 -39
- package/dist/runtime/socket-client.d.ts +2 -0
- package/dist/runtime/socket-client.js +30 -0
- package/dist/runtime/webrtc.d.ts +47 -0
- package/dist/runtime/webrtc.js +178 -0
- package/dist/shared/graceful-shutdown.d.ts +8 -0
- package/dist/shared/graceful-shutdown.js +36 -0
- package/dist/shared/health.d.ts +8 -0
- package/dist/shared/health.js +25 -0
- package/dist/shared/llms-txt.d.ts +31 -0
- package/dist/shared/llms-txt.js +85 -0
- package/dist/shared/logger.d.ts +32 -0
- package/dist/shared/logger.js +93 -0
- package/dist/shared/meta.d.ts +27 -0
- package/dist/shared/meta.js +71 -0
- package/dist/shared/middleware-runner.d.ts +9 -0
- package/dist/shared/middleware-runner.js +29 -0
- package/dist/shared/rate-limit.d.ts +18 -0
- package/dist/shared/rate-limit.js +71 -0
- package/dist/shared/request-id.d.ts +5 -0
- package/dist/shared/request-id.js +18 -0
- package/dist/shared/route-matching.js +16 -1
- package/dist/shared/security-headers.d.ts +18 -0
- package/dist/shared/security-headers.js +38 -0
- package/dist/shared/socket-io-setup.d.ts +11 -0
- package/dist/shared/socket-io-setup.js +51 -0
- package/dist/shared/types.d.ts +16 -0
- package/dist/shared/utils.d.ts +37 -7
- package/dist/shared/utils.js +175 -26
- package/dist/storage/adapters/local.d.ts +44 -0
- package/dist/storage/adapters/local.js +85 -0
- package/dist/storage/adapters/s3.d.ts +32 -0
- package/dist/storage/adapters/s3.js +116 -0
- package/dist/storage/adapters/types.d.ts +53 -0
- package/dist/storage/adapters/types.js +1 -0
- package/dist/storage/index.d.ts +76 -0
- package/dist/storage/index.js +83 -0
- package/package.json +20 -1
- package/templates/blog/api/posts.ts +6 -0
- package/templates/blog/data/migrations/001_init.sql +13 -0
- package/templates/blog/lumenjs.config.ts +3 -0
- package/templates/blog/package.json +14 -0
- package/templates/blog/pages/_layout.ts +25 -0
- package/templates/blog/pages/index.ts +65 -0
- package/templates/blog/pages/posts/[slug].ts +60 -0
- package/templates/blog/pages/tag/[tag].ts +44 -0
- package/templates/dashboard/api/stats.ts +10 -0
- package/templates/dashboard/data/migrations/001_init.sql +13 -0
- package/templates/dashboard/lumenjs.config.ts +3 -0
- package/templates/dashboard/package.json +14 -0
- package/templates/dashboard/pages/_layout.ts +25 -0
- package/templates/dashboard/pages/index.ts +72 -0
- package/templates/dashboard/pages/settings/index.ts +29 -0
- package/templates/default/lumenjs.config.ts +3 -0
- package/templates/default/package.json +14 -0
- package/templates/default/pages/index.ts +24 -0
- package/templates/social/api/posts/[id].ts +14 -0
- package/templates/social/api/posts.ts +11 -0
- package/templates/social/api/profile/[username].ts +10 -0
- package/templates/social/api/upload.ts +19 -0
- package/templates/social/data/migrations/001_init.sql +78 -0
- package/templates/social/data/migrations/002_add_image_url.sql +1 -0
- package/templates/social/data/migrations/003_auth.sql +7 -0
- package/templates/social/docs/architecture.md +76 -0
- package/templates/social/docs/components.md +100 -0
- package/templates/social/docs/data.md +89 -0
- package/templates/social/docs/pages.md +96 -0
- package/templates/social/docs/theming.md +52 -0
- package/templates/social/lib/media.ts +130 -0
- package/templates/social/lumenjs.auth.ts +21 -0
- package/templates/social/lumenjs.config.ts +3 -0
- package/templates/social/package.json +5 -0
- package/templates/social/pages/_layout.ts +239 -0
- package/templates/social/pages/apps/[id].ts +173 -0
- package/templates/social/pages/apps/index.ts +116 -0
- package/templates/social/pages/auth/login.ts +92 -0
- package/templates/social/pages/bookmarks.ts +57 -0
- package/templates/social/pages/explore.ts +73 -0
- package/templates/social/pages/index.ts +351 -0
- package/templates/social/pages/messages.ts +298 -0
- package/templates/social/pages/new.ts +77 -0
- package/templates/social/pages/notifications.ts +73 -0
- package/templates/social/pages/post/[id].ts +124 -0
- package/templates/social/pages/profile/[username].ts +100 -0
- package/templates/social/pages/settings/accessibility.ts +153 -0
- package/templates/social/pages/settings/account.ts +260 -0
- package/templates/social/pages/settings/help.ts +141 -0
- package/templates/social/pages/settings/language.ts +103 -0
- package/templates/social/pages/settings/privacy.ts +183 -0
- package/templates/social/pages/settings/security.ts +133 -0
- package/templates/social/pages/settings.ts +185 -0
package/dist/build/serve.js
CHANGED
|
@@ -5,11 +5,23 @@ import { readProjectConfig } from '../dev-server/config.js';
|
|
|
5
5
|
import { installDomShims } from '../shared/dom-shims.js';
|
|
6
6
|
import { serveStaticFile, sendCompressed } from './serve-static.js';
|
|
7
7
|
import { handleApiRoute } from './serve-api.js';
|
|
8
|
-
import { handleLoaderRequest, handleLayoutLoaderRequest } from './serve-loaders.js';
|
|
8
|
+
import { handleLoaderRequest, handleLayoutLoaderRequest, handleSubscribeRequest, handleLayoutSubscribeRequest } from './serve-loaders.js';
|
|
9
9
|
import { handlePageRoute } from './serve-ssr.js';
|
|
10
10
|
import { renderErrorPage } from './error-page.js';
|
|
11
11
|
import { handleI18nRequest } from './serve-i18n.js';
|
|
12
12
|
import { resolveLocale } from '../dev-server/middleware/locale.js';
|
|
13
|
+
import { runMiddlewareChain, extractMiddleware } from '../shared/middleware-runner.js';
|
|
14
|
+
import { getMiddlewareDirsForPathname } from './scan.js';
|
|
15
|
+
import { createAuthMiddleware } from '../auth/middleware.js';
|
|
16
|
+
import { handleAuthRoutes } from '../auth/routes.js';
|
|
17
|
+
import { loadAuthConfigProd } from '../auth/config.js';
|
|
18
|
+
import { initLogger, logger } from '../shared/logger.js';
|
|
19
|
+
import { createSecurityHeadersMiddleware } from '../shared/security-headers.js';
|
|
20
|
+
import { createRateLimiter, createAuthRateLimiter } from '../shared/rate-limit.js';
|
|
21
|
+
import { createHealthCheckHandler } from '../shared/health.js';
|
|
22
|
+
import { createRequestIdMiddleware } from '../shared/request-id.js';
|
|
23
|
+
import { getRequestId } from '../shared/request-id.js';
|
|
24
|
+
import { setupGracefulShutdown } from '../shared/graceful-shutdown.js';
|
|
13
25
|
export async function serveProject(options) {
|
|
14
26
|
const { projectDir } = options;
|
|
15
27
|
const port = options.port || 3000;
|
|
@@ -17,35 +29,198 @@ export async function serveProject(options) {
|
|
|
17
29
|
const clientDir = path.join(outDir, 'client');
|
|
18
30
|
const serverDir = path.join(outDir, 'server');
|
|
19
31
|
const manifestPath = path.join(outDir, 'manifest.json');
|
|
32
|
+
// Initialize structured logging
|
|
33
|
+
initLogger();
|
|
20
34
|
if (!fs.existsSync(manifestPath)) {
|
|
21
|
-
|
|
35
|
+
logger.fatal('No build found. Run `lumenjs build` first.');
|
|
22
36
|
process.exit(1);
|
|
23
37
|
}
|
|
24
38
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
25
|
-
const
|
|
39
|
+
const config = readProjectConfig(projectDir);
|
|
40
|
+
const { title } = config;
|
|
26
41
|
const localesDir = path.join(outDir, 'locales');
|
|
42
|
+
// Production middleware stack
|
|
43
|
+
const requestIdMiddleware = createRequestIdMiddleware();
|
|
44
|
+
const securityHeaders = createSecurityHeadersMiddleware(config.securityHeaders);
|
|
45
|
+
const rateLimiter = createRateLimiter(config.rateLimit);
|
|
46
|
+
const authRateLimiter = createAuthRateLimiter();
|
|
47
|
+
const healthCheck = createHealthCheckHandler({ version: config.version });
|
|
27
48
|
// Read the built index.html shell
|
|
28
49
|
const indexHtmlPath = path.join(clientDir, 'index.html');
|
|
29
50
|
if (!fs.existsSync(indexHtmlPath)) {
|
|
30
|
-
|
|
51
|
+
logger.fatal('No index.html found in build output.');
|
|
31
52
|
process.exit(1);
|
|
32
53
|
}
|
|
33
54
|
const indexHtmlShell = fs.readFileSync(indexHtmlPath, 'utf-8');
|
|
34
|
-
// Load bundled SSR runtime first —
|
|
35
|
-
//
|
|
55
|
+
// Load bundled SSR runtime first — its install-global-dom-shim sets up
|
|
56
|
+
// the proper HTMLElement/window/document shims that @lit-labs/ssr needs.
|
|
57
|
+
// The lit-shared chunk handles missing HTMLElement via a fallback to its own shim,
|
|
58
|
+
// so no pre-installation is needed.
|
|
36
59
|
const ssrRuntimePath = path.join(serverDir, 'ssr-runtime.js');
|
|
37
60
|
let ssrRuntime = null;
|
|
38
61
|
if (fs.existsSync(ssrRuntimePath)) {
|
|
39
62
|
ssrRuntime = await import(ssrRuntimePath);
|
|
40
63
|
}
|
|
41
64
|
// Install additional DOM shims that NuralyUI components may need
|
|
65
|
+
// (must run AFTER SSR runtime so we don't block its window/HTMLElement setup)
|
|
42
66
|
installDomShims();
|
|
43
67
|
const pagesDir = path.join(projectDir, 'pages');
|
|
68
|
+
// Load bundled middleware at startup
|
|
69
|
+
const middlewareModules = new Map();
|
|
70
|
+
if (manifest.middlewares) {
|
|
71
|
+
for (const entry of manifest.middlewares) {
|
|
72
|
+
const modPath = path.join(serverDir, entry.module);
|
|
73
|
+
if (fs.existsSync(modPath)) {
|
|
74
|
+
try {
|
|
75
|
+
const mod = await import(modPath);
|
|
76
|
+
middlewareModules.set(entry.dir, extractMiddleware(mod));
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
logger.error(`Failed to load middleware (${entry.dir || 'root'})`, { error: err?.message });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const middlewareEntries = manifest.middlewares
|
|
85
|
+
? manifest.middlewares.map(e => ({ dir: e.dir, filePath: '' }))
|
|
86
|
+
: [];
|
|
87
|
+
// Load auth config if present
|
|
88
|
+
let authConfig = null;
|
|
89
|
+
let authMiddleware = null;
|
|
90
|
+
let authDb = null;
|
|
91
|
+
if (manifest.auth) {
|
|
92
|
+
try {
|
|
93
|
+
authConfig = await loadAuthConfigProd(serverDir, manifest.auth.configModule);
|
|
94
|
+
// Initialize DB for native auth
|
|
95
|
+
const { hasNativeAuth } = await import('../auth/config.js');
|
|
96
|
+
if (hasNativeAuth(authConfig)) {
|
|
97
|
+
try {
|
|
98
|
+
const { setProjectDir } = await import('../db/context.js');
|
|
99
|
+
const { useDb, waitForMigrations } = await import('../db/index.js');
|
|
100
|
+
const { ensureUsersTable } = await import('../auth/native-auth.js');
|
|
101
|
+
setProjectDir(projectDir);
|
|
102
|
+
authDb = useDb();
|
|
103
|
+
await waitForMigrations();
|
|
104
|
+
await ensureUsersTable(authDb);
|
|
105
|
+
// Run seed if not yet applied (SQLite and PG)
|
|
106
|
+
{
|
|
107
|
+
const seedModule = path.join(serverDir, 'seed.js');
|
|
108
|
+
if (fs.existsSync(seedModule)) {
|
|
109
|
+
try {
|
|
110
|
+
if (authDb.isPg) {
|
|
111
|
+
await authDb.exec(`CREATE TABLE IF NOT EXISTS _lumen_seed_applied (
|
|
112
|
+
name TEXT PRIMARY KEY,
|
|
113
|
+
applied_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
114
|
+
)`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
await authDb.exec(`CREATE TABLE IF NOT EXISTS _lumen_seed_applied (
|
|
118
|
+
name TEXT PRIMARY KEY,
|
|
119
|
+
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
120
|
+
)`);
|
|
121
|
+
}
|
|
122
|
+
const seedRow = authDb.isPg
|
|
123
|
+
? await authDb.get('SELECT 1 FROM _lumen_seed_applied WHERE name = $1', 'data/seed.ts')
|
|
124
|
+
: await authDb.get('SELECT 1 FROM _lumen_seed_applied WHERE name = ?', 'data/seed.ts');
|
|
125
|
+
if (!seedRow) {
|
|
126
|
+
if (authDb.isPg) {
|
|
127
|
+
await authDb.run('INSERT INTO _lumen_seed_applied (name) VALUES ($1) ON CONFLICT DO NOTHING', 'data/seed.ts');
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
await authDb.run('INSERT OR IGNORE INTO _lumen_seed_applied (name) VALUES (?)', 'data/seed.ts');
|
|
131
|
+
}
|
|
132
|
+
logger.info('Running seed file (prod)...');
|
|
133
|
+
const { default: seedFn } = await import(seedModule);
|
|
134
|
+
if (typeof seedFn === 'function')
|
|
135
|
+
await seedFn();
|
|
136
|
+
logger.info('Seed applied (prod).');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (seedErr) {
|
|
140
|
+
try {
|
|
141
|
+
if (authDb.isPg) {
|
|
142
|
+
await authDb.run('DELETE FROM _lumen_seed_applied WHERE name = $1', 'data/seed.ts');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
await authDb.run('DELETE FROM _lumen_seed_applied WHERE name = ?', 'data/seed.ts');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
logger.warn('Seed failed', { error: seedErr?.message });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (dbErr) {
|
|
155
|
+
logger.warn('Native auth DB init failed', { error: dbErr?.message });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
authMiddleware = createAuthMiddleware(authConfig, authDb);
|
|
159
|
+
logger.info('Auth middleware loaded.');
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
logger.error('Failed to load auth config', { error: err?.message });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const runMiddleware = (mw, req, res) => new Promise((resolve, reject) => mw(req, res, (err) => err ? reject(err) : resolve()));
|
|
44
166
|
const server = http.createServer(async (req, res) => {
|
|
167
|
+
const startTime = Date.now();
|
|
45
168
|
const url = req.url || '/';
|
|
46
169
|
const [pathname, queryString] = url.split('?');
|
|
47
170
|
const method = req.method || 'GET';
|
|
48
171
|
try {
|
|
172
|
+
// --- Production middleware pipeline ---
|
|
173
|
+
// Request ID (always first)
|
|
174
|
+
await runMiddleware(requestIdMiddleware, req, res);
|
|
175
|
+
// Health check (bypass everything else)
|
|
176
|
+
let healthHandled = false;
|
|
177
|
+
await new Promise(resolve => healthCheck(req, res, () => { resolve(); }));
|
|
178
|
+
if (res.writableEnded)
|
|
179
|
+
return;
|
|
180
|
+
// Security headers
|
|
181
|
+
await runMiddleware(securityHeaders, req, res);
|
|
182
|
+
// Rate limiting (stricter for auth routes)
|
|
183
|
+
if (pathname.startsWith('/__nk_auth/')) {
|
|
184
|
+
await runMiddleware(authRateLimiter, req, res);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
await runMiddleware(rateLimiter, req, res);
|
|
188
|
+
}
|
|
189
|
+
if (res.writableEnded)
|
|
190
|
+
return;
|
|
191
|
+
// --- Original request handling ---
|
|
192
|
+
// -2. Auth session middleware (attach req.nkAuth — must run before auth routes and user middleware)
|
|
193
|
+
if (authMiddleware && !pathname.includes('.') && !pathname.startsWith('/@')) {
|
|
194
|
+
await new Promise(resolve => authMiddleware(req, res, resolve));
|
|
195
|
+
if (res.writableEnded)
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// -1. Auth routes (login, logout, me, signup, etc.)
|
|
199
|
+
if (authConfig && pathname.startsWith('/__nk_auth/')) {
|
|
200
|
+
const handled = await handleAuthRoutes(authConfig, req, res, authDb);
|
|
201
|
+
if (handled)
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
// 0. Run user middleware chain
|
|
205
|
+
if (middlewareModules.size > 0 && !pathname.includes('.') && !pathname.startsWith('/__nk_')) {
|
|
206
|
+
const matching = getMiddlewareDirsForPathname(pathname, middlewareEntries);
|
|
207
|
+
const allMw = [];
|
|
208
|
+
for (const entry of matching) {
|
|
209
|
+
const mws = middlewareModules.get(entry.dir);
|
|
210
|
+
if (mws)
|
|
211
|
+
allMw.push(...mws);
|
|
212
|
+
}
|
|
213
|
+
if (allMw.length > 0) {
|
|
214
|
+
const err = await new Promise(resolve => runMiddlewareChain(allMw, req, res, resolve));
|
|
215
|
+
if (err) {
|
|
216
|
+
res.statusCode = 500;
|
|
217
|
+
res.end('Internal Server Error');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (res.writableEnded)
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
49
224
|
// 1. API routes
|
|
50
225
|
if (pathname.startsWith('/api/')) {
|
|
51
226
|
await handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res);
|
|
@@ -62,17 +237,28 @@ export async function serveProject(options) {
|
|
|
62
237
|
handleI18nRequest(localesDir, manifest.i18n.locales, pathname, req, res);
|
|
63
238
|
return;
|
|
64
239
|
}
|
|
65
|
-
// 4. Layout
|
|
240
|
+
// 4. Layout subscribe endpoint (SSE)
|
|
241
|
+
if (pathname === '/__nk_subscribe/__layout/' || pathname === '/__nk_subscribe/__layout') {
|
|
242
|
+
const authUser = req.nkAuth?.user ?? undefined;
|
|
243
|
+
await handleLayoutSubscribeRequest(manifest, serverDir, queryString, req.headers, res, authUser);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
// 5. Subscribe endpoint (SSE)
|
|
247
|
+
if (pathname.startsWith('/__nk_subscribe/')) {
|
|
248
|
+
await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// 6. Layout loader endpoint
|
|
66
252
|
if (pathname === '/__nk_loader/__layout/' || pathname === '/__nk_loader/__layout') {
|
|
67
|
-
await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res);
|
|
253
|
+
await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
|
|
68
254
|
return;
|
|
69
255
|
}
|
|
70
|
-
//
|
|
256
|
+
// 7. Loader endpoint for client-side navigation
|
|
71
257
|
if (pathname.startsWith('/__nk_loader/')) {
|
|
72
|
-
await handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res);
|
|
258
|
+
await handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
|
|
73
259
|
return;
|
|
74
260
|
}
|
|
75
|
-
//
|
|
261
|
+
// 8. Resolve locale and strip prefix for page routing
|
|
76
262
|
let resolvedPathname = pathname;
|
|
77
263
|
let locale;
|
|
78
264
|
if (manifest.i18n) {
|
|
@@ -80,16 +266,45 @@ export async function serveProject(options) {
|
|
|
80
266
|
resolvedPathname = result.pathname;
|
|
81
267
|
locale = result.locale;
|
|
82
268
|
}
|
|
83
|
-
//
|
|
269
|
+
// 9. Check for pre-rendered HTML file
|
|
270
|
+
const prerenderFile = path.join(clientDir, resolvedPathname === '/' ? '' : resolvedPathname, 'index.html');
|
|
271
|
+
if (resolvedPathname !== '/' && fs.existsSync(prerenderFile)) {
|
|
272
|
+
const prerenderHtml = fs.readFileSync(prerenderFile, 'utf-8');
|
|
273
|
+
sendCompressed(req, res, 200, 'text/html; charset=utf-8', prerenderHtml);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
// Check if the root index.html is a pre-rendered page (has data-nk-ssr attribute)
|
|
277
|
+
if (resolvedPathname === '/') {
|
|
278
|
+
const rootRoute = manifest.routes.find(r => r.path === '/');
|
|
279
|
+
if (rootRoute?.prerender) {
|
|
280
|
+
sendCompressed(req, res, 200, 'text/html; charset=utf-8', indexHtmlShell);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// 10. Page routes — SSR render
|
|
84
285
|
await handlePageRoute(manifest, serverDir, pagesDir, resolvedPathname, queryString, indexHtmlShell, title, ssrRuntime, req, res);
|
|
85
286
|
}
|
|
86
287
|
catch (err) {
|
|
87
|
-
|
|
288
|
+
logger.error('Request error', {
|
|
289
|
+
method, url: pathname, error: err?.message, stack: err?.stack,
|
|
290
|
+
requestId: getRequestId(req),
|
|
291
|
+
});
|
|
88
292
|
const html = renderErrorPage(500, 'Something went wrong', 'An unexpected error occurred while processing your request.', process.env.NODE_ENV !== 'production' ? err?.stack || err?.message : undefined);
|
|
89
293
|
sendCompressed(req, res, 500, 'text/html; charset=utf-8', html);
|
|
90
294
|
}
|
|
295
|
+
finally {
|
|
296
|
+
// Log request completion
|
|
297
|
+
const duration = Date.now() - startTime;
|
|
298
|
+
logger.request(req, res.statusCode, duration, { requestId: getRequestId(req) });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
// Graceful shutdown
|
|
302
|
+
setupGracefulShutdown(server, {
|
|
303
|
+
onShutdown: async () => {
|
|
304
|
+
logger.info('Cleaning up resources...');
|
|
305
|
+
},
|
|
91
306
|
});
|
|
92
307
|
server.listen(port, () => {
|
|
93
|
-
|
|
308
|
+
logger.info(`Production server running at http://localhost:${port}`, { port });
|
|
94
309
|
});
|
|
95
310
|
}
|
package/dist/cli.js
CHANGED
|
@@ -9,19 +9,32 @@ function getArg(name) {
|
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
11
11
|
const USAGE = `Usage:
|
|
12
|
-
lumenjs
|
|
13
|
-
lumenjs
|
|
14
|
-
lumenjs
|
|
15
|
-
lumenjs
|
|
16
|
-
|
|
12
|
+
lumenjs create <name> [--template <default|blog|dashboard|social>]
|
|
13
|
+
lumenjs dev [--project <dir>] [--port <port>] [--base <path>] [--editor-mode]
|
|
14
|
+
lumenjs build [--project <dir>] [--out <dir>]
|
|
15
|
+
lumenjs serve [--project <dir>] [--port <port>]
|
|
16
|
+
lumenjs add <integration>
|
|
17
|
+
lumenjs db seed [--force] [--project <dir>]`;
|
|
18
|
+
if (!command || !['create', 'dev', 'build', 'serve', 'add', 'db'].includes(command)) {
|
|
17
19
|
console.error(USAGE);
|
|
18
20
|
process.exit(1);
|
|
19
21
|
}
|
|
20
22
|
const projectDir = path.resolve(getArg('project') || '.');
|
|
21
23
|
async function main() {
|
|
22
|
-
if (command === '
|
|
24
|
+
if (command === 'create') {
|
|
25
|
+
const { createProject } = await import('./create.js');
|
|
26
|
+
const name = args[1];
|
|
27
|
+
const template = getArg('template') || 'default';
|
|
28
|
+
await createProject(name, template);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
else if (command === 'dev') {
|
|
23
32
|
const { createDevServer } = await import('./dev-server/server.js');
|
|
24
33
|
const port = parseInt(getArg('port') || '3000', 10);
|
|
34
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
35
|
+
console.error(`Invalid port number. Must be between 1 and 65535.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
25
38
|
const editorMode = args.includes('--editor-mode');
|
|
26
39
|
const base = getArg('base') || '/';
|
|
27
40
|
console.log(`[LumenJS] Starting dev server...`);
|
|
@@ -53,11 +66,29 @@ async function main() {
|
|
|
53
66
|
else if (command === 'serve') {
|
|
54
67
|
const { serveProject } = await import('./build/serve.js');
|
|
55
68
|
const port = parseInt(getArg('port') || '3000', 10);
|
|
69
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
70
|
+
console.error(`Invalid port number. Must be between 1 and 65535.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
56
73
|
console.log(`[LumenJS] Starting production server...`);
|
|
57
74
|
console.log(` Project: ${projectDir}`);
|
|
58
75
|
console.log(` Port: ${port}`);
|
|
59
76
|
await serveProject({ projectDir, port });
|
|
60
77
|
}
|
|
78
|
+
else if (command === 'db') {
|
|
79
|
+
const subcommand = args[1];
|
|
80
|
+
if (subcommand === 'seed') {
|
|
81
|
+
const { setProjectDir } = await import('./db/context.js');
|
|
82
|
+
const { runSeed } = await import('./db/seed.js');
|
|
83
|
+
setProjectDir(projectDir);
|
|
84
|
+
const force = args.includes('--force');
|
|
85
|
+
await runSeed(projectDir, force);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.error(`Unknown db subcommand: ${subcommand}\n\n${USAGE}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
61
92
|
}
|
|
62
93
|
main().catch(err => {
|
|
63
94
|
console.error('[LumenJS] Failed to start:', err);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { KeyBundle, EncryptedEnvelope } from './types.js';
|
|
2
|
+
import type { CommunicationStore } from './store.js';
|
|
3
|
+
import type { LumenDb } from '../db/index.js';
|
|
4
|
+
/** Context for encryption handlers */
|
|
5
|
+
export interface EncryptionContext {
|
|
6
|
+
userId: string;
|
|
7
|
+
store: CommunicationStore;
|
|
8
|
+
/** Emit data to the current socket */
|
|
9
|
+
push: (data: any) => void;
|
|
10
|
+
/** Emit to a specific user's sockets */
|
|
11
|
+
emitToUser: (targetUserId: string, data: any) => void;
|
|
12
|
+
/** LumenJS database instance (optional) */
|
|
13
|
+
db?: LumenDb;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Handle a client uploading their public key bundle.
|
|
17
|
+
* Server stores the bundle — it never sees private keys.
|
|
18
|
+
*/
|
|
19
|
+
export declare function handleUploadKeys(ctx: EncryptionContext, data: KeyBundle): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Handle a client requesting another user's key bundle for session setup.
|
|
22
|
+
* Pops one one-time pre-key (consumed once).
|
|
23
|
+
*/
|
|
24
|
+
export declare function handleRequestKeys(ctx: EncryptionContext, data: {
|
|
25
|
+
recipientId: string;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Relay a session initialization message to the recipient.
|
|
29
|
+
* The server never reads the envelope — just forwards it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function handleSessionInit(ctx: EncryptionContext, data: {
|
|
32
|
+
recipientId: string;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
envelope: EncryptedEnvelope;
|
|
35
|
+
}): void;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ── Key Upload ──────────────────────────────────────────────────
|
|
2
|
+
/**
|
|
3
|
+
* Handle a client uploading their public key bundle.
|
|
4
|
+
* Server stores the bundle — it never sees private keys.
|
|
5
|
+
*/
|
|
6
|
+
export async function handleUploadKeys(ctx, data) {
|
|
7
|
+
// Ensure the bundle belongs to the sender
|
|
8
|
+
const bundle = {
|
|
9
|
+
...data,
|
|
10
|
+
userId: ctx.userId,
|
|
11
|
+
uploadedAt: new Date().toISOString(),
|
|
12
|
+
};
|
|
13
|
+
// Store in memory
|
|
14
|
+
ctx.store.setKeyBundle(ctx.userId, bundle);
|
|
15
|
+
// Persist to DB if available
|
|
16
|
+
if (ctx.db) {
|
|
17
|
+
await ctx.db.run(`INSERT OR REPLACE INTO encryption_keys (user_id, identity_key, signed_pre_key_id, signed_pre_key, signed_pre_key_signature, uploaded_at)
|
|
18
|
+
VALUES (?, ?, ?, ?, ?, ?)`, ctx.userId, bundle.identityKey, bundle.signedPreKey.keyId, bundle.signedPreKey.publicKey, bundle.signedPreKeySignature, bundle.uploadedAt);
|
|
19
|
+
// Replace one-time pre-keys
|
|
20
|
+
await ctx.db.run(`DELETE FROM encryption_prekeys WHERE user_id = ?`, ctx.userId);
|
|
21
|
+
for (const otk of bundle.oneTimePreKeys) {
|
|
22
|
+
await ctx.db.run(`INSERT INTO encryption_prekeys (user_id, key_id, public_key) VALUES (?, ?, ?)`, ctx.userId, otk.keyId, otk.publicKey);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
ctx.push({ event: 'encryption:keys-uploaded', data: { userId: ctx.userId, keyCount: bundle.oneTimePreKeys.length } });
|
|
26
|
+
}
|
|
27
|
+
// ── Key Request ─────────────────────────────────────────────────
|
|
28
|
+
/**
|
|
29
|
+
* Handle a client requesting another user's key bundle for session setup.
|
|
30
|
+
* Pops one one-time pre-key (consumed once).
|
|
31
|
+
*/
|
|
32
|
+
export async function handleRequestKeys(ctx, data) {
|
|
33
|
+
let bundle = ctx.store.getKeyBundle(data.recipientId);
|
|
34
|
+
// Try loading from DB if not in memory
|
|
35
|
+
if (!bundle && ctx.db) {
|
|
36
|
+
const row = await ctx.db.get(`SELECT * FROM encryption_keys WHERE user_id = ?`, data.recipientId);
|
|
37
|
+
if (row) {
|
|
38
|
+
const prekeys = await ctx.db.all(`SELECT key_id, public_key FROM encryption_prekeys WHERE user_id = ? ORDER BY key_id`, data.recipientId);
|
|
39
|
+
bundle = {
|
|
40
|
+
userId: data.recipientId,
|
|
41
|
+
identityKey: row.identity_key,
|
|
42
|
+
signedPreKey: { keyId: row.signed_pre_key_id, publicKey: row.signed_pre_key },
|
|
43
|
+
signedPreKeySignature: row.signed_pre_key_signature,
|
|
44
|
+
oneTimePreKeys: prekeys.map((pk) => ({ keyId: pk.key_id, publicKey: pk.public_key })),
|
|
45
|
+
uploadedAt: row.uploaded_at,
|
|
46
|
+
};
|
|
47
|
+
ctx.store.setKeyBundle(data.recipientId, bundle);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!bundle) {
|
|
51
|
+
ctx.push({ event: 'encryption:keys-response', data: { error: 'no_keys', recipientId: data.recipientId } });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Pop one one-time pre-key (consumed once per session)
|
|
55
|
+
const oneTimePreKey = ctx.store.popOneTimePreKey(data.recipientId);
|
|
56
|
+
// Remove from DB too
|
|
57
|
+
if (oneTimePreKey && ctx.db) {
|
|
58
|
+
await ctx.db.run(`DELETE FROM encryption_prekeys WHERE user_id = ? AND key_id = ?`, data.recipientId, oneTimePreKey.keyId);
|
|
59
|
+
}
|
|
60
|
+
const response = {
|
|
61
|
+
recipientId: data.recipientId,
|
|
62
|
+
identityKey: bundle.identityKey,
|
|
63
|
+
signedPreKey: bundle.signedPreKey,
|
|
64
|
+
signedPreKeySignature: bundle.signedPreKeySignature,
|
|
65
|
+
oneTimePreKey,
|
|
66
|
+
};
|
|
67
|
+
ctx.push({ event: 'encryption:keys-response', data: response });
|
|
68
|
+
// Notify recipient if their one-time pre-keys are depleted
|
|
69
|
+
if (ctx.store.getOneTimePreKeyCount(data.recipientId) === 0) {
|
|
70
|
+
ctx.emitToUser(data.recipientId, {
|
|
71
|
+
event: 'encryption:keys-depleted',
|
|
72
|
+
data: { userId: data.recipientId },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ── Session Init Relay ──────────────────────────────────────────
|
|
77
|
+
/**
|
|
78
|
+
* Relay a session initialization message to the recipient.
|
|
79
|
+
* The server never reads the envelope — just forwards it.
|
|
80
|
+
*/
|
|
81
|
+
export function handleSessionInit(ctx, data) {
|
|
82
|
+
ctx.emitToUser(data.recipientId, {
|
|
83
|
+
event: 'encryption:session-init',
|
|
84
|
+
data: {
|
|
85
|
+
senderId: ctx.userId,
|
|
86
|
+
sessionId: data.sessionId,
|
|
87
|
+
envelope: data.envelope,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CommunicationConfig } from '../types.js';
|
|
2
|
+
import type { CommunicationStore } from '../store.js';
|
|
3
|
+
import type { LumenDb } from '../../db/index.js';
|
|
4
|
+
import type { StorageAdapter } from '../../storage/adapters/types.js';
|
|
5
|
+
/** Context passed to every handler from the socket connection */
|
|
6
|
+
export interface HandlerContext {
|
|
7
|
+
userId: string;
|
|
8
|
+
store: CommunicationStore;
|
|
9
|
+
/** Resolved communication config */
|
|
10
|
+
config: CommunicationConfig;
|
|
11
|
+
/** Emit data to the current socket */
|
|
12
|
+
push: (data: any) => void;
|
|
13
|
+
/** Broadcast to all sockets in a room */
|
|
14
|
+
broadcastAll: (room: string, data: any) => void;
|
|
15
|
+
/** Broadcast to all sockets in a room except the sender */
|
|
16
|
+
broadcast: (room: string, data: any) => void;
|
|
17
|
+
/** Join a socket room */
|
|
18
|
+
joinRoom: (room: string) => void;
|
|
19
|
+
/** Leave a socket room */
|
|
20
|
+
leaveRoom: (room: string) => void;
|
|
21
|
+
/** Emit data to all sockets for a specific user */
|
|
22
|
+
emitToUser?: (userId: string, data: any) => void;
|
|
23
|
+
/** LumenJS database instance (optional — only if app has DB set up) */
|
|
24
|
+
db?: LumenDb;
|
|
25
|
+
/** Storage adapter for file uploads (optional — only if storage is configured) */
|
|
26
|
+
storage?: StorageAdapter;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HandlerContext } from './context.js';
|
|
2
|
+
export declare function handleConversationCreate(ctx: HandlerContext, data: {
|
|
3
|
+
type: 'direct' | 'group';
|
|
4
|
+
name?: string;
|
|
5
|
+
participantIds: string[];
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function handleConversationJoin(ctx: HandlerContext, data: {
|
|
8
|
+
conversationId: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function handleConversationLeave(ctx: HandlerContext, data: {
|
|
11
|
+
conversationId: string;
|
|
12
|
+
}): void;
|
|
13
|
+
export declare function handleConversationArchive(ctx: HandlerContext, data: {
|
|
14
|
+
conversationId: string;
|
|
15
|
+
archived: boolean;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
export declare function handleConversationMute(ctx: HandlerContext, data: {
|
|
18
|
+
conversationId: string;
|
|
19
|
+
muted: boolean;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
export declare function handleConversationPin(ctx: HandlerContext, data: {
|
|
22
|
+
conversationId: string;
|
|
23
|
+
pinned: boolean;
|
|
24
|
+
}): Promise<void>;
|