@gnar-engine/cli 1.0.5 → 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 +14 -0
- package/bootstrap/secrets.localdev.yml +7 -3
- 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/portal/src/services/client.js +3 -0
- package/bootstrap/services/user/src/commands/user.handler.js +35 -18
- package/bootstrap/services/user/src/tests/commands/user.test.js +15 -6
- package/install-from-clone.sh +1 -1
- package/package.json +1 -1
- package/src/cli.js +0 -6
- package/src/config.js +8 -0
- package/src/dev/commands.js +2 -2
- package/src/dev/dev.service.js +19 -6
- package/src/helpers/helpers.js +24 -0
- package/src/profiles/command.js +41 -0
- package/src/profiles/profiles.client.js +23 -0
- package/src/scaffolder/commands.js +57 -1
- package/src/scaffolder/scaffolder.handler.js +127 -60
- 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/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/notification/src/tests/notification.test.js +0 -0
|
@@ -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
|
+
};
|
|
@@ -121,31 +121,48 @@ commands.register('userService.getManyUsers', async ({}) => {
|
|
|
121
121
|
* Creat users with random password
|
|
122
122
|
*
|
|
123
123
|
* @param {Object} params
|
|
124
|
-
* @param {
|
|
124
|
+
* @param {Array} params.users - New user data
|
|
125
125
|
*/
|
|
126
|
-
commands.register('userService.createUserWithRandomPassword', async ({
|
|
126
|
+
commands.register('userService.createUserWithRandomPassword', async ({users}) => {
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const userData = {
|
|
131
|
-
...user,
|
|
132
|
-
password: password
|
|
133
|
-
};
|
|
128
|
+
const validationErrors = [];
|
|
129
|
+
let createdNewUsers = [];
|
|
134
130
|
|
|
135
|
-
|
|
131
|
+
// validate user data
|
|
132
|
+
for (const newUserData of users) {
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
// create random password
|
|
135
|
+
const password = Math.random().toString(36);
|
|
136
|
+
newUserData.password = password;
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
const { errors } = validateUser(newUserData);
|
|
139
|
+
|
|
140
|
+
if (errors?.length) {
|
|
141
|
+
validationErrors.push(errors);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!newUserData.role || newUserData.role !== 'service_admin') {
|
|
146
|
+
// ensure emails are unique
|
|
147
|
+
const existingUser = await user.getByEmail({email: newUserData.email});
|
|
148
|
+
|
|
149
|
+
if (existingUser) {
|
|
150
|
+
validationErrors.push(`User with email ${newUserData.email} already exists`);
|
|
151
|
+
}
|
|
143
152
|
}
|
|
144
|
-
|
|
145
|
-
return newUsers[0];
|
|
146
|
-
} catch (error) {
|
|
147
|
-
throw new error.badRequest('User creation failed: ' + error);
|
|
148
153
|
}
|
|
154
|
+
|
|
155
|
+
if (validationErrors.length) {
|
|
156
|
+
throw new error.badRequest(`Invalid user data: ${validationErrors}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// add users
|
|
160
|
+
for (const newUserData of users) {
|
|
161
|
+
const newUser = await user.create(newUserData);
|
|
162
|
+
createdNewUsers.push(newUser);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return createdNewUsers;
|
|
149
166
|
});
|
|
150
167
|
|
|
151
168
|
/**
|
|
@@ -8,15 +8,24 @@ test.prep(async () => {
|
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
// Test create user command
|
|
11
|
-
test.run('Create
|
|
12
|
-
const users = await commands.execute('createUsers', [
|
|
11
|
+
test.run('Create user command', async () => {
|
|
12
|
+
const users = await commands.execute('createUsers', { users: [
|
|
13
13
|
{
|
|
14
|
-
email: 'test@gnar.co.uk'
|
|
14
|
+
email: 'test@gnar.co.uk',
|
|
15
|
+
password: 'p4ssw0rd987'
|
|
15
16
|
}
|
|
16
|
-
]);
|
|
17
|
-
|
|
17
|
+
]});
|
|
18
18
|
test.assert(users.length === 1, 'User was not created successfully');
|
|
19
19
|
test.assert(users[0].email === 'test@gnar.co.uk', 'User email does not match');
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Test create user with random password command
|
|
23
|
+
test.run('Create user with random password command', async () => {
|
|
24
|
+
const users = await commands.execute('createUserWithRandomPassword', { users: [
|
|
25
|
+
{
|
|
26
|
+
email: 'test2@gnar.co.uk'
|
|
27
|
+
}
|
|
28
|
+
]});
|
|
29
|
+
test.assert(users.length === 1, 'User was not created successfully');
|
|
30
|
+
test.assert(users[0].email === 'test3@gnar.co.uk', 'User email does not match');
|
|
31
|
+
});
|
package/install-from-clone.sh
CHANGED
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -30,9 +30,3 @@ G n a r E n g i n e - A powerful, AI ready microservice framework for modern a
|
|
|
30
30
|
// Parse CLI input
|
|
31
31
|
program.parse(process.argv);
|
|
32
32
|
|
|
33
|
-
// Consts
|
|
34
|
-
export const directories = {
|
|
35
|
-
scaffolderTemplates: path.join(import.meta.dirname, '../templates/service'),
|
|
36
|
-
bootstrap: path.join(import.meta.dirname, '../bootstrap'),
|
|
37
|
-
provisioner: path.join(import.meta.dirname, './provisioner')
|
|
38
|
-
}
|
package/src/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
|
|
2
3
|
export const gnarEngineCliConfig = {
|
|
3
4
|
|
|
@@ -7,3 +8,10 @@ export const gnarEngineCliConfig = {
|
|
|
7
8
|
corePath: '/usr/gnar_engine/app/node_modules/@gnar-engine/core'
|
|
8
9
|
|
|
9
10
|
}
|
|
11
|
+
|
|
12
|
+
export const directories = {
|
|
13
|
+
scaffolderServiceTemplates: path.join(import.meta.dirname, '../templates/service'),
|
|
14
|
+
scaffolderEntityTemplates: path.join(import.meta.dirname, '../templates/entity'),
|
|
15
|
+
bootstrap: path.join(import.meta.dirname, '../bootstrap'),
|
|
16
|
+
provisioner: path.join(import.meta.dirname, './provisioner')
|
|
17
|
+
};
|
package/src/dev/commands.js
CHANGED
|
@@ -10,10 +10,10 @@ export function registerDevCommands(program) {
|
|
|
10
10
|
devCmd
|
|
11
11
|
.command('up')
|
|
12
12
|
.description('🛠️ Up Development Containers')
|
|
13
|
-
.option('-b, --build', '
|
|
13
|
+
.option('-b, --build', 'Build without cache')
|
|
14
14
|
.option('-d, --detach', 'Run containers in background')
|
|
15
15
|
.option('-t --test', 'Run the tests with ephemeral databases')
|
|
16
|
-
.option('--test-service <service>', 'Run the tests for the specified service with ephemeral databases')
|
|
16
|
+
.option('--test-service <service>', 'Run the tests for the specified service with ephemeral databases (e.g. --test-service user)')
|
|
17
17
|
.addOption(new Option('--core-dev').hideHelp())
|
|
18
18
|
.action(async (options) => {
|
|
19
19
|
let response = {};
|
package/src/dev/dev.service.js
CHANGED
|
@@ -5,7 +5,7 @@ import fs from "fs/promises";
|
|
|
5
5
|
import path from "path";
|
|
6
6
|
import yaml from "js-yaml";
|
|
7
7
|
import { gnarEngineCliConfig } from "../config.js";
|
|
8
|
-
import { directories } from "../
|
|
8
|
+
import { directories } from "../config.js";
|
|
9
9
|
|
|
10
10
|
const docker = new Docker();
|
|
11
11
|
|
|
@@ -17,13 +17,13 @@ const docker = new Docker();
|
|
|
17
17
|
* @param {object} options
|
|
18
18
|
* @param {string} options.projectDir - The project directory
|
|
19
19
|
* @param {boolean} [options.build=false] - Whether to re-build images
|
|
20
|
-
* @param {boolean} [options.
|
|
20
|
+
* @param {boolean} [options.detach=false] - Whether to run containers in background
|
|
21
21
|
* @param {boolean} [options.coreDev=false] - Whether to run in core development mode (requires access to core source)
|
|
22
22
|
* @param {boolean} [options.test=false] - Whether to run tests with ephemeral databases
|
|
23
23
|
* @param {string} [options.testService=''] - The service to run tests for (only applicable if test=true)
|
|
24
24
|
* @param {boolean} [options.removeOrphans=true] - Whether to remove orphaned containers
|
|
25
25
|
*/
|
|
26
|
-
export async function up({ projectDir, build = false,
|
|
26
|
+
export async function up({ projectDir, build = false, detach = false, coreDev = false, test = false, testService = '', removeOrphans = true }) {
|
|
27
27
|
|
|
28
28
|
// core dev
|
|
29
29
|
if (coreDev) {
|
|
@@ -74,7 +74,7 @@ export async function up({ projectDir, build = false, detached = false, coreDev
|
|
|
74
74
|
args.push("--build");
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (
|
|
77
|
+
if (detach) {
|
|
78
78
|
args.push("-d");
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -111,11 +111,11 @@ export async function up({ projectDir, build = false, detached = false, coreDev
|
|
|
111
111
|
*/
|
|
112
112
|
export async function down({ projectDir, allContainers = false }) {
|
|
113
113
|
// list all containers
|
|
114
|
-
|
|
114
|
+
let containers = await docker.listContainers();
|
|
115
115
|
|
|
116
116
|
// filter containers by image name
|
|
117
117
|
if (!allContainers) {
|
|
118
|
-
|
|
118
|
+
containers = containers.filter(c => c.Image.includes("ge-localdev"));
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
if (containers.length === 0) {
|
|
@@ -137,6 +137,18 @@ export async function down({ projectDir, allContainers = false }) {
|
|
|
137
137
|
});
|
|
138
138
|
})
|
|
139
139
|
);
|
|
140
|
+
|
|
141
|
+
// remove each container
|
|
142
|
+
await Promise.all(
|
|
143
|
+
containers.map(c => {
|
|
144
|
+
const container = docker.getContainer(c.Id);
|
|
145
|
+
return container.remove({ force: true }).catch(err => {
|
|
146
|
+
console.error(`Failed to remove ${c.Names[0]}: ${err.message}`);
|
|
147
|
+
});
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
console.log('Containers stopped and removed.');
|
|
140
152
|
}
|
|
141
153
|
|
|
142
154
|
/**
|
|
@@ -235,6 +247,7 @@ async function createDynamicDockerCompose({ config, secrets, gnarHiddenDir, proj
|
|
|
235
247
|
if (secrets.services?.[svc.name]) {
|
|
236
248
|
secrets.services[svc.name].NODE_ENV = 'test';
|
|
237
249
|
|
|
250
|
+
console.log(testService, svc.name);
|
|
238
251
|
if (testService && svc.name === testService) {
|
|
239
252
|
secrets.services[svc.name].RUN_TESTS = 'true';
|
|
240
253
|
}
|
package/src/helpers/helpers.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
1
4
|
|
|
2
5
|
/**
|
|
3
6
|
* CLI helper functions
|
|
@@ -59,5 +62,26 @@ export const helpers = {
|
|
|
59
62
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
60
63
|
}
|
|
61
64
|
return result;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
getDbTypeFromSecrets: async (serviceName, projectDir) => {
|
|
68
|
+
let dbType;
|
|
69
|
+
const secretsPath = path.join(projectDir, "secrets.localdev.yml");
|
|
70
|
+
const parsedSecrets = yaml.load(await fs.readFile(secretsPath, "utf8"));
|
|
71
|
+
const serviceSecrets = parsedSecrets.services[serviceName.toLowerCase()];
|
|
72
|
+
|
|
73
|
+
Object.keys(serviceSecrets).forEach(key => {
|
|
74
|
+
if (key.toLowerCase().includes('host')) {
|
|
75
|
+
const host = serviceSecrets[key].toLowerCase();
|
|
76
|
+
|
|
77
|
+
if (host.includes('mongo')) {
|
|
78
|
+
dbType = 'mongodb';
|
|
79
|
+
} else if (host.includes('mysql')) {
|
|
80
|
+
dbType = 'mysql';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return dbType;
|
|
62
86
|
}
|
|
63
87
|
}
|
package/src/profiles/command.js
CHANGED
|
@@ -166,5 +166,46 @@ export function registerProfileCommand(program) {
|
|
|
166
166
|
});
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
+
// delete profile
|
|
170
|
+
profile
|
|
171
|
+
.command('delete <profileName>')
|
|
172
|
+
.description('Delete an existing profile')
|
|
173
|
+
.action(async (profileName) => {
|
|
174
|
+
const config = profiles.getAllProfiles();
|
|
175
|
+
const activeProfileName = config.activeProfile;
|
|
176
|
+
|
|
177
|
+
if (activeProfileName === profileName) {
|
|
178
|
+
console.error(`Cannot delete active profile "${profileName}". Please set another profile as active first.`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!config.profiles[profileName]) {
|
|
183
|
+
console.error(`Profile "${profileName}" not found.`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
// confirm deletion with user
|
|
189
|
+
const confirmation = await inquirer.prompt([
|
|
190
|
+
{
|
|
191
|
+
type: 'confirm',
|
|
192
|
+
name: 'confirmDelete',
|
|
193
|
+
message: `Are you sure you want to delete profile "${profileName}"?`,
|
|
194
|
+
default: false,
|
|
195
|
+
},
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
if (!confirmation.confirmDelete) {
|
|
199
|
+
console.log('❌ Deletion cancelled.');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
profiles.deleteProfile({ profileName });
|
|
204
|
+
console.log(`✅ Profile "${profileName}" deleted successfully.`);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(error.message);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
169
210
|
program.addCommand(profile);
|
|
170
211
|
}
|
|
@@ -90,6 +90,29 @@ export const profiles = {
|
|
|
90
90
|
this.saveProfiles(allProfiles);
|
|
91
91
|
},
|
|
92
92
|
|
|
93
|
+
deleteProfile: function ({ profileName }) {
|
|
94
|
+
if (!profileName) {
|
|
95
|
+
throw new Error('Invalid profile name');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const allProfiles = this.getAllProfiles().profiles || {};
|
|
99
|
+
|
|
100
|
+
if (!allProfiles[profileName]) {
|
|
101
|
+
throw new Error(`Profile "${profileName}" not found`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const activeProfileName = this.getActiveProfile()?.name;
|
|
105
|
+
|
|
106
|
+
if (activeProfileName === profileName) {
|
|
107
|
+
throw new Error(`Cannot delete active profile "${profileName}". Please set another profile as active first.`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Prompt user to confirm deletion in the console
|
|
111
|
+
delete allProfiles[profileName];
|
|
112
|
+
|
|
113
|
+
this.saveProfiles(allProfiles);
|
|
114
|
+
},
|
|
115
|
+
|
|
93
116
|
saveProfiles: function (profilesObj) {
|
|
94
117
|
const dir = path.dirname(this.configPath);
|
|
95
118
|
if (!fs.existsSync(dir)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { profiles } from '../profiles/profiles.client.js';
|
|
3
3
|
import { scaffolder } from './scaffolder.handler.js';
|
|
4
|
+
import { helpers } from '../helpers/helpers.js';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
|
|
6
7
|
export const registerScaffolderCommands = (program) => {
|
|
@@ -33,11 +34,16 @@ export const registerScaffolderCommands = (program) => {
|
|
|
33
34
|
}
|
|
34
35
|
]);
|
|
35
36
|
|
|
37
|
+
// validate absolute path, if it is not absolute, make it absolute
|
|
38
|
+
if (!path.isAbsolute(answers.projectDir)) {
|
|
39
|
+
answers.projectDir = path.join(process.cwd(), answers.projectDir);
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
// create the project
|
|
37
43
|
try {
|
|
38
44
|
scaffolder.createNewProject({
|
|
39
45
|
projectName: projectName,
|
|
40
|
-
projectDir: answers.projectDir,
|
|
46
|
+
projectDir: path.join('/', answers.projectDir),
|
|
41
47
|
rootAdminEmail: answers.rootAdminEmail
|
|
42
48
|
});
|
|
43
49
|
} catch (error) {
|
|
@@ -127,4 +133,54 @@ export const registerScaffolderCommands = (program) => {
|
|
|
127
133
|
}
|
|
128
134
|
}
|
|
129
135
|
});
|
|
136
|
+
|
|
137
|
+
create
|
|
138
|
+
.command('entity <entity>')
|
|
139
|
+
.description('📦 Create a new entity in an existing service')
|
|
140
|
+
.option('--in-service <serviceName>', 'The service in which to add the entity')
|
|
141
|
+
.action(async (entity, options) => {
|
|
142
|
+
// validate
|
|
143
|
+
if (!entity) {
|
|
144
|
+
console.error('❌ Please specify an entity name using gnar create entity <entityName> --in-service <serviceName>');
|
|
145
|
+
}
|
|
146
|
+
if (!options.inService) {
|
|
147
|
+
console.error('❌ Please specify the service using --in-service <serviceName>');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let activeProfile;
|
|
151
|
+
try {
|
|
152
|
+
activeProfile = profiles.getActiveProfile();
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error('❌ No active profile found. Please create or set one using `gnar profile create` or `gnar profile set-active <profileName>`');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// create the entity
|
|
159
|
+
try {
|
|
160
|
+
// add trailing slash to project dir if missing
|
|
161
|
+
let projectDir = activeProfile.profile.PROJECT_DIR;
|
|
162
|
+
|
|
163
|
+
if (!activeProfile.profile.PROJECT_DIR.endsWith(path.sep)) {
|
|
164
|
+
projectDir += path.sep;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const dbType = await helpers.getDbTypeFromSecrets(options.inService, projectDir);
|
|
168
|
+
const serviceDir = path.join(projectDir, 'services', options.inService.toLowerCase());
|
|
169
|
+
|
|
170
|
+
console.log('Creating new entity in... ' + serviceDir);
|
|
171
|
+
|
|
172
|
+
scaffolder.createNewEntity({
|
|
173
|
+
entityName: entity,
|
|
174
|
+
inService: options.inService,
|
|
175
|
+
serviceDir: serviceDir,
|
|
176
|
+
database: dbType
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
console.log('Created entity ' + entity + ' in service ' + options.inService);
|
|
180
|
+
console.log('👉 Remember to add the new entities handler and controllers to your service\'s app.js');
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('❌ Error creating entity:', error.message);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
130
186
|
}
|