Skip to content

Commit 6a96add

Browse files
authored
feat(runtime): impl cloud function import and cache function (#1005)
* feat(runtime): impl func import and cache func * feat: opt init and cache * feat: opt func cache & add pkg types * style: opt style
1 parent a3dbf80 commit 6a96add

21 files changed

+348
-187
lines changed

runtimes/nodejs/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"watch": "tsc -p tsconfig.json -w",
1313
"prepublishOnly": "npm run build",
1414
"trace-gc": "node --trace_gc --trace_gc_verbose ./dist/index.js",
15-
"init": "node ./dist/init.js"
15+
"init": "node ./dist/init.js",
16+
"prettier": "npx prettier --write ./src"
1617
},
1718
"keywords": [
1819
"laf",

runtimes/nodejs/src/config.ts

+24-26
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import * as dotenv from "dotenv";
1+
import * as dotenv from 'dotenv'
22

33
/**
44
* parse environment vars from the `.env` file if existing
55
*/
6-
dotenv.config();
6+
dotenv.config()
77

88
/**
99
* configuration management
@@ -13,80 +13,78 @@ export default class Config {
1313
* mongodb connection configuration
1414
*/
1515
static get DB_URI() {
16-
if (!process.env["DB_URI"]) {
17-
throw new Error("env: `DB_URI` is missing");
16+
if (!process.env['DB_URI']) {
17+
throw new Error('env: `DB_URI` is missing')
1818
}
19-
return process.env["DB_URI"];
19+
return process.env['DB_URI']
2020
}
2121

2222
/**
2323
* the server secret salt, mainly used for generating tokens
2424
*/
2525
static get SERVER_SECRET(): string {
26-
const secret_salt = process.env["SERVER_SECRET"];
26+
const secret_salt = process.env['SERVER_SECRET']
2727
if (!secret_salt) {
28-
throw new Error("env: `SERVER_SECRET` is missing");
28+
throw new Error('env: `SERVER_SECRET` is missing')
2929
}
30-
return secret_salt;
30+
return secret_salt
3131
}
3232

3333
/**
3434
* the logger level : 'fatal', 'error', 'warning', 'info', 'debug', 'trace'
3535
*/
3636
static get LOG_LEVEL():
37-
| "fatal"
38-
| "error"
39-
| "warning"
40-
| "info"
41-
| "debug"
42-
| "trace" {
43-
return (
44-
(process.env["LOG_LEVEL"] as any) ?? (this.isProd ? "info" : "debug")
45-
);
37+
| 'fatal'
38+
| 'error'
39+
| 'warning'
40+
| 'info'
41+
| 'debug'
42+
| 'trace' {
43+
return (process.env['LOG_LEVEL'] as any) ?? (this.isProd ? 'info' : 'debug')
4644
}
4745

4846
/**
4947
* the serving port, default is 8000
5048
*/
5149
static get PORT(): number {
52-
return (process.env.PORT ?? 8000) as number;
50+
return (process.env.PORT ?? 8000) as number
5351
}
5452

5553
/**
5654
* in production deploy or not
5755
*/
5856
static get isProd(): boolean {
59-
return process.env.NODE_ENV === "production";
57+
return process.env.NODE_ENV === 'production'
6058
}
6159

6260
/**
6361
* Expired time of function logs, in seconds
6462
*/
6563
static get FUNCTION_LOG_EXPIRED_TIME(): number {
66-
return (process.env.FUNCTION_LOG_EXPIRED_TIME ?? 3600 * 24 * 3) as number;
64+
return (process.env.FUNCTION_LOG_EXPIRED_TIME ?? 3600 * 24 * 3) as number
6765
}
6866

6967
static get RUNTIME_IMAGE(): string {
70-
return process.env.RUNTIME_IMAGE;
68+
return process.env.RUNTIME_IMAGE
7169
}
7270

7371
static get RUNTIME_VERSION(): string {
74-
return require("../package.json")?.version;
72+
return require('../package.json')?.version
7573
}
7674

7775
static get APP_ID(): string {
78-
return process.env.APP_ID;
76+
return process.env.APP_ID
7977
}
8078

8179
static get NPM_INSTALL_FLAGS(): string {
82-
return process.env.NPM_INSTALL_FLAGS || "";
80+
return process.env.NPM_INSTALL_FLAGS || ''
8381
}
8482

8583
static get REQUEST_LIMIT_SIZE(): string {
86-
return process.env.REQUEST_LIMIT_SIZE || "10mb";
84+
return process.env.REQUEST_LIMIT_SIZE || '10mb'
8785
}
8886

8987
static get PACKAGES(): string {
90-
return process.env.PACKAGES || "";
88+
return process.env.PACKAGES || ''
9189
}
9290
}

runtimes/nodejs/src/constants.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
/**
32
* Collection names
43
*/
@@ -7,8 +6,7 @@ export const POLICY_COLLECTION = '__policies__'
76
export const FUNCTION_LOG_COLLECTION = '__function_logs__'
87
export const CONFIG_COLLECTION = '__config__'
98

10-
119
export const WEBSOCKET_FUNCTION_NAME = '__websocket__'
1210
export const INTERCEPTOR_FUNCTION_NAME = '__interceptor__'
1311
export const DEFAULT_FUNCTION_NAME = '__default__'
14-
export const INIT_FUNCTION_NAME = '__init__'
12+
export const INIT_FUNCTION_NAME = '__init__'

runtimes/nodejs/src/db.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
* @Author: Maslow<[email protected]>
33
* @Date: 2021-08-16 15:29:15
44
* @LastEditTime: 2022-02-03 00:42:33
5-
* @Description:
5+
* @Description:
66
*/
77

8-
98
import { MongoAccessor } from 'database-proxy'
109
import Config from './config'
1110
import { createLogger, logger } from './support/logger'
1211
import * as mongodb_uri from 'mongodb-uri'
13-
12+
import { FunctionCache } from './support/function-engine/cache'
1413

1514
/**
1615
* Database Management
@@ -34,18 +33,20 @@ export class DatabaseAgent {
3433

3534
/**
3635
* Create MongoAccessor instance
37-
* @returns
36+
* @returns
3837
*/
3938
private static _createAccessor() {
4039
const { database } = mongodb_uri.parse(Config.DB_URI)
4140
const accessor = new MongoAccessor(database, Config.DB_URI)
4241

4342
accessor.setLogger(createLogger('accessor', 'warning'))
44-
accessor.init()
43+
accessor
44+
.init()
4545
.then(async () => {
4646
logger.info('db connected')
47+
FunctionCache.initialize()
4748
})
48-
.catch(error => {
49+
.catch((error) => {
4950
logger.error(error)
5051
setTimeout(() => process.exit(101), 0)
5152
})

runtimes/nodejs/src/handler/db-proxy.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import { Response } from 'express'
32
import { Proxy } from 'database-proxy'
43
import Config from '../config'
@@ -7,7 +6,6 @@ import { logger } from '../support/logger'
76
import { PolicyAgent } from '../support/policy'
87
import { IRequest } from '../support/types'
98

10-
119
export async function handleDatabaseProxy(req: IRequest, res: Response) {
1210
const accessor = DatabaseAgent.accessor
1311

@@ -37,7 +35,7 @@ export async function handleDatabaseProxy(req: IRequest, res: Response) {
3735
return res.status(403).send({
3836
code: 'permission denied',
3937
error: result.errors,
40-
injections: Config.isProd ? undefined : injections
38+
injections: Config.isProd ? undefined : injections,
4139
})
4240
}
4341

@@ -49,14 +47,14 @@ export async function handleDatabaseProxy(req: IRequest, res: Response) {
4947

5048
return res.send({
5149
code: 0,
52-
data
50+
data,
5351
})
5452
} catch (error) {
5553
logger.error(requestId, 'execute query got error: ', error)
5654
return res.send({
5755
code: 1,
5856
error: error.toString(),
59-
injections: Config.isProd ? undefined : injections
57+
injections: Config.isProd ? undefined : injections,
6058
})
6159
}
62-
}
60+
}

runtimes/nodejs/src/handler/debug-func.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ export async function handleDebugFunction(req: IRequest, res: Response) {
2626
return res.status(400).send('x-laf-func-data is required')
2727
}
2828

29-
3029
// parse func_data
3130
let func_data: ICloudFunctionData
3231
try {
33-
const decoded = decodeURIComponent(func_str)
32+
const decoded = decodeURIComponent(func_str)
3433
func_data = JSON.parse(decoded)
3534
} catch (error) {
3635
return res.status(400).send('x-laf-func-data is invalid')

runtimes/nodejs/src/handler/invoke-func.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export async function handleInvokeFunction(req: IRequest, res: Response) {
3232
const func_name = req.params?.name
3333

3434
// load function data from db
35-
let funcData = await CloudFunction.getFunctionByName(func_name)
35+
let funcData = CloudFunction.getFunctionByName(func_name)
3636
if (!funcData) {
3737
// load default function from db
38-
funcData = await CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME)
38+
funcData = CloudFunction.getFunctionByName(DEFAULT_FUNCTION_NAME)
3939
if (!funcData) {
4040
return res.status(404).send('Function Not Found')
4141
}
@@ -103,7 +103,7 @@ async function invokeInterceptor(req: IRequest, res: Response) {
103103
const func_name = INTERCEPTOR_FUNCTION_NAME
104104

105105
// load function data from db
106-
const funcData = await CloudFunction.getFunctionByName(func_name)
106+
const funcData = CloudFunction.getFunctionByName(func_name)
107107
// pass if no interceptor
108108
if (!funcData) {
109109
return true

runtimes/nodejs/src/handler/router.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @Author: Maslow<[email protected]>
33
* @Date: 2021-07-30 10:30:29
44
* @LastEditTime: 2022-02-03 00:32:16
5-
* @Description:
5+
* @Description:
66
*/
77

88
import { Router } from 'express'
@@ -13,7 +13,6 @@ import { handlePackageTypings } from './typings'
1313
import { generateUUID } from '../support/utils'
1414
import { handleInvokeFunction } from './invoke-func'
1515

16-
1716
/**
1817
* multer uploader config
1918
*/
@@ -22,18 +21,19 @@ const uploader = multer({
2221
filename: (_req, file, cb) => {
2322
const { ext } = path.parse(file.originalname)
2423
cb(null, generateUUID() + ext)
25-
}
24+
},
2625
}),
2726
fileFilter(_req, file, callback) {
2827
// solve the problem of garbled unicode names
29-
file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')
28+
file.originalname = Buffer.from(file.originalname, 'latin1').toString(
29+
'utf8',
30+
)
3031
callback(null, true)
3132
},
3233
})
3334

3435
export const router = Router()
3536

36-
3737
router.post('/proxy/:policy', handleDatabaseProxy)
3838
router.get('/_/typing/package', handlePackageTypings)
3939
router.get('/_/healthz', (_req, res) => res.status(200).send('ok'))
@@ -42,4 +42,4 @@ router.get('/_/healthz', (_req, res) => res.status(200).send('ok'))
4242
* Invoke cloud function through HTTP request.
4343
* @method *
4444
*/
45-
router.all('/:name', uploader.any(), handleInvokeFunction)
45+
router.all('/:name', uploader.any(), handleInvokeFunction)

runtimes/nodejs/src/handler/typings.ts

+21-5
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
* @Author: Maslow<[email protected]>
33
* @Date: 2021-07-30 10:30:29
44
* @LastEditTime: 2021-08-18 16:49:35
5-
* @Description:
5+
* @Description:
66
*/
77

88
import { Response } from 'express'
99
import { PackageDeclaration, NodePackageDeclarations } from 'node-modules-utils'
1010
import path = require('path')
1111
import { logger } from '../support/logger'
1212
import { IRequest } from '../support/types'
13+
import { FunctionCache } from '../support/function-engine/cache'
1314

1415
const nodeModulesRoot = path.resolve(__dirname, '../../node_modules')
1516

@@ -35,7 +36,7 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
3536

3637
return res.send({
3738
code: 0,
38-
data: rets
39+
data: rets,
3940
})
4041
}
4142

@@ -46,7 +47,22 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
4647

4748
return res.send({
4849
code: 0,
49-
data: [r]
50+
data: [r],
51+
})
52+
}
53+
54+
// get cloud function types
55+
if (packageName.startsWith('@/')) {
56+
const func = FunctionCache.getFunctionByName(packageName.replace('@/', ''))
57+
const r = {
58+
packageName: packageName,
59+
content: func.source.code,
60+
path: `${packageName}/index.ts`,
61+
from: 'node',
62+
}
63+
return res.send({
64+
code: 0,
65+
data: [r],
5066
})
5167
}
5268

@@ -56,13 +72,13 @@ export async function handlePackageTypings(req: IRequest, res: Response) {
5672
await pkd.load()
5773
return res.send({
5874
code: 0,
59-
data: pkd.declarations
75+
data: pkd.declarations,
6076
})
6177
} catch (error) {
6278
logger.error(requestId, 'failed to get package typings', error)
6379
return res.send({
6480
code: 1,
65-
error: error.toString()
81+
error: error.toString(),
6682
})
6783
}
6884
}

runtimes/nodejs/src/support/cloud-sdk.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function createCloudSdk() {
5252
* @returns
5353
*/
5454
async function invokeInFunction(name: string, param?: FunctionContext) {
55-
const data = await CloudFunction.getFunctionByName(name)
55+
const data = CloudFunction.getFunctionByName(name)
5656
const func = new CloudFunction(data)
5757

5858
if (!func) {

0 commit comments

Comments
 (0)