@cyberismo/backend 0.0.16 → 0.0.18

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.
@@ -11,7 +11,7 @@
11
11
  name="msapplication-TileImage"
12
12
  content="/cropped-favicon-270x270.png"
13
13
  />
14
- <script type="module" crossorigin src="/assets/index-D410yunq.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-CocN6Fmw.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/index-DnK7MBer.css">
16
16
  </head>
17
17
  <body>
package/package.json CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
2
  "name": "@cyberismo/backend",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Express backend for Cyberismo",
5
5
  "main": "dist/index.js",
6
6
  "keywords": [],
7
- "author": "",
7
+ "author": "sami.merila@cyberismo.com",
8
8
  "license": "AGPL-3.0",
9
+ "homepage": "https://github.com/CyberismoCom/cyberismo",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/CyberismoCom/cyberismo.git"
13
+ },
14
+ "bugs": "https://github.com/CyberismoCom/cyberismo/issues",
9
15
  "dependencies": {
10
16
  "@asciidoctor/core": "^3.0.4",
11
17
  "@hono/node-server": "^1.19.2",
12
18
  "@hono/zod-validator": "^0.7.5",
13
19
  "@types/mime-types": "^3.0.1",
14
20
  "dotenv": "^17.2.3",
15
- "hono": "^4.10.6",
21
+ "hono": "^4.10.7",
16
22
  "mime-types": "^3.0.2",
17
- "zod": "^4.1.12",
18
- "@cyberismo/data-handler": "0.0.16"
23
+ "zod": "^4.1.13",
24
+ "@cyberismo/data-handler": "0.0.18"
19
25
  },
