@gnar-engine/cli 1.0.0 → 1.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.
- package/assets/gnar-engine-logo-white.svg +9 -0
- package/bootstrap/deploy.localdev.yml +51 -0
- package/bootstrap/secrets.localdev.yml +27 -0
- package/bootstrap/services/agent/Dockerfile +23 -0
- package/bootstrap/services/agent/notes.md +28 -0
- package/bootstrap/services/agent/package.json +16 -0
- package/bootstrap/services/agent/src/app.js +52 -0
- package/bootstrap/services/agent/src/commands/agent.handler.js +104 -0
- package/bootstrap/services/agent/src/config.js +52 -0
- package/bootstrap/services/agent/src/controllers/http.controller.js +44 -0
- package/bootstrap/services/agent/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/agent/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +36 -0
- package/bootstrap/services/agent/src/policies/agent.policy.js +13 -0
- package/bootstrap/services/agent/src/schema/Agent.schema.js +17 -0
- package/bootstrap/services/agent/src/services/agent.service.js +259 -0
- package/bootstrap/services/agent/src/services/chatgpt.service.js +46 -0
- package/bootstrap/services/agent/src/services/manifest.service.js +21 -0
- package/bootstrap/services/control/Dockerfile +23 -0
- package/bootstrap/services/control/Dockerfile.prod +37 -0
- package/bootstrap/services/control/README.md +25 -0
- package/bootstrap/services/control/package.json +16 -0
- package/bootstrap/services/control/src/app.js +45 -0
- package/bootstrap/services/control/src/commands/control.handler.js +231 -0
- package/bootstrap/services/control/src/commands/service.handler.js +81 -0
- package/bootstrap/services/control/src/commands/task.handler.js +247 -0
- package/bootstrap/services/control/src/config.js +55 -0
- package/bootstrap/services/control/src/controllers/http.controller.js +228 -0
- package/bootstrap/services/control/src/controllers/message.controller.js +40 -0
- package/bootstrap/services/control/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/control/src/db/migrations/02-control-service-init.js +60 -0
- package/bootstrap/services/control/src/db/migrations/03-alter-tasks.js +29 -0
- package/bootstrap/services/control/src/policies/task.policy.js +53 -0
- package/bootstrap/services/control/src/schema/control.schema.js +42 -0
- package/bootstrap/services/control/src/services/registry.service.js +83 -0
- package/bootstrap/services/control/src/services/reset.service.js +28 -0
- package/bootstrap/services/control/src/services/task.service.js +153 -0
- package/bootstrap/services/control/src/tests/control.test.js +50 -0
- package/bootstrap/services/notification/Dockerfile +23 -0
- package/bootstrap/services/notification/Dockerfile.prod +37 -0
- package/bootstrap/services/notification/README.md +3 -0
- package/bootstrap/services/notification/package.json +34 -0
- package/bootstrap/services/notification/src/app.js +51 -0
- package/bootstrap/services/notification/src/commands/command-bus.js +20 -0
- package/bootstrap/services/notification/src/commands/handlers/control.handler.js +18 -0
- package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +157 -0
- package/bootstrap/services/notification/src/config.js +15 -0
- package/bootstrap/services/notification/src/controllers/message.controller.js +82 -0
- package/bootstrap/services/notification/src/services/logger.service.js +16 -0
- package/bootstrap/services/notification/src/services/ses.service.js +23 -0
- package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +136 -0
- package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +87 -0
- package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +132 -0
- package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +77 -0
- package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
- package/bootstrap/services/portal/Dockerfile +23 -0
- package/bootstrap/services/portal/Dockerfile.remote +40 -0
- package/bootstrap/services/portal/README.md +22 -0
- package/bootstrap/services/portal/nginx.conf +12 -0
- package/bootstrap/services/portal/package.json +59 -0
- package/bootstrap/services/portal/public/favicon.ico +0 -0
- package/bootstrap/services/portal/public/gnar-white.png +0 -0
- package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
- package/bootstrap/services/portal/public/index.html +43 -0
- package/bootstrap/services/portal/public/logo192.png +0 -0
- package/bootstrap/services/portal/public/logo512.png +0 -0
- package/bootstrap/services/portal/public/manifest.json +25 -0
- package/bootstrap/services/portal/public/robots.txt +3 -0
- package/bootstrap/services/portal/src/App.js +56 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +1 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +1 -0
- package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +1 -0
- package/bootstrap/services/portal/src/assets/activity.svg +3 -0
- package/bootstrap/services/portal/src/assets/arrow.svg +3 -0
- package/bootstrap/services/portal/src/assets/bin-white.svg +3 -0
- package/bootstrap/services/portal/src/assets/bin.svg +3 -0
- package/bootstrap/services/portal/src/assets/check.svg +3 -0
- package/bootstrap/services/portal/src/assets/chevron.svg +3 -0
- package/bootstrap/services/portal/src/assets/contact.svg +3 -0
- package/bootstrap/services/portal/src/assets/dots-vertical.svg +5 -0
- package/bootstrap/services/portal/src/assets/eye-off.svg +3 -0
- package/bootstrap/services/portal/src/assets/eye.svg +4 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +47 -0
- package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +47 -0
- package/bootstrap/services/portal/src/assets/gnar_engine.svg +3 -0
- package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
- package/bootstrap/services/portal/src/assets/home.svg +3 -0
- package/bootstrap/services/portal/src/assets/link.svg +3 -0
- package/bootstrap/services/portal/src/assets/lock.svg +3 -0
- package/bootstrap/services/portal/src/assets/package.svg +4 -0
- package/bootstrap/services/portal/src/assets/raffle.svg +3 -0
- package/bootstrap/services/portal/src/assets/settings.svg +4 -0
- package/bootstrap/services/portal/src/assets/shopping-bag.svg +3 -0
- package/bootstrap/services/portal/src/assets/user-black.svg +3 -0
- package/bootstrap/services/portal/src/assets/user.svg +3 -0
- package/bootstrap/services/portal/src/assets/users.svg +3 -0
- package/bootstrap/services/portal/src/assets/wallet.svg +3 -0
- package/bootstrap/services/portal/src/css/style.css +1007 -0
- package/bootstrap/services/portal/src/data/data.js +70 -0
- package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +32 -0
- package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +160 -0
- package/bootstrap/services/portal/src/features/crud/crudEdit.less +230 -0
- package/bootstrap/services/portal/src/features/crud/crudList.less +134 -0
- package/bootstrap/services/portal/src/features/crud/crudPage.less +31 -0
- package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +108 -0
- package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +243 -0
- package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +109 -0
- package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +315 -0
- package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +104 -0
- package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +388 -0
- package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +104 -0
- package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +208 -0
- package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +110 -0
- package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +261 -0
- package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +107 -0
- package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +402 -0
- package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +30 -0
- package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +113 -0
- package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +56 -0
- package/bootstrap/services/portal/src/features/loginForm/loginForm.less +56 -0
- package/bootstrap/services/portal/src/features/notes/Notes.jsx +18 -0
- package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +96 -0
- package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +74 -0
- package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +102 -0
- package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +24 -0
- package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +99 -0
- package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +46 -0
- package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +64 -0
- package/bootstrap/services/portal/src/features/sidebar/sidebar.less +49 -0
- package/bootstrap/services/portal/src/features/skus/Skus.jsx +109 -0
- package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +44 -0
- package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +32 -0
- package/bootstrap/services/portal/src/features/user/User.jsx +54 -0
- package/bootstrap/services/portal/src/features/user/user.less +57 -0
- package/bootstrap/services/portal/src/includes/utilities.js +259 -0
- package/bootstrap/services/portal/src/index.js +14 -0
- package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +50 -0
- package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +17 -0
- package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +48 -0
- package/bootstrap/services/portal/src/layouts/loginLayout.less +33 -0
- package/bootstrap/services/portal/src/layouts/portalLayout.less +67 -0
- package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +199 -0
- package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +17 -0
- package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +10 -0
- package/bootstrap/services/portal/src/pages/login/Login.jsx +15 -0
- package/bootstrap/services/portal/src/pages/login/login.less +10 -0
- package/bootstrap/services/portal/src/pages/orders/Orders.jsx +199 -0
- package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +15 -0
- package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +15 -0
- package/bootstrap/services/portal/src/pages/payments/Payments.jsx +10 -0
- package/bootstrap/services/portal/src/pages/portal/Portal.jsx +43 -0
- package/bootstrap/services/portal/src/pages/products/Products.jsx +212 -0
- package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +124 -0
- package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +186 -0
- package/bootstrap/services/portal/src/pages/reports/Reports.jsx +10 -0
- package/bootstrap/services/portal/src/pages/settings/Settings.jsx +10 -0
- package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +199 -0
- package/bootstrap/services/portal/src/pages/users/Users.jsx +193 -0
- package/bootstrap/services/portal/src/pages/users/users.less +25 -0
- package/bootstrap/services/portal/src/slices/authSlice.js +71 -0
- package/bootstrap/services/portal/src/store/configureStore.js +12 -0
- package/bootstrap/services/portal/src/styles/global.less +159 -0
- package/bootstrap/services/portal/src/styles/inputs.less +157 -0
- package/bootstrap/services/portal/src/styles/main.less +26 -0
- package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +97 -0
- package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +23 -0
- package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +17 -0
- package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +42 -0
- package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customSelect/CustomSelect.jsx +63 -0
- package/bootstrap/services/portal/src/ui/customSelect/customSelect.less +92 -0
- package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +19 -0
- package/bootstrap/services/portal/src/ui/loader/Loader.jsx +12 -0
- package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +23 -0
- package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +29 -0
- package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +69 -0
- package/bootstrap/services/portal/src/ui/saveButton/saveButton.less +0 -0
- package/bootstrap/services/rabbit-mq/Dockerfile.prod +9 -0
- package/bootstrap/services/user/Dockerfile +23 -0
- package/bootstrap/services/user/Dockerfile.prod +37 -0
- package/bootstrap/services/user/README.md +8 -0
- package/bootstrap/services/user/package.json +16 -0
- package/bootstrap/services/user/src/app.js +45 -0
- package/bootstrap/services/user/src/commands/session.handler.js +23 -0
- package/bootstrap/services/user/src/commands/user.handler.js +286 -0
- package/bootstrap/services/user/src/config.js +73 -0
- package/bootstrap/services/user/src/controllers/http.controller.js +156 -0
- package/bootstrap/services/user/src/controllers/message.controller.js +51 -0
- package/bootstrap/services/user/src/db/migrations/01-init.js +50 -0
- package/bootstrap/services/user/src/db/migrations/02-user-service-init.js +63 -0
- package/bootstrap/services/user/src/db/migrations/03-unauth-sessions.js +43 -0
- package/bootstrap/services/user/src/db/seeders/development/01-root-user.js +29 -0
- package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +27 -0
- package/bootstrap/services/user/src/db/seeders/production/01-root-user.js +29 -0
- package/bootstrap/services/user/src/policies/user.policy.js +81 -0
- package/bootstrap/services/user/src/schema/user.schema.js +69 -0
- package/bootstrap/services/user/src/services/authentication.service.js +127 -0
- package/bootstrap/services/user/src/services/session.service.js +58 -0
- package/bootstrap/services/user/src/services/user.service.js +130 -0
- package/bootstrap/services/user/src/tests/user.test.js +126 -0
- package/package.json +3 -7
- package/src/agent/agent.client.js +28 -0
- package/src/agent/commands.js +48 -0
- package/src/cli.js +30 -0
- package/src/config.js +8 -0
- package/src/control/commands.js +156 -0
- package/src/control/control.client.js +127 -0
- package/src/dev/commands.js +71 -0
- package/src/dev/dev.service.js +320 -0
- package/src/engine/infra.js +142 -0
- package/src/helpers/helpers.js +63 -0
- package/src/profiles/command.js +170 -0
- package/src/profiles/profiles.client.js +101 -0
- package/src/scaffolder/commands.js +123 -0
- package/src/scaffolder/scaffolder.handler.js +252 -0
- package/src/services/client.js +174 -0
- package/templates/service/Dockerfile.hbs +20 -0
- package/templates/service/app.js.hbs +38 -0
- package/templates/service/commands/{{serviceName}}.handler.js.hbs +97 -0
- package/templates/service/config.js.hbs +48 -0
- package/templates/service/controllers/http.controller.js.hbs +87 -0
- package/templates/service/controllers/message.controller.js.hbs +51 -0
- package/templates/service/db/migrations/01-init.js.hbs +50 -0
- package/templates/service/db/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +23 -0
- package/templates/service/package.json.hbs +18 -0
- package/templates/service/policies/{{serviceName}}.policy.js.hbs +49 -0
- package/templates/service/schema/{{serviceName}}.schema.js.hbs +14 -0
- package/templates/service/services/{{serviceName}}.service.js.hbs +32 -0
- package/dist/cli.js +0 -18
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { commands } from '@gnar-engine/core';
|
|
2
|
+
import { authorise } from '../policies/{{serviceName}}.policy.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* HTTP controller
|
|
6
|
+
*/
|
|
7
|
+
export const httpController = {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get single {{serviceName}}
|
|
11
|
+
*/
|
|
12
|
+
getSingle: {
|
|
13
|
+
method: 'GET',
|
|
14
|
+
url: '/{{lowerCasePlural serviceName}}/: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('getSingle{{pascalCase serviceName}}', params);
|
|
21
|
+
reply.code(200).send({ {{serviceName}}: result });
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get multiple {{lowerCasePlural serviceName}}
|
|
27
|
+
*/
|
|
28
|
+
getMany: {
|
|
29
|
+
method: 'GET',
|
|
30
|
+
url: '/{{lowerCasePlural serviceName}}/',
|
|
31
|
+
preHandler: async (request, reply) => authorise.getMany(request, reply),
|
|
32
|
+
handler: async (request, reply) => {
|
|
33
|
+
const params = {};
|
|
34
|
+
const results = await commands.execute('getMany{{pascalCasePlural serviceName}}', params);
|
|
35
|
+
reply.code(200).send({ {{lowerCasePlural serviceName}}: results });
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create new {{serviceName}}
|
|
41
|
+
*/
|
|
42
|
+
create: {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
url: '/{{lowerCasePlural serviceName}}/',
|
|
45
|
+
preHandler: async (request, reply) => authorise.create(request, reply),
|
|
46
|
+
handler: async (request, reply) => {
|
|
47
|
+
const params = {
|
|
48
|
+
{{lowerCasePlural serviceName}}: [request.body.{{serviceName}}]
|
|
49
|
+
};
|
|
50
|
+
const results = await commands.execute('create{{pascalCasePlural serviceName}}', params);
|
|
51
|
+
reply.code(200).send({ {{lowerCasePlural serviceName}}: results });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Update {{serviceName}}
|
|
57
|
+
*/
|
|
58
|
+
update: {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
url: '/{{lowerCasePlural serviceName}}/:id',
|
|
61
|
+
preHandler: async (request, reply) => authorise.update(request, reply),
|
|
62
|
+
handler: async (request, reply) => {
|
|
63
|
+
const params = {
|
|
64
|
+
id: request.params.id,
|
|
65
|
+
new{{pascalCase serviceName}}Data: request.body
|
|
66
|
+
};
|
|
67
|
+
const result = await commands.execute('update{{pascalCase serviceName}}', params);
|
|
68
|
+
reply.code(200).send({ {{serviceName}}: result });
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete {{serviceName}}
|
|
74
|
+
*/
|
|
75
|
+
delete: {
|
|
76
|
+
method: 'DELETE',
|
|
77
|
+
url: '/{{lowerCasePlural serviceName}}/: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('delete{{pascalCase serviceName}}', params);
|
|
84
|
+
reply.code(200).send({ message: '{{pascalCase serviceName}} deleted' });
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { commands } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
export const messageHandlers = {
|
|
4
|
+
|
|
5
|
+
get{{pascalCase serviceName}}: async (payload) => {
|
|
6
|
+
let result;
|
|
7
|
+
if (payload.data?.id) {
|
|
8
|
+
result = await commands.execute('getSingle{{pascalCase serviceName}}', {
|
|
9
|
+
id: payload.data.id
|
|
10
|
+
});
|
|
11
|
+
} else if (payload.data?.email) {
|
|
12
|
+
result = await commands.execute('getSingle{{pascalCase serviceName}}', {
|
|
13
|
+
email: payload.data.email
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
throw new Error('No {{serviceName}} ID or email provided');
|
|
17
|
+
}
|
|
18
|
+
if (!result) {
|
|
19
|
+
throw new Error('{{pascalCase serviceName}} not found');
|
|
20
|
+
}
|
|
21
|
+
return { {{serviceName}}: result };
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
getMany{{pascalCasePlural serviceName}}: async (payload) => {
|
|
25
|
+
const results = await commands.execute('getMany{{pascalCasePlural serviceName}}', {});
|
|
26
|
+
return { {{lowerCasePlural serviceName}}: results };
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
create{{pascalCase serviceName}}: async (payload) => {
|
|
30
|
+
const results = await commands.execute('create{{pascalCasePlural serviceName}}', {
|
|
31
|
+
{{lowerCasePlural serviceName}}: [payload.data.{{serviceName}}]
|
|
32
|
+
});
|
|
33
|
+
return { {{lowerCasePlural serviceName}}: results };
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
update{{pascalCase serviceName}}: async (payload) => {
|
|
37
|
+
const result = await commands.execute('update{{pascalCase serviceName}}', {
|
|
38
|
+
id: payload.data.id,
|
|
39
|
+
new{{pascalCase serviceName}}Data: payload.data
|
|
40
|
+
});
|
|
41
|
+
return { {{serviceName}}: result };
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
delete{{pascalCase serviceName}}: async (payload) => {
|
|
45
|
+
await commands.execute('delete{{pascalCase serviceName}}', {
|
|
46
|
+
id: payload.data.id
|
|
47
|
+
});
|
|
48
|
+
return { message: '{{pascalCase serviceName}} deleted' };
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
};
|
|
@@ -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: {{lowerCasePlural serviceName}}');
|
|
8
|
+
await db.query(`
|
|
9
|
+
CREATE TABLE {{lowerCasePlural serviceName}} (
|
|
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: {{lowerCasePlural serviceName}}');
|
|
22
|
+
await db.query('DROP TABLE IF EXISTS {{lowerCasePlural serviceName}}');
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{lowerCase serviceName}}-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "{{pascalCase serviceName}} microservice for Gnar Engine",
|
|
5
|
+
"main": "src/app.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node src/app.js",
|
|
8
|
+
"dev": "nodemon src/app.js"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@gnar-engine/core": "^1.0.1"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"nodemon": "^3.0.0"
|
|
15
|
+
},
|
|
16
|
+
"author": "Gnar Software",
|
|
17
|
+
"license": "MIT"
|
|
18
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
|
|
3
|
+
export const authorise = {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authorise get single {{serviceName}}
|
|
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 {{lowerCasePlural serviceName}}
|
|
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 {{lowerCasePlural serviceName}}
|
|
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 {{serviceName}}
|
|
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 {{serviceName}}
|
|
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,14 @@
|
|
|
1
|
+
import { schema } from '@gnar-engine/core';
|
|
2
|
+
import { config } from '../config.js';
|
|
3
|
+
|
|
4
|
+
export const {{serviceName}}Schema = {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
// Add your properties here
|
|
8
|
+
|
|
9
|
+
},
|
|
10
|
+
required: [],
|
|
11
|
+
additionalProperties: false,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const validate{{pascalCase serviceName}} = schema.compile({{serviceName}}Schema);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { db } from '@gnar-engine/core';
|
|
2
|
+
|
|
3
|
+
export const {{serviceName}} = {
|
|
4
|
+
async getById({ id }) {
|
|
5
|
+
const [result] = await db.query('SELECT * FROM {{lowerCasePlural serviceName}} 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 {{lowerCasePlural serviceName}}');
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
async create(data) {
|
|
19
|
+
const { insertId } = await db.query('INSERT INTO {{lowerCasePlural serviceName}} (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 {{lowerCasePlural serviceName}} 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 {{lowerCasePlural serviceName}} WHERE id = ?', [id]);
|
|
30
|
+
return true;
|
|
31
|
+
},
|
|
32
|
+
};
|
package/dist/cli.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import{Command as Ae}from"commander";import{Command as se}from"commander";import u from"fs";import Y from"path";import ne from"os";var m={configPath:Y.join(ne.homedir(),".gnarengine","config.json"),getAllProfiles:function(e=!1){return u.existsSync(this.configPath)?JSON.parse(u.readFileSync(this.configPath,"utf-8"))||{}:(e||console.error(`Config file not found at ${this.configPath}`),{})},getActiveProfile:function(){let e=this.getAllProfiles();if(!e||Object.keys(e).length===0)return console.error("No profiles found"),null;if(!u.existsSync(this.configPath))return ignoreNotFound||console.error(`Config file not found at ${this.configPath}`),{};let r=JSON.parse(u.readFileSync(this.configPath,"utf-8")).activeProfile;return{name:r,profile:e.profiles[r]}},setActiveProfile:function({profileName:e}){if(!u.existsSync(this.configPath)){console.error(`Config file not found at ${this.configPath}`);return}let t=JSON.parse(u.readFileSync(this.configPath,"utf-8"));t.activeProfile=e,u.writeFileSync(this.configPath,JSON.stringify(t,null,2))},createProfile:function({profileName:e,config:t}){if(!e||!t.CLI_API_URL||!t.CLI_API_USERNAME)throw new Error("Invalid profile data");let o=this.getAllProfiles(!0).profiles||{};if(o[e])throw new Error(`Profile "${e}" already exists`);return o[e]=t,this.saveProfiles(o),o[e]},updateProfile:function({profileName:e,config:t}){if(!e||!t.CLI_API_URL||!t.CLI_API_USERNAME||!t.CLI_API_KEY)throw new Error("Invalid profile data");let r=this.getAllProfiles().profiles||{};if(!r[e])throw new Error(`Profile "${e}" not found`);r[e]=t,this.saveProfiles(r)},saveProfiles:function(e){let t=Y.dirname(this.configPath);u.existsSync(t)||u.mkdirSync(t,{recursive:!0}),u.writeFileSync(this.configPath,JSON.stringify({profiles:e},null,2))}};import x from"inquirer";function K(e){let t=new se("profile").description("\u{1F464} Manage CLI profiles");t.command("get-all").description("List all profiles").action(()=>{let r=m.getAllProfiles();if(!r||!r.profiles||Object.keys(r.profiles).length===0){console.error("No profiles found. Please create a profile using gnar profile create");return}Object.keys(r.profiles).forEach(o=>{r.activeProfile==o?console.log("- "+o+" (active)"):console.log("- "+o)})}),t.command("get-active").description("Show the active profile").action(()=>{try{let{name:r,profile:o}=m.getActiveProfile();console.log(`Active profile: ${r}`)}catch{console.error("No active profile found. Please set one using `gnar profile set-active <profileName>`")}}),t.command("set-active").description("Select active profile").action(()=>{try{let r=m.getAllProfiles(),o=Object.keys(r.profiles).map(n=>r.activeProfile==n?{name:n+" (active)",value:n}:{name:n,value:n});x.prompt([{type:"list",name:"profileName",message:"Select a profile to set as active:",choices:o}]).then(n=>{let s=n.profileName;m.setActiveProfile({profileName:s}),console.log(`\u2705 Profile "${s}" set as active.`)});return}catch(r){console.error(`\u274C Error setting active profile: ${r.message}`)}}),t.command("create").description("Create a new profile").action(async()=>{let r=await x.prompt([{name:"profile",message:"Profile name",default:"mywebsite:local"},{name:"CLI_API_URL",message:"API URL",default:"http://localhost"},{name:"CLI_API_USERNAME",message:"API Username",default:"gnarlyroot"},{name:"CLI_API_KEY",message:"API Key"},{name:"PROJECT_DIR",message:"Project directory",default:process.cwd()},{name:"AWS_ACCESS_KEY_ID",message:"AWS Access Key"},{name:"AWS_SECRET_ACCESS_KEY",message:"AWS Secret Access Key"},{name:"AWS_REGION",message:"AWS Region"},{type:"confirm",name:"setActive",message:"Set this profile as active?",default:!0}]),o=r.profile,n={CLI_API_URL:r.CLI_API_URL,CLI_API_USERNAME:r.CLI_API_USERNAME,CLI_API_KEY:r.CLI_API_KEY,PROJECT_DIR:r.PROJECT_DIR||process.cwd(),AWS_ACCESS_KEY_ID:r.AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY:r.AWS_SECRET_ACCESS_KEY,AWS_REGION:r.AWS_REGION};m.createProfile({profileName:o,config:n}),r.setActive&&(console.log("setting active"),m.setActiveProfile({profileName:o}))}),t.command("update <profileName>").description("Update an existing profile").action(async r=>{let o=m.getAllProfiles();if(!o.profiles[r]){console.error(`Profile "${r}" not found.`);return}let n=o.profiles[r],s=await x.prompt([{name:"CLI_API_URL",message:"API URL",default:n.CLI_API_URL},{name:"CLI_API_USERNAME",message:"API Username",default:n.CLI_API_USERNAME},{name:"CLI_API_KEY",message:"API Key",default:n.CLI_API_KEY},{name:"PROJECT_DIR",message:"Project directory",default:n.PROJECT_DIR||process.cwd()},{name:"AWS_ACCESS_KEY_ID",message:"AWS Access Key",default:n.AWS_ACCESS_KEY_ID},{name:"AWS_SECRET_ACCESS_KEY",message:"AWS Secret Access Key",default:n.AWS_SECRET_ACCESS_KEY},{name:"AWS_REGION",message:"AWS Region",default:n.AWS_REGION}]);m.updateProfile({profileName:r,config:{CLI_API_URL:s.CLI_API_URL,CLI_API_USERNAME:s.CLI_API_USERNAME,CLI_API_KEY:s.CLI_API_KEY,PROJECT_DIR:s.PROJECT_DIR||process.cwd(),AWS_ACCESS_KEY_ID:s.AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY:s.AWS_SECRET_ACCESS_KEY,AWS_REGION:s.AWS_REGION}})}),e.addCommand(t)}import{Command as pe,Option as fe}from"commander";import{spawn as ae}from"child_process";import ie from"dockerode";import k from"fs/promises";import O from"path";import $ from"js-yaml";var G={corePath:"/usr/gnar_engine/app/gnarengine-service-core"};var J=new ie;async function Q({projectDir:e,build:t=!1,detached:r=!1,coreDev:o=!1}){let n=O.join(e,"deploy.localdev.yml"),s=O.join(e,"secrets.localdev.yml"),i=$.load(await k.readFile(n,"utf8")),l=$.load(await k.readFile(s,"utf8")),a=O.join(e,".gnarengine");await me(a);let c=O.join(a,"nginx","nginx.conf"),d=await ce({config:i.config});await k.writeFile(c,d);let _=O.join(a,"docker-compose.dev.yml"),h=await le({config:i.config,secrets:l,gnarHiddenDir:a,projectDir:e,coreDev:o});await k.writeFile(_,$.dump(h));let S=["-f",_,"up"];t&&S.push("--build"),r&&S.push("-d");let I=ae("docker-compose",S,{cwd:e,stdio:"inherit",shell:"/bin/sh"}),v=await new Promise(L=>{I.on("close",L)});if(v!==0)throw new Error(`docker-compose up exited with code ${v}`)}async function B({projectDir:e,allContainers:t=!1}){let r=await J.listContainers();if(!t){let o=o.filter(n=>n.Image.includes("ge-dev"))}if(r.length===0){console.log("No running containers found.");return}console.log("Stopping containers..."),r.forEach(o=>{console.log(` - ${o.Names[0]} (${o.Id})`)}),await Promise.all(r.map(o=>J.getContainer(o.Id).stop().catch(s=>{console.error(`Failed to stop ${o.Names[0]}: ${s.message}`)})))}async function ce({config:e,outputPath:t}){let r=`
|
|
3
|
-
http {
|
|
4
|
-
server {
|
|
5
|
-
listen 80;
|
|
6
|
-
server_name ${e.namespace};
|
|
7
|
-
`;for(let o of e.services||[]){let n=o.name,s=o.listener_rules?.paths||[],i=o.ports&&o.ports.length>0?o.ports[0].split(":")[1]:"3000";for(let l of s){let a=l.replace(/\/+$/,"");r+=`
|
|
8
|
-
# ${n} service
|
|
9
|
-
location ${a} {
|
|
10
|
-
rewrite ^${a}$ ${a}/ break;
|
|
11
|
-
proxy_pass http://${n}-service:${i}${a};
|
|
12
|
-
}
|
|
13
|
-
`}}return r+=`
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
`,r}async function le({config:e,secrets:t,gnarHiddenDir:r,projectDir:o,coreDev:n=!1}){let s=3306,i=27017,l={};l.nginx={image:"nginx:latest",container_name:`ge-${e.environment}-${e.namespace}-nginx`,ports:["80:80","443:443"],volumes:[`${r}/nginx/nginx.conf:/etc/nginx/nginx.conf`],restart:"always"},l.rabbitmq={image:"rabbitmq:management",container_name:`ge-${e.environment}-${e.namespace}-rabbitmq`,ports:["5672:5672","15672:15672"],environment:{RABBITMQ_DEFAULT_USER:t.global.RABBITMQ_USER||"",RABBITMQ_DEFAULT_PASS:t.global.RABBITMQ_PASS||""},restart:"always"};for(let a of e.services){let c=t.services?.[a.name]||{},d={};for(let[h,S]of Object.entries(c))d[a.name.toUpperCase()+"_"+h]=S;let _={...t.global||{},...d||{}};l[`${a.name}-service`]={container_name:`ge-${e.environment}-${e.namespace}-${a.name}`,image:`ge-${e.environment}-${e.namespace}-${a.name}`,build:{context:o,dockerfile:`./services/${a.name}/Dockerfile`},command:a.command||[],environment:_,ports:a.ports||[],depends_on:a.depends_on||[],volumes:[`${o}/services/${a.name}/src:/usr/gnar_engine/app/src`],restart:"always"},n&&l[`${a.name}-service`].volumes.push(`../../../gnar-engine-core:${G.corePath}`),c.MYSQL_HOST&&c.MYSQL_DATABASE&&c.MYSQL_USER&&c.MYSQL_PASSWORD&&c.MYSQL_RANDOM_ROOT_PASSWORD&&(l[`${a.name}-db`]={container_name:`ge-${e.environment}-${e.namespace}-${a.name}-db`,image:"mysql",ports:[`${s}:${s}`],restart:"always",environment:{MYSQL_HOST:c.MYSQL_HOST,MYSQL_DATABASE:c.MYSQL_DATABASE,MYSQL_USER:c.MYSQL_USER,MYSQL_PASSWORD:c.MYSQL_PASSWORD,MYSQL_RANDOM_ROOT_PASSWORD:c.MYSQL_RANDOM_ROOT_PASSWORD},volumes:[`${r}/data/${a.name}-db-data:/var/lib/mysql`]},s++),c.MONGO_URL&&c.MONGO_ROOT_PASSWORD&&c.MONGO_USER&&c.MONGO_PASSWORD&&(l[`${a.name}-mongo`]={container_name:`ge-${e.environment}-${e.namespace}-${a.name}-mongo`,image:`ge-${e.environment}-${e.namespace}-${a.name}-mongo`,ports:[`${i}:${i}`],restart:"always",environment:{[`${a.name.toUpperCase()}_MONGO_URL`]:c.MONGO_URL,[`${a.name.toUpperCase()}_MONGO_ROOT_PASSWORD`]:c.MONGO_ROOT_PASSWORD,[`${a.name.toUpperCase()}_MONGO_USER`]:c.MONGO_USER,[`${a.name.toUpperCase()}_MONGO_PASSWORD`]:c.MONGO_PASSWORD},volumes:[`${r}/Data/${a.name}-mongo-data:/data/db`]},i++)}return{version:"3.9",services:l}}async function me(e){await k.mkdir(e,{recursive:!0})}function q(e){let t=new pe("dev").description("\u{1F6E0}\uFE0F Start Gnar Engine Development Environment");t.command("up").description("\u{1F6E0}\uFE0F Up Development Containers").option("-b, --build","Ruild without cache").option("-d, --detach","Run containers in background").addOption(new fe("--core-dev").hideHelp()).action(async r=>{let o={},{profile:n}=m.getActiveProfile();if(!n){o.error="No active profile found";return}let s=n.PROJECT_DIR;try{Q({projectDir:s,build:r.build||!1,detach:r.detach||!1,coreDev:r.coreDev||!1})}catch(i){console.error("\u274C Error running containers:",i.message),process.exit(1)}}),t.command("down").description("\u{1F6E0}\uFE0F Down Development Containers").option("-a, --all-containers","Stop all running containers (not just Gnar Engine ones)").action(async r=>{let{profile:o}=m.getActiveProfile();if(!o){console.error("No active profile found");return}let n=o.PROJECT_DIR;try{B({projectDir:n,allContainers:r.allContainers||!1})}catch(s){console.error("\u274C Error running containers:",s.message),process.exit(1)}}),e.addCommand(t)}import{Command as ge}from"commander";import H from"axios";import D from"fs";import b from"path";import X from"os";var M,y,N,T,C,z,V,A=()=>{try{y=m.getActiveProfile(),T=y.profile.CLI_API_URL,z=y.profile.CLI_API_USERNAME,V=y.profile.CLI_API_KEY,N=y.name,console.log("Profile: "+y.name+" | "+T)}catch(e){throw console.error("Error retrieving active profile:",e.message),new Error("Active profile not found. Please set an active profile using `gnar profile set-active <profileName>`")}M=H.create({baseURL:T,withCredentials:!0}),M.interceptors.request.use(async e=>{try{return C=await de({activeProfileName:N}),C&&(e.headers.Authorization=`Bearer ${C}`),e}catch{}try{let t=await H.post(`${T}/authenticate/`,{username:z,apiKey:V});if(!t.data?.token)throw new Error("Authentication failed",t.data?.error||"No token received");return C=t.data.token,ue({activeProfileName:N,accessToken:C,accessTokenExpires:new Date(Date.now()+36e5).toISOString()}),C&&(e.headers.Authorization=`Bearer ${C}`),e}catch(t){throw console.error("Authentication failed:",t.response?.data?.message||t.message),t}},e=>Promise.reject(e))},g=M;var de=async({activeProfileName:e})=>{let t=null,r=null;try{let o=b.join(X.homedir(),".gnarengine",e+".json"),n=JSON.parse(D.readFileSync(o,"utf-8"));if(t=n.accessToken,r=n.accessTokenExpires,!t||!r)throw new Error("Access token or expiration not found in cache");if(new Date(r)<new Date)throw new Error("Access token has expired")}catch(o){throw o}return t},ue=async({activeProfileName:e,accessToken:t,accessTokenExpires:r})=>{try{let o={},n=b.join(X.homedir(),".gnarengine",e+".json");if(D.existsSync(n))try{o=JSON.parse(D.readFileSync(n,"utf-8"))}catch{}else D.mkdirSync(b.dirname(n),{recursive:!0});o.accessToken=t,o.accessTokenExpires=r,D.writeFileSync(n,JSON.stringify(o,null,2))}catch(o){throw new Error("Unable to write to profile cache file: "+o.message)}};var w={runMigrations:async()=>{try{await A();let e=await g.post("/control/migrations",{});if(e.status!==200)throw new Error("Failed to run migrations");return e.data}catch(e){return{error:"Migrations failed: "+e}}},runSeeders:async()=>{try{await A();let e=await g.post("/control/seeders",{});if(e.status!==200)throw new Error("Failed to run seeders");return e.data}catch(e){return{error:"Seeders failed: "+e}}},runReset:async()=>{try{await A();let e=await g.post("/control/reset",{});if(e.status!==200)throw new Error("Failed to run reset");return e.data}catch(e){return{error:"Reset failed: "+e}}},getTasks:async e=>{try{await A();let t=await g.get(`/tasks/?status=${e}`);if(t.status!==200)throw new Error("Failed to get tasks");return t.data}catch(t){return{error:"Failed to get tasks: "+t}}},handleTaskBatch:async e=>{try{await A(),console.log("Executing task batch...");let t=await g.post("/tasks/execute-batch",{status:e});if(t.status!==200)throw new Error("Failed to execute task batch");if(t.data.errors&&t.data.errors.length>0)throw new Error("Task batch run but had errors: "+JSON.stringify(t.data.errors,null,2));return t.data}catch(t){return{error:"Task execution failed: "+t}}},deleteTask:async e=>{try{await A(),console.log(`Deleting task with id ${e}...`);let t=await g.delete(`/tasks/${e}`);if(t.status!==200)throw new Error("Failed to delete task");return t.data}catch(t){return{error:"Failed to delete task: "+t}}},deleteFailedTasks:async()=>{try{await A(),console.log("Deleting failed tasks...");let e=await g.post("/tasks/delete-failed",{});if(e.status!==200)throw new Error("Failed to delete failed tasks");return e.data}catch(e){return{error:"Failed to delete failed tasks: "+e}}}};import he from"inquirer";function Z(e){let t=new ge("control").description("\u{1F39B}\uFE0F Control related commands");t.command("migrate").description("\u{1F4E6} Run migrations").option("-a, --all","Run all migrations").option("-s, --service <service>","Run migrations for a specific service").option("-m, --migration <migration>","Run a specific migration (must also include service)").action(async r=>{let o={};r.all?(console.log("\u{1F4E6} Running all migrations..."),o=await w.runMigrations()):o.error="No options provided",o.error?console.error("\u274C "+o.error):console.log("\u2705 "+o.message)}),t.command("seed").description("\u{1F331} Run seeders").option("-a, --all","Run all seeders").option("-s, --service <service>","Run seeders for a specific service").option("-S, --seed <seed>","Run a specific seed (must also include service)").action(async r=>{let o={};r.all?(console.log("\u{1F331} Running all seeders..."),o=await w.runSeeders()):o.error="No options provided",o.error?console.error("\u274C "+o.error):console.log("\u2705 "+o.message)}),t.command("reset").description("\u{1F4E6} Full Reset. Drop all data to return to initial state with root user only (development mode only)").option("-a, --all","Reset everything").option("-s, --service <service>","Run migrations for a specific service").action(async r=>{let o={};if(r.all){if(!(await he.prompt([{type:"confirm",name:"confirmReset",message:"\u26A0\uFE0F Are you sure you want to run a full reset? This will delete all data.",default:!1}])).confirmReset){console.log("\u274C Reset cancelled.");return}console.log("\u{1F4E6} Running full reset..."),o=await w.runReset()}else o.error="No options provided";o.error?console.error("\u274C "+o.error):console.log("\u2705 "+o.message)}),t.command("get-tasks").description("\u{1F6E0}\uFE0F Get tasks").option("-s, --status <status>","Task status (default: scheduled)").action(async r=>{r.status||(r.status="scheduled"),console.log("\u{1F6E0}\uFE0F Getting tasks with status: "+r.status);let o=await w.getTasks(r.status);o.error?console.error("\u274C "+o.error):(console.log(JSON.stringify(o.tasks,null,2)),console.log("\u2705 Got tasks "+o.tasks.length))}),t.command("execute-tasks").description("\u{1F6E0}\uFE0F Execute tasks").option("-s, --status <status>","Task status (default: scheduled)").action(async r=>{r.status||(r.status="scheduled"),console.log("\u{1F6E0}\uFE0F Executing tasks with status: "+r.status);let o=await w.handleTaskBatch(r.status);o.error?console.error("\u274C "+o.error):console.log("\u2705 "+o.message)}),t.command("delete-task").description("\u{1F6E0}\uFE0F Delete task").requiredOption("-id, --id <id>","Task ID").action(async r=>{console.log("\u{1F6E0}\uFE0F Deleting task with id: "+r.id);let o=await w.deleteTask(r.id);o.error?console.error("\u274C "+o.error):console.log("\u2705 "+o.numDeletedTasks+" "+o.message)}),t.command("delete-failed-tasks").description("\u{1F6E0}\uFE0F Delete failed tasks").action(async()=>{console.log("\u{1F6E0}\uFE0F Deleting failed tasks");let r=await w.deleteFailedTasks();r.error?console.error("\u274C "+r.error):console.log("\u2705 "+r.numDeletedTasks+" "+r.message)}),e.addCommand(t)}import F from"inquirer";import f from"path";import p from"fs";import ee from"js-yaml";var R={assertTrailingSlash:e=>e.endsWith("/")?e:e+"/",pascalCase:e=>e.replace(/[-_\s]+(.)?/g,(t,r)=>r?r.toUpperCase():"").replace(/^(.)/,(t,r)=>r.toUpperCase()),pascalCasePlural:e=>{let t=R.lowerCasePlural(e);return R.pascalCase(t)},lowerCase:e=>e.toLowerCase(),upperCase:e=>e.toUpperCase(),plural:e=>e.endsWith("y")?e.slice(0,-1).toLowerCase()+"ies":e+"s",lowerCasePlural:e=>e.endsWith("y")?e.slice(0,-1).toLowerCase()+"ies":e.toLowerCase()+"s",capitaliseFirstLetter:e=>typeof e!="string"||e.length===0?e:e.charAt(0).toUpperCase()+e.slice(1),generateRandomString:e=>{let t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r="";for(let o=0;o<e;o++)r+=t.charAt(Math.floor(Math.random()*t.length));return r}};import U from"handlebars";var E={createNewService:function({serviceName:e,database:t,projectDir:r}){let o=r+"services/"+e;if(console.log("Service directory:",o),p.existsSync(o))throw new Error(`Service "${e}" already exists at ${o}`);p.mkdirSync(o,{recursive:!0});let n=f.join(import.meta.dirname,"../../templates/service"),s=E.getAllTemplateFiles({dir:n,baseDir:n});return console.log("Template files:",s),Object.entries(R).forEach(([i,l])=>{U.registerHelper(i,l)}),s.forEach(i=>{let l,a,c={serviceName:e,database:t};switch(i.extension){case".hbs":let d=p.readFileSync(i.fullPath,"utf8"),h=U.compile(d)(c),I=U.compile(i.relativePath.replace(/\.hbs$/,""))(c);a=f.join(o,I),p.mkdirSync(f.dirname(a),{recursive:!0}),p.writeFileSync(a,h,"utf8");break;default:l=i.fullPath,a=f.join(o,i.relativePath),p.mkdirSync(f.dirname(a),{recursive:!0}),p.copyFileSync(l,a);break}}),{message:`Service "${e}" created successfully at ${o}`,servicePath:o}},createNewFrontEndService:function({serviceName:e,projectDir:t}){let r=t+"services/"+e;if(console.log("Service directory:",r),p.existsSync(r))throw new Error(`Service "${e}" already exists at ${r}`);p.mkdirSync(r,{recursive:!0})},getAllTemplateFiles:function({dir:e,baseDir:t=e,fileList:r=[]}){return p.readdirSync(e,{withFileTypes:!0}).forEach(n=>{let s=f.join(e,n.name);if(n.isDirectory())n.name!=="node_modules"&&n.name!=="data"&&(!n.name.startsWith(".")||n.name==".gnarengine")&&E.getAllTemplateFiles({dir:s,baseDir:t,fileList:r});else{let i=f.relative(t,s);n.name!=="docker-compose.dev.yml"&&r.push({fullPath:s,relativePath:i,extension:f.extname(n.name)})}}),r},createNewProject:function({projectName:e,projectDir:t,rootAdminEmail:r}){e=e.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9\-]/g,"");let o=f.join(t,e);console.log("Creating project in:",o);let n=e+":local",s=m.getAllProfiles();if(s&&s.profiles&&s.profiles[n])throw new Error(`A profile with the name "${e}:local" already exists. Please choose a different project name or delete the existing profile.`);p.existsSync(o)||(p.mkdirSync(o,{recursive:!0}),p.mkdirSync(f.join(o,"data"),{recursive:!0}));let i=f.join(import.meta.dirname,"../../bootstrap"),l=E.getAllTemplateFiles({dir:f.join(i),baseDir:f.join(i)}),a="";if(console.log("Copying bootstrap"),!l||l.length===0)throw new Error("No bootstrap files found");l.forEach(d=>{let _,h;if(_=d.fullPath,h=f.join(o,d.relativePath),d.relativePath==="secrets.localdev.yml"){console.log("Creating random secrets: secrets.localdev.yml");let S=f.join(i,"secrets.localdev.yml"),I=p.readFileSync(S,"utf8"),v=ee.load(I);Object.keys(v.services).forEach(j=>{Object.keys(v.services[j]).forEach(W=>{W.toLowerCase().includes("pass")&&(v.services[j][W]=R.generateRandomString(16))})}),a=R.generateRandomString(32),v.services.user.ROOT_ADMIN_API_KEY=a,v.services.user.ROOT_ADMIN_EMAIL=r||"admin@"+e+".local";let L=ee.dump(v);p.writeFileSync(S,L,"utf8")}p.mkdirSync(f.dirname(h),{recursive:!0}),p.copyFileSync(_,h)}),console.log("Creating new CLI profile: "+n);let c={CLI_API_URL:"http://localhost",CLI_API_USERNAME:"gnarlyroot",CLI_API_KEY:a,PROJECT_DIR:o};m.createProfile({profileName:n,config:c}),console.log("Setting CLI profile to active"),m.setActiveProfile({profileName:n}),console.log("g n a r e n g i n e - Created new project: "+e),console.log("Run `gnar dev up` to start the development server")}};import Se from"path";var re=e=>{let t=e.command("create").description("\u{1F4E6} Scaffold new services and components");t.command("project <projectName>").description("\u{1F680} Create a new project").action(async(r,o)=>{if(!r){console.error("\u274C Please specify a project name using gnar create project <projectName>");return}let n=await F.prompt([{type:"input",name:"projectDir",message:"Choose directory to create project in",default:Se.join(process.cwd())},{type:"input",name:"rootAdminEmail",message:"Root Admin Email",default:""}]);try{E.createNewProject({projectName:r,projectDir:n.projectDir,rootAdminEmail:n.rootAdminEmail})}catch(s){console.error("\u274C Error creating project:",s.message)}}),t.command("service <service>").description("\u{1F4E6} Create a new service: back-end|front-end").action(async r=>{r.service||console.error("\u274C Please specify a service name using gnar create service <serviceName>");let o;try{o=m.getActiveProfile()}catch{console.error("\u274C No active profile found. Please create or set one using `gnar profile create` or `gnar profile set-active <profileName>`");return}if((await F.prompt([{type:"list",name:"serviceType",message:"Service Type",choices:[{name:"Back-End",value:"backend"},{name:"Front-End",value:"frontend"}],default:"backend"}])).serviceType==="backend"){let s=await F.prompt([{type:"list",name:"database",message:"Database",choices:[{name:"MYSQL",value:"mysql"},{name:"Mongo DB",value:"mongodb"}],default:"mongodb"}]);try{console.log("Creating new service in... "+o.profile.PROJECT_DIR),E.createNewService({serviceName:r.service,database:s.database,projectDir:o.profile.PROJECT_DIR})}catch(i){console.error("\u274C Error creating service:",i.message)}}else try{console.log("Creating new service in... "+o.profile.PROJECT_DIR),E.createNewFrontEndService({serviceName:r.service,projectDir:o.profile.PROJECT_DIR})}catch(s){console.error("\u274C Error creating service:",s.message)}})};import{Command as ve}from"commander";import _e from"inquirer";var oe={prompt:async(e,t)=>{try{let r=await g.post("/agent/prompt",{textInput:e,chatId:t});if(r.status!==200)throw new Error("Failed to prompt agent");return r.data}catch(r){return r?.response?.data?.error?{error:r.response.data.error}:{error:"Agent prompt failed: "+r}}}};import"uuid";function te(e){let t=new ve("agent").description("\u{1F916} LLM Agent Commands");t.command("session").description("\u{1F916} Prompt the Engine Agent").action(async r=>{let o;for(;;){let n=await _e.prompt([{type:"text",name:"prompt",message:'Enter your prompt (or type "exit" to quit):'}]);if(n.prompt.toLowerCase()==="exit"){console.log("Exiting Engine agent.");break}try{let s=await oe.prompt(n.prompt,o);s.error?console.error("\u274C "+s.error):s.structuredResponse?.responseText?(console.log(s.structuredResponse.responseText),o=s.chatId):console.error("\u274C Error parsing reply")}catch(s){console.error("\u274C Error prompting agent:",s.message)}}}),e.addCommand(t)}var P=new Ae;q(P);K(P);Z(P);re(P);te(P);P.command("help [command]").description("\u2753 Display help for command");P.addHelpText("beforeAll",`
|
|
17
|
-
G n a r E n g i n e - A powerful, AI ready microservice framework for modern applications.
|
|
18
|
-
`);P.parse(process.argv);
|