@gnar-engine/cli 1.0.4 → 1.0.6
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/bootstrap/deploy.localdev.yml +44 -3
- package/bootstrap/secrets.localdev.yml +20 -5
- package/bootstrap/services/control/src/config.js +4 -0
- package/bootstrap/services/notification/Dockerfile +2 -2
- package/bootstrap/services/notification/package.json +14 -32
- package/bootstrap/services/notification/src/app.js +50 -48
- package/bootstrap/services/notification/src/commands/notification.handler.js +96 -0
- package/bootstrap/services/notification/src/config.js +55 -12
- package/bootstrap/services/notification/src/controllers/http.controller.js +87 -0
- package/bootstrap/services/notification/src/controllers/message.controller.js +39 -70
- package/bootstrap/services/notification/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/notification/src/db/migrations/02-notification-service-init.js +23 -0
- package/bootstrap/services/notification/src/policies/notification.policy.js +49 -0
- package/bootstrap/services/notification/src/schema/notification.schema.js +17 -0
- package/bootstrap/services/notification/src/services/notification.service.js +32 -0
- package/bootstrap/services/page/Dockerfile +23 -0
- package/bootstrap/services/page/package.json +16 -0
- package/bootstrap/services/page/src/app.js +50 -0
- package/bootstrap/services/page/src/commands/block.handler.js +94 -0
- package/bootstrap/services/page/src/commands/page.handler.js +167 -0
- package/bootstrap/services/page/src/config.js +62 -0
- package/bootstrap/services/page/src/controllers/block.http.controller.js +87 -0
- package/bootstrap/services/page/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/page/src/controllers/page.http.controller.js +89 -0
- package/bootstrap/services/page/src/policies/block.policy.js +50 -0
- package/bootstrap/services/page/src/policies/page.policy.js +49 -0
- package/bootstrap/services/page/src/schema/page.schema.js +139 -0
- package/bootstrap/services/page/src/services/block.service.js +83 -0
- package/bootstrap/services/page/src/services/page.service.js +83 -0
- package/bootstrap/services/portal/Dockerfile +20 -0
- package/bootstrap/services/portal/README.md +73 -0
- package/bootstrap/services/portal/index.html +13 -0
- package/bootstrap/services/portal/nginx.conf +5 -0
- package/bootstrap/services/portal/package.json +33 -0
- package/bootstrap/services/portal/public/vite.svg +1 -0
- package/bootstrap/services/portal/react-router.config.js +7 -0
- package/bootstrap/services/portal/src/App.jsx +16 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-white-logo.svg +9 -0
- package/bootstrap/services/portal/src/assets/icon-agent.svg +6 -0
- package/bootstrap/services/portal/src/assets/icon-cog.svg +4 -0
- package/bootstrap/services/portal/src/assets/icon-delete.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-home.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-padlock.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-page.svg +6 -0
- package/bootstrap/services/portal/src/assets/icon-reports.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-user.svg +3 -0
- package/bootstrap/services/portal/src/assets/icon-users.svg +3 -0
- package/bootstrap/services/portal/src/assets/login-green-rad-back-1.jpg +0 -0
- package/bootstrap/services/portal/src/assets/react.svg +1 -0
- package/bootstrap/services/portal/src/components/CrudList/CrudList.jsx +85 -0
- package/bootstrap/services/portal/src/components/CrudList/CrudList.less +59 -0
- package/bootstrap/services/portal/src/components/CustomSelect/CustomSelect.jsx +81 -0
- package/bootstrap/services/portal/src/components/LoginForm/LoginForm.jsx +58 -0
- package/bootstrap/services/portal/src/components/PageBlockSwitch/PageBlockSwitch.jsx +129 -0
- package/bootstrap/services/portal/src/components/Sidebar/Sidebar.jsx +33 -0
- package/bootstrap/services/portal/src/components/Sidebar/Sidebar.less +37 -0
- package/bootstrap/services/portal/src/components/Topbar/Topbar.jsx +19 -0
- package/bootstrap/services/portal/src/components/Topbar/Topbar.less +22 -0
- package/bootstrap/services/portal/src/components/UserInfo/UserInfo.jsx +33 -0
- package/bootstrap/services/portal/src/components/UserInfo/UserInfo.less +21 -0
- package/bootstrap/services/portal/src/css/style.css +711 -0
- package/bootstrap/services/portal/src/data/pages.data.js +10 -0
- package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.jsx +65 -0
- package/bootstrap/services/portal/src/elements/CustomSelect/CustomSelect.less +102 -0
- package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.jsx +115 -0
- package/bootstrap/services/portal/src/elements/ImageInput/ImageInput.less +43 -0
- package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.jsx +124 -0
- package/bootstrap/services/portal/src/elements/ImageMultiInput/ImageMultiInput.less +0 -0
- package/bootstrap/services/portal/src/elements/Repeater/Repeater.jsx +52 -0
- package/bootstrap/services/portal/src/elements/Repeater/Repeater.less +70 -0
- package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.jsx +18 -0
- package/bootstrap/services/portal/src/elements/RichTextInput/RichTextInput.less +37 -0
- package/bootstrap/services/portal/src/elements/SaveButton/SaveButton.jsx +45 -0
- package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.jsx +63 -0
- package/bootstrap/services/portal/src/elements/SelectRepeater/SelectRepeater.less +23 -0
- package/bootstrap/services/portal/src/elements/TextInput/TextInput.jsx +17 -0
- package/bootstrap/services/portal/src/layouts/Card/Card.jsx +15 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.jsx +29 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout/PortalLayout.less +49 -0
- package/bootstrap/services/portal/src/main.jsx +51 -0
- package/bootstrap/services/portal/src/pages/BlockSinglePage/BlockSinglePage.jsx +277 -0
- package/bootstrap/services/portal/src/pages/BlocksPage/BlocksPage.jsx +23 -0
- package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.jsx +11 -0
- package/bootstrap/services/portal/src/pages/DashboardPage/DashboardPage.less +0 -0
- package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.jsx +21 -0
- package/bootstrap/services/portal/src/pages/LoginPage/LoginPage.less +51 -0
- package/bootstrap/services/portal/src/pages/PageSinglePage/PageSinglePage.jsx +338 -0
- package/bootstrap/services/portal/src/pages/PagesPage/PagesPage.jsx +23 -0
- package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.jsx +9 -0
- package/bootstrap/services/portal/src/pages/UserSinglePage/UserSinglePage.less +0 -0
- package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.jsx +25 -0
- package/bootstrap/services/portal/src/pages/UsersPage/UsersPage.less +0 -0
- package/bootstrap/services/portal/src/services/block.js +28 -0
- package/bootstrap/services/portal/src/services/client.js +70 -0
- package/bootstrap/services/portal/src/services/gravatar.js +14 -0
- package/bootstrap/services/portal/src/services/page.js +28 -0
- package/bootstrap/services/portal/src/services/storage.js +62 -0
- package/bootstrap/services/portal/src/services/user.js +41 -0
- package/bootstrap/services/portal/src/slices/authSlice.js +101 -0
- package/bootstrap/services/portal/src/store/configureStore.js +10 -0
- package/bootstrap/services/portal/src/style/cards.less +57 -0
- package/bootstrap/services/portal/src/style/global.less +204 -0
- package/bootstrap/services/portal/src/style/icons.less +21 -0
- package/bootstrap/services/portal/src/style/inputs.less +52 -0
- package/bootstrap/services/portal/src/style/main.less +28 -0
- package/bootstrap/services/portal/src/utils/utils.js +9 -0
- package/bootstrap/services/portal/vite.config.js +12 -0
- package/bootstrap/services/user/src/app.js +6 -1
- package/bootstrap/services/user/src/commands/user.handler.js +35 -21
- package/bootstrap/services/user/src/config.js +5 -1
- package/bootstrap/services/user/src/policies/user.policy.js +3 -1
- package/bootstrap/services/user/src/tests/commands/user.test.js +31 -0
- package/install-from-clone.sh +30 -0
- package/package.json +1 -1
- package/src/cli.js +2 -0
- package/src/config.js +8 -0
- package/src/dev/commands.js +11 -3
- package/src/dev/dev.service.js +164 -64
- package/src/helpers/helpers.js +24 -0
- package/src/profiles/command.js +41 -0
- package/src/profiles/profiles.client.js +23 -0
- package/src/provisioner/Dockerfile +27 -0
- package/src/provisioner/package.json +19 -0
- package/src/provisioner/src/app.js +56 -0
- package/src/provisioner/src/services/mongodb.js +58 -0
- package/src/provisioner/src/services/mysql.js +51 -0
- package/src/provisioner/src/services/secrets.js +84 -0
- package/src/scaffolder/commands.js +58 -2
- package/src/scaffolder/scaffolder.handler.js +164 -72
- package/templates/entity/src/commands/{{entityName}}.handler.js.hbs +94 -0
- package/templates/entity/src/controllers/{{entityName}}.http.controller.js.hbs +87 -0
- package/templates/entity/src/mysql.db/migrations/03-{{entityName}}-entity-init.js.hbs +23 -0
- package/templates/entity/src/policies/{{entityName}}.policy.js.hbs +49 -0
- package/templates/entity/src/schema/{{entityName}}.schema.js.hbs +17 -0
- package/templates/entity/src/services/mongodb.{{entityName}}.service.js.hbs +70 -0
- package/templates/entity/src/services/mysql.{{entityName}}.service.js.hbs +27 -0
- package/templates/service/src/app.js.hbs +12 -1
- package/templates/service/src/commands/{{serviceName}}.handler.js.hbs +1 -1
- package/templates/service/src/mongodb.config.js.hbs +5 -1
- package/templates/service/src/mysql.config.js.hbs +4 -0
- package/bootstrap/services/notification/Dockerfile.prod +0 -37
- package/bootstrap/services/notification/README.md +0 -3
- package/bootstrap/services/notification/src/commands/command-bus.js +0 -20
- package/bootstrap/services/notification/src/commands/handlers/control.handler.js +0 -18
- package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +0 -157
- package/bootstrap/services/notification/src/services/logger.service.js +0 -16
- package/bootstrap/services/notification/src/services/ses.service.js +0 -23
- package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +0 -136
- package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +0 -87
- package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +0 -132
- package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +0 -77
- package/bootstrap/services/user/src/tests/user.test.js +0 -126
- /package/bootstrap/services/{notification/src/tests/notification.test.js → portal/src/components/CustomSelect/CustomSelect.less} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { logger, db } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Up
|
|
5
|
+
*/
|
|
6
|
+
export const up = async () => {
|
|
7
|
+
await initDatabaseTables();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Down
|
|
12
|
+
*/
|
|
13
|
+
export const down = async () => {
|
|
14
|
+
await dropDatabaseTables();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create all tables
|
|
19
|
+
*/
|
|
20
|
+
export const initDatabaseTables = async () => {
|
|
21
|
+
|
|
22
|
+
// Migrations table
|
|
23
|
+
logger.info("Creating migrations table");
|
|
24
|
+
const createMigrationsTableQuery = `
|
|
25
|
+
CREATE TABLE migrations (
|
|
26
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
27
|
+
name VARCHAR(255) NOT NULL UNIQUE,
|
|
28
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
29
|
+
)`;
|
|
30
|
+
await db.query(createMigrationsTableQuery);
|
|
31
|
+
|
|
32
|
+
// Seeders table
|
|
33
|
+
logger.info("Creating seeders table");
|
|
34
|
+
const createSeedersTableQuery = `
|
|
35
|
+
CREATE TABLE seeders (
|
|
36
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
37
|
+
name VARCHAR(255) NOT NULL UNIQUE,
|
|
38
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
39
|
+
)`;
|
|
40
|
+
await db.query(createSeedersTableQuery);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Drop all tables
|
|
45
|
+
*/
|
|
46
|
+
export const dropDatabaseTables = async () => {
|
|
47
|
+
logger.info('Dropping tables');
|
|
48
|
+
await db.query('DROP TABLE IF EXISTS migrations');
|
|
49
|
+
await db.query('DROP TABLE IF EXISTS seeders');
|
|
50
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { logger, db } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Up
|
|
5
|
+
*/
|
|
6
|
+
export const up = async () => {
|
|
7
|
+
logger.info('Creating table: notifications');
|
|
8
|
+
await db.query(`
|
|
9
|
+
CREATE TABLE notifications (
|
|
10
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
11
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
12
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
13
|
+
)
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Down
|
|
19
|
+
*/
|
|
20
|
+
export const down = async () => {
|
|
21
|
+
logger.info('Dropping table: notifications');
|
|
22
|
+
await db.query('DROP TABLE IF EXISTS notifications');
|
|
23
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
|
|
3
|
+
export const authorise = {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authorise get single notification
|
|
7
|
+
*/
|
|
8
|
+
getSingle: async (request, reply) => {
|
|
9
|
+
if (!request.user || request.user.role !== 'service_admin') {
|
|
10
|
+
reply.code(403).send({error: 'not authorised'});
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Authorise get many notifications
|
|
16
|
+
*/
|
|
17
|
+
getMany: async (request, reply) => {
|
|
18
|
+
if (!request.user || request.user.role !== 'service_admin') {
|
|
19
|
+
reply.code(403).send({error: 'not authorised'});
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Authorise create notifications
|
|
25
|
+
*/
|
|
26
|
+
create: async (request, reply) => {
|
|
27
|
+
if (!request.user || request.user.role !== 'service_admin') {
|
|
28
|
+
reply.code(403).send({error: 'not authorised'});
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Authorise update notification
|
|
34
|
+
*/
|
|
35
|
+
update: async (request, reply) => {
|
|
36
|
+
if (!request.user || request.user.role !== 'service_admin') {
|
|
37
|
+
reply.code(403).send({error: 'not authorised'});
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Authorise delete notification
|
|
43
|
+
*/
|
|
44
|
+
delete: async (request, reply) => {
|
|
45
|
+
if (!request.user || request.user.role !== 'service_admin') {
|
|
46
|
+
reply.code(403).send({error: 'not authorised'});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { schema } from '@gnar-engine/core';
|
|
2
|
+
import { config } from '../config.js';
|
|
3
|
+
|
|
4
|
+
export const notificationSchema = {
|
|
5
|
+
schemaName: 'notificationService.notificationSchema',
|
|
6
|
+
schema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
// Add your properties here
|
|
10
|
+
|
|
11
|
+
},
|
|
12
|
+
required: [],
|
|
13
|
+
additionalProperties: false
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const validateNotification = schema.compile(notificationSchema);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { db } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
export const notification = {
|
|
4
|
+
async getById({ id }) {
|
|
5
|
+
const [result] = await db.query('SELECT * FROM notifications WHERE id = ?', [id]);
|
|
6
|
+
return result || null;
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
async getByEmail({ email }) {
|
|
10
|
+
// Placeholder: implement if your service uses email
|
|
11
|
+
return null;
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
async getAll() {
|
|
15
|
+
return await db.query('SELECT * FROM notifications');
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async create(data) {
|
|
19
|
+
const { insertId } = await db.query('INSERT INTO notifications (created_at, updated_at) VALUES (CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)');
|
|
20
|
+
return await this.getById({ id: insertId });
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
async update({ id, ...data }) {
|
|
24
|
+
await db.query('UPDATE notifications SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', [id]);
|
|
25
|
+
return await this.getById({ id });
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
async delete({ id }) {
|
|
29
|
+
await db.query('DELETE FROM notifications WHERE id = ?', [id]);
|
|
30
|
+
return true;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Dockerfile for Page Service
|
|
2
|
+
FROM node:20-alpine
|
|
3
|
+
|
|
4
|
+
# Set the working directory
|
|
5
|
+
WORKDIR /usr/gnar_engine/app
|
|
6
|
+
|
|
7
|
+
# Define a global env var
|
|
8
|
+
ENV GLOBAL_SERVICE_BASE_DIR=/usr/gnar_engine/app/src/
|
|
9
|
+
|
|
10
|
+
# Copy package.json and package-lock.json
|
|
11
|
+
COPY ./services/page/package*.json ./
|
|
12
|
+
|
|
13
|
+
# Install nodemon
|
|
14
|
+
RUN npm install -g nodemon
|
|
15
|
+
|
|
16
|
+
# Install app dependencies
|
|
17
|
+
RUN npm install
|
|
18
|
+
|
|
19
|
+
# Expose the port the service will run on
|
|
20
|
+
EXPOSE 3000
|
|
21
|
+
|
|
22
|
+
# Start the application
|
|
23
|
+
CMD ["nodemon", "--watch", "./gnar_engine", "./gnar_engine/app.js"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "page-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Page microservice for Gnar Engine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./src/app.js",
|
|
8
|
+
"start:dev": "nodemon --watch ./src ./src/app.js",
|
|
9
|
+
"test": "jest --watchAll --verbose"
|
|
10
|
+
},
|
|
11
|
+
"author": "Gnar Software Ltd.",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@gnar-engine/core": "^1.0.1"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { message, http, logger, db, registerService, webSockets, test } from '@gnar-engine/core';
|
|
2
|
+
import { config } from './config.js';
|
|
3
|
+
import { messageHandlers } from './controllers/message.controller.js';
|
|
4
|
+
import { httpController as pagePlatformHttpController } from './controllers/page.http.controller.js';
|
|
5
|
+
import { httpController as blockPlatformHttpController } from './controllers/block.http.controller.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Initialise service
|
|
9
|
+
*/
|
|
10
|
+
export const initService = async () => {
|
|
11
|
+
|
|
12
|
+
// Run seeders
|
|
13
|
+
db.seeders.runSeeders({config});
|
|
14
|
+
|
|
15
|
+
// Import command handlers after the command bus is initialised
|
|
16
|
+
await import('./commands/page.handler.js');
|
|
17
|
+
await import('./commands/block.handler.js');
|
|
18
|
+
|
|
19
|
+
// Initialise and register message handlers
|
|
20
|
+
await message.init({
|
|
21
|
+
config: config.message,
|
|
22
|
+
handlers: messageHandlers
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Initialise websocket client & server
|
|
26
|
+
await webSockets.init(config.webSockets, config.serviceName);
|
|
27
|
+
|
|
28
|
+
// Register http routes
|
|
29
|
+
await http.registerRoutes({
|
|
30
|
+
controllers: [
|
|
31
|
+
pagePlatformHttpController,
|
|
32
|
+
blockPlatformHttpController
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Start the HTTP server
|
|
37
|
+
await http.start();
|
|
38
|
+
|
|
39
|
+
// Register service with control service
|
|
40
|
+
await registerService();
|
|
41
|
+
|
|
42
|
+
logger.info('G n a r E n g i n e | Page Service initialised successfully.');
|
|
43
|
+
|
|
44
|
+
// Tests
|
|
45
|
+
if (config.environment === 'test' && config.runTests) {
|
|
46
|
+
test.runCommandTests({config});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
initService();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { commands, logger, error } from '@gnar-engine/core';
|
|
2
|
+
import { block } from '../services/block.service.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
import { validateBlock } from '../schema/page.schema.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get single block
|
|
9
|
+
*/
|
|
10
|
+
commands.register('pageService.getSingleBlock', async ({id}) => {
|
|
11
|
+
if (id) {
|
|
12
|
+
return await block.getById({id: id});
|
|
13
|
+
} else {
|
|
14
|
+
throw new error.badRequest('Block id required');
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get many blocks
|
|
20
|
+
*/
|
|
21
|
+
commands.register('pageService.getManyBlocks', async ({}) => {
|
|
22
|
+
return await block.getAll();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create blocks
|
|
27
|
+
*/
|
|
28
|
+
commands.register('pageService.createBlocks', async ({ blocks }) => {
|
|
29
|
+
const validationErrors = [];
|
|
30
|
+
let createdNewBlocks = [];
|
|
31
|
+
|
|
32
|
+
for (const newData of blocks) {
|
|
33
|
+
const { errors } = validateBlock(newData);
|
|
34
|
+
if (errors?.length) {
|
|
35
|
+
validationErrors.push(errors);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const created = await block.create(newData);
|
|
40
|
+
createdNewBlocks.push(created);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (validationErrors.length) {
|
|
44
|
+
throw new error.badRequest(`Invalid block data: ${validationErrors}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return createdNewBlocks;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Update block
|
|
52
|
+
*/
|
|
53
|
+
commands.register('pageService.updateBlock', async ({id, newBlockData}) => {
|
|
54
|
+
|
|
55
|
+
const validationErrors = [];
|
|
56
|
+
|
|
57
|
+
if (!id) {
|
|
58
|
+
throw new error.badRequest('Block ID required');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const obj = await block.getById({id: id});
|
|
62
|
+
|
|
63
|
+
if (!obj) {
|
|
64
|
+
throw new error.notFound('Block not found');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
delete newBlockData.id;
|
|
68
|
+
|
|
69
|
+
const { errors } = validateBlock(newBlockData);
|
|
70
|
+
|
|
71
|
+
if (errors?.length) {
|
|
72
|
+
validationErrors.push(errors);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (validationErrors.length) {
|
|
76
|
+
throw new error.badRequest(`Invalid block data: ${validationErrors}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return await block.update({
|
|
80
|
+
id: id,
|
|
81
|
+
updatedData: newBlockData
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Delete block
|
|
87
|
+
*/
|
|
88
|
+
commands.register('pageService.deleteBlock', async ({id}) => {
|
|
89
|
+
const obj = await block.getById({id: id});
|
|
90
|
+
if (!obj) {
|
|
91
|
+
throw new error.notFound('Block not found');
|
|
92
|
+
}
|
|
93
|
+
return await block.delete({id: id});
|
|
94
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { commands, logger, error, storage } from '@gnar-engine/core';
|
|
2
|
+
import { page } from '../services/page.service.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
import { validatePage } from '../schema/page.schema.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get single page
|
|
9
|
+
*/
|
|
10
|
+
commands.register('pageService.getSinglePage', async ({id}) => {
|
|
11
|
+
if (id) {
|
|
12
|
+
return await page.getById({id: id});
|
|
13
|
+
} else {
|
|
14
|
+
throw new error.badRequest('Page email or id required');
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get many pages
|
|
20
|
+
*/
|
|
21
|
+
commands.register('pageService.getManyPages', async ({}) => {
|
|
22
|
+
return await page.getAll();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create pages
|
|
27
|
+
*/
|
|
28
|
+
commands.register('pageService.createPages', async ({ pages, requestUser }) => {
|
|
29
|
+
const validationErrors = [];
|
|
30
|
+
let createdNewPages = [];
|
|
31
|
+
|
|
32
|
+
for (const newData of pages) {
|
|
33
|
+
const { errors } = validatePage(newData);
|
|
34
|
+
if (errors?.length) {
|
|
35
|
+
validationErrors.push(errors);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
newData = await commands.execute('processUploadsInData', { data: newData, requestUser });
|
|
40
|
+
|
|
41
|
+
const created = await page.create(newData);
|
|
42
|
+
createdNewPages.push(created);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (validationErrors.length) {
|
|
46
|
+
throw new error.badRequest(`Invalid page data: ${validationErrors}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return createdNewPages;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Update page
|
|
54
|
+
*/
|
|
55
|
+
commands.register('pageService.updatePage', async ({id, newPageData, requestUser}) => {
|
|
56
|
+
|
|
57
|
+
const validationErrors = [];
|
|
58
|
+
|
|
59
|
+
if (!id) {
|
|
60
|
+
throw new error.badRequest('Page ID required');
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const obj = await page.getById({id: id});
|
|
65
|
+
|
|
66
|
+
if (!obj) {
|
|
67
|
+
throw new error.notFound('Page not found');
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
delete newPageData.id;
|
|
72
|
+
|
|
73
|
+
const { errors } = validatePage(newPageData);
|
|
74
|
+
|
|
75
|
+
if (errors?.length) {
|
|
76
|
+
validationErrors.push(errors);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (validationErrors.length) {
|
|
80
|
+
throw new error.badRequest(`Invalid page data: ${validationErrors}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
newPageData = await commands.execute('processUploadsInData', { data: newPageData, requestUser });
|
|
84
|
+
|
|
85
|
+
return await page.update({
|
|
86
|
+
id: id,
|
|
87
|
+
updatedData: newPageData
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Delete page
|
|
93
|
+
*/
|
|
94
|
+
commands.register('pageService.deletePage', async ({id}) => {
|
|
95
|
+
const obj = await page.getById({id: id});
|
|
96
|
+
if (!obj) {
|
|
97
|
+
throw new error.notFound('Page not found');
|
|
98
|
+
}
|
|
99
|
+
return await page.delete({id: id});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Find file and image uploads and store them
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} data - The data object to search for files/images
|
|
107
|
+
* @param {Function} uploadFn - Function to handle the actual upload process
|
|
108
|
+
* @returns {Object} - The updated data object with stored file/image references
|
|
109
|
+
*/
|
|
110
|
+
commands.register('pageService.processUploadsInData', async ({ data, requestUser }) => {
|
|
111
|
+
|
|
112
|
+
const uploadFilesRecursive = async (data) => {
|
|
113
|
+
if (Array.isArray(data)) {
|
|
114
|
+
return Promise.all(data.map(item => uploadFilesRecursive(item)));
|
|
115
|
+
} else if (data && typeof data === 'object') {
|
|
116
|
+
const result = { ...data };
|
|
117
|
+
|
|
118
|
+
for (const [key, value] of Object.entries(data)) {
|
|
119
|
+
if (key === 'file' && typeof value === 'string') {
|
|
120
|
+
|
|
121
|
+
logger.info('Processing file upload in page data');
|
|
122
|
+
|
|
123
|
+
// Filename
|
|
124
|
+
const fileName = result.fileName || `upload_${Date.now()}`;
|
|
125
|
+
|
|
126
|
+
// Mime type
|
|
127
|
+
let mimeType = result.mimeType;
|
|
128
|
+
let base64Data = value;
|
|
129
|
+
|
|
130
|
+
const matches = value.match(/^data:(.+);base64,(.+)$/);
|
|
131
|
+
if (matches) {
|
|
132
|
+
mimeType = mimeType || matches[1];
|
|
133
|
+
base64Data = matches[2];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!mimeType) mimeType = 'application/octet-stream';
|
|
137
|
+
|
|
138
|
+
// Upload
|
|
139
|
+
const url = await storage.upload({
|
|
140
|
+
file: Buffer.from(base64Data, 'base64'),
|
|
141
|
+
key: 'public/page-content/' + fileName,
|
|
142
|
+
contentType: mimeType,
|
|
143
|
+
metadata: {
|
|
144
|
+
uploadedAt: new Date().toISOString(),
|
|
145
|
+
uploadedBy: requestUser ? requestUser.id : 'unknown'
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Add url and remove upload keys
|
|
150
|
+
result.url = url;
|
|
151
|
+
delete result.file;
|
|
152
|
+
if (result.fileName) delete result.fileName;
|
|
153
|
+
if (result.mimeType) delete result.mimeType;
|
|
154
|
+
|
|
155
|
+
} else {
|
|
156
|
+
result[key] = await uploadFilesRecursive(value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return data;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return await uploadFilesRecursive(data);
|
|
167
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnar Engine Service Config
|
|
3
|
+
*/
|
|
4
|
+
export const config = {
|
|
5
|
+
// service name
|
|
6
|
+
serviceName: 'pageService',
|
|
7
|
+
|
|
8
|
+
// microservice | modular-monolith
|
|
9
|
+
architecture: process.env.GLOBAL_ARCHITECTURE || 'microservice',
|
|
10
|
+
|
|
11
|
+
// web server
|
|
12
|
+
http: {
|
|
13
|
+
allowedOrigins: [],
|
|
14
|
+
allowedMethods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
15
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
16
|
+
rateLimiting: {
|
|
17
|
+
max: 5,
|
|
18
|
+
timeWindow: '1 minute',
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// database
|
|
23
|
+
db: {
|
|
24
|
+
// type: mongodb | mysql
|
|
25
|
+
type: 'mongodb',
|
|
26
|
+
|
|
27
|
+
// MongoDB
|
|
28
|
+
host: process.env.PAGE_MONGO_HOST,
|
|
29
|
+
database: process.env.PAGE_MONGO_DATABASE,
|
|
30
|
+
user: process.env.PAGE_MONGO_USER,
|
|
31
|
+
password: process.env.PAGE_MONGO_PASSWORD,
|
|
32
|
+
port: process.env.PAGE_MONGO_PORT || 27017,
|
|
33
|
+
connectionArgs: {},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// storage
|
|
37
|
+
storage: {
|
|
38
|
+
// driver: s3
|
|
39
|
+
driver: 's3',
|
|
40
|
+
uploadsUrl: process.env.UPLOADS_URL,
|
|
41
|
+
|
|
42
|
+
// s3
|
|
43
|
+
bucket: process.env.S3_BUCKET,
|
|
44
|
+
region: process.env.AWS_REGION,
|
|
45
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
46
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// message broker
|
|
50
|
+
message: {
|
|
51
|
+
url: process.env.RABBITMQ_URL,
|
|
52
|
+
queueName: 'pageServiceQueue',
|
|
53
|
+
prefetch: 20
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
webSockets: {
|
|
57
|
+
reconnectInterval: 5000,
|
|
58
|
+
maxInitialConnectionAttempts: 5
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
hashNameSpace: '',
|
|
62
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { commands } from '@gnar-engine/core';
|
|
2
|
+
import { authorise } from '../policies/block.policy.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HTTP controller
|
|
6
|
+
*/
|
|
7
|
+
export const httpController = {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get single block
|
|
11
|
+
*/
|
|
12
|
+
getSingle: {
|
|
13
|
+
method: 'GET',
|
|
14
|
+
url: '/blocks/:id',
|
|
15
|
+
preHandler: async (request, reply) => authorise.getSingle(request, reply),
|
|
16
|
+
handler: async (request, reply) => {
|
|
17
|
+
const params = {
|
|
18
|
+
id: request.params.id
|
|
19
|
+
};
|
|
20
|
+
const result = await commands.execute('getSingleBlock', params);
|
|
21
|
+
reply.code(200).send({ block: result });
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get multiple blocks
|
|
27
|
+
*/
|
|
28
|
+
getMany: {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
url: '/blocks/',
|
|
31
|
+
preHandler: async (request, reply) => authorise.getMany(request, reply),
|
|
32
|
+
handler: async (request, reply) => {
|
|
33
|
+
const params = {};
|
|
34
|
+
const results = await commands.execute('getManyBlocks', params);
|
|
35
|
+
reply.code(200).send({ blocks: results });
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create new block
|
|
41
|
+
*/
|
|
42
|
+
create: {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
url: '/blocks/',
|
|
45
|
+
preHandler: async (request, reply) => authorise.create(request, reply),
|
|
46
|
+
handler: async (request, reply) => {
|
|
47
|
+
const params = {
|
|
48
|
+
blocks: [request.body.block]
|
|
49
|
+
};
|
|
50
|
+
const results = await commands.execute('createBlocks', params);
|
|
51
|
+
reply.code(200).send({ blocks: results });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Update block
|
|
57
|
+
*/
|
|
58
|
+
update: {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
url: '/blocks/:id',
|
|
61
|
+
preHandler: async (request, reply) => authorise.update(request, reply),
|
|
62
|
+
handler: async (request, reply) => {
|
|
63
|
+
const params = {
|
|
64
|
+
id: request.params.id,
|
|
65
|
+
newBlockData: request.body.block
|
|
66
|
+
};
|
|
67
|
+
const result = await commands.execute('updateBlock', params);
|
|
68
|
+
reply.code(200).send({ page: result });
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete block
|
|
74
|
+
*/
|
|
75
|
+
delete: {
|
|
76
|
+
method: 'DELETE',
|
|
77
|
+
url: '/blocks/:id',
|
|
78
|
+
preHandler: async (request, reply) => authorise.delete(request, reply),
|
|
79
|
+
handler: async (request, reply) => {
|
|
80
|
+
const params = {
|
|
81
|
+
id: request.params.id
|
|
82
|
+
};
|
|
83
|
+
await commands.execute('deleteBlock', params);
|
|
84
|
+
reply.code(200).send({ message: 'Block deleted' });
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|