@lowdefy/server 5.5.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{pages/_app.js → client/App.jsx} +26 -34
- package/client/Page.jsx +109 -0
- package/client/main.jsx +39 -0
- package/lib/build/app.js +8 -1
- package/lib/build/appMeta.js +8 -1
- package/lib/build/auth.js +8 -1
- package/lib/build/config.js +8 -1
- package/lib/build/i18n.js +8 -1
- package/lib/build/logger.js +8 -1
- package/lib/build/theme.js +8 -1
- package/lib/client/auth/{Auth.js → Auth.jsx} +10 -2
- package/lib/client/auth/{AuthConfigured.js → AuthConfigured.jsx} +22 -10
- package/lib/client/sentry/captureSentryError.js +1 -1
- package/lib/client/sentry/initSentryClient.js +1 -1
- package/lib/client/sentry/setSentryUser.js +1 -1
- package/lib/server/apiWrapper.js +2 -9
- package/lib/server/auth/{getAuthOptions.js → getAuthConfig.js} +5 -4
- package/lib/server/auth/{getServerSession.js → session.js} +9 -6
- package/lib/server/auth/strategies.js +47 -0
- package/lib/server/log/createHandleError.js +15 -2
- package/lib/server/log/createLogger.js +18 -1
- package/lib/server/log/logRequest.js +41 -45
- package/lib/server/sentry/captureSentryError.js +1 -1
- package/lib/server/sentry/initSentry.js +6 -4
- package/lib/server/sentry/setSentryUser.js +1 -1
- package/lowdefy/createCustomPluginTypesMap.mjs +1 -0
- package/package.json +52 -39
- package/package.original.json +54 -41
- package/src/app.js +140 -0
- package/src/html/getAssets.js +45 -0
- package/src/html/renderPage.js +80 -0
- package/src/html/template.js +95 -0
- package/src/index.js +85 -0
- package/src/lib/getPathSegments.js +25 -0
- package/src/lib/safeScriptJson.js +38 -0
- package/src/middleware/apiContext.js +135 -0
- package/src/middleware/errorHandler.js +54 -0
- package/src/middleware/sentry.js +33 -0
- package/{pages/api/agent/[...path].js → src/routes/agent.js} +20 -42
- package/src/routes/apiPage.js +37 -0
- package/src/routes/auth.js +38 -0
- package/{pages/api/client-error.js → src/routes/clientError.js} +14 -16
- package/src/routes/cron.js +44 -0
- package/src/routes/cronForward.js +47 -0
- package/src/routes/detached.js +47 -0
- package/src/routes/endpoints.js +71 -0
- package/src/routes/mcp.js +34 -0
- package/{pages/api/request/[...path].js → src/routes/request.js} +10 -10
- package/src/routes/usage.js +46 -0
- package/src/routes/websocket.js +46 -0
- package/vite.config.js +61 -0
- package/.eslintrc.yaml +0 -1
- package/lib/client/Page.js +0 -56
- package/lib/server/serverSidePropsWrapper.js +0 -66
- package/next.config.js +0 -34
- package/pages/404.js +0 -49
- package/pages/[[...pageId]].js +0 -85
- package/pages/_document.js +0 -106
- package/pages/api/auth/[...nextauth].js +0 -37
- package/pages/api/endpoints/[...endpointId].js +0 -32
- package/pages/api/usage.js +0 -67
- /package/{postcss.config.js → postcss.config.cjs} +0 -0
|
@@ -14,13 +14,7 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
// CSS layer order — MUST be the first CSS import. Next.js treats this as critical
|
|
18
|
-
// CSS that loads before hydration, locking the cascade priority (antd > base/preflight)
|
|
19
|
-
// before antd's StyleProvider injects @layer antd {} at runtime.
|
|
20
|
-
import '../build/layer-order.css';
|
|
21
|
-
|
|
22
17
|
import React, { useCallback, useRef } from 'react';
|
|
23
|
-
import dynamic from 'next/dynamic';
|
|
24
18
|
|
|
25
19
|
import { ErrorBoundary } from '@lowdefy/block-utils';
|
|
26
20
|
import { useDarkMode, useLocale } from '@lowdefy/client';
|
|
@@ -28,23 +22,20 @@ import { StyleProvider } from '@ant-design/cssinjs';
|
|
|
28
22
|
import { App as AntdApp, theme as antdTheme } from 'antd';
|
|
29
23
|
import { XProvider } from '@ant-design/x';
|
|
30
24
|
|
|
25
|
+
import { serializer } from '@lowdefy/helpers';
|
|
26
|
+
|
|
31
27
|
import antdLocaleLoaders from '../build/i18n/antdLocales.js';
|
|
32
28
|
import antdXLocaleLoaders from '../build/i18n/antdXLocales.js';
|
|
33
29
|
import dayjsLocaleMap from '../build/i18n/dayjsLocales.js';
|
|
34
|
-
import
|
|
30
|
+
import rawLoggerConfig from '../build/logger.json';
|
|
31
|
+
import Auth from '../lib/client/auth/Auth.jsx';
|
|
35
32
|
import createLogUsage from '../lib/client/createLogUsage.js';
|
|
36
33
|
import initSentryClient from '../lib/client/sentry/initSentryClient.js';
|
|
37
|
-
import loggerConfig from '../lib/build/logger.js';
|
|
38
34
|
import setSentryUser from '../lib/client/sentry/setSentryUser.js';
|
|
35
|
+
import Page from './Page.jsx';
|
|
39
36
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// Initialize Sentry client once on module load
|
|
44
|
-
initSentryClient({
|
|
45
|
-
sentryDsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
46
|
-
sentryConfig: loggerConfig.sentry,
|
|
47
|
-
});
|
|
37
|
+
// Deserialize to restore arrays (e.g. sentry.userFields) from ~arr markers.
|
|
38
|
+
const loggerConfig = serializer.deserialize(rawLoggerConfig);
|
|
48
39
|
|
|
49
40
|
function ThemeTokenResolver({ lowdefyRef, children }) {
|
|
50
41
|
const { token } = antdTheme.useToken();
|
|
@@ -55,7 +46,16 @@ function ThemeTokenResolver({ lowdefyRef, children }) {
|
|
|
55
46
|
return children;
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
function App({
|
|
49
|
+
function App({ config }) {
|
|
50
|
+
const { rootConfig, session } = config;
|
|
51
|
+
|
|
52
|
+
// Sentry DSN arrives at runtime via the embedded config (the server reads
|
|
53
|
+
// SENTRY_DSN per process) — no build-time env inlining.
|
|
54
|
+
initSentryClient({
|
|
55
|
+
sentryDsn: config.sentryDsn,
|
|
56
|
+
sentryConfig: loggerConfig.sentry,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
59
|
const usageDataRef = useRef({});
|
|
60
60
|
const lowdefyRef = useRef({ eventCallback: createLogUsage({ usageDataRef }) });
|
|
61
61
|
if (rootConfig?.theme) {
|
|
@@ -67,7 +67,11 @@ function App({ Component, pageProps: { session, rootConfig, pageConfig } }) {
|
|
|
67
67
|
configDarkMode: lowdefyRef.current.theme?.darkMode,
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
const {
|
|
70
|
+
const {
|
|
71
|
+
active: activeLocale,
|
|
72
|
+
antdLocale,
|
|
73
|
+
antdXLocale,
|
|
74
|
+
} = useLocale({
|
|
71
75
|
i18n: rootConfig?.i18n,
|
|
72
76
|
antdLocaleLoaders,
|
|
73
77
|
antdXLocaleLoaders,
|
|
@@ -98,9 +102,8 @@ function App({ Component, pageProps: { session, rootConfig, pageConfig } }) {
|
|
|
98
102
|
// gives X components (Bubble, Sender, Conversations, ...) their built-in strings
|
|
99
103
|
// alongside antd's. antd X ships only en_US + zh_CN; other locales fall back
|
|
100
104
|
// to en_US for X-native strings.
|
|
101
|
-
const mergedLocale =
|
|
102
|
-
? { ...(antdLocale ?? {}), ...(antdXLocale ?? {}) }
|
|
103
|
-
: undefined;
|
|
105
|
+
const mergedLocale =
|
|
106
|
+
antdLocale || antdXLocale ? { ...(antdLocale ?? {}), ...(antdXLocale ?? {}) } : undefined;
|
|
104
107
|
|
|
105
108
|
return (
|
|
106
109
|
<StyleProvider layer>
|
|
@@ -131,14 +134,7 @@ function App({ Component, pageProps: { session, rootConfig, pageConfig } }) {
|
|
|
131
134
|
user: auth.session,
|
|
132
135
|
sentryConfig: loggerConfig.sentry,
|
|
133
136
|
});
|
|
134
|
-
return
|
|
135
|
-
<Component
|
|
136
|
-
auth={auth}
|
|
137
|
-
lowdefy={lowdefyRef.current}
|
|
138
|
-
rootConfig={rootConfig}
|
|
139
|
-
pageConfig={pageConfig}
|
|
140
|
-
/>
|
|
141
|
-
);
|
|
137
|
+
return <Page auth={auth} config={config} lowdefy={lowdefyRef.current} />;
|
|
142
138
|
}}
|
|
143
139
|
</Auth>
|
|
144
140
|
</ErrorBoundary>
|
|
@@ -149,8 +145,4 @@ function App({ Component, pageProps: { session, rootConfig, pageConfig } }) {
|
|
|
149
145
|
);
|
|
150
146
|
}
|
|
151
147
|
|
|
152
|
-
|
|
153
|
-
ssr: false,
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
export default DynamicApp;
|
|
148
|
+
export default App;
|
package/client/Page.jsx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
18
|
+
|
|
19
|
+
import Client from '@lowdefy/client';
|
|
20
|
+
import createRouter from '@lowdefy/client/adapters/createRouter.js';
|
|
21
|
+
import createLinkComponent from '@lowdefy/client/adapters/Link.js';
|
|
22
|
+
import { createUrl } from '@lowdefy/client/adapters/url.js';
|
|
23
|
+
import Head from '@lowdefy/client/adapters/Head.js';
|
|
24
|
+
|
|
25
|
+
import actions from '../build/plugins/actions.js';
|
|
26
|
+
import blockMetas from '../build/plugins/blockMetas.json';
|
|
27
|
+
import blocks from '../build/plugins/blocks.js';
|
|
28
|
+
import icons from '../build/plugins/icons.js';
|
|
29
|
+
import operators from '../build/plugins/operators/client.js';
|
|
30
|
+
import jsMap from '../build/plugins/operators/clientJsMap.js';
|
|
31
|
+
|
|
32
|
+
// Replaces lib/client/Page.js. The first page renders from the config
|
|
33
|
+
// embedded in the HTML; SPA navigations fetch /api/page/* and swap pageConfig.
|
|
34
|
+
function Page({ auth, config, lowdefy }) {
|
|
35
|
+
const [pageConfig, setPageConfig] = useState(config.pageConfig);
|
|
36
|
+
|
|
37
|
+
const routerRef = useRef(null);
|
|
38
|
+
if (!routerRef.current) {
|
|
39
|
+
const router = createRouter({ basePath: config.basePath ?? '', window });
|
|
40
|
+
routerRef.current = {
|
|
41
|
+
router,
|
|
42
|
+
Link: createLinkComponent({ router }),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const { router, Link } = routerRef.current;
|
|
46
|
+
|
|
47
|
+
// Temporary sequence guard for the superseded-navigation race: a slow
|
|
48
|
+
// response for an abandoned navigation must not paint over a newer one.
|
|
49
|
+
// Remove when the loader-based lifecycle replaces these fetch paths.
|
|
50
|
+
const latestNavRef = useRef(0);
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const unsubscribe = router.subscribe(async ({ pageId }) => {
|
|
54
|
+
const token = ++latestNavRef.current;
|
|
55
|
+
const targetPageId = pageId ?? config.rootConfig.home.pageId;
|
|
56
|
+
try {
|
|
57
|
+
// Forward the current query string so server-side Dynamic block
|
|
58
|
+
// resolution sees the same urlQuery as an initial HTML load.
|
|
59
|
+
const res = await fetch(
|
|
60
|
+
`${router.basePath}/api/page/${targetPageId}${window.location.search}`
|
|
61
|
+
);
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
if (token !== latestNavRef.current) return;
|
|
64
|
+
if (targetPageId !== '404') {
|
|
65
|
+
router.replace({ pathname: '/404' });
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const { pageConfig: nextPageConfig } = await res.json();
|
|
70
|
+
if (token !== latestNavRef.current) return;
|
|
71
|
+
setPageConfig(nextPageConfig);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Network failure on SPA navigation — fall back to a full page load.
|
|
74
|
+
if (token !== latestNavRef.current) return;
|
|
75
|
+
window.location.assign(
|
|
76
|
+
createUrl({
|
|
77
|
+
basePath: router.basePath,
|
|
78
|
+
pathname: targetPageId === config.rootConfig.home.pageId ? '/' : `/${targetPageId}`,
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return unsubscribe;
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<Client
|
|
88
|
+
auth={auth}
|
|
89
|
+
Components={{ Head, Link }}
|
|
90
|
+
config={{
|
|
91
|
+
pageConfig,
|
|
92
|
+
rootConfig: config.rootConfig,
|
|
93
|
+
}}
|
|
94
|
+
jsMap={jsMap}
|
|
95
|
+
lowdefy={lowdefy}
|
|
96
|
+
router={router}
|
|
97
|
+
types={{
|
|
98
|
+
actions,
|
|
99
|
+
blockMetas,
|
|
100
|
+
blocks,
|
|
101
|
+
icons,
|
|
102
|
+
operators,
|
|
103
|
+
}}
|
|
104
|
+
window={window}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default Page;
|
package/client/main.jsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// CSS layer order — MUST be the first CSS import. This locks the cascade
|
|
18
|
+
// priority (antd > base/preflight) before antd's StyleProvider injects
|
|
19
|
+
// @layer antd {} at runtime.
|
|
20
|
+
import '../build/layer-order.css';
|
|
21
|
+
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import { createRoot } from 'react-dom/client';
|
|
24
|
+
|
|
25
|
+
import App from './App.jsx';
|
|
26
|
+
|
|
27
|
+
import '../build/globals.css';
|
|
28
|
+
|
|
29
|
+
const config = JSON.parse(document.getElementById('__LOWDEFY_CONFIG__').textContent);
|
|
30
|
+
const container = document.getElementById('root');
|
|
31
|
+
|
|
32
|
+
// Keep one React root across Vite HMR updates — re-running createRoot on a
|
|
33
|
+
// container that already has a root is an error.
|
|
34
|
+
const root = import.meta.hot?.data.root ?? createRoot(container);
|
|
35
|
+
if (import.meta.hot) {
|
|
36
|
+
import.meta.hot.data.root = root;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
root.render(<App config={config} />);
|
package/lib/build/app.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/app.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/appMeta.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/appMeta.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/auth.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/auth.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/config.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/config.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/i18n.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/i18n.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/logger.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/logger.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/theme.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/theme.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
|
@@ -17,10 +17,18 @@
|
|
|
17
17
|
/* eslint-disable react/jsx-props-no-spreading */
|
|
18
18
|
|
|
19
19
|
import React from 'react';
|
|
20
|
-
import AuthConfigured from './AuthConfigured.
|
|
20
|
+
import AuthConfigured from './AuthConfigured.jsx';
|
|
21
21
|
import AuthNotConfigured from './AuthNotConfigured.js';
|
|
22
22
|
|
|
23
|
-
import
|
|
23
|
+
import { serializer } from '@lowdefy/helpers';
|
|
24
|
+
|
|
25
|
+
// Client code imports the build JSON directly — Vite handles JSON imports;
|
|
26
|
+
// the lib/build/*.js wrappers are server-only (they read from disk).
|
|
27
|
+
// Deserialize to restore arrays from their ~arr build markers (providers
|
|
28
|
+
// must be a real array for single-provider inference in createAuthMethods).
|
|
29
|
+
import rawAuthConfig from '../../../build/auth.json';
|
|
30
|
+
|
|
31
|
+
const authConfig = serializer.deserialize(rawAuthConfig);
|
|
24
32
|
|
|
25
33
|
function Auth({ children, session }) {
|
|
26
34
|
if (authConfig.configured === true) {
|
|
@@ -17,9 +17,26 @@
|
|
|
17
17
|
/* eslint-disable react/jsx-props-no-spreading */
|
|
18
18
|
|
|
19
19
|
import React, { useEffect, useRef } from 'react';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
authConfigManager,
|
|
22
|
+
getSession,
|
|
23
|
+
SessionProvider,
|
|
24
|
+
signIn,
|
|
25
|
+
signOut,
|
|
26
|
+
useSession,
|
|
27
|
+
} from '@hono/auth-js/react';
|
|
21
28
|
|
|
22
|
-
import
|
|
29
|
+
import { serializer } from '@lowdefy/helpers';
|
|
30
|
+
|
|
31
|
+
import rawLowdefyConfig from '../../../build/config.json';
|
|
32
|
+
|
|
33
|
+
const lowdefyConfig = serializer.deserialize(rawLowdefyConfig);
|
|
34
|
+
|
|
35
|
+
// @hono/auth-js/react configures its fetch paths through a module-level
|
|
36
|
+
// manager instead of SessionProvider props.
|
|
37
|
+
if (lowdefyConfig.basePath) {
|
|
38
|
+
authConfigManager.setConfig({ basePath: `${lowdefyConfig.basePath}/api/auth` });
|
|
39
|
+
}
|
|
23
40
|
|
|
24
41
|
function Session({ children }) {
|
|
25
42
|
const wasAuthenticated = useRef(false);
|
|
@@ -32,9 +49,8 @@ function Session({ children }) {
|
|
|
32
49
|
}
|
|
33
50
|
}, [status]);
|
|
34
51
|
|
|
35
|
-
// If session is passed to SessionProvider from
|
|
36
|
-
// we won't have a loading state here.
|
|
37
|
-
// But 404 uses getStaticProps so we have this for 404.
|
|
52
|
+
// If session is passed to SessionProvider from the server-rendered config
|
|
53
|
+
// we won't have a loading state here, but unauthenticated first loads do.
|
|
38
54
|
if (status === 'loading') {
|
|
39
55
|
return '';
|
|
40
56
|
}
|
|
@@ -43,12 +59,8 @@ function Session({ children }) {
|
|
|
43
59
|
|
|
44
60
|
function AuthConfigured({ authConfig, children, serverSession }) {
|
|
45
61
|
const auth = { authConfig, getSession, signIn, signOut };
|
|
46
|
-
let basePath = lowdefyConfig.basePath;
|
|
47
|
-
if (basePath) {
|
|
48
|
-
basePath = `${basePath}/api/auth`;
|
|
49
|
-
}
|
|
50
62
|
return (
|
|
51
|
-
<SessionProvider session={serverSession}
|
|
63
|
+
<SessionProvider session={serverSession}>
|
|
52
64
|
<Session>
|
|
53
65
|
{(session) => {
|
|
54
66
|
auth.session = session;
|
package/lib/server/apiWrapper.js
CHANGED
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import path from 'path';
|
|
18
|
-
import { createApiContext } from '@lowdefy/api';
|
|
18
|
+
import { createApiContext, redactErrorResponse } from '@lowdefy/api';
|
|
19
19
|
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
20
|
-
import { serializer } from '@lowdefy/helpers';
|
|
21
20
|
import { v4 as uuid } from 'uuid';
|
|
22
21
|
|
|
23
22
|
import agents from '../../build/plugins/agents.js';
|
|
@@ -80,13 +79,7 @@ function apiWrapper(handler) {
|
|
|
80
79
|
return response;
|
|
81
80
|
} catch (error) {
|
|
82
81
|
await context.handleError(error);
|
|
83
|
-
|
|
84
|
-
if (serialized?.['~e']) {
|
|
85
|
-
delete serialized['~e'].received;
|
|
86
|
-
delete serialized['~e'].stack;
|
|
87
|
-
delete serialized['~e'].configKey;
|
|
88
|
-
}
|
|
89
|
-
res.status(500).json(serialized);
|
|
82
|
+
res.status(500).json(redactErrorResponse(context, error));
|
|
90
83
|
}
|
|
91
84
|
};
|
|
92
85
|
}
|
|
@@ -14,17 +14,18 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { getAuthConfig as getApiAuthConfig } from '@lowdefy/api';
|
|
18
18
|
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
19
19
|
|
|
20
20
|
import adapters from '../../../build/plugins/auth/adapters.js';
|
|
21
|
+
import appMeta from '../../build/appMeta.js';
|
|
21
22
|
import authJson from '../../build/auth.js';
|
|
22
23
|
import callbacks from '../../../build/plugins/auth/callbacks.js';
|
|
23
24
|
import events from '../../../build/plugins/auth/events.js';
|
|
24
25
|
import providers from '../../../build/plugins/auth/providers.js';
|
|
25
26
|
|
|
26
|
-
function
|
|
27
|
-
return
|
|
27
|
+
function getAuthConfig({ logger }) {
|
|
28
|
+
return getApiAuthConfig({
|
|
28
29
|
appMeta,
|
|
29
30
|
authJson,
|
|
30
31
|
logger,
|
|
@@ -33,4 +34,4 @@ function getAuthOptions({ appMeta, logger }) {
|
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export default
|
|
37
|
+
export default getAuthConfig;
|
|
@@ -14,15 +14,18 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { getAuthUser } from '@hono/auth-js';
|
|
18
18
|
|
|
19
19
|
import authJson from '../../build/auth.js';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
// Replaces getServerSession.js — reads the session from the Hono context
|
|
22
|
+
// populated by the initAuthConfig middleware.
|
|
23
|
+
async function getSession(c) {
|
|
24
|
+
if (authJson.configured !== true) {
|
|
25
|
+
return undefined;
|
|
24
26
|
}
|
|
25
|
-
|
|
27
|
+
const authUser = await getAuthUser(c);
|
|
28
|
+
return authUser?.session ?? undefined;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export default
|
|
31
|
+
export default getSession;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { getAuthStrategies, resolveStrategyCaller } from '@lowdefy/api';
|
|
18
|
+
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
19
|
+
|
|
20
|
+
import appMeta from '../../build/appMeta.js';
|
|
21
|
+
import authJson from '../../build/auth.js';
|
|
22
|
+
import strategies from '../../../build/plugins/auth/strategies.js';
|
|
23
|
+
|
|
24
|
+
// Secrets are process-static env, so read once at module load rather than
|
|
25
|
+
// on every sessionless request.
|
|
26
|
+
const secrets = getSecretsFromEnv();
|
|
27
|
+
|
|
28
|
+
// API auth strategies (apiKey/jwt headers) let MCP and service clients that
|
|
29
|
+
// cannot hold a session cookie authenticate. Tried only when no session
|
|
30
|
+
// resolved; a match is synthesized as session.user so createAuthorize and
|
|
31
|
+
// _user work unchanged.
|
|
32
|
+
async function getStrategyCaller(c, logger) {
|
|
33
|
+
const authStrategies = getAuthStrategies({
|
|
34
|
+
appMeta,
|
|
35
|
+
authJson,
|
|
36
|
+
logger,
|
|
37
|
+
plugins: { strategies },
|
|
38
|
+
secrets,
|
|
39
|
+
});
|
|
40
|
+
return resolveStrategyCaller({
|
|
41
|
+
headers: c.req.raw.headers,
|
|
42
|
+
logger,
|
|
43
|
+
strategies: authStrategies,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default getStrategyCaller;
|
|
@@ -59,7 +59,20 @@ function getEventType(error) {
|
|
|
59
59
|
function createHandleError({ context }) {
|
|
60
60
|
return async function handleError(error) {
|
|
61
61
|
try {
|
|
62
|
-
|
|
62
|
+
// Set first, not after the log call: this function is the server's error
|
|
63
|
+
// sink, so reaching it is what makes the error already-logged, and the catch
|
|
64
|
+
// below guarantees something is written even when the steps here fail. The
|
|
65
|
+
// client reads `handled` to decide whether to POST the error to
|
|
66
|
+
// /api/client-error, so a later throw skipping this assignment would have it
|
|
67
|
+
// logged a second time. Not keyed on `source`, which a LowdefyInternalError
|
|
68
|
+
// never gets - location resolution is skipped for it below.
|
|
69
|
+
error.handled = true;
|
|
70
|
+
|
|
71
|
+
// `?? {}` rather than a destructuring default: context.user is null (not
|
|
72
|
+
// undefined) for an anonymous caller and for every system context, so a
|
|
73
|
+
// default would not fire and reading user.id below would throw.
|
|
74
|
+
const headers = context.headers ?? {};
|
|
75
|
+
const user = context.user ?? {};
|
|
63
76
|
const eventType = getEventType(error);
|
|
64
77
|
const isServiceError = error instanceof ServiceError;
|
|
65
78
|
const isLowdefyInternalError = error instanceof LowdefyInternalError;
|
|
@@ -114,7 +127,7 @@ function createHandleError({ context }) {
|
|
|
114
127
|
host: headers.host,
|
|
115
128
|
referer: headers.referer,
|
|
116
129
|
// Non localhost headers
|
|
117
|
-
'x-
|
|
130
|
+
'x-forwarded-for': headers['x-forwarded-for'],
|
|
118
131
|
// Vercel headers
|
|
119
132
|
'x-vercel-id': headers['x-vercel-id'],
|
|
120
133
|
'x-real-ip': headers['x-real-ip'],
|