@lowdefy/server 5.6.0 → 6.1.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 +122 -0
- package/client/clientBuildImports.test.mjs +61 -0
- package/client/main.jsx +39 -0
- package/client/shouldReloadForBuild.js +48 -0
- package/client/shouldReloadForBuild.test.mjs +75 -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} +21 -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/auth/{getAuthOptions.js → getAuthConfig.js} +7 -4
- package/{pages/api/auth/[...nextauth].js → lib/server/auth/session.js} +9 -15
- package/lib/server/auth/strategies.js +47 -0
- package/lib/server/log/createHandleError.js +10 -5
- package/lib/server/log/createHandleError.test.mjs +85 -0
- package/lib/server/log/createLogger.js +28 -1
- package/lib/server/log/createLogger.test.mjs +102 -0
- package/lib/server/log/logRequest.js +47 -45
- package/lib/server/log/maskCredentialKeys.js +37 -0
- package/lib/server/log/projectErrorForLog.js +55 -0
- package/lib/server/log/redactUrlQuery.js +75 -0
- package/lib/server/log/redactUrlQuery.test.mjs +73 -0
- package/lib/server/{auth/getServerSession.js → log/serializeErrorForLog.js} +5 -8
- package/lib/server/log/serializeErrorForLog.test.mjs +325 -0
- package/lib/server/scrubSecrets.js +21 -0
- package/lib/server/sentry/captureSentryError.js +13 -1
- package/lib/server/sentry/captureSentryError.test.mjs +104 -0
- package/lib/server/sentry/initSentry.js +17 -4
- package/lib/server/sentry/initSentry.test.mjs +237 -0
- package/lib/server/sentry/scrubEvent.js +29 -0
- package/lib/server/sentry/scrubEvent.test.mjs +88 -0
- package/lib/server/sentry/setSentryUser.js +1 -1
- package/lowdefy/createCustomPluginTypesMap.mjs +1 -0
- package/package.json +54 -42
- package/package.original.json +56 -44
- 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 +95 -0
- package/src/lib/getPathSegments.js +25 -0
- package/src/lib/safeScriptJson.js +38 -0
- package/src/middleware/apiContext.js +138 -0
- package/src/middleware/apiContext.test.mjs +118 -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 +40 -0
- package/src/routes/apiPage.test.mjs +59 -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/apiWrapper.js +0 -87
- 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 -108
- package/pages/api/endpoints/[...endpointId].js +0 -32
- package/pages/api/usage.js +0 -67
- /package/{postcss.config.js → postcss.config.cjs} +0 -0
|
@@ -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 { forwardScheduledEndpoint } from '@lowdefy/api';
|
|
18
|
+
|
|
19
|
+
import getPathSegments from '../lib/getPathSegments.js';
|
|
20
|
+
|
|
21
|
+
// Triggered by Vercel Cron on the production deployment: an HTTP GET to
|
|
22
|
+
// /api/cron-forward/<environment>/<endpointId>, registered by `lowdefy vercel-output` for the
|
|
23
|
+
// schedules of every environment declared with a cron.secret in config.environments. Vercel fires crons
|
|
24
|
+
// only on production, so production pings that environment's own /api/cron/<endpointId> (with the
|
|
25
|
+
// environment's CRON_SECRET) and answers immediately. Same transport auth as cron (CRON_SECRET,
|
|
26
|
+
// fails closed).
|
|
27
|
+
async function cronForwardHandler(c) {
|
|
28
|
+
if (c.req.method !== 'GET') {
|
|
29
|
+
throw new Error('Only GET requests are supported.');
|
|
30
|
+
}
|
|
31
|
+
const context = c.get('lowdefyContext');
|
|
32
|
+
|
|
33
|
+
const secret = process.env.CRON_SECRET;
|
|
34
|
+
if (!secret || c.req.header('authorization') !== `Bearer ${secret}`) {
|
|
35
|
+
context.logger.warn({ event: 'cron_forward_unauthorized' });
|
|
36
|
+
return c.json({ error: 'Unauthorized' }, 401);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const [environment, ...endpointSegments] = getPathSegments(c, '/api/cron-forward/');
|
|
40
|
+
const endpointId = endpointSegments.join('/');
|
|
41
|
+
const cron = c.req.header('x-vercel-cron-schedule');
|
|
42
|
+
context.logger.info({ event: 'call_cron_forward', environment, endpointId, cron });
|
|
43
|
+
const response = await forwardScheduledEndpoint(context, { environment, endpointId, cron });
|
|
44
|
+
return c.json(response);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default cronForwardHandler;
|
|
@@ -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 { runDetachedEndpoint } from '@lowdefy/api';
|
|
18
|
+
|
|
19
|
+
import getPathSegments from '../lib/getPathSegments.js';
|
|
20
|
+
|
|
21
|
+
// Target of a CallApi step with `detached: true`: the calling invocation
|
|
22
|
+
// fire-and-forgets a POST here, so the endpoint runs in its OWN function
|
|
23
|
+
// invocation with a fresh duration budget (chainable bounded work without a
|
|
24
|
+
// queue). Same transport auth as cron (CRON_SECRET, fails closed) and the same
|
|
25
|
+
// system execution context — InternalApi endpoints are callable, `_user` is
|
|
26
|
+
// undefined. Delivery is at-most-once with no retry: targets must be
|
|
27
|
+
// idempotent.
|
|
28
|
+
async function detachedHandler(c) {
|
|
29
|
+
if (c.req.method !== 'POST') {
|
|
30
|
+
throw new Error('Only POST requests are supported.');
|
|
31
|
+
}
|
|
32
|
+
const context = c.get('lowdefyContext');
|
|
33
|
+
|
|
34
|
+
const secret = process.env.CRON_SECRET;
|
|
35
|
+
if (!secret || c.req.header('authorization') !== `Bearer ${secret}`) {
|
|
36
|
+
context.logger.warn({ event: 'detached_unauthorized' });
|
|
37
|
+
return c.json({ error: 'Unauthorized' }, 401);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const endpointId = getPathSegments(c, '/api/detached/').join('/');
|
|
41
|
+
const { payload } = await c.req.json();
|
|
42
|
+
context.logger.info({ event: 'call_detached_endpoint', endpointId });
|
|
43
|
+
const response = await runDetachedEndpoint(context, { endpointId, payload });
|
|
44
|
+
return c.json(response);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default detachedHandler;
|
|
@@ -0,0 +1,71 @@
|
|
|
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 { callEndpoint, getEndpointConfig, runWebhookEndpoint } from '@lowdefy/api';
|
|
18
|
+
|
|
19
|
+
import getPathSegments from '../lib/getPathSegments.js';
|
|
20
|
+
|
|
21
|
+
async function endpointsHandler(c) {
|
|
22
|
+
if (c.req.method !== 'POST') {
|
|
23
|
+
throw new Error('Only POST requests are supported.');
|
|
24
|
+
}
|
|
25
|
+
const context = c.get('lowdefyContext');
|
|
26
|
+
const endpointId = getPathSegments(c, '/api/endpoints/').join('/');
|
|
27
|
+
|
|
28
|
+
// Endpoints opting in with `webhook: true` are third-party webhook receivers
|
|
29
|
+
// (SNS, Event Grid, Stripe, ...): they take the request RAW — body in the
|
|
30
|
+
// caller's own format (not the { payload } envelope), plus query + headers —
|
|
31
|
+
// and their return value is sent back verbatim, because webhook handshakes
|
|
32
|
+
// (e.g. Event Grid's validationResponse) require exact response shapes.
|
|
33
|
+
// The config lookup is a cached file read; an unknown endpoint falls through
|
|
34
|
+
// to the standard path so its error shape is unchanged. Everything about
|
|
35
|
+
// non-webhook endpoints is untouched.
|
|
36
|
+
let endpointConfig = null;
|
|
37
|
+
try {
|
|
38
|
+
endpointConfig = await getEndpointConfig(context, { endpointId });
|
|
39
|
+
} catch {
|
|
40
|
+
/* unknown endpoint — the standard path below reports it exactly as before */
|
|
41
|
+
}
|
|
42
|
+
if (endpointConfig?.webhook === true) {
|
|
43
|
+
// SNS posts JSON as text/plain — parse regardless of content-type; an
|
|
44
|
+
// unparseable body arrives as the raw string.
|
|
45
|
+
const raw = await c.req.text();
|
|
46
|
+
let body = raw;
|
|
47
|
+
try {
|
|
48
|
+
body = JSON.parse(raw);
|
|
49
|
+
} catch {
|
|
50
|
+
/* non-JSON body — raw string passthrough */
|
|
51
|
+
}
|
|
52
|
+
context.logger.info({ event: 'call_webhook_endpoint', endpointId });
|
|
53
|
+
const result = await runWebhookEndpoint(context, {
|
|
54
|
+
endpointId,
|
|
55
|
+
body,
|
|
56
|
+
query: c.req.query(),
|
|
57
|
+
headers: c.req.header(),
|
|
58
|
+
});
|
|
59
|
+
if (!result.success) {
|
|
60
|
+
return c.json({ error: 'Webhook failed.' }, 500);
|
|
61
|
+
}
|
|
62
|
+
return c.json(result.response ?? { ok: true });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { blockId, payload, pageId } = await c.req.json();
|
|
66
|
+
context.logger.info({ event: 'call_api_endpoint', blockId, endpointId, pageId });
|
|
67
|
+
const response = await callEndpoint(context, { blockId, endpointId, pageId, payload });
|
|
68
|
+
return c.json(response);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default endpointsHandler;
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { StreamableHTTPTransport } from '@hono/mcp';
|
|
18
|
+
import { createMcpServer } from '@lowdefy/api';
|
|
19
|
+
|
|
20
|
+
// Stateless per-request MCP server over the app's configured endpoints. The
|
|
21
|
+
// apiContext middleware has already resolved the caller's session, so tool
|
|
22
|
+
// listing and calls are authorized per request.
|
|
23
|
+
async function mcpHandler(c) {
|
|
24
|
+
const context = c.get('lowdefyContext');
|
|
25
|
+
const server = await createMcpServer({ context });
|
|
26
|
+
if (!server) {
|
|
27
|
+
return c.json({ error: 'MCP is not configured.' }, 404);
|
|
28
|
+
}
|
|
29
|
+
const transport = new StreamableHTTPTransport();
|
|
30
|
+
await server.connect(transport);
|
|
31
|
+
return transport.handleRequest(c);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default mcpHandler;
|
|
@@ -16,23 +16,23 @@
|
|
|
16
16
|
|
|
17
17
|
import { callRequest } from '@lowdefy/api';
|
|
18
18
|
|
|
19
|
-
import
|
|
19
|
+
import getPathSegments from '../lib/getPathSegments.js';
|
|
20
20
|
|
|
21
|
-
async function
|
|
22
|
-
if (req.method !== 'POST') {
|
|
21
|
+
async function requestHandler(c) {
|
|
22
|
+
if (c.req.method !== 'POST') {
|
|
23
23
|
throw new Error('Only POST requests are supported.');
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return;
|
|
25
|
+
const context = c.get('lowdefyContext');
|
|
26
|
+
const segments = getPathSegments(c, '/api/request/');
|
|
27
|
+
if (segments.length < 2) {
|
|
28
|
+
return c.json({ error: 'Invalid request path' }, 400);
|
|
29
29
|
}
|
|
30
30
|
const requestId = segments[segments.length - 1];
|
|
31
31
|
const pageId = segments.slice(0, -1).join('/');
|
|
32
|
-
const { actionId, blockId, payload } = req.
|
|
32
|
+
const { actionId, blockId, payload } = await c.req.json();
|
|
33
33
|
context.logger.info({ event: 'call_request', pageId, requestId, blockId, actionId });
|
|
34
34
|
const response = await callRequest(context, { blockId, pageId, payload, requestId });
|
|
35
|
-
|
|
35
|
+
return c.json(response);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export default
|
|
38
|
+
export default requestHandler;
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
|
|
21
|
+
|
|
22
|
+
async function usageHandler(c) {
|
|
23
|
+
if (c.req.method !== 'POST') {
|
|
24
|
+
throw new Error('Only POST requests are supported.');
|
|
25
|
+
}
|
|
26
|
+
const context = c.get('lowdefyContext');
|
|
27
|
+
const { user, machine } = await c.req.json();
|
|
28
|
+
const host = c.req.header('host');
|
|
29
|
+
context.logger.info({ event: 'log_usage', user, machine });
|
|
30
|
+
|
|
31
|
+
const timestamp = Date.now();
|
|
32
|
+
|
|
33
|
+
return c.json({
|
|
34
|
+
offline: false,
|
|
35
|
+
data: {
|
|
36
|
+
gitSha: context.appMeta.gitSha,
|
|
37
|
+
host,
|
|
38
|
+
machine,
|
|
39
|
+
timestamp,
|
|
40
|
+
user,
|
|
41
|
+
version: packageJson.version,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default usageHandler;
|
|
@@ -0,0 +1,46 @@
|
|
|
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 { upgradeWebSocket } from '@hono/node-server';
|
|
18
|
+
import { createChannelRegistry, createWebSocketConnection } from '@lowdefy/api';
|
|
19
|
+
|
|
20
|
+
// One registry per server process — channels and their running source
|
|
21
|
+
// resolvers are shared across all websocket connections on this instance.
|
|
22
|
+
const registry = createChannelRegistry();
|
|
23
|
+
|
|
24
|
+
const websocketHandler = upgradeWebSocket((c) => {
|
|
25
|
+
const context = c.get('lowdefyContext');
|
|
26
|
+
let connection;
|
|
27
|
+
return {
|
|
28
|
+
onOpen(event, ws) {
|
|
29
|
+
connection = createWebSocketConnection(context, {
|
|
30
|
+
registry,
|
|
31
|
+
send: (message) => ws.send(message),
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
onMessage(event) {
|
|
35
|
+
connection?.handleMessage(event.data);
|
|
36
|
+
},
|
|
37
|
+
onClose() {
|
|
38
|
+
connection?.close();
|
|
39
|
+
},
|
|
40
|
+
onError(event) {
|
|
41
|
+
context.logger.debug({ event: 'ws_error' }, String(event?.error ?? 'WebSocket error'));
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
export default websocketHandler;
|
package/vite.config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
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 { defineConfig } from 'vite';
|
|
19
|
+
import react from '@vitejs/plugin-react';
|
|
20
|
+
import { sentryVitePlugin } from '@sentry/vite-plugin';
|
|
21
|
+
|
|
22
|
+
// basePath from the Lowdefy build — assets are served under it.
|
|
23
|
+
let basePath = '';
|
|
24
|
+
try {
|
|
25
|
+
const config = JSON.parse(fs.readFileSync('./build/config.json', 'utf8'));
|
|
26
|
+
basePath = config.basePath ?? '';
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// No build yet (e.g. editor tooling) — default base.
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const uploadSourceMaps = Boolean(process.env.SENTRY_AUTH_TOKEN);
|
|
32
|
+
|
|
33
|
+
export default defineConfig(({ mode }) => ({
|
|
34
|
+
base: `${basePath}/`,
|
|
35
|
+
plugins: [
|
|
36
|
+
react(),
|
|
37
|
+
uploadSourceMaps &&
|
|
38
|
+
sentryVitePlugin({
|
|
39
|
+
org: process.env.SENTRY_ORG,
|
|
40
|
+
project: process.env.SENTRY_PROJECT,
|
|
41
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
42
|
+
}),
|
|
43
|
+
].filter(Boolean),
|
|
44
|
+
define: {
|
|
45
|
+
// Vite does not replace process.env.NODE_ENV inside dependencies —
|
|
46
|
+
// plugin and client code branch on it.
|
|
47
|
+
'process.env.NODE_ENV': JSON.stringify(mode === 'production' ? 'production' : 'development'),
|
|
48
|
+
},
|
|
49
|
+
build: {
|
|
50
|
+
outDir: 'dist/client',
|
|
51
|
+
manifest: true,
|
|
52
|
+
sourcemap: uploadSourceMaps,
|
|
53
|
+
rollupOptions: {
|
|
54
|
+
input: 'client/main.jsx',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
resolve: {
|
|
58
|
+
// linked plugin packages (pnpm link: / workspace) must share one React.
|
|
59
|
+
dedupe: ['react', 'react-dom'],
|
|
60
|
+
},
|
|
61
|
+
}));
|
package/.eslintrc.yaml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
extends: 'plugin:@next/next/core-web-vitals'
|
package/lib/client/Page.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
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 from 'react';
|
|
18
|
-
|
|
19
|
-
import { useRouter } from 'next/router';
|
|
20
|
-
import Client from '@lowdefy/client';
|
|
21
|
-
import Head from 'next/head';
|
|
22
|
-
import Link from 'next/link';
|
|
23
|
-
|
|
24
|
-
import actions from '../../build/plugins/actions.js';
|
|
25
|
-
import blockMetas from '../../build/plugins/blockMetas.json';
|
|
26
|
-
import blocks from '../../build/plugins/blocks.js';
|
|
27
|
-
import icons from '../../build/plugins/icons.js';
|
|
28
|
-
import operators from '../../build/plugins/operators/client.js';
|
|
29
|
-
import jsMap from '../../build/plugins/operators/clientJsMap.js';
|
|
30
|
-
|
|
31
|
-
const Page = ({ auth, lowdefy, pageConfig, rootConfig }) => {
|
|
32
|
-
const router = useRouter();
|
|
33
|
-
return (
|
|
34
|
-
<Client
|
|
35
|
-
auth={auth}
|
|
36
|
-
Components={{ Head, Link }}
|
|
37
|
-
config={{
|
|
38
|
-
pageConfig,
|
|
39
|
-
rootConfig,
|
|
40
|
-
}}
|
|
41
|
-
jsMap={jsMap}
|
|
42
|
-
lowdefy={lowdefy}
|
|
43
|
-
router={router}
|
|
44
|
-
types={{
|
|
45
|
-
actions,
|
|
46
|
-
blockMetas,
|
|
47
|
-
blocks,
|
|
48
|
-
icons,
|
|
49
|
-
operators,
|
|
50
|
-
}}
|
|
51
|
-
window={window}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export default Page;
|
package/lib/server/apiWrapper.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
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 path from 'path';
|
|
18
|
-
import { createApiContext, redactErrorResponse } from '@lowdefy/api';
|
|
19
|
-
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
20
|
-
import { v4 as uuid } from 'uuid';
|
|
21
|
-
|
|
22
|
-
import agents from '../../build/plugins/agents.js';
|
|
23
|
-
import appMeta from '../build/appMeta.js';
|
|
24
|
-
import config from '../build/config.js';
|
|
25
|
-
import i18nConfig from '../build/i18n.js';
|
|
26
|
-
import connections from '../../build/plugins/connections.js';
|
|
27
|
-
import createLogger from './log/createLogger.js';
|
|
28
|
-
import fileCache from './fileCache.js';
|
|
29
|
-
import getServerSession from './auth/getServerSession.js';
|
|
30
|
-
import createHandleError from './log/createHandleError.js';
|
|
31
|
-
import logRequest from './log/logRequest.js';
|
|
32
|
-
import operators from '../../build/plugins/operators/server.js';
|
|
33
|
-
import jsMap from '../../build/plugins/operators/serverJsMap.js';
|
|
34
|
-
import getAuthOptions from './auth/getAuthOptions.js';
|
|
35
|
-
import loggerConfig from '../build/logger.js';
|
|
36
|
-
import setSentryUser from './sentry/setSentryUser.js';
|
|
37
|
-
|
|
38
|
-
const secrets = getSecretsFromEnv();
|
|
39
|
-
|
|
40
|
-
function apiWrapper(handler) {
|
|
41
|
-
return async function wrappedHandler(req, res) {
|
|
42
|
-
const context = {
|
|
43
|
-
// Important to give absolute path so Next can trace build files
|
|
44
|
-
rid: uuid(),
|
|
45
|
-
agents,
|
|
46
|
-
appMeta,
|
|
47
|
-
buildDirectory: path.join(process.cwd(), 'build'),
|
|
48
|
-
config,
|
|
49
|
-
connections,
|
|
50
|
-
fileCache,
|
|
51
|
-
headers: req?.headers,
|
|
52
|
-
i18n: i18nConfig,
|
|
53
|
-
jsMap,
|
|
54
|
-
handleError: async (err) => {
|
|
55
|
-
console.error(err);
|
|
56
|
-
},
|
|
57
|
-
logger: console,
|
|
58
|
-
operators,
|
|
59
|
-
req,
|
|
60
|
-
res,
|
|
61
|
-
secrets,
|
|
62
|
-
};
|
|
63
|
-
try {
|
|
64
|
-
context.logger = createLogger({ rid: context.rid });
|
|
65
|
-
context.handleError = createHandleError({ context });
|
|
66
|
-
context.authOptions = getAuthOptions(context);
|
|
67
|
-
if (!req.url.startsWith('/api/auth')) {
|
|
68
|
-
context.session = await getServerSession(context);
|
|
69
|
-
// Set Sentry user context for authenticated requests
|
|
70
|
-
setSentryUser({
|
|
71
|
-
user: context.session?.user,
|
|
72
|
-
sentryConfig: loggerConfig.sentry,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
createApiContext(context);
|
|
76
|
-
logRequest({ context });
|
|
77
|
-
// Await here so that if handler throws it is caught.
|
|
78
|
-
const response = await handler({ context, req, res });
|
|
79
|
-
return response;
|
|
80
|
-
} catch (error) {
|
|
81
|
-
await context.handleError(error);
|
|
82
|
-
res.status(500).json(redactErrorResponse(context, error));
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export default apiWrapper;
|
|
@@ -1,66 +0,0 @@
|
|
|
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 path from 'path';
|
|
18
|
-
import { createApiContext } from '@lowdefy/api';
|
|
19
|
-
import { v4 as uuid } from 'uuid';
|
|
20
|
-
|
|
21
|
-
import appMeta from '../build/appMeta.js';
|
|
22
|
-
import config from '../build/config.js';
|
|
23
|
-
import createLogger from './log/createLogger.js';
|
|
24
|
-
import fileCache from './fileCache.js';
|
|
25
|
-
import getServerSession from './auth/getServerSession.js';
|
|
26
|
-
import createHandleError from './log/createHandleError.js';
|
|
27
|
-
import logRequest from './log/logRequest.js';
|
|
28
|
-
import getAuthOptions from './auth/getAuthOptions.js';
|
|
29
|
-
|
|
30
|
-
// TODO: Merge serverSidePropsWrapper and apiWrapper?
|
|
31
|
-
function serverSidePropsWrapper(handler) {
|
|
32
|
-
return async function wrappedHandler(nextContext) {
|
|
33
|
-
const context = {
|
|
34
|
-
// Important to give absolute path so Next can trace build files
|
|
35
|
-
rid: uuid(),
|
|
36
|
-
appMeta,
|
|
37
|
-
buildDirectory: path.join(process.cwd(), 'build'),
|
|
38
|
-
config,
|
|
39
|
-
fileCache,
|
|
40
|
-
handleError: async (err) => {
|
|
41
|
-
console.error(err);
|
|
42
|
-
},
|
|
43
|
-
headers: nextContext?.req?.headers,
|
|
44
|
-
logger: console,
|
|
45
|
-
nextContext,
|
|
46
|
-
req: nextContext?.req,
|
|
47
|
-
res: nextContext?.res,
|
|
48
|
-
};
|
|
49
|
-
try {
|
|
50
|
-
context.logger = createLogger({ rid: context.rid });
|
|
51
|
-
context.handleError = createHandleError({ context });
|
|
52
|
-
context.authOptions = getAuthOptions(context);
|
|
53
|
-
context.session = await getServerSession(context);
|
|
54
|
-
createApiContext(context);
|
|
55
|
-
logRequest({ context });
|
|
56
|
-
// Await here so that if handler throws it is caught.
|
|
57
|
-
const response = await handler({ context, nextContext });
|
|
58
|
-
return response;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
await context.handleError(error);
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export default serverSidePropsWrapper;
|
package/next.config.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
const { withSentryConfig } = require('@sentry/nextjs');
|
|
2
|
-
const lowdefyConfig = require('./build/config.json');
|
|
3
|
-
const blockPackages = require('./build/blockPackages.json');
|
|
4
|
-
const agentFileSystems = require('./build/agentFileSystems.json');
|
|
5
|
-
const serverExternalPackages = require('./build/serverExternalPackages.json');
|
|
6
|
-
|
|
7
|
-
const nextConfig = {
|
|
8
|
-
basePath: lowdefyConfig.basePath,
|
|
9
|
-
reactStrictMode: true,
|
|
10
|
-
transpilePackages: [
|
|
11
|
-
'@lowdefy/client',
|
|
12
|
-
'@ant-design/x',
|
|
13
|
-
'@ant-design/x-markdown',
|
|
14
|
-
...blockPackages,
|
|
15
|
-
],
|
|
16
|
-
serverExternalPackages,
|
|
17
|
-
turbopack: {},
|
|
18
|
-
poweredByHeader: false,
|
|
19
|
-
// productionBrowserSourceMaps: true
|
|
20
|
-
output: process.env.LOWDEFY_BUILD_OUTPUT_STANDALONE === '1' ? 'standalone' : undefined,
|
|
21
|
-
outputFileTracingIncludes: agentFileSystems.length
|
|
22
|
-
? { '/api/agent/*': agentFileSystems.map((p) => `${p}/**/*`) }
|
|
23
|
-
: undefined,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Only wrap with Sentry if SENTRY_DSN is configured
|
|
27
|
-
// This enables source map uploads when SENTRY_AUTH_TOKEN is present
|
|
28
|
-
module.exports = process.env.SENTRY_DSN
|
|
29
|
-
? withSentryConfig(nextConfig, {
|
|
30
|
-
// Sentry options
|
|
31
|
-
silent: true,
|
|
32
|
-
hideSourceMaps: true,
|
|
33
|
-
})
|
|
34
|
-
: nextConfig;
|