@paralect/hive 0.1.49 → 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.
Files changed (145) 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 +123 -0
  125. package/cli/helpers/findPort.js +32 -0
  126. package/cli/hive.js +84 -12
  127. package/package.json +1 -1
  128. package/test-app/.cursor/commands/add-endpoint.md +262 -0
  129. package/test-app/.cursor/commands/add-handler.md +137 -0
  130. package/test-app/.cursor/commands/add-middleware.md +95 -0
  131. package/test-app/.cursor/commands/add-resource.md +71 -0
  132. package/test-app/.cursor/commands/add-scheduler.md +138 -0
  133. package/test-app/.cursor/commands/add-service.md +188 -0
  134. package/test-app/.cursor/skills/hive-auth/SKILL.md +134 -0
  135. package/test-app/.cursor/skills/hive-database/SKILL.md +103 -0
  136. package/test-app/.cursor/skills/hive-endpoint/SKILL.md +103 -0
  137. package/test-app/.cursor/skills/hive-handler/SKILL.md +88 -0
  138. package/test-app/.cursor/skills/hive-mapping/SKILL.md +85 -0
  139. package/test-app/.cursor/skills/hive-middleware/SKILL.md +104 -0
  140. package/test-app/.cursor/skills/hive-overview/SKILL.md +50 -0
  141. package/test-app/.cursor/skills/hive-scheduler/SKILL.md +94 -0
  142. package/test-app/.cursor/skills/hive-schema/SKILL.md +73 -0
  143. package/test-app/.cursor/skills/hive-service/SKILL.md +90 -0
  144. package/test-app/package-lock.json +8684 -0
  145. package/test-app/package.json +21 -0