20
26
  "devDependencies": {
21
27
  "@cyberismo/app": "0.0.2"
package/src/app.ts CHANGED
@@ -34,6 +34,7 @@ import logicProgramsRouter from './domain/logicPrograms/index.js';
34
34
  import { isSSGContext } from 'hono/ssg';
35
35
  import type { AppVars, TreeOptions } from './types.js';
36
36
  import treeMiddleware from './middleware/tree.js';
37
+ import projectRouter from './domain/project/index.js';
37
38
 
38
39
  /**
39
40
  * Create the Hono app for the backend
@@ -70,6 +71,7 @@ export function createApp(projectPath?: string, opts?: TreeOptions) {
70
71
  app.route('/api/resources', resourcesRouter);
71
72
  app.route('/api/logicPrograms', logicProgramsRouter);
72
73
  app.route('/api/labels', labelsRouter);
74
+ app.route('/api/project', projectRouter);
73
75
 
74
76
  // serve index.html for all other routes
75
77
  app.notFound(async (c) => {
@@ -0,0 +1,58 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
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.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. 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 { zValidator } from '../../middleware/zvalidator.js';
16
+ import { moduleParamSchema, updateProjectSchema } from './schema.js';
17
+ import * as projectService from './service.js';
18
+
19
+ const router = new Hono();
20
+
21
+ router.get('/', async (c) => {
22
+ const commands = c.get('commands');
23
+
24
+ const project = await projectService.getProject(commands);
25
+ return c.json(project);
26
+ });
27
+
28
+ router.patch('/', zValidator('json', updateProjectSchema), async (c) => {
29
+ const commands = c.get('commands');
30
+ const updates = c.req.valid('json');
31
+
32
+ const project = await projectService.updateProject(commands, updates);
33
+ return c.json(project);
34
+ });
35
+
36
+ router.post(
37
+ '/modules/:module/update',
38
+ zValidator('param', moduleParamSchema),
39
+ async (c) => {
40
+ const commands = c.get('commands');
41
+ const { module } = c.req.valid('param');
42
+ await projectService.updateModule(commands, module);
43
+ return c.json({ message: 'Module updated' });
44
+ },
45
+ );
46
+
47
+ router.delete(
48
+ '/modules/:module',
49
+ zValidator('param', moduleParamSchema),
50
+ async (c) => {
51
+ const commands = c.get('commands');
52
+ const { module } = c.req.valid('param');
53
+ await projectService.deleteModule(commands, module);
54
+ return c.json({ message: 'Module removed' });
55
+ },
56
+ );
57
+
58
+ export default router;
@@ -0,0 +1,23 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
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.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. 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 { z } from 'zod';
15
+
16
+ export const moduleParamSchema = z.object({
17
+ module: z.string().min(1),
18
+ });
19
+
20
+ export const updateProjectSchema = z.object({
21
+ name: z.string().optional(),
22
+ cardKeyPrefix: z.string().optional(),
23
+ });
@@ -0,0 +1,88 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
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.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. 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 CommandManager } from '@cyberismo/data-handler';
15
+
16
+ export interface ProjectModule {
17
+ name: string;
18
+ cardKeyPrefix: string;
19
+ }
20
+
21
+ export interface ProjectInfo {
22
+ name: string;
23
+ cardKeyPrefix: string;
24
+ modules: ProjectModule[];
25
+ }
26
+
27
+ export interface ProjectUpdatePayload {
28
+ name?: string;
29
+ cardKeyPrefix?: string;
30
+ }
31
+
32
+ async function toModuleInfo(
33
+ commands: CommandManager,
34
+ moduleName: string,
35
+ ): Promise<ProjectModule> {
36
+ try {
37
+ const data = await commands.showCmd.showModule(moduleName);
38
+ return {
39
+ name: data.name || moduleName,
40
+ cardKeyPrefix: data.cardKeyPrefix || moduleName,
41
+ };
42
+ } catch {
43
+ return {
44
+ name: moduleName,
45
+ cardKeyPrefix: moduleName,
46
+ };
47
+ }
48
+ }
49
+
50
+ export async function getProject(
51
+ commands: CommandManager,
52
+ ): Promise<ProjectInfo> {
53
+ const project = await commands.showCmd.showProject();
54
+ const modules = await commands.showCmd.showModules();
55
+ const moduleDetails = await Promise.all(
56
+ modules.map((mod) => toModuleInfo(commands, mod)),
57
+ );
58
+
59
+ return {
60
+ name: project.name,
61
+ cardKeyPrefix: project.prefix,
62
+ modules: moduleDetails,
63
+ };
64
+ }
65
+
66
+ export async function updateProject(
67
+ commands: CommandManager,
68
+ updates: ProjectUpdatePayload,
69
+ ): Promise<ProjectInfo> {
70
+ const { name, cardKeyPrefix } = updates;
71
+
72
+ if (cardKeyPrefix) {
73
+ await commands.renameCmd.rename(cardKeyPrefix);
74
+ }
75
+ if (name) {
76
+ await commands.project.configuration.setProjectName(name);
77
+ }
78
+
79
+ return getProject(commands);
80
+ }
81
+
82
+ export async function updateModule(commands: CommandManager, module: string) {
83
+ await commands.importCmd.updateModule(module);
84
+ }
85
+
86
+ export async function deleteModule(commands: CommandManager, module: string) {
87
+ await commands.removeCmd.remove('module', module);
88
+ }
@@ -40,12 +40,51 @@ const resourceTypes: ResourceFolderType[] = [
40
40
  'workflows',
41
41
  ];
42
42
 
43
+ async function getModules(commands: CommandManager) {
44
+ try {
45
+ const moduleNames = commands.showCmd.showModules();
46
+ return Promise.all(
47
+ moduleNames.map(async (moduleName) => {
48
+ try {
49
+ const module = await commands.showCmd.showModule(moduleName);
50
+ return { name: module.name, cardKeyPrefix: module.cardKeyPrefix };
51
+ } catch (error) {
52
+ return { name: moduleName, cardKeyPrefix: moduleName };
53
+ }
54
+ }),
55
+ );
56
+ } catch {
57
+ return [];
58
+ }
59
+ }
60
+
43
61
  export async function buildResourceTree(commands: CommandManager) {
44
62
  const project = await commands.showCmd.showProject();
45
63
  const tree: unknown[] = [];
46
- const allModuleResources: {
47
- [prefix: string]: { [type: string]: unknown[] };
48
- } = {};
64
+
65
+ const sortByDisplayName = (
66
+ nodes: { data: { displayName: string; name: string } }[],
67
+ ) =>
68
+ nodes.sort((a, b) =>
69
+ (a.data.displayName || a.data.name.split('/')[2] || '').localeCompare(
70
+ b.data.displayName || b.data.name.split('/')[2] || '',
71
+ ),
72
+ );
73
+
74
+ const modules = await getModules(commands);
75
+
76
+ // General section first (single node with project + modules metadata)
77
+ tree.push({
78
+ id: 'general-project',
79
+ type: 'general',
80
+ name: 'project',
81
+ data: {
82
+ name: project.name,
83
+ cardKeyPrefix: project.prefix,
84
+ modules,
85
+ },
86
+ readOnly: false,
87
+ });
49
88
 
50
89
  // Process each resource type
51
90
  for (const resourceType of resourceTypes) {
@@ -65,47 +104,54 @@ export async function buildResourceTree(commands: CommandManager) {
65
104
  ));
66
105
  }
67
106
 
68
- // Add root level resources
69
- if (rootResources.length > 0) {
107
+ // Sort resources by display name if present
108
+ sortByDisplayName(
109
+ rootResources as { data: { displayName: string; name: string } }[],
110
+ );
111
+
112
+ Object.values(moduleResources).forEach((resources) =>
113
+ sortByDisplayName(
114
+ resources as { data: { displayName: string; name: string } }[],
115
+ ),
116
+ );
117
+
118
+ const projectNode =
119
+ rootResources.length > 0
120
+ ? [
121
+ {
122
+ id: `${resourceType}-project`,
123
+ type: 'module',
124
+ name: 'project',
125
+ children: rootResources,
126
+ readOnly: false,
127
+ },
128
+ ]
129
+ : [];
130
+
131
+ const moduleNodes = Object.entries(moduleResources)
132
+ .map(([prefix, resources]) => ({
133
+ id: `${resourceType}-module-${prefix}`,
134
+ type: 'module',
135
+ name:
136
+ modules.find((module) => module.cardKeyPrefix === prefix)?.name ||
137
+ prefix,
138
+ prefix,
139
+ children: resources,
140
+ readOnly: true,
141
+ }))
142
+ .sort((a, b) => a.name.localeCompare(b.name));
143
+
144
+ const allResources = [...projectNode, ...moduleNodes];
145
+
146
+ // Add combined resources (project + modules nested under module nodes) under the same group
147
+ if (allResources.length > 0) {
70
148
  tree.push({
71
149
  id: resourceType,
72
150
  type: 'resourceGroup',
73
151
  name: resourceType,
74
- children: rootResources,
152
+ children: allResources,
75
153
  });
76
154
  }
77
-
78
- // Collect module resources
79
- Object.entries(moduleResources).forEach(([prefix, resources]) => {
80
- if (!allModuleResources[prefix]) {
81
- allModuleResources[prefix] = {};
82
- }
83
- allModuleResources[prefix][resourceType] = resources;
84
- });
85
- }
86
-
87
- // Build modules section
88
- if (Object.keys(allModuleResources).length > 0) {
89
- const modules = Object.entries(allModuleResources).map(
90
- ([prefix, resourcesByType]) => ({
91
- id: `modules-${prefix}`,
92
- type: 'module',
93
- name: prefix,
94
- children: Object.entries(resourcesByType).map(([type, resources]) => ({
95
- id: `modules-${prefix}-${type}`,
96
- type: 'resourceGroup',
97
- name: type,
98
- children: resources,
99
- })),
100
- }),
101
- );
102
-
103
- tree.push({
104
- id: 'modules',
105
- type: 'modulesGroup',
106
- name: 'modules',
107
- children: modules,
108
- });
109
155
  }
110
156
 
111
157
  return tree;