@cyberismo/backend 0.0.22 → 0.0.24
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/dist/app.js +3 -1
- package/dist/app.js.map +1 -1
- package/dist/domain/cards/index.js +169 -12
- package/dist/domain/cards/index.js.map +1 -1
- package/dist/domain/cards/lib.js +4 -4
- package/dist/domain/cards/lib.js.map +1 -1
- package/dist/domain/cards/presence.d.ts +50 -0
- package/dist/domain/cards/presence.js +93 -0
- package/dist/domain/cards/presence.js.map +1 -0
- package/dist/domain/cards/schema.d.ts +55 -0
- package/dist/domain/cards/schema.js +44 -0
- package/dist/domain/cards/schema.js.map +1 -0
- package/dist/domain/cards/service.d.ts +7 -2
- package/dist/domain/cards/service.js +37 -4
- package/dist/domain/cards/service.js.map +1 -1
- package/dist/domain/connectors/index.d.ts +15 -0
- package/dist/domain/connectors/index.js +37 -0
- package/dist/domain/connectors/index.js.map +1 -0
- package/dist/domain/connectors/service.d.ts +23 -0
- package/dist/domain/connectors/service.js +46 -0
- package/dist/domain/connectors/service.js.map +1 -0
- package/dist/domain/project/index.js +12 -1
- package/dist/domain/project/index.js.map +1 -1
- package/dist/domain/project/schema.d.ts +3 -0
- package/dist/domain/project/schema.js +8 -0
- package/dist/domain/project/schema.js.map +1 -1
- package/dist/domain/project/service.d.ts +3 -1
- package/dist/domain/project/service.js +7 -1
- package/dist/domain/project/service.js.map +1 -1
- package/dist/domain/resources/index.js +63 -1
- package/dist/domain/resources/index.js.map +1 -1
- package/dist/domain/resources/schema.d.ts +9 -0
- package/dist/domain/resources/schema.js +8 -1
- package/dist/domain/resources/schema.js.map +1 -1
- package/dist/domain/resources/service.d.ts +12 -0
- package/dist/domain/resources/service.js +47 -4
- package/dist/domain/resources/service.js.map +1 -1
- package/dist/domain/tree/service.js +0 -1
- package/dist/domain/tree/service.js.map +1 -1
- package/dist/export.js +1 -2
- package/dist/export.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/public/THIRD-PARTY.txt +4283 -1734
- package/dist/public/assets/arc-DFTvTCxD.js +1 -0
- package/dist/public/assets/architecture-YZFGNWBL-DsWVZJri.js +1 -0
- package/dist/public/assets/architectureDiagram-Q4EWVU46-AN0fWZIG.js +36 -0
- package/dist/public/assets/array-xS8TccZC.js +1 -0
- package/dist/public/assets/blockDiagram-DXYQGD6D-RrIidZT3.js +132 -0
- package/dist/public/assets/c4Diagram-AHTNJAMY-CW7AUKhY.js +10 -0
- package/dist/public/assets/channel-BxffgrNT.js +1 -0
- package/dist/public/assets/chunk-2KRD3SAO-D5XH6bj9.js +1 -0
- package/dist/public/assets/chunk-336JU56O-CVDEj5x8.js +2 -0
- package/dist/public/assets/chunk-426QAEUC-CIWkCWTf.js +1 -0
- package/dist/public/assets/chunk-4BX2VUAB-O8dxzEpn.js +1 -0
- package/dist/public/assets/chunk-4TB4RGXK-Bt4fWDlh.js +206 -0
- package/dist/public/assets/chunk-55IACEB6-R-yr7oHq.js +1 -0
- package/dist/public/assets/chunk-5FUZZQ4R-D7L4hZzZ.js +62 -0
- package/dist/public/assets/chunk-5PVQY5BW-D46cRkay.js +2 -0
- package/dist/public/assets/chunk-67CJDMHE-1fLguPDq.js +1 -0
- package/dist/public/assets/chunk-7N4EOEYR-BPbEiVZr.js +1 -0
- package/dist/public/assets/chunk-AA7GKIK3-DZOqN73n.js +1 -0
- package/dist/public/assets/chunk-BSJP7CBP-BQPMRa-Q.js +1 -0
- package/dist/public/assets/chunk-CFjPhJqf.js +1 -0
- package/dist/public/assets/chunk-CIAEETIT-CoKBG93U.js +1 -0
- package/dist/public/assets/chunk-EDXVE4YY-BxmMvdBY.js +1 -0
- package/dist/public/assets/chunk-ENJZ2VHE-BrjxYY_T.js +10 -0
- package/dist/public/assets/chunk-FMBD7UC4-DChB7ne2.js +15 -0
- package/dist/public/assets/chunk-FOC6F5B3-DyK4SoM2.js +1 -0
- package/dist/public/assets/chunk-ICPOFSXX-6-ABzkfw.js +122 -0
- package/dist/public/assets/chunk-K5T4RW27-432kjUXO.js +94 -0
- package/dist/public/assets/chunk-KGLVRYIC-ChpiuJys.js +1 -0
- package/dist/public/assets/chunk-LIHQZDEY-C65uflEj.js +1 -0
- package/dist/public/assets/chunk-ORNJ4GCN-DPvOqXKg.js +1 -0
- package/dist/public/assets/chunk-OYMX7WX6-BecMUsrL.js +231 -0
- package/dist/public/assets/chunk-QZHKN3VN-d1i_Lm1t.js +1 -0
- package/dist/public/assets/chunk-U2HBQHQK-C6yvwOAo.js +70 -0
- package/dist/public/assets/chunk-X2U36JSP-D1C3tOED.js +1 -0
- package/dist/public/assets/chunk-XPW4576I-BH37LuuF.js +32 -0
- package/dist/public/assets/chunk-YZCP3GAM-D83gHdmx.js +1 -0
- package/dist/public/assets/chunk-ZZ45TVLE-DrCjtkdu.js +1 -0
- package/dist/public/assets/classDiagram-6PBFFD2Q-CRYacdjd.js +1 -0
- package/dist/public/assets/classDiagram-v2-HSJHXN6E-qOJXC_A9.js +1 -0
- package/dist/public/assets/clone-Bno0nirE.js +1 -0
- package/dist/public/assets/colors-DZGTowqM.js +1 -0
- package/dist/public/assets/cose-bilkent-S5V4N54A-BdSMsFO7.js +1 -0
- package/dist/public/assets/cytoscape.esm-DpmcErfs.js +321 -0
- package/dist/public/assets/dagre-D0KOcyEK.js +1 -0
- package/dist/public/assets/dagre-KV5264BT-C_l1Bck_.js +4 -0
- package/dist/public/assets/defaultLocale-B6dPnyNg.js +1 -0
- package/dist/public/assets/diagram-5BDNPKRD-D6FxiIv1.js +10 -0
- package/dist/public/assets/diagram-G4DWMVQ6-COGBv7tQ.js +24 -0
- package/dist/public/assets/diagram-MMDJMWI5-U6E9w334.js +43 -0
- package/dist/public/assets/diagram-TYMM5635-Tvfdnlo4.js +24 -0
- package/dist/public/assets/dist-C6vvxvVV.js +1 -0
- package/dist/public/assets/erDiagram-SMLLAGMA-VcPXkmy9.js +85 -0
- package/dist/public/assets/flatten-BKeNCJRx.js +1 -0
- package/dist/public/assets/flowDiagram-DWJPFMVM-PRQdlypB.js +162 -0
- package/dist/public/assets/ganttDiagram-T4ZO3ILL-CSVaTciZ.js +292 -0
- package/dist/public/assets/gitGraph-7Q5UKJZL-C6GVmHC4.js +1 -0
- package/dist/public/assets/gitGraphDiagram-UUTBAWPF-DMa-Gcy1.js +106 -0
- package/dist/public/assets/graphlib-BYojtb6k.js +1 -0
- package/dist/public/assets/identity-nF-8qK2-.js +1 -0
- package/dist/public/assets/index-oVbUFngg.js +737 -0
- package/dist/public/assets/index-ypsafPwV.css +1 -0
- package/dist/public/assets/info-OMHHGYJF-CjsmQeYR.js +1 -0
- package/dist/public/assets/infoDiagram-42DDH7IO-COL_Na4w.js +2 -0
- package/dist/public/assets/init-B9nbfZCT.js +1 -0
- package/dist/public/assets/isEmpty-Di-NpihJ.js +1 -0
- package/dist/public/assets/ishikawaDiagram-UXIWVN3A-B3HoSFaq.js +70 -0
- package/dist/public/assets/journeyDiagram-VCZTEJTY-kH30WNF3.js +139 -0
- package/dist/public/assets/kanban-definition-6JOO6SKY-RdjEsejt.js +89 -0
- package/dist/public/assets/katex-Bfn1OZEl.js +257 -0
- package/dist/public/assets/line-DrKJYWGi.js +1 -0
- package/dist/public/assets/linear-C8MDrvxz.js +1 -0
- package/dist/public/assets/mermaid-parser.core-BeZK8g2G.js +4 -0
- package/dist/public/assets/mindmap-definition-QFDTVHPH-CNWWI4MF.js +96 -0
- package/dist/public/assets/ordinal-BXaEVJbT.js +1 -0
- package/dist/public/assets/packet-4T2RLAQJ-Dq0Z9G3r.js +1 -0
- package/dist/public/assets/path-C7Bv0Qdk.js +1 -0
- package/dist/public/assets/pie-ZZUOXDRM-Coeb8Atk.js +1 -0
- package/dist/public/assets/pieDiagram-DEJITSTG-CrDW-30P.js +30 -0
- package/dist/public/assets/quadrantDiagram-34T5L4WZ-BoDA5WQ-.js +7 -0
- package/dist/public/assets/radar-PYXPWWZC-BF2NN2TG.js +1 -0
- package/dist/public/assets/range-DuD7Go1R.js +1 -0
- package/dist/public/assets/reduce-5tLTMRkg.js +1 -0
- package/dist/public/assets/requirementDiagram-MS252O5E-BnG7epLH.js +84 -0
- package/dist/public/assets/rough.esm-DE7XMpOC.js +1 -0
- package/dist/public/assets/sankeyDiagram-XADWPNL6-oyTFoxhB.js +10 -0
- package/dist/public/assets/sequenceDiagram-FGHM5R23-hwG2jT_2.js +157 -0
- package/dist/public/assets/src-ps-3oTnY.js +1 -0
- package/dist/public/assets/stateDiagram-FHFEXIEX-BiYqw63j.js +1 -0
- package/dist/public/assets/stateDiagram-v2-QKLJ7IA2-CggSrcDh.js +1 -0
- package/dist/public/assets/time-Bgnk7ODM.js +1 -0
- package/dist/public/assets/timeline-definition-GMOUNBTQ-9MnBod43.js +120 -0
- package/dist/public/assets/treeView-SZITEDCU-IRdgIoRV.js +1 -0
- package/dist/public/assets/treemap-DK8fitek.js +1 -0
- package/dist/public/assets/treemap-W4RFUUIX-C-JBwyWW.js +1 -0
- package/dist/public/assets/vennDiagram-DHZGUBPP-sEA6JNL-.js +34 -0
- package/dist/public/assets/wardley-RL74JXVD-DdU04Q7E.js +1 -0
- package/dist/public/assets/wardleyDiagram-NUSXRM2D-Dws321pY.js +20 -0
- package/dist/public/assets/xychartDiagram-5P7HB3ND-BcqyvmmW.js +7 -0
- package/dist/public/images/broken_link.svg +7 -0
- package/dist/public/index.html +31 -2
- package/package.json +7 -7
- package/src/app.ts +3 -1
- package/src/domain/cards/index.ts +271 -48
- package/src/domain/cards/lib.ts +6 -5
- package/src/domain/cards/presence.ts +124 -0
- package/src/domain/cards/schema.ts +50 -0
- package/src/domain/cards/service.ts +91 -5
- package/src/domain/connectors/index.ts +39 -0
- package/src/domain/connectors/service.ts +67 -0
- package/src/domain/project/index.ts +23 -1
- package/src/domain/project/schema.ts +9 -0
- package/src/domain/project/service.ts +18 -2
- package/src/domain/resources/index.ts +74 -0
- package/src/domain/resources/schema.ts +14 -0
- package/src/domain/resources/service.ts +50 -4
- package/src/domain/tree/service.ts +0 -1
- package/src/export.ts +1 -2
- package/src/index.ts +8 -4
- package/dist/public/assets/index-B_lh6qtv.css +0 -1
- package/dist/public/assets/index-CEol8Bfi.js +0 -164949
package/dist/public/index.html
CHANGED
|
@@ -11,8 +11,37 @@
|
|
|
11
11
|
name="msapplication-TileImage"
|
|
12
12
|
content="/cropped-favicon-270x270.png"
|
|
13
13
|
/>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-oVbUFngg.js"></script>
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-CFjPhJqf.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/src-ps-3oTnY.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-ICPOFSXX-6-ABzkfw.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-C6vvxvVV.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/identity-nF-8qK2-.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-5PVQY5BW-D46cRkay.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-U2HBQHQK-C6yvwOAo.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/defaultLocale-B6dPnyNg.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/init-B9nbfZCT.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/linear-C8MDrvxz.js">
|
|
25
|
+
<link rel="modulepreload" crossorigin href="/assets/time-Bgnk7ODM.js">
|
|
26
|
+
<link rel="modulepreload" crossorigin href="/assets/range-DuD7Go1R.js">
|
|
27
|
+
<link rel="modulepreload" crossorigin href="/assets/treemap-DK8fitek.js">
|
|
28
|
+
<link rel="modulepreload" crossorigin href="/assets/path-C7Bv0Qdk.js">
|
|
29
|
+
<link rel="modulepreload" crossorigin href="/assets/colors-DZGTowqM.js">
|
|
30
|
+
<link rel="modulepreload" crossorigin href="/assets/ordinal-BXaEVJbT.js">
|
|
31
|
+
<link rel="modulepreload" crossorigin href="/assets/arc-DFTvTCxD.js">
|
|
32
|
+
<link rel="modulepreload" crossorigin href="/assets/array-xS8TccZC.js">
|
|
33
|
+
<link rel="modulepreload" crossorigin href="/assets/line-DrKJYWGi.js">
|
|
34
|
+
<link rel="modulepreload" crossorigin href="/assets/isEmpty-Di-NpihJ.js">
|
|
35
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-X2U36JSP-D1C3tOED.js">
|
|
36
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-ZZ45TVLE-DrCjtkdu.js">
|
|
37
|
+
<link rel="modulepreload" crossorigin href="/assets/rough.esm-DE7XMpOC.js">
|
|
38
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-5FUZZQ4R-D7L4hZzZ.js">
|
|
39
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-BSJP7CBP-BQPMRa-Q.js">
|
|
40
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-ENJZ2VHE-BrjxYY_T.js">
|
|
41
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-336JU56O-CVDEj5x8.js">
|
|
42
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-426QAEUC-CIWkCWTf.js">
|
|
43
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-XPW4576I-BH37LuuF.js">
|
|
44
|
+
<link rel="stylesheet" crossorigin href="/assets/index-ypsafPwV.css">
|
|
16
45
|
</head>
|
|
17
46
|
<body>
|
|
18
47
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyberismo/backend",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"description": "Express backend for Cyberismo",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
@@ -14,17 +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.
|
|
18
|
-
"@hono/node-server": "^1.19.
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
18
|
+
"@hono/node-server": "^1.19.13",
|
|
19
19
|
"@hono/zod-validator": "^0.7.6",
|
|
20
20
|
"@types/mime-types": "^3.0.1",
|
|
21
21
|
"dotenv": "^17.3.1",
|
|
22
|
-
"hono": "^4.
|
|
23
|
-
"jose": "^6.
|
|
22
|
+
"hono": "^4.12.14",
|
|
23
|
+
"jose": "^6.2.3",
|
|
24
24
|
"mime-types": "^3.0.2",
|
|
25
25
|
"zod": "^4.3.6",
|
|
26
|
-
"@cyberismo/data-handler": "0.0.
|
|
27
|
-
"@cyberismo/mcp": "0.0.
|
|
26
|
+
"@cyberismo/data-handler": "0.0.24",
|
|
27
|
+
"@cyberismo/mcp": "0.0.24"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@cyberismo/app": "0.0.2"
|
package/src/app.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { attachCommandManager } from './middleware/commandManager.js';
|
|
|
17
17
|
import calculationsRouter from './domain/calculations/index.js';
|
|
18
18
|
import cardsRouter from './domain/cards/index.js';
|
|
19
19
|
import cardTypesRouter from './domain/cardTypes/index.js';
|
|
20
|
+
import connectorsRouter from './domain/connectors/index.js';
|
|
20
21
|
import fieldTypesRouter from './domain/fieldTypes/index.js';
|
|
21
22
|
import graphModelsRouter from './domain/graphModels/index.js';
|
|
22
23
|
import graphViewsRouter from './domain/graphViews/index.js';
|
|
@@ -70,6 +71,7 @@ export function createApp(
|
|
|
70
71
|
app.route('/api/calculations', calculationsRouter);
|
|
71
72
|
app.route('/api/cards', cardsRouter);
|
|
72
73
|
app.route('/api/cardTypes', cardTypesRouter);
|
|
74
|
+
app.route('/api/connectors', connectorsRouter);
|
|
73
75
|
app.route('/api/fieldTypes', fieldTypesRouter);
|
|
74
76
|
app.route('/api/graphModels', graphModelsRouter);
|
|
75
77
|
app.route('/api/graphViews', graphViewsRouter);
|
|
@@ -96,7 +98,7 @@ export function createApp(
|
|
|
96
98
|
// serve index.html for all other routes
|
|
97
99
|
app.notFound(async (c) => {
|
|
98
100
|
if (c.req.path.startsWith('/api')) {
|
|
99
|
-
return c.text('Not Found',
|
|
101
|
+
return c.text('Not Found', 404);
|
|
100
102
|
}
|
|
101
103
|
const file = await readFile(
|
|
102
104
|
path.join(import.meta.dirname, 'public', 'index.html'),
|
|
@@ -13,12 +13,22 @@
|
|
|
13
13
|
|
|
14
14
|
import { type Context, Hono } from 'hono';
|
|
15
15
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
|
16
|
+
import { streamSSE } from 'hono/streaming';
|
|
16
17
|
import { getCardDetails } from './lib.js';
|
|
17
18
|
import * as cardService from './service.js';
|
|
18
19
|
import { isSSGContext, ssgParams } from 'hono/ssg';
|
|
19
20
|
import type { AppContext } from '../../types.js';
|
|
20
21
|
import { UserRole } from '../../types.js';
|
|
22
|
+
import { presenceStore } from './presence.js';
|
|
21
23
|
import { requireRole } from '../../middleware/auth.js';
|
|
24
|
+
import { zValidator } from '../../middleware/zvalidator.js';
|
|
25
|
+
import {
|
|
26
|
+
createLinkSchema,
|
|
27
|
+
removeLinkSchema,
|
|
28
|
+
updateLinkSchema,
|
|
29
|
+
exportCardPdfSchema,
|
|
30
|
+
type ExportCardPdfRequestBody,
|
|
31
|
+
} from './schema.js';
|
|
22
32
|
|
|
23
33
|
const router = new Hono();
|
|
24
34
|
|
|
@@ -50,6 +60,77 @@ router.get('/', requireRole(UserRole.Reader), async (c) => {
|
|
|
50
60
|
}
|
|
51
61
|
});
|
|
52
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @swagger
|
|
65
|
+
* /api/cards/export-pdf:
|
|
66
|
+
* post:
|
|
67
|
+
* summary: Export a card as a PDF
|
|
68
|
+
* description: Exports the specified card as a PDF
|
|
69
|
+
* parameters:
|
|
70
|
+
* - name: key
|
|
71
|
+
* in: path
|
|
72
|
+
* required: true
|
|
73
|
+
* description: Card key (string)
|
|
74
|
+
* requestBody:
|
|
75
|
+
* content:
|
|
76
|
+
* application/json:
|
|
77
|
+
* schema:
|
|
78
|
+
* type: object
|
|
79
|
+
* properties:
|
|
80
|
+
* title:
|
|
81
|
+
* type: string
|
|
82
|
+
* description: Title of the exported PDF
|
|
83
|
+
* name:
|
|
84
|
+
* type: string
|
|
85
|
+
* description: Name of the exported PDF
|
|
86
|
+
* exportChildCards:
|
|
87
|
+
* type: boolean
|
|
88
|
+
* description: Whether to export child cards
|
|
89
|
+
* version:
|
|
90
|
+
* type: string
|
|
91
|
+
* description: Version of the exported PDF (optional)
|
|
92
|
+
* responses:
|
|
93
|
+
* 200:
|
|
94
|
+
* description: Card exported successfully
|
|
95
|
+
* 400:
|
|
96
|
+
* description: Missing or invalid parameters.
|
|
97
|
+
* 500:
|
|
98
|
+
* description: project_path not set
|
|
99
|
+
*/
|
|
100
|
+
router.post(
|
|
101
|
+
'/export-pdf',
|
|
102
|
+
requireRole(UserRole.Reader),
|
|
103
|
+
zValidator('json', exportCardPdfSchema),
|
|
104
|
+
async (c) => {
|
|
105
|
+
const commands = c.get('commands');
|
|
106
|
+
const body = (await c.req.json()) as ExportCardPdfRequestBody;
|
|
107
|
+
try {
|
|
108
|
+
const result = await cardService.exportCard(commands, {
|
|
109
|
+
cardKey: body.cardKey,
|
|
110
|
+
title: body.title,
|
|
111
|
+
name: body.name,
|
|
112
|
+
recursive: body.exportChildCards,
|
|
113
|
+
version: body.version,
|
|
114
|
+
});
|
|
115
|
+
return new Response(result, {
|
|
116
|
+
status: 200,
|
|
117
|
+
headers: {
|
|
118
|
+
'Content-Type': 'application/pdf',
|
|
119
|
+
'Cache-Control': 'no-store',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return c.json(
|
|
124
|
+
{
|
|
125
|
+
error:
|
|
126
|
+
error instanceof Error ? error.message : 'Failed to export card',
|
|
127
|
+
},
|
|
128
|
+
500,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
53
134
|
/**
|
|
54
135
|
* @swagger
|
|
55
136
|
* /api/cards/{key}:
|
|
@@ -514,33 +595,36 @@ router.post('/:key/parse', requireRole(UserRole.Reader), async (c) => {
|
|
|
514
595
|
* 500:
|
|
515
596
|
* description: Server error
|
|
516
597
|
*/
|
|
517
|
-
router.post(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
598
|
+
router.post(
|
|
599
|
+
'/:key/links',
|
|
600
|
+
requireRole(UserRole.Editor),
|
|
601
|
+
zValidator('json', createLinkSchema),
|
|
602
|
+
async (c) => {
|
|
603
|
+
const commands = c.get('commands');
|
|
604
|
+
const key = c.req.param('key');
|
|
605
|
+
const { toCard, linkType, description, direction } = c.req.valid('json');
|
|
525
606
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
607
|
+
try {
|
|
608
|
+
const result = await cardService.createLink(
|
|
609
|
+
commands,
|
|
610
|
+
key,
|
|
611
|
+
toCard,
|
|
612
|
+
linkType,
|
|
613
|
+
direction,
|
|
614
|
+
description,
|
|
615
|
+
);
|
|
616
|
+
return c.json(result);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
return c.json(
|
|
619
|
+
{
|
|
620
|
+
error:
|
|
621
|
+
error instanceof Error ? error.message : 'Failed to create link',
|
|
622
|
+
},
|
|
623
|
+
500,
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
);
|
|
544
628
|
|
|
545
629
|
/**
|
|
546
630
|
* @swagger
|
|
@@ -573,33 +657,122 @@ router.post('/:key/links', requireRole(UserRole.Editor), async (c) => {
|
|
|
573
657
|
* 500:
|
|
574
658
|
* description: Server error
|
|
575
659
|
*/
|
|
576
|
-
router.delete(
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
660
|
+
router.delete(
|
|
661
|
+
'/:key/links',
|
|
662
|
+
requireRole(UserRole.Editor),
|
|
663
|
+
zValidator('json', removeLinkSchema),
|
|
664
|
+
async (c) => {
|
|
665
|
+
const commands = c.get('commands');
|
|
666
|
+
const key = c.req.param('key');
|
|
667
|
+
const { toCard, linkType, description, direction } = c.req.valid('json');
|
|
580
668
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
669
|
+
try {
|
|
670
|
+
const result = await cardService.removeLink(
|
|
671
|
+
commands,
|
|
672
|
+
key,
|
|
673
|
+
toCard,
|
|
674
|
+
linkType,
|
|
675
|
+
direction,
|
|
676
|
+
description,
|
|
677
|
+
);
|
|
678
|
+
return c.json(result);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
return c.json(
|
|
681
|
+
{
|
|
682
|
+
error:
|
|
683
|
+
error instanceof Error ? error.message : 'Failed to remove link',
|
|
684
|
+
},
|
|
685
|
+
500,
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
);
|
|
584
690
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
691
|
+
/**
|
|
692
|
+
* @swagger
|
|
693
|
+
* /api/cards/{key}/links:
|
|
694
|
+
* put:
|
|
695
|
+
* summary: Update a link between cards
|
|
696
|
+
* parameters:
|
|
697
|
+
* - name: key
|
|
698
|
+
* in: path
|
|
699
|
+
* required: true
|
|
700
|
+
* schema:
|
|
701
|
+
* type: string
|
|
702
|
+
* requestBody:
|
|
703
|
+
* content:
|
|
704
|
+
* application/json:
|
|
705
|
+
* schema:
|
|
706
|
+
* type: object
|
|
707
|
+
* properties:
|
|
708
|
+
* toCard:
|
|
709
|
+
* type: string
|
|
710
|
+
* linkType:
|
|
711
|
+
* type: string
|
|
712
|
+
* description:
|
|
713
|
+
* type: string
|
|
714
|
+
* direction:
|
|
715
|
+
* type: string
|
|
716
|
+
* previousToCard:
|
|
717
|
+
* type: string
|
|
718
|
+
* previousLinkType:
|
|
719
|
+
* type: string
|
|
720
|
+
* previousDirection:
|
|
721
|
+
* type: string
|
|
722
|
+
* previousDescription:
|
|
723
|
+
* type: string
|
|
724
|
+
* responses:
|
|
725
|
+
* 200:
|
|
726
|
+
* description: Link updated successfully
|
|
727
|
+
* 400:
|
|
728
|
+
* description: Invalid request
|
|
729
|
+
* 500:
|
|
730
|
+
* description: Server error
|
|
731
|
+
*/
|
|
732
|
+
router.put(
|
|
733
|
+
'/:key/links',
|
|
734
|
+
requireRole(UserRole.Editor),
|
|
735
|
+
zValidator('json', updateLinkSchema),
|
|
736
|
+
async (c) => {
|
|
737
|
+
const commands = c.get('commands');
|
|
738
|
+
const key = c.req.param('key');
|
|
739
|
+
|
|
740
|
+
const {
|
|
589
741
|
toCard,
|
|
590
742
|
linkType,
|
|
591
743
|
description,
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
744
|
+
direction,
|
|
745
|
+
previousToCard,
|
|
746
|
+
previousLinkType,
|
|
747
|
+
previousDirection,
|
|
748
|
+
previousDescription,
|
|
749
|
+
} = c.req.valid('json');
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
const result = await cardService.updateLink(
|
|
753
|
+
commands,
|
|
754
|
+
key,
|
|
755
|
+
toCard,
|
|
756
|
+
linkType,
|
|
757
|
+
direction,
|
|
758
|
+
previousToCard,
|
|
759
|
+
previousLinkType,
|
|
760
|
+
previousDirection,
|
|
761
|
+
description,
|
|
762
|
+
previousDescription,
|
|
763
|
+
);
|
|
764
|
+
return c.json(result);
|
|
765
|
+
} catch (error) {
|
|
766
|
+
return c.json(
|
|
767
|
+
{
|
|
768
|
+
error:
|
|
769
|
+
error instanceof Error ? error.message : 'Failed to update link',
|
|
770
|
+
},
|
|
771
|
+
500,
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
);
|
|
603
776
|
|
|
604
777
|
/**
|
|
605
778
|
* @swagger
|
|
@@ -670,5 +843,55 @@ router.get(
|
|
|
670
843
|
}
|
|
671
844
|
},
|
|
672
845
|
);
|
|
846
|
+
/**
|
|
847
|
+
* @swagger
|
|
848
|
+
* /api/cards/{key}/presence:
|
|
849
|
+
* get:
|
|
850
|
+
* summary: SSE stream of users currently viewing or editing this card
|
|
851
|
+
* parameters:
|
|
852
|
+
* - name: key
|
|
853
|
+
* in: path
|
|
854
|
+
* required: true
|
|
855
|
+
* description: Card key (string)
|
|
856
|
+
* - name: mode
|
|
857
|
+
* in: query
|
|
858
|
+
* required: false
|
|
859
|
+
* schema:
|
|
860
|
+
* type: string
|
|
861
|
+
* enum: [viewing, editing]
|
|
862
|
+
* description: Whether the user is viewing or editing (default: viewing)
|
|
863
|
+
* responses:
|
|
864
|
+
* 200:
|
|
865
|
+
* description: SSE stream with presence events
|
|
866
|
+
*/
|
|
867
|
+
router.get('/:key/presence', requireRole(UserRole.Reader), (c) => {
|
|
868
|
+
const key = c.req.param('key');
|
|
869
|
+
const mode = c.req.query('mode') === 'editing' ? 'editing' : 'viewing';
|
|
870
|
+
const user = c.get('user');
|
|
871
|
+
|
|
872
|
+
if (!key) {
|
|
873
|
+
return c.text('No card key', 400);
|
|
874
|
+
}
|
|
673
875
|
|
|
876
|
+
return streamSSE(c, async (stream) => {
|
|
877
|
+
const connId = presenceStore.add(
|
|
878
|
+
key,
|
|
879
|
+
user,
|
|
880
|
+
mode,
|
|
881
|
+
(data) => void stream.writeSSE(data),
|
|
882
|
+
);
|
|
883
|
+
|
|
884
|
+
let aborted = false;
|
|
885
|
+
stream.onAbort(() => {
|
|
886
|
+
aborted = true;
|
|
887
|
+
presenceStore.remove(key, connId);
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// Keep connection alive with periodic heartbeat
|
|
891
|
+
while (!aborted) {
|
|
892
|
+
await stream.write(': hb\n\n');
|
|
893
|
+
await stream.sleep(30000);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
});
|
|
674
897
|
export default router;
|
package/src/domain/cards/lib.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import Processor from '@asciidoctor/core';
|
|
15
15
|
import type { Card } from '@cyberismo/data-handler/interfaces/project-interfaces';
|
|
16
16
|
import { type CommandManager, evaluateMacros } from '@cyberismo/data-handler';
|
|
17
|
+
import { preprocessMermaidBlocksForHtml } from '@cyberismo/data-handler/utils/mermaid-renderer';
|
|
17
18
|
import { getCardQueryResult } from '../../export.js';
|
|
18
19
|
import type { TreeOptions } from '../../types.js';
|
|
19
20
|
import type { QueryResult } from '@cyberismo/data-handler/types/queries';
|
|
@@ -42,11 +43,6 @@ export async function getCardDetails(
|
|
|
42
43
|
return { status: 400, message: `Card ${key} not found from project` };
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
// always parse for now if not in export mode
|
|
46
|
-
if (!staticMode && !raw) {
|
|
47
|
-
await commands.calculateCmd.generate();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
let asciidocContent = '';
|
|
51
47
|
try {
|
|
52
48
|
asciidocContent = await evaluateMacros(
|
|
@@ -62,6 +58,9 @@ export async function getCardDetails(
|
|
|
62
58
|
asciidocContent = `Macro error: ${error instanceof Error ? error.message : 'Unknown error'}\n\n${asciidocContent}`;
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
// Convert [mermaid] AsciiDoc blocks to passthrough HTML before asciidoctor processes them
|
|
62
|
+
asciidocContent = preprocessMermaidBlocksForHtml(asciidocContent);
|
|
63
|
+
|
|
65
64
|
const htmlContent = Processor()
|
|
66
65
|
.convert(asciidocContent, {
|
|
67
66
|
safe: 'safe',
|
|
@@ -110,6 +109,7 @@ export async function getCardDetails(
|
|
|
110
109
|
cardTypeDisplayName: cardDetailsResponse.metadata.cardType,
|
|
111
110
|
workflowState: '',
|
|
112
111
|
lastUpdated: cardDetailsResponse.metadata.lastUpdated,
|
|
112
|
+
createdAt: cardDetailsResponse.metadata.createdAt,
|
|
113
113
|
fields,
|
|
114
114
|
labels: cardDetailsResponse.metadata?.labels || [],
|
|
115
115
|
links: [],
|
|
@@ -137,6 +137,7 @@ export async function getCardDetails(
|
|
|
137
137
|
: await commands.calculateCmd.runQuery('card', 'localApp', {
|
|
138
138
|
cardKey: key,
|
|
139
139
|
});
|
|
140
|
+
|
|
140
141
|
if (card.length !== 1) {
|
|
141
142
|
throw new Error('Query failed. Check card-query syntax');
|
|
142
143
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
import type { SSEMessage } from 'hono/streaming';
|
|
16
|
+
|
|
17
|
+
export interface PresenceEntry {
|
|
18
|
+
userId: string;
|
|
19
|
+
userName: string;
|
|
20
|
+
mode: 'viewing' | 'editing';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Connection {
|
|
24
|
+
user: UserInfo;
|
|
25
|
+
mode: 'viewing' | 'editing';
|
|
26
|
+
send: (message: SSEMessage) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* In-memory presence tracker for card editing/viewing.
|
|
31
|
+
* Tracks which users are currently looking at or editing each card,
|
|
32
|
+
* and notifies all connected clients via SSE when presence changes.
|
|
33
|
+
*/
|
|
34
|
+
class PresenceStore {
|
|
35
|
+
// cardKey -> connectionId -> Connection
|
|
36
|
+
private connections = new Map<string, Map<string, Connection>>();
|
|
37
|
+
private nextId = 0;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Add a user connection for a card. Returns a connection ID for later removal.
|
|
41
|
+
*/
|
|
42
|
+
add(
|
|
43
|
+
cardKey: string,
|
|
44
|
+
user: UserInfo,
|
|
45
|
+
mode: 'viewing' | 'editing',
|
|
46
|
+
send: (message: SSEMessage) => void,
|
|
47
|
+
): string {
|
|
48
|
+
const connId = String(this.nextId++);
|
|
49
|
+
|
|
50
|
+
if (!this.connections.has(cardKey)) {
|
|
51
|
+
this.connections.set(cardKey, new Map());
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.connections.get(cardKey)!.set(connId, { user, mode, send });
|
|
55
|
+
this.broadcast(cardKey);
|
|
56
|
+
|
|
57
|
+
return connId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Remove a connection and broadcast updated presence.
|
|
62
|
+
*/
|
|
63
|
+
remove(cardKey: string, connId: string): void {
|
|
64
|
+
const cardConns = this.connections.get(cardKey);
|
|
65
|
+
if (!cardConns) return;
|
|
66
|
+
|
|
67
|
+
cardConns.delete(connId);
|
|
68
|
+
|
|
69
|
+
if (cardConns.size === 0) {
|
|
70
|
+
this.connections.delete(cardKey);
|
|
71
|
+
} else {
|
|
72
|
+
this.broadcast(cardKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get current presence list for a card.
|
|
78
|
+
*/
|
|
79
|
+
getPresence(cardKey: string): PresenceEntry[] {
|
|
80
|
+
const cardConns = this.connections.get(cardKey);
|
|
81
|
+
if (!cardConns) return [];
|
|
82
|
+
|
|
83
|
+
// Deduplicate by userId — if a user has multiple connections,
|
|
84
|
+
// prefer the 'editing' mode
|
|
85
|
+
const byUser = new Map<string, PresenceEntry>();
|
|
86
|
+
for (const conn of cardConns.values()) {
|
|
87
|
+
const existing = byUser.get(conn.user.id);
|
|
88
|
+
if (!existing || conn.mode === 'editing') {
|
|
89
|
+
byUser.set(conn.user.id, {
|
|
90
|
+
userId: conn.user.id,
|
|
91
|
+
userName: conn.user.name,
|
|
92
|
+
mode: conn.mode,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Array.from(byUser.values());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Broadcast current presence to all connected clients for a card.
|
|
102
|
+
*/
|
|
103
|
+
private broadcast(cardKey: string): void {
|
|
104
|
+
const cardConns = this.connections.get(cardKey);
|
|
105
|
+
if (!cardConns) return;
|
|
106
|
+
|
|
107
|
+
const presence = this.getPresence(cardKey);
|
|
108
|
+
const data = JSON.stringify({ editors: presence });
|
|
109
|
+
const message: SSEMessage = { event: 'presence', data };
|
|
110
|
+
|
|
111
|
+
for (const conn of cardConns.values()) {
|
|
112
|
+
conn.send(message);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Remove all connections. Intended for test cleanup.
|
|
118
|
+
*/
|
|
119
|
+
removeAll(): void {
|
|
120
|
+
this.connections.clear();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const presenceStore = new PresenceStore();
|
|
@@ -0,0 +1,50 @@
|
|
|
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.
|
|
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
|
+
const linkDirection = z.enum(['outbound', 'inbound']);
|
|
17
|
+
|
|
18
|
+
export const createLinkSchema = z.object({
|
|
19
|
+
toCard: z.string(),
|
|
20
|
+
linkType: z.string(),
|
|
21
|
+
direction: linkDirection.default('outbound'),
|
|
22
|
+
description: z.string().optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const removeLinkSchema = z.object({
|
|
26
|
+
toCard: z.string(),
|
|
27
|
+
linkType: z.string(),
|
|
28
|
+
direction: linkDirection.default('outbound'),
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const updateLinkSchema = z.object({
|
|
33
|
+
toCard: z.string(),
|
|
34
|
+
linkType: z.string(),
|
|
35
|
+
direction: linkDirection,
|
|
36
|
+
description: z.string().optional(),
|
|
37
|
+
previousToCard: z.string(),
|
|
38
|
+
previousLinkType: z.string(),
|
|
39
|
+
previousDirection: linkDirection,
|
|
40
|
+
previousDescription: z.string().optional(),
|
|
41
|
+
});
|
|
42
|
+
export const exportCardPdfSchema = z.object({
|
|
43
|
+
title: z.string().min(1),
|
|
44
|
+
name: z.string().min(1),
|
|
45
|
+
cardKey: z.string(),
|
|
46
|
+
exportChildCards: z.boolean(),
|
|
47
|
+
version: z.string().min(1).optional(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export type ExportCardPdfRequestBody = z.infer<typeof exportCardPdfSchema>;
|