@friggframework/core 2.0.0-next.4 → 2.0.0-next.41
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/core/create-handler.js +2 -1
- package/database/models/WebsocketConnection.js +5 -0
- package/database/mongo.js +131 -5
- package/encrypt/encrypt.js +5 -33
- package/handlers/app-handler-helpers.js +59 -0
- package/handlers/backend-utils.js +85 -0
- package/handlers/routers/HEALTHCHECK.md +240 -0
- package/handlers/routers/auth.js +26 -0
- package/handlers/routers/health.js +844 -0
- package/handlers/routers/health.test.js +203 -0
- package/handlers/routers/integration-defined-routers.js +42 -0
- package/handlers/routers/middleware/loadUser.js +15 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +12 -0
- package/handlers/routers/user.js +41 -0
- package/handlers/routers/websocket.js +55 -0
- package/handlers/workers/integration-defined-workers.js +24 -0
- package/index.js +4 -0
- package/module-plugin/requester/requester.js +1 -0
- package/package.json +20 -12
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
process.env.HEALTH_API_KEY = 'test-api-key';
|
|
2
|
+
|
|
3
|
+
jest.mock('mongoose', () => ({
|
|
4
|
+
set: jest.fn(),
|
|
5
|
+
connection: {
|
|
6
|
+
readyState: 1,
|
|
7
|
+
db: {
|
|
8
|
+
admin: () => ({
|
|
9
|
+
ping: jest.fn().mockResolvedValue(true)
|
|
10
|
+
})
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
jest.mock('./../backend-utils', () => ({
|
|
16
|
+
moduleFactory: {
|
|
17
|
+
moduleTypes: ['test-module', 'another-module']
|
|
18
|
+
},
|
|
19
|
+
integrationFactory: {
|
|
20
|
+
integrationTypes: ['test-integration', 'another-integration']
|
|
21
|
+
}
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
jest.mock('./../app-handler-helpers', () => ({
|
|
25
|
+
createAppHandler: jest.fn((name, router) => ({ name, router }))
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
const { router } = require('./health');
|
|
29
|
+
const mongoose = require('mongoose');
|
|
30
|
+
|
|
31
|
+
const mockRequest = (path, headers = {}) => ({
|
|
32
|
+
path,
|
|
33
|
+
headers
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const mockResponse = () => {
|
|
37
|
+
const res = {};
|
|
38
|
+
res.status = jest.fn().mockReturnValue(res);
|
|
39
|
+
res.json = jest.fn().mockReturnValue(res);
|
|
40
|
+
return res;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
describe('Health Check Endpoints', () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
mongoose.connection.readyState = 1;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('Middleware - validateApiKey', () => {
|
|
49
|
+
it('should allow access to /health without authentication', async () => {
|
|
50
|
+
expect(true).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('GET /health', () => {
|
|
55
|
+
it('should return basic health status', async () => {
|
|
56
|
+
const req = mockRequest('/health');
|
|
57
|
+
const res = mockResponse();
|
|
58
|
+
|
|
59
|
+
const routeHandler = router.stack.find(layer =>
|
|
60
|
+
layer.route && layer.route.path === '/health'
|
|
61
|
+
).route.stack[0].handle;
|
|
62
|
+
|
|
63
|
+
await routeHandler(req, res);
|
|
64
|
+
|
|
65
|
+
expect(res.status).toHaveBeenCalledWith(200);
|
|
66
|
+
expect(res.json).toHaveBeenCalledWith({
|
|
67
|
+
status: 'ok',
|
|
68
|
+
timestamp: expect.any(String),
|
|
69
|
+
service: 'frigg-core-api'
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('GET /health/detailed', () => {
|
|
75
|
+
it('should return detailed health status when healthy', async () => {
|
|
76
|
+
const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
|
|
77
|
+
const res = mockResponse();
|
|
78
|
+
|
|
79
|
+
const originalPromiseAll = Promise.all;
|
|
80
|
+
Promise.all = jest.fn().mockResolvedValue([
|
|
81
|
+
{ name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
|
|
82
|
+
{ name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const routeHandler = router.stack.find(layer =>
|
|
86
|
+
layer.route && layer.route.path === '/health/detailed'
|
|
87
|
+
).route.stack[0].handle;
|
|
88
|
+
|
|
89
|
+
await routeHandler(req, res);
|
|
90
|
+
|
|
91
|
+
Promise.all = originalPromiseAll;
|
|
92
|
+
|
|
93
|
+
expect(res.status).toHaveBeenCalledWith(200);
|
|
94
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
|
|
95
|
+
status: 'healthy',
|
|
96
|
+
service: 'frigg-core-api',
|
|
97
|
+
timestamp: expect.any(String),
|
|
98
|
+
checks: expect.objectContaining({
|
|
99
|
+
database: expect.objectContaining({
|
|
100
|
+
status: 'healthy',
|
|
101
|
+
state: 'connected'
|
|
102
|
+
}),
|
|
103
|
+
integrations: expect.objectContaining({
|
|
104
|
+
status: 'healthy'
|
|
105
|
+
})
|
|
106
|
+
}),
|
|
107
|
+
responseTime: expect.any(Number)
|
|
108
|
+
}));
|
|
109
|
+
|
|
110
|
+
const response = res.json.mock.calls[0][0];
|
|
111
|
+
expect(response).not.toHaveProperty('version');
|
|
112
|
+
expect(response).not.toHaveProperty('uptime');
|
|
113
|
+
expect(response.checks).not.toHaveProperty('memory');
|
|
114
|
+
expect(response.checks.database).not.toHaveProperty('type');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should return 503 when database is disconnected', async () => {
|
|
118
|
+
mongoose.connection.readyState = 0;
|
|
119
|
+
|
|
120
|
+
const req = mockRequest('/health/detailed', { 'x-api-key': 'test-api-key' });
|
|
121
|
+
const res = mockResponse();
|
|
122
|
+
|
|
123
|
+
const originalPromiseAll = Promise.all;
|
|
124
|
+
Promise.all = jest.fn().mockResolvedValue([
|
|
125
|
+
{ name: 'github', status: 'healthy', reachable: true, statusCode: 200, responseTime: 100 },
|
|
126
|
+
{ name: 'npm', status: 'healthy', reachable: true, statusCode: 200, responseTime: 150 }
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
const routeHandler = router.stack.find(layer =>
|
|
130
|
+
layer.route && layer.route.path === '/health/detailed'
|
|
131
|
+
).route.stack[0].handle;
|
|
132
|
+
|
|
133
|
+
await routeHandler(req, res);
|
|
134
|
+
|
|
135
|
+
Promise.all = originalPromiseAll;
|
|
136
|
+
|
|
137
|
+
expect(res.status).toHaveBeenCalledWith(503);
|
|
138
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
|
|
139
|
+
status: 'unhealthy'
|
|
140
|
+
}));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('GET /health/live', () => {
|
|
145
|
+
it('should return alive status', async () => {
|
|
146
|
+
const req = mockRequest('/health/live', { 'x-api-key': 'test-api-key' });
|
|
147
|
+
const res = mockResponse();
|
|
148
|
+
|
|
149
|
+
const routeHandler = router.stack.find(layer =>
|
|
150
|
+
layer.route && layer.route.path === '/health/live'
|
|
151
|
+
).route.stack[0].handle;
|
|
152
|
+
|
|
153
|
+
routeHandler(req, res);
|
|
154
|
+
|
|
155
|
+
expect(res.status).toHaveBeenCalledWith(200);
|
|
156
|
+
expect(res.json).toHaveBeenCalledWith({
|
|
157
|
+
status: 'alive',
|
|
158
|
+
timestamp: expect.any(String)
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('GET /health/ready', () => {
|
|
164
|
+
it('should return ready when all checks pass', async () => {
|
|
165
|
+
const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
|
|
166
|
+
const res = mockResponse();
|
|
167
|
+
|
|
168
|
+
const routeHandler = router.stack.find(layer =>
|
|
169
|
+
layer.route && layer.route.path === '/health/ready'
|
|
170
|
+
).route.stack[0].handle;
|
|
171
|
+
|
|
172
|
+
await routeHandler(req, res);
|
|
173
|
+
|
|
174
|
+
expect(res.status).toHaveBeenCalledWith(200);
|
|
175
|
+
expect(res.json).toHaveBeenCalledWith({
|
|
176
|
+
ready: true,
|
|
177
|
+
timestamp: expect.any(String),
|
|
178
|
+
checks: {
|
|
179
|
+
database: true,
|
|
180
|
+
modules: true
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should return 503 when database is not connected', async () => {
|
|
186
|
+
mongoose.connection.readyState = 0;
|
|
187
|
+
|
|
188
|
+
const req = mockRequest('/health/ready', { 'x-api-key': 'test-api-key' });
|
|
189
|
+
const res = mockResponse();
|
|
190
|
+
|
|
191
|
+
const routeHandler = router.stack.find(layer =>
|
|
192
|
+
layer.route && layer.route.path === '/health/ready'
|
|
193
|
+
).route.stack[0].handle;
|
|
194
|
+
|
|
195
|
+
await routeHandler(req, res);
|
|
196
|
+
|
|
197
|
+
expect(res.status).toHaveBeenCalledWith(503);
|
|
198
|
+
expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
|
|
199
|
+
ready: false
|
|
200
|
+
}));
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { createAppHandler } = require('./../app-handler-helpers');
|
|
2
|
+
const {
|
|
3
|
+
integrationFactory,
|
|
4
|
+
loadRouterFromObject,
|
|
5
|
+
} = require('./../backend-utils');
|
|
6
|
+
const { Router } = require('express');
|
|
7
|
+
|
|
8
|
+
const handlers = {};
|
|
9
|
+
for (const IntegrationClass of integrationFactory.integrationClasses) {
|
|
10
|
+
const router = Router();
|
|
11
|
+
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
12
|
+
|
|
13
|
+
console.log(`\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`);
|
|
14
|
+
|
|
15
|
+
for (const routeDef of IntegrationClass.Definition.routes) {
|
|
16
|
+
if (typeof routeDef === 'function') {
|
|
17
|
+
router.use(basePath, routeDef(IntegrationClass));
|
|
18
|
+
console.log(`│ ANY ${basePath}/* (function handler)`);
|
|
19
|
+
} else if (typeof routeDef === 'object') {
|
|
20
|
+
router.use(
|
|
21
|
+
basePath,
|
|
22
|
+
loadRouterFromObject(IntegrationClass, routeDef)
|
|
23
|
+
);
|
|
24
|
+
const method = (routeDef.method || 'ANY').toUpperCase();
|
|
25
|
+
const fullPath = `${basePath}${routeDef.path}`;
|
|
26
|
+
console.log(`│ ${method} ${fullPath}`);
|
|
27
|
+
} else if (routeDef instanceof express.Router) {
|
|
28
|
+
router.use(basePath, routeDef);
|
|
29
|
+
console.log(`│ ANY ${basePath}/* (express router)`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
console.log('│');
|
|
33
|
+
|
|
34
|
+
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
35
|
+
handler: createAppHandler(
|
|
36
|
+
`HTTP Event: ${IntegrationClass.Definition.name}`,
|
|
37
|
+
router
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { handlers };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const catchAsyncError = require('express-async-handler');
|
|
2
|
+
const { User } = require('../../backend-utils');
|
|
3
|
+
|
|
4
|
+
module.exports = catchAsyncError(async (req, res, next) => {
|
|
5
|
+
const authorizationHeader = req.headers.authorization;
|
|
6
|
+
|
|
7
|
+
if (authorizationHeader) {
|
|
8
|
+
// Removes "Bearer " and trims
|
|
9
|
+
const token = authorizationHeader.split(' ')[1].trim();
|
|
10
|
+
// Load user for later middleware/routes to use
|
|
11
|
+
req.user = await User.newUser({ token });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return next();
|
|
15
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const Boom = require('@hapi/boom');
|
|
2
|
+
|
|
3
|
+
// CheckLoggedIn Middleware
|
|
4
|
+
const requireLoggedInUser = (req, res, next) => {
|
|
5
|
+
if (!req.user || !req.user.isLoggedIn()) {
|
|
6
|
+
throw Boom.unauthorized('Invalid Token');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
next();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports = { requireLoggedInUser };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const { createAppHandler } = require('../app-handler-helpers');
|
|
3
|
+
const { checkRequiredParams } = require('@friggframework/core');
|
|
4
|
+
const { User } = require('../backend-utils');
|
|
5
|
+
const catchAsyncError = require('express-async-handler');
|
|
6
|
+
|
|
7
|
+
const router = express();
|
|
8
|
+
|
|
9
|
+
// define the login endpoint
|
|
10
|
+
router.route('/user/login').post(
|
|
11
|
+
catchAsyncError(async (req, res) => {
|
|
12
|
+
const { username, password } = checkRequiredParams(req.body, [
|
|
13
|
+
'username',
|
|
14
|
+
'password',
|
|
15
|
+
]);
|
|
16
|
+
const user = await User.loginUser({ username, password });
|
|
17
|
+
const token = await user.createUserToken(120);
|
|
18
|
+
res.status(201);
|
|
19
|
+
res.json({ token });
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
router.route('/user/create').post(
|
|
24
|
+
catchAsyncError(async (req, res) => {
|
|
25
|
+
const { username, password } = checkRequiredParams(req.body, [
|
|
26
|
+
'username',
|
|
27
|
+
'password',
|
|
28
|
+
]);
|
|
29
|
+
const user = await User.createIndividualUser({
|
|
30
|
+
username,
|
|
31
|
+
password,
|
|
32
|
+
});
|
|
33
|
+
const token = await user.createUserToken(120);
|
|
34
|
+
res.status(201);
|
|
35
|
+
res.json({ token });
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const handler = createAppHandler('HTTP Event: User', router);
|
|
40
|
+
|
|
41
|
+
module.exports = { handler, router };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const { createHandler } = require('@friggframework/core');
|
|
2
|
+
const { WebsocketConnection } = require('@friggframework/core');
|
|
3
|
+
|
|
4
|
+
const handleWebSocketConnection = async (event, context) => {
|
|
5
|
+
// Handle different WebSocket events
|
|
6
|
+
switch (event.requestContext.eventType) {
|
|
7
|
+
case 'CONNECT':
|
|
8
|
+
// Handle new connection
|
|
9
|
+
try {
|
|
10
|
+
const connectionId = event.requestContext.connectionId;
|
|
11
|
+
await WebsocketConnection.create({ connectionId });
|
|
12
|
+
console.log(`Stored new connection: ${connectionId}`);
|
|
13
|
+
return { statusCode: 200, body: 'Connected.' };
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error('Error storing connection:', error);
|
|
16
|
+
return { statusCode: 500, body: 'Error connecting.' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
case 'DISCONNECT':
|
|
20
|
+
// Handle disconnection
|
|
21
|
+
try {
|
|
22
|
+
const connectionId = event.requestContext.connectionId;
|
|
23
|
+
await WebsocketConnection.deleteOne({ connectionId });
|
|
24
|
+
console.log(`Removed connection: ${connectionId}`);
|
|
25
|
+
return { statusCode: 200, body: 'Disconnected.' };
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error('Error removing connection:', error);
|
|
28
|
+
return { statusCode: 500, body: 'Error disconnecting.' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
case 'MESSAGE':
|
|
32
|
+
// Handle incoming message
|
|
33
|
+
const message = JSON.parse(event.body);
|
|
34
|
+
console.log('Received message:', message);
|
|
35
|
+
|
|
36
|
+
// Process the message and send a response
|
|
37
|
+
const responseMessage = { message: 'Message received' };
|
|
38
|
+
return {
|
|
39
|
+
statusCode: 200,
|
|
40
|
+
body: JSON.stringify(responseMessage),
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
default:
|
|
44
|
+
return { statusCode: 400, body: 'Unhandled event type.' };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const handler = createHandler({
|
|
49
|
+
eventName: 'WebSocket Event',
|
|
50
|
+
method: handleWebSocketConnection,
|
|
51
|
+
shouldUseDatabase: true, // Set to true as we're using the database
|
|
52
|
+
isUserFacingResponse: true, // This is a server-to-server response
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
module.exports = { handler };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { createHandler } = require('@friggframework/core');
|
|
2
|
+
const { integrationFactory, createQueueWorker } = require('../backend-utils');
|
|
3
|
+
|
|
4
|
+
const handlers = {};
|
|
5
|
+
integrationFactory.integrationClasses.forEach((IntegrationClass) => {
|
|
6
|
+
const defaultQueueWorker = createQueueWorker(IntegrationClass, integrationFactory);
|
|
7
|
+
|
|
8
|
+
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
9
|
+
queueWorker: createHandler({
|
|
10
|
+
eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
|
|
11
|
+
isUserFacingResponse: false,
|
|
12
|
+
method: async (event, context) => {
|
|
13
|
+
const worker = new defaultQueueWorker();
|
|
14
|
+
await worker.run(event, context);
|
|
15
|
+
return {
|
|
16
|
+
message: 'Successfully processed the Generic Queue Worker',
|
|
17
|
+
input: event,
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
module.exports = { handlers };
|
package/index.js
CHANGED
|
@@ -59,6 +59,7 @@ const {
|
|
|
59
59
|
ModuleFactory,
|
|
60
60
|
Auther,
|
|
61
61
|
} = require('./module-plugin/index');
|
|
62
|
+
const utils = require('./utils');
|
|
62
63
|
|
|
63
64
|
// const {Sync } = require('./syncs/model');
|
|
64
65
|
|
|
@@ -137,4 +138,7 @@ module.exports = {
|
|
|
137
138
|
|
|
138
139
|
// queues
|
|
139
140
|
QueuerUtil,
|
|
141
|
+
|
|
142
|
+
// utils
|
|
143
|
+
...utils,
|
|
140
144
|
};
|
package/package.json
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0-next.
|
|
4
|
+
"version": "2.0.0-next.41",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"aws-sdk": "^2.1200.0",
|
|
8
8
|
"bcryptjs": "^2.4.3",
|
|
9
|
+
"body-parser": "^1.20.2",
|
|
9
10
|
"common-tags": "^1.8.2",
|
|
10
|
-
"
|
|
11
|
+
"cors": "^2.8.5",
|
|
12
|
+
"dotenv": "^16.4.7",
|
|
13
|
+
"express": "^4.19.2",
|
|
11
14
|
"express-async-handler": "^1.2.0",
|
|
12
|
-
"
|
|
15
|
+
"form-data": "^4.0.0",
|
|
16
|
+
"fs-extra": "^11.2.0",
|
|
17
|
+
"lodash": "4.17.21",
|
|
13
18
|
"lodash.get": "^4.4.2",
|
|
14
19
|
"mongoose": "6.11.6",
|
|
15
|
-
"node-fetch": "^2.6.7"
|
|
20
|
+
"node-fetch": "^2.6.7",
|
|
21
|
+
"serverless-http": "^2.7.0",
|
|
22
|
+
"uuid": "^9.0.1"
|
|
16
23
|
},
|
|
17
24
|
"devDependencies": {
|
|
18
|
-
"@friggframework/eslint-config": "2.0.0-next.
|
|
19
|
-
"@friggframework/prettier-config": "2.0.0-next.
|
|
20
|
-
"@friggframework/test": "2.0.0-next.
|
|
21
|
-
"@types/lodash": "
|
|
25
|
+
"@friggframework/eslint-config": "2.0.0-next.41",
|
|
26
|
+
"@friggframework/prettier-config": "2.0.0-next.41",
|
|
27
|
+
"@friggframework/test": "2.0.0-next.41",
|
|
28
|
+
"@types/lodash": "4.17.15",
|
|
22
29
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
23
30
|
"chai": "^4.3.6",
|
|
24
31
|
"eslint": "^8.22.0",
|
|
@@ -26,9 +33,7 @@
|
|
|
26
33
|
"eslint-plugin-n": "^17.10.2",
|
|
27
34
|
"eslint-plugin-promise": "^7.0.0",
|
|
28
35
|
"jest": "^29.7.0",
|
|
29
|
-
"
|
|
30
|
-
"mongodb-memory-server": "^8.9.0",
|
|
31
|
-
"prettier": "^2.8.5",
|
|
36
|
+
"prettier": "^2.7.1",
|
|
32
37
|
"sinon": "^16.1.1",
|
|
33
38
|
"typescript": "^5.0.2"
|
|
34
39
|
},
|
|
@@ -48,5 +53,8 @@
|
|
|
48
53
|
},
|
|
49
54
|
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
50
55
|
"description": "",
|
|
51
|
-
"
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"gitHead": "a5c413164c1b492a228689851d037706f7f869af"
|
|
52
60
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('node:path');
|
|
3
|
+
const PACKAGE_JSON = 'package.json';
|
|
4
|
+
|
|
5
|
+
function findNearestBackendPackageJson() {
|
|
6
|
+
let currentDir = process.cwd();
|
|
7
|
+
|
|
8
|
+
// First check if we're in production by looking for package.json in the current directory
|
|
9
|
+
const rootPackageJson = path.join(currentDir, PACKAGE_JSON);
|
|
10
|
+
if (fs.existsSync(rootPackageJson)) {
|
|
11
|
+
// In production environment, check for index.js in the same directory
|
|
12
|
+
const indexJs = path.join(currentDir, 'index.js');
|
|
13
|
+
if (fs.existsSync(indexJs)) {
|
|
14
|
+
return rootPackageJson;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// If not found at root or not in production, look for it in the backend directory
|
|
19
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
20
|
+
const packageJsonPath = path.join(currentDir, 'backend', PACKAGE_JSON);
|
|
21
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
22
|
+
return packageJsonPath;
|
|
23
|
+
}
|
|
24
|
+
currentDir = path.dirname(currentDir);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validateBackendPath(backendPath) {
|
|
30
|
+
if (!backendPath) {
|
|
31
|
+
throw new Error('Could not find a backend package.json file.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
findNearestBackendPackageJson,
|
|
37
|
+
validateBackendPath,
|
|
38
|
+
};
|