@paralect/hive 0.0.1

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 (71) hide show
  1. package/cli/cli.js +10 -0
  2. package/package.json +60 -0
  3. package/starter/Dockerfile +13 -0
  4. package/starter/Dockerfile.dev +33 -0
  5. package/starter/Dockerfile.prod +29 -0
  6. package/starter/README.md +11 -0
  7. package/starter/bin/init-project.sh +22 -0
  8. package/starter/bin/start.sh +2 -0
  9. package/starter/bootstrap-hive.js +118 -0
  10. package/starter/initial-data.json +176 -0
  11. package/starter/mongodb-ca-certificate.cer +32 -0
  12. package/starter/package-lock.json +6711 -0
  13. package/starter/package.json +84 -0
  14. package/starter/ship_logo.png +0 -0
  15. package/starter/src/app.js +61 -0
  16. package/starter/src/assets/emails/components/header.mjml +13 -0
  17. package/starter/src/assets/emails/dist/.gitkeep +0 -0
  18. package/starter/src/assets/emails/signup-welcome.mjml +34 -0
  19. package/starter/src/assets/emails/styles/index.mjml +77 -0
  20. package/starter/src/autoMap/addHandlers.js +167 -0
  21. package/starter/src/autoMap/mapSchema.js +112 -0
  22. package/starter/src/autoMap/schemaMappings.js +7 -0
  23. package/starter/src/config/app.js +3 -0
  24. package/starter/src/config/index.js +24 -0
  25. package/starter/src/db.js +48 -0
  26. package/starter/src/helpers/db/ifUpdated.js +22 -0
  27. package/starter/src/helpers/getResourceEndpoints.js +26 -0
  28. package/starter/src/helpers/getResources.js +25 -0
  29. package/starter/src/helpers/getSchemas.js +25 -0
  30. package/starter/src/helpers/prettierFormat.js +8 -0
  31. package/starter/src/ioEmitter.js +10 -0
  32. package/starter/src/jsconfig.json +5 -0
  33. package/starter/src/lib/node-mongo/.github/workflows/npm-publish.yml +32 -0
  34. package/starter/src/lib/node-mongo/API.md +654 -0
  35. package/starter/src/lib/node-mongo/CHANGELOG.md +98 -0
  36. package/starter/src/lib/node-mongo/README.md +97 -0
  37. package/starter/src/lib/node-mongo/package.json +74 -0
  38. package/starter/src/lib/node-mongo/src/index.js +67 -0
  39. package/starter/src/lib/node-mongo/src/mongo-query-service.js +72 -0
  40. package/starter/src/lib/node-mongo/src/mongo-service-error.js +15 -0
  41. package/starter/src/lib/node-mongo/src/mongo-service.js +279 -0
  42. package/starter/src/logger.js +30 -0
  43. package/starter/src/middlewares/global/extractUserTokens.js +15 -0
  44. package/starter/src/middlewares/global/tryToAttachUser.js +32 -0
  45. package/starter/src/middlewares/isAuthorized.js +9 -0
  46. package/starter/src/middlewares/shouldExist.js +17 -0
  47. package/starter/src/middlewares/shouldNotExist.js +19 -0
  48. package/starter/src/middlewares/uploadFile.js +5 -0
  49. package/starter/src/middlewares/validate.js +39 -0
  50. package/starter/src/migrations/migration.js +8 -0
  51. package/starter/src/migrations/migration.service.js +75 -0
  52. package/starter/src/migrations/migrations/1.js +22 -0
  53. package/starter/src/migrations/migrations-log/migration-log.schema.js +15 -0
  54. package/starter/src/migrations/migrations-log/migration-log.service.js +51 -0
  55. package/starter/src/migrations/migrations.schema.js +9 -0
  56. package/starter/src/migrations/migrator.js +77 -0
  57. package/starter/src/migrator.js +5 -0
  58. package/starter/src/resources/_dev/endpoints/triggerSchedulerHandler.js +30 -0
  59. package/starter/src/resources/health/endpoints/get.js +10 -0
  60. package/starter/src/resources/schemaMappings/schemaMappings.schema.js +9 -0
  61. package/starter/src/resources/users/endpoints/getCurrentUser.js +13 -0
  62. package/starter/src/resources/users/endpoints/getUserProfile.js +16 -0
  63. package/starter/src/resources/users/users.schema.js +14 -0
  64. package/starter/src/routes/index.js +151 -0
  65. package/starter/src/routes/middlewares/attachCustomErrors.js +28 -0
  66. package/starter/src/routes/middlewares/routeErrorHandler.js +27 -0
  67. package/starter/src/scheduler/handlers/sendDailyReport.example.js +7 -0
  68. package/starter/src/scheduler.js +21 -0
  69. package/starter/src/security.util.js +38 -0
  70. package/starter/src/services/globalTest.js +0 -0
  71. package/starter/src/socketIo.js +91 -0
