@paralect/hive 0.1.49 → 0.1.50-alpha.2

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.
Files changed (189) hide show
  1. package/.hive/.babelrc +3 -0
  2. package/.hive/.cursor/commands/add-endpoint.md +262 -0
  3. package/.hive/.cursor/commands/add-handler.md +137 -0
  4. package/.hive/.cursor/commands/add-middleware.md +95 -0
  5. package/.hive/.cursor/commands/add-resource.md +71 -0
  6. package/.hive/.cursor/commands/add-scheduler.md +138 -0
  7. package/.hive/.cursor/commands/add-service.md +188 -0
  8. package/.hive/.cursor/skills/hive-auth/SKILL.md +134 -0
  9. package/.hive/.cursor/skills/hive-database/SKILL.md +103 -0
  10. package/.hive/.cursor/skills/hive-endpoint/SKILL.md +103 -0
  11. package/.hive/.cursor/skills/hive-handler/SKILL.md +88 -0
  12. package/.hive/.cursor/skills/hive-mapping/SKILL.md +85 -0
  13. package/.hive/.cursor/skills/hive-middleware/SKILL.md +104 -0
  14. package/.hive/.cursor/skills/hive-overview/SKILL.md +50 -0
  15. package/.hive/.cursor/skills/hive-scheduler/SKILL.md +94 -0
  16. package/.hive/.cursor/skills/hive-schema/SKILL.md +73 -0
  17. package/.hive/.cursor/skills/hive-service/SKILL.md +90 -0
  18. package/.hive/.dockerignore +1 -0
  19. package/.hive/Dockerfile +22 -0
  20. package/.hive/Dockerfile.dev +33 -0
  21. package/.hive/Dockerfile.prod +29 -0
  22. package/.hive/README.md +11 -0
  23. package/.hive/bin/deploy.sh +5 -0
  24. package/.hive/bin/start.sh +2 -0
  25. package/.hive/bootstrap-hive.js +118 -0
  26. package/.hive/deploy/api/Chart.yaml +6 -0
  27. package/.hive/deploy/api/staging.yaml +3 -0
  28. package/.hive/deploy/api/templates/deployment.yaml +44 -0
  29. package/.hive/deploy/api/templates/ingress.yaml +26 -0
  30. package/.hive/deploy/api/templates/service.yaml +14 -0
  31. package/.hive/deploy/script/Dockerfile +39 -0
  32. package/.hive/deploy/script/package-lock.json +1499 -0
  33. package/.hive/deploy/script/package.json +12 -0
  34. package/.hive/deploy/script/src/config.js +48 -0
  35. package/.hive/deploy/script/src/index.js +108 -0
  36. package/.hive/deploy/script/src/util.js +19 -0
  37. package/.hive/initial-data.json +176 -0
  38. package/.hive/package-lock.json +10242 -0
  39. package/.hive/package.json +98 -0
  40. package/.hive/ship_logo.png +0 -0
  41. package/.hive/src/app-config/app.js +3 -0
  42. package/.hive/src/app-config/assertEnv.js +15 -0
  43. package/.hive/src/app-config/index.js +62 -0
  44. package/.hive/src/app.js +69 -0
  45. package/.hive/src/assets/emails/components/header.mjml +13 -0
  46. package/.hive/src/assets/emails/dist/.gitkeep +0 -0
  47. package/.hive/src/assets/emails/signup-welcome.mjml +34 -0
  48. package/.hive/src/assets/emails/styles/index.mjml +77 -0
  49. package/.hive/src/autoMap/addHandlers.js +142 -0
  50. package/.hive/src/autoMap/getDependentFields.js +37 -0
  51. package/.hive/src/autoMap/mapSchema.js +99 -0
  52. package/.hive/src/autoMap/schemaMappings.js +13 -0
  53. package/.hive/src/autoMap/schemaMappings.json +3 -0
  54. package/.hive/src/bullMqBus.js +21 -0
  55. package/.hive/src/bullMqWrapper.js +23 -0
  56. package/.hive/src/db.js +52 -0
  57. package/.hive/src/emails/MyEmailComponent.jsx +14 -0
  58. package/.hive/src/emails/compiled/MyEmailComponent.js +18 -0
  59. package/.hive/src/emails/compiled/compiled/MyEmailComponent.js +18 -0
  60. package/.hive/src/helpers/db/ifUpdated.js +22 -0
  61. package/.hive/src/helpers/getMiddlewares.js +38 -0
  62. package/.hive/src/helpers/getResourceEndpoints.js +28 -0
  63. package/.hive/src/helpers/getResources.js +32 -0
  64. package/.hive/src/helpers/getSchemas.js +50 -0
  65. package/.hive/src/helpers/importHandlers.js +29 -0
  66. package/.hive/src/helpers/isZodArray.js +13 -0
  67. package/.hive/src/helpers/prettierFormat.js +8 -0
  68. package/.hive/src/helpers/schema/db.schema.js +9 -0
  69. package/.hive/src/helpers/schema/pagination.schema.js +14 -0
  70. package/.hive/src/ioEmitter.js +9 -0
  71. package/.hive/src/jsconfig.json +5 -0
  72. package/.hive/src/lib/node-mongo/.github/workflows/npm-publish.yml +32 -0
  73. package/.hive/src/lib/node-mongo/API.md +654 -0
  74. package/.hive/src/lib/node-mongo/CHANGELOG.md +98 -0
  75. package/.hive/src/lib/node-mongo/README.md +97 -0
  76. package/.hive/src/lib/node-mongo/package-lock.json +3682 -0
  77. package/.hive/src/lib/node-mongo/package.json +74 -0
  78. package/.hive/src/lib/node-mongo/src/index.js +64 -0
  79. package/.hive/src/lib/node-mongo/src/mongo-query-service.js +78 -0
  80. package/.hive/src/lib/node-mongo/src/mongo-service-error.js +15 -0
  81. package/.hive/src/lib/node-mongo/src/mongo-service.js +303 -0
  82. package/.hive/src/logger.js +43 -0
  83. package/.hive/src/middlewares/allowNoAuth.js +9 -0
  84. package/.hive/src/middlewares/attachUser.js +41 -0
  85. package/.hive/src/middlewares/global/extractUserTokens.js +15 -0
  86. package/.hive/src/middlewares/global/tryToAttachUser.js +33 -0
  87. package/.hive/src/middlewares/isAuthorized.js +18 -0
  88. package/.hive/src/middlewares/shouldExist.js +37 -0
  89. package/.hive/src/middlewares/shouldNotExist.js +19 -0
  90. package/.hive/src/middlewares/uploadFile.js +5 -0
  91. package/.hive/src/middlewares/validate.js +32 -0
  92. package/.hive/src/migrations/migration.js +8 -0
  93. package/.hive/src/migrations/migration.service.js +73 -0
  94. package/.hive/src/migrations/migrations/1.js +22 -0
  95. package/.hive/src/migrations/migrations-log/migration-log.schema.js +13 -0
  96. package/.hive/src/migrations/migrations-log/migration-log.service.js +50 -0
  97. package/.hive/src/migrations/migrations.schema.js +6 -0
  98. package/.hive/src/migrations/migrator.js +75 -0
  99. package/.hive/src/migrator.js +4 -0
  100. package/.hive/src/resources/_dev/endpoints/triggerSchedulerHandler.js +32 -0
  101. package/.hive/src/resources/health/endpoints/get.js +19 -0
  102. package/.hive/src/resources/schemaMappings/schemaMappings.schema.js +6 -0
  103. package/.hive/src/resources/tokens/methods/generateSecureToken.js +9 -0
  104. package/.hive/src/resources/tokens/methods/setToken.js +8 -0
  105. package/.hive/src/resources/tokens/methods/storeToken.js +35 -0
  106. package/.hive/src/resources/tokens/tokens.schema.js +11 -0
  107. package/.hive/src/resources/users/endpoints/getCurrentUser.js +14 -0
  108. package/.hive/src/resources/users/endpoints/getUserProfile.js +19 -0
  109. package/.hive/src/resources/users/handlers/test.js +1 -0
  110. package/.hive/src/resources/users/methods/ensureUserCreated.js +68 -0
  111. package/.hive/src/resources/users/users.schema.js +16 -0
  112. package/.hive/src/routes/index.js +172 -0
  113. package/.hive/src/routes/middlewares/attachCustomErrors.js +28 -0
  114. package/.hive/src/routes/middlewares/routeErrorHandler.js +27 -0
  115. package/.hive/src/scheduler/handlers/sendDailyReport.example.js +7 -0
  116. package/.hive/src/scheduler.js +32 -0
  117. package/.hive/src/security.util.js +38 -0
  118. package/.hive/src/services/emailService.js +15 -0
  119. package/.hive/src/services/globalTest.js +0 -0
  120. package/.hive/src/services/setCookie.js +21 -0
  121. package/.hive/src/socketIo.js +99 -0
  122. package/.hive/tsconfig.json +31 -0
  123. package/cli/helpers/docker.js +59 -0
  124. package/cli/helpers/envCheck.js +127 -0
  125. package/cli/helpers/findPort.js +32 -0
  126. package/cli/hive.js +91 -17
  127. package/package.json +1 -1
  128. package/starter/loader.mjs +40 -0
  129. package/starter/package-lock.json +3512 -262
  130. package/starter/package.json +7 -2
  131. package/starter/register.mjs +6 -0
  132. package/starter/src/app-config/index.js +3 -0
  133. package/starter/src/app.js +10 -14
  134. package/starter/src/autoMap/addHandlers.js +3 -3
  135. package/starter/src/autoMap/getDependentFields.js +1 -1
  136. package/starter/src/autoMap/mapSchema.js +2 -2
  137. package/starter/src/bullMqBus.js +1 -1
  138. package/starter/src/bullMqWrapper.js +1 -1
  139. package/starter/src/db.js +12 -11
  140. package/starter/src/helpers/esm.js +56 -0
  141. package/starter/src/helpers/getMiddlewares.js +3 -0
  142. package/starter/src/helpers/getResourceEndpoints.js +3 -0
  143. package/starter/src/helpers/getResources.js +3 -0
  144. package/starter/src/helpers/getSchemas.js +3 -0
  145. package/starter/src/helpers/importHandlers.js +11 -20
  146. package/starter/src/ioEmitter.js +1 -1
  147. package/starter/src/logger.js +1 -1
  148. package/starter/src/middlewares/attachUser.js +2 -2
  149. package/starter/src/middlewares/global/tryToAttachUser.js +1 -1
  150. package/starter/src/middlewares/shouldExist.js +1 -1
  151. package/starter/src/middlewares/shouldNotExist.js +1 -1
  152. package/starter/src/migrations/migration.service.js +4 -1
  153. package/starter/src/migrations/migrations-log/migration-log.schema.js +1 -1
  154. package/starter/src/migrations/migrations-log/migration-log.service.js +1 -1
  155. package/starter/src/migrations/migrations.schema.js +1 -1
  156. package/starter/src/migrations/migrator.js +1 -1
  157. package/starter/src/migrator.js +2 -3
  158. package/starter/src/resources/schemaMappings/schemaMappings.schema.js +1 -1
  159. package/starter/src/resources/tokens/methods/setToken.js +1 -1
  160. package/starter/src/resources/tokens/methods/storeToken.js +2 -2
  161. package/starter/src/resources/tokens/tokens.schema.js +1 -1
  162. package/starter/src/resources/users/endpoints/getCurrentUser.js +1 -1
  163. package/starter/src/resources/users/endpoints/getUserProfile.js +1 -1
  164. package/starter/src/resources/users/methods/ensureUserCreated.js +1 -1
  165. package/starter/src/resources/users/users.schema.js +1 -1
  166. package/starter/src/routes/index.js +8 -8
  167. package/starter/src/routes/middlewares/routeErrorHandler.js +1 -1
  168. package/starter/src/scheduler.js +10 -7
  169. package/starter/src/services/emailService.js +1 -1
  170. package/starter/src/services/setCookie.js +2 -2
  171. package/starter/src/socketIo.js +3 -3
  172. package/test-app/.cursor/commands/add-endpoint.md +262 -0
  173. package/test-app/.cursor/commands/add-handler.md +137 -0
  174. package/test-app/.cursor/commands/add-middleware.md +95 -0
  175. package/test-app/.cursor/commands/add-resource.md +71 -0
  176. package/test-app/.cursor/commands/add-scheduler.md +138 -0
  177. package/test-app/.cursor/commands/add-service.md +188 -0
  178. package/test-app/.cursor/skills/hive-auth/SKILL.md +134 -0
  179. package/test-app/.cursor/skills/hive-database/SKILL.md +103 -0
  180. package/test-app/.cursor/skills/hive-endpoint/SKILL.md +103 -0
  181. package/test-app/.cursor/skills/hive-handler/SKILL.md +88 -0
  182. package/test-app/.cursor/skills/hive-mapping/SKILL.md +85 -0
  183. package/test-app/.cursor/skills/hive-middleware/SKILL.md +104 -0
  184. package/test-app/.cursor/skills/hive-overview/SKILL.md +50 -0
  185. package/test-app/.cursor/skills/hive-scheduler/SKILL.md +94 -0
  186. package/test-app/.cursor/skills/hive-schema/SKILL.md +73 -0
  187. package/test-app/.cursor/skills/hive-service/SKILL.md +90 -0
  188. package/test-app/package-lock.json +462 -0
  189. package/test-app/package.json +21 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "deploy",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "author": "",
