@paralect/hive 0.0.1 → 0.0.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.
package/cli/hive.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const axios = require('axios');
5
+ const fs = require('fs-extra');
6
+ const path = require('path');
7
+
8
+ const GITHUB_API_URL = 'https://api.github.com/repos';
9
+
10
+ async function fetchDirectoryTree(owner, repo, dirPath) {
11
+ const url = `${GITHUB_API_URL}/${owner}/${repo}/contents/${dirPath}`;
12
+ const response = await axios.get(url);
13
+ return response.data;
14
+ }
15
+
16
+ async function downloadFile(fileUrl, filePath) {
17
+ const response = await axios({
18
+ url: fileUrl,
19
+ method: 'GET',
20
+ responseType: 'stream'
21
+ });
22
+ await fs.ensureDir(path.dirname(filePath));
23
+ response.data.pipe(fs.createWriteStream(filePath));
24
+ return new Promise((resolve, reject) => {
25
+ response.data.on('end', resolve);
26
+ response.data.on('error', reject);
27
+ });
28
+ }
29
+
30
+ async function downloadDirectory(pluginName, baseDir = '') {
31
+ const owner = 'paralect';
32
+ const repo = 'hive-plugins';
33
+
34
+ const destDir = path.join(process.cwd(), baseDir);
35
+
36
+ const tree = await fetchDirectoryTree(owner, repo, pluginName);
37
+ for (const item of tree) {
38
+ const relativePath = path.relative(pluginName, item.path);
39
+ const filePath = path.join(destDir, relativePath);
40
+ if (item.type === 'file') {
41
+ console.log(`Downloading ${item.path}...`);
42
+ await downloadFile(item.download_url, filePath);
43
+ } else if (item.type === 'dir') {
44
+ await downloadDirectory(item.path, path.join(baseDir, relativePath));
45
+ }
46
+ }
47
+ }
48
+
49
+ program
50
+ .command('run [dirPath]')
51
+ .description('Run Hive server')
52
+ .action(async (dirPath = '.') => {
53
+ try {
54
+ process.env.HIVE_SRC = path.resolve(process.cwd(), dirPath);
55
+ require('./../starter/src/app.js');
56
+ } catch (error) {
57
+ console.error('An error occurred:', error.message);
58
+ }
59
+ });
60
+
61
+ program
62
+ .command('install <plugin>')
63
+ .description('Installs Hive plugin')
64
+ .action(async (plugin) => {
65
+ try {
66
+ const destDir = process.cwd();
67
+
68
+ await downloadDirectory(plugin);
69
+ } catch (error) {
70
+ console.error('An error occurred:', error.message);
71
+ }
72
+ });
73
+
74
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@paralect/hive",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "hive": "cli/cli.js"
7
+ "hive": "cli/hive.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -19,9 +19,12 @@
19
19
  "@socket.io/redis-emitter": "4.1.1",
20
20
  "app-module-path": "2.2.0",
21
21
  "aws-sdk": "2.1080.0",
22
+ "axios": "^1.7.2",
22
23
  "bcryptjs": "2.4.3",
24
+ "commander": "^12.1.0",
23
25
  "dotenv": "16.0.0",
24
26
  "eslint-config-prettier": "8.5.0",
27
+ "fs-extra": "^11.2.0",
25
28
  "handlebars": "4.7.7",
26
29
  "joi": "17.6.0",
27
30
  "koa": "2.13.4",
@@ -42,6 +45,7 @@
42
45
  "psl": "1.8.0",
43
46
  "redis": "3.1.2",
44
47
  "require-dir": "1.2.0",
48
+ "slug": "^9.1.0",
45
49
  "socket.io": "4.4.1",
46
50
  "socket.io-emitter": "3.2.0",
47
51
  "tail": "2.2.4",
@@ -41,6 +41,7 @@
41
41
  "psl": "1.8.0",
42
42
  "redis": "3.1.2",
43
43
  "require-dir": "1.2.0",
44
+ "slug": "^9.1.0",
44
45
  "socket.io": "4.4.1",
45
46
  "socket.io-emitter": "3.2.0",
46
47
  "tail": "2.2.4",
@@ -5547,6 +5548,14 @@
5547
5548
  "node": "*"
5548
5549
  }
5549
5550
  },
