chore: Split routes and add typings (#5)

This commit is contained in:
Eric Tuvesson
2024-01-25 12:09:50 +01:00
committed by GitHub
parent f39f6410f2
commit c2f102f603
5 changed files with 208 additions and 104 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@noodl/cloudservice",
"version": "1.0.0",
"version": "0.2.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@noodl/cloudservice",
"version": "1.0.0",
"version": "0.2.4",
"license": "MIT",
"dependencies": {
"isolated-vm": "^4.4.2",
@@ -16,6 +16,7 @@
"winston-mongodb": "^5.1.0"
},
"devDependencies": {
"@types/express": "^4.17.21",
"ts-node": "^10.9.2",
"typescript": "^5.2.2"
}
@@ -937,12 +938,12 @@
}
},
"node_modules/@types/express": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
@@ -975,6 +976,17 @@
"express-unless": "*"
}
},
"node_modules/@types/express/node_modules/@types/express-serve-static-core": {
"version": "4.17.41",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
"integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"node_modules/@types/fs-capacitor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz",
@@ -1046,6 +1058,20 @@
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/send/node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
},
"node_modules/@types/serve-static": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
@@ -1450,6 +1476,17 @@
"graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0"
}
},
"node_modules/apollo-server-express/node_modules/@types/express": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/apollo-server-express/node_modules/subscriptions-transport-ws": {
"version": "0.9.19",
"resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz",
@@ -7615,14 +7652,27 @@
}
},
"@types/express": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
},
"dependencies": {
"@types/express-serve-static-core": {
"version": "4.17.41",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz",
"integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==",
"requires": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
}
}
},
"@types/express-jwt": {
@@ -7723,6 +7773,22 @@
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
},
"@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"requires": {
"@types/mime": "^1",
"@types/node": "*"
},
"dependencies": {
"@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
}
}
},
"@types/serve-static": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz",
@@ -8042,6 +8108,17 @@
"type-is": "^1.6.16"
},
"dependencies": {
"@types/express": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz",
"integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"subscriptions-transport-ws": {
"version": "0.9.19",
"resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz",

View File

@@ -25,7 +25,8 @@
"winston-mongodb": "^5.1.0"
},
"devDependencies": {
"typescript": "^5.2.2",
"ts-node": "^10.9.2"
"@types/express": "^4.17.21",
"ts-node": "^10.9.2",
"typescript": "^5.2.2"
}
}

View File