7
+ "license": "ISC",
8
+ "dependencies": {
9
+ "execa": "4.0.0",
10
+ "prompt-list": "3.2.0"
11
+ }
12
+ }
@@ -0,0 +1,48 @@
1
+ const path = require("path");
2
+ const rootDir = path.resolve(__dirname, "./../../../");
3
+
4
+ const ENV = process.env;
5
+
6
+ const config = {
7
+ rootDir,
8
+
9
+ environment: ENV.ENVIRONMENT || "staging",
10
+
11
+ namespace: ENV.NAMESPACE || "staging",
12
+
13
+ kubeConfig: ENV.KUBE_CONFIG,
14
+
15
+ home: ENV.HOME,
16
+
17
+ dockerRegistry: {
18
+ name: "paralect/hive-api",
19
+ username: ENV.DOCKER_AUTH_USERNAME,
20
+ password: ENV.DOCKER_AUTH_PASSWORD,
21
+
22
+ imageTag: ENV.IMAGE_TAG,
23
+ },
24
+ };
25
+
26
+ const deployConfig = {
27
+ dockerRepo: `${config.dockerRegistry.name}`,
28
+ dir: `${rootDir}`,
29
+ folder: "",
30
+ dockerFilePath: `${rootDir}/Dockerfile`,
31
+ dockerContextDir: rootDir
32
+ };
33
+
34
+ // Object.keys(deployConfig).forEach((serviceName) => {
35
+ // if (!deployConfig[serviceName].dockerFilePath) {
36
+ // deployConfig[
37
+ // serviceName
38
+ // ].dockerFilePath = `${deployConfig[serviceName].dir}/Dockerfile`;
39
+ // }
40
+
41
+ // if (!deployConfig[serviceName].dockerContextDir) {
42
+ // deployConfig[serviceName].dockerContextDir = deployConfig[serviceName].dir;
43
+ // }
44
+ // });
45
+
46
+ config.deploy = deployConfig;
47
+
48
+ module.exports = config;
@@ -0,0 +1,108 @@
1
+ const fs = require("fs");
2
+ const List = require("prompt-list");
3
+
4
+ const config = require("./config");
5
+ const { execCommand } = require("./util");
6
+
7
+
8
+ const buildAndPushImage = async ({
9
+ dockerFilePath,
10
+ dockerRepo,
11
+ dockerContextDir,
12
+ imageTag,
13
+ environment,
14
+ }) => {
15
+ await execCommand(`docker build \
16
+ --build-arg APP_ENV=${environment} \
17
+ -f ${dockerFilePath} \
18
+ -t ${dockerRepo} \
19
+ ${dockerContextDir}`);
20
+ await execCommand(`docker tag ${dockerRepo} ${imageTag}`);
21
+ await execCommand(`docker push ${imageTag}`);
22
+ await execCommand(`docker push ${dockerRepo}:latest`);
23
+ };
24
+
25
+ const pushToKubernetes = async ({ imageTag, appName, deployConfig }) => {
26
+ const deployDir = `${config.rootDir}/deploy/api`;
27
+
28
+ if (config.kubeConfig && !fs.existsSync(`${config.home}/.kube/config`)) {
29
+ console.log("Creating kubeconfig");
30
+ fs.mkdirSync(`${config.home}/.kube`);
31
+ fs.writeFileSync(`${config.home}/.kube/config`, config.kubeConfig);
32
+ }
33
+
34
+ let projectId = process.env.PROJECT_ID || 'core';
35
+ let attachedDomains;
36
+
37
+ await execCommand(
38
+ `helm upgrade --force --install projects-${projectId} ${deployDir} \
39
+ --namespace apps --create-namespace \
40
+ --set containerRegistry=paralect/hive-api \
41
+ --set service=hive-api-${projectId} \
42
+ --set imagesVersion=${imageTag} \
43
+ --set domain={hive-api-${projectId}.paralect.co} \
44
+ --set projectId=${projectId} \
45
+ --set env\[0\].name=MONGODB_URI \
46
+ --set env\[0\].value='${process.env.MONGODB_URI}' \
47
+ -f ${deployDir}/staging.yaml --timeout 35m`,
48
+ {
49
+ // cwd: `/app`,
50
+ }
51
+ );
52
+ };
53
+
54
+ const deploy = async () => {
55
+ if (config.dockerRegistry.password) {
56
+ await execCommand(
57
+ `docker login --username ${config.dockerRegistry.username} --password ${config.dockerRegistry.password} registry.digitalocean.com`
58
+ );
59
+ }
60
+ const deployConfig = config.deploy;
61
+
62
+ let imageTag = config.dockerRegistry.imageTag;
63
+
64
+ if (!imageTag) {
65
+ const { stdout: branch } = await execCommand(
66
+ "git rev-parse --abbrev-ref HEAD",
67
+ { stdio: "pipe" }
68
+ );
69
+ const { stdout: commitSHA } = await execCommand("git rev-parse HEAD", {
70
+ stdio: "pipe",
71
+ });
72
+
73
+ imageTag = `${branch}.${commitSHA}`;
74
+ }
75
+
76
+ try {
77
+ await execCommand(
78
+ "kubectl delete secrets sh.helm.release.v1.apps-staging-api.v1"
79
+ );
80
+
81
+ await execCommand(
82
+ "kubectl delete secrets sh.helm.release.v1.apps-staging-api.v2"
83
+ );
84
+ } catch (err) {}
85
+
86
+ // push api image to registry
87
+ await buildAndPushImage({
88
+ ...deployConfig,
89
+ imageTag: `${deployConfig.dockerRepo}:${imageTag}`,
90
+ environment: config.environment,
91
+ });
92
+
93
+ if (!process.env.SKIP_KUBERNETES) {
94
+ // deploy api to kubernetes and deploy migrator through helm hooks
95
+ await pushToKubernetes({
96
+ imageTag,
97
+ appName: "api",
98
+ deployConfig,
99
+ });
100
+ }
101
+ };
102
+
103
+ deploy();
104
+
105
+ process.on("unhandledRejection", (error) => {
106
+ console.error(error);
107
+ process.exit(1);
108
+ });
@@ -0,0 +1,19 @@
1
+ const execa = require('execa');
2
+
3
+ const execCommand = async (command, options = {}) => {
4
+ let commandParts = command;
5
+
6
+ if (typeof command === 'string') {
7
+ commandParts = command.split(' ').filter(part => !!part.trim());
8
+ }
9
+
10
+ const commandName = commandParts.shift();
11
+ const commandArguments = commandParts;
12
+ console.log('command', command);
13
+
14
+ return execa(commandName, commandArguments, { stdio: 'inherit', ...options });
15
+ };
16
+
17
+ module.exports = {
18
+ execCommand,
19
+ };
@@ -0,0 +1,176 @@
1
+ {
2
+ "resources": [
3
+ {
4
+ "name": "users",
5
+ "endpoints": [
6
+ {
7
+ "name": "getCurrentUser",
8
+ "endpoint": { "method": "GET", "url": "/me" },
9
+ "requestSchema": {},
10
+ "handler": "module.exports.handler = async (ctx) => { const user = await userService.findOne({ _id: ctx.state.user._id }); ctx.body = user; }"
11
+ },
12
+ {
13
+ "name": "getUserProfile",
14
+ "endpoint": { "method": "GET", "url": "/profile/:userId" },
15
+ "requestSchema": {
16
+ "userId": "Joi.string().required()"
17
+ },
18
+ "handler": "module.exports.handler = async (ctx) => { const { userId } = ctx.params; const user = await userService.findOne({ _id: userId }); ctx.body = user; }"
19
+ }
20
+ ],
21
+ "handlers": [],
22
+ "methods": [],
23
+ "schemas": [
24
+ {
25
+ "name": "users",
26
+ "schema": {
27
+ "username": "Joi.string().required()",
28
+ "email": "Joi.string().email().required()",
29
+ "password": "Joi.string().required()",
30
+ "bio": "Joi.string()",
31
+ "avatarUrl": "Joi.string().uri()",
32
+ "_id": "Joi.string()",
33
+ "createdOn": "Joi.date()",
34
+ "updatedOn": "Joi.date()"
35
+ },
36
+ "_schemaMappings": {}
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "name": "follows",
42
+ "endpoints": [
43
+ {
44
+ "name": "followUser",
45
+ "endpoint": { "method": "POST", "url": "/" },
46
+ "requestSchema": {
47
+ "followingId": "Joi.string().required()"
48
+ },
49
+ "handler": "module.exports.handler = async (ctx) => { const { followingId } = ctx.validatedData; const followerId = ctx.state.user._id; const newFollow = await followService.create({ follower: { _id: followerId }, following: { _id: followingId } }); ctx.body = newFollow; }"
50
+ },
51
+ {
52
+ "name": "unfollowUser",
53
+ "endpoint": { "method": "DELETE", "url": "/" },
54
+ "requestSchema": {
55
+ "followingId": "Joi.string().required()"
56
+ },
57
+ "handler": "module.exports.handler = async (ctx) => { const { followingId } = ctx.validatedData; const followerId = ctx.state.user._id; await followService.remove({ follower: { _id: followerId }, following: { _id: followingId } }); ctx.body = { message: 'Unfollowed successfully' }; }"
58
+ }
59
+ ],
60
+ "handlers": [
61
+ {
62
+ "name": "updateFollowersCount",
63
+ "handler": "const userService = require('../../../db').services.users; const followService = require('../../../db').services.follows; followService.on('created', async ({ doc: follow }) => { const { followingId } = follow; await userService.atomic.update({ _id: followingId }, { $inc: { followersCount: 1 } }); }); followService.on('removed', async ({ doc: follow }) => { const { followingId } = follow; await userService.atomic.update({ _id: followingId }, { $inc: { followersCount: -1 } }); });"
64
+ }
65
+ ],
66
+ "methods": [],
67
+ "schemas": [
68
+ {
69
+ "name": "follows",
70
+ "schema": {
71
+ "follower": { "_id": "Joi.string().required()" },
72
+ "following": { "_id": "Joi.string().required()" },
73
+ "_id": "Joi.string()",
74
+ "createdOn": "Joi.date()",
75
+ "updatedOn": "Joi.date()"
76
+ },
77
+ "_schemaMappings": {}
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "name": "posts",
83
+ "endpoints": [
84
+ {
85
+ "name": "createPost",
86
+ "endpoint": { "method": "POST", "url": "/" },
87
+ "requestSchema": {
88
+ "content": "Joi.string().max(280).required()",
89
+ "fileUrl": "Joi.string().allow(null, '')"
90
+ },
91
+ "handler": "module.exports.handler = async (ctx) => { const { content, fileUrl } = ctx.validatedData; const userId = ctx.state.user._id; const username = ctx.state.user.username; const newPost = await postService.create({ content, fileUrl, creator: { _id: userId, username: username } }); ctx.body = newPost; }"
92
+ },
93
+ {
94
+ "name": "getFeed",
95
+ "endpoint": { "method": "GET", "url": "/feed" },
96
+ "requestSchema": {},
97
+ "handler": "module.exports.handler = async (ctx) => { const posts = await postService.find({}, { sort: { createdOn: -1 } }); ctx.body = posts; }"
98
+ },
99
+ {
100
+ "name": "likePost",
101
+ "endpoint": { "method": "POST", "url": "/:postId/like" },
102
+ "requestSchema": {},
103
+ "handler": "module.exports.handler = async (ctx) => { const { postId } = ctx.params; const userId = ctx.state.user._id; await likeService.create({ post: { _id: postId }, user: { _id: userId } }); await postService.atomic.update({ _id: postId }, { $inc: { likesCount: 1 } }); ctx.body = { message: 'Post liked' }; }"
104
+ },
105
+ {
106
+ "name": "commentOnPost",
107
+ "endpoint": { "method": "POST", "url": "/:postId/comment" },
108
+ "requestSchema": {
109
+ "content": "Joi.string().required()"
110
+ },
111
+ "handler": "module.exports.handler = async (ctx) => { const { postId } = ctx.params; const { content } = ctx.validatedData; const userId = ctx.state.user._id; const username = ctx.state.user.username; const newComment = await commentService.create({ post: { _id: postId }, content, creator: { _id: userId, username: username }, createdOn: new Date() }); await postService.atomic.update({ _id: postId }, { $inc: { commentsCount: 1 } }); ctx.body = newComment; }"
112
+ }
113
+ ],
114
+ "handlers": [
115
+ {
116
+ "name": "updateCommentsCount",
117
+ "handler": "const postService = require('../../../db').services.posts; const commentService = require('../../../db').services.comments; commentService.on('created', async ({ doc: comment }) => { const { post } = comment; await postService.atomic.update({ _id: post._id }, { $inc: { commentsCount: 1 } }); }); commentService.on('removed', async ({ doc: comment }) => { const { post } = comment; await postService.atomic.update({ _id: post._id }, { $inc: { commentsCount: -1 } }); });"
118
+ },
119
+ {
120
+ "name": "updateLikesCount",
121
+ "handler": "const postService = require('../../../db').services.posts; const likeService = require('../../../db').services.likes; likeService.on('created', async ({ doc: like }) => { const { postId } = like; await postService.atomic.update({ _id: postId }, { $inc: { likesCount: 1 } }); }); likeService.on('removed', async ({ doc: like }) => { const { postId } = like; await postService.atomic.update({ _id: postId }, { $inc: { likesCount: -1 } }); });"
122
+ },
123
+ {
124
+ "name": "notifyUserOnComment",
125
+ "handler": ""
126
+ }
127
+ ],
128
+ "methods": [],
129
+ "schemas": [
130
+ {
131
+ "name": "posts",
132
+ "schema": {
133
+ "content": "Joi.string().max(280).required()",
134
+ "fileUrl": "Joi.string().allow(null, '')",
135
+ "creator": {
136
+ "_id": "Joi.string().required()",
137
+ "username": "Joi.string()"
138
+ },
139
+ "likesCount": "Joi.number().default(0)",
140
+ "commentsCount": "Joi.number().default(0)",
141
+ "parentPost": {
142
+ "_id": "Joi.string()",
143
+ "content": "Joi.string()",
144
+ "creator": "Joi.object()",
145
+ "likesCount": "Joi.number().default(0)",
146
+ "commentsCount": "Joi.number().default(0)"
147
+ },
148
+ "_id": "Joi.string()",
149
+ "createdOn": "Joi.date()",
150
+ "updatedOn": "Joi.date()"
151
+ },
152
+ "_schemaMappings": {}
153
+ }
154
+ ]
155
+ },
156
+ {
157
+ "name": "likes",
158
+ "endpoints": [],
159
+ "handlers": [],
160
+ "methods": [],
161
+ "schemas": [
162
+ {
163
+ "name": "likes",
164
+ "schema": {
165
+ "post": { "_id": "Joi.string().required()" },
166
+ "user": { "_id": "Joi.string().required()" },
167
+ "_id": "Joi.string()",
168
+ "createdOn": "Joi.date()",
169
+ "updatedOn": "Joi.date()"
170
+ },
171
+ "_schemaMappings": {}
172
+ }
173
+ ]
174
+ }
175
+ ]
176
+ }