@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.
Files changed (230) hide show
  1. package/assets/gnar-engine-logo-white.svg +9 -0
  2. package/bootstrap/deploy.localdev.yml +51 -0
  3. package/bootstrap/secrets.localdev.yml +27 -0
  4. package/bootstrap/services/agent/Dockerfile +23 -0
  5. package/bootstrap/services/agent/notes.md +28 -0
  6. package/bootstrap/services/agent/package.json +16 -0
  7. package/bootstrap/services/agent/src/app.js +52 -0
  8. package/bootstrap/services/agent/src/commands/agent.handler.js +104 -0
  9. package/bootstrap/services/agent/src/config.js +52 -0
  10. package/bootstrap/services/agent/src/controllers/http.controller.js +44 -0
  11. package/bootstrap/services/agent/src/controllers/message.controller.js +51 -0
  12. package/bootstrap/services/agent/src/db/migrations/01-init.js +50 -0
  13. package/bootstrap/services/agent/src/db/migrations/02-agent-service-init.js +36 -0
  14. package/bootstrap/services/agent/src/policies/agent.policy.js +13 -0
  15. package/bootstrap/services/agent/src/schema/Agent.schema.js +17 -0
  16. package/bootstrap/services/agent/src/services/agent.service.js +259 -0
  17. package/bootstrap/services/agent/src/services/chatgpt.service.js +46 -0
  18. package/bootstrap/services/agent/src/services/manifest.service.js +21 -0
  19. package/bootstrap/services/control/Dockerfile +23 -0
  20. package/bootstrap/services/control/Dockerfile.prod +37 -0
  21. package/bootstrap/services/control/README.md +25 -0
  22. package/bootstrap/services/control/package.json +16 -0
  23. package/bootstrap/services/control/src/app.js +45 -0
  24. package/bootstrap/services/control/src/commands/control.handler.js +231 -0
  25. package/bootstrap/services/control/src/commands/service.handler.js +81 -0
  26. package/bootstrap/services/control/src/commands/task.handler.js +247 -0
  27. package/bootstrap/services/control/src/config.js +55 -0
  28. package/bootstrap/services/control/src/controllers/http.controller.js +228 -0
  29. package/bootstrap/services/control/src/controllers/message.controller.js +40 -0
  30. package/bootstrap/services/control/src/db/migrations/01-init.js +50 -0
  31. package/bootstrap/services/control/src/db/migrations/02-control-service-init.js +60 -0
  32. package/bootstrap/services/control/src/db/migrations/03-alter-tasks.js +29 -0
  33. package/bootstrap/services/control/src/policies/task.policy.js +53 -0
  34. package/bootstrap/services/control/src/schema/control.schema.js +42 -0
  35. package/bootstrap/services/control/src/services/registry.service.js +83 -0
  36. package/bootstrap/services/control/src/services/reset.service.js +28 -0
  37. package/bootstrap/services/control/src/services/task.service.js +153 -0
  38. package/bootstrap/services/control/src/tests/control.test.js +50 -0
  39. package/bootstrap/services/notification/Dockerfile +23 -0
  40. package/bootstrap/services/notification/Dockerfile.prod +37 -0
  41. package/bootstrap/services/notification/README.md +3 -0
  42. package/bootstrap/services/notification/package.json +34 -0
  43. package/bootstrap/services/notification/src/app.js +51 -0
  44. package/bootstrap/services/notification/src/commands/command-bus.js +20 -0
  45. package/bootstrap/services/notification/src/commands/handlers/control.handler.js +18 -0
  46. package/bootstrap/services/notification/src/commands/handlers/notification.handler.js +157 -0
  47. package/bootstrap/services/notification/src/config.js +15 -0
  48. package/bootstrap/services/notification/src/controllers/message.controller.js +82 -0
  49. package/bootstrap/services/notification/src/services/logger.service.js +16 -0
  50. package/bootstrap/services/notification/src/services/ses.service.js +23 -0
  51. package/bootstrap/services/notification/src/templates/admin-order-recieved.hbs +136 -0
  52. package/bootstrap/services/notification/src/templates/admin-subscription-failed.hbs +87 -0
  53. package/bootstrap/services/notification/src/templates/customer-order-recieved.hbs +132 -0
  54. package/bootstrap/services/notification/src/templates/customer-subscription-failed.hbs +77 -0
  55. package/bootstrap/services/notification/src/tests/notification.test.js +0 -0
  56. package/bootstrap/services/portal/Dockerfile +23 -0
  57. package/bootstrap/services/portal/Dockerfile.remote +40 -0
  58. package/bootstrap/services/portal/README.md +22 -0
  59. package/bootstrap/services/portal/nginx.conf +12 -0
  60. package/bootstrap/services/portal/package.json +59 -0
  61. package/bootstrap/services/portal/public/favicon.ico +0 -0
  62. package/bootstrap/services/portal/public/gnar-white.png +0 -0
  63. package/bootstrap/services/portal/public/gnarengine-logo-black.png +0 -0
  64. package/bootstrap/services/portal/public/index.html +43 -0
  65. package/bootstrap/services/portal/public/logo192.png +0 -0
  66. package/bootstrap/services/portal/public/logo512.png +0 -0
  67. package/bootstrap/services/portal/public/manifest.json +25 -0
  68. package/bootstrap/services/portal/public/robots.txt +3 -0
  69. package/bootstrap/services/portal/src/App.js +56 -0
  70. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black.svg +1 -0
  71. package/bootstrap/services/portal/src/assets/Logo_Anchord_Black_Green.svg +1 -0
  72. package/bootstrap/services/portal/src/assets/Logo_Anchord_White_Green.svg +1 -0
  73. package/bootstrap/services/portal/src/assets/activity.svg +3 -0
  74. package/bootstrap/services/portal/src/assets/arrow.svg +3 -0
  75. package/bootstrap/services/portal/src/assets/bin-white.svg +3 -0
  76. package/bootstrap/services/portal/src/assets/bin.svg +3 -0
  77. package/bootstrap/services/portal/src/assets/check.svg +3 -0
  78. package/bootstrap/services/portal/src/assets/chevron.svg +3 -0
  79. package/bootstrap/services/portal/src/assets/contact.svg +3 -0
  80. package/bootstrap/services/portal/src/assets/dots-vertical.svg +5 -0
  81. package/bootstrap/services/portal/src/assets/eye-off.svg +3 -0
  82. package/bootstrap/services/portal/src/assets/eye.svg +4 -0
  83. package/bootstrap/services/portal/src/assets/gnar-engine-black.svg +47 -0
  84. package/bootstrap/services/portal/src/assets/gnar-engine-white.svg +47 -0
  85. package/bootstrap/services/portal/src/assets/gnar_engine.svg +3 -0
  86. package/bootstrap/services/portal/src/assets/gnarengine-logo-black.png +0 -0
  87. package/bootstrap/services/portal/src/assets/home.svg +3 -0
  88. package/bootstrap/services/portal/src/assets/link.svg +3 -0
  89. package/bootstrap/services/portal/src/assets/lock.svg +3 -0
  90. package/bootstrap/services/portal/src/assets/package.svg +4 -0
  91. package/bootstrap/services/portal/src/assets/raffle.svg +3 -0
  92. package/bootstrap/services/portal/src/assets/settings.svg +4 -0
  93. package/bootstrap/services/portal/src/assets/shopping-bag.svg +3 -0
  94. package/bootstrap/services/portal/src/assets/user-black.svg +3 -0
  95. package/bootstrap/services/portal/src/assets/user.svg +3 -0
  96. package/bootstrap/services/portal/src/assets/users.svg +3 -0
  97. package/bootstrap/services/portal/src/assets/wallet.svg +3 -0
  98. package/bootstrap/services/portal/src/css/style.css +1007 -0
  99. package/bootstrap/services/portal/src/data/data.js +70 -0
  100. package/bootstrap/services/portal/src/features/attributeFormRow/AttributeFormRow.jsx +32 -0
  101. package/bootstrap/services/portal/src/features/billingShipping/BillingShipping.jsx +160 -0
  102. package/bootstrap/services/portal/src/features/crud/crudEdit.less +230 -0
  103. package/bootstrap/services/portal/src/features/crud/crudList.less +134 -0
  104. package/bootstrap/services/portal/src/features/crud/crudPage.less +31 -0
  105. package/bootstrap/services/portal/src/features/crudContact/CrudContactList.jsx +108 -0
  106. package/bootstrap/services/portal/src/features/crudContact/CrudContactSingle.jsx +243 -0
  107. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderList.jsx +109 -0
  108. package/bootstrap/services/portal/src/features/crudOrder/CrudOrderSingle.jsx +315 -0
  109. package/bootstrap/services/portal/src/features/crudProducts/CrudProductList.jsx +104 -0
  110. package/bootstrap/services/portal/src/features/crudProducts/CrudProductSingle.jsx +388 -0
  111. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesList.jsx +104 -0
  112. package/bootstrap/services/portal/src/features/crudRaffles/CrudRafflesSingle.jsx +208 -0
  113. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionList.jsx +110 -0
  114. package/bootstrap/services/portal/src/features/crudSubscription/CrudSubscriptionSingle.jsx +261 -0
  115. package/bootstrap/services/portal/src/features/crudUser/CrudUserList.jsx +107 -0
  116. package/bootstrap/services/portal/src/features/crudUser/CrudUserSingle.jsx +402 -0
  117. package/bootstrap/services/portal/src/features/inventoryFormRow/InventoryFormRow.jsx +30 -0
  118. package/bootstrap/services/portal/src/features/lineItems/LineItems.jsx +113 -0
  119. package/bootstrap/services/portal/src/features/loginForm/LoginForm.jsx +56 -0
  120. package/bootstrap/services/portal/src/features/loginForm/loginForm.less +56 -0
  121. package/bootstrap/services/portal/src/features/notes/Notes.jsx +18 -0
  122. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetForm.jsx +96 -0
  123. package/bootstrap/services/portal/src/features/passwordReset/PasswordResetRequestForm.jsx +74 -0
  124. package/bootstrap/services/portal/src/features/priceFormRow/PriceFormRow.jsx +102 -0
  125. package/bootstrap/services/portal/src/features/priceFormRow/priceFormRow.less +24 -0
  126. package/bootstrap/services/portal/src/features/raffleEntriesList/RaffleEntriesList.jsx +99 -0
  127. package/bootstrap/services/portal/src/features/raffleProductFormRow/RaffleProductFormRow.jsx +46 -0
  128. package/bootstrap/services/portal/src/features/sidebar/Sidebar.jsx +64 -0
  129. package/bootstrap/services/portal/src/features/sidebar/sidebar.less +49 -0
  130. package/bootstrap/services/portal/src/features/skus/Skus.jsx +109 -0
  131. package/bootstrap/services/portal/src/features/subscriptionSchedule/SubscriptionSchedule.jsx +44 -0
  132. package/bootstrap/services/portal/src/features/taxonomyFormRow/TaxonomyFormRow.jsx +32 -0
  133. package/bootstrap/services/portal/src/features/user/User.jsx +54 -0
  134. package/bootstrap/services/portal/src/features/user/user.less +57 -0
  135. package/bootstrap/services/portal/src/includes/utilities.js +259 -0
  136. package/bootstrap/services/portal/src/index.js +14 -0
  137. package/bootstrap/services/portal/src/layouts/CrudLayout.jsx +50 -0
  138. package/bootstrap/services/portal/src/layouts/LoginLayout.jsx +17 -0
  139. package/bootstrap/services/portal/src/layouts/PortalLayout.jsx +48 -0
  140. package/bootstrap/services/portal/src/layouts/loginLayout.less +33 -0
  141. package/bootstrap/services/portal/src/layouts/portalLayout.less +67 -0
  142. package/bootstrap/services/portal/src/pages/contacts/Contacts.jsx +199 -0
  143. package/bootstrap/services/portal/src/pages/dashboard/Dashboard.jsx +17 -0
  144. package/bootstrap/services/portal/src/pages/integrations/Integrations.jsx +10 -0
  145. package/bootstrap/services/portal/src/pages/login/Login.jsx +15 -0
  146. package/bootstrap/services/portal/src/pages/login/login.less +10 -0
  147. package/bootstrap/services/portal/src/pages/orders/Orders.jsx +199 -0
  148. package/bootstrap/services/portal/src/pages/passwordReset/PasswordResetPage.jsx +15 -0
  149. package/bootstrap/services/portal/src/pages/passwordResetRequest/PasswordResetRequestPage.jsx +15 -0
  150. package/bootstrap/services/portal/src/pages/payments/Payments.jsx +10 -0
  151. package/bootstrap/services/portal/src/pages/portal/Portal.jsx +43 -0
  152. package/bootstrap/services/portal/src/pages/products/Products.jsx +212 -0
  153. package/bootstrap/services/portal/src/pages/raffleEntries/RaffleEntries.jsx +124 -0
  154. package/bootstrap/services/portal/src/pages/raffles/Raffles.jsx +186 -0
  155. package/bootstrap/services/portal/src/pages/reports/Reports.jsx +10 -0
  156. package/bootstrap/services/portal/src/pages/settings/Settings.jsx +10 -0
  157. package/bootstrap/services/portal/src/pages/subscriptions/Subscriptions.jsx +199 -0
  158. package/bootstrap/services/portal/src/pages/users/Users.jsx +193 -0
  159. package/bootstrap/services/portal/src/pages/users/users.less +25 -0
  160. package/bootstrap/services/portal/src/slices/authSlice.js +71 -0
  161. package/bootstrap/services/portal/src/store/configureStore.js +12 -0
  162. package/bootstrap/services/portal/src/styles/global.less +159 -0
  163. package/bootstrap/services/portal/src/styles/inputs.less +157 -0
  164. package/bootstrap/services/portal/src/styles/main.less +26 -0
  165. package/bootstrap/services/portal/src/ui/collapsible/Collapsible.jsx +97 -0
  166. package/bootstrap/services/portal/src/ui/collapsible/collapsible.less +23 -0
  167. package/bootstrap/services/portal/src/ui/customCheckbox/CustomCheckbox.jsx +17 -0
  168. package/bootstrap/services/portal/src/ui/customCheckbox/customCheckbox.less +42 -0
  169. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelect.jsx +63 -0
  170. package/bootstrap/services/portal/src/ui/customMultiSelect/CustomMultiSelectPeriod.jsx +63 -0
  171. package/bootstrap/services/portal/src/ui/customSelect/CustomSelect.jsx +63 -0
  172. package/bootstrap/services/portal/src/ui/customSelect/customSelect.less +92 -0
  173. package/bootstrap/services/portal/src/ui/goBack/GoBack.jsx +19 -0
  174. package/bootstrap/services/portal/src/ui/loader/Loader.jsx +12 -0
  175. package/bootstrap/services/portal/src/ui/pagination/Pagination.jsx +23 -0
  176. package/bootstrap/services/portal/src/ui/repeater/Repeater.jsx +29 -0
  177. package/bootstrap/services/portal/src/ui/saveButton/SaveButton.jsx +69 -0
  178. package/bootstrap/services/portal/src/ui/saveButton/saveButton.less +0 -0
  179. package/bootstrap/services/rabbit-mq/Dockerfile.prod +9 -0
  180. package/bootstrap/services/user/Dockerfile +23 -0
  181. package/bootstrap/services/user/Dockerfile.prod +37 -0
  182. package/bootstrap/services/user/README.md +8 -0
  183. package/bootstrap/services/user/package.json +16 -0
  184. package/bootstrap/services/user/src/app.js +45 -0
  185. package/bootstrap/services/user/src/commands/session.handler.js +23 -0
  186. package/bootstrap/services/user/src/commands/user.handler.js +286 -0
  187. package/bootstrap/services/user/src/config.js +73 -0
  188. package/bootstrap/services/user/src/controllers/http.controller.js +156 -0
  189. package/bootstrap/services/user/src/controllers/message.controller.js +51 -0
  190. package/bootstrap/services/user/src/db/migrations/01-init.js +50 -0
  191. package/bootstrap/services/user/src/db/migrations/02-user-service-init.js +63 -0
  192. package/bootstrap/services/user/src/db/migrations/03-unauth-sessions.js +43 -0
  193. package/bootstrap/services/user/src/db/seeders/development/01-root-user.js +29 -0
  194. package/bootstrap/services/user/src/db/seeders/development/02-portal-admin-user.js +27 -0
  195. package/bootstrap/services/user/src/db/seeders/production/01-root-user.js +29 -0
  196. package/bootstrap/services/user/src/policies/user.policy.js +81 -0
  197. package/bootstrap/services/user/src/schema/user.schema.js +69 -0
  198. package/bootstrap/services/user/src/services/authentication.service.js +127 -0
  199. package/bootstrap/services/user/src/services/session.service.js +58 -0
  200. package/bootstrap/services/user/src/services/user.service.js +130 -0
  201. package/bootstrap/services/user/src/tests/user.test.js +126 -0
  202. package/package.json +3 -7
  203. package/src/agent/agent.client.js +28 -0
  204. package/src/agent/commands.js +48 -0
  205. package/src/cli.js +30 -0
  206. package/src/config.js +8 -0
  207. package/src/control/commands.js +156 -0
  208. package/src/control/control.client.js +127 -0
  209. package/src/dev/commands.js +71 -0
  210. package/src/dev/dev.service.js +320 -0
  211. package/src/engine/infra.js +142 -0
  212. package/src/helpers/helpers.js +63 -0
  213. package/src/profiles/command.js +170 -0
  214. package/src/profiles/profiles.client.js +101 -0
  215. package/src/scaffolder/commands.js +123 -0
  216. package/src/scaffolder/scaffolder.handler.js +252 -0
  217. package/src/services/client.js +174 -0
  218. package/templates/service/Dockerfile.hbs +20 -0
  219. package/templates/service/app.js.hbs +38 -0
  220. package/templates/service/commands/{{serviceName}}.handler.js.hbs +97 -0
  221. package/templates/service/config.js.hbs +48 -0
  222. package/templates/service/controllers/http.controller.js.hbs +87 -0
  223. package/templates/service/controllers/message.controller.js.hbs +51 -0
  224. package/templates/service/db/migrations/01-init.js.hbs +50 -0
  225. package/templates/service/db/migrations/02-{{lowerCase serviceName}}-service-init.js.hbs +23 -0
  226. package/templates/service/package.json.hbs +18 -0
  227. package/templates/service/policies/{{serviceName}}.policy.js.hbs +49 -0
  228. package/templates/service/schema/{{serviceName}}.schema.js.hbs +14 -0
  229. package/templates/service/services/{{serviceName}}.service.js.hbs +32 -0
  230. package/dist/cli.js +0 -18