@@ -1,97 +1,14 @@
import { NoodlParseServerOptions, createNoodlParseServer } from "./parse";
import { executeFunction } from "./function";
import { CFVersion, deployFunctions, getLatestVersion } from "./function-deploy";
import { Logger } from "./logger";
import type { Request, Response, NextFunction } from "express"
import { NoodlParseServerOptions, NoodlParseServerResult, createNoodlParseServer } from "./parse";
import { routeFunctions } from "./routes/functions";
import { routeFunctionsAdmin } from "./routes/functions-admin";
function createMiddleware(noodlServer) {
return async function middleware(req, res, next) {
function createMiddleware(noodlServer: NoodlParseServerResult) {
return async function middleware(req: Request, res: Response, next: NextFunction) {
if (req.url.startsWith('/functions/') && req.method === 'POST') {
try {
const path = req.url;
const functionId = decodeURIComponent(path.split('/')[2]);
if (functionId === undefined)
return next()
console.log('Running cloud function ' + functionId);
let requestVersion = req.headers['x-noodl-cloud-version'];
let version: CFVersion = requestVersion
? { functionVersion: requestVersion }
: await getLatestVersion(noodlServer.options)
// Execute the request
const cfResponse = await executeFunction({
port: noodlServer.options.port,
appId: noodlServer.options.appId,
masterKey: noodlServer.options.masterKey,
version,
logger: new Logger(noodlServer),
headers: req.headers,
functionId,
body: req.body,
timeOut: noodlServer.functionOptions.timeOut,
memoryLimit: noodlServer.functionOptions.memoryLimit,
})
if (cfResponse.headers) {
res.status(cfResponse.statusCode)
.set(cfResponse.headers)
.send(cfResponse.body)
} else {
res.status(cfResponse.statusCode)
.set({ 'Content-Type': 'application/json' })
.send(cfResponse.body)
}
} catch (e) {
console.log('Something went wrong when running function', e)
res.status(400).json({
error: "Something when wrong..."
})
}
routeFunctions(noodlServer, req, res, next);
} else if (req.url.startsWith('/functions-admin')) {
if (req.headers['x-parse-master-key'] !== noodlServer.options.masterKey) {
return res.status(401).json({
message: 'Not authorized'
})
}
if (req.headers['x-parse-application-id'] !== noodlServer.options.appId) {
return res.status(401).json({
message: 'Not authorized'
})
}
// Deploy a new version
if (req.method === 'POST' && req.url === "/functions-admin/deploy") {
if (!req.body || typeof req.body.deploy !== "string" || typeof req.body.runtime !== "string") {
return res.status(400).json({
message: 'Must supply deploy and runtime'
})
}
console.log('Uploading deploy...')
const { version } = await deployFunctions({
port: noodlServer.options.port,
appId: noodlServer.options.appId,
masterKey: noodlServer.options.masterKey,
runtime: req.body.runtime,
data: req.body.deploy
})
console.log('Upload completed, version: ' + version)
res.json({
status: 'success',
version
})
} else if (req.method === 'GET' && req.url === "/functions-admin/info") {
res.json({
version: '1.0'
})
} else res.status(400).json({
message: 'Function not supported'
})
routeFunctionsAdmin(noodlServer, req, res, next);
} else {
next()
}
@@ -104,7 +21,7 @@ export function createNoodlServer(options: NoodlParseServerOptions) {
const cfMiddleware = createMiddleware(noodlServer);
// Combine the Noodl Cloud Function middleware with the Parse middleware into one middleware.
const middleware = (req: Request, res: Response, next: () => void) => {
const middleware = (req: Request, res: Response, next: NextFunction) => {
cfMiddleware(req, res, () => {
noodlServer.server.app(req, res, next);
});

View File

@@ -0,0 +1,52 @@
import type { Request, Response, NextFunction } from "express"
import { NoodlParseServerResult } from "../parse"
import { deployFunctions } from "../function-deploy"
export async function routeFunctionsAdmin(
noodlServer: NoodlParseServerResult,
req: Request,
res: Response,
_next: NextFunction
) {
if (req.headers['x-parse-master-key'] !== noodlServer.options.masterKey) {
return res.status(401).json({
message: 'Not authorized'
})
}
if (req.headers['x-parse-application-id'] !== noodlServer.options.appId) {
return res.status(401).json({
message: 'Not authorized'
})
}
// Deploy a new version
if (req.method === 'POST' && req.url === "/functions-admin/deploy") {
if (!req.body || typeof req.body.deploy !== "string" || typeof req.body.runtime !== "string") {
return res.status(400).json({
message: 'Must supply deploy and runtime'
})
}
console.log('Uploading deploy...')
const { version } = await deployFunctions({
port: noodlServer.options.port,
appId: noodlServer.options.appId,
masterKey: noodlServer.options.masterKey,
runtime: req.body.runtime,
data: req.body.deploy
})
console.log('Upload completed, version: ' + version)
res.json({
status: 'success',
version
})
} else if (req.method === 'GET' && req.url === "/functions-admin/info") {
res.json({
version: '1.0'
})
} else res.status(400).json({
message: 'Function not supported'
})
}

View File

@@ -0,0 +1,57 @@
import type { Request, Response, NextFunction } from "express"
import { executeFunction } from "../function";
import { CFVersion, getLatestVersion } from "../function-deploy";
import { Logger } from "../logger";
import { NoodlParseServerResult } from "../parse";
export async function routeFunctions(
noodlServer: NoodlParseServerResult,
req: Request,
res: Response,
next: NextFunction
) {
try {
const path = req.url;
const functionId = decodeURIComponent(path.split("/")[2]);
if (functionId === undefined) return next();
console.log("Running cloud function " + functionId);
let requestVersion = req.headers["x-noodl-cloud-version"];
let version: CFVersion = requestVersion
? { functionVersion: String(requestVersion) }
: await getLatestVersion(noodlServer.options);
// Execute the request
const cfResponse = await executeFunction({
port: noodlServer.options.port,
appId: noodlServer.options.appId,
masterKey: noodlServer.options.masterKey,
version,
logger: new Logger(noodlServer),
headers: req.headers,
functionId,
body: req.body,
timeOut: noodlServer.functionOptions.timeOut,
memoryLimit: noodlServer.functionOptions.memoryLimit,
});
if (cfResponse.headers) {
res
.status(cfResponse.statusCode)
.set(cfResponse.headers)
.send(cfResponse.body);
} else {
res
.status(cfResponse.statusCode)
.set({ "Content-Type": "application/json" })
.send(cfResponse.body);
}
} catch (e) {
console.log("Something went wrong when running function", e);
res.status(400).json({
error: "Something when wrong...",
});
}
}