@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
|
@@ -15,10 +15,27 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { createNodeLogger } from '@lowdefy/logger/node';
|
|
18
|
+
import { type } from '@lowdefy/helpers';
|
|
19
|
+
|
|
20
|
+
import appMeta from '../../build/appMeta.js';
|
|
21
|
+
|
|
22
|
+
// Deploy identity on every log line — pid/hostname are stripped, so these
|
|
23
|
+
// fields are what correlates a line to an app build across replicas.
|
|
24
|
+
const base = { pid: undefined, hostname: undefined };
|
|
25
|
+
if (!type.isNone(appMeta.name)) {
|
|
26
|
+
base.app_name = appMeta.name;
|
|
27
|
+
}
|
|
28
|
+
if (!type.isNone(appMeta.version)) {
|
|
29
|
+
base.app_version = appMeta.version;
|
|
30
|
+
}
|
|
31
|
+
if (!type.isNone(appMeta.gitSha)) {
|
|
32
|
+
base.git_sha = appMeta.gitSha;
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
const logger = createNodeLogger({
|
|
19
36
|
name: 'lowdefy_server',
|
|
20
37
|
level: process.env.LOWDEFY_LOG_LEVEL ?? 'info',
|
|
21
|
-
base
|
|
38
|
+
base,
|
|
22
39
|
});
|
|
23
40
|
|
|
24
41
|
function createLogger(metadata = {}) {
|
|
@@ -14,53 +14,49 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
function logRequest({ context }) {
|
|
17
|
+
// Emits one access-log line per request, after the response is finalized.
|
|
18
|
+
function logRequest({ context, status, durationMs }) {
|
|
19
19
|
const { headers = {}, user = {} } = context;
|
|
20
|
-
context.logger.info(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
context.logger.info(
|
|
21
|
+
{
|
|
22
|
+
user: {
|
|
23
|
+
id: user.id,
|
|
24
|
+
roles: user.roles,
|
|
25
|
+
sub: user.sub,
|
|
26
|
+
session_id: user.session_id, // TODO: Implement session id
|
|
27
|
+
},
|
|
28
|
+
url: context.req.url,
|
|
29
|
+
method: context.req.method,
|
|
30
|
+
hostname: context.req.hostname,
|
|
31
|
+
status,
|
|
32
|
+
duration_ms: durationMs,
|
|
33
|
+
headers: {
|
|
34
|
+
'accept-language': headers['accept-language'],
|
|
35
|
+
'sec-ch-ua-mobile': headers['sec-ch-ua-mobile'],
|
|
36
|
+
'sec-ch-ua-platform': headers['sec-ch-ua-platform'],
|
|
37
|
+
'sec-ch-ua': headers['sec-ch-ua'],
|
|
38
|
+
'user-agent': headers['user-agent'],
|
|
39
|
+
host: headers.host,
|
|
40
|
+
referer: headers.referer,
|
|
41
|
+
'x-forwarded-for': headers['x-forwarded-for'],
|
|
42
|
+
// Vercel headers
|
|
43
|
+
'x-vercel-id': headers['x-vercel-id'],
|
|
44
|
+
'x-real-ip': headers['x-real-ip'],
|
|
45
|
+
'x-vercel-ip-country': headers['x-vercel-ip-country'],
|
|
46
|
+
'x-vercel-ip-country-region': headers['x-vercel-ip-country-region'],
|
|
47
|
+
'x-vercel-ip-city': headers['x-vercel-ip-city'],
|
|
48
|
+
'x-vercel-ip-latitude': headers['x-vercel-ip-latitude'],
|
|
49
|
+
'x-vercel-ip-longitude': headers['x-vercel-ip-longitude'],
|
|
50
|
+
'x-vercel-ip-timezone': headers['x-vercel-ip-timezone'],
|
|
51
|
+
// Cloudflare headers
|
|
52
|
+
'cf-connecting-ip': headers['cf-connecting-ip'],
|
|
53
|
+
'cf-ray': headers['cf-ray'],
|
|
54
|
+
'cf-ipcountry': headers['cf-ipcountry'],
|
|
55
|
+
'cf-visitor': headers['cf-visitor'],
|
|
56
|
+
},
|
|
32
57
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
resolvedUrl: context.nextContext?.resolvedUrl,
|
|
36
|
-
hostname: context.req.hostname,
|
|
37
|
-
headers: {
|
|
38
|
-
'accept-language': headers['accept-language'],
|
|
39
|
-
'sec-ch-ua-mobile': headers['sec-ch-ua-mobile'],
|
|
40
|
-
'sec-ch-ua-platform': headers['sec-ch-ua-platform'],
|
|
41
|
-
'sec-ch-ua': headers['sec-ch-ua'],
|
|
42
|
-
'user-agent': headers['user-agent'],
|
|
43
|
-
host: headers.host,
|
|
44
|
-
referer: headers.referer,
|
|
45
|
-
'x-forward-for': headers['x-forward-for'],
|
|
46
|
-
// Vercel headers
|
|
47
|
-
'x-vercel-id': headers['x-vercel-id'],
|
|
48
|
-
'x-real-ip': headers['x-real-ip'],
|
|
49
|
-
'x-vercel-ip-country': headers['x-vercel-ip-country'],
|
|
50
|
-
'x-vercel-ip-country-region': headers['x-vercel-ip-country-region'],
|
|
51
|
-
'x-vercel-ip-city': headers['x-vercel-ip-city'],
|
|
52
|
-
'x-vercel-ip-latitude': headers['x-vercel-ip-latitude'],
|
|
53
|
-
'x-vercel-ip-longitude': headers['x-vercel-ip-longitude'],
|
|
54
|
-
'x-vercel-ip-timezone': headers['x-vercel-ip-timezone'],
|
|
55
|
-
// Cloudflare headers
|
|
56
|
-
'cf-connecting-ip': headers['cf-connecting-ip'],
|
|
57
|
-
'cf-ray': headers['cf-ray'],
|
|
58
|
-
'cf-ipcountry': headers['cf-ipcountry'],
|
|
59
|
-
'cf-visitor': headers['cf-visitor'],
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
// TODO:
|
|
63
|
-
// Next local? nextContext.locale, nextContext.locales, nextContext.defaultLocale
|
|
58
|
+
'request completed'
|
|
59
|
+
);
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
export default logRequest;
|
|
@@ -14,21 +14,23 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import * as Sentry from '@sentry/
|
|
17
|
+
import * as Sentry from '@sentry/node';
|
|
18
18
|
|
|
19
19
|
import loggerConfig from '../../build/logger.js';
|
|
20
20
|
|
|
21
|
+
// Returns true when Sentry was initialized, so the caller can log it through
|
|
22
|
+
// the structured logger — this module runs before the logger exists.
|
|
21
23
|
function initSentryServer() {
|
|
22
24
|
// No-op if SENTRY_DSN not set
|
|
23
25
|
if (!process.env.SENTRY_DSN) {
|
|
24
|
-
return;
|
|
26
|
+
return false;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
const sentryConfig = loggerConfig.sentry || {};
|
|
28
30
|
|
|
29
31
|
// No-op if server logging is explicitly disabled
|
|
30
32
|
if (sentryConfig.server === false) {
|
|
31
|
-
return;
|
|
33
|
+
return false;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
Sentry.init({
|
|
@@ -37,7 +39,7 @@ function initSentryServer() {
|
|
|
37
39
|
tracesSampleRate: sentryConfig.tracesSampleRate ?? 0.1,
|
|
38
40
|
});
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
return true;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export default initSentryServer;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -28,68 +28,81 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"lib/*",
|
|
30
30
|
"lowdefy/*",
|
|
31
|
-
"pages/*",
|
|
32
31
|
"public_default/*",
|
|
33
|
-
"next.config.js",
|
|
34
|
-
"postcss.config.js",
|
|
35
32
|
"package.original.json",
|
|
36
33
|
".eslintrc.yaml",
|
|
37
|
-
".npmrc"
|
|
34
|
+
".npmrc",
|
|
35
|
+
"src/*",
|
|
36
|
+
"client/*",
|
|
37
|
+
"vite.config.js",
|
|
38
|
+
"postcss.config.cjs"
|
|
38
39
|
],
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@ant-design/cssinjs": "2.1.2",
|
|
41
42
|
"@ant-design/x": "2.7.0",
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@
|
|
45
|
-
"@
|
|
46
|
-
"@lowdefy/
|
|
47
|
-
"@lowdefy/
|
|
48
|
-
"@lowdefy/
|
|
49
|
-
"@lowdefy/blocks-
|
|
50
|
-
"@lowdefy/
|
|
51
|
-
"@lowdefy/
|
|
52
|
-
"@lowdefy/
|
|
53
|
-
"@lowdefy/
|
|
54
|
-
"@lowdefy/
|
|
55
|
-
"@lowdefy/
|
|
56
|
-
"@lowdefy/
|
|
57
|
-
"@lowdefy/
|
|
43
|
+
"@auth/core": "0.41.2",
|
|
44
|
+
"@hono/auth-js": "1.1.1",
|
|
45
|
+
"@hono/mcp": "0.3.0",
|
|
46
|
+
"@hono/node-server": "2.0.4",
|
|
47
|
+
"@lowdefy/actions-core": "6.0.0",
|
|
48
|
+
"@lowdefy/api": "6.0.0",
|
|
49
|
+
"@lowdefy/block-utils": "6.0.0",
|
|
50
|
+
"@lowdefy/blocks-aggrid": "6.0.0",
|
|
51
|
+
"@lowdefy/blocks-antd": "6.0.0",
|
|
52
|
+
"@lowdefy/blocks-antd-x": "6.0.0",
|
|
53
|
+
"@lowdefy/blocks-basic": "6.0.0",
|
|
54
|
+
"@lowdefy/blocks-echarts": "6.0.0",
|
|
55
|
+
"@lowdefy/blocks-files": "6.0.0",
|
|
56
|
+
"@lowdefy/blocks-loaders": "6.0.0",
|
|
57
|
+
"@lowdefy/blocks-markdown": "6.0.0",
|
|
58
|
+
"@lowdefy/blocks-tiptap": "6.0.0",
|
|
59
|
+
"@lowdefy/client": "6.0.0",
|
|
60
|
+
"@lowdefy/errors": "6.0.0",
|
|
61
|
+
"@lowdefy/helpers": "6.0.0",
|
|
62
|
+
"@lowdefy/layout": "6.0.0",
|
|
63
|
+
"@lowdefy/logger": "6.0.0",
|
|
64
|
+
"@lowdefy/node-utils": "6.0.0",
|
|
65
|
+
"@lowdefy/operators-js": "6.0.0",
|
|
66
|
+
"@lowdefy/plugin-next-auth": "6.0.0",
|
|
67
|
+
"@lowdefy/websockets-core": "6.0.0",
|
|
68
|
+
"@sentry/browser": "10.57.0",
|
|
58
69
|
"@sentry/nextjs": "8.53.0",
|
|
59
|
-
"@
|
|
70
|
+
"@sentry/node": "10.57.0",
|
|
71
|
+
"@sentry/vite-plugin": "5.3.0",
|
|
72
|
+
"@tailwindcss/postcss": "4.3.1",
|
|
73
|
+
"@vitejs/plugin-react": "6.0.2",
|
|
60
74
|
"antd": "6.3.1",
|
|
61
|
-
"dayjs": "1.11.
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"pino": "8.16.2",
|
|
75
|
+
"dayjs": "1.11.20",
|
|
76
|
+
"hono": "4.12.25",
|
|
77
|
+
"pino": "10.3.1",
|
|
65
78
|
"react": "18.2.0",
|
|
66
79
|
"react-dom": "18.2.0",
|
|
67
80
|
"react-icons": "5.6.0",
|
|
68
|
-
"tailwindcss": "4.
|
|
69
|
-
"uuid": "
|
|
81
|
+
"tailwindcss": "4.3.1",
|
|
82
|
+
"uuid": "14.0.0",
|
|
83
|
+
"vite": "8.0.16",
|
|
84
|
+
"ws": "8.18.0"
|
|
70
85
|
},
|
|
71
86
|
"devDependencies": {
|
|
72
|
-
"@lowdefy/build": "
|
|
87
|
+
"@lowdefy/build": "6.0.0",
|
|
73
88
|
"@next/eslint-plugin-next": "16.1.6",
|
|
74
|
-
"@tailwindcss/postcss": "4.
|
|
75
|
-
"tailwindcss": "4.
|
|
89
|
+
"@tailwindcss/postcss": "4.3.1",
|
|
90
|
+
"tailwindcss": "4.3.1",
|
|
76
91
|
"webpack": "5.94.0",
|
|
77
|
-
"yaml": "2.
|
|
78
|
-
"yargs": "
|
|
92
|
+
"yaml": "2.9.0",
|
|
93
|
+
"yargs": "18.0.0"
|
|
79
94
|
},
|
|
80
95
|
"engines": {
|
|
81
|
-
"node": ">=
|
|
96
|
+
"node": ">=22"
|
|
82
97
|
},
|
|
83
98
|
"publishConfig": {
|
|
84
99
|
"access": "public"
|
|
85
100
|
},
|
|
101
|
+
"type": "module",
|
|
86
102
|
"scripts": {
|
|
87
103
|
"build": "cp package.json package.original.json || copy package.json package.original.json",
|
|
104
|
+
"build:client": "vite build",
|
|
88
105
|
"build:lowdefy": "node lowdefy/build.mjs",
|
|
89
|
-
"
|
|
90
|
-
"dev": "next dev",
|
|
91
|
-
"start": "next start",
|
|
92
|
-
"lint": "next lint",
|
|
93
|
-
"next": "next"
|
|
106
|
+
"start": "node src/index.js"
|
|
94
107
|
}
|
|
95
108
|
}
|
package/package.original.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -28,69 +28,82 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"lib/*",
|
|
30
30
|
"lowdefy/*",
|
|
31
|
-
"pages/*",
|
|
32
31
|
"public_default/*",
|
|
33
|
-
"next.config.js",
|
|
34
|
-
"postcss.config.js",
|
|
35
32
|
"package.original.json",
|
|
36
33
|
".eslintrc.yaml",
|
|
37
|
-
".npmrc"
|
|
34
|
+
".npmrc",
|
|
35
|
+
"src/*",
|
|
36
|
+
"client/*",
|
|
37
|
+
"vite.config.js",
|
|
38
|
+
"postcss.config.cjs"
|
|
38
39
|
],
|
|
39
40
|
"scripts": {
|
|
40
41
|
"build": "cp package.json package.original.json || copy package.json package.original.json",
|
|
42
|
+
"build:client": "vite build",
|
|
41
43
|
"build:lowdefy": "node lowdefy/build.mjs",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"start": "next start",
|
|
45
|
-
"lint": "next lint",
|
|
46
|
-
"next": "next",
|
|
47
|
-
"prepublishOnly": "pnpm build"
|
|
44
|
+
"prepublishOnly": "pnpm build",
|
|
45
|
+
"start": "node src/index.js"
|
|
48
46
|
},
|
|
49
47
|
"dependencies": {
|
|
50
48
|
"@ant-design/cssinjs": "2.1.2",
|
|
51
49
|
"@ant-design/x": "2.7.0",
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@
|
|
55
|
-
"@
|
|
56
|
-
"@lowdefy/
|
|
57
|
-
"@lowdefy/
|
|
58
|
-
"@lowdefy/
|
|
59
|
-
"@lowdefy/blocks-
|
|
60
|
-
"@lowdefy/
|
|
61
|
-
"@lowdefy/
|
|
62
|
-
"@lowdefy/
|
|
63
|
-
"@lowdefy/
|
|
64
|
-
"@lowdefy/
|
|
65
|
-
"@lowdefy/
|
|
66
|
-
"@lowdefy/
|
|
67
|
-
"@lowdefy/
|
|
50
|
+
"@auth/core": "0.41.2",
|
|
51
|
+
"@hono/auth-js": "1.1.1",
|
|
52
|
+
"@hono/mcp": "0.3.0",
|
|
53
|
+
"@hono/node-server": "2.0.4",
|
|
54
|
+
"@lowdefy/actions-core": "6.0.0",
|
|
55
|
+
"@lowdefy/api": "6.0.0",
|
|
56
|
+
"@lowdefy/block-utils": "6.0.0",
|
|
57
|
+
"@lowdefy/blocks-aggrid": "6.0.0",
|
|
58
|
+
"@lowdefy/blocks-antd": "6.0.0",
|
|
59
|
+
"@lowdefy/blocks-antd-x": "6.0.0",
|
|
60
|
+
"@lowdefy/blocks-basic": "6.0.0",
|
|
61
|
+
"@lowdefy/blocks-echarts": "6.0.0",
|
|
62
|
+
"@lowdefy/blocks-files": "6.0.0",
|
|
63
|
+
"@lowdefy/blocks-loaders": "6.0.0",
|
|
64
|
+
"@lowdefy/blocks-markdown": "6.0.0",
|
|
65
|
+
"@lowdefy/blocks-tiptap": "6.0.0",
|
|
66
|
+
"@lowdefy/client": "6.0.0",
|
|
67
|
+
"@lowdefy/errors": "6.0.0",
|
|
68
|
+
"@lowdefy/helpers": "6.0.0",
|
|
69
|
+
"@lowdefy/layout": "6.0.0",
|
|
70
|
+
"@lowdefy/logger": "6.0.0",
|
|
71
|
+
"@lowdefy/node-utils": "6.0.0",
|
|
72
|
+
"@lowdefy/operators-js": "6.0.0",
|
|
73
|
+
"@lowdefy/plugin-next-auth": "6.0.0",
|
|
74
|
+
"@lowdefy/websockets-core": "6.0.0",
|
|
75
|
+
"@sentry/browser": "10.57.0",
|
|
68
76
|
"@sentry/nextjs": "8.53.0",
|
|
69
|
-
"@
|
|
77
|
+
"@sentry/node": "10.57.0",
|
|
78
|
+
"@sentry/vite-plugin": "5.3.0",
|
|
79
|
+
"@tailwindcss/postcss": "4.3.1",
|
|
80
|
+
"@vitejs/plugin-react": "6.0.2",
|
|
70
81
|
"antd": "6.3.1",
|
|
71
|
-
"dayjs": "1.11.
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"pino": "8.16.2",
|
|
82
|
+
"dayjs": "1.11.20",
|
|
83
|
+
"hono": "4.12.25",
|
|
84
|
+
"pino": "10.3.1",
|
|
75
85
|
"react": "18.2.0",
|
|
76
86
|
"react-dom": "18.2.0",
|
|
77
87
|
"react-icons": "5.6.0",
|
|
78
|
-
"tailwindcss": "4.
|
|
79
|
-
"uuid": "
|
|
88
|
+
"tailwindcss": "4.3.1",
|
|
89
|
+
"uuid": "14.0.0",
|
|
90
|
+
"vite": "8.0.16",
|
|
91
|
+
"ws": "8.18.0"
|
|
80
92
|
},
|
|
81
93
|
"devDependencies": {
|
|
82
|
-
"@lowdefy/build": "
|
|
94
|
+
"@lowdefy/build": "6.0.0",
|
|
83
95
|
"@next/eslint-plugin-next": "16.1.6",
|
|
84
|
-
"@tailwindcss/postcss": "4.
|
|
85
|
-
"tailwindcss": "4.
|
|
96
|
+
"@tailwindcss/postcss": "4.3.1",
|
|
97
|
+
"tailwindcss": "4.3.1",
|
|
86
98
|
"webpack": "5.94.0",
|
|
87
|
-
"yaml": "2.
|
|
88
|
-
"yargs": "
|
|
99
|
+
"yaml": "2.9.0",
|
|
100
|
+
"yargs": "18.0.0"
|
|
89
101
|
},
|
|
90
102
|
"engines": {
|
|
91
|
-
"node": ">=
|
|
103
|
+
"node": ">=22"
|
|
92
104
|
},
|
|
93
105
|
"publishConfig": {
|
|
94
106
|
"access": "public"
|
|
95
|
-
}
|
|
107
|
+
},
|
|
108
|
+
"type": "module"
|
|
96
109
|
}
|
package/src/app.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
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 { Hono } from 'hono';
|
|
18
|
+
import { bodyLimit } from 'hono/body-limit';
|
|
19
|
+
import { compress } from 'hono/compress';
|
|
20
|
+
import { timeout } from 'hono/timeout';
|
|
21
|
+
import { initAuthConfig } from '@hono/auth-js';
|
|
22
|
+
import { serveStatic } from '@hono/node-server/serve-static';
|
|
23
|
+
|
|
24
|
+
import agentHandler from './routes/agent.js';
|
|
25
|
+
import apiContext from './middleware/apiContext.js';
|
|
26
|
+
import apiPageHandler from './routes/apiPage.js';
|
|
27
|
+
import authJson from '../lib/build/auth.js';
|
|
28
|
+
import authMiddleware from './routes/auth.js';
|
|
29
|
+
import clientErrorHandler from './routes/clientError.js';
|
|
30
|
+
import createErrorHandler from './middleware/errorHandler.js';
|
|
31
|
+
import createLogger from '../lib/server/log/createLogger.js';
|
|
32
|
+
import cronForwardHandler from './routes/cronForward.js';
|
|
33
|
+
import cronHandler from './routes/cron.js';
|
|
34
|
+
import detachedHandler from './routes/detached.js';
|
|
35
|
+
import endpointsHandler from './routes/endpoints.js';
|
|
36
|
+
import getAuthConfig from '../lib/server/auth/getAuthConfig.js';
|
|
37
|
+
import lowdefyConfig from '../lib/build/config.js';
|
|
38
|
+
import mcpHandler from './routes/mcp.js';
|
|
39
|
+
import renderPage from './html/renderPage.js';
|
|
40
|
+
import requestHandler from './routes/request.js';
|
|
41
|
+
import sentryMiddleware from './middleware/sentry.js';
|
|
42
|
+
import usageHandler from './routes/usage.js';
|
|
43
|
+
import websocketHandler from './routes/websocket.js';
|
|
44
|
+
|
|
45
|
+
const basePath = lowdefyConfig.basePath ?? '';
|
|
46
|
+
|
|
47
|
+
// Cap how long a single request may run. A request that hangs on an upstream call (DB, SMTP, an
|
|
48
|
+
// external API) would otherwise run to the platform's function limit — on serverless (e.g. Vercel
|
|
49
|
+
// Fluid) that is billed compute. Configured via `config.requestTimeout` (ms) in lowdefy.yaml;
|
|
50
|
+
// defaults to 30s, 0 disables. Agent streaming is exempt below since it is long-lived by design.
|
|
51
|
+
const requestTimeoutMs = lowdefyConfig.requestTimeout ?? 30000;
|
|
52
|
+
|
|
53
|
+
// `serveStaticAssets` is true for the Node server (it serves `dist/client` itself). On a
|
|
54
|
+
// platform that serves the built client + public files from a CDN (e.g. Vercel), pass false so
|
|
55
|
+
// the request handler skips the Node-only static middleware and only owns the dynamic routes.
|
|
56
|
+
function createApp({ serveStaticAssets = true } = {}) {
|
|
57
|
+
const app = basePath ? new Hono().basePath(basePath) : new Hono();
|
|
58
|
+
const logger = createLogger({ server: 'lowdefy' });
|
|
59
|
+
|
|
60
|
+
// Liveness endpoint for container health checks and orchestrator probes. Registered
|
|
61
|
+
// before all middleware so probes skip auth, session, request logging, and Sentry.
|
|
62
|
+
app.get('/api/lowdefy-health', (c) => c.json({ status: 'ok' }));
|
|
63
|
+
|
|
64
|
+
app.use('*', sentryMiddleware());
|
|
65
|
+
|
|
66
|
+
// Bound request duration so a hung upstream returns an error instead of running to the platform's
|
|
67
|
+
// function limit (cost protection on serverless). Agent streaming is long-lived by design, so it
|
|
68
|
+
// is exempt. Configured via `config.requestTimeout` in lowdefy.yaml (0 disables).
|
|
69
|
+
if (requestTimeoutMs > 0) {
|
|
70
|
+
app.use('*', async (c, next) => {
|
|
71
|
+
if (
|
|
72
|
+
c.req.path.includes('/api/agent/') ||
|
|
73
|
+
c.req.path.includes('/api/mcp') ||
|
|
74
|
+
c.req.path.includes('/api/websocket')
|
|
75
|
+
) {
|
|
76
|
+
return next();
|
|
77
|
+
}
|
|
78
|
+
return timeout(requestTimeoutMs)(c, next);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Next.js compressed responses by default — keep parity, but leave
|
|
83
|
+
// streaming responses (agent SSE) and websocket upgrades uncompressed.
|
|
84
|
+
app.use('*', async (c, next) => {
|
|
85
|
+
if (
|
|
86
|
+
c.req.path.includes('/api/agent/') ||
|
|
87
|
+
c.req.path.includes('/api/mcp') ||
|
|
88
|
+
c.req.path.includes('/api/websocket')
|
|
89
|
+
) {
|
|
90
|
+
return next();
|
|
91
|
+
}
|
|
92
|
+
return compress()(c, next);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (authJson.configured === true) {
|
|
96
|
+
app.use(
|
|
97
|
+
'*',
|
|
98
|
+
initAuthConfig(() => getAuthConfig({ logger }))
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
app.use('/api/*', apiContext());
|
|
103
|
+
app.use('/api/auth/*', authMiddleware());
|
|
104
|
+
app.all('/api/request/*', requestHandler);
|
|
105
|
+
// Endpoint payloads may carry base64 file content (emitFileContent + CallAPI);
|
|
106
|
+
// cap bodies at 10 MiB to match the agent route.
|
|
107
|
+
app.all('/api/endpoints/*', bodyLimit({ maxSize: 10 * 1024 * 1024 }), endpointsHandler);
|
|
108
|
+
app.get('/api/cron/*', cronHandler);
|
|
109
|
+
app.get('/api/cron-forward/*', cronForwardHandler);
|
|
110
|
+
app.post('/api/detached/*', detachedHandler);
|
|
111
|
+
app.all('/api/client-error', clientErrorHandler);
|
|
112
|
+
app.all('/api/usage', usageHandler);
|
|
113
|
+
app.all('/api/agent/*', bodyLimit({ maxSize: 10 * 1024 * 1024 }), agentHandler);
|
|
114
|
+
app.all('/api/mcp', bodyLimit({ maxSize: 10 * 1024 * 1024 }), mcpHandler);
|
|
115
|
+
app.get('/api/websocket', websocketHandler);
|
|
116
|
+
app.get('/api/page/*', apiPageHandler);
|
|
117
|
+
|
|
118
|
+
// Vite build output (includes public/ via Vite's publicDir copy). Falls
|
|
119
|
+
// through to the page routes when no file matches.
|
|
120
|
+
if (serveStaticAssets) {
|
|
121
|
+
app.use(
|
|
122
|
+
'/*',
|
|
123
|
+
serveStatic({
|
|
124
|
+
root: './dist/client',
|
|
125
|
+
rewriteRequestPath: basePath ? (path) => path.replace(basePath, '') : undefined,
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
app.use('/*', apiContext());
|
|
131
|
+
app.get('/', (c) => renderPage(c, { pageId: '' }));
|
|
132
|
+
app.get('/404', (c) => renderPage(c, { pageId: '404', status: 404 }));
|
|
133
|
+
app.get('/:rest{.+}', (c) => renderPage(c, { pageId: c.req.param('rest') }));
|
|
134
|
+
|
|
135
|
+
app.onError(createErrorHandler({ basePath, logger }));
|
|
136
|
+
|
|
137
|
+
return app;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default createApp;
|
|
@@ -0,0 +1,45 @@
|
|
|
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 fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
|
|
20
|
+
// The Vite manifest is read once at startup — deploys start a new server
|
|
21
|
+
// process after `vite build` completes, so assets never change at runtime.
|
|
22
|
+
// In-place deploys must build before restarting (documented).
|
|
23
|
+
let assets = null;
|
|
24
|
+
|
|
25
|
+
function getAssets() {
|
|
26
|
+
if (assets) {
|
|
27
|
+
return assets;
|
|
28
|
+
}
|
|
29
|
+
const manifestPath = path.join(process.cwd(), 'dist/client/.vite/manifest.json');
|
|
30
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
31
|
+
const entry = manifest['client/main.jsx'];
|
|
32
|
+
if (!entry) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
'Vite manifest has no "client/main.jsx" entry. Run the client build before starting the server.'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
assets = {
|
|
38
|
+
js: entry.file,
|
|
39
|
+
css: entry.css ?? [],
|
|
40
|
+
imports: (entry.imports ?? []).map((key) => manifest[key]?.file).filter(Boolean),
|
|
41
|
+
};
|
|
42
|
+
return assets;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default getAssets;
|