5551
+ "node_modules/slug": {
5552
+ "version": "9.1.0",
5553
+ "resolved": "https://registry.npmjs.org/slug/-/slug-9.1.0.tgz",
5554
+ "integrity": "sha512-ioOsCfzQSu+D6NZ8XMCR8IW9FgvF8W7Xzz56hBkB/ALvNaWeBs2MUvvPugq3GCrxfHPFeK6hAxGkY/WLnfX2Lg==",
5555
+ "bin": {
5556
+ "slug": "cli.js"
5557
+ }
5558
+ },
5550
5559
  "node_modules/socket.io": {
5551
5560
  "version": "4.4.1",
5552
5561
  "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.4.1.tgz",
@@ -59,6 +59,7 @@
59
59
  "psl": "1.8.0",
60
60
  "redis": "3.1.2",
61
61
  "require-dir": "1.2.0",
62
+ "slug": "^9.1.0",
62
63
  "socket.io": "4.4.1",
63
64
  "socket.io-emitter": "3.2.0",
64
65
  "tail": "2.2.4",
@@ -0,0 +1,15 @@
1
+ const _ = require('lodash');
2
+
3
+ module.exports = (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,46 @@
1
+ const fs = require('fs');
2
+
3
+ require("dotenv").config({ path: `${__dirname}/.env` });
4
+ require("dotenv").config({ path: `${__dirname}/.env.app` });
5
+
6
+ if (process.env.HIVE_SRC) {
7
+ require("dotenv").config({ path: `${process.env.HIVE_SRC}/app-config/.env.app` });
8
+ }
9
+
10
+ let appConfig = require("./app");
11
+
12
+ if (process.env.HIVE_SRC) {
13
+ let pluginConfigPath = `${process.env.HIVE_SRC}/app-config/app.js`;
14
+
15
+ if (fs.existsSync(pluginConfigPath)) {
16
+ let pluginConfig = require(pluginConfigPath);
17
+ appConfig = { ...appConfig, ...pluginConfig };
18
+ }
19
+ }
20
+
21
+
22
+ const env = process.env.APP_ENV || "development";
23
+
24
+ const config = {
25
+ env,
26
+ port: process.env.PORT || 3001,
27
+ domain: env === 'production' ? 'https://api.yourapp.com': process.env.DOMAIN || '',
28
+ isDev: env === "development",
29
+
30
+ mongoUri: process.env.MONGODB_URI,
31
+
32
+ redis: {
33
+ url: process.env.REDIS_URI,
34
+ },
35
+
36
+ ...appConfig,
37
+
38
+ assert(configKey) {
39
+ if (!config[configKey]) {
40
+ throw Error(`Config [${configKey}] is missing`);
41
+ }
42
+ },
43
+ };
44
+
45
+ module.exports = config;
46
+
@@ -8,7 +8,7 @@ const fs = require("fs");
8
8
 
9
9
  const Koa = require("koa");
10
10
  const http = require("http");
11
- const config = require("config");
11
+ const config = require("app-config");
12
12
  const logger = require("logger");
13
13
  const cors = require("@koa/cors");
14
14
  const helmet = require("koa-helmet");
package/starter/src/db.js CHANGED
@@ -5,7 +5,7 @@ const requireDir = require("require-dir");
5
5
  const getSchemas = require("helpers/getSchemas");
6
6
  const getResources = require("helpers/getResources");
7
7
 
8
- const config = require("config");
8
+ const config = require("app-config");
9
9
  const db = require("lib/node-mongo").connect(config.mongoUri);
10
10
 
11
11
  db.services = {};
@@ -30,8 +30,6 @@ db.init = async () => {
30
30
 
31
31
  const resourcePaths = await getResources();
32
32
 
33
- console.log('resourcePaths', resourcePaths);
34
-
35
33
  _.each(resourcePaths, ({ dir }) => {
36
34
  if (fs.existsSync(`${dir}/handlers`)) {
37
35
  requireDir(`${dir}/handlers`);
@@ -1,7 +1,10 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+
1
4
  const {
2
5
  promises: { readdir },
3
- } = require("fs");
4
- const path = require("path");
6
+ } = fs;
7
+
5
8
 
6
9
  const getDirectories = async (source) => {
7
10
  return (await readdir(source, { withFileTypes: true }))
@@ -13,7 +16,11 @@ module.exports = async () => {
13
16
  let resourceDirs = await getDirectories(`${__dirname}/../resources`);
14
17
 
15
18
  if (process.env.HIVE_SRC) {
16
- resourceDirs = [...resourceDirs, ...((await getDirectories(`${process.env.HIVE_SRC}/resources`)).map(r => ({ dirName: r.dirName, isHive: true })))]
19
+ let hiveResourcesDirPath = `${process.env.HIVE_SRC}/resources`;
20
+
21
+ if (fs.existsSync(hiveResourcesDirPath)) {
22
+ resourceDirs = [...resourceDirs, ...((await getDirectories(hiveResourcesDirPath)).map(r => ({ dirName: r.dirName, isHive: true })))]
23
+ }
17
24
  }
18
25
 
19
26
  return resourceDirs
@@ -1,4 +1,4 @@
1
- const config = require("config");
1
+ const config = require("app-config");
2
2
 
3
3
  const { Emitter } = require("@socket.io/redis-emitter");
4
4
  const { createClient } = require("redis");
@@ -1,5 +1,5 @@
1
1
  const winston = require("winston");
2
- const config = require("config");
2
+ const config = require("app-config");
3
3
 
4
4
  const colorizer = winston.format.colorize();
5
5
 
@@ -1,4 +1,31 @@
1
- module.exports = (ctx, next) => {
1
+ const userService = require('db').services.users;
2
+ const tokenService = require('db').services.tokens;
3
+
4
+ const storeTokenToState = async (ctx) => {
5
+ let accessToken = ctx.cookies.get('access_token');
6
+
7
+ const { authorization } = ctx.headers;
8
+
9
+ if (!accessToken && authorization) {
10
+ accessToken = authorization.replace('Bearer', '').trim();
11
+ }
12
+
13
+ ctx.state.accessToken = accessToken;
14
+ };
15
+
16
+ module.exports = async (ctx, next) => {
17
+ storeTokenToState(ctx);
18
+
19
+ let token;
20
+
21
+ if (ctx.state.accessToken) {
22
+ token = await tokenService.findOne({ token: ctx.state.accessToken });
23
+ }
24
+
25
+ if (token) {
26
+ ctx.state.user = await userService.findOne({ _id: token.user._id });
27
+ }
28
+
2
29
  if (ctx.state.user) {
3
30
  return next();
4
31
  }
@@ -6,4 +33,4 @@ module.exports = (ctx, next) => {
6
33
  ctx.status = 401;
7
34
  ctx.body = {};
8
35
  return null;
9
- };
36
+ };
@@ -0,0 +1,9 @@
1
+ const crypto = require('crypto');
2
+ const util = require('util');
3
+
4
+ const randomBytes = util.promisify(crypto.randomBytes, crypto);
5
+
6
+ module.exports = async (tokenLength = 32) => {
7
+ const buf = await randomBytes(tokenLength);
8
+ return buf.toString('hex');
9
+ };
@@ -0,0 +1,7 @@
1
+ const tokenService = require('db').services.tokens;
2
+
3
+ module.exports = async () => {
4
+ const { results } = await tokenService.aggregate([]);
5
+
6
+ return results;
7
+ };
@@ -0,0 +1,33 @@
1
+ const crypto = require('crypto');
2
+ const util = require('util');
3
+
4
+ const randomBytes = util.promisify(crypto.randomBytes, crypto);
5
+
6
+ const setCookie = require('services/setCookie');
7
+
8
+ const tokenService = require('db').services.tokens;
9
+
10
+ const generateSecureToken = async (tokenLength = 32) => {
11
+ const buf = await randomBytes(tokenLength);
12
+ return buf.toString('hex');
13
+ };
14
+
15
+ module.exports = async (ctx, { userId }) => {
16
+ const token = await generateSecureToken();
17
+ const otp = await generateSecureToken();
18
+
19
+ await tokenService.create({
20
+ token,
21
+ user: {
22
+ _id: userId,
23
+ },
24
+ otp,
25
+ });
26
+
27
+ setCookie(ctx, { name: 'access_token', value: token });
28
+
29
+ return {
30
+ token,
31
+ otp,
32
+ };
33
+ };
@@ -0,0 +1,12 @@
1
+ const Joi = require('joi');
2
+
3
+ module.exports = Joi.object({
4
+ _id: Joi.string(),
5
+ createdOn: Joi.date(),
6
+ updatedOn: Joi.date(),
7
+ user: Joi.object({
8
+ _id: Joi.string(),
9
+ }).required(),
10
+ token: Joi.string().required(),
11
+ otp: Joi.string().allow(null).allow(''),
12
+ });
@@ -0,0 +1,67 @@
1
+ const _ = require('lodash');
2
+ const slug = require('slug');
3
+
4
+ const userService = require('db').services.users;
5
+
6
+ const formatUsername = ({ username, fullName }) => {
7
+ return (
8
+ username ||
9
+ slug(`${fullName.split(' ')[0]} ${fullName.split(' ')[1] || ''}`, '.')
10
+ );
11
+ };
12
+
13
+ const createUserAccount = async (userData) => {
14
+ let username = formatUsername({ fullName: userData.fullName });
15
+
16
+ if (await userService.exists({ username })) {
17
+ username += _.random(1000, 9999);
18
+ }
19
+
20
+ const user = await userService.create({
21
+ fullName: userData.fullName,
22
+ username,
23
+ email: userData.email,
24
+ isEmailVerified: true,
25
+ avatarUrl: userData.avatarUrl,
26
+ oauth: userData.oauth,
27
+ });
28
+
29
+ return user;
30
+ };
31
+
32
+ const ensureUserCreated = async (userData) => {
33
+ const user = await userService.findOne({ email: userData.email });
34
+
35
+ if (user) {
36
+ if (userData.oauth) {
37
+ const changedUser = await userService.updateOne(
38
+ { _id: user._id },
39
+ (old) => {
40
+ const newUser = old;
41
+ newUser.oauth = old.oauth || {};
42
+ newUser.oauth = {
43
+ ...newUser.oauth,
44
+ ...userData.oauth
45
+ }
46
+ newUser.isEmailVerified = true;
47
+
48
+ return newUser;
49
+ }
50
+ );
51
+
52
+ return { user: changedUser, isNew: true };
53
+ } else {
54
+ throw new Error(`User with email ${userData.email} already exists`);
55
+ }
56
+ }
57
+
58
+ return {
59
+ user: await createUserAccount(userData),
60
+ isNew: true,
61
+ };
62
+ };
63
+
64
+ module.exports = async (userData) => {
65
+ const { updatedUser, isNew } = await ensureUserCreated(userData);
66
+ return updatedUser;
67
+ };
@@ -1,14 +1,20 @@
1
1
  const Joi = require("joi");
2
2
 
3
3
  const users = Joi.object({
4
- username: Joi.string().required(),
5
- email: Joi.string().email().required(),
6
- password: Joi.string().required(),
7
- bio: Joi.string(),
8
- avatarUrl: Joi.string().uri(),
9
4
  _id: Joi.string(),
10
5
  createdOn: Joi.date(),
11
6
  updatedOn: Joi.date(),
7
+
8
+ email: Joi.string().email().required(),
9
+ fullName: Joi.string().required(),
10
+ username: Joi.string().required(),
11
+ avatarUrl: Joi.string().uri(),
12
+
13
+ password: Joi.string(),
14
+
15
+ oauth: Joi.object({
16
+ google: Joi.object({}),
17
+ }),
12
18
  });
13
19
 
14
20
  module.exports = users;
@@ -0,0 +1,22 @@
1
+ const psl = require('psl');
2
+ const url = require('url');
3
+ const { env } = require('app-config');
4
+
5
+ const { webUri } = require('app-config');
6
+
7
+ let cookiesDomain;
8
+
9
+ if (webUri) {
10
+ const parsedUrl = url.parse(webUri);
11
+ const parsed = psl.parse(parsedUrl.hostname);
12
+ cookiesDomain = parsed.domain;
13
+ }
14
+
15
+ module.exports = (ctx, { name, value }, options = {}) => {
16
+ ctx.cookies.set(name, value, {
17
+ domain: '',
18
+ expires: new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000),
19
+ httpOnly: false,
20
+ ...options,
21
+ });
22
+ };
@@ -2,7 +2,7 @@ const { Server } = require("socket.io");
2
2
  const { createClient } = require("redis");
3
3
  const { createAdapter } = require("@socket.io/redis-adapter");
4
4
 
5
- const config = require("config");
5
+ const config = require("app-config");
6
6
  const logger = require("logger");
7
7
 
8
8
  module.exports = (server) => {
package/cli/cli.js DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const path = require('path');
4
-
5
- // Get the routes directory from the command line arguments
6
- const args = process.argv.slice(2);
7
-
8
- process.env.HIVE_SRC = path.resolve(process.cwd(), args[0]);
9
-
10
- require('./../starter/src/app.js');
@@ -1,32 +0,0 @@
1
- -----BEGIN CERTIFICATE-----
2
- MIIFgjCCA2qgAwIBAgIRAJgporgNYE10qopO4ueSLR8wDQYJKoZIhvcNAQELBQAw
3
- WTEVMBMGA1UEChMMRGlnaXRhbE9jZWFuMUAwPgYDVQQDEzdtb25nb2RiIENsdXN0
4
- ZXIgQ0E6OTgyOWEyYjgtMGQ2MC00ZDc0LWFhOGEtNGVlMmU3OTIyZDFmMB4XDTIy
5
- MDQyODE1MDI1NloXDTQyMDQyODE1MDI1NlowWTEVMBMGA1UEChMMRGlnaXRhbE9j
6
- ZWFuMUAwPgYDVQQDEzdtb25nb2RiIENsdXN0ZXIgQ0E6OTgyOWEyYjgtMGQ2MC00
7
- ZDc0LWFhOGEtNGVlMmU3OTIyZDFmMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
8
- CgKCAgEAvfroNWnTm6ycVLh5Ra/miiLe1ol0ZCzixbCvf8zxdCrg0scp51XL5eFR
9
- 40hncT4bzV0AUCu97QGviNAxHsLUUxNCd5zZwKYx6BvbZNXrb9NtFlXf9XHPW4DT
10
- cV4KTQMvce6p7KjknL4pazqr3AmV6Nm2p4CHdmOtlMcspw76/DunRAgKMw8pOV0e
11
- VZu+mV9f7UMFnXaA0tPhPqTd2ZWTt47nsFnEZEiCnePYDNf+BtWvGyxefjw1mHxr
12
- h/9sm5YNsNnhQiUT71GGHIFgiYM8y47t6jGCEqjhD3pXnkaAuoyyoMZYing+1CXN
13
- rd6s1Ca7w+F+aNxTOSMZdYeBAokNYyQhvzeGbHEpuRsl10Vx48zIA5ej6UrlADzc
14
- Ch0arO+LGSpz59UZ3Inr6O+a+CNKCcD5J7ZA0Qrp9vqJaR5wEHd7MTR2zIp8OaN3
15
- beE/n+dohpVDClpRO4j2ACUBS2YMk6bsVKwlwU7uJ2l/pucNMrZaleJAZoGEJ/8I
16
- EMv5WfiZz504zSgIxGzGpDt8+0D36FW8tz1uYlLmy9IdvHr5dshsfDVvMpqyiphU
17
- NTkO3jRnP5IDbn9w7CXohNPFvfqelx6e3gWtjINN/K+XMansqCqZWtMNuEbsX+oK
18
- 6T4j8LOQB0NYFhzLthFGxEDiaeHHi2tRRRX54dv9ogfxFBpUlbkCAwEAAaNFMEMw
19
- DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFD+P
20
- ZtQ9wxLE6gLewtQC3U/jQIEiMA0GCSqGSIb3DQEBCwUAA4ICAQAsbuX1SBWMvWJR
21
- hesLsIpwG7KhIsrvhOIE9G8cumJW3jssWFBdyt7HH+/IpSG7SVkuSz+/NAcd0q6K
22
- p7UWnp8MW+rfzET671S6hNSv4h/zgLA3r0fSqSRMU7rZ+qSvIrQpxav7o/3wPtj+
23
- gkc2/x5wTUfAYnV2liqj0LfWI5bXsWgupdyDa8VA8T026e70QctpdqwIVLUIUDQz
24
- gS0aF8UZymDsajETKJLxdtAAyhfnqCoQ5qNfroNhioisYDa9QsYyZ8cw9nJAAWPm
25
- SQfGEf4/m8ookTPRDlI8KaTR2TbM0QJThWxjeiJoZS4gA34ao+4a1uyVgaxU0Ygd
26
- 73NcQYEWfSH9N1Lhnaw5pQNBfjta04GO26p+/c+9t1kMFFz2ZfTxpN6xjn9ZJJYZ
27
- nnafp6nqWADgnbD3cWbTsHLGm6Rqbh18oy4jPrxCYQQQCCn1eba75KAVxf70GI32
28
- VhyPBBV4rHBi4gmKmBueSzb5gjzXgn8R0kxKPhhmLmZ1XK7gF/EbD9B1JGUQLp7S
29
- ITv+WZMHuErZt61cnZ0XM6jJYg3QAejY9V0UIp8j2ud83H1Np7fQlWyOZYxa+czK
30
- x6ZyNLojjJmUEwpn/pjieFI73n7nmITxvkl78c7ga11xbrJEXiN5GOQh5Wr+lBAF
31
- lNQexy8hyosbG0wXXPHX5wwu5eXVeA==
32
- -----END CERTIFICATE-----
@@ -1,24 +0,0 @@
1
- require("dotenv").config({ path: `${__dirname}/.env` });
2
- require("dotenv").config({ path: `${__dirname}/.env.app` });
3
-
4
- const appConfig = require("./app");
5
-
6
- const env = process.env.APP_ENV || "development";
7
-
8
- const config = {
9
- env,
10
- port: process.env.PORT || 3001,
11
- isDev: env === "development",
12
-
13
- projectId: process.env.PROJECT_ID,
14
-
15
- mongoUri: process.env.MONGODB_URI,
16
-
17
- redis: {
18
- url: process.env.REDIS_URI,
19
- },
20
-
21
- ...appConfig,
22
- };
23
-
24
- module.exports = config;
File without changes