@nuraly/lumenjs 0.1.3 → 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 +62 -282
- 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 +48 -120
- package/dist/build/scan.d.ts +17 -0
- package/dist/build/scan.js +76 -6
- package/dist/build/serve-api.js +8 -2
- package/dist/build/serve-loaders.d.ts +4 -4
- package/dist/build/serve-loaders.js +26 -18
- package/dist/build/serve-ssr.js +38 -11
- package/dist/build/serve-static.js +3 -3
- package/dist/build/serve.js +218 -15
- 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/index.d.ts +17 -13
- package/dist/db/index.js +205 -26
- 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 +11 -0
- package/dist/dev-server/config.js +23 -20
- 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.js +146 -13
- package/dist/dev-server/plugins/vite-plugin-routes.js +15 -5
- 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 +127 -13
- 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 +3 -0
- package/dist/runtime/router-data.js +102 -17
- package/dist/runtime/router-hydration.js +25 -0
- package/dist/runtime/router.d.ts +16 -1
- package/dist/runtime/router.js +188 -42
- 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 +14 -0
- package/dist/shared/utils.d.ts +33 -7
- package/dist/shared/utils.js +164 -27
- 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 +19 -7
- package/templates/blog/api/posts.ts +4 -18
- package/templates/blog/data/migrations/001_init.sql +6 -5
- 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 +48 -22
- package/templates/blog/pages/posts/[slug].ts +45 -20
- package/templates/blog/pages/tag/[tag].ts +44 -0
- package/templates/dashboard/api/stats.ts +8 -5
- 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 +54 -23
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import type { BuildManifest } from '../shared/types.js';
|
|
3
|
-
export declare function handleLayoutLoaderRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
|
|
4
|
-
export declare function handleLayoutSubscribeRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
|
|
5
|
-
export declare function handleSubscribeRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
|
|
6
|
-
export declare function handleLoaderRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse): Promise<void>;
|
|
3
|
+
export declare function handleLayoutLoaderRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
|
|
4
|
+
export declare function handleLayoutSubscribeRequest(manifest: BuildManifest, serverDir: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
|
|
5
|
+
export declare function handleSubscribeRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
|
|
6
|
+
export declare function handleLoaderRequest(manifest: BuildManifest, serverDir: string, pagesDir: string, pathname: string, queryString: string | undefined, headers: http.IncomingHttpHeaders, res: http.ServerResponse, user?: any): Promise<void>;
|
|
@@ -2,7 +2,15 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { isRedirectResponse } from '../shared/utils.js';
|
|
4
4
|
import { matchRoute } from '../shared/route-matching.js';
|
|
5
|
-
|
|
5
|
+
import { logger } from '../shared/logger.js';
|
|
6
|
+
/** Resolve a module path, falling back to bracket-sanitized filename (Rollup replaces [] with _) */
|
|
7
|
+
function resolveModulePath(serverDir, moduleName) {
|
|
8
|
+
const p = path.join(serverDir, moduleName);
|
|
9
|
+
if (fs.existsSync(p))
|
|
10
|
+
return p;
|
|
11
|
+
return path.join(serverDir, moduleName.replace(/\[/g, '_').replace(/\]/g, '_'));
|
|
12
|
+
}
|
|
13
|
+
export async function handleLayoutLoaderRequest(manifest, serverDir, queryString, headers, res, user) {
|
|
6
14
|
const query = {};
|
|
7
15
|
if (queryString) {
|
|
8
16
|
for (const pair of queryString.split('&')) {
|
|
@@ -18,7 +26,7 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
|
|
|
18
26
|
res.end(JSON.stringify({ __nk_no_loader: true }));
|
|
19
27
|
return;
|
|
20
28
|
}
|
|
21
|
-
const modulePath =
|
|
29
|
+
const modulePath = resolveModulePath(serverDir, layout.module);
|
|
22
30
|
if (!fs.existsSync(modulePath)) {
|
|
23
31
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
24
32
|
res.end(JSON.stringify({ __nk_no_loader: true }));
|
|
@@ -34,7 +42,7 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
|
|
|
34
42
|
// Parse locale from query for layout loader
|
|
35
43
|
const locale = query.__locale;
|
|
36
44
|
delete query.__locale;
|
|
37
|
-
const result = await mod.loader({ params: {}, query: {}, url: `/__layout/${dir}`, headers, locale });
|
|
45
|
+
const result = await mod.loader({ params: {}, query: {}, url: `/__layout/${dir}`, headers, locale, user: user ?? null });
|
|
38
46
|
if (isRedirectResponse(result)) {
|
|
39
47
|
res.writeHead(result.status || 302, { Location: result.location });
|
|
40
48
|
res.end();
|
|
@@ -49,14 +57,14 @@ export async function handleLayoutLoaderRequest(manifest, serverDir, queryString
|
|
|
49
57
|
res.end();
|
|
50
58
|
return;
|
|
51
59
|
}
|
|
52
|
-
|
|
60
|
+
logger.error(`Layout loader error`, { dir, error: err?.message });
|
|
53
61
|
const status = err?.status || 500;
|
|
54
|
-
const message = err?.message || 'Layout loader failed';
|
|
62
|
+
const message = status < 500 ? (err?.message || 'Layout loader failed') : 'Internal server error';
|
|
55
63
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
56
64
|
res.end(JSON.stringify({ error: message }));
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
|
-
export async function handleLayoutSubscribeRequest(manifest, serverDir, queryString, headers, res) {
|
|
67
|
+
export async function handleLayoutSubscribeRequest(manifest, serverDir, queryString, headers, res, user) {
|
|
60
68
|
const query = {};
|
|
61
69
|
if (queryString) {
|
|
62
70
|
for (const pair of queryString.split('&')) {
|
|
@@ -71,7 +79,7 @@ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryStr
|
|
|
71
79
|
res.end();
|
|
72
80
|
return;
|
|
73
81
|
}
|
|
74
|
-
const modulePath =
|
|
82
|
+
const modulePath = resolveModulePath(serverDir, layout.module);
|
|
75
83
|
if (!fs.existsSync(modulePath)) {
|
|
76
84
|
res.writeHead(204);
|
|
77
85
|
res.end();
|
|
@@ -93,21 +101,21 @@ export async function handleLayoutSubscribeRequest(manifest, serverDir, queryStr
|
|
|
93
101
|
const push = (data) => {
|
|
94
102
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
95
103
|
};
|
|
96
|
-
const cleanup = mod.subscribe({ params: {}, push, headers, locale });
|
|
104
|
+
const cleanup = mod.subscribe({ params: {}, push, headers, locale, user: user ?? null });
|
|
97
105
|
res.on('close', () => {
|
|
98
106
|
if (typeof cleanup === 'function')
|
|
99
107
|
cleanup();
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
110
|
catch (err) {
|
|
103
|
-
|
|
111
|
+
logger.error(`Layout subscribe error`, { dir, error: err?.message });
|
|
104
112
|
if (!res.headersSent) {
|
|
105
113
|
res.writeHead(500);
|
|
106
114
|
res.end();
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
|
-
export async function handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res) {
|
|
118
|
+
export async function handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
|
|
111
119
|
const pagePath = pathname.replace('/__nk_subscribe', '') || '/';
|
|
112
120
|
const query = {};
|
|
113
121
|
if (queryString) {
|
|
@@ -130,7 +138,7 @@ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, path
|
|
|
130
138
|
res.end();
|
|
131
139
|
return;
|
|
132
140
|
}
|
|
133
|
-
const modulePath =
|
|
141
|
+
const modulePath = resolveModulePath(serverDir, matched.route.module);
|
|
134
142
|
if (!fs.existsSync(modulePath)) {
|
|
135
143
|
res.writeHead(204);
|
|
136
144
|
res.end();
|
|
@@ -152,21 +160,21 @@ export async function handleSubscribeRequest(manifest, serverDir, pagesDir, path
|
|
|
152
160
|
const push = (data) => {
|
|
153
161
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
154
162
|
};
|
|
155
|
-
const cleanup = mod.subscribe({ params: matched.params, push, headers, locale });
|
|
163
|
+
const cleanup = mod.subscribe({ params: matched.params, push, headers, locale, user: user ?? null });
|
|
156
164
|
res.on('close', () => {
|
|
157
165
|
if (typeof cleanup === 'function')
|
|
158
166
|
cleanup();
|
|
159
167
|
});
|
|
160
168
|
}
|
|
161
169
|
catch (err) {
|
|
162
|
-
|
|
170
|
+
logger.error(`Subscribe error`, { pagePath, error: err?.message });
|
|
163
171
|
if (!res.headersSent) {
|
|
164
172
|
res.writeHead(500);
|
|
165
173
|
res.end();
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
}
|
|
169
|
-
export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res) {
|
|
177
|
+
export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathname, queryString, headers, res, user) {
|
|
170
178
|
const pagePath = pathname.replace('/__nk_loader', '') || '/';
|
|
171
179
|
// Parse query params
|
|
172
180
|
const query = {};
|
|
@@ -191,7 +199,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
|
|
|
191
199
|
res.end(JSON.stringify({ __nk_no_loader: true }));
|
|
192
200
|
return;
|
|
193
201
|
}
|
|
194
|
-
const modulePath =
|
|
202
|
+
const modulePath = resolveModulePath(serverDir, matched.route.module);
|
|
195
203
|
if (!fs.existsSync(modulePath)) {
|
|
196
204
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
197
205
|
res.end(JSON.stringify({ __nk_no_loader: true }));
|
|
@@ -206,7 +214,7 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
|
|
|
206
214
|
}
|
|
207
215
|
const locale = query.__locale;
|
|
208
216
|
delete query.__locale;
|
|
209
|
-
const result = await mod.loader({ params: matched.params, query, url: pagePath, headers, locale });
|
|
217
|
+
const result = await mod.loader({ params: matched.params, query, url: pagePath, headers, locale, user: user ?? null });
|
|
210
218
|
if (isRedirectResponse(result)) {
|
|
211
219
|
res.writeHead(result.status || 302, { Location: result.location });
|
|
212
220
|
res.end();
|
|
@@ -221,9 +229,9 @@ export async function handleLoaderRequest(manifest, serverDir, pagesDir, pathnam
|
|
|
221
229
|
res.end();
|
|
222
230
|
return;
|
|
223
231
|
}
|
|
224
|
-
|
|
232
|
+
logger.error(`Loader error`, { pagePath, error: err?.message });
|
|
225
233
|
const status = err?.status || 500;
|
|
226
|
-
const message = err?.message || 'Loader failed';
|
|
234
|
+
const message = status < 500 ? (err?.message || 'Loader failed') : 'Internal server error';
|
|
227
235
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
228
236
|
res.end(JSON.stringify({ error: message }));
|
|
229
237
|
}
|
package/dist/build/serve-ssr.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { stripOuterLitMarkers, dirToLayoutTagName, isRedirectResponse } from '../shared/utils.js';
|
|
3
|
+
import { stripOuterLitMarkers, dirToLayoutTagName, isRedirectResponse, patchLoaderDataSpread } from '../shared/utils.js';
|
|
4
4
|
import { matchRoute } from '../shared/route-matching.js';
|
|
5
5
|
import { sendCompressed } from './serve-static.js';
|
|
6
|
+
import { logger } from '../shared/logger.js';
|
|
6
7
|
export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, queryString, indexHtmlShell, title, ssrRuntime, req, res) {
|
|
7
8
|
// Find matching route (any route, not just those with loaders)
|
|
8
9
|
const allMatched = matchRoute(manifest.routes, pathname);
|
|
9
10
|
// Try SSR for routes with loaders
|
|
10
11
|
const matched = matchRoute(manifest.routes.filter(r => r.hasLoader), pathname);
|
|
11
12
|
if (matched && matched.route.module) {
|
|
12
|
-
|
|
13
|
+
// Rollup sanitizes brackets in filenames: [...path] → _...path_
|
|
14
|
+
let modulePath = path.join(serverDir, matched.route.module);
|
|
15
|
+
if (!fs.existsSync(modulePath)) {
|
|
16
|
+
modulePath = path.join(serverDir, matched.route.module.replace(/\[/g, '_').replace(/\]/g, '_'));
|
|
17
|
+
}
|
|
13
18
|
if (fs.existsSync(modulePath)) {
|
|
14
19
|
try {
|
|
15
20
|
const mod = await import(modulePath);
|
|
16
21
|
// Run loader
|
|
17
22
|
let loaderData = undefined;
|
|
18
23
|
if (mod.loader && typeof mod.loader === 'function') {
|
|
19
|
-
loaderData = await mod.loader({ params: matched.params, query: {}, url: pathname, headers: req.headers });
|
|
24
|
+
loaderData = await mod.loader({ params: matched.params, query: {}, url: pathname, headers: req.headers, user: req.nkAuth?.user ?? null });
|
|
20
25
|
if (isRedirectResponse(loaderData)) {
|
|
21
26
|
res.writeHead(loaderData.status || 302, { Location: loaderData.location });
|
|
22
27
|
res.end();
|
|
@@ -39,7 +44,7 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
|
|
|
39
44
|
if (fs.existsSync(layoutModulePath)) {
|
|
40
45
|
const layoutMod = await import(layoutModulePath);
|
|
41
46
|
if (layoutMod.loader && typeof layoutMod.loader === 'function') {
|
|
42
|
-
layoutLoaderData = await layoutMod.loader({ params: {}, query: {}, url: pathname, headers: req.headers });
|
|
47
|
+
layoutLoaderData = await layoutMod.loader({ params: {}, query: {}, url: pathname, headers: req.headers, user: req.nkAuth?.user ?? null });
|
|
43
48
|
if (isRedirectResponse(layoutLoaderData)) {
|
|
44
49
|
res.writeHead(layoutLoaderData.status || 302, { Location: layoutLoaderData.location });
|
|
45
50
|
res.end();
|
|
@@ -52,6 +57,12 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
|
|
|
52
57
|
layoutsData.push({ loaderPath: dir, data: layoutLoaderData });
|
|
53
58
|
layoutModules.push({ tagName: layoutTagName, loaderData: layoutLoaderData });
|
|
54
59
|
}
|
|
60
|
+
// Patch element classes to spread loaderData into individual properties
|
|
61
|
+
for (const lm of layoutModules) {
|
|
62
|
+
patchLoaderDataSpread(lm.tagName);
|
|
63
|
+
}
|
|
64
|
+
if (tagName)
|
|
65
|
+
patchLoaderDataSpread(tagName);
|
|
55
66
|
if (tagName && ssrRuntime) {
|
|
56
67
|
// SSR render with the bundled @lit-labs/ssr runtime
|
|
57
68
|
try {
|
|
@@ -90,14 +101,18 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
|
|
|
90
101
|
const loaderDataScript = ssrDataObj !== undefined
|
|
91
102
|
? `<script type="application/json" id="__nk_ssr_data__">${JSON.stringify(ssrDataObj).replace(/</g, '\\u003c')}</script>`
|
|
92
103
|
: '';
|
|
93
|
-
|
|
104
|
+
// Auth: inline user data for client hydration
|
|
105
|
+
const authUser = req.nkAuth?.user ?? null;
|
|
106
|
+
const authScript = authUser
|
|
107
|
+
? `<script type="application/json" id="__nk_auth__">${JSON.stringify(authUser).replace(/</g, '\\u003c')}</script>`
|
|
108
|
+
: '';
|
|
94
109
|
let html_out = indexHtmlShell;
|
|
95
|
-
html_out = html_out.replace(/<nk-app><\/nk-app>/, `${loaderDataScript}<nk-app data-nk-ssr><div id="nk-router-outlet">${ssrHtml}</div></nk-app
|
|
110
|
+
html_out = html_out.replace(/<nk-app><\/nk-app>/, `${authScript}${loaderDataScript}<nk-app data-nk-ssr><div id="nk-router-outlet">${ssrHtml}</div></nk-app>`);
|
|
96
111
|
sendCompressed(req, res, 200, 'text/html; charset=utf-8', html_out);
|
|
97
112
|
return;
|
|
98
113
|
}
|
|
99
114
|
catch (ssrErr) {
|
|
100
|
-
|
|
115
|
+
logger.warn('SSR render failed, falling back to CSR', { error: ssrErr?.message });
|
|
101
116
|
}
|
|
102
117
|
}
|
|
103
118
|
// Fallback: inject loader data without SSR HTML
|
|
@@ -106,16 +121,28 @@ export async function handlePageRoute(manifest, serverDir, pagesDir, pathname, q
|
|
|
106
121
|
? { page: loaderData, layouts: layoutsData }
|
|
107
122
|
: loaderData;
|
|
108
123
|
const loaderDataScript = `<script type="application/json" id="__nk_ssr_data__">${JSON.stringify(ssrDataObj).replace(/</g, '\\u003c')}</script>`;
|
|
109
|
-
|
|
124
|
+
const authUserFb = req.nkAuth?.user ?? null;
|
|
125
|
+
const authScriptFb = authUserFb
|
|
126
|
+
? `<script type="application/json" id="__nk_auth__">${JSON.stringify(authUserFb).replace(/</g, '\\u003c')}</script>`
|
|
127
|
+
: '';
|
|
128
|
+
let html_out = indexHtmlShell.replace('<nk-app>', `${authScriptFb}${loaderDataScript}<nk-app>`);
|
|
110
129
|
sendCompressed(req, res, 200, 'text/html; charset=utf-8', html_out);
|
|
111
130
|
return;
|
|
112
131
|
}
|
|
113
132
|
}
|
|
114
133
|
catch (err) {
|
|
115
|
-
|
|
134
|
+
logger.error('Page handler error', { error: err?.message });
|
|
116
135
|
}
|
|
117
136
|
}
|
|
118
137
|
}
|
|
119
|
-
// SPA fallback — serve the built index.html
|
|
120
|
-
|
|
138
|
+
// SPA fallback — serve the built index.html with auth data injected
|
|
139
|
+
const fallbackUser = req.nkAuth?.user ?? null;
|
|
140
|
+
if (fallbackUser) {
|
|
141
|
+
const authTag = `<script type="application/json" id="__nk_auth__">${JSON.stringify(fallbackUser).replace(/</g, '\\u003c')}</script>`;
|
|
142
|
+
const html = indexHtmlShell.replace('<nk-app>', `${authTag}<nk-app>`);
|
|
143
|
+
sendCompressed(req, res, 200, 'text/html; charset=utf-8', html);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
sendCompressed(req, res, 200, 'text/html; charset=utf-8', indexHtmlShell);
|
|
147
|
+
}
|
|
121
148
|
}
|
|
@@ -52,9 +52,9 @@ export function sendCompressed(req, res, statusCode, contentType, body) {
|
|
|
52
52
|
}
|
|
53
53
|
export function serveStaticFile(clientDir, pathname, req, res) {
|
|
54
54
|
// Prevent directory traversal
|
|
55
|
-
const
|
|
56
|
-
const filePath = path.
|
|
57
|
-
if (!filePath.startsWith(
|
|
55
|
+
const resolvedClientDir = path.resolve(clientDir);
|
|
56
|
+
const filePath = path.resolve(resolvedClientDir, pathname.replace(/^\/+/, ''));
|
|
57
|
+
if (!filePath.startsWith(resolvedClientDir + path.sep) && filePath !== resolvedClientDir) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
60
|
if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
|
package/dist/build/serve.js
CHANGED
|
@@ -10,44 +10,217 @@ 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 {
|
|
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';
|
|
14
25
|
export async function serveProject(options) {
|
|
15
26
|
const { projectDir } = options;
|
|
16
|
-
setProjectDir(projectDir);
|
|
17
27
|
const port = options.port || 3000;
|
|
18
28
|
const outDir = path.join(projectDir, '.lumenjs');
|
|
19
29
|
const clientDir = path.join(outDir, 'client');
|
|
20
30
|
const serverDir = path.join(outDir, 'server');
|
|
21
31
|
const manifestPath = path.join(outDir, 'manifest.json');
|
|
32
|
+
// Initialize structured logging
|
|
33
|
+
initLogger();
|
|
22
34
|
if (!fs.existsSync(manifestPath)) {
|
|
23
|
-
|
|
35
|
+
logger.fatal('No build found. Run `lumenjs build` first.');
|
|
24
36
|
process.exit(1);
|
|
25
37
|
}
|
|
26
38
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
27
|
-
const
|
|
39
|
+
const config = readProjectConfig(projectDir);
|
|
40
|
+
const { title } = config;
|
|
28
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 });
|
|
29
48
|
// Read the built index.html shell
|
|
30
49
|
const indexHtmlPath = path.join(clientDir, 'index.html');
|
|
31
50
|
if (!fs.existsSync(indexHtmlPath)) {
|
|
32
|
-
|
|
51
|
+
logger.fatal('No index.html found in build output.');
|
|
33
52
|
process.exit(1);
|
|
34
53
|
}
|
|
35
54
|
const indexHtmlShell = fs.readFileSync(indexHtmlPath, 'utf-8');
|
|
36
|
-
// Load bundled SSR runtime first —
|
|
37
|
-
//
|
|
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.
|
|
38
59
|
const ssrRuntimePath = path.join(serverDir, 'ssr-runtime.js');
|
|
39
60
|
let ssrRuntime = null;
|
|
40
61
|
if (fs.existsSync(ssrRuntimePath)) {
|
|
41
62
|
ssrRuntime = await import(ssrRuntimePath);
|
|
42
63
|
}
|
|
43
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)
|
|
44
66
|
installDomShims();
|
|
45
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()));
|
|
46
166
|
const server = http.createServer(async (req, res) => {
|
|
167
|
+
const startTime = Date.now();
|
|
47
168
|
const url = req.url || '/';
|
|
48
169
|
const [pathname, queryString] = url.split('?');
|
|
49
170
|
const method = req.method || 'GET';
|
|
50
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
|
+
}
|
|
51
224
|
// 1. API routes
|
|
52
225
|
if (pathname.startsWith('/api/')) {
|
|
53
226
|
await handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res);
|
|
@@ -66,25 +239,26 @@ export async function serveProject(options) {
|
|
|
66
239
|
}
|
|
67
240
|
// 4. Layout subscribe endpoint (SSE)
|
|
68
241
|
if (pathname === '/__nk_subscribe/__layout/' || pathname === '/__nk_subscribe/__layout') {
|
|
69
|
-
|
|
242
|
+
const authUser = req.nkAuth?.user ?? undefined;
|
|
243
|
+
await handleLayoutSubscribeRequest(manifest, serverDir, queryString, req.headers, res, authUser);
|
|
70
244
|
return;
|
|
71
245
|
}
|
|
72
246
|
// 5. Subscribe endpoint (SSE)
|
|
73
247
|
if (pathname.startsWith('/__nk_subscribe/')) {
|
|
74
|
-
await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res);
|
|
248
|
+
await handleSubscribeRequest(manifest, serverDir, pagesDir, pathname, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
|
|
75
249
|
return;
|
|
76
250
|
}
|
|
77
251
|
// 6. Layout loader endpoint
|
|
78
252
|
if (pathname === '/__nk_loader/__layout/' || pathname === '/__nk_loader/__layout') {
|
|
79
|
-
await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res);
|
|
253
|
+
await handleLayoutLoaderRequest(manifest, serverDir, queryString, req.headers, res, req.nkAuth?.user ?? undefined);
|
|
80
254
|
return;
|
|
81
255
|
}
|
|
82
256
|
// 7. Loader endpoint for client-side navigation
|
|
83
257
|
if (pathname.startsWith('/__nk_loader/')) {
|
|
84
|
-
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);
|
|
85
259
|
return;
|
|
86
260
|
}
|
|
87
|
-
//
|
|
261
|
+
// 8. Resolve locale and strip prefix for page routing
|
|
88
262
|
let resolvedPathname = pathname;
|
|
89
263
|
let locale;
|
|
90
264
|
if (manifest.i18n) {
|
|
@@ -92,16 +266,45 @@ export async function serveProject(options) {
|
|
|
92
266
|
resolvedPathname = result.pathname;
|
|
93
267
|
locale = result.locale;
|
|
94
268
|
}
|
|
95
|
-
//
|
|
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
|
|
96
285
|
await handlePageRoute(manifest, serverDir, pagesDir, resolvedPathname, queryString, indexHtmlShell, title, ssrRuntime, req, res);
|
|
97
286
|
}
|
|
98
287
|
catch (err) {
|
|
99
|
-
|
|
288
|
+
logger.error('Request error', {
|
|
289
|
+
method, url: pathname, error: err?.message, stack: err?.stack,
|
|
290
|
+
requestId: getRequestId(req),
|
|
291
|
+
});
|
|
100
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);
|
|
101
293
|
sendCompressed(req, res, 500, 'text/html; charset=utf-8', html);
|
|
102
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
|
+
},
|
|
103
306
|
});
|
|
104
307
|
server.listen(port, () => {
|
|
105
|
-
|
|
308
|
+
logger.info(`Production server running at http://localhost:${port}`, { port });
|
|
106
309
|
});
|
|
107
310
|
}
|