@cyberismo/backend 0.0.21 → 0.0.22

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 (126) hide show
  1. package/dist/app.d.ts +5 -2
  2. package/dist/app.js +22 -9
  3. package/dist/app.js.map +1 -1
  4. package/dist/auth/index.d.ts +16 -0
  5. package/dist/auth/index.js +15 -0
  6. package/dist/auth/index.js.map +1 -0
  7. package/dist/auth/keycloak.d.ts +27 -0
  8. package/dist/auth/keycloak.js +81 -0
  9. package/dist/auth/keycloak.js.map +1 -0
  10. package/dist/auth/mock.d.ts +23 -0
  11. package/dist/auth/mock.js +28 -0
  12. package/dist/auth/mock.js.map +1 -0
  13. package/dist/auth/types.d.ts +16 -0
  14. package/dist/auth/types.js +14 -0
  15. package/dist/auth/types.js.map +1 -0
  16. package/dist/domain/auth/index.d.ts +14 -0
  17. package/dist/domain/auth/index.js +30 -0
  18. package/dist/domain/auth/index.js.map +1 -0
  19. package/dist/domain/calculations/index.js +3 -1
  20. package/dist/domain/calculations/index.js.map +1 -1
  21. package/dist/domain/calculations/service.js +13 -11
  22. package/dist/domain/calculations/service.js.map +1 -1
  23. package/dist/domain/cardTypes/index.js +5 -3
  24. package/dist/domain/cardTypes/index.js.map +1 -1
  25. package/dist/domain/cardTypes/service.js +24 -72
  26. package/dist/domain/cardTypes/service.js.map +1 -1
  27. package/dist/domain/cards/index.js +19 -15
  28. package/dist/domain/cards/index.js.map +1 -1
  29. package/dist/domain/cards/lib.js +95 -93
  30. package/dist/domain/cards/lib.js.map +1 -1
  31. package/dist/domain/cards/service.d.ts +2 -1
  32. package/dist/domain/cards/service.js +60 -87
  33. package/dist/domain/cards/service.js.map +1 -1
  34. package/dist/domain/fieldTypes/index.js +4 -2
  35. package/dist/domain/fieldTypes/index.js.map +1 -1
  36. package/dist/domain/graphModels/index.js +3 -1
  37. package/dist/domain/graphModels/index.js.map +1 -1
  38. package/dist/domain/graphViews/index.js +3 -1
  39. package/dist/domain/graphViews/index.js.map +1 -1
  40. package/dist/domain/labels/index.js +4 -2
  41. package/dist/domain/labels/index.js.map +1 -1
  42. package/dist/domain/labels/service.d.ts +1 -1
  43. package/dist/domain/labels/service.js +2 -2
  44. package/dist/domain/labels/service.js.map +1 -1
  45. package/dist/domain/linkTypes/index.js +4 -2
  46. package/dist/domain/linkTypes/index.js.map +1 -1
  47. package/dist/domain/logicPrograms/index.js +3 -1
  48. package/dist/domain/logicPrograms/index.js.map +1 -1
  49. package/dist/domain/mcp/index.d.ts +15 -0
  50. package/dist/domain/mcp/index.js +127 -0
  51. package/dist/domain/mcp/index.js.map +1 -0
  52. package/dist/domain/project/index.js +7 -5
  53. package/dist/domain/project/index.js.map +1 -1
  54. package/dist/domain/project/service.js +18 -14
  55. package/dist/domain/project/service.js.map +1 -1
  56. package/dist/domain/reports/index.js +3 -1
  57. package/dist/domain/reports/index.js.map +1 -1
  58. package/dist/domain/resources/index.js +6 -4
  59. package/dist/domain/resources/index.js.map +1 -1
  60. package/dist/domain/resources/service.js +66 -64
  61. package/dist/domain/resources/service.js.map +1 -1
  62. package/dist/domain/templates/index.js +5 -3
  63. package/dist/domain/templates/index.js.map +1 -1
  64. package/dist/domain/tree/index.js +3 -1
  65. package/dist/domain/tree/index.js.map +1 -1
  66. package/dist/domain/workflows/index.js +3 -1
  67. package/dist/domain/workflows/index.js.map +1 -1
  68. package/dist/export.d.ts +6 -5
  69. package/dist/export.js +15 -11
  70. package/dist/export.js.map +1 -1
  71. package/dist/index.d.ts +8 -2
  72. package/dist/index.js +5 -3
  73. package/dist/index.js.map +1 -1
  74. package/dist/main.js +29 -2
  75. package/dist/main.js.map +1 -1
  76. package/dist/middleware/auth.d.ts +40 -0
  77. package/dist/middleware/auth.js +68 -0
  78. package/dist/middleware/auth.js.map +1 -0
  79. package/dist/middleware/commandManager.d.ts +2 -2
  80. package/dist/middleware/commandManager.js +9 -11
  81. package/dist/middleware/commandManager.js.map +1 -1
  82. package/dist/public/THIRD-PARTY.txt +37 -11
  83. package/dist/public/assets/index-B_lh6qtv.css +1 -0
  84. package/dist/public/assets/{index-Ca10XaMv.js → index-CEol8Bfi.js} +43823 -43030
  85. package/dist/public/config.json +1 -0
  86. package/dist/public/index.html +2 -2
  87. package/dist/types.d.ts +25 -0
  88. package/dist/types.js +13 -1
  89. package/dist/types.js.map +1 -1
  90. package/package.json +8 -5
  91. package/src/app.ts +34 -14
  92. package/src/auth/index.ts +17 -0
  93. package/src/auth/keycloak.ts +109 -0
  94. package/src/auth/mock.ts +38 -0
  95. package/src/auth/types.ts +18 -0
  96. package/src/domain/auth/index.ts +35 -0
  97. package/src/domain/calculations/index.ts +13 -6
  98. package/src/domain/calculations/service.ts +16 -14
  99. package/src/domain/cardTypes/index.ts +24 -16
  100. package/src/domain/cardTypes/service.ts +41 -95
  101. package/src/domain/cards/index.ts +62 -44
  102. package/src/domain/cards/lib.ts +105 -100
  103. package/src/domain/cards/service.ts +73 -89
  104. package/src/domain/fieldTypes/index.ts +23 -16
  105. package/src/domain/graphModels/index.ts +13 -6
  106. package/src/domain/graphViews/index.ts +13 -6
  107. package/src/domain/labels/index.ts +5 -2
  108. package/src/domain/labels/service.ts +2 -2
  109. package/src/domain/linkTypes/index.ts +14 -7
  110. package/src/domain/logicPrograms/index.ts +3 -0
  111. package/src/domain/mcp/index.ts +159 -0
  112. package/src/domain/project/index.ts +17 -8
  113. package/src/domain/project/service.ts +20 -16
  114. package/src/domain/reports/index.ts +13 -6
  115. package/src/domain/resources/index.ts +6 -1
  116. package/src/domain/resources/service.ts +102 -97
  117. package/src/domain/templates/index.ts +31 -19
  118. package/src/domain/tree/index.ts +3 -1
  119. package/src/domain/workflows/index.ts +13 -6
  120. package/src/export.ts +16 -13
  121. package/src/index.ts +10 -3
  122. package/src/main.ts +44 -2
  123. package/src/middleware/auth.ts +90 -0
  124. package/src/middleware/commandManager.ts +11 -14
  125. package/src/types.ts +27 -0
  126. package/dist/public/assets/index-CRSBseQM.css +0 -1
