@lowdefy/server 4.5.2 → 4.7.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/lib/build/app.js +19 -0
- package/lib/build/auth.js +19 -0
- package/lib/build/config.js +19 -0
- package/lib/build/logger.js +19 -0
- package/lib/client/Page.js +1 -1
- package/lib/client/auth/Auth.js +2 -2
- package/lib/client/auth/AuthConfigured.js +2 -2
- package/lib/client/auth/AuthNotConfigured.js +1 -1
- package/lib/client/createLogUsage.js +1 -1
- package/lib/client/sentry/captureSentryError.js +43 -0
- package/lib/client/sentry/initSentryClient.js +55 -0
- package/lib/client/sentry/setSentryUser.js +41 -0
- package/lib/server/apiWrapper.js +23 -5
- package/lib/server/auth/getAuthOptions.js +2 -2
- package/lib/server/auth/getServerSession.js +2 -2
- package/lib/server/fileCache.js +1 -1
- package/lib/server/log/createHandleError.js +151 -0
- package/lib/server/log/createLogger.js +3 -5
- package/lib/server/log/logRequest.js +1 -1
- package/lib/server/sentry/captureSentryError.js +57 -0
- package/lib/server/sentry/initSentry.js +44 -0
- package/lib/server/sentry/setSentryUser.js +46 -0
- package/lib/server/serverSidePropsWrapper.js +8 -4
- package/lowdefy/build.mjs +13 -10
- package/lowdefy/createCustomPluginTypesMap.mjs +1 -1
- package/next.config.js +12 -1
- package/package.json +24 -20
- package/package.original.json +24 -20
- package/pages/404.js +2 -2
- package/pages/[pageId].js +1 -1
- package/pages/_app.js +26 -3
- package/pages/_document.js +3 -3
- package/pages/api/auth/[...nextauth].js +2 -2
- package/pages/api/client-error.js +58 -0
- package/pages/api/endpoints/[endpointId].js +1 -1
- package/pages/api/request/[pageId]/[requestId].js +3 -3
- package/pages/api/usage.js +2 -2
- package/pages/index.js +1 -1
- package/lib/server/log/logError.js +0 -72
package/lib/build/app.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
import { serializer } from '@lowdefy/helpers';
|
|
17
|
+
import raw from '../../build/app.json';
|
|
18
|
+
|
|
19
|
+
export default serializer.deserialize(raw);
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
import { serializer } from '@lowdefy/helpers';
|
|
17
|
+
import raw from '../../build/auth.json';
|
|
18
|
+
|
|
19
|
+
export default serializer.deserialize(raw);
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
import { serializer } from '@lowdefy/helpers';
|
|
17
|
+
import raw from '../../build/config.json';
|
|
18
|
+
|
|
19
|
+
export default serializer.deserialize(raw);
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
import { serializer } from '@lowdefy/helpers';
|
|
17
|
+
import raw from '../../build/logger.json';
|
|
18
|
+
|
|
19
|
+
export default serializer.deserialize(raw);
|
package/lib/client/Page.js
CHANGED
package/lib/client/auth/Auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -20,7 +20,7 @@ import React from 'react';
|
|
|
20
20
|
import AuthConfigured from './AuthConfigured.js';
|
|
21
21
|
import AuthNotConfigured from './AuthNotConfigured.js';
|
|
22
22
|
|
|
23
|
-
import authConfig from '
|
|
23
|
+
import authConfig from '../../build/auth.js';
|
|
24
24
|
|
|
25
25
|
function Auth({ children, session }) {
|
|
26
26
|
if (authConfig.configured === true) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import React, { useEffect, useRef } from 'react';
|
|
20
20
|
import { getSession, SessionProvider, signIn, signOut, useSession } from 'next-auth/react';
|
|
21
21
|
|
|
22
|
-
import lowdefyConfig from '
|
|
22
|
+
import lowdefyConfig from '../../build/config.js';
|
|
23
23
|
|
|
24
24
|
function Session({ children }) {
|
|
25
25
|
const wasAuthenticated = useRef(false);
|
|
@@ -0,0 +1,43 @@
|
|
|
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 * as Sentry from '@sentry/nextjs';
|
|
18
|
+
|
|
19
|
+
function captureSentryError({ error, pageId, blockId, configLocation }) {
|
|
20
|
+
const tags = {};
|
|
21
|
+
const extra = {};
|
|
22
|
+
|
|
23
|
+
// Add Lowdefy-specific context
|
|
24
|
+
if (pageId) {
|
|
25
|
+
tags.pageId = pageId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (blockId) {
|
|
29
|
+
tags.blockId = blockId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Add config location context
|
|
33
|
+
if (configLocation) {
|
|
34
|
+
extra.configLocation = configLocation;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Sentry.captureException(error, {
|
|
38
|
+
tags,
|
|
39
|
+
extra,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default captureSentryError;
|
|
@@ -0,0 +1,55 @@
|
|
|
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 * as Sentry from '@sentry/nextjs';
|
|
18
|
+
|
|
19
|
+
let initialized = false;
|
|
20
|
+
|
|
21
|
+
function initSentryClient({ sentryDsn, sentryConfig }) {
|
|
22
|
+
// No-op if already initialized
|
|
23
|
+
if (initialized) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// No-op if SENTRY_DSN not set
|
|
28
|
+
if (!sentryDsn) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// No-op if client logging is explicitly disabled
|
|
33
|
+
if (sentryConfig?.client === false) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const config = sentryConfig || {};
|
|
38
|
+
|
|
39
|
+
Sentry.init({
|
|
40
|
+
dsn: sentryDsn,
|
|
41
|
+
environment: config.environment || process.env.NODE_ENV || 'production',
|
|
42
|
+
tracesSampleRate: config.tracesSampleRate ?? 0.1,
|
|
43
|
+
replaysSessionSampleRate: config.replaysSessionSampleRate ?? 0,
|
|
44
|
+
replaysOnErrorSampleRate: config.replaysOnErrorSampleRate ?? 0.1,
|
|
45
|
+
integrations: [
|
|
46
|
+
Sentry.replayIntegration(),
|
|
47
|
+
...(config.feedback ? [Sentry.feedbackIntegration({ colorScheme: 'system' })] : []),
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
initialized = true;
|
|
52
|
+
console.log('Sentry enabled: client');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default initSentryClient;
|
|
@@ -0,0 +1,41 @@
|
|
|
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 * as Sentry from '@sentry/nextjs';
|
|
18
|
+
|
|
19
|
+
function setSentryUser({ user, sentryConfig }) {
|
|
20
|
+
// No-op if no user
|
|
21
|
+
if (!user) {
|
|
22
|
+
Sentry.setUser(null);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const userFields = sentryConfig?.userFields || ['id', '_id'];
|
|
27
|
+
const sentryUser = {};
|
|
28
|
+
|
|
29
|
+
userFields.forEach((field) => {
|
|
30
|
+
if (user[field] !== undefined) {
|
|
31
|
+
sentryUser[field] = user[field];
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Only set user if we have at least one field
|
|
36
|
+
if (Object.keys(sentryUser).length > 0) {
|
|
37
|
+
Sentry.setUser(sentryUser);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default setSentryUser;
|
package/lib/server/apiWrapper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -17,18 +17,21 @@
|
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import { createApiContext } from '@lowdefy/api';
|
|
19
19
|
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
20
|
+
import { serializer } from '@lowdefy/helpers';
|
|
20
21
|
import { v4 as uuid } from 'uuid';
|
|
21
22
|
|
|
22
|
-
import config from '
|
|
23
|
+
import config from '../build/config.js';
|
|
23
24
|
import connections from '../../build/plugins/connections.js';
|
|
24
25
|
import createLogger from './log/createLogger.js';
|
|
25
26
|
import fileCache from './fileCache.js';
|
|
26
27
|
import getServerSession from './auth/getServerSession.js';
|
|
27
|
-
import
|
|
28
|
+
import createHandleError from './log/createHandleError.js';
|
|
28
29
|
import logRequest from './log/logRequest.js';
|
|
29
30
|
import operators from '../../build/plugins/operators/server.js';
|
|
30
31
|
import jsMap from '../../build/plugins/operators/serverJsMap.js';
|
|
31
32
|
import getAuthOptions from './auth/getAuthOptions.js';
|
|
33
|
+
import loggerConfig from '../build/logger.js';
|
|
34
|
+
import setSentryUser from './sentry/setSentryUser.js';
|
|
32
35
|
|
|
33
36
|
const secrets = getSecretsFromEnv();
|
|
34
37
|
|
|
@@ -43,6 +46,9 @@ function apiWrapper(handler) {
|
|
|
43
46
|
fileCache,
|
|
44
47
|
headers: req?.headers,
|
|
45
48
|
jsMap,
|
|
49
|
+
handleError: async (err) => {
|
|
50
|
+
console.error(err);
|
|
51
|
+
},
|
|
46
52
|
logger: console,
|
|
47
53
|
operators,
|
|
48
54
|
req,
|
|
@@ -51,9 +57,15 @@ function apiWrapper(handler) {
|
|
|
51
57
|
};
|
|
52
58
|
try {
|
|
53
59
|
context.logger = createLogger({ rid: context.rid });
|
|
60
|
+
context.handleError = createHandleError({ context });
|
|
54
61
|
context.authOptions = getAuthOptions(context);
|
|
55
62
|
if (!req.url.startsWith('/api/auth')) {
|
|
56
63
|
context.session = await getServerSession(context);
|
|
64
|
+
// Set Sentry user context for authenticated requests
|
|
65
|
+
setSentryUser({
|
|
66
|
+
user: context.session?.user,
|
|
67
|
+
sentryConfig: loggerConfig.sentry,
|
|
68
|
+
});
|
|
57
69
|
}
|
|
58
70
|
createApiContext(context);
|
|
59
71
|
logRequest({ context });
|
|
@@ -61,8 +73,14 @@ function apiWrapper(handler) {
|
|
|
61
73
|
const response = await handler({ context, req, res });
|
|
62
74
|
return response;
|
|
63
75
|
} catch (error) {
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
await context.handleError(error);
|
|
77
|
+
const serialized = serializer.serialize(error);
|
|
78
|
+
if (serialized?.['~e']) {
|
|
79
|
+
delete serialized['~e'].received;
|
|
80
|
+
delete serialized['~e'].stack;
|
|
81
|
+
delete serialized['~e'].configKey;
|
|
82
|
+
}
|
|
83
|
+
res.status(500).json(serialized);
|
|
66
84
|
}
|
|
67
85
|
};
|
|
68
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -18,7 +18,7 @@ import { getNextAuthConfig } 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 authJson from '
|
|
21
|
+
import authJson from '../../build/auth.js';
|
|
22
22
|
import callbacks from '../../../build/plugins/auth/callbacks.js';
|
|
23
23
|
import events from '../../../build/plugins/auth/events.js';
|
|
24
24
|
import providers from '../../../build/plugins/auth/providers.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { getServerSession as getNextAuthServerSession } from 'next-auth/next';
|
|
18
18
|
|
|
19
|
-
import authJson from '
|
|
19
|
+
import authJson from '../../build/auth.js';
|
|
20
20
|
|
|
21
21
|
function getServerSession({ authOptions, req, res }) {
|
|
22
22
|
if (authJson.configured === true) {
|
package/lib/server/fileCache.js
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
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 {
|
|
18
|
+
ActionError,
|
|
19
|
+
BlockError,
|
|
20
|
+
ConfigError,
|
|
21
|
+
LowdefyInternalError,
|
|
22
|
+
OperatorError,
|
|
23
|
+
PluginError,
|
|
24
|
+
RequestError,
|
|
25
|
+
loadAndResolveErrorLocation,
|
|
26
|
+
ServiceError,
|
|
27
|
+
} from '@lowdefy/errors';
|
|
28
|
+
|
|
29
|
+
import captureSentryError from '../sentry/captureSentryError.js';
|
|
30
|
+
|
|
31
|
+
function getEventType(error) {
|
|
32
|
+
if (error instanceof ServiceError) {
|
|
33
|
+
return 'service_error';
|
|
34
|
+
}
|
|
35
|
+
if (error instanceof OperatorError) {
|
|
36
|
+
return 'operator_error';
|
|
37
|
+
}
|
|
38
|
+
if (error instanceof ActionError) {
|
|
39
|
+
return 'action_error';
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof RequestError) {
|
|
42
|
+
return 'request_error';
|
|
43
|
+
}
|
|
44
|
+
if (error instanceof BlockError) {
|
|
45
|
+
return 'block_error';
|
|
46
|
+
}
|
|
47
|
+
if (error instanceof PluginError) {
|
|
48
|
+
return 'plugin_error';
|
|
49
|
+
}
|
|
50
|
+
if (error instanceof ConfigError) {
|
|
51
|
+
return 'config_error';
|
|
52
|
+
}
|
|
53
|
+
if (error instanceof LowdefyInternalError) {
|
|
54
|
+
return 'lowdefy_error';
|
|
55
|
+
}
|
|
56
|
+
return 'error';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createHandleError({ context }) {
|
|
60
|
+
return async function handleError(error) {
|
|
61
|
+
try {
|
|
62
|
+
const { headers = {}, user = {} } = context;
|
|
63
|
+
const eventType = getEventType(error);
|
|
64
|
+
const isServiceError = error instanceof ServiceError;
|
|
65
|
+
const isLowdefyInternalError = error instanceof LowdefyInternalError;
|
|
66
|
+
|
|
67
|
+
// For internal lowdefy errors, don't resolve config location
|
|
68
|
+
const location = isLowdefyInternalError
|
|
69
|
+
? null
|
|
70
|
+
: await loadAndResolveErrorLocation({
|
|
71
|
+
error,
|
|
72
|
+
readConfigFile: context.readConfigFile,
|
|
73
|
+
configDirectory: context.configDirectory,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Attach resolved location to error for consistency
|
|
77
|
+
if (location) {
|
|
78
|
+
error.source = location.source;
|
|
79
|
+
error.config = location.config;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Single structured log call — pino serializes error via extractErrorProps,
|
|
83
|
+
// message string gives human-readable display via CLI logger
|
|
84
|
+
const errorName = error?.name || 'Error';
|
|
85
|
+
context.logger.error(
|
|
86
|
+
{
|
|
87
|
+
err: error,
|
|
88
|
+
// Top-level fields for log drain consumers (duplicated from err for queryability)
|
|
89
|
+
event: eventType,
|
|
90
|
+
errorName,
|
|
91
|
+
errorMessage: error.message,
|
|
92
|
+
isServiceError,
|
|
93
|
+
pageId: context.pageId || null,
|
|
94
|
+
timestamp: new Date().toISOString(),
|
|
95
|
+
source: error.source || null,
|
|
96
|
+
config: error.config || null,
|
|
97
|
+
// Production fields
|
|
98
|
+
user: {
|
|
99
|
+
id: user.id,
|
|
100
|
+
roles: user.roles,
|
|
101
|
+
sub: user.sub,
|
|
102
|
+
session_id: user.session_id,
|
|
103
|
+
},
|
|
104
|
+
url: context.req.url,
|
|
105
|
+
method: context.req.method,
|
|
106
|
+
resolvedUrl: context.nextContext?.resolvedUrl,
|
|
107
|
+
hostname: context.req.hostname,
|
|
108
|
+
headers: {
|
|
109
|
+
'accept-language': headers['accept-language'],
|
|
110
|
+
'sec-ch-ua-mobile': headers['sec-ch-ua-mobile'],
|
|
111
|
+
'sec-ch-ua-platform': headers['sec-ch-ua-platform'],
|
|
112
|
+
'sec-ch-ua': headers['sec-ch-ua'],
|
|
113
|
+
'user-agent': headers['user-agent'],
|
|
114
|
+
host: headers.host,
|
|
115
|
+
referer: headers.referer,
|
|
116
|
+
// Non localhost headers
|
|
117
|
+
'x-forward-for': headers['x-forward-for'],
|
|
118
|
+
// Vercel headers
|
|
119
|
+
'x-vercel-id': headers['x-vercel-id'],
|
|
120
|
+
'x-real-ip': headers['x-real-ip'],
|
|
121
|
+
'x-vercel-ip-country': headers['x-vercel-ip-country'],
|
|
122
|
+
'x-vercel-ip-country-region': headers['x-vercel-ip-country-region'],
|
|
123
|
+
'x-vercel-ip-city': headers['x-vercel-ip-city'],
|
|
124
|
+
'x-vercel-ip-latitude': headers['x-vercel-ip-latitude'],
|
|
125
|
+
'x-vercel-ip-longitude': headers['x-vercel-ip-longitude'],
|
|
126
|
+
'x-vercel-ip-timezone': headers['x-vercel-ip-timezone'],
|
|
127
|
+
// Cloudflare headers
|
|
128
|
+
'cf-connecting-ip': headers['cf-connecting-ip'],
|
|
129
|
+
'cf-ray': headers['cf-ray'],
|
|
130
|
+
'cf-ipcountry': headers['cf-ipcountry'],
|
|
131
|
+
'cf-visitor': headers['cf-visitor'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
error.message
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
// Capture error to Sentry (no-op if Sentry not configured)
|
|
138
|
+
captureSentryError({
|
|
139
|
+
error,
|
|
140
|
+
context,
|
|
141
|
+
configLocation: location,
|
|
142
|
+
});
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.error(error);
|
|
145
|
+
console.error('An error occurred while logging the error.');
|
|
146
|
+
console.error(e);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default createHandleError;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
// TODO: Pino does not serialize error.cause properties if the cause object is not an Error (or Error-like)
|
|
20
|
-
const logger = pino({
|
|
17
|
+
import { createNodeLogger } from '@lowdefy/logger/node';
|
|
18
|
+
const logger = createNodeLogger({
|
|
21
19
|
name: 'lowdefy_server',
|
|
22
20
|
level: process.env.LOWDEFY_LOG_LEVEL ?? 'info',
|
|
23
21
|
base: { pid: undefined, hostname: undefined },
|
|
@@ -0,0 +1,57 @@
|
|
|
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 * as Sentry from '@sentry/nextjs';
|
|
18
|
+
|
|
19
|
+
function captureSentryError({ error, context, configLocation }) {
|
|
20
|
+
// No-op if Sentry not initialized (DSN not set)
|
|
21
|
+
if (!process.env.SENTRY_DSN) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const tags = {};
|
|
26
|
+
const extra = {};
|
|
27
|
+
|
|
28
|
+
// Add Lowdefy-specific context
|
|
29
|
+
if (context?.pageId) {
|
|
30
|
+
tags.pageId = context.pageId;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (error?.blockId) {
|
|
34
|
+
tags.blockId = error.blockId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (error?.isServiceError !== undefined) {
|
|
38
|
+
tags.isServiceError = error.isServiceError;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Add config location context
|
|
42
|
+
if (configLocation) {
|
|
43
|
+
extra.configLocation = configLocation;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add config key for reference
|
|
47
|
+
if (error?.configKey) {
|
|
48
|
+
extra.configKey = error.configKey;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Sentry.captureException(error, {
|
|
52
|
+
tags,
|
|
53
|
+
extra,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default captureSentryError;
|
|
@@ -0,0 +1,44 @@
|
|
|
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 * as Sentry from '@sentry/nextjs';
|
|
18
|
+
|
|
19
|
+
import loggerConfig from '../../build/logger.js';
|
|
20
|
+
|
|
21
|
+
function initSentryServer() {
|
|
22
|
+
// No-op if SENTRY_DSN not set
|
|
23
|
+
if (!process.env.SENTRY_DSN) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const sentryConfig = loggerConfig.sentry || {};
|
|
28
|
+
|
|
29
|
+
// No-op if server logging is explicitly disabled
|
|
30
|
+
if (sentryConfig.server === false) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Sentry.init({
|
|
35
|
+
dsn: process.env.SENTRY_DSN,
|
|
36
|
+
environment: sentryConfig.environment || process.env.NODE_ENV || 'production',
|
|
37
|
+
tracesSampleRate: sentryConfig.tracesSampleRate ?? 0.1,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log('Sentry enabled: server');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default initSentryServer;
|
|
44
|
+
export { initSentryServer };
|