@@ -0,0 +1,127 @@
1
+ import { logger, db, utils } from '@gnar-engine/core';
2
+ import { config } from '../config.js';
3
+
4
+
5
+ /**
6
+ * Authentication Service
7
+ */
8
+ export const auth = {
9
+
10
+ /**
11
+ * Create new authenticated session
12
+ *
13
+ * @param {*} userId
14
+ * @returns {string} Session token
15
+ */
16
+ createSessionToken: async (userId) => {
17
+
18
+ try {
19
+ const token = utils.uuid();
20
+
21
+ const [result] = await db.execute(
22
+ 'INSERT INTO `sessions` (`token`, `user_id`) VALUES (?, ?)',
23
+ [token, userId]
24
+ );
25
+
26
+ return token;
27
+ } catch (error) {
28
+ logger.error("Error creating session token: " + error);
29
+ throw error;
30
+ }
31
+ },
32
+
33
+ /**
34
+ * Get authenticated user from token
35
+ *
36
+ * @param {string} token - Session token
37
+ * @returns {int} User ID
38
+ */
39
+ getAuthenticatedUser: async (token) => {
40
+
41
+ try {
42
+ const [result] = await db.execute(
43
+ 'SELECT * FROM `sessions` WHERE `token` = ?',
44
+ [token]
45
+ );
46
+
47
+ if (result.length === 0) {
48
+ return null;
49
+ }
50
+
51
+ const session = result[0];
52
+ const createdAt = new Date(session.created_at);
53
+ const now = new Date();
54
+ const oneHour = 60 * 60 * 1000;
55
+ if (now - createdAt > oneHour) {
56
+ return null;
57
+ }
58
+
59
+ return result[0].user_id;
60
+ } catch (error) {
61
+ logger.error("Error fetching authenticated user:" + error);
62
+ throw error;
63
+ }
64
+ },
65
+
66
+ /**
67
+ * Verify user credentials
68
+ *
69
+ * @param {Object} params
70
+ * @param {string} params.username - Username (or email)
71
+ * @param {string} params.password - Password
72
+ * @returns {int} User ID
73
+ */
74
+ verifyCredentials: async ({username, password}) => {
75
+
76
+ const passwordHash = utils.hash(password, config.hashNameSpace);
77
+
78
+ try {
79
+ // try username
80
+ let [result] = await db.execute(
81
+ 'SELECT * FROM `users` WHERE `username` = ? AND `password` = ?',
82
+ [username, passwordHash]
83
+ );
84
+
85
+ if (result.length > 0) {
86
+ return result[0].id;
87
+ }
88
+
89
+ // try username as email
90
+ [result] = await db.execute(
91
+ 'SELECT * FROM `users` WHERE `email` = ? AND `password` = ?',
92
+ [username, passwordHash]
93
+ );
94
+
95
+ if (result.length > 0) {
96
+ return result[0].id;
97
+ }
98
+ } catch (error) {
99
+ logger.error("Error verifying credentials:" + error);
100
+ throw error;
101
+ }
102
+ },
103
+
104
+ /**
105
+ * Verify API key with username
106
+ *
107
+ * @param {Object} params
108
+ * @param {string} params.apiKey - API key
109
+ * @param {string} params.username - Username
110
+ * @returns {int} User ID
111
+ */
112
+ verifyApiKey: async ({apiKey, username}) => {
113
+ try {
114
+ const [result] = await db.execute(
115
+ 'SELECT * FROM `users` WHERE `api_key` = ? AND `username` = ?',
116
+ [apiKey, username]
117
+ );
118
+
119
+ if (result.length > 0) {
120
+ return result[0].id;
121
+ }
122
+ } catch (error) {
123
+ logger.error("Error verifying API key:" + error);
124
+ throw error;
125
+ }
126
+ }
127
+ };
@@ -0,0 +1,58 @@
1
+ import { logger, db, utils } from '@gnar-engine/core';
2
+
3
+
4
+ /**
5
+ * Authentication Service
6
+ */
7
+ export const unauthenticatedSession = {
8
+
9
+ /**
10
+ * Create new unauthenticated session token
11
+ *
12
+ * @returns {string} Session token
13
+ */
14
+ createSessionToken: async () => {
15
+
16
+ try {
17
+ const sessionToken = utils.uuid();
18
+
19
+ const [result] = await db.execute(
20
+ 'INSERT INTO `unauthenticated_sessions` (`token`) VALUES (?)',
21
+ [sessionToken]
22
+ );
23
+
24
+ return sessionToken;
25
+ } catch (error) {
26
+ logger.error("Error creating unauthenticated session token: " + error);
27
+ throw error;
28
+ }
29
+ },
30
+
31
+ /**
32
+ * Verify unauthenticated session token
33
+ *
34
+ * @param {Object} params
35
+ * @param {string} params.sessionToken
36
+ * @returns {boolean} Session token valid
37
+ */
38
+ verifySessionToken: async ({sessionToken}) => {
39
+
40
+ try {
41
+ [result] = await db.execute(
42
+ 'SELECT * FROM `unauthenticated_sessions` WHERE `token` = ?',
43
+ [sessionToken]
44
+ );
45
+
46
+ if (result.length > 0) {
47
+ if (result[0].expires_at > new Date()) {
48
+ return true;
49
+ }
50
+ }
51
+
52
+ return false;
53
+ } catch (error) {
54
+ logger.error("Error verifying unauthenticated session token:", error);
55
+ throw error;
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,130 @@
1
+ import { logger, db, utils } from '@gnar-engine/core';
2
+ import { config} from '../config.js';
3
+
4
+
5
+ /**
6
+ * User Service
7
+ */
8
+ export const user = {
9
+
10
+ // Get all users
11
+ getAll: async () => {
12
+ try {
13
+ const [results, fields] = await db.execute(
14
+ 'SELECT id, username, email, role FROM `users`'
15
+ );
16
+
17
+ return results;
18
+ } catch (error) {
19
+ logger.error("Error fetching users:", error);
20
+ throw error;
21
+ }
22
+ },
23
+
24
+ // Create a user
25
+ create: async ({email, role, password = null, username = null, apiKey = null}) => {
26
+ try {
27
+ const id = utils.uuid();
28
+ let passwordHash = null;
29
+
30
+ if (password) {
31
+ passwordHash = utils.hash(password, config.hashNameSpace);
32
+ }
33
+ console.log('creating user');
34
+ const [result] = await db.execute(
35
+ 'INSERT INTO `users` (`id`, `email`, `password`, `username`, `role`, `api_key`) VALUES (?, ?, ?, ?, ?, ?)',
36
+ [id, email, passwordHash, username, role, apiKey]
37
+ );
38
+
39
+ const [newUser] = await db.execute(
40
+ 'SELECT * FROM `users` WHERE `id` = ?',
41
+ [id]
42
+ );
43
+
44
+ return newUser[0];
45
+ } catch (error) {
46
+ logger.error("Error creating user:", error);
47
+ throw error;
48
+ }
49
+ },
50
+
51
+ // Get a user by ID
52
+ getById: async ({id}) => {
53
+ try {
54
+
55
+ const [result] = await db.execute(
56
+ 'SELECT * FROM `users` WHERE `id` = ?',
57
+ [id]
58
+ );
59
+
60
+ if (!result || result.length === 0) {
61
+ return null;
62
+ }
63
+
64
+ return result[0];
65
+ } catch (error) {
66
+ logger.error("Error fetching user:", error);
67
+ throw error;
68
+ }
69
+ },
70
+
71
+ // Get a user by email
72
+ getByEmail: async ({email}) => {
73
+ try {
74
+ const [result] = await db.execute(
75
+ 'SELECT * FROM `users` WHERE `email` = ?',
76
+ [email]
77
+ );
78
+
79
+ if (!result || result.length === 0) {
80
+ return null;
81
+ }
82
+
83
+ return result[0];
84
+ } catch (error) {
85
+ logger.error("Error fetching user by email:", error);
86
+ throw error;
87
+ }
88
+ },
89
+
90
+ // Update a user
91
+ update: async ({id, username, email, role}) => {
92
+ try {
93
+ const [result] = await db.execute(
94
+ 'UPDATE `users` SET `username` = ?, `email` = ?, `role` = ? WHERE `id` = ?',
95
+ [username, email, role, id]
96
+ );
97
+
98
+ const [updatedUser] = await db.execute(
99
+ 'SELECT * FROM `users` WHERE `id` = ?',
100
+ [id]
101
+ );
102
+
103
+ return updatedUser[0];
104
+ } catch (error) {
105
+ logger.error("Error updating user:", error);
106
+ throw error;
107
+ }
108
+ },
109
+
110
+ // Delete a user
111
+ delete: async ({id}) => {
112
+ try {
113
+ const [sessionResult] = await db.execute(
114
+ 'DELETE FROM sessions WHERE user_id = ?',
115
+ [id]
116
+ );
117
+
118
+ const [userResult] = await db.execute(
119
+ 'DELETE FROM `users` WHERE `id` = ?',
120
+ [id]
121
+ );
122
+
123
+ return userResult.affectedRows;
124
+ } catch (error) {
125
+ logger.error("Error deleting user:", error);
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ };
@@ -0,0 +1,126 @@
1
+ const request = require('supertest');
2
+ const url = 'http://localhost';
3
+
4
+ describe('User API', () => {
5
+
6
+ let adminAuthToken;
7
+ let customerAuthToken;
8
+ let userId;
9
+ let testCustomerEmail;
10
+
11
+ beforeAll(async () => {
12
+ // check we are not in production mode
13
+ if (process.env.NODE_ENV === 'production') {
14
+ throw new Error('Do not run tests in production mode!');
15
+ }
16
+
17
+ testCustomerEmail = 'customertest13@gnar.co.uk';
18
+ });
19
+
20
+ // Test authenticate with email and password
21
+ it('POST /authenticate (as admin)', async () => {
22
+ const response = await request(url).post('/authenticate')
23
+ .set('Content-Type', 'application/json')
24
+ .send({
25
+ username: 'root@gnar.co.uk',
26
+ password: 'gn4rlyR00tP0rt4lP4ss'
27
+ });
28
+ console.log(response.body);
29
+ expect(response.status).toBe(200);
30
+ expect(response.body).toHaveProperty('token');
31
+
32
+ adminAuthToken = response.body.token;
33
+ });
34
+
35
+ // Create new customer user (success no auth required)
36
+ it('POST /users (create new customer user no auth required)', async () => {
37
+ const response = await request(url).post('/users')
38
+ .set('Content-Type', 'application/json')
39
+ .send({
40
+ user:
41
+ {
42
+ email: testCustomerEmail,
43
+ password: 'password1234'
44
+ }
45
+ })
46
+ console.log(response.body);
47
+ expect(response.status).toBe(200);
48
+ expect(response.body.users[0].email).toBe(testCustomerEmail);
49
+
50
+ // Save the new user id
51
+ userId = response.body.users[0].id;
52
+ console.log('New user id: ' + userId);
53
+ });
54
+
55
+ // Test authenticate with new user
56
+ it('POST /authenticate (as new customer user)', async () => {
57
+ const response = await request(url).post('/authenticate')
58
+ .set('Content-Type', 'application/json')
59
+ .send({
60
+ username: testCustomerEmail,
61
+ password: 'password1234'
62
+ });
63
+ console.log(response.body);
64
+ expect(response.status).toBe(200);
65
+ expect(response.body).toHaveProperty('token');
66
+
67
+ customerAuthToken = response.body.token;
68
+ });
69
+
70
+ // Fetch single customer user (as customer)
71
+ it('GET /users/{id} (as customer)', async () => {
72
+ const response = await request(url).get('/users/' + userId)
73
+ .set('Authorization', 'Bearer ' + customerAuthToken);
74
+ console.log(response.body);
75
+ expect(response.status).toBe(200);
76
+ });
77
+
78
+ // Fetch all users (not authorised as customer)
79
+ it('GET /users (not authorised as customer)', async () => {
80
+ const response = await request(url).get('/users')
81
+ .set('Authorization', 'Bearer ' + customerAuthToken);
82
+ console.log(response.body);
83
+ expect(response.status).toBe(403);
84
+ });
85
+
86
+ // Fetch all users (success as admin)
87
+ it('GET /users (as admin)', async () => {
88
+ const response = await request(url).get('/users')
89
+ .set('Authorization', 'Bearer ' + adminAuthToken);
90
+ console.log(response.body);
91
+ expect(response.status).toBe(200);
92
+ });
93
+
94
+ // Update customer user (as customer)
95
+ it('POST /users/{id} (as customer)', async () => {
96
+ const response = await request(url).post('/users/' + userId)
97
+ .set('Authorization', 'Bearer ' + customerAuthToken)
98
+ .set('Content-Type', 'application/json')
99
+ .send({
100
+ email: 'changedcustomeremail2@gnar.co.uk'
101
+ });
102
+ console.log(response.body);
103
+ expect(response.status).toBe(200);
104
+ expect(response.body.user.email).toBe('changedcustomeremail2@gnar.co.uk');
105
+ });
106
+
107
+ // Update customer user (check cannot elevate role to admin)
108
+ it('POST /users/{id} (as customer)', async () => {
109
+ const response = await request(url).post('/users/' + userId)
110
+ .set('Authorization', 'Bearer ' + customerAuthToken)
111
+ .set('Content-Type', 'application/json')
112
+ .send({
113
+ role: 'service_admin'
114
+ });
115
+ console.log(response.body);
116
+ expect(response.status).toBe(403);
117
+ });-
118
+
119
+ // Delete customer user (as admin)
120
+ it('DELETE /users/{id} (as admin)', async () => {
121
+ const response = await request(url).delete('/users/' + userId)
122
+ .set('Authorization', 'Bearer ' + adminAuthToken);
123
+ console.log(response.body);
124
+ expect(response.status).toBe(200);
125
+ });
126
+ });
package/package.json CHANGED
@@ -1,16 +1,12 @@
1
1
  {
2
2
  "name": "@gnar-engine/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Gnar Engine Development Framework CLI: Project bootstrap, scaffolder & control plane.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "gnar": "./dist/cli.js"
8
- },
9
- "main": "./dist/cli.js",
10
- "files": ["dist"],
11
- "scripts": {
12
- "build": "esbuild src/cli.js --minify --bundle --format=esm --platform=node --outfile=dist/cli.js --packages=external"
7
+ "gnar": "./src/cli.js"
13
8
  },
9
+ "main": "./src/cli.js",
14
10
  "author": "Gnar Software Ltd.",
15
11
  "license": "MIT",
16
12
  "repository": {
@@ -0,0 +1,28 @@
1
+ import client from "../services/client.js";
2
+
3
+ export const agent = {
4
+
5
+ prompt: async (input, chatId) => {
6
+ try {
7
+ const response = await client.post('/agent/prompt', {
8
+ textInput: input,
9
+ chatId: chatId
10
+ });
11
+
12
+ if (response.status !== 200) {
13
+ throw new Error("Failed to prompt agent");
14
+ }
15
+
16
+ //console.log("Agent response: " + response.data);
17
+
18
+ return response.data;
19
+ } catch (error) {
20
+ // get response message if available
21
+ if (error?.response?.data?.error) {
22
+ return {error: error.response.data.error};
23
+ }
24
+
25
+ return {error: "Agent prompt failed: " + error};
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,48 @@
1
+ // control/commands.js
2
+ import { Command } from 'commander';
3
+ import inquirer from 'inquirer';
4
+ import { agent } from './agent.client.js';
5
+ import { v4 as uuidv4 } from 'uuid';
6
+
7
+ export function registerAgentCommands(program) {
8
+ const agentCmd = new Command('agent').description('🤖 LLM Agent Commands');
9
+
10
+ agentCmd
11
+ .command('session')
12
+ .description('🤖 Prompt the Engine Agent')
13
+ .action(async (options) => {
14
+ let chatId;
15
+
16
+ while (true) {
17
+ const input = await inquirer.prompt([
18
+ {
19
+ type: 'text',
20
+ name: 'prompt',
21
+ message: 'Enter your prompt (or type "exit" to quit):'
22
+ },
23
+ ]);
24
+
25
+ if (input.prompt.toLowerCase() === 'exit') {
26
+ console.log('Exiting Engine agent.');
27
+ break;
28
+ }
29
+
30
+ try {
31
+ const response = await agent.prompt(input.prompt, chatId);
32
+
33
+ if (response.error) {
34
+ console.error('❌ ' + response.error);
35
+ } else if (!response.structuredResponse?.responseText) {
36
+ console.error('❌ Error parsing reply');
37
+ } else {
38
+ console.log(response.structuredResponse.responseText);
39
+ chatId = response.chatId;
40
+ }
41
+ } catch (error) {
42
+ console.error('❌ Error prompting agent:', error.message);
43
+ }
44
+ }
45
+ });
46
+
47
+ program.addCommand(agentCmd);
48
+ }
package/src/cli.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { registerProfileCommand } from './profiles/command.js';
5
+ import { registerDevCommands } from './dev/commands.js';
6
+ import { registerControlCommands } from './control/commands.js';
7
+ import { registerScaffolderCommands } from './scaffolder/commands.js';
8
+ import { registerAgentCommands } from './agent/commands.js';
9
+
10
+ // Create a new program
11
+ const program = new Command();
12
+
13
+ // Register CLI commands
14
+ registerDevCommands(program);
15
+ registerProfileCommand(program);
16
+ registerControlCommands(program);
17
+ registerScaffolderCommands(program);
18
+ registerAgentCommands(program);
19
+
20
+ // Help
21
+ program
22
+ .command('help [command]')
23
+ .description('❓ Display help for command');
24
+
25
+ program.addHelpText('beforeAll', `
26
+ G n a r E n g i n e - A powerful, AI ready microservice framework for modern applications.
27
+ `);
28
+
29
+ // Parse CLI input
30
+ program.parse(process.argv);
package/src/config.js ADDED
@@ -0,0 +1,8 @@
1
+
2
+ export const gnarEngineCliConfig = {
3
+
4
+ /**
5
+ * The path the Gnar Engine service core should be found in the service containers
6
+ */
7
+ corePath: '/usr/gnar_engine/app/gnarengine-service-core'
8
+ }