@@ -1,3 +1,4 @@
1
1
  {
2
+ "logoutUrl": "",
2
3
  "staticMode": false
3
4
  }
@@ -11,8 +11,8 @@
11
11
  name="msapplication-TileImage"
12
12
  content="/cropped-favicon-270x270.png"
13
13
  />
14
- <script type="module" crossorigin src="/assets/index-Ca10XaMv.js"></script>
15
- <link rel="stylesheet" crossorigin href="/assets/index-CRSBseQM.css">
14
+ <script type="module" crossorigin src="/assets/index-CEol8Bfi.js"></script>
15
+ <link rel="stylesheet" crossorigin href="/assets/index-B_lh6qtv.css">
16
16
  </head>
17
17
  <body>
18
18
  <div id="root"></div>
package/dist/types.d.ts CHANGED
@@ -21,8 +21,33 @@ export interface TreeOptions {
21
21
  recursive?: boolean;
22
22
  cardKey?: string;
23
23
  }
24
+ /**
25
+ * User roles - checked directly in middleware
26
+ * Role hierarchy: Admin > Editor > Reader
27
+ * - Admin: All operations including configuration
28
+ * - Editor: Can create, edit, and manage content but not config
29
+ * - Reader: Can only view content
30
+ */
31
+ export declare enum UserRole {
32
+ Admin = "admin",
33
+ Editor = "editor",
34
+ Reader = "reader"
35
+ }
36
+ /**
37
+ * User information returned by the /me endpoint
38
+ */
39
+ export interface UserInfo {
40
+ id: string;
41
+ email: string;
42
+ name: string;
43
+ role: UserRole;
44
+ }
45
+ /**
46
+ * Extended app variables including authentication info
47
+ */
24
48
  export interface AppVars {
25
49
  tree?: TreeOptions;
50
+ user?: UserInfo;
26
51
  }
