@paralect/hive 0.1.48 → 0.1.50-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/commands/deslop.md +12 -0
- package/.hive/.babelrc +3 -0
- package/.hive/.cursor/commands/add-endpoint.md +262 -0
- package/.hive/.cursor/commands/add-handler.md +137 -0
- package/.hive/.cursor/commands/add-middleware.md +95 -0
- package/.hive/.cursor/commands/add-resource.md +71 -0
- package/.hive/.cursor/commands/add-scheduler.md +138 -0
- package/.hive/.cursor/commands/add-service.md +188 -0
- package/.hive/.cursor/skills/hive-auth/SKILL.md +134 -0
- package/.hive/.cursor/skills/hive-database/SKILL.md +103 -0
- package/.hive/.cursor/skills/hive-endpoint/SKILL.md +103 -0
- package/.hive/.cursor/skills/hive-handler/SKILL.md +88 -0
- package/.hive/.cursor/skills/hive-mapping/SKILL.md +85 -0
- package/.hive/.cursor/skills/hive-middleware/SKILL.md +104 -0
- package/.hive/.cursor/skills/hive-overview/SKILL.md +50 -0
- package/.hive/.cursor/skills/hive-scheduler/SKILL.md +94 -0
- package/.hive/.cursor/skills/hive-schema/SKILL.md +73 -0
- package/.hive/.cursor/skills/hive-service/SKILL.md +90 -0
- package/.hive/.dockerignore +1 -0
- package/.hive/Dockerfile +22 -0
- package/.hive/Dockerfile.dev +33 -0
- package/.hive/Dockerfile.prod +29 -0
- package/.hive/README.md +11 -0
- package/.hive/bin/deploy.sh +5 -0
- package/.hive/bin/start.sh +2 -0
- package/.hive/bootstrap-hive.js +118 -0
- package/.hive/deploy/api/Chart.yaml +6 -0
- package/.hive/deploy/api/staging.yaml +3 -0
- package/.hive/deploy/api/templates/deployment.yaml +44 -0
- package/.hive/deploy/api/templates/ingress.yaml +26 -0
- package/.hive/deploy/api/templates/service.yaml +14 -0
- package/.hive/deploy/script/Dockerfile +39 -0
- package/.hive/deploy/script/package-lock.json +1499 -0
- package/.hive/deploy/script/package.json +12 -0
- package/.hive/deploy/script/src/config.js +48 -0
- package/.hive/deploy/script/src/index.js +108 -0
- package/.hive/deploy/script/src/util.js +19 -0
- package/.hive/initial-data.json +176 -0
- package/.hive/package-lock.json +10242 -0
- package/.hive/package.json +98 -0
- package/.hive/ship_logo.png +0 -0
- package/.hive/src/app-config/app.js +3 -0
- package/.hive/src/app-config/assertEnv.js +15 -0
- package/.hive/src/app-config/index.js +62 -0
- package/.hive/src/app.js +69 -0
- package/.hive/src/assets/emails/components/header.mjml +13 -0
- package/.hive/src/assets/emails/dist/.gitkeep +0 -0
- package/.hive/src/assets/emails/signup-welcome.mjml +34 -0
- package/.hive/src/assets/emails/styles/index.mjml +77 -0
- package/.hive/src/autoMap/addHandlers.js +142 -0
- package/.hive/src/autoMap/getDependentFields.js +37 -0
- package/.hive/src/autoMap/mapSchema.js +99 -0
- package/.hive/src/autoMap/schemaMappings.js +13 -0
- package/.hive/src/autoMap/schemaMappings.json +3 -0
- package/.hive/src/bullMqBus.js +21 -0
- package/.hive/src/bullMqWrapper.js +23 -0
- package/.hive/src/db.js +52 -0
- package/.hive/src/emails/MyEmailComponent.jsx +14 -0
- package/.hive/src/emails/compiled/MyEmailComponent.js +18 -0
- package/.hive/src/emails/compiled/compiled/MyEmailComponent.js +18 -0
- package/.hive/src/helpers/db/ifUpdated.js +22 -0
- package/.hive/src/helpers/getMiddlewares.js +38 -0
- package/.hive/src/helpers/getResourceEndpoints.js +28 -0
- package/.hive/src/helpers/getResources.js +32 -0
- package/.hive/src/helpers/getSchemas.js +50 -0
- package/.hive/src/helpers/importHandlers.js +29 -0
- package/.hive/src/helpers/isZodArray.js +13 -0
- package/.hive/src/helpers/prettierFormat.js +8 -0
- package/.hive/src/helpers/schema/db.schema.js +9 -0
- package/.hive/src/helpers/schema/pagination.schema.js +14 -0
- package/.hive/src/ioEmitter.js +9 -0
- package/.hive/src/jsconfig.json +5 -0
- package/.hive/src/lib/node-mongo/.github/workflows/npm-publish.yml +32 -0
- package/.hive/src/lib/node-mongo/API.md +654 -0
- package/.hive/src/lib/node-mongo/CHANGELOG.md +98 -0
- package/.hive/src/lib/node-mongo/README.md +97 -0
- package/.hive/src/lib/node-mongo/package-lock.json +3682 -0
- package/.hive/src/lib/node-mongo/package.json +74 -0
- package/.hive/src/lib/node-mongo/src/index.js +64 -0
- package/.hive/src/lib/node-mongo/src/mongo-query-service.js +78 -0
- package/.hive/src/lib/node-mongo/src/mongo-service-error.js +15 -0
- package/.hive/src/lib/node-mongo/src/mongo-service.js +303 -0
- package/.hive/src/logger.js +43 -0
- package/.hive/src/middlewares/allowNoAuth.js +9 -0
- package/.hive/src/middlewares/attachUser.js +41 -0
- package/.hive/src/middlewares/global/extractUserTokens.js +15 -0
- package/.hive/src/middlewares/global/tryToAttachUser.js +33 -0
- package/.hive/src/middlewares/isAuthorized.js +18 -0
- package/.hive/src/middlewares/shouldExist.js +37 -0
- package/.hive/src/middlewares/shouldNotExist.js +19 -0
- package/.hive/src/middlewares/uploadFile.js +5 -0
- package/.hive/src/middlewares/validate.js +32 -0
- package/.hive/src/migrations/migration.js +8 -0
- package/.hive/src/migrations/migration.service.js +73 -0
- package/.hive/src/migrations/migrations/1.js +22 -0
- package/.hive/src/migrations/migrations-log/migration-log.schema.js +13 -0
- package/.hive/src/migrations/migrations-log/migration-log.service.js +50 -0
- package/.hive/src/migrations/migrations.schema.js +6 -0
- package/.hive/src/migrations/migrator.js +75 -0
- package/.hive/src/migrator.js +4 -0
- package/.hive/src/resources/_dev/endpoints/triggerSchedulerHandler.js +32 -0
- package/.hive/src/resources/health/endpoints/get.js +19 -0
- package/.hive/src/resources/schemaMappings/schemaMappings.schema.js +6 -0
- package/.hive/src/resources/tokens/methods/generateSecureToken.js +9 -0
- package/.hive/src/resources/tokens/methods/setToken.js +8 -0
- package/.hive/src/resources/tokens/methods/storeToken.js +35 -0
- package/.hive/src/resources/tokens/tokens.schema.js +11 -0
- package/.hive/src/resources/users/endpoints/getCurrentUser.js +14 -0
- package/.hive/src/resources/users/endpoints/getUserProfile.js +19 -0
- package/.hive/src/resources/users/handlers/test.js +1 -0
- package/.hive/src/resources/users/methods/ensureUserCreated.js +68 -0
- package/.hive/src/resources/users/users.schema.js +16 -0
- package/.hive/src/routes/index.js +172 -0
- package/.hive/src/routes/middlewares/attachCustomErrors.js +28 -0
- package/.hive/src/routes/middlewares/routeErrorHandler.js +27 -0
- package/.hive/src/scheduler/handlers/sendDailyReport.example.js +7 -0
- package/.hive/src/scheduler.js +32 -0
- package/.hive/src/security.util.js +38 -0
- package/.hive/src/services/emailService.js +15 -0
- package/.hive/src/services/globalTest.js +0 -0
- package/.hive/src/services/setCookie.js +21 -0
- package/.hive/src/socketIo.js +99 -0
- package/.hive/tsconfig.json +31 -0
- package/AGENTS.md +96 -0
- package/README.md +271 -0
- package/cli/helpers/docker.js +59 -0
- package/cli/helpers/envCheck.js +123 -0
- package/cli/helpers/findPort.js +32 -0
- package/cli/hive.js +155 -15
- package/package.json +1 -1
- package/starter/.cursor/commands/add-endpoint.md +262 -0
- package/starter/.cursor/commands/add-handler.md +137 -0
- package/starter/.cursor/commands/add-middleware.md +95 -0
- package/starter/.cursor/commands/add-resource.md +71 -0
- package/starter/.cursor/commands/add-scheduler.md +138 -0
- package/starter/.cursor/commands/add-service.md +188 -0
- package/starter/.cursor/skills/hive-auth/SKILL.md +134 -0
- package/starter/.cursor/skills/hive-database/SKILL.md +103 -0
- package/starter/.cursor/skills/hive-endpoint/SKILL.md +103 -0
- package/starter/.cursor/skills/hive-handler/SKILL.md +88 -0
- package/starter/.cursor/skills/hive-mapping/SKILL.md +85 -0
- package/starter/.cursor/skills/hive-middleware/SKILL.md +104 -0
- package/starter/.cursor/skills/hive-overview/SKILL.md +50 -0
- package/starter/.cursor/skills/hive-scheduler/SKILL.md +94 -0
- package/starter/.cursor/skills/hive-schema/SKILL.md +73 -0
- package/starter/.cursor/skills/hive-service/SKILL.md +90 -0
- package/starter/src/app.js +4 -3
- package/test-app/.cursor/commands/add-endpoint.md +262 -0
- package/test-app/.cursor/commands/add-handler.md +137 -0
- package/test-app/.cursor/commands/add-middleware.md +95 -0
- package/test-app/.cursor/commands/add-resource.md +71 -0
- package/test-app/.cursor/commands/add-scheduler.md +138 -0
- package/test-app/.cursor/commands/add-service.md +188 -0
- package/test-app/.cursor/skills/hive-auth/SKILL.md +134 -0
- package/test-app/.cursor/skills/hive-database/SKILL.md +103 -0
- package/test-app/.cursor/skills/hive-endpoint/SKILL.md +103 -0
- package/test-app/.cursor/skills/hive-handler/SKILL.md +88 -0
- package/test-app/.cursor/skills/hive-mapping/SKILL.md +85 -0
- package/test-app/.cursor/skills/hive-middleware/SKILL.md +104 -0
- package/test-app/.cursor/skills/hive-overview/SKILL.md +50 -0
- package/test-app/.cursor/skills/hive-scheduler/SKILL.md +94 -0
- package/test-app/.cursor/skills/hive-schema/SKILL.md +73 -0
- package/test-app/.cursor/skills/hive-service/SKILL.md +90 -0
- package/test-app/package-lock.json +8684 -0
- package/test-app/package.json +21 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "koa-api-starter",
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Koa api",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "src/app.js",
|
|
7
|
+
"author": "Paralect",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"koa",
|
|
11
|
+
"rest api",
|
|
12
|
+
"paralect"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/paralect/koa-api-starter.git"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build-assets": "mjml ./src/assets/emails/*.mjml -o ./src/assets/emails/dist/",
|
|
20
|
+
"build-emails": "babel src/emails --out-dir src/emails/compiled",
|
|
21
|
+
"dev": "tsx --watch src/app.js",
|
|
22
|
+
"init-project": "./bin/init-project.sh",
|
|
23
|
+
"start": "tsx src/app.js",
|
|
24
|
+
"migrate": "node ./src/migrator.js",
|
|
25
|
+
"schedule-dev": "nodemon --watch ./src ./src/scheduler ./src/scheduler.js",
|
|
26
|
+
"schedule": "node ./src/scheduler.js",
|
|
27
|
+
"precommit": "lint-staged",
|
|
28
|
+
"prepare": "husky install"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@koa/cors": "5.0.0",
|
|
32
|
+
"@koa/multer": "3.0.2",
|
|
33
|
+
"@koa/router": "12.0.1",
|
|
34
|
+
"@paralect/node-mongo": "3.2.0",
|
|
35
|
+
"@react-email/components": "^0.0.21",
|
|
36
|
+
"@react-email/render": "^0.0.16",
|
|
37
|
+
"@sendgrid/mail": "8.1.3",
|
|
38
|
+
"@socket.io/redis-adapter": "8.3.0",
|
|
39
|
+
"@socket.io/redis-emitter": "5.1.0",
|
|
40
|
+
"app-module-path": "2.2.0",
|
|
41
|
+
"aws-sdk": "2.1661.0",
|
|
42
|
+
"bcryptjs": "2.4.3",
|
|
43
|
+
"bullmq": "^5.10.3",
|
|
44
|
+
"dotenv": "16.4.5",
|
|
45
|
+
"eslint-config-prettier": "9.1.0",
|
|
46
|
+
"handlebars": "4.7.8",
|
|
47
|
+
"joi": "^17.13.3",
|
|
48
|
+
"koa": "2.15.3",
|
|
49
|
+
"koa-bodyparser": "4.4.1",
|
|
50
|
+
"koa-helmet": "7.0.2",
|
|
51
|
+
"koa-logger": "3.2.1",
|
|
52
|
+
"koa-mount": "4.0.0",
|
|
53
|
+
"koa-qs": "3.0.0",
|
|
54
|
+
"lodash": "4.17.21",
|
|
55
|
+
"mjml": "4.15.3",
|
|
56
|
+
"mkdirp": "3.0.1",
|
|
57
|
+
"moment": "2.30.1",
|
|
58
|
+
"moment-duration-format": "2.3.2",
|
|
59
|
+
"monk": "^7.3.4",
|
|
60
|
+
"multer": "1.4.4",
|
|
61
|
+
"node-schedule": "2.1.1",
|
|
62
|
+
"nodemailer": "^6.9.14",
|
|
63
|
+
"prettier": "3.3.3",
|
|
64
|
+
"psl": "1.9.0",
|
|
65
|
+
"redis": "^3.1.2",
|
|
66
|
+
"require-dir": "1.2.0",
|
|
67
|
+
"slug": "^9.1.0",
|
|
68
|
+
"socket.io": "4.7.5",
|
|
69
|
+
"socket.io-emitter": "3.2.0",
|
|
70
|
+
"tail": "2.2.6",
|
|
71
|
+
"winston": "3.13.1",
|
|
72
|
+
"zod": "^3.23.8"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@babel/cli": "^7.24.8",
|
|
76
|
+
"@babel/core": "^7.24.9",
|
|
77
|
+
"@babel/preset-env": "^7.24.8",
|
|
78
|
+
"@babel/preset-react": "^7.24.7",
|
|
79
|
+
"@types/node": "^20.14.11",
|
|
80
|
+
"eslint": "^8.2.0",
|
|
81
|
+
"eslint-config-airbnb-base": "^15.0.0",
|
|
82
|
+
"eslint-plugin-import": "^2.25.2",
|
|
83
|
+
"husky": "9.1.1",
|
|
84
|
+
"lint-staged": "15.2.7",
|
|
85
|
+
"tsx": "^4.16.2",
|
|
86
|
+
"typescript": "^5.5.4"
|
|
87
|
+
},
|
|
88
|
+
"lint-staged": {
|
|
89
|
+
"*.js": [
|
|
90
|
+
"prettier --write",
|
|
91
|
+
"git add"
|
|
92
|
+
],
|
|
93
|
+
".js": [
|
|
94
|
+
"npm run lint:fix",
|
|
95
|
+
"npm run lint"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
|
|
3
|
+
export default (envs = []) => {
|
|
4
|
+
let missingEnvs = [];
|
|
5
|
+
|
|
6
|
+
_.each(envs, envName => {
|
|
7
|
+
if (!process.env[envName]) {
|
|
8
|
+
missingEnvs.push(envName);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!_.isEmpty(missingEnvs)) {
|
|
13
|
+
throw Error(`Missing env variables: ${missingEnvs.join(', ')}`)
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
import appConfig from "./app.js";
|
|
5
|
+
|
|
6
|
+
dotenv.config({ path: `${__dirname}/.env` });
|
|
7
|
+
dotenv.config({ path: `${__dirname}/.env.app` });
|
|
8
|
+
|
|
9
|
+
console.log('process.env.HIVE_SRC', process.env.HIVE_SRC)
|
|
10
|
+
|
|
11
|
+
if (process.env.HIVE_SRC) {
|
|
12
|
+
dotenv.config({
|
|
13
|
+
path: `${process.env.HIVE_SRC}/app-config/.env`,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
dotenv.config({
|
|
17
|
+
path: `${process.env.HIVE_SRC}/app-config/.env.app`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const env = process.env.APP_ENV || "development";
|
|
22
|
+
|
|
23
|
+
let config = {
|
|
24
|
+
async init() {
|
|
25
|
+
if (process.env.HIVE_SRC) {
|
|
26
|
+
let pluginConfigPath = `${process.env.HIVE_SRC}/app-config/app.js`;
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(pluginConfigPath)) {
|
|
29
|
+
const { default: pluginConfig } = await (import(pluginConfigPath))
|
|
30
|
+
_.extend(config, pluginConfig);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
_hive: {},
|
|
36
|
+
env,
|
|
37
|
+
port: process.env.PORT || 3001,
|
|
38
|
+
domain:
|
|
39
|
+
env === "production" ? "https://api.yourapp.com" : process.env.DOMAIN || "",
|
|
40
|
+
isDev: env === "development",
|
|
41
|
+
mongoUri: process.env.MONGODB_URI,
|
|
42
|
+
redis: {
|
|
43
|
+
url: process.env.REDIS_URI,
|
|
44
|
+
},
|
|
45
|
+
smtp: {
|
|
46
|
+
host: process.env.SMTP_SERVER,
|
|
47
|
+
port: process.env.SMTP_PORT,
|
|
48
|
+
secure: true,
|
|
49
|
+
auth: {
|
|
50
|
+
user: process.env.SMTP_USER,
|
|
51
|
+
pass: process.env.SMTP_KEY,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
...appConfig,
|
|
55
|
+
assert(configKey) {
|
|
56
|
+
if (!config[configKey]) {
|
|
57
|
+
throw Error(`Config [${configKey}] is missing`);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default config;
|
package/.hive/src/app.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
import appModulePath from "app-module-path";
|
|
3
|
+
appModulePath.addPath(__dirname);
|
|
4
|
+
|
|
5
|
+
if (process.env.HIVE_SRC) {
|
|
6
|
+
appModulePath.addPath(process.env.HIVE_SRC);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
import Koa from "koa";
|
|
10
|
+
import http from "http";
|
|
11
|
+
import config from "app-config";
|
|
12
|
+
import logger from "logger";
|
|
13
|
+
import cors from "@koa/cors";
|
|
14
|
+
import helmet from "koa-helmet";
|
|
15
|
+
import qs from "koa-qs";
|
|
16
|
+
import bodyParser from "koa-bodyparser";
|
|
17
|
+
|
|
18
|
+
import requestLogger from "koa-logger";
|
|
19
|
+
import db from "db";
|
|
20
|
+
import socketIo from "socketIo";
|
|
21
|
+
import mount from "koa-mount";
|
|
22
|
+
import get from "resources/health/endpoints/get";
|
|
23
|
+
|
|
24
|
+
const main = async () => {
|
|
25
|
+
await config.init();
|
|
26
|
+
await db.init();
|
|
27
|
+
|
|
28
|
+
const routes = (await import("routes")).default;
|
|
29
|
+
const scheduler = (await import("./scheduler")).default;
|
|
30
|
+
|
|
31
|
+
process.on("unhandledRejection", (reason, p) => {
|
|
32
|
+
console.trace(reason.stack);
|
|
33
|
+
logger.error(reason.stack);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const app = new Koa();
|
|
37
|
+
|
|
38
|
+
app.use(cors({ credentials: true }));
|
|
39
|
+
app.use(helmet());
|
|
40
|
+
|
|
41
|
+
qs(app);
|
|
42
|
+
|
|
43
|
+
app.use(bodyParser({
|
|
44
|
+
enableTypes: ["json", "form", "text"],
|
|
45
|
+
|
|
46
|
+
formLimit: config._hive.bodyParser?.formLimit || "10mb",
|
|
47
|
+
textLimit: config._hive.bodyParser?.textLimit || "10mb",
|
|
48
|
+
jsonLimit: config._hive.bodyParser?.jsonLimit || "10mb",
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
app.use(mount("/health", get.handler));
|
|
52
|
+
app.use(requestLogger());
|
|
53
|
+
|
|
54
|
+
await routes(app);
|
|
55
|
+
|
|
56
|
+
const server = http.createServer(app.callback());
|
|
57
|
+
|
|
58
|
+
server.listen(config.port, () => {
|
|
59
|
+
console.log(`Api server listening on ${config.port}, in ${process.env.NODE_ENV} mode and ${process.env.APP_ENV} environment`);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
scheduler();
|
|
63
|
+
|
|
64
|
+
await socketIo(server);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
main();
|
|
68
|
+
|
|
69
|
+
export default main;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<mj-section background-color="#ffffff" padding-bottom="0px" padding-top="0">
|
|
2
|
+
<mj-column vertical-align="top" width="100%">
|
|
3
|
+
<mj-text
|
|
4
|
+
align="center"
|
|
5
|
+
font-size="30px"
|
|
6
|
+
font-weight="bold"
|
|
7
|
+
font-family="Trebuchet MS, Arial, sans-serif"
|
|
8
|
+
color="#55299a"
|
|
9
|
+
>
|
|
10
|
+
Ship
|
|
11
|
+
</mj-text>
|
|
12
|
+
</mj-column>
|
|
13
|
+
</mj-section>
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<mjml>
|
|
2
|
+
<mj-head>
|
|
3
|
+
<mj-include path="./styles/index.mjml" />
|
|
4
|
+
</mj-head>
|
|
5
|
+
|
|
6
|
+
<mj-body>
|
|
7
|
+
<mj-include path="./components/header.mjml" />
|
|
8
|
+
|
|
9
|
+
<mj-section mj-class="main-section">
|
|
10
|
+
<mj-column vertical-align="middle" width="100%">
|
|
11
|
+
<mj-text mj-class="big-text"> Hi, there. </mj-text>
|
|
12
|
+
|
|
13
|
+
<mj-text mj-class="text">
|
|
14
|
+
Thanks for checking out Ship, we hope our products can make your
|
|
15
|
+
routine life a little more enjoyable.
|
|
16
|
+
</mj-text>
|
|
17
|
+
|
|
18
|
+
<mj-button mj-class="btn" href="{{verifyEmailUrl}}">
|
|
19
|
+
Verify Your Email
|
|
20
|
+
</mj-button>
|
|
21
|
+
</mj-column>
|
|
22
|
+
</mj-section>
|
|
23
|
+
|
|
24
|
+
<mj-section mj-class="bottom-section">
|
|
25
|
+
<mj-column vertical-align="middle" width="70%">
|
|
26
|
+
<mj-text mj-class="small-text"> INTERESTING FACT </mj-text>
|
|
27
|
+
|
|
28
|
+
<mj-text mj-class="tiny-text">
|
|
29
|
+
It took more than three years to build the Titanic.
|
|
30
|
+
</mj-text>
|
|
31
|
+
</mj-column>
|
|
32
|
+
</mj-section>
|
|
33
|
+
</mj-body>
|
|
34
|
+
</mjml>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<mj-attributes>
|
|
2
|
+
<mj-class
|
|
3
|
+
name="big-text"
|
|
4
|
+
align="center"
|
|
5
|
+
font-weight="bold"
|
|
6
|
+
font-family="Trebuchet MS, Arial, sans-serif"
|
|
7
|
+
color="#3b1d6c"
|
|
8
|
+
font-size="30px"
|
|
9
|
+
padding-left="25px"
|
|
10
|
+
padding-right="25px"
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<mj-class
|
|
14
|
+
name="text"
|
|
15
|
+
align="center"
|
|
16
|
+
color="#000"
|
|
17
|
+
font-size="18px"
|
|
18
|
+
font-family="Century Gothic, Arial, sans-serif"
|
|
19
|
+
padding-left="25px"
|
|
20
|
+
padding-right="25px"
|
|
21
|
+
line-height="26px"
|
|
22
|
+
/>
|
|
23
|
+
|
|
24
|
+
<mj-class
|
|
25
|
+
name="small-text"
|
|
26
|
+
align="center"
|
|
27
|
+
font-weight="bold"
|
|
28
|
+
font-family="Trebuchet MS, Arial, sans-serif"
|
|
29
|
+
color="#3b1d6c"
|
|
30
|
+
font-size="15px"
|
|
31
|
+
padding-left="25px"
|
|
32
|
+
padding-right="25px"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
<mj-class
|
|
36
|
+
name="tiny-text"
|
|
37
|
+
align="center"
|
|
38
|
+
color="#000"
|
|
39
|
+
font-size="15px"
|
|
40
|
+
font-family="Verdana, Arial, sans-serif"
|
|
41
|
+
line-height="20px"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<mj-class
|
|
45
|
+
name="btn"
|
|
46
|
+
align="center"
|
|
47
|
+
font-size="22px"
|
|
48
|
+
background-color="#4c258b"
|
|
49
|
+
border-radius="4px"
|
|
50
|
+
color="#fff"
|
|
51
|
+
font-family="open Sans Helvetica, Arial, sans-serif"
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<mj-class
|
|
55
|
+
name="main-section"
|
|
56
|
+
background-color="#cccccc"
|
|
57
|
+
vertical-align="top"
|
|
58
|
+
padding-bottom="20px"
|
|
59
|
+
padding-top="0"
|
|
60
|
+
/>
|
|
61
|
+
|
|
62
|
+
<mj-class
|
|
63
|
+
name="main-section-image"
|
|
64
|
+
background-color="#cccccc"
|
|
65
|
+
vertical-align="top"
|
|
66
|
+
padding-bottom="0"
|
|
67
|
+
padding-top="0"
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<mj-class
|
|
71
|
+
name="bottom-section"
|
|
72
|
+
background-color="#ffffff"
|
|
73
|
+
vertical-align="top"
|
|
74
|
+
padding-bottom="20px"
|
|
75
|
+
padding-top="20px"
|
|
76
|
+
/>
|
|
77
|
+
</mj-attributes>
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import db from "db";
|
|
3
|
+
import ifUpdated from "helpers/db/ifUpdated";
|
|
4
|
+
import schemaMappings from "./schemaMappings";
|
|
5
|
+
import getDependentFields from './getDependentFields';
|
|
6
|
+
import isZodArray from "helpers/isZodArray";
|
|
7
|
+
|
|
8
|
+
const updatedSchemaMappings = (() => {
|
|
9
|
+
const result = {};
|
|
10
|
+
const schemaNames = Object.keys(schemaMappings);
|
|
11
|
+
schemaNames.forEach((schemaName) => {
|
|
12
|
+
const dependentFieldNames = Object.keys(schemaMappings[schemaName]);
|
|
13
|
+
const schema = db.schemas[schemaName];
|
|
14
|
+
dependentFieldNames.forEach((dependentFieldName) => {
|
|
15
|
+
const dependentFields = getDependentFields(schema, dependentFieldName);
|
|
16
|
+
if (!_.isEmpty(dependentFields)) {
|
|
17
|
+
result[schemaName] = result[schemaName] || {};
|
|
18
|
+
result[schemaName][dependentFieldName] = {
|
|
19
|
+
schema: schemaMappings[schemaName][dependentFieldName].schema,
|
|
20
|
+
dependentFields,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
});
|
|
26
|
+
return result;
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
const getToUpdate = async ({ doc, schemaName }) => {
|
|
30
|
+
const dependentFieldNames = Object.keys(updatedSchemaMappings[schemaName]);
|
|
31
|
+
const toUpdate = {};
|
|
32
|
+
await Promise.all(
|
|
33
|
+
dependentFieldNames.map(async (dependentFieldName) => {
|
|
34
|
+
const { schema: dependentSchemaName } =
|
|
35
|
+
updatedSchemaMappings[schemaName][dependentFieldName];
|
|
36
|
+
const { dependentFields } =
|
|
37
|
+
updatedSchemaMappings[schemaName][dependentFieldName];
|
|
38
|
+
if (!_.isEmpty(doc[dependentFieldName])) {
|
|
39
|
+
if (_.isArray(doc[dependentFieldName])) {
|
|
40
|
+
toUpdate[dependentFieldName] = (
|
|
41
|
+
await db.services[dependentSchemaName].find(
|
|
42
|
+
{
|
|
43
|
+
_id: { $in: _.uniq(_.map(doc[dependentFieldName], "_id")) },
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
fields: dependentFields,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
).results;
|
|
50
|
+
} else {
|
|
51
|
+
toUpdate[dependentFieldName] = await db.services[
|
|
52
|
+
dependentSchemaName
|
|
53
|
+
].findOne(
|
|
54
|
+
{
|
|
55
|
+
_id: doc[dependentFieldName]._id,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
fields: dependentFields,
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
return toUpdate;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const populateOnCreate = ({ schemaName }) => {
|
|
69
|
+
db.services[schemaName]._options.onBeforeCreated = async ({ docs }) => {
|
|
70
|
+
const res = await Promise.all(
|
|
71
|
+
docs.map(async (doc) => {
|
|
72
|
+
const toUpdate = await getToUpdate({ schemaName, doc });
|
|
73
|
+
return {
|
|
74
|
+
...doc,
|
|
75
|
+
...toUpdate,
|
|
76
|
+
};
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
return res;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// const addOnEntityCreatedHandler = ({ schemaName }) => {
|
|
84
|
+
// db.services[schemaName].on('created', async ({ doc }) => {
|
|
85
|
+
// const toUpdate = await getToUpdate({ schemaName, doc });
|
|
86
|
+
// if (!_.isEmpty(toUpdate)) {
|
|
87
|
+
// await db.services[schemaName].atomic.update(
|
|
88
|
+
// { _id: doc._id },
|
|
89
|
+
// { $set: toUpdate }
|
|
90
|
+
// );
|
|
91
|
+
// }
|
|
92
|
+
// });
|
|
93
|
+
// };
|
|
94
|
+
|
|
95
|
+
const addOnDependentEntitiesUpdatedHandlers = ({ schemaName }) => {
|
|
96
|
+
const dependentFieldNames = Object.keys(schemaMappings[schemaName]);
|
|
97
|
+
dependentFieldNames.forEach((dependentFieldName) => {
|
|
98
|
+
const dependentFieldSchemaName =
|
|
99
|
+
schemaMappings[schemaName][dependentFieldName].schema;
|
|
100
|
+
const schema = db.schemas[schemaName];
|
|
101
|
+
const dependentFields = getDependentFields(schema, dependentFieldName);
|
|
102
|
+
db.services[dependentFieldSchemaName].on(
|
|
103
|
+
"updated",
|
|
104
|
+
ifUpdated(dependentFields, async ({ doc }) => {
|
|
105
|
+
const toUpdate = _.pick(doc, ["_id", ...dependentFields]);
|
|
106
|
+
if (isZodArray(schema.shape[dependentFieldName])) {
|
|
107
|
+
db.services[schemaName].atomic.update(
|
|
108
|
+
{ [`${dependentFieldName}._id`]: doc._id },
|
|
109
|
+
{
|
|
110
|
+
$set: {
|
|
111
|
+
[`${dependentFieldName}.$`]: toUpdate,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
multi: true,
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
db.services[schemaName].atomic.update(
|
|
120
|
+
{ [`${dependentFieldName}._id`]: doc._id },
|
|
121
|
+
{
|
|
122
|
+
$set: {
|
|
123
|
+
[`${dependentFieldName}`]: toUpdate,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
multi: true,
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
export default async () => {
|
|
136
|
+
const schemaNames = Object.keys(updatedSchemaMappings);
|
|
137
|
+
schemaNames.forEach((schemaName) => {
|
|
138
|
+
// addOnEntityCreatedHandler({ schemaName });
|
|
139
|
+
populateOnCreate({ schemaName });
|
|
140
|
+
addOnDependentEntitiesUpdatedHandlers({ schemaName });
|
|
141
|
+
});
|
|
142
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import isZodArray from "helpers/isZodArray";
|
|
4
|
+
|
|
5
|
+
const getZodKeys = schema => {
|
|
6
|
+
// make sure schema is not null or undefined
|
|
7
|
+
if (schema === null || schema === undefined) return [];
|
|
8
|
+
// check if schema is nullable or optional
|
|
9
|
+
if (schema instanceof z.ZodNullable || schema instanceof z.ZodOptional) return getZodKeys(schema.unwrap());
|
|
10
|
+
// check if schema is an array
|
|
11
|
+
if (isZodArray(schema)) return getZodKeys(schema.element);
|
|
12
|
+
// check if schema is an object
|
|
13
|
+
if (schema instanceof z.ZodObject) {
|
|
14
|
+
// get key/value pairs from schema
|
|
15
|
+
const entries = Object.entries(schema.shape);
|
|
16
|
+
// loop through key/value pairs
|
|
17
|
+
return entries.flatMap(([key, value]) => {
|
|
18
|
+
// get nested keys
|
|
19
|
+
const nested = value instanceof z.ZodType ? getZodKeys(value).map(subKey => `${key}.${subKey}`) : [];
|
|
20
|
+
// return nested keys
|
|
21
|
+
return nested.length ? nested : key;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// return empty array
|
|
25
|
+
return [];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default (schema, dependentFieldName) => {
|
|
29
|
+
let targetSchema = schema.shape[dependentFieldName];
|
|
30
|
+
let zodKeys = getZodKeys(targetSchema);
|
|
31
|
+
|
|
32
|
+
zodKeys = _.uniq(zodKeys.map(key => key.split('.')[0]));
|
|
33
|
+
|
|
34
|
+
return zodKeys.filter(
|
|
35
|
+
(key) => !_.includes(['_id', 'createdOn', 'updatedOn'], key)
|
|
36
|
+
);
|
|
37
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import db from "db";
|
|
3
|
+
|
|
4
|
+
import schemaMappings from "./schemaMappings";
|
|
5
|
+
import getDependentFields from './getDependentFields';
|
|
6
|
+
import isZodArray from "helpers/isZodArray";
|
|
7
|
+
|
|
8
|
+
const schemaMappingService = db.services.schemaMappings;
|
|
9
|
+
|
|
10
|
+
const zodSchemaToSchemaMappings = () => {
|
|
11
|
+
const newSchemaMappings = {};
|
|
12
|
+
|
|
13
|
+
Object.keys(schemaMappings).forEach((schemaName) => {
|
|
14
|
+
const schema = db.schemas[schemaName];
|
|
15
|
+
|
|
16
|
+
newSchemaMappings[schemaName] = {};
|
|
17
|
+
|
|
18
|
+
Object.keys(schemaMappings[schemaName]).forEach((fieldName) => {
|
|
19
|
+
newSchemaMappings[schemaName][fieldName] = {
|
|
20
|
+
schema: schemaMappings[schemaName][fieldName].schema,
|
|
21
|
+
fields: getDependentFields(schema, fieldName),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
console.log('newSchemaMappings', newSchemaMappings)
|
|
26
|
+
return newSchemaMappings;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export default async () => {
|
|
31
|
+
const prevSchema = await schemaMappingService.findOne({});
|
|
32
|
+
const prevSchemaMappings = prevSchema?.mappings;
|
|
33
|
+
if (!prevSchemaMappings) {
|
|
34
|
+
const initialSchemaMappings = zodSchemaToSchemaMappings();
|
|
35
|
+
schemaMappingService.create({ mappings: initialSchemaMappings });
|
|
36
|
+
} else {
|
|
37
|
+
const schemaNames = Object.keys(schemaMappings);
|
|
38
|
+
await Promise.all(
|
|
39
|
+
schemaNames.map(async (schemaName) => {
|
|
40
|
+
const schema = db.schemas[schemaName];
|
|
41
|
+
|
|
42
|
+
const fieldNames = Object.keys(schemaMappings[schemaName]);
|
|
43
|
+
await Promise.all(
|
|
44
|
+
fieldNames.map(async (fieldName) => {
|
|
45
|
+
const dependentFields = getDependentFields(schema, fieldName);
|
|
46
|
+
const prevSchema = (prevSchemaMappings[schemaName] &&
|
|
47
|
+
prevSchemaMappings[schemaName][fieldName]) || { fields: [] };
|
|
48
|
+
const { fields: prevDependentFields } = prevSchema;
|
|
49
|
+
if (
|
|
50
|
+
_.difference(dependentFields, prevDependentFields).length !== 0
|
|
51
|
+
) {
|
|
52
|
+
console.log(`Mapping schema changes: ${schemaName}.${fieldName}`);
|
|
53
|
+
const uniqueDependentEntityIds = await db.services[
|
|
54
|
+
schemaName
|
|
55
|
+
].distinct(`${fieldName}._id`);
|
|
56
|
+
const { results: uniqueDependentEntities } = await db.services[
|
|
57
|
+
schemaMappings[schemaName][fieldName].schema
|
|
58
|
+
].find(
|
|
59
|
+
{
|
|
60
|
+
_id: { $in: uniqueDependentEntityIds },
|
|
61
|
+
},
|
|
62
|
+
{ fields: ["_id", ...dependentFields] }
|
|
63
|
+
);
|
|
64
|
+
await Promise.all(
|
|
65
|
+
uniqueDependentEntities.map(async (entity) => {
|
|
66
|
+
if (isZodArray(schema.shape[fieldName])) {
|
|
67
|
+
await db.services[schemaName].atomic.update(
|
|
68
|
+
{ [`${fieldName}._id`]: entity._id },
|
|
69
|
+
{
|
|
70
|
+
$set: { [`${fieldName}.$`]: entity },
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
multi: true,
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
await db.services[schemaName].atomic.update(
|
|
78
|
+
{ [`${fieldName}._id`]: entity._id },
|
|
79
|
+
{
|
|
80
|
+
$set: { [`${fieldName}`]: entity },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
multi: true,
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
);
|
|
92
|
+
})
|
|
93
|
+
);
|
|
94
|
+
schemaMappingService.atomic.update(
|
|
95
|
+
{ _id: prevSchema._id },
|
|
96
|
+
{ $set: { mappings: zodSchemaToSchemaMappings() } }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
let schemaMappings = {};
|
|
4
|
+
|
|
5
|
+
if (fs.existsSync('./schemaMappings.json')) {
|
|
6
|
+
schemaMappings = JSON.parse(fs.readFileSync('./schemaMappings.json', 'utf8') || '{}');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (process.env.HIVE_SRC && fs.existsSync(`${process.env.HIVE_SRC}/autoMap/schemaMappings.json`)) {
|
|
10
|
+
schemaMappings = JSON.parse(fs.readFileSync(`${process.env.HIVE_SRC}/autoMap/schemaMappings.json`, 'utf8') || '{}');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default schemaMappings;
|