From 7cf8694af6c2b3587cdca4f01838c8bbd87046a0 Mon Sep 17 00:00:00 2001 From: Maslow Date: Wed, 7 Dec 2022 18:14:10 +0800 Subject: [PATCH] feat(server): implement instance manage in server (#494) * fix(core): fix app db user role missing Signed-off-by: maslow * feat(server): implement instance manage in server Signed-off-by: maslow --- .vscode/settings.json | 1 + core/controllers/database/dbm/mongo.go | 2 +- deploy/scripts/README.md | 6 - deploy/scripts/install-laf-core.sh | 3 + runtimes/nodejs/Dockerfile.init | 2 +- runtimes/nodejs/init.sh | 5 + runtimes/nodejs/package-lock.json | 578 ++++++++++-------- runtimes/nodejs/package.json | 2 +- runtimes/nodejs/src/cloud-sdk/index.ts | 149 ++--- runtimes/nodejs/src/config.ts | 60 +- runtimes/nodejs/src/support/token.ts | 44 +- server/package-lock.json | 109 +++- server/package.json | 2 + server/prisma/schema.prisma | 2 + server/src/app.module.ts | 12 +- .../applications/applications.controller.ts | 14 +- .../src/applications/applications.service.ts | 35 +- server/src/common/getter.ts | 2 +- server/src/core/application.cr.service.ts | 2 - .../initializer/initializer.service.spec.ts | 18 - server/src/initializer/initializer.service.ts | 33 +- server/src/instance/instance-task.service.ts | 193 ++++++ server/src/instance/instance.module.ts | 11 + server/src/instance/instance.service.ts | 219 +++++++ 24 files changed, 1014 insertions(+), 490 deletions(-) create mode 100644 runtimes/nodejs/init.sh delete mode 100644 server/src/initializer/initializer.service.spec.ts create mode 100644 server/src/instance/instance-task.service.ts create mode 100644 server/src/instance/instance.module.ts create mode 100644 server/src/instance/instance.service.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index efb08e7816..b8284a931a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,6 +46,7 @@ "fullname", "ghaction", "gonanoid", + "healthz", "hostpath", "Kube", "kubebuilder", diff --git a/core/controllers/database/dbm/mongo.go b/core/controllers/database/dbm/mongo.go index 91322ce9d2..345df74a3b 100644 --- a/core/controllers/database/dbm/mongo.go +++ b/core/controllers/database/dbm/mongo.go @@ -34,7 +34,7 @@ func (m *MongoManager) CreateDatabase(databaseName string, username string, pass command := bson.D{ {Key: "createUser", Value: username}, {Key: "pwd", Value: password}, - {Key: "roles", Value: []bson.M{{"role": "readWrite", "db": databaseName}}}} + {Key: "roles", Value: []bson.M{{"role": "readWrite", "db": databaseName}, {"role": "dbAdmin", "db": databaseName}}}} err := m.client.Database(databaseName).RunCommand(m.context, command).Decode(&result) if err != nil { diff --git a/deploy/scripts/README.md b/deploy/scripts/README.md index d546d9c2a8..8fd77281bc 100644 --- a/deploy/scripts/README.md +++ b/deploy/scripts/README.md @@ -15,9 +15,6 @@ sh install-k8s.sh # setup laf core sh install-laf-core.sh - -# apply laf cluster resources -kubectl apply -f init-laf/ ``` ## Create development environment on MacOS @@ -40,9 +37,6 @@ sh init-vm.sh # setup laf core multipass exec laf-dev -- sudo -u root sh /laf/deploy/scripts/install-laf-core.sh - -# apply laf cluster resource -multipass exec laf-dev -- sudo -u root kubectl apply -f /laf/deploy/scripts/init-laf/ ``` 3. Start laf server diff --git a/deploy/scripts/install-laf-core.sh b/deploy/scripts/install-laf-core.sh index 69b3396ae5..4e15faaa62 100644 --- a/deploy/scripts/install-laf-core.sh +++ b/deploy/scripts/install-laf-core.sh @@ -69,3 +69,6 @@ helm install apisix \ --namespace laf \ $CHARTS_DIR/apisix + +# init laf resources +kubectl apply -f $SCRIPT_DIR/init-laf/ \ No newline at end of file diff --git a/runtimes/nodejs/Dockerfile.init b/runtimes/nodejs/Dockerfile.init index a55202c42e..86d5045a10 100644 --- a/runtimes/nodejs/Dockerfile.init +++ b/runtimes/nodejs/Dockerfile.init @@ -17,4 +17,4 @@ RUN chown node:node /app/package.json RUN chown node:node /app/package-lock.json USER node -CMD [ "node", "./dist/init.js" ] \ No newline at end of file +CMD [ "sh", "/app/init.sh" ] \ No newline at end of file diff --git a/runtimes/nodejs/init.sh b/runtimes/nodejs/init.sh new file mode 100644 index 0000000000..c74e496d07 --- /dev/null +++ b/runtimes/nodejs/init.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# echo "****** init start ******" +exec node ./dist/init.js +# echo "****** init end *******" diff --git a/runtimes/nodejs/package-lock.json b/runtimes/nodejs/package-lock.json index 34cdb74a5c..1c5f5b6ca3 100644 --- a/runtimes/nodejs/package-lock.json +++ b/runtimes/nodejs/package-lock.json @@ -1,12 +1,12 @@ { "name": "runtime-nodejs", - "version": "0.8.11", + "version": "1.0.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "runtime-nodejs", - "version": "0.8.11", + "version": "1.0.0-alpha.0", "dependencies": { "@aws-sdk/client-s3": "^3.72.0", "@aws-sdk/client-sts": "^3.72.0", @@ -28,7 +28,7 @@ "minio": "^7.0.28", "mongodb": "^4.1.3", "mongodb-uri": "^0.9.7", - "multer": "^1.4.2", + "multer": "^1.4.4", "node-modules-utils": "^0.8.2", "nodemailer": "^6.6.3", "validator": "^13.7.0", @@ -1623,12 +1623,12 @@ "optional": true }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" @@ -1938,23 +1938,26 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "node_modules/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, "node_modules/body-parser/node_modules/iconv-lite": { @@ -2193,9 +2196,9 @@ } }, "node_modules/bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -2319,21 +2322,16 @@ } }, "node_modules/content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "safe-buffer": "5.1.2" + "safe-buffer": "5.2.1" }, "engines": { "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -2343,9 +2341,9 @@ } }, "node_modules/cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } @@ -2592,11 +2590,11 @@ } }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/des.js": { @@ -2609,9 +2607,13 @@ } }, "node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, "node_modules/dicer": { "version": "0.2.5", @@ -2749,7 +2751,7 @@ "node_modules/encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } @@ -2878,7 +2880,7 @@ "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } @@ -2915,37 +2917,38 @@ } }, "node_modules/express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -2965,10 +2968,13 @@ "node": ">=0.10" } }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/extend": { "version": "3.0.2", @@ -3057,22 +3063,30 @@ } }, "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/flatted": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", @@ -3148,9 +3162,9 @@ } }, "node_modules/forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } @@ -3158,7 +3172,7 @@ "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } @@ -3520,24 +3534,27 @@ } }, "node_modules/http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/http-proxy-agent": { "version": "4.0.1", @@ -4301,19 +4318,19 @@ } }, "node_modules/mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "1.46.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -4544,9 +4561,9 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "engines": { "node": ">= 0.6" } @@ -4661,9 +4678,9 @@ } }, "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dependencies": { "ee-first": "1.1.1" }, @@ -4897,11 +4914,11 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "node_modules/proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dependencies": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { @@ -5011,11 +5028,17 @@ } }, "node_modules/qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/querystring": { @@ -5061,12 +5084,12 @@ } }, "node_modules/raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -5160,9 +5183,9 @@ } }, "node_modules/request/node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", "engines": { "node": ">=0.6" } @@ -5260,32 +5283,40 @@ } }, "node_modules/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dependencies": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/send/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, "node_modules/seq-queue": { "version": "0.0.5", @@ -5293,23 +5324,23 @@ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" }, "node_modules/serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", @@ -5738,9 +5769,9 @@ } }, "node_modules/toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "engines": { "node": ">=0.6" } @@ -7523,12 +7554,12 @@ "optional": true }, "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" } }, "acorn": { @@ -7774,20 +7805,22 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" }, "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "requires": { - "bytes": "3.1.0", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "dependencies": { "iconv-lite": { @@ -7996,9 +8029,9 @@ "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" }, "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "call-bind": { "version": "1.0.2", @@ -8095,18 +8128,11 @@ } }, "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "5.2.1" } }, "content-type": { @@ -8115,9 +8141,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, "cookie-signature": { "version": "1.0.6", @@ -8324,9 +8350,9 @@ "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==" }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, "des.js": { "version": "1.0.1", @@ -8338,9 +8364,9 @@ } }, "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, "dicer": { "version": "0.2.5", @@ -8468,7 +8494,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" }, "end-of-stream": { "version": "1.4.4", @@ -8563,7 +8589,7 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" }, "evp_bytestokey": { "version": "1.0.3", @@ -8591,46 +8617,47 @@ } }, "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "requires": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.4.0", + "cookie": "0.5.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", + "depd": "2.0.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "~1.1.2", + "finalhandler": "1.2.0", "fresh": "0.5.2", + "http-errors": "2.0.0", "merge-descriptors": "1.0.1", "methods": "~1.1.2", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" }, "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -8712,17 +8739,24 @@ } }, "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "on-finished": "~2.3.0", + "on-finished": "2.4.1", "parseurl": "~1.3.3", - "statuses": "~1.5.0", + "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } } }, "flatted": { @@ -8776,14 +8810,14 @@ } }, "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" }, "fs-extra": { "version": "9.1.0", @@ -9076,21 +9110,21 @@ } }, "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -9701,16 +9735,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.46.0" + "mime-db": "1.52.0" } }, "mimic-fn": { @@ -9897,9 +9931,9 @@ } }, "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, "netmask": { "version": "2.0.2", @@ -9983,9 +10017,9 @@ "optional": true }, "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "requires": { "ee-first": "1.1.1" } @@ -10162,11 +10196,11 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "requires": { - "forwarded": "~0.1.2", + "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, @@ -10263,9 +10297,12 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } }, "querystring": { "version": "0.2.0", @@ -10300,12 +10337,12 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", + "bytes": "3.1.2", + "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, @@ -10387,9 +10424,9 @@ }, "dependencies": { "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" } } }, @@ -10465,29 +10502,34 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", + "depd": "2.0.0", + "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "~1.7.2", + "http-errors": "2.0.0", "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", + "ms": "2.1.3", + "on-finished": "2.4.1", "range-parser": "~1.2.1", - "statuses": "~1.5.0" + "statuses": "2.0.1" }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" } } }, @@ -10497,20 +10539,20 @@ "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" }, "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.17.1" + "send": "0.18.0" } }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { "version": "2.4.11", @@ -10853,9 +10895,9 @@ } }, "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { "version": "2.5.0", diff --git a/runtimes/nodejs/package.json b/runtimes/nodejs/package.json index 18c5f8f1d0..eec91ce72e 100644 --- a/runtimes/nodejs/package.json +++ b/runtimes/nodejs/package.json @@ -42,7 +42,7 @@ "minio": "^7.0.28", "mongodb": "^4.1.3", "mongodb-uri": "^0.9.7", - "multer": "^1.4.2", + "multer": "^1.4.4", "node-modules-utils": "^0.8.2", "nodemailer": "^6.6.3", "validator": "^13.7.0", diff --git a/runtimes/nodejs/src/cloud-sdk/index.ts b/runtimes/nodejs/src/cloud-sdk/index.ts index 3d852db8be..fc77b52952 100644 --- a/runtimes/nodejs/src/cloud-sdk/index.ts +++ b/runtimes/nodejs/src/cloud-sdk/index.ts @@ -1,47 +1,52 @@ -import { AxiosStatic } from "axios" -import { Db, getDb } from "database-proxy" -import { CloudFunction, FunctionContext } from "../support/function-engine" -import * as mongodb from "mongodb" -import { DatabaseAgent } from "../db" -import request from 'axios' -import { SchedulerInstance } from "../support/scheduler" -import { getToken, parseToken } from "../support/token" -import { addFunctionLog } from "../support/function-log" -import { WebSocket } from "ws" -import { WebSocketAgent } from "../support/ws" -import Config from "../config" - - -export type InvokeFunctionType = (name: string, param: FunctionContext) => Promise -export type EmitFunctionType = (event: string, param: any) => void -export type GetTokenFunctionType = (payload: any, secret?: string) => string -export type ParseTokenFunctionType = (token: string, secret?: string) => any | null +import { AxiosStatic } from "axios"; +import { Db, getDb } from "database-proxy"; +import { CloudFunction, FunctionContext } from "../support/function-engine"; +import * as mongodb from "mongodb"; +import { DatabaseAgent } from "../db"; +import request from "axios"; +import { SchedulerInstance } from "../support/scheduler"; +import { getToken, parseToken } from "../support/token"; +import { addFunctionLog } from "../support/function-log"; +import { WebSocket } from "ws"; +import { WebSocketAgent } from "../support/ws"; +import Config from "../config"; + +export type InvokeFunctionType = ( + name: string, + param: FunctionContext +) => Promise; +export type EmitFunctionType = (event: string, param: any) => void; +export type GetTokenFunctionType = (payload: any, secret?: string) => string; +export type ParseTokenFunctionType = ( + token: string, + secret?: string +) => any | null; export interface MongoDriverObject { - client: mongodb.MongoClient - db: mongodb.Db + client: mongodb.MongoClient; + db: mongodb.Db; } export interface CloudSdkInterface { /** * Sending an HTTP request is actually an Axios instance. You can refer to the Axios documentation directly */ - fetch: AxiosStatic + fetch: AxiosStatic; /** * Get a laf.js database-ql instance */ - database(): Db, + database(): Db; /** - * Invoke cloud function + * Invoke cloud function */ - invoke: InvokeFunctionType + invoke: InvokeFunctionType; /** * Emit a cloud function event that other cloud functions can set triggers to listen for */ - emit: EmitFunctionType + emit: EmitFunctionType; /** * Cloud function global memory `shared` object, which can share data across multiple requests and different cloud functions @@ -49,22 +54,22 @@ export interface CloudSdkInterface { * 2. You can share some common methods, such as checkPermission(), to improve the performance of cloud functions * 3. It can cache hot data and is recommended to use it in a small amount (this object is allocated in the node VM heap because of the memory limit of the node VM heap) */ - shared: Map + shared: Map; /** * Generate a JWT Token, if don't provide `secret` fields, use current server secret key to do signature */ - getToken: GetTokenFunctionType + getToken: GetTokenFunctionType; /** * Parse a JWT Token, if don't provide `secret` fields, use current server secret key to verify signature */ - parseToken: ParseTokenFunctionType + parseToken: ParseTokenFunctionType; /** * The mongodb instance of MongoDB node.js native driver. - * - * Because the laf.js database-QL has only partial data manipulation capability, + * + * Because the laf.js database-QL has only partial data manipulation capability, * expose this `mongo` object to the cloud function, so that the cloud function has full database manipulation capability: * 1. Transaction operations * ```js @@ -89,50 +94,49 @@ export interface CloudSdkInterface { * .toArray() * ``` */ - mongo: MongoDriverObject + mongo: MongoDriverObject; /** * Websocket connection list */ - sockets: Set + sockets: Set; /** * Current app id */ - appid: string + appid: string; env: { - DB_URI?: string - SERVER_SECRET_SALT?: string - APP_ID?: string - OSS_ACCESS_KEY?: string - OSS_ACCESS_SECRET?: string - OSS_REGION?: string - OSS_INTERNAL_ENDPOINT?: string - OSS_EXTERNAL_ENDPOINT?: string - NPM_INSTALL_FLAGS?: string - RUNTIME_IMAGE?: string - } + DB_URI?: string; + SERVER_SECRET_SALT?: string; + APP_ID?: string; + OSS_ACCESS_KEY?: string; + OSS_ACCESS_SECRET?: string; + OSS_REGION?: string; + OSS_INTERNAL_ENDPOINT?: string; + OSS_EXTERNAL_ENDPOINT?: string; + NPM_INSTALL_FLAGS?: string; + RUNTIME_IMAGE?: string; + }; } - /** * Cloud SDK instance */ -const cloud: CloudSdkInterface = create() +const cloud: CloudSdkInterface = create(); /** * After the database connection is successful, update its Mongo object, otherwise it is null */ DatabaseAgent.accessor.ready.then(() => { - cloud.mongo.client = DatabaseAgent.accessor.conn - cloud.mongo.db = DatabaseAgent.accessor.db -}) + cloud.mongo.client = DatabaseAgent.accessor.conn; + cloud.mongo.db = DatabaseAgent.accessor.db; +}); /** * Create a new Cloud SDK instance - * - * @returns + * + * @returns */ export function create() { const cloud: CloudSdkInterface = { @@ -145,51 +149,50 @@ export function create() { parseToken: parseToken, mongo: { client: DatabaseAgent.accessor.conn, - db: DatabaseAgent.accessor.db + db: DatabaseAgent.accessor.db, }, sockets: WebSocketAgent.clients, appid: Config.APP_ID, env: { DB_URI: Config.DB_URI, - SERVER_SECRET_SALT: Config.SERVER_SECRET_SALT, + SERVER_SECRET_SALT: Config.SERVER_SECRET, APP_ID: process.env.APP_ID, OSS_ACCESS_KEY: process.env.APP_ID, OSS_ACCESS_SECRET: process.env.OSS_ACCESS_SECRET, OSS_REGION: process.env.OSS_REGION, OSS_INTERNAL_ENDPOINT: process.env.OSS_INTERNAL_ENDPOINT, OSS_EXTERNAL_ENDPOINT: process.env.OSS_EXTERNAL_ENDPOINT, - NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || '', - RUNTIME_IMAGE: process.env.RUNTIME_IMAGE - } - } - return cloud + NPM_INSTALL_FLAGS: process.env.NPM_INSTALL_FLAGS || "", + RUNTIME_IMAGE: process.env.RUNTIME_IMAGE, + }, + }; + return cloud; } -export default cloud - +export default cloud; /** * The cloud function is invoked in the cloud function, which runs in the cloud function. - * + * * @param name the name of cloud function to be invoked * @param param the invoke params - * @returns + * @returns */ async function invokeInFunction(name: string, param?: FunctionContext) { - const data = await CloudFunction.getFunctionByName(name) - const func = new CloudFunction(data) + const data = await CloudFunction.getFunctionByName(name); + const func = new CloudFunction(data); if (!func) { - throw new Error(`invoke() failed to get function: ${name}`) + throw new Error(`invoke() failed to get function: ${name}`); } - param = param ?? {} + param = param ?? {}; - param.requestId = param.requestId ?? 'invoke' + param.requestId = param.requestId ?? "invoke"; - param.method = param.method ?? 'call' + param.method = param.method ?? "call"; - const result = await func.invoke(param) + const result = await func.invoke(param); await addFunctionLog({ requestId: param.requestId, @@ -200,11 +203,11 @@ async function invokeInFunction(name: string, param?: FunctionContext) { time_usage: result.time_usage, data: result.data, error: result.error, - }) + }); if (result.error) { - throw result.error + throw result.error; } - return result.data -} \ No newline at end of file + return result.data; +} diff --git a/runtimes/nodejs/src/config.ts b/runtimes/nodejs/src/config.ts index 9891aa4449..169f5924e8 100644 --- a/runtimes/nodejs/src/config.ts +++ b/runtimes/nodejs/src/config.ts @@ -1,10 +1,10 @@ -import * as path from 'path' -import * as dotenv from 'dotenv' +import * as path from "path"; +import * as dotenv from "dotenv"; /** * parse environment vars from the `.env` file if existing */ -dotenv.config() +dotenv.config(); /** * configuration management @@ -14,44 +14,51 @@ export default class Config { * mongodb connection configuration */ static get DB_URI() { - if (!process.env['DB_URI']) { - throw new Error('env: `DB_URI` is missing') + if (!process.env["DB_URI"]) { + throw new Error("env: `DB_URI` is missing"); } - return process.env['DB_URI'] + return process.env["DB_URI"]; } - /** * the server secret salt, mainly used for generating tokens */ - static get SERVER_SECRET_SALT(): string { - const secret_salt = process.env['SERVER_SECRET_SALT'] ?? process.env['SERVER_SALT'] + static get SERVER_SECRET(): string { + const secret_salt = process.env["SERVER_SECRET"]; if (!secret_salt) { - throw new Error('env: `SERVER_SECRET_SALT` is missing') + throw new Error("env: `SERVER_SECRET` is missing"); } - return secret_salt + return secret_salt; } /** * the `temp path` */ static get TMP_PATH(): string { - const tmp_path = process.env['TMP_PATH'] ?? path.join(process.cwd(), "tmp") - return tmp_path + const tmp_path = process.env["TMP_PATH"] ?? path.join(process.cwd(), "tmp"); + return tmp_path; } /** * the logger level : 'fatal', 'error', 'warning', 'info', 'debug', 'trace' */ - static get LOG_LEVEL(): 'fatal' | 'error' | 'warning' | 'info' | 'debug' | 'trace' { - return process.env['LOG_LEVEL'] as any ?? (this.isProd ? 'info' : 'debug') + static get LOG_LEVEL(): + | "fatal" + | "error" + | "warning" + | "info" + | "debug" + | "trace" { + return ( + (process.env["LOG_LEVEL"] as any) ?? (this.isProd ? "info" : "debug") + ); } /** * the serving port, default is 8000 */ static get PORT(): number { - return (process.env.PORT ?? 8000) as number + return (process.env.PORT ?? 8000) as number; } /** @@ -60,44 +67,41 @@ export default class Config { * - `debug` means that only logging for debug invokes * - `never` no logging any case */ - static get ENABLE_CLOUD_FUNCTION_LOG(): 'always' | 'debug' | 'never' { - return (process.env.ENABLE_CLOUD_FUNCTION_LOG as any ?? 'always') + static get ENABLE_CLOUD_FUNCTION_LOG(): "always" | "debug" | "never" { + return (process.env.ENABLE_CLOUD_FUNCTION_LOG as any) ?? "always"; } /** * in production deploy or not */ static get isProd(): boolean { - return process.env.NODE_ENV === 'production' + return process.env.NODE_ENV === "production"; } /** * Expired time of function logs, in seconds */ static get FUNCTION_LOG_EXPIRED_TIME(): number { - return (process.env.FUNCTION_LOG_EXPIRED_TIME ?? 3600 * 24 * 3) as number + return (process.env.FUNCTION_LOG_EXPIRED_TIME ?? 3600 * 24 * 3) as number; } static get RUNTIME_IMAGE(): string { - return process.env.RUNTIME_IMAGE + return process.env.RUNTIME_IMAGE; } static get RUNTIME_VERSION(): string { - return require('../package.json')?.version + return require("../package.json")?.version; } static get APP_ID(): string { - return process.env.APP_ID + return process.env.APP_ID; } static get NPM_INSTALL_FLAGS(): string { - return process.env.NPM_INSTALL_FLAGS || '' + return process.env.NPM_INSTALL_FLAGS || ""; } static get REQUEST_LIMIT_SIZE(): string { - return process.env.REQUEST_LIMIT_SIZE || '10mb' + return process.env.REQUEST_LIMIT_SIZE || "10mb"; } - } - - diff --git a/runtimes/nodejs/src/support/token.ts b/runtimes/nodejs/src/support/token.ts index cc170a2049..61b3eed3f6 100644 --- a/runtimes/nodejs/src/support/token.ts +++ b/runtimes/nodejs/src/support/token.ts @@ -2,47 +2,47 @@ * @Author: Maslow * @Date: 2021-07-30 10:30:29 * @LastEditTime: 2021-08-18 16:47:35 - * @Description: + * @Description: */ -import Config from '../config' -import * as jwt from 'jsonwebtoken' -const DEFAULT_SALT = Config.SERVER_SECRET_SALT +import Config from "../config"; +import * as jwt from "jsonwebtoken"; +const DEFAULT_SALT = Config.SERVER_SECRET; /** * Generate a JWT token - * @param payload + * @param payload * @param expire 秒 - * @returns + * @returns */ export function getToken(payload: any, secret?: string): string { - return jwt.sign(payload, secret ?? DEFAULT_SALT) + return jwt.sign(payload, secret ?? DEFAULT_SALT); } /** * Parse a JWT token - * @param token - * @returns + * @param token + * @returns */ export function parseToken(token: string, secret?: string): any | null { - if (!token) return null - try { - const ret = jwt.verify(token, secret ?? DEFAULT_SALT) - return ret - } catch (error) { - return null - } + if (!token) return null; + try { + const ret = jwt.verify(token, secret ?? DEFAULT_SALT); + return ret; + } catch (error) { + return null; + } } /** * split bearer token * @param bearer "Bearer xxxxx" - * @returns + * @returns */ export function splitBearerToken(bearer: string): string | null { - if (!bearer) return null + if (!bearer) return null; - const splitted = bearer?.split(' ') - const token = splitted?.length === 2 ? splitted[1] : null - return token -} \ No newline at end of file + const splitted = bearer?.split(" "); + const token = splitted?.length === 2 ? splitted[1] : null; + return token; +} diff --git a/server/package-lock.json b/server/package-lock.json index 2ccca39011..518915db65 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,12 +1,12 @@ { - "name": "server", - "version": "0.0.1", + "name": "laf-server", + "version": "1.0.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "server", - "version": "0.0.1", + "name": "laf-server", + "version": "1.0.0-alpha.0", "license": "UNLICENSED", "dependencies": { "@kubernetes/client-node": "^0.17.1", @@ -18,6 +18,7 @@ "@nestjs/mapped-types": "*", "@nestjs/passport": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/schedule": "^2.1.0", "@nestjs/swagger": "^6.1.3", "@nestjs/terminus": "^9.1.2", "@nestjs/throttler": "^3.1.0", @@ -45,6 +46,7 @@ "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.2.0", "@types/compression": "^1.7.2", + "@types/cron": "^2.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", "@types/node": "^16.0.0", @@ -4287,6 +4289,28 @@ "@nestjs/core": "^9.0.0" } }, + "node_modules/@nestjs/schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.1.0.tgz", + "integrity": "sha512-4Xaw56WiW3VsxEPPnj/iDtfjcO+sUZyYAeRxD0gnF5havncxjAnv52Iw7UH3DuzzUA784xPGgGje3Fq0Gu925g==", + "dependencies": { + "cron": "2.0.0", + "uuid": "8.3.2" + }, + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0", + "reflect-metadata": "^0.1.12" + } + }, + "node_modules/@nestjs/schedule/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@nestjs/schematics": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", @@ -4707,6 +4731,16 @@ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, + "node_modules/@types/cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-xZM08fqvwIXgghtPVkSPKNgC+JoMQ2OHazEvyTKnNf7aWu1aB6/4lBbQFrb03Td2cUGG7ITzMv3mFYnMu6xRaQ==", + "dev": true, + "dependencies": { + "@types/luxon": "*", + "@types/node": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -4813,6 +4847,12 @@ "@types/node": "*" } }, + "node_modules/@types/luxon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.1.0.tgz", + "integrity": "sha512-gCd/HcCgjqSxfMrgtqxCgYk/22NBQfypwFUG7ZAyG/4pqs51WLTcUzVp1hqTbieDYeHS3WoVEh2Yv/2l+7B0Vg==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -6861,6 +6901,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-RPeRunBCFr/WEo7WLp8Jnm45F/ziGJiHVvVQEBSDTSGu6uHW49b2FOP2O14DcXlGJRLhwE7TIoDzHHK4KmlL6g==", + "dependencies": { + "luxon": "^1.23.x" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -10524,6 +10572,14 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "node_modules/luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==", + "engines": { + "node": "*" + } + }, "node_modules/macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", @@ -17544,6 +17600,22 @@ "tslib": "2.4.1" } }, + "@nestjs/schedule": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-2.1.0.tgz", + "integrity": "sha512-4Xaw56WiW3VsxEPPnj/iDtfjcO+sUZyYAeRxD0gnF5havncxjAnv52Iw7UH3DuzzUA784xPGgGje3Fq0Gu925g==", + "requires": { + "cron": "2.0.0", + "uuid": "8.3.2" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, "@nestjs/schematics": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.3.tgz", @@ -17869,6 +17941,16 @@ "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", "dev": true }, + "@types/cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-xZM08fqvwIXgghtPVkSPKNgC+JoMQ2OHazEvyTKnNf7aWu1aB6/4lBbQFrb03Td2cUGG7ITzMv3mFYnMu6xRaQ==", + "dev": true, + "requires": { + "@types/luxon": "*", + "@types/node": "*" + } + }, "@types/eslint": { "version": "8.4.10", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", @@ -17975,6 +18057,12 @@ "@types/node": "*" } }, + "@types/luxon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.1.0.tgz", + "integrity": "sha512-gCd/HcCgjqSxfMrgtqxCgYk/22NBQfypwFUG7ZAyG/4pqs51WLTcUzVp1hqTbieDYeHS3WoVEh2Yv/2l+7B0Vg==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -19570,6 +19658,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "cron": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.0.0.tgz", + "integrity": "sha512-RPeRunBCFr/WEo7WLp8Jnm45F/ziGJiHVvVQEBSDTSGu6uHW49b2FOP2O14DcXlGJRLhwE7TIoDzHHK4KmlL6g==", + "requires": { + "luxon": "^1.23.x" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -22335,6 +22431,11 @@ "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, + "luxon": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz", + "integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==" + }, "macos-release": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", diff --git a/server/package.json b/server/package.json index dc6af1f17e..7e0ad59f33 100644 --- a/server/package.json +++ b/server/package.json @@ -31,6 +31,7 @@ "@nestjs/mapped-types": "*", "@nestjs/passport": "^9.0.0", "@nestjs/platform-express": "^9.0.0", + "@nestjs/schedule": "^2.1.0", "@nestjs/swagger": "^6.1.3", "@nestjs/terminus": "^9.1.2", "@nestjs/throttler": "^3.1.0", @@ -58,6 +59,7 @@ "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.2.0", "@types/compression": "^1.7.2", + "@types/cron": "^2.0.0", "@types/express": "^4.17.13", "@types/jest": "28.1.8", "@types/node": "^16.0.0", diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 16c26d43ce..dfa1625b88 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -48,6 +48,8 @@ model Bundle { displayName String limitCPU Int limitMemory Int + requestCPU Int + requestMemory Int databaseCapacity Int storageCapacity Int networkTrafficOutbound Int diff --git a/server/src/app.module.ts b/server/src/app.module.ts index bbfd26ce48..014599e1fb 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -12,9 +12,16 @@ import { ApplicationsModule } from './applications/applications.module' import { AuthModule } from './auth/auth.module' import { ThrottlerModule } from '@nestjs/throttler' import { InitializerModule } from './initializer/initializer.module' +import { InstanceModule } from './instance/instance.module' +import { ScheduleModule } from '@nestjs/schedule' @Module({ imports: [ + ScheduleModule.forRoot(), + ThrottlerModule.forRoot({ + ttl: 60, + limit: 10, + }), FunctionsModule, PoliciesModule, BucketsModule, @@ -24,11 +31,8 @@ import { InitializerModule } from './initializer/initializer.module' AuthModule, CoreModule, ApplicationsModule, - ThrottlerModule.forRoot({ - ttl: 60, - limit: 10, - }), InitializerModule, + InstanceModule, ], controllers: [AppController], providers: [AppService], diff --git a/server/src/applications/applications.controller.ts b/server/src/applications/applications.controller.ts index 6ac882ef14..d9cdde9118 100644 --- a/server/src/applications/applications.controller.ts +++ b/server/src/applications/applications.controller.ts @@ -8,8 +8,6 @@ import { Delete, UseGuards, Req, - HttpException, - HttpStatus, Logger, } from '@nestjs/common' import { @@ -91,20 +89,10 @@ export class ApplicationsController { @ApiOperation({ summary: 'Get an application by appid' }) @UseGuards(JwtAuthGuard, ApplicationAuthGuard) @Get(':appid') - async findOne(@Param('appid') appid: string, @Req() req: IRequest) { - const app = req.application - + async findOne(@Param('appid') appid: string) { // get sub resources const resources = await this.appService.getSubResources(appid) - // update app phase if needed - await this.appService.updatePhaseIfSubResourceCreated( - app, - resources.database, - resources.gateway, - resources.oss, - ) - const data = await this.appService.findOne(appid) const res = { ...data, ...resources } diff --git a/server/src/applications/applications.service.ts b/server/src/applications/applications.service.ts index b306ea7862..9dd5d162aa 100644 --- a/server/src/applications/applications.service.ts +++ b/server/src/applications/applications.service.ts @@ -4,10 +4,7 @@ import { Application, ApplicationPhase, ApplicationState, - Bundle, Prisma, - Region, - Runtime, } from '@prisma/client' import { PrismaService } from '../prisma.service' import { UpdateApplicationDto } from './dto/update-application.dto' @@ -16,10 +13,6 @@ import { GatewayCoreService } from 'src/core/gateway.cr.service' import { OSSUserCoreService } from 'src/core/oss-user.cr.service' import { APPLICATION_SECRET_KEY } from 'src/constants' import { GenerateAlphaNumericPassword } from 'src/common/random' -import { Database } from 'src/core/api/database.cr' -import { Gateway } from 'src/core/api/gateway.cr' -import { OSSUser } from 'src/core/api/oss-user.cr' -import { isConditionTrue } from 'src/common/getter' @Injectable() export class ApplicationsService { @@ -133,38 +126,20 @@ export class ApplicationsService { }) } - async findOne(appid: string) { + async findOne(appid: string, include?: Prisma.ApplicationInclude) { const application = await this.prisma.application.findUnique({ where: { appid }, include: { - region: true, - bundle: true, - runtime: true, + region: include?.region, + bundle: include?.bundle, + runtime: include?.runtime, + configuration: include?.configuration, }, }) return application } - async updatePhaseIfSubResourceCreated( - app: Application, - database: Database, - gateway: Gateway, - oss: OSSUser, - ) { - if (app.phase !== ApplicationPhase.Creating) return - if (!isConditionTrue('Ready', database.status?.conditions)) return - if (!isConditionTrue('Ready', gateway.status?.conditions)) return - if (!isConditionTrue('Ready', oss.status?.conditions)) return - - return await this.prisma.application.update({ - where: { appid: app.appid }, - data: { - phase: ApplicationPhase.Created, - }, - }) - } - async update(appid: string, dto: UpdateApplicationDto) { try { // update app in db diff --git a/server/src/common/getter.ts b/server/src/common/getter.ts index 3547eec502..4f3abe5949 100644 --- a/server/src/common/getter.ts +++ b/server/src/common/getter.ts @@ -18,7 +18,7 @@ export function GetApplicationNamespaceById(appid: string): string { return appid } -export function isConditionTrue(type: string, conditions: Condition[]) { +export function isConditionTrue(type: string, conditions: Condition[] | any[]) { if (!conditions) return false for (const condition of conditions) { diff --git a/server/src/core/application.cr.service.ts b/server/src/core/application.cr.service.ts index 291fcb010f..80cb2af71d 100644 --- a/server/src/core/application.cr.service.ts +++ b/server/src/core/application.cr.service.ts @@ -4,8 +4,6 @@ import * as k8s from '@kubernetes/client-node' import * as nanoid from 'nanoid' import { ResourceLabels } from '../constants' import { GetApplicationNamespaceById } from '../common/getter' -import { CreateApplicationDto } from '../applications/dto/create-application.dto' -import { UpdateApplicationDto } from '../applications/dto/update-application.dto' @Injectable() export class ApplicationCoreService { diff --git a/server/src/initializer/initializer.service.spec.ts b/server/src/initializer/initializer.service.spec.ts deleted file mode 100644 index 8f6a5356b0..0000000000 --- a/server/src/initializer/initializer.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing' -import { InitializerService } from './initializer.service' - -describe('InitializerService', () => { - let service: InitializerService - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [InitializerService], - }).compile() - - service = module.get(InitializerService) - }) - - it('should be defined', () => { - expect(service).toBeDefined() - }) -}) diff --git a/server/src/initializer/initializer.service.ts b/server/src/initializer/initializer.service.ts index 35aedc1f6f..404d81d356 100644 --- a/server/src/initializer/initializer.service.ts +++ b/server/src/initializer/initializer.service.ts @@ -9,11 +9,9 @@ export class InitializerService { async createDefaultRegion() { // check if exists - const region = await this.prisma.region.findUnique({ - where: { name: 'default' }, - }) - if (region) { - this.logger.debug('default region already exists') + const existed = await this.prisma.region.count() + if (existed) { + this.logger.debug('region already exists') return } @@ -27,10 +25,8 @@ export class InitializerService { async createDefaultBundle() { // check if exists - const bundle = await this.prisma.bundle.findUnique({ - where: { name: 'mini' }, - }) - if (bundle) { + const existed = await this.prisma.bundle.count() + if (existed) { this.logger.debug('default bundle already exists') return } @@ -38,10 +34,12 @@ export class InitializerService { // create default bundle const res = await this.prisma.bundle.create({ data: { - name: 'mini', - displayName: 'Mini', + name: 'standard', + displayName: 'Standard', limitCPU: 0.5 * CPU_UNIT, - limitMemory: 128, + limitMemory: 256, + requestCPU: 0.05 * CPU_UNIT, + requestMemory: 128, databaseCapacity: 1024, storageCapacity: 1024 * 5, networkTrafficOutbound: 1024 * 5, @@ -54,10 +52,8 @@ export class InitializerService { async createDefaultRuntime() { // check if exists - const runtime = await this.prisma.runtime.findUnique({ - where: { name: 'node-laf' }, - }) - if (runtime) { + const existed = await this.prisma.runtime.count() + if (existed) { this.logger.debug('default runtime already exists') return } @@ -65,12 +61,13 @@ export class InitializerService { // create default runtime const res = await this.prisma.runtime.create({ data: { - name: 'node-laf', + name: 'node', type: 'node:laf', image: { main: 'docker.io/lafyun/runtime-node:1.0.0-alpha.0', + init: 'docker.io/lafyun/runtime-node-init:1.0.0-alpha.0', }, - version: '1.0-alpha.0', + version: '1.0.0-alpha.0', latest: true, }, }) diff --git a/server/src/instance/instance-task.service.ts b/server/src/instance/instance-task.service.ts new file mode 100644 index 0000000000..b8f8b6d51f --- /dev/null +++ b/server/src/instance/instance-task.service.ts @@ -0,0 +1,193 @@ +import { Injectable, Logger } from '@nestjs/common' +import { Cron, CronExpression } from '@nestjs/schedule' +import { ApplicationPhase, ApplicationState } from '@prisma/client' +import { ApplicationsService } from 'src/applications/applications.service' +import { isConditionTrue } from 'src/common/getter' +import { DatabaseCoreService } from 'src/core/database.cr.service' +import { GatewayCoreService } from 'src/core/gateway.cr.service' +import { OSSUserCoreService } from 'src/core/oss-user.cr.service' +import { PrismaService } from 'src/prisma.service' +import { InstanceService } from './instance.service' + +@Injectable() +export class InstanceTaskService { + private readonly logger = new Logger(InstanceTaskService.name) + + constructor( + private readonly instanceService: InstanceService, + private readonly databaseCore: DatabaseCoreService, + private readonly gatewayCore: GatewayCoreService, + private readonly ossCore: OSSUserCoreService, + private readonly prisma: PrismaService, + ) {} + + @Cron(CronExpression.EVERY_SECOND) + async handleCreating() { + const apps = await this.prisma.application.findMany({ + where: { + phase: ApplicationPhase.Creating, + }, + take: 5, + }) + + for (const app of apps) { + try { + const appid = app.appid + const database = await this.databaseCore.findOne(appid) + const oss = await this.ossCore.findOne(appid) + const gateway = await this.gatewayCore.findOne(appid) + if (!isConditionTrue('Ready', database.status?.conditions)) return + if (!isConditionTrue('Ready', gateway.status?.conditions)) return + if (!isConditionTrue('Ready', oss.status?.conditions)) return + + await this.prisma.application.updateMany({ + where: { + appid: app.appid, + phase: ApplicationPhase.Creating, + }, + data: { + phase: ApplicationPhase.Created, + }, + }) + this.logger.debug(`Application ${app.appid} updated to phase created`) + } catch (e) { + this.logger.error(e) + } + } + } + + @Cron(CronExpression.EVERY_SECOND) + async handlePreparedStart() { + const apps = await this.prisma.application.findMany({ + where: { + state: ApplicationState.Running, + phase: { + in: [ApplicationPhase.Created, ApplicationPhase.Stopped], + }, + }, + take: 5, + }) + + for (const app of apps) { + try { + const appid = app.appid + await this.instanceService.create(appid) + + await this.prisma.application.updateMany({ + where: { + appid: app.appid, + state: ApplicationState.Running, + phase: { + in: [ApplicationPhase.Created, ApplicationPhase.Stopped], + }, + }, + data: { + phase: ApplicationPhase.Starting, + }, + }) + + this.logger.debug(`Application ${app.appid} updated to phase starting`) + } catch (error) { + this.logger.error(error, error.response?.body) + } + } + } + + @Cron(CronExpression.EVERY_SECOND) + async handleStarting() { + const apps = await this.prisma.application.findMany({ + where: { + phase: ApplicationPhase.Starting, + }, + take: 5, + }) + + for (const app of apps) { + try { + const appid = app.appid + const instance = await this.instanceService.get(appid) + if (!isConditionTrue('Ready', instance.deployment.status?.conditions)) + continue + + if (!instance.service) continue + + // update application state + await this.prisma.application.updateMany({ + where: { + appid, + phase: ApplicationPhase.Starting, + }, + data: { + phase: ApplicationPhase.Started, + }, + }) + } catch (error) { + this.logger.error(error) + } + } + } + + @Cron(CronExpression.EVERY_SECOND) + async handlePreparedStop() { + const apps = await this.prisma.application.findMany({ + where: { + state: ApplicationState.Stopped, + phase: ApplicationPhase.Started, + }, + take: 5, + }) + + for (const app of apps) { + try { + const appid = app.appid + await this.instanceService.remove(appid) + + await this.prisma.application.updateMany({ + where: { + appid: app.appid, + state: ApplicationState.Stopped, + phase: ApplicationPhase.Started, + }, + data: { + phase: ApplicationPhase.Stopping, + }, + }) + + this.logger.debug(`Application ${app.appid} updated to phase stopping`) + } catch (error) { + this.logger.error(error) + } + } + } + + @Cron(CronExpression.EVERY_SECOND) + async handleStopping() { + const apps = await this.prisma.application.findMany({ + where: { + phase: ApplicationPhase.Stopping, + }, + take: 5, + }) + + for (const app of apps) { + try { + const appid = app.appid + const instance = await this.instanceService.get(appid) + if (instance.deployment) continue + + // update application state + await this.prisma.application.updateMany({ + where: { + appid, + phase: ApplicationPhase.Stopping, + }, + data: { + phase: ApplicationPhase.Stopped, + }, + }) + } catch (error) { + this.logger.error(error) + } + } + } +} diff --git a/server/src/instance/instance.module.ts b/server/src/instance/instance.module.ts new file mode 100644 index 0000000000..7f22997df8 --- /dev/null +++ b/server/src/instance/instance.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common' +import { InstanceService } from './instance.service' +import { InstanceTaskService } from './instance-task.service' +import { CoreModule } from 'src/core/core.module' +import { PrismaService } from 'src/prisma.service' + +@Module({ + imports: [CoreModule], + providers: [InstanceService, InstanceTaskService, PrismaService], +}) +export class InstanceModule {} diff --git a/server/src/instance/instance.service.ts b/server/src/instance/instance.service.ts new file mode 100644 index 0000000000..780c9c46b6 --- /dev/null +++ b/server/src/instance/instance.service.ts @@ -0,0 +1,219 @@ +import { V1Deployment } from '@kubernetes/client-node' +import { Injectable, Logger } from '@nestjs/common' +import { GetApplicationNamespaceById } from 'src/common/getter' +import { CPU_UNIT, ResourceLabels } from 'src/constants' +import { DatabaseCoreService } from 'src/core/database.cr.service' +import { KubernetesService } from 'src/core/kubernetes.service' +import { OSSUserCoreService } from 'src/core/oss-user.cr.service' +import { PrismaService } from 'src/prisma.service' + +@Injectable() +export class InstanceService { + private logger = new Logger('InstanceService') + constructor( + private readonly k8sService: KubernetesService, + private readonly databaseCore: DatabaseCoreService, + private readonly ossCore: OSSUserCoreService, + private readonly prisma: PrismaService, + ) {} + + async create(appid: string) { + const labels = { [ResourceLabels.APP_ID]: appid } + + const res = await this.get(appid) + if (!res.deployment) { + await this.createDeployment(appid, labels) + } + + if (!res.service) { + await this.createService(appid, labels) + } + } + + async createDeployment(appid: string, labels: any) { + const namespace = GetApplicationNamespaceById(appid) + const app = await this.prisma.application.findUnique({ + where: { appid }, + include: { + configuration: true, + bundle: true, + runtime: true, + region: true, + }, + }) + const database = await this.databaseCore.findOne(appid) + const oss = await this.ossCore.findOne(appid) + + // prepare params + const limitMemory = app.bundle.limitMemory + const limitCpu = app.bundle.limitCPU + const requestMemory = app.bundle.requestMemory + const requestCpu = app.bundle.requestCPU + const max_old_space_size = ~~(limitMemory * 0.8) + + const env = [ + { name: 'DB_URI', value: database.status?.connectionUri }, + { name: 'APP_ID', value: app.appid }, + { name: 'OSS_ACCESS_KEY', value: oss.status?.accessKey }, + { name: 'OSS_ACCESS_SECRET', value: oss.status?.secretKey }, + { name: 'OSS_INTERNAL_ENDPOINT', value: oss.status?.endpoint }, + { name: 'OSS_EXTERNAL_ENDPOINT', value: oss.status?.endpoint }, + { name: 'OSS_REGION', value: oss.status?.region }, + { name: 'FLAGS', value: `--max_old_space_size=${max_old_space_size}` }, + ] + + // merge env from app configuration, override if exists + const extraEnv = app.configuration.environments || [] + extraEnv.forEach((e) => { + const index = env.findIndex((x) => x.name === e.name) + if (index > -1) { + env[index] = e + } else { + env.push(e) + } + }) + + // create deployment + const data = new V1Deployment() + data.metadata = { name: app.appid, labels } + data.spec = { + replicas: 1, + selector: { matchLabels: labels }, + template: { + metadata: { labels }, + spec: { + terminationGracePeriodSeconds: 15, + automountServiceAccountToken: false, + containers: [ + { + image: app.runtime.image.main, + command: ['sh', '/app/start.sh'], + name: app.appid, + env, + ports: [{ containerPort: 8000, name: 'http' }], + resources: { + limits: { + cpu: `${limitCpu}m`, + memory: `${limitMemory}Mi`, + }, + requests: { + cpu: `${requestCpu}m`, + memory: `${requestMemory}Mi`, + }, + }, + startupProbe: { + httpGet: { + path: '/__healthz__', + port: 'http', + httpHeaders: [{ name: 'Referer', value: 'startupProbe' }], + }, + initialDelaySeconds: 0, + periodSeconds: 3, + timeoutSeconds: 3, + failureThreshold: 240, + }, + readinessProbe: { + httpGet: { + path: '/__healthz__', + port: 'http', + httpHeaders: [{ name: 'Referer', value: 'readinessProbe' }], + }, + initialDelaySeconds: 0, + periodSeconds: 60, + timeoutSeconds: 5, + failureThreshold: 3, + }, + }, + ], + initContainers: [ + { + name: 'init', + image: app.runtime.image.init, + command: ['sh', '/app/init.sh'], + env, + }, + ], + }, + }, + } + + const res = await this.k8sService.appsV1Api.createNamespacedDeployment( + namespace, + data, + ) + + this.logger.log(`create k8s deployment ${res.body?.metadata?.name}`) + + return res.body + } + + async createService(appid: string, labels: any) { + const namespace = GetApplicationNamespaceById(appid) + const serviceName = this.getServiceName(appid) + const res = await this.k8sService.coreV1Api.createNamespacedService( + namespace, + { + metadata: { name: serviceName, labels }, + spec: { + selector: labels, + type: 'ClusterIP', + ports: [{ port: 8000, targetPort: 8000, protocol: 'TCP' }], + }, + }, + ) + this.logger.log(`create k8s service ${res.body?.metadata?.name}`) + return res.body + } + + async remove(appid: string) { + const { deployment, service } = await this.get(appid) + const namespace = GetApplicationNamespaceById(appid) + if (deployment) { + await this.k8sService.appsV1Api.deleteNamespacedDeployment( + appid, + namespace, + ) + } + if (service) { + const name = this.getServiceName(appid) + await this.k8sService.coreV1Api.deleteNamespacedService(name, namespace) + } + } + + async get(appid: string) { + const deployment = await this.getDeployment(appid) + const service = await this.getService(appid) + return { deployment, service } + } + + async getDeployment(appid: string) { + try { + const namespace = GetApplicationNamespaceById(appid) + const res = await this.k8sService.appsV1Api.readNamespacedDeployment( + appid, + namespace, + ) + return res.body + } catch (error) { + return null + } + } + + async getService(appid: string) { + try { + const serviceName = this.getServiceName(appid) + const namespace = GetApplicationNamespaceById(appid) + const res = await this.k8sService.coreV1Api.readNamespacedService( + serviceName, + namespace, + ) + return res.body + } catch (error) { + return null + } + } + + getServiceName(appid: string) { + return `s-${appid}` + } +}