27
52
  export type AppContext = Context<{
28
53
  Variables: AppVars;
package/dist/types.js CHANGED
@@ -10,5 +10,17 @@
10
10
  details. You should have received a copy of the GNU Affero General Public
11
11
  License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
12
  */
13
- export {};
13
+ /**
14
+ * User roles - checked directly in middleware
15
+ * Role hierarchy: Admin > Editor > Reader
16
+ * - Admin: All operations including configuration
17
+ * - Editor: Can create, edit, and manage content but not config
18
+ * - Reader: Can only view content
19
+ */
20
+ export var UserRole;
21
+ (function (UserRole) {
22
+ UserRole["Admin"] = "admin";
23
+ UserRole["Editor"] = "editor";
24
+ UserRole["Reader"] = "reader";
25
+ })(UserRole || (UserRole = {}));
14
26
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE;AAiBF;;;;;;GAMG;AACH,MAAM,CAAN,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,2BAAe,CAAA;IACf,6BAAiB,CAAA;IACjB,6BAAiB,CAAA;AACnB,CAAC,EAJW,QAAQ,KAAR,QAAQ,QAInB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyberismo/backend",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "Express backend for Cyberismo",
5
5
  "main": "dist/index.js",
6
6
  "keywords": [],
@@ -14,14 +14,17 @@
14
14
  "bugs": "https://github.com/CyberismoCom/cyberismo/issues",
15
15
  "dependencies": {
16
16
  "@asciidoctor/core": "^3.0.4",
17
+ "@modelcontextprotocol/sdk": "^1.27.1",
17
18
  "@hono/node-server": "^1.19.2",
18
19
  "@hono/zod-validator": "^0.7.6",
19
20
  "@types/mime-types": "^3.0.1",
20
- "dotenv": "^17.2.3",
21
+ "dotenv": "^17.3.1",
21
22
  "hono": "^4.10.7",
23
+ "jose": "^6.1.3",
22
24
  "mime-types": "^3.0.2",
23
25
  "zod": "^4.3.6",
24
- "@cyberismo/data-handler": "0.0.21"
26
+ "@cyberismo/data-handler": "0.0.22",
27
+ "@cyberismo/mcp": "0.0.21"
25
28
  },
26
29
  "devDependencies": {
27
30
  "@cyberismo/app": "0.0.2"
@@ -37,8 +40,8 @@
37
40
  ],
38
41
  "scripts": {
39
42
  "start": "tsx src/main.ts",
40
- "start-e2e": "node dist/main.js",
41
- "dev": "tsx watch src/main.ts",
43
+ "start-e2e": "cross-env AUTH_MODE=mock node dist/main.js",
44
+ "dev": "cross-env AUTH_MODE=mock tsx watch src/main.ts",
42
45
  "debug": "tsx --inspect-brk src/main.ts",
43
46
  "export": "pnpm build && node dist/main.js --export",
44
47
  "build": "tsc -p tsconfig.build.json && shx rm -rf ./dist/public && shx cp -r ../app/dist ./dist/public",
package/src/app.ts CHANGED
@@ -12,7 +12,6 @@
12
12
  */
13
13
  import { Hono } from 'hono';
14
14
  import { staticFrontendDirRelative } from './utils.js';
15
- import { cors } from 'hono/cors';
16
15
  import { serveStatic } from '@hono/node-server/serve-static';
17
16
  import { attachCommandManager } from './middleware/commandManager.js';
18
17
  import calculationsRouter from './domain/calculations/index.js';
@@ -32,31 +31,42 @@ import path from 'node:path';
32
31
  import resourcesRouter from './domain/resources/index.js';
33
32
  import logicProgramsRouter from './domain/logicPrograms/index.js';
34
33
  import { isSSGContext } from 'hono/ssg';
34
+ import type { CommandManager } from '@cyberismo/data-handler';
35
35
  import type { AppVars, TreeOptions } from './types.js';
36
36
  import treeMiddleware from './middleware/tree.js';
37
37
  import projectRouter from './domain/project/index.js';
38
+ import mcpRouter from './domain/mcp/index.js';
39
+ import { createAuthRouter } from './domain/auth/index.js';
40
+ import { createAuthMiddleware } from './middleware/auth.js';
41
+ import type { AuthProvider } from './auth/types.js';
38
42
 
39
43
  /**
40
44
  * Create the Hono app for the backend
41
- * @param projectPath - Path to the project
45
+ * @param authProvider - Authentication provider
46
+ * @param commands - CommandManager instance for the project
42
47
  */
43
- export function createApp(projectPath?: string, opts?: TreeOptions) {
48
+ export function createApp(
49
+ authProvider: AuthProvider,
50
+ commands: CommandManager,
51
+ opts?: TreeOptions,
52
+ ) {
44
53
  const app = new Hono<{ Variables: AppVars }>();
45
54
 
46
- app.use('/api', cors());
47
-
48
- app.use(
49
- '*',
50
- serveStatic({
51
- root: staticFrontendDirRelative,
52
- }),
53
- );
54
-
55
55
  app.use(treeMiddleware(opts));
56
- // Attach CommandManager to all requests
57
- app.use(attachCommandManager(projectPath));
56
+ // Apply authentication middleware to all API and MCP routes
57
+ app.use('/api/*', createAuthMiddleware(authProvider));
58
+ app.use('/mcp', createAuthMiddleware(authProvider));
59
+ app.use('/mcp/*', createAuthMiddleware(authProvider));
58
60
 
61
+ // Attach CommandManager to API and MCP routes
62
+ const commandManagerMiddleware = attachCommandManager(commands);
63
+ app.use('/api/*', commandManagerMiddleware);
64
+ app.use('/mcp', commandManagerMiddleware);
65
+ app.use('/mcp/*', commandManagerMiddleware);
59
66
  // Wire up routes
67
+ app.route('/api/auth', createAuthRouter());
68
+
69
+ // Mount routers
60
70
  app.route('/api/calculations', calculationsRouter);
61
71
  app.route('/api/cards', cardsRouter);
62
72
  app.route('/api/cardTypes', cardTypesRouter);
@@ -73,6 +83,16 @@ export function createApp(projectPath?: string, opts?: TreeOptions) {
73
83
  app.route('/api/labels', labelsRouter);
74
84
  app.route('/api/project', projectRouter);
75
85
 
86
+ // MCP endpoint for AI assistant integration
87
+ app.route('/mcp', mcpRouter);
88
+
89
+ app.use(
90
+ '*',
91
+ serveStatic({
92
+ root: staticFrontendDirRelative,
93
+ }),
94
+ );
95
+
76
96
  // serve index.html for all other routes
77
97
  app.notFound(async (c) => {
78
98
  if (c.req.path.startsWith('/api')) {
@@ -0,0 +1,17 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ export type { AuthProvider } from './types.js';
15
+ export { MockAuthProvider } from './mock.js';
16
+ export { KeycloakAuthProvider } from './keycloak.js';
17
+ export type { KeycloakConfig } from './keycloak.js';
@@ -0,0 +1,109 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
15
+ import type { JWTPayload } from 'jose';
16
+ import { UserRole } from '../types.js';
17
+ import type { UserInfo } from '../types.js';
18
+ import type { AuthProvider } from './types.js';
19
+
20
+ export interface KeycloakConfig {
21
+ issuer: string;
22
+ audience: string;
23
+ }
24
+
25
+ interface KeycloakJWTPayload extends JWTPayload {
26
+ email?: string;
27
+ name?: string;
28
+ preferred_username?: string;
29
+ realm_access?: {
30
+ roles?: string[];
31
+ };
32
+ }
33
+
34
+ export class KeycloakAuthProvider implements AuthProvider {
35
+ private readonly issuer: string;
36
+ private readonly audience: string;
37
+ private jwks: ReturnType<typeof createRemoteJWKSet> | null = null;
38
+
39
+ constructor(config: KeycloakConfig) {
40
+ this.issuer = config.issuer;
41
+ this.audience = config.audience;
42
+ }
43
+
44
+ private getJWKS(): ReturnType<typeof createRemoteJWKSet> {
45
+ if (!this.jwks) {
46
+ const jwksUrl = new URL(
47
+ `${this.issuer.replace(/\/$/, '')}/protocol/openid-connect/certs`,
48
+ );
49
+ this.jwks = createRemoteJWKSet(jwksUrl);
50
+ }
51
+ return this.jwks;
52
+ }
53
+
54
+ async authenticate(req: Request): Promise<UserInfo | null> {
55
+ const authHeader = req.headers.get('authorization');
56
+ if (!authHeader) {
57
+ return null;
58
+ }
59
+
60
+ const token = authHeader.replace(/^Bearer\s+/i, '');
61
+ if (!token) {
62
+ return null;
63
+ }
64
+
65
+ try {
66
+ const jwks = this.getJWKS();
67
+ const { payload } = await jwtVerify(token, jwks, {
68
+ issuer: this.issuer,
69
+ audience: this.audience,
70
+ });
71
+
72
+ const claims = payload as KeycloakJWTPayload;
73
+ const role = this.mapRole(claims.realm_access?.roles);
74
+
75
+ if (!claims.sub) {
76
+ throw new Error('Missing sub');
77
+ }
78
+ if (!claims.email) {
79
+ throw new Error('Missing email');
80
+ }
81
+
82
+ return {
83
+ id: claims.sub,
84
+ email: claims.email,
85
+ name: claims.name ?? claims.preferred_username ?? 'Unknown',
86
+ role,
87
+ };
88
+ } catch {
89
+ // TODO: add proper logging
90
+ return null;
91
+ }
92
+ }
93
+
94
+ private mapRole(roles?: string[]): UserRole {
95
+ if (!roles) {
96
+ throw new Error('Token missing realm_access roles');
97
+ }
98
+ if (roles.includes('admin')) {
99
+ return UserRole.Admin;
100
+ }
101
+ if (roles.includes('editor')) {
102
+ return UserRole.Editor;
103
+ }
104
+ if (roles.includes('reader')) {
105
+ return UserRole.Reader;
106
+ }
107
+ throw new Error('No recognized role found in token');
108
+ }
109
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { UserRole } from '../types.js';
15
+ import type { UserInfo } from '../types.js';
16
+ import type { AuthProvider } from './types.js';
17
+
18
+ export interface MockUserConfig {
19
+ name?: string;
20
+ email?: string;
21
+ }
22
+
23
+ export class MockAuthProvider implements AuthProvider {
24
+ private readonly userConfig: MockUserConfig;
25
+
26
+ constructor(config?: MockUserConfig) {
27
+ this.userConfig = config ?? {};
28
+ }
29
+
30
+ async authenticate(): Promise<UserInfo> {
31
+ return {
32
+ id: 'mock-user',
33
+ email: this.userConfig.email ?? 'admin@cyberismo.local',
34
+ name: this.userConfig.name ?? 'Local Admin',
35
+ role: UserRole.Admin,
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import type { UserInfo } from '../types.js';
15
+
16
+ export interface AuthProvider {
17
+ authenticate(req: Request): Promise<UserInfo | null>;
18
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2026
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { Hono } from 'hono';
15
+ import { getCurrentUser } from '../../middleware/auth.js';
16
+
17
+ export function createAuthRouter() {
18
+ const router = new Hono();
19
+
20
+ /**
21
+ * GET /api/auth/me
22
+ * Returns the current user's information (id, email, name, role)
23
+ */
24
+ router.get('/me', async (c) => {
25
+ const user = getCurrentUser(c);
26
+
27
+ if (!user) {
28
+ return c.json({ error: 'Unauthorized' }, 401);
29
+ }
30
+
31
+ return c.json(user);
32
+ });
33
+
34
+ return router;
35
+ }
@@ -15,6 +15,8 @@ import { Hono } from 'hono';
15
15
  import * as calculationService from './service.js';
16
16
  import { createCalculationSchema } from './schema.js';
17
17
  import { zValidator } from '../../middleware/zvalidator.js';
18
+ import { UserRole } from '../../types.js';
19
+ import { requireRole } from '../../middleware/auth.js';
18
20
 
19
21
  const router = new Hono();
20
22
 
@@ -43,12 +45,17 @@ const router = new Hono();
43
45
  * 500:
44
46
  * description: Server error
45
47
  */
46
- router.post('/', zValidator('json', createCalculationSchema), async (c) => {
47
- const commands = c.get('commands');
48
- const { identifier } = c.req.valid('json');
48
+ router.post(
49
+ '/',
50
+ requireRole(UserRole.Admin),
51
+ zValidator('json', createCalculationSchema),
52
+ async (c) => {
53
+ const commands = c.get('commands');
54
+ const { identifier } = c.req.valid('json');
49
55
 
50
- await calculationService.createCalculation(commands, identifier);
51
- return c.json({ message: 'Calculation created successfully' });
52
- });
56
+ await calculationService.createCalculation(commands, identifier);
57
+ return c.json({ message: 'Calculation created successfully' });
58
+ },
59
+ );
53
60
 
54
61
  export default router;
@@ -26,20 +26,22 @@ export async function createCalculation(
26
26
  commands: CommandManager,
27
27
  identifier: string,
28
28
  ) {
29
- await commands.createCmd.createCalculation(identifier);
29
+ await commands.atomic(async () => {
30
+ await commands.createCmd.createCalculation(identifier);
30
31
 
31
- // Set displayName to capitalized version of identifier
32
- const project = await commands.showCmd.showProject();
33
- await updateResourceWithOperation(
34
- commands,
35
- { prefix: project.prefix, type: 'calculations', identifier },
36
- {
37
- updateKey: { key: 'displayName' },
38
- operation: {
39
- name: 'change',
40
- target: '',
41
- to: capitalize(identifier),
32
+ // Set displayName to capitalized version of identifier
33
+ const project = await commands.showCmd.showProject();
34
+ await updateResourceWithOperation(
35
+ commands,
36
+ { prefix: project.prefix, type: 'calculations', identifier },
37
+ {
38
+ updateKey: { key: 'displayName' },
39
+ operation: {
40
+ name: 'change',
41
+ target: '',
42
+ to: capitalize(identifier),
43
+ },
42
44
  },
43
- },
44
- );
45
+ );
46
+ }, `Create calculation ${identifier}`);
45
47
  }
@@ -19,6 +19,8 @@ import {
19
19
  fieldVisibilityBodySchema,
20
20
  } from './schema.js';
21
21
  import { zValidator } from '../../middleware/zvalidator.js';
22
+ import { UserRole } from '../../types.js';
23
+ import { requireRole } from '../../middleware/auth.js';
22
24
 
23
25
  const router = new Hono();
24
26
 
@@ -36,7 +38,7 @@ const router = new Hono();
36
38
  * 500:
37
39
  * description: project_path not set or other internal error
38
40
  */
39
- router.get('/', async (c) => {
41
+ router.get('/', requireRole(UserRole.Reader), async (c) => {
40
42
  const commands = c.get('commands');
41
43
 
42
44
  try {
@@ -80,22 +82,27 @@ router.get('/', async (c) => {
80
82
  * 500:
81
83
  * description: Server error
82
84
  */
83
- router.post('/', zValidator('json', createCardTypeSchema), async (c) => {
84
- const commands = c.get('commands');
85
- const { identifier, workflowName } = c.req.valid('json');
85
+ router.post(
86
+ '/',
87
+ requireRole(UserRole.Admin),
88
+ zValidator('json', createCardTypeSchema),
89
+ async (c) => {
90
+ const commands = c.get('commands');
91
+ const { identifier, workflowName } = c.req.valid('json');
86
92
 
87
- try {
88
- await cardTypeService.createCardType(commands, identifier, workflowName);
89
- return c.json({ message: 'Card type created successfully' });
90
- } catch (error) {
91
- return c.json(
92
- {
93
- error: `${error instanceof Error ? error.message : 'Unknown error'}`,
94
- },
95
- 500,
96
- );
97
- }
98
- });
93
+ try {
94
+ await cardTypeService.createCardType(commands, identifier, workflowName);
95
+ return c.json({ message: 'Card type created successfully' });
96
+ } catch (error) {
97
+ return c.json(
98
+ {
99
+ error: `${error instanceof Error ? error.message : 'Unknown error'}`,
100
+ },
101
+ 500,
102
+ );
103
+ }
104
+ },
105
+ );
99
106
 
100
107
  /**
101
108
  * @swagger
@@ -142,6 +149,7 @@ router.post('/', zValidator('json', createCardTypeSchema), async (c) => {
142
149
  */
143
150
  router.patch(
144
151
  '/:cardTypeName/field-visibility',
152
+ requireRole(UserRole.Admin),
145
153
  zValidator('param', cardTypeNameParamSchema),
146
154
  zValidator('json', fieldVisibilityBodySchema),
147
155
  async (c) => {