@@ -0,0 +1,172 @@
1
+ import _ from "lodash";
2
+ import getResources from "helpers/getResources";
3
+ import getMiddlewares from "helpers/getMiddlewares";
4
+ import getResourceEndpoints from "helpers/getResourceEndpoints";
5
+ import mount from "koa-mount";
6
+ import Router from "@koa/router";
7
+ import validate from "middlewares/validate";
8
+ import db from "db";
9
+ import tryToAttachUser from "middlewares/global/tryToAttachUser";
10
+ import attachCustomErrors from "./middlewares/attachCustomErrors";
11
+ import routeErrorHandler from "./middlewares/routeErrorHandler";
12
+ import isAuthorized from "middlewares/isAuthorized";
13
+ import config from 'app-config';
14
+
15
+ const requestLogService = db.createService("_request_logs");
16
+
17
+ const logRequestToMongo = async (ctx, next) => {
18
+ const startedOn = new Date();
19
+
20
+ const saveLog = async ({ error = null } = {}) => {
21
+ if (ctx.state.isSkipLog) {
22
+ return;
23
+ }
24
+
25
+ if (ctx.state.resourceName && ctx.state.endpoint) {
26
+ const requestLog = {
27
+ isSuccess: true,
28
+ request: {
29
+ url: ctx.originalUrl,
30
+ method: ctx.request.method,
31
+ query: ctx.query,
32
+ body: ctx.request.body,
33
+ params: ctx.params,
34
+ headers: ctx.headers,
35
+ },
36
+ response: {
37
+ status: ctx.status,
38
+ body: ctx.body,
39
+ },
40
+ resourceName: ctx.state.resourceName,
41
+ endpoint: ctx.state.endpoint,
42
+ time: new Date() - startedOn,
43
+ };
44
+ if (error) {
45
+ requestLog.isSuccess = false;
46
+ requestLog.error = {
47
+ message: error.message,
48
+ stack: error.stack,
49
+ };
50
+ }
51
+ await requestLogService.create(requestLog);
52
+ }
53
+ };
54
+
55
+ try {
56
+ await next();
57
+
58
+ await saveLog({ error: ctx.state.error });
59
+
60
+ } catch (err) {
61
+ await saveLog({ error: err });
62
+ throw err;
63
+ }
64
+ };
65
+
66
+ export default async (app) => {
67
+ app.use(logRequestToMongo);
68
+ app.use(attachCustomErrors);
69
+ app.use(routeErrorHandler);
70
+ app.use(tryToAttachUser);
71
+
72
+ const [resources, allMiddlewares] = await Promise.all([getResources(), getMiddlewares()]);
73
+
74
+ await Promise.all(_.map(resources, async ({ name: resourceName }) => {
75
+ const resourceRouter = new Router();
76
+ const globalRouter = new Router();
77
+ const endpoints = await Promise.all((await getResourceEndpoints(resourceName))
78
+ .map(async ({ file: endpointFile, name }) => {
79
+ let endpointDef = (await import(endpointFile));
80
+
81
+ if (!endpointDef.endpoint) {
82
+ console.log('missing endpoint for', name);
83
+ }
84
+ endpointDef.endpoint.name = name;
85
+
86
+ return {
87
+ endpoint: endpointDef.endpoint,
88
+ requestSchema: endpointDef.requestSchema,
89
+ middlewares: endpointDef.middlewares,
90
+ handler: endpointDef.handler,
91
+ };
92
+ })
93
+ .sort((e) => {
94
+ e.endpoint = e.endpoint || { method: "get", url: "/" };
95
+ const url = e.endpoint.url || e.endpoint.absoluteUrl;
96
+ if (url.includes("/:")) {
97
+ return 1;
98
+ }
99
+ return -1;
100
+ }));
101
+
102
+ endpoints.forEach(({ endpoint, requestSchema, middlewares = [], handler }) => {
103
+ let targetRouter;
104
+ console.log('[routes] Register endpoint', resourceName, endpoint?.method || 'GET', endpoint?.url);
105
+
106
+ let url = endpoint.absoluteUrl || endpoint.url;
107
+
108
+ if (url.startsWith("$HOST/")) {
109
+ url = url.replace("$HOST", "");
110
+ targetRouter = globalRouter;
111
+ } else if (endpoint.absoluteUrl) {
112
+ targetRouter = globalRouter;
113
+ } else {
114
+ targetRouter = resourceRouter;
115
+ }
116
+
117
+ const globalMiddleware = allMiddlewares.find(m => m.name === 'global');
118
+
119
+ if (globalMiddleware) {
120
+ globalMiddleware.runOrder = 0;
121
+ middlewares.unshift(globalMiddleware.fn);
122
+ }
123
+
124
+ middlewares = middlewares.map(middleware => {
125
+ if (_.isString(middleware)) {
126
+ if (!allMiddlewares.find(m => m.name === middleware)) {
127
+ throw new Error(`Middleware ${middleware} not found`);
128
+ }
129
+
130
+ middleware = allMiddlewares.find(m => m.name === middleware).fn;
131
+ } else if (middleware?.name && middleware?.args) {
132
+ if (!allMiddlewares.find(m => m.name === middleware.name)) {
133
+ throw new Error(`Middleware ${middleware.name} not found`);
134
+ };
135
+ middleware = allMiddlewares.find(m => m.name === middleware.name).fn(...middleware.args);
136
+ }
137
+
138
+ return middleware;
139
+ }).map(middleware => {
140
+ middleware.runOrder = _.isNumber(middleware.runOrder) ? middleware.runOrder : 1;
141
+ return middleware;
142
+ });
143
+
144
+
145
+ if (config._hive.isRequireAuthAllEndpoints) {
146
+ isAuthorized.runOrder = 0;
147
+ middlewares.unshift(isAuthorized);
148
+ }
149
+
150
+ targetRouter[endpoint.method?.toLowerCase() || "get"](
151
+ url,
152
+ async (ctx, next) => {
153
+ ctx.state.resourceName = resourceName;
154
+ ctx.state.endpoint = endpoint;
155
+ await next();
156
+ },
157
+ validate(requestSchema),
158
+ ..._.sortBy(middlewares, m => m.runOrder),
159
+ async ctx => {
160
+ const result = await handler(ctx);
161
+
162
+ if (!ctx.body) {
163
+ ctx.body = result || { isOk: true };
164
+ }
165
+ }
166
+ );
167
+ });
168
+
169
+ app.use(globalRouter.routes());
170
+ app.use(mount(`/${resourceName}`, resourceRouter.routes()));
171
+ }));
172
+ };
@@ -0,0 +1,28 @@
1
+ import _ from 'lodash';
2
+
3
+ const formatError = (customError) => {
4
+ const errors = {};
5
+
6
+ Object.keys(customError).forEach((key) => {
7
+ errors[key] = _.isArray(customError[key])
8
+ ? customError[key]
9
+ : [customError[key]];
10
+ });
11
+
12
+ return errors;
13
+ };
14
+
15
+ const attachCustomErrors = async (ctx, next) => {
16
+ ctx.throwError = (message) => ctx.throw(500, { message });
17
+ ctx.assertError = (condition, message) =>
18
+ ctx.assert(condition, 500, { message });
19
+
20
+ ctx.throwClientError = (errors) =>
21
+ ctx.throw(400, { errors: formatError(errors) });
22
+ ctx.assertClientError = (condition, errors) =>
23
+ ctx.assert(condition, 400, { errors: formatError(errors) });
24
+
25
+ await next();
26
+ };
27
+
28
+ export default attachCustomErrors;
@@ -0,0 +1,27 @@
1
+ import logger from 'logger';
2
+
3
+ const routeErrorHandler = async (ctx, next) => {
4
+ try {
5
+ await next();
6
+ } catch (error) {
7
+ console.log("Route Error", error, error.stack);
8
+
9
+ const clientError = error.errors;
10
+ const serverError = { global: error.message };
11
+
12
+ const errors = clientError || serverError;
13
+
14
+ logger.error(errors);
15
+
16
+ if (serverError && (error.status === 500 || !error.status) && process.env.APP_ENV === "production") {
17
+ serverError.global = "Something went wrong";
18
+ }
19
+
20
+ ctx.state.error = error;
21
+
22
+ ctx.status = error.status || 500;
23
+ ctx.body = { errors };
24
+ }
25
+ };
26
+
27
+ export default routeErrorHandler;
@@ -0,0 +1,7 @@
1
+ import moment from 'moment';
2
+
3
+ export const cron = "* * * * *";
4
+
5
+ export const handler = () => {
6
+ const yesterday = moment().add(-1, "day").toDate();
7
+ };
@@ -0,0 +1,32 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import moment from 'moment';
4
+ import schedule from 'node-schedule';
5
+ import requireDir from 'require-dir';
6
+
7
+ export default () => {
8
+ const paths = [path.resolve(__dirname, './scheduler/handlers')];
9
+ if (process.env.HIVE_SRC) {
10
+ paths.push(path.resolve(process.env.HIVE_SRC, './scheduler/handlers'))
11
+ }
12
+
13
+ paths.forEach((pathName) => {
14
+ if (fs.existsSync(pathName)) {
15
+ requireDir(pathName, {
16
+ mapValue: (handler, handlerName) => {
17
+ console.log(
18
+ `[scheduler] Registering handler ${handlerName} with cron ${handler.cron}`
19
+ );
20
+
21
+ schedule.scheduleJob(handler.cron, () => {
22
+ console.log(
23
+ `[scheduler] ${moment().format()} executing ${handlerName} with cron ${handler.cron
24
+ }`
25
+ );
26
+ handler.handler();
27
+ });
28
+ },
29
+ });
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,38 @@
1
+ import crypto from 'crypto';
2
+ import bcrypt from 'bcryptjs';
3
+ import util from 'util';
4
+
5
+ const randomBytes = util.promisify(crypto.randomBytes, crypto);
6
+ const bcryptHash = util.promisify(bcrypt.hash, bcrypt);
7
+ const compare = util.promisify(bcrypt.compare, bcrypt);
8
+
9
+ /**
10
+ * @desc Generates random string, useful for creating secure tokens
11
+ *
12
+ * @return {string} - random string
13
+ */
14
+ export const generateSecureToken = async (tokenLength = 48) => {
15
+ const buf = await randomBytes(tokenLength);
16
+ return buf.toString("hex");
17
+ };
18
+
19
+ /**
20
+ * @desc Generate hash from any string. Could be used to generate a hash from password
21
+ *
22
+ * @param text {string} - a text to produce hash from
23
+ * @return {Promise} - a hash from input text
24
+ */
25
+ export const getHash = (text) => {
26
+ return bcryptHash(text, 10);
27
+ };
28
+
29
+ /**
30
+ * @desc Compares if text and hash are equal
31
+ *
32
+ * @param text {string} - a text to compare with hash
33
+ * @param hash {string} - a hash to compare with text
34
+ * @return {Promise} - are hash and text equal
35
+ */
36
+ export const compareTextWithHash = (text, hash) => {
37
+ return compare(text, hash);
38
+ };
@@ -0,0 +1,15 @@
1
+ import config from 'app-config';
2
+ import nodemailer from 'nodemailer';
3
+
4
+ config.assert('smtp');
5
+
6
+ export default {
7
+ sendEmail: async ({ to, subject, html }) => {
8
+ const transporter = nodemailer.createTransport(config.smtp)
9
+
10
+ return await transporter.sendMail({
11
+ from: config.smtp.fromEmail,
12
+ to, subject, html
13
+ })
14
+ }
15
+ };
File without changes
@@ -0,0 +1,21 @@
1
+ import psl from 'psl';
2
+ import url from 'url';
3
+ import { env } from 'app-config';
4
+ import { webUri } from 'app-config';
5
+
6
+ let cookiesDomain;
7
+
8
+ if (webUri) {
9
+ const parsedUrl = url.parse(webUri);
10
+ const parsed = psl.parse(parsedUrl.hostname);
11
+ cookiesDomain = parsed.domain;
12
+ }
13
+
14
+ export default (ctx, { name, value }, options = {}) => {
15
+ ctx.cookies.set(name, value, {
16
+ domain: '',
17
+ expires: new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000),
18
+ httpOnly: false,
19
+ ...options,
20
+ });
21
+ };
@@ -0,0 +1,99 @@
1
+ import db from 'db';
2
+ import { Server } from 'socket.io';
3
+ import { createClient } from 'redis';
4
+ import { createAdapter } from '@socket.io/redis-adapter';
5
+ import config from 'app-config';
6
+ import logger from 'logger';
7
+
8
+ export default (server) => {
9
+ const io = new Server(server);
10
+
11
+ const pubClient = createClient({ url: config.redis.url });
12
+ const subClient = pubClient.duplicate();
13
+
14
+ io.adapter(createAdapter(pubClient, subClient));
15
+
16
+ const getCookie = (cookieString, name) => {
17
+ const value = `; ${cookieString}`;
18
+ const parts = value.split(`; ${name}=`);
19
+
20
+ if (parts.length === 2) {
21
+ return parts.pop().split(";").shift();
22
+ }
23
+
24
+ return null;
25
+ };
26
+
27
+ // #TODO get user using accessToken
28
+ const getUserData = async (socket) => {
29
+ const accessToken = getCookie(
30
+ socket.handshake.headers.cookie,
31
+ "access_token"
32
+ ) || '';
33
+
34
+ console.log('socket: cookie access token', accessToken[0], accessToken[1], accessToken[2]);
35
+
36
+ let tokenDoc = null;
37
+
38
+ if (!accessToken) {
39
+ logger.info(
40
+ "Note: socket io anonymous auth. Add user authentication in socketIoService"
41
+ );
42
+ } else {
43
+ tokenDoc = await db.services.tokens.findOne({ token: accessToken });
44
+ }
45
+
46
+ return {
47
+ _id: tokenDoc?.user?._id || "anonymous",
48
+ };
49
+ };
50
+
51
+ io.use(async (socket, next) => {
52
+ const userData = await getUserData(socket);
53
+
54
+ if (userData) {
55
+ // eslint-disable-next-line no-param-reassign
56
+ socket.handshake.data = {
57
+ userId: userData.userId,
58
+ };
59
+
60
+ return next();
61
+ }
62
+
63
+ return next(new Error("token is invalid"));
64
+ });
65
+
66
+ function checkAccessToRoom(roomId, data) {
67
+ let result = false;
68
+ const [roomType, id] = roomId.split("-");
69
+
70
+ switch (roomType) {
71
+ case "user":
72
+ result = id === data.userId;
73
+ break;
74
+ default:
75
+ result = true;
76
+ }
77
+
78
+ return result;
79
+ }
80
+
81
+ io.on("connection", (client) => {
82
+ client.on("subscribe", (roomId) => {
83
+ const { userId } = client.handshake.data;
84
+ // const hasAccessToRoom = checkAccessToRoom(roomId, { userId });
85
+
86
+ const hasAccessToRoom = true;
87
+
88
+ if (hasAccessToRoom) {
89
+ client.join(roomId);
90
+ }
91
+ });
92
+
93
+ client.on("unsubscribe", (roomId) => {
94
+ client.leave(roomId);
95
+ });
96
+ });
97
+
98
+ console.log(`Socket.io server is started on app instance`);
99
+ };
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+
4
+ // Treat files as modules even if it doesn't use import/export
5
+ "moduleDetection": "force",
6
+
7
+ // Ignore module structure
8
+ "module": "Preserve",
9
+
10
+ // Allow JSON modules to be imported
11
+ "resolveJsonModule": true,
12
+
13
+ // Allow JS files to be imported from TS and vice versa
14
+ "allowJs": true,
15
+
16
+ // Use correct ESM import behavior
17
+ "esModuleInterop": true,
18
+
19
+ // Disallow features that require cross-file awareness
20
+ "isolatedModules": true,
21
+
22
+ "baseUrl": "src",
23
+ "paths": {
24
+ "*": ["*"]
25
+ },
26
+ },
27
+ "include": [
28
+ "src/**/*",
29
+ ".hive/**/*"
30
+ ]
31
+ }
@@ -0,0 +1,59 @@
1
+ const { execSync } = require('child_process');
2
+
3
+ const isDockerInstalled = () => {
4
+ try {
5
+ execSync('docker --version', { stdio: 'ignore' });
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ };
11
+
12
+ const getContainerStatus = (name) => {
13
+ try {
14
+ const result = execSync(`docker inspect --format='{{.State.Status}}' ${name}`, {
15
+ encoding: 'utf8',
16
+ stdio: ['pipe', 'pipe', 'ignore']
17
+ });
18
+ return result.trim();
19
+ } catch {
20
+ return null; // Container doesn't exist
21
+ }
22
+ };
23
+
24
+ const startContainer = async (name, image, port, extraArgs = '') => {
25
+ const status = getContainerStatus(name);
26
+
27
+ if (status === 'running') {
28
+ console.log(` ${name} already running`);
29
+ return true;
30
+ }
31
+
32
+ if (status === 'exited' || status === 'created') {
33
+ console.log(` Starting ${name}...`);
34
+ execSync(`docker start ${name}`, { stdio: 'inherit' });
35
+ return true;
36
+ }
37
+
38
+ // Container doesn't exist, create it
39
+ console.log(` Creating ${name}...`);
40
+ const cmd = `docker run -d --name ${name} -p ${port}:${port} ${extraArgs} ${image}`;
41
+ execSync(cmd, { stdio: 'inherit' });
42
+ return true;
43
+ };
44
+
45
+ const startMongo = async () => {
46
+ return startContainer('hive-mongo', 'mongo:7', 27017);
47
+ };
48
+
49
+ const startRedis = async () => {
50
+ return startContainer('hive-redis', 'redis:7-alpine', 6379);
51
+ };
52
+
53
+ module.exports = {
54
+ isDockerInstalled,
55
+ getContainerStatus,
56
+ startContainer,
57
+ startMongo,
58
+ startRedis,
59
+ };
@@ -0,0 +1,123 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const inquirer = require('inquirer');
4
+
5
+ const hashCode = (str) => {
6
+ let hash = 0;
7
+ for (let i = 0; i < str.length; i++) {
8
+ const char = str.charCodeAt(i);
9
+ hash = ((hash << 5) - hash) + char;
10
+ hash = hash & hash;
11
+ }
12
+ return Math.abs(hash);
13
+ };
14
+
15
+ const readEnvFile = (envPath) => {
16
+ if (!fs.existsSync(envPath)) {
17
+ return {};
18
+ }
19
+
20
+ const content = fs.readFileSync(envPath, 'utf8');
21
+ const env = {};
22
+
23
+ content.split('\n').forEach(line => {
24
+ const trimmed = line.trim();
25
+ if (trimmed && !trimmed.startsWith('#')) {
26
+ const [key, ...valueParts] = trimmed.split('=');
27
+ if (key) {
28
+ env[key.trim()] = valueParts.join('=').trim();
29
+ }
30
+ }
31
+ });
32
+
33
+ return env;
34
+ };
35
+
36
+ const writeEnvFile = (envPath, env) => {
37
+ const content = Object.entries(env)
38
+ .map(([key, value]) => `${key}=${value}`)
39
+ .join('\n') + '\n';
40
+
41
+ fs.writeFileSync(envPath, content);
42
+ };
43
+
44
+ const updateEnvFile = (envPath, updates) => {
45
+ const env = readEnvFile(envPath);
46
+ Object.assign(env, updates);
47
+ writeEnvFile(envPath, env);
48
+ };
49
+
50
+ const ensureEnvConfig = async (projectDir) => {
51
+ const envPath = path.join(projectDir, '.env');
52
+ const env = readEnvFile(envPath);
53
+ const projectName = path.basename(projectDir);
54
+ const updates = {};
55
+
56
+ // Check MONGODB_URI
57
+ if (!env.MONGODB_URI) {
58
+ const { mongoChoice } = await inquirer.prompt([{
59
+ type: 'list',
60
+ name: 'mongoChoice',
61
+ message: 'MONGODB_URI not set. How would you like to configure MongoDB?',
62
+ choices: [
63
+ { name: 'Use local Docker (localhost:27017)', value: 'local' },
64
+ { name: 'Enter custom URI', value: 'custom' },
65
+ ]
66
+ }]);
67
+
68
+ if (mongoChoice === 'local') {
69
+ updates.MONGODB_URI = `mongodb://localhost:27017/${projectName}`;
70
+ } else {
71
+ const { mongoUri } = await inquirer.prompt([{
72
+ type: 'input',
73
+ name: 'mongoUri',
74
+ message: 'Enter MongoDB URI:',
75
+ default: `mongodb://localhost:27017/${projectName}`
76
+ }]);
77
+ updates.MONGODB_URI = mongoUri;
78
+ }
79
+ }
80
+
81
+ // Check REDIS_URI
82
+ if (!env.REDIS_URI) {
83
+ const { redisChoice } = await inquirer.prompt([{
84
+ type: 'list',
85
+ name: 'redisChoice',
86
+ message: 'REDIS_URI not set. How would you like to configure Redis?',
87
+ choices: [
88
+ { name: 'Use local Docker (localhost:6379)', value: 'local' },
89
+ { name: 'Enter custom URI', value: 'custom' },
90
+ { name: 'Skip (Redis optional)', value: 'skip' },
91
+ ]
92
+ }]);
93
+
94
+ if (redisChoice === 'local') {
95
+ const redisDb = hashCode(projectName) % 16;
96
+ updates.REDIS_URI = `redis://localhost:6379/${redisDb}`;
97
+ } else if (redisChoice === 'custom') {
98
+ const { redisUri } = await inquirer.prompt([{
99
+ type: 'input',
100
+ name: 'redisUri',
101
+ message: 'Enter Redis URI:',
102
+ default: `redis://localhost:6379/0`
103
+ }]);
104
+ updates.REDIS_URI = redisUri;
105
+ }
106
+ }
107
+
108
+ // Write updates if any
109
+ if (Object.keys(updates).length > 0) {
110
+ updateEnvFile(envPath, updates);
111
+ console.log(`\n Updated .env with: ${Object.keys(updates).join(', ')}\n`);
112
+ }
113
+
114
+ return { ...env, ...updates };
115
+ };
116
+
117
+ module.exports = {
118
+ readEnvFile,
119
+ writeEnvFile,
120
+ updateEnvFile,
121
+ ensureEnvConfig,
122
+ hashCode,
123
+ };
@@ -0,0 +1,32 @@
1
+ const net = require('net');
2
+
3
+ const isPortAvailable = (port) => {
4
+ return new Promise((resolve) => {
5
+ const server = net.createServer();
6
+
7
+ server.once('error', () => {
8
+ resolve(false);
9
+ });
10
+
11
+ server.once('listening', () => {
12
+ server.close();
13
+ resolve(true);
14
+ });
15
+
16
+ server.listen(port, '127.0.0.1');
17
+ });
18
+ };
19
+
20
+ const findAvailablePort = async (startPort = 3001, maxAttempts = 100) => {
21
+ for (let port = startPort; port < startPort + maxAttempts; port++) {
22
+ if (await isPortAvailable(port)) {
23
+ return port;
24
+ }
25
+ }
26
+ throw new Error(`No available port found between ${startPort} and ${startPort + maxAttempts - 1}`);
27
+ };
28
+
29
+ module.exports = {
30
+ isPortAvailable,
31
+ findAvailablePort,
32
+ };