package/cli/cli.js ADDED
@@ -0,0 +1,10 @@
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');
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@paralect/hive",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "hive": "cli/cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "dependencies": {
13
+ "@koa/cors": "3.1.0",
14
+ "@koa/multer": "3.0.0",
15
+ "@koa/router": "10.1.1",
16
+ "@paralect/node-mongo": "2.1.1",
17
+ "@sendgrid/mail": "7.6.1",
18
+ "@socket.io/redis-adapter": "7.1.0",
19
+ "@socket.io/redis-emitter": "4.1.1",
20
+ "app-module-path": "2.2.0",
21
+ "aws-sdk": "2.1080.0",
22
+ "bcryptjs": "2.4.3",
23
+ "dotenv": "16.0.0",
24
+ "eslint-config-prettier": "8.5.0",
25
+ "handlebars": "4.7.7",
26
+ "joi": "17.6.0",
27
+ "koa": "2.13.4",
28
+ "koa-bodyparser": "4.3.0",
29
+ "koa-helmet": "6.1.0",
30
+ "koa-logger": "3.2.1",
31
+ "koa-mount": "4.0.0",
32
+ "koa-qs": "3.0.0",
33
+ "lodash": "4.17.21",
34
+ "mjml": "4.12.0",
35
+ "mkdirp": "1.0.4",
36
+ "moment": "2.29.1",
37
+ "moment-duration-format": "2.3.2",
38
+ "multer": "1.4.4",
39
+ "node-schedule": "2.1.0",
40
+ "nodemon": "2.0.15",
41
+ "prettier": "2.6.2",
42
+ "psl": "1.8.0",
43
+ "redis": "3.1.2",
44
+ "require-dir": "1.2.0",
45
+ "socket.io": "4.4.1",
46
+ "socket.io-emitter": "3.2.0",
47
+ "tail": "2.2.4",
48
+ "winston": "3.6.0"
49
+ },
50
+ "devDependencies": {
51
+ "eslint": "8.9.0",
52
+ "eslint-config-airbnb-base": "15.0.0",
53
+ "eslint-plugin-import": "2.25.4",
54
+ "husky": "7.0.4",
55
+ "lint-staged": "12.3.4"
56
+ },
57
+ "keywords": [],
58
+ "author": "",
59
+ "license": "MIT"
60
+ }
@@ -0,0 +1,13 @@
1
+ FROM --platform=linux/amd64 node:16.13.1-alpine3.13 as base
2
+ RUN apk add --no-cache python3 py3-pip
3
+
4
+ # On staging containers running in development mode
5
+ ARG NODE_ENV=development
6
+
7
+ ARG APP_ENV
8
+ ENV NODE_ENV=$NODE_ENV
9
+ ENV APP_ENV=$APP_ENV
10
+
11
+ WORKDIR /project
12
+
13
+ CMD npm run dev 2>&1 | tee /project/logs/log.txt
@@ -0,0 +1,33 @@
1
+ FROM node:16.13.1-alpine3.13 as base
2
+ RUN apk add --no-cache python3 py3-pip
3
+
4
+ ARG NODE_ENV=development
5
+ ARG APP_ENV
6
+ ARG PROJECT_SRC=/project
7
+
8
+ ENV NODE_ENV=$NODE_ENV
9
+ ENV APP_ENV=$APP_ENV
10
+ ENV PROJECT_SRC=$PROJECT_SRC
11
+
12
+ WORKDIR /app
13
+ COPY ["./package*.json", "/app/"]
14
+ # Disable husky
15
+ RUN npm set-script prepare ""
16
+
17
+ RUN npm ci --quiet
18
+ COPY . ./
19
+
20
+ RUN npm run build-assets
21
+
22
+ FROM base as migrator
23
+ CMD npm run migrate
24
+
25
+ FROM base as scheduler
26
+ CMD npm run schedule-dev
27
+
28
+ FROM base as api
29
+
30
+ EXPOSE 3001
31
+ EXPOSE 3012
32
+
33
+ CMD npm run dev 2>&1 | tee /logs/log.txt
@@ -0,0 +1,29 @@
1
+ FROM node:16.13.1-alpine3.13 as base
2
+ RUN apk add --no-cache python3 py3-pip
3
+
4
+ ENV NODE_ENV=production
5
+ ENV APP_ENV=production
6
+
7
+ WORKDIR /app
8
+ EXPOSE 3001
9
+ COPY ["./package*.json", "./.eslintrc.js", "/app/"]
10
+
11
+ RUN npm set progress=false && npm config set depth 0
12
+
13
+ RUN npm set-script prepare ""
14
+
15
+ RUN npm ci --only=production --quiet
16
+
17
+ COPY ./ ./
18
+
19
+ RUN ls /app/src/config/.env
20
+
21
+ RUN rm /app/src/config/.env
22
+ RUN mv /app/src/config/.env.production /app/src/config/.env
23
+
24
+ RUN cat /app/src/config/.env
25
+
26
+ RUN mkdir -p /project/logs
27
+ RUN touch /project/logs/log.txt
28
+
29
+ CMD npm start 2>&1 | tee /project/logs/log.txt
@@ -0,0 +1,11 @@
1
+ # API 🧩
2
+
3
+ ## Start
4
+
5
+ To start a server in the docker container use bash `./bin/start.sh`:
6
+
7
+ ```bash
8
+ $ ./bin/start.sh
9
+ ```
10
+
11
+ It'll start a docker-compose in dev mode (API + Mongo)
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ DIR=$PROJECT_SRC
3
+ echo CHECKING $DIR
4
+
5
+ if [ -f "$DIR/package.json" ]; then
6
+ echo "Project already initialized"
7
+ else
8
+ ### Control will jump here if $DIR does NOT exists ###
9
+ echo "Configuring project..."
10
+ cd "$( dirname "${BASH_SOURCE[0]}" )"
11
+ cd ..
12
+ pwd
13
+ mkdir -p $PROJECT_SRC
14
+
15
+ echo Unzipping project starter to $DIR
16
+ unzip ./project-api-starter.zip -d $DIR
17
+
18
+ echo dir before script
19
+ ls $PROJECT_SRC
20
+ fi
21
+
22
+ exit 1
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ docker-compose up --build
@@ -0,0 +1,118 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ // Function to create directories recursively
5
+ function mkdirRecursive(dir) {
6
+ if (!fs.existsSync(dir)) {
7
+ fs.mkdirSync(dir, { recursive: true });
8
+ }
9
+ }
10
+
11
+ // Function to convert JSON schema to JavaScript code
12
+ function convertJsonToJs(schema) {
13
+ let jsCode = "const Joi = require('joi');\n\n";
14
+ jsCode += `const ${schema.name} = Joi.object({\n`;
15
+ for (const [key, value] of Object.entries(schema.schema)) {
16
+ if (typeof value === "object" && !Array.isArray(value)) {
17
+ jsCode += ` ${key}: Joi.object({\n`;
18
+ for (const [subKey, subValue] of Object.entries(value)) {
19
+ jsCode += ` ${subKey}: ${subValue},\n`;
20
+ }
21
+ jsCode += ` }),\n`;
22
+ } else {
23
+ jsCode += ` ${key}: ${value},\n`;
24
+ }
25
+ }
26
+ jsCode += "});\n\n";
27
+ jsCode += `module.exports = ${schema.name};\n`;
28
+ return jsCode;
29
+ }
30
+
31
+ // Function to generate endpoint code
32
+ function createEndpointCode(endpoint) {
33
+ let requestSchema = "const Joi = require('joi');\n\n";
34
+ requestSchema += "module.exports.requestSchema = Joi.object({\n";
35
+ for (const [key, value] of Object.entries(endpoint.requestSchema)) {
36
+ requestSchema += ` ${key}: ${value},\n`;
37
+ }
38
+ requestSchema += "});\n\n";
39
+
40
+ return `
41
+ ${endpoint.handler}
42
+
43
+ ${requestSchema}
44
+ module.exports.endpoint = ${JSON.stringify(endpoint.endpoint, null, 2)};
45
+ `;
46
+ }
47
+
48
+ // Main function to generate the project structure
49
+ function generateProjectStructure(resources) {
50
+ resources.forEach((resource) => {
51
+ const resourceDir = path.join(__dirname, "src", "resources", resource.name);
52
+ mkdirRecursive(resourceDir);
53
+
54
+ // Create schemas directory and files
55
+ if (resource.schemas) {
56
+ const schemaDir = path.join(resourceDir);
57
+ mkdirRecursive(schemaDir);
58
+ resource.schemas.forEach((schema) => {
59
+ const schemaCode = convertJsonToJs(schema);
60
+ fs.writeFileSync(
61
+ path.join(schemaDir, `${schema.name.toLowerCase()}.schema.js`),
62
+ schemaCode
63
+ );
64
+ });
65
+ }
66
+
67
+ // Create endpoints directory and files
68
+ if (resource.endpoints) {
69
+ const endpointDir = path.join(resourceDir, "endpoints");
70
+ mkdirRecursive(endpointDir);
71
+ resource.endpoints.forEach((endpoint) => {
72
+ const endpointCode = createEndpointCode(endpoint);
73
+ fs.writeFileSync(
74
+ path.join(endpointDir, `${endpoint.name}.js`),
75
+ endpointCode
76
+ );
77
+ });
78
+ }
79
+
80
+ // Create handlers directory and files
81
+ if (resource.handlers) {
82
+ const handlerDir = path.join(resourceDir, "handlers");
83
+ mkdirRecursive(handlerDir);
84
+ resource.handlers.forEach((handler) => {
85
+ const handlerCode = handler.handler;
86
+ fs.writeFileSync(
87
+ path.join(handlerDir, `${handler.name}.js`),
88
+ handlerCode
89
+ );
90
+ });
91
+ }
92
+
93
+ // Create methods directory and files
94
+ if (resource.methods) {
95
+ const methodDir = path.join(resourceDir, "methods");
96
+ mkdirRecursive(methodDir);
97
+ resource.methods.forEach((method) => {
98
+ const methodCode = method.handler;
99
+ fs.writeFileSync(path.join(methodDir, `${method.name}.js`), methodCode);
100
+ });
101
+ }
102
+ });
103
+ }
104
+
105
+ // Main entry point
106
+ const [, , jsonFilePath] = process.argv;
107
+
108
+ if (!jsonFilePath) {
109
+ console.error("Please provide the path to the JSON file.");
110
+ process.exit(1);
111
+ }
112
+
113
+ const rawData = fs.readFileSync(jsonFilePath, "utf8");
114
+ const { resources } = JSON.parse(rawData);
115
+
116
+ generateProjectStructure(resources);
117
+
118
+ console.log("Hive-based project structure generated successfully.");
@@ -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
+ }
@@ -0,0 +1,32 @@
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-----