@cyberismo/backend 1.1.2 → 1.2.0
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/README.md +21 -0
- package/dist/app.js +19 -7
- package/dist/app.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keycloak.d.ts +2 -3
- package/dist/auth/keycloak.js +6 -0
- package/dist/auth/keycloak.js.map +1 -1
- package/dist/auth/mock.d.ts +12 -8
- package/dist/auth/mock.js +119 -31
- package/dist/auth/mock.js.map +1 -1
- package/dist/auth/types.d.ts +7 -1
- package/dist/domain/cards/index.js +11 -84
- package/dist/domain/cards/index.js.map +1 -1
- package/dist/domain/cards/lib.js +2 -11
- package/dist/domain/cards/lib.js.map +1 -1
- package/dist/domain/cards/schema.js +9 -3
- package/dist/domain/cards/schema.js.map +1 -1
- package/dist/domain/cards/service.d.ts +2 -5
- package/dist/domain/cards/service.js +8 -15
- package/dist/domain/cards/service.js.map +1 -1
- package/dist/domain/events/index.d.ts +15 -0
- package/dist/domain/events/index.js +94 -0
- package/dist/domain/events/index.js.map +1 -0
- package/dist/domain/events/lifetime.d.ts +14 -0
- package/dist/domain/events/lifetime.js +25 -0
- package/dist/domain/events/lifetime.js.map +1 -0
- package/dist/domain/events/project-events.d.ts +67 -0
- package/dist/domain/events/project-events.js +200 -0
- package/dist/domain/events/project-events.js.map +1 -0
- package/dist/domain/events/schema.d.ts +22 -0
- package/dist/domain/events/schema.js +20 -0
- package/dist/domain/events/schema.js.map +1 -0
- package/dist/domain/project/index.js +17 -5
- package/dist/domain/project/index.js.map +1 -1
- package/dist/domain/project/schema.d.ts +8 -0
- package/dist/domain/project/schema.js +34 -6
- package/dist/domain/project/schema.js.map +1 -1
- package/dist/domain/project/service.d.ts +9 -5
- package/dist/domain/project/service.js +22 -16
- package/dist/domain/project/service.js.map +1 -1
- package/dist/domain/projects/index.js +5 -1
- package/dist/domain/projects/index.js.map +1 -1
- package/dist/domain/resources/index.js +74 -2
- package/dist/domain/resources/index.js.map +1 -1
- package/dist/domain/resources/schema.d.ts +8 -2
- package/dist/domain/resources/schema.js +6 -1
- package/dist/domain/resources/schema.js.map +1 -1
- package/dist/domain/resources/service.d.ts +13 -1
- package/dist/domain/resources/service.js +45 -2
- package/dist/domain/resources/service.js.map +1 -1
- package/dist/domain/skills/index.d.ts +15 -0
- package/dist/domain/skills/index.js +52 -0
- package/dist/domain/skills/index.js.map +1 -0
- package/dist/domain/skills/schema.d.ts +16 -0
- package/dist/domain/skills/schema.js +18 -0
- package/dist/domain/skills/schema.js.map +1 -0
- package/dist/domain/skills/service.d.ts +14 -0
- package/dist/domain/skills/service.js +16 -0
- package/dist/domain/skills/service.js.map +1 -0
- package/dist/export.d.ts +3 -2
- package/dist/export.js +53 -38
- package/dist/export.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +22 -7
- package/dist/main.js.map +1 -1
- package/dist/middleware/auth.d.ts +5 -0
- package/dist/middleware/auth.js +20 -4
- package/dist/middleware/auth.js.map +1 -1
- package/dist/middleware/commandManager.d.ts +2 -0
- package/dist/middleware/commandManager.js +1 -0
- package/dist/middleware/commandManager.js.map +1 -1
- package/dist/overlay.d.ts +45 -0
- package/dist/overlay.js +157 -0
- package/dist/overlay.js.map +1 -0
- package/dist/project-errors.d.ts +18 -0
- package/dist/project-errors.js +25 -0
- package/dist/project-errors.js.map +1 -0
- package/dist/project-registry.d.ts +9 -1
- package/dist/project-registry.js +37 -9
- package/dist/project-registry.js.map +1 -1
- package/dist/public/THIRD-PARTY.txt +528 -435
- package/dist/public/assets/__vite-browser-external-PaJ7c2l8.js +1 -0
- package/dist/public/assets/abnfDiagram-VCTEODGH-DB2dfOGe.js +1 -0
- package/dist/public/assets/architecture-7GRP2DOG-2V0X8hxt.js +1 -0
- package/dist/public/assets/architectureDiagram-5GKGNRK7-CRhUR2Ul.js +36 -0
- package/dist/public/assets/blockDiagram-I7D4REHJ-DJKsYF1v.js +129 -0
- package/dist/public/assets/c4Diagram-7LVT6UL2-BTwMzoAF.js +38 -0
- package/dist/public/assets/channel-C0JtPpMA.js +1 -0
- package/dist/public/assets/chunk-4HAMMTFA-DjjgrBsB.js +62 -0
- package/dist/public/assets/chunk-75Z2AOVW-CKqXRGJV.js +2 -0
- package/dist/public/assets/chunk-DU6HZSFF-iJGdiRt7.js +127 -0
- package/dist/public/assets/chunk-F27PBJKO-tDfs6zeO.js +1 -0
- package/dist/public/assets/chunk-FOHPRMQF-DzxwRta7.js +161 -0
- package/dist/public/assets/chunk-GMAD6QVW-DxHVEaGj.js +72 -0
- package/dist/public/assets/chunk-GVQU2GXP-AMxD4_Ta.js +1 -0
- package/dist/public/assets/{chunk-EX3LRPZG-Db0XYyxy.js → chunk-IMKFNOWR-0rz8QrYC.js} +2 -2
- package/dist/public/assets/chunk-L3NEJ4N5-BK3KAKMe.js +1 -0
- package/dist/public/assets/chunk-OSK3NFVY-B8MdrI_M.js +10 -0
- package/dist/public/assets/chunk-P2QGCYS3-D3J5AcHg.js +1 -0
- package/dist/public/assets/{chunk-VR4S4FIN-tUhbELKr.js → chunk-POPQ4Y6H-CsYg_XkK.js} +1 -1
- package/dist/public/assets/{chunk-HOUHSVGY-L35GPy9G.js → chunk-PWAF6VOD-D9cdEIZp.js} +1 -1
- package/dist/public/assets/chunk-SHT3W25Y-1jjokCf3.js +168 -0
- package/dist/public/assets/{chunk-MOJQB5TN-D1shKbMs.js → chunk-SVP7TREG-CIQMl7hl.js} +1 -1
- package/dist/public/assets/{chunk-V7JOEXUC-71b0ABpv.js → chunk-TICWLB2K-Ccsy2PYz.js} +3 -3
- package/dist/public/assets/chunk-XXDRQBXY-B7bWyHjt.js +1 -0
- package/dist/public/assets/classDiagram-ZZMXUADV-mWyXNAXi.js +1 -0
- package/dist/public/assets/classDiagram-v2-VYDZK3BY-mWyXNAXi.js +1 -0
- package/dist/public/assets/cose-bilkent-JH36ORCC-DPzc3Pv4.js +1 -0
- package/dist/public/assets/cynefin-OW5HDTMX-BIVDCYOU.js +1 -0
- package/dist/public/assets/cynefinDiagram-5FMLGOSQ-_V_nrIhD.js +62 -0
- package/dist/public/assets/cytoscape.esm-NgwPT4ez.js +321 -0
- package/dist/public/assets/dagre-C-rnJpZF.js +1 -0
- package/dist/public/assets/dagre-GXQ25YYZ-CJc0wc6f.js +4 -0
- package/dist/public/assets/diagram-S7CK7UJ4-25IjIKyo.js +30 -0
- package/dist/public/assets/diagram-UQ7AKVKN-C5VvsV7x.js +41 -0
- package/dist/public/assets/{diagram-FQU43EPY-DS-ryuRg.js → diagram-VSXAHHWV-Dw3rKVL_.js} +3 -3
- package/dist/public/assets/diagram-VX7I27RA-De_UQv8C.js +24 -0
- package/dist/public/assets/diagram-Z3DM3KII-Bd-65QQW.js +24 -0
- package/dist/public/assets/dist-BDCdLWk4.js +1 -0
- package/dist/public/assets/ebnfDiagram-PWID7BFC-CrxdET5U.js +1 -0
- package/dist/public/assets/erDiagram-RLTQ6QDP-BKo0Uyna.js +99 -0
- package/dist/public/assets/eventmodeling-NTZA5JFV-DKkAdJZd.js +1 -0
- package/dist/public/assets/flowDiagram-HODETNUW-CEE2wjdt.js +1 -0
- package/dist/public/assets/ganttDiagram-EL5Y4UJY-BHFn8vk0.js +292 -0
- package/dist/public/assets/gitGraph-4MIJSDKK-Bpvnb45u.js +1 -0
- package/dist/public/assets/{gitGraphDiagram-IHSO6WYX-CksvJvsQ.js → gitGraphDiagram-WWUBYQGX-Bw0EoxBY.js} +9 -9
- package/dist/public/assets/index-D3HS7YxF.js +2228 -0
- package/dist/public/assets/index-D6WqGtsG.js +27876 -0
- package/dist/public/assets/{index-COSSLZqt.css → index-Sv3WxZ_8.css} +1 -1
- package/dist/public/assets/info-A6RAGUB7-BvvG_zrk.js +1 -0
- package/dist/public/assets/infoDiagram-27XIBGKW-CtpdJmtL.js +2 -0
- package/dist/public/assets/{ishikawaDiagram-FXEZZL3T-CiLpASIW.js → ishikawaDiagram-5VMMS53U-KvT4DU1u.js} +2 -2
- package/dist/public/assets/journeyDiagram-3NMN7TZE-PhGRkXkq.js +139 -0
- package/dist/public/assets/{kanban-definition-HUTT4EX6-DYP_x6zD.js → kanban-definition-UXKFOSKX-Oyx5QgRq.js} +4 -4
- package/dist/public/assets/{katex-DolUETbr.js → katex-ZlcWpGUi.js} +3 -3
- package/dist/public/assets/{line-C8Sfsgt8.js → line-Dr41HTk1.js} +1 -1
- package/dist/public/assets/linear-DDBi9FwZ.js +1 -0
- package/dist/public/assets/mermaid-parser.core-DsIAxDBa.js +7 -0
- package/dist/public/assets/{mindmap-definition-LN4V7U3C-3IAd4gp3.js → mindmap-definition-YA3MSWOX-r8hvd6gP.js} +1 -1
- package/dist/public/assets/packet-AYTQ26CC-B0EpJ2Sc.js +1 -0
- package/dist/public/assets/path-B_0haWDX.js +1 -0
- package/dist/public/assets/pegDiagram-XKGWAZYB-DaNkQTQF.js +1 -0
- package/dist/public/assets/pie-WAS4IAKB-Dt2zttL9.js +1 -0
- package/dist/public/assets/pieDiagram-E7YTZNPT-x0XWEGbd.js +39 -0
- package/dist/public/assets/{quadrantDiagram-ABIIQ3AL-B3czMbdS.js → quadrantDiagram-AXDQQJYC-B9dlNtZl.js} +3 -3
- package/dist/public/assets/radar-RG4KPBEZ-DjbR4-5l.js +1 -0
- package/dist/public/assets/railroad-74A4TZTK-Bk3B5r_R.js +1 -0
- package/dist/public/assets/railroad-abnf-HS5TGJTU-DxaWfACD.js +1 -0
- package/dist/public/assets/railroad-ebnf-LZEXJU2U-DscxXZO0.js +1 -0
- package/dist/public/assets/railroad-peg-WCYAUIDC-BCWzRFzw.js +1 -0
- package/dist/public/assets/railroadDiagram-O6MQD6OU-DhwsoX6A.js +1 -0
- package/dist/public/assets/{requirementDiagram-TGXJPOKE-CmvJWy_S.js → requirementDiagram-BXWQKSXE-BiqZ6axf.js} +2 -2
- package/dist/public/assets/rolldown-runtime-B0Z9INg1.js +1 -0
- package/dist/public/assets/{rough.esm-By172zw-.js → rough.esm-Dy-Kn_BL.js} +1 -1
- package/dist/public/assets/{sankeyDiagram-HTMAVEWB-tAeOzMTn.js → sankeyDiagram-P5KCCOFB-C4rb2pZ9.js} +1 -1
- package/dist/public/assets/{sequenceDiagram-DBY2YBRQ-r0pwijM4.js → sequenceDiagram-WJ2MYXX4-DRKTnGD7.js} +9 -9
- package/dist/public/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/public/assets/src-an_qrnIj.js +1 -0
- package/dist/public/assets/stateDiagram-D77RDMKH-xxdybg5Z.js +1 -0
- package/dist/public/assets/stateDiagram-v2-MP3YSRHH-CXzmJy_a.js +1 -0
- package/dist/public/assets/swimlanes-42K2YHIH-DyGI1gI6.js +1 -0
- package/dist/public/assets/swimlanesDiagram-VR7AAH4N-7hqb6VoW.js +8 -0
- package/dist/public/assets/timeline-definition-24CTP7MA-C2pIp77Q.js +120 -0
- package/dist/public/assets/treeView-Q6P3EWNA-CxRtaaB8.js +1 -0
- package/dist/public/assets/treemap-WGGIJYW6-G1IHiwTa.js +1 -0
- package/dist/public/assets/{vennDiagram-L72KCM5P-ti2qNcvo.js → vennDiagram-4TSXK5OY-CwimQRR9.js} +2 -2
- package/dist/public/assets/wardley-WFR3VGLG-DkQ3TjZX.js +1 -0
- package/dist/public/assets/{wardleyDiagram-EHGQE667-C8lblm8x.js → wardleyDiagram-VM6X3IG4-BOXpwLCv.js} +15 -15
- package/dist/public/assets/{xychartDiagram-FW5EYKEG-US-_k5eu.js → xychartDiagram-S5SC5T6Z-ChOPYsHc.js} +3 -3
- package/dist/public/index.html +19 -26
- package/dist/public/sbom.cdx.json +1 -0
- package/package.json +12 -13
- package/src/app.ts +19 -7
- package/src/auth/index.ts +1 -1
- package/src/auth/keycloak.ts +8 -3
- package/src/auth/mock.ts +141 -35
- package/src/auth/types.ts +8 -1
- package/src/domain/cards/index.ts +12 -102
- package/src/domain/cards/lib.ts +2 -14
- package/src/domain/cards/schema.ts +11 -3
- package/src/domain/cards/service.ts +9 -24
- package/src/domain/events/index.ts +112 -0
- package/src/domain/events/lifetime.ts +27 -0
- package/src/domain/events/project-events.ts +254 -0
- package/src/domain/events/schema.ts +21 -0
- package/src/domain/project/index.ts +31 -3
- package/src/domain/project/schema.ts +39 -6
- package/src/domain/project/service.ts +42 -17
- package/src/domain/projects/index.ts +5 -1
- package/src/domain/resources/index.ts +85 -2
- package/src/domain/resources/schema.ts +12 -2
- package/src/domain/resources/service.ts +55 -3
- package/src/domain/skills/index.ts +61 -0
- package/src/domain/skills/schema.ts +19 -0
- package/src/domain/skills/service.ts +18 -0
- package/src/export.ts +60 -42
- package/src/index.ts +3 -0
- package/src/main.ts +26 -8
- package/src/middleware/auth.ts +23 -4
- package/src/middleware/commandManager.ts +3 -0
- package/src/overlay.ts +175 -0
- package/src/project-errors.ts +33 -0
- package/src/project-registry.ts +41 -10
- package/dist/domain/cards/presence.d.ts +0 -50
- package/dist/domain/cards/presence.js +0 -93
- package/dist/domain/cards/presence.js.map +0 -1
- package/dist/public/assets/abnfDiagram-VRR7QNED-Djr4KJ5E.js +0 -1
- package/dist/public/assets/arc-DrR4k_H-.js +0 -1
- package/dist/public/assets/architecture-TIHT7OUA-XL6N-4o9.js +0 -1
- package/dist/public/assets/architectureDiagram-ZJ3FMSHR-DsMU2Uwa.js +0 -36
- package/dist/public/assets/blockDiagram-677ZJIJ3-DRBmQUQJ.js +0 -132
- package/dist/public/assets/c4Diagram-LMCZKHZV-E0rM02LD.js +0 -10
- package/dist/public/assets/channel-D5fnImJy.js +0 -1
- package/dist/public/assets/chunk-32BRIVSS-BnDY0aSV.js +0 -1
- package/dist/public/assets/chunk-52WLFC77-N5YugKaZ.js +0 -10
- package/dist/public/assets/chunk-5FCAYU7R-I3_t_Ic_.js +0 -1
- package/dist/public/assets/chunk-5HE753X5-Dxz40rzH.js +0 -1
- package/dist/public/assets/chunk-5JV3BV7I-CNwCKRLH.js +0 -1
- package/dist/public/assets/chunk-5TONJI2A-ezWQ_Z2j.js +0 -2
- package/dist/public/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/dist/public/assets/chunk-BIQX33UG-DS132-R_.js +0 -1
- package/dist/public/assets/chunk-C7G6YPKG-BN-cNjoj.js +0 -1
- package/dist/public/assets/chunk-CQNSW5MT-D8Mn1I73.js +0 -1
- package/dist/public/assets/chunk-CYSBUYHQ-Dc64Dwdo.js +0 -1
- package/dist/public/assets/chunk-EMLP6XTP-aIK0V8GX.js +0 -1
- package/dist/public/assets/chunk-FWX5IMBZ-BF_P2-Do.js +0 -2
- package/dist/public/assets/chunk-ICXQ74PX-Bb8zaHde.js +0 -2
- package/dist/public/assets/chunk-JG7HCLWE-BtAXK3iQ.js +0 -2
- package/dist/public/assets/chunk-KEIR6QF5-DEvma7oi.js +0 -161
- package/dist/public/assets/chunk-MOZMSUNE-5FIqIBsY.js +0 -1
- package/dist/public/assets/chunk-OGEWGWER-BVIFy9xz.js +0 -1
- package/dist/public/assets/chunk-OSBZ3O6U-Cfd7kf1R.js +0 -1
- package/dist/public/assets/chunk-PUDLZKDR-Db3FSNR9.js +0 -156
- package/dist/public/assets/chunk-Q4XR5HBZ-BuKFRUzR.js +0 -70
- package/dist/public/assets/chunk-QBLGF6JB-DUGZXFh-.js +0 -1
- package/dist/public/assets/chunk-R7FJI6CG-D7mUd-qN.js +0 -1
- package/dist/public/assets/chunk-RYQCIY6F-C_3rESBd.js +0 -1
- package/dist/public/assets/chunk-U6XO7XAA-C3UhlwA1.js +0 -2
- package/dist/public/assets/chunk-VAUOI2AC-C-GBw18h.js +0 -1
- package/dist/public/assets/chunk-WYO6CB5R-BQoO62-B.js +0 -127
- package/dist/public/assets/chunk-XXDRQBXY-Co8n4ZHT.js +0 -1
- package/dist/public/assets/chunk-YOTPTUD7-DhCRKU1A.js +0 -1
- package/dist/public/assets/chunk-ZGVPDNZ5-hpz2Ryq5.js +0 -62
- package/dist/public/assets/chunk-ZIRB5QZD-BpeS1aZ7.js +0 -32
- package/dist/public/assets/classDiagram-OUVF2IWQ-CXdVorPK.js +0 -1
- package/dist/public/assets/classDiagram-v2-EOCWNBFH-CXdVorPK.js +0 -1
- package/dist/public/assets/colors-Cg5Vh3br.js +0 -1
- package/dist/public/assets/cose-bilkent-JH36ORCC-4GoRbloS.js +0 -1
- package/dist/public/assets/cynefin-VYW2F7L2-BPH-25fI.js +0 -1
- package/dist/public/assets/cynefinDiagram-TSTJHNR4-PEf16euv.js +0 -62
- package/dist/public/assets/cytoscape.esm-C3UvhD5t.js +0 -321
- package/dist/public/assets/dagre-Buvkdvvj.js +0 -1
- package/dist/public/assets/dagre-VKFMJZFB-DPsCMBF1.js +0 -4
- package/dist/public/assets/diagram-G47NLZAW-CzUoYkFf.js +0 -24
- package/dist/public/assets/diagram-NH7WQ7WH-MhGnE2fh.js +0 -24
- package/dist/public/assets/diagram-OA4YK3LP-CsQ9aFR_.js +0 -30
- package/dist/public/assets/diagram-WEI45ONY-Er3q49ew.js +0 -41
- package/dist/public/assets/dist-BzwexiH1.js +0 -1
- package/dist/public/assets/ebnfDiagram-CCIWWBDH-AnJAsk14.js +0 -1
- package/dist/public/assets/erDiagram-Q63AITRT-DulA16my.js +0 -85
- package/dist/public/assets/eventmodeling-45OFAUF4-CoIhTpHU.js +0 -1
- package/dist/public/assets/flowDiagram-23GEKE2U-BGMXUq18.js +0 -1
- package/dist/public/assets/ganttDiagram-NO4QXBWP-DAvCPT7_.js +0 -292
- package/dist/public/assets/gitGraph-TEB2WS4Q-BcPsZts5.js +0 -1
- package/dist/public/assets/index-Y8Ckwau7.js +0 -447
- package/dist/public/assets/info-DKCQHKI2-7VGzrAVB.js +0 -1
- package/dist/public/assets/infoDiagram-FWYZ7A6U-D_by7lSL.js +0 -2
- package/dist/public/assets/journeyDiagram-5HDEW3XC-DpvFmk7c.js +0 -139
- package/dist/public/assets/linear-BWTvbP1l.js +0 -1
- package/dist/public/assets/map-BaFkSB1l.js +0 -1
- package/dist/public/assets/mermaid-parser.core--8614tkK.js +0 -4
- package/dist/public/assets/ordinal-BdBDtYTt.js +0 -1
- package/dist/public/assets/packet-7NZHBO7P-KYt33H0C.js +0 -1
- package/dist/public/assets/path-HTXttWil.js +0 -1
- package/dist/public/assets/pegDiagram-2B236MQR-BQNFxYz2.js +0 -1
- package/dist/public/assets/pie-RZYD4A2V-xKz9s9I4.js +0 -1
- package/dist/public/assets/pieDiagram-ENE6RG2P-Cy13tg8_.js +0 -39
- package/dist/public/assets/radar-I7S5WNFK-B4QTX-fX.js +0 -1
- package/dist/public/assets/railroad-3IZDKUUU-Co_9rPVC.js +0 -1
- package/dist/public/assets/railroad-abnf-AHOZXSZD-Bhy_c3ol.js +0 -1
- package/dist/public/assets/railroad-ebnf-EBAXGLYW-B9ENjG34.js +0 -1
- package/dist/public/assets/railroad-peg-LSFZ7HO6-CyJxDBsB.js +0 -1
- package/dist/public/assets/railroadDiagram-RFXS5EU6-M2j5SJF2.js +0 -1
- package/dist/public/assets/range-EplFIxc7.js +0 -1
- package/dist/public/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/dist/public/assets/src-DOh7_en_.js +0 -1
- package/dist/public/assets/stateDiagram-2N3HPSRC-a9IdkSQG.js +0 -1
- package/dist/public/assets/stateDiagram-v2-6OUMAXLB-s2blPtZ-.js +0 -1
- package/dist/public/assets/swimlanes-5IMT3BWC-5w0Cdxk6.js +0 -2
- package/dist/public/assets/swimlanesDiagram-G3AALYLV-BWa-KRed.js +0 -8
- package/dist/public/assets/time-BM2d9MsQ.js +0 -1
- package/dist/public/assets/timeline-definition-FHXFAJF6-CvXlv_4Y.js +0 -120
- package/dist/public/assets/treeView-QDETBFTQ-B9cxjfpB.js +0 -1
- package/dist/public/assets/treemap-6X3UGDF4-BplEq4hB.js +0 -1
- package/dist/public/assets/treemap-BfjIY-m6.js +0 -1
- package/dist/public/assets/wardley-OPB4EBWU-BX2ghg5e.js +0 -1
- package/src/domain/cards/presence.ts +0 -124
package/src/auth/mock.ts
CHANGED
|
@@ -11,30 +11,103 @@
|
|
|
11
11
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import type { MiddlewareHandler } from 'hono';
|
|
14
|
+
import type { Context, MiddlewareHandler } from 'hono';
|
|
15
15
|
import { setCookie } from 'hono/cookie';
|
|
16
16
|
import { UserRole } from '../types.js';
|
|
17
17
|
import type { UserInfo } from '../types.js';
|
|
18
|
-
import type { AuthProvider } from './types.js';
|
|
18
|
+
import type { AuthProvider, AuthResult } from './types.js';
|
|
19
19
|
|
|
20
20
|
export interface MockUserConfig {
|
|
21
21
|
name?: string;
|
|
22
22
|
email?: string;
|
|
23
|
+
roster?: boolean;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const MOCK_ROLE_COOKIE = 'mock-role';
|
|
26
|
-
const
|
|
27
|
+
export const MOCK_USER_COOKIE = 'mock-user';
|
|
28
|
+
export const MOCK_EXP_COOKIE = 'mock-exp';
|
|
29
|
+
export const MOCK_AS_PARAM = 'as';
|
|
30
|
+
const RESET_VALUE = 'default';
|
|
31
|
+
const MAX_TTL_SECONDS = 86_400;
|
|
32
|
+
// Mock sessions do not expire. Saying so with a distant deadline rather than
|
|
33
|
+
// no deadline keeps `exp` total for every consumer: it is far beyond the
|
|
34
|
+
// 30-minute stream lifetime, so computeLifetimeMs() min()s it away and
|
|
35
|
+
// rotation still falls to the jitter, exactly as an absent one did.
|
|
36
|
+
const NO_EXPIRY_SECONDS = 100 * 365 * 24 * 60 * 60;
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
function noExpiry(): number {
|
|
39
|
+
return Math.floor(Date.now() / 1000) + NO_EXPIRY_SECONDS;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ROLE_ALIASES = new Map<string, UserRole>([
|
|
43
|
+
['reader', UserRole.Reader],
|
|
44
|
+
['editor', UserRole.Editor],
|
|
45
|
+
['admin', UserRole.Admin],
|
|
46
|
+
['connector', UserRole.Connector],
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
interface MockUser {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
email: string;
|
|
53
|
+
role: UserRole;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Opt-in: `cyberismo app` ships mock auth, where a roster identity would forge
|
|
57
|
+
// git commit authorship. Dev and e2e need several users in one backend process.
|
|
58
|
+
const MOCK_USERS = new Map<string, MockUser>([
|
|
59
|
+
[
|
|
60
|
+
'alice',
|
|
61
|
+
{
|
|
62
|
+
id: 'mock-user-alice',
|
|
63
|
+
name: 'Alice',
|
|
64
|
+
email: 'alice@example.com',
|
|
65
|
+
role: UserRole.Admin,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
'bob',
|
|
70
|
+
{
|
|
71
|
+
id: 'mock-user-bob',
|
|
72
|
+
name: 'Bob',
|
|
73
|
+
email: 'bob@example.com',
|
|
74
|
+
role: UserRole.Editor,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'carol',
|
|
79
|
+
{
|
|
80
|
+
id: 'mock-user-carol',
|
|
81
|
+
name: 'Carol',
|
|
82
|
+
email: 'carol@example.com',
|
|
83
|
+
role: UserRole.Reader,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
]);
|
|
34
87
|
|
|
35
88
|
function parseRole(value: string | null | undefined): UserRole | null {
|
|
36
89
|
if (!value) return null;
|
|
37
|
-
return ROLE_ALIASES
|
|
90
|
+
return ROLE_ALIASES.get(value.toLowerCase()) ?? null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseUser(value: string | null | undefined): MockUser | null {
|
|
94
|
+
if (!value) return null;
|
|
95
|
+
return MOCK_USERS.get(value.toLowerCase()) ?? null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** An unreadable deadline is dropped rather than treated as long past, which
|
|
99
|
+
* would lock the user out with no request left to clear the cookie. */
|
|
100
|
+
function parseExpiry(value: string | null | undefined): number | null {
|
|
101
|
+
const exp = Number(value);
|
|
102
|
+
return value && Number.isInteger(exp) && exp > 0 ? exp : null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Seconds from now to the absolute epoch-second deadline to store. */
|
|
106
|
+
function toDeadline(value: string): string | null {
|
|
107
|
+
const seconds = Number(value);
|
|
108
|
+
if (!Number.isInteger(seconds) || seconds <= 0) return null;
|
|
109
|
+
if (seconds > MAX_TTL_SECONDS) return null;
|
|
110
|
+
return String(Math.floor(Date.now() / 1000) + seconds);
|
|
38
111
|
}
|
|
39
112
|
|
|
40
113
|
function readCookie(header: string | null, name: string): string | undefined {
|
|
@@ -48,6 +121,22 @@ function readCookie(header: string | null, name: string): string | undefined {
|
|
|
48
121
|
return undefined;
|
|
49
122
|
}
|
|
50
123
|
|
|
124
|
+
function applyOverride(
|
|
125
|
+
c: Context,
|
|
126
|
+
override: string | null,
|
|
127
|
+
cookieName: string,
|
|
128
|
+
toCookieValue: (value: string) => string | null,
|
|
129
|
+
): void {
|
|
130
|
+
if (!override) return;
|
|
131
|
+
if (override.toLowerCase() === RESET_VALUE) {
|
|
132
|
+
setCookie(c, cookieName, '', { path: '/', maxAge: 0 });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const value = toCookieValue(override);
|
|
136
|
+
if (value === null) return;
|
|
137
|
+
setCookie(c, cookieName, value, { path: '/', sameSite: 'Lax' });
|
|
138
|
+
}
|
|
139
|
+
|
|
51
140
|
export class MockAuthProvider implements AuthProvider {
|
|
52
141
|
private readonly userConfig: MockUserConfig;
|
|
53
142
|
|
|
@@ -55,36 +144,53 @@ export class MockAuthProvider implements AuthProvider {
|
|
|
55
144
|
this.userConfig = config ?? {};
|
|
56
145
|
}
|
|
57
146
|
|
|
58
|
-
async authenticate(req: Request): Promise<
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
|
|
147
|
+
async authenticate(req: Request): Promise<AuthResult | null> {
|
|
148
|
+
const cookies = req.headers.get('cookie');
|
|
149
|
+
const cookieRole = parseRole(readCookie(cookies, MOCK_ROLE_COOKIE));
|
|
150
|
+
if (this.userConfig.roster) {
|
|
151
|
+
// One-shot: a second identity from inside a tab already signed in as
|
|
152
|
+
// someone else, so neither the cookie nor the role override applies.
|
|
153
|
+
const asUser = parseUser(
|
|
154
|
+
new URL(req.url).searchParams.get(MOCK_AS_PARAM),
|
|
155
|
+
);
|
|
156
|
+
if (asUser) return { ...asUser, exp: noExpiry() };
|
|
157
|
+
}
|
|
158
|
+
const rosterUser = this.userConfig.roster
|
|
159
|
+
? parseUser(readCookie(cookies, MOCK_USER_COOKIE))
|
|
160
|
+
: null;
|
|
161
|
+
const expiry = this.userConfig.roster
|
|
162
|
+
? parseExpiry(readCookie(cookies, MOCK_EXP_COOKIE))
|
|
163
|
+
: null;
|
|
164
|
+
if (expiry !== null && expiry * 1000 <= Date.now()) return null;
|
|
165
|
+
const exp = expiry ?? noExpiry();
|
|
166
|
+
|
|
167
|
+
const identity: UserInfo = rosterUser ?? {
|
|
63
168
|
id: 'mock-user',
|
|
64
169
|
email: this.userConfig.email ?? 'admin@cyberismo.local',
|
|
65
170
|
name: this.userConfig.name ?? 'Local Admin',
|
|
66
|
-
role:
|
|
171
|
+
role: UserRole.Admin,
|
|
67
172
|
};
|
|
173
|
+
return { ...identity, role: cookieRole ?? identity.role, exp };
|
|
68
174
|
}
|
|
69
|
-
}
|
|
70
175
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Turns ?role= and, with the roster on, ?user= and ?ttl= into cookies;
|
|
178
|
+
* "default" clears. ?ttl= seconds gives the session an expiry, which is
|
|
179
|
+
* what drives stream rotation and the 401 that follows it.
|
|
180
|
+
*/
|
|
181
|
+
cookieMiddleware(): MiddlewareHandler {
|
|
182
|
+
return async (c, next) => {
|
|
183
|
+
const params = new URL(c.req.url).searchParams;
|
|
184
|
+
applyOverride(c, params.get('role'), MOCK_ROLE_COOKIE, (value) =>
|
|
185
|
+
parseRole(value) ? value.toLowerCase() : null,
|
|
186
|
+
);
|
|
187
|
+
if (this.userConfig.roster) {
|
|
188
|
+
applyOverride(c, params.get('user'), MOCK_USER_COOKIE, (value) =>
|
|
189
|
+
parseUser(value) ? value.toLowerCase() : null,
|
|
190
|
+
);
|
|
191
|
+
applyOverride(c, params.get('ttl'), MOCK_EXP_COOKIE, toDeadline);
|
|
86
192
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
193
|
+
await next();
|
|
194
|
+
};
|
|
195
|
+
}
|
|
90
196
|
}
|
package/src/auth/types.ts
CHANGED
|
@@ -13,6 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
import type { UserInfo } from '../types.js';
|
|
15
15
|
|
|
16
|
+
export interface AuthResult extends UserInfo {
|
|
17
|
+
/** Credential deadline (epoch seconds). Always present: a provider whose
|
|
18
|
+
* sessions do not expire says so with a deadline far enough out to be
|
|
19
|
+
* inert, rather than leaving every consumer to handle its absence. */
|
|
20
|
+
exp: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
export interface AuthProvider {
|
|
17
|
-
authenticate(req: Request): Promise<
|
|
24
|
+
authenticate(req: Request): Promise<AuthResult | null>;
|
|
18
25
|
}
|
|
@@ -13,13 +13,11 @@
|
|
|
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';
|
|
17
16
|
import { getCardDetails } from './lib.js';
|
|
18
17
|
import * as cardService from './service.js';
|
|
19
18
|
import { isSSGContext, ssgParams } from 'hono/ssg';
|
|
20
19
|
import type { AppContext } from '../../types.js';
|
|
21
20
|
import { UserRole } from '../../types.js';
|
|
22
|
-
import { presenceStore } from './presence.js';
|
|
23
21
|
import { requireRole } from '../../middleware/auth.js';
|
|
24
22
|
import { zValidator } from '../../middleware/zvalidator.js';
|
|
25
23
|
import {
|
|
@@ -32,6 +30,10 @@ import {
|
|
|
32
30
|
|
|
33
31
|
const router = new Hono();
|
|
34
32
|
|
|
33
|
+
function notifyCardsUpdated(c: Context, keys: string[]) {
|
|
34
|
+
c.get('events').cardsUpdated(keys, c.get('user'));
|
|
35
|
+
}
|
|
36
|
+
|
|
35
37
|
/**
|
|
36
38
|
* @swagger
|
|
37
39
|
* /api/cards:
|
|
@@ -240,7 +242,9 @@ router.patch('/:key', requireRole(UserRole.Editor), async (c) => {
|
|
|
240
242
|
const body = await c.req.json();
|
|
241
243
|
|
|
242
244
|
try {
|
|
243
|
-
await cardService.updateCard(commands, key, body)
|
|
245
|
+
if (await cardService.updateCard(commands, key, body)) {
|
|
246
|
+
notifyCardsUpdated(c, [key]);
|
|
247
|
+
}
|
|
244
248
|
const result = await getCardDetails(
|
|
245
249
|
c.get('commands'),
|
|
246
250
|
key,
|
|
@@ -400,6 +404,7 @@ router.post('/:key/attachments', requireRole(UserRole.Editor), async (c) => {
|
|
|
400
404
|
key,
|
|
401
405
|
files as File[],
|
|
402
406
|
);
|
|
407
|
+
notifyCardsUpdated(c, [key]);
|
|
403
408
|
return c.json(result);
|
|
404
409
|
} catch (error) {
|
|
405
410
|
return c.json(
|
|
@@ -451,6 +456,7 @@ router.delete(
|
|
|
451
456
|
key,
|
|
452
457
|
filename,
|
|
453
458
|
);
|
|
459
|
+
notifyCardsUpdated(c, [key]);
|
|
454
460
|
return c.json(result);
|
|
455
461
|
} catch (error) {
|
|
456
462
|
return c.json(
|
|
@@ -466,54 +472,6 @@ router.delete(
|
|
|
466
472
|
},
|
|
467
473
|
);
|
|
468
474
|
|
|
469
|
-
/**
|
|
470
|
-
* @swagger
|
|
471
|
-
* /api/cards/{key}/attachments/{filename}/open:
|
|
472
|
-
* post:
|
|
473
|
-
* summary: Open an attachment using the system's default application
|
|
474
|
-
* parameters:
|
|
475
|
-
* - name: key
|
|
476
|
-
* in: path
|
|
477
|
-
* required: true
|
|
478
|
-
* schema:
|
|
479
|
-
* type: string
|
|
480
|
-
* - name: filename
|
|
481
|
-
* in: path
|
|
482
|
-
* required: true
|
|
483
|
-
* schema:
|
|
484
|
-
* type: string
|
|
485
|
-
* responses:
|
|
486
|
-
* 200:
|
|
487
|
-
* description: Attachment opened successfully
|
|
488
|
-
* 400:
|
|
489
|
-
* description: Invalid request
|
|
490
|
-
* 500:
|
|
491
|
-
* description: Server error
|
|
492
|
-
*/
|
|
493
|
-
router.post(
|
|
494
|
-
'/:key/attachments/:filename/open',
|
|
495
|
-
requireRole(UserRole.Reader),
|
|
496
|
-
async (c) => {
|
|
497
|
-
const commands = c.get('commands');
|
|
498
|
-
const { key, filename } = c.req.param();
|
|
499
|
-
|
|
500
|
-
try {
|
|
501
|
-
const result = await cardService.openAttachment(commands, key, filename);
|
|
502
|
-
return c.json(result);
|
|
503
|
-
} catch (error) {
|
|
504
|
-
return c.json(
|
|
505
|
-
{
|
|
506
|
-
error:
|
|
507
|
-
error instanceof Error
|
|
508
|
-
? error.message
|
|
509
|
-
: 'Failed to open attachment',
|
|
510
|
-
},
|
|
511
|
-
500,
|
|
512
|
-
);
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
);
|
|
516
|
-
|
|
517
475
|
/**
|
|
518
476
|
* @swagger
|
|
519
477
|
* /api/cards/{key}/parse:
|
|
@@ -613,6 +571,7 @@ router.post(
|
|
|
613
571
|
direction,
|
|
614
572
|
description,
|
|
615
573
|
);
|
|
574
|
+
notifyCardsUpdated(c, [key, toCard]);
|
|
616
575
|
return c.json(result);
|
|
617
576
|
} catch (error) {
|
|
618
577
|
return c.json(
|
|
@@ -675,6 +634,7 @@ router.delete(
|
|
|
675
634
|
direction,
|
|
676
635
|
description,
|
|
677
636
|
);
|
|
637
|
+
notifyCardsUpdated(c, [key, toCard]);
|
|
678
638
|
return c.json(result);
|
|
679
639
|
} catch (error) {
|
|
680
640
|
return c.json(
|
|
@@ -761,6 +721,7 @@ router.put(
|
|
|
761
721
|
description,
|
|
762
722
|
previousDescription,
|
|
763
723
|
);
|
|
724
|
+
notifyCardsUpdated(c, [key, toCard, previousToCard]);
|
|
764
725
|
return c.json(result);
|
|
765
726
|
} catch (error) {
|
|
766
727
|
return c.json(
|
|
@@ -843,55 +804,4 @@ router.get(
|
|
|
843
804
|
}
|
|
844
805
|
},
|
|
845
806
|
);
|
|
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
|
-
}
|
|
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
|
-
});
|
|
897
807
|
export default router;
|
package/src/domain/cards/lib.ts
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { convert } from '@asciidoctor/core';
|
|
15
14
|
import type { Card } from '@cyberismo/data-handler/interfaces/project-interfaces';
|
|
16
15
|
import { type CommandManager, evaluateMacros } from '@cyberismo/data-handler';
|
|
17
16
|
import { preprocessMermaidBlocksForHtml } from '@cyberismo/data-handler/utils/mermaid-renderer';
|
|
@@ -70,17 +69,6 @@ export async function getCardDetails(
|
|
|
70
69
|
staticMode ? 'staticSite' : 'inject',
|
|
71
70
|
);
|
|
72
71
|
|
|
73
|
-
const projectPrefix = commands.project.projectPrefix;
|
|
74
|
-
const htmlContent = (
|
|
75
|
-
await convert(asciidocContent, {
|
|
76
|
-
safe: 'safe',
|
|
77
|
-
attributes: {
|
|
78
|
-
imagesdir: `/api/projects/${projectPrefix}/cards/${key}/a`,
|
|
79
|
-
icons: 'font',
|
|
80
|
-
},
|
|
81
|
-
})
|
|
82
|
-
).toString();
|
|
83
|
-
|
|
84
72
|
if (raw) {
|
|
85
73
|
if (!cardDetailsResponse.metadata) {
|
|
86
74
|
throw new Error('Card has no metadata');
|
|
@@ -143,7 +131,7 @@ export async function getCardDetails(
|
|
|
143
131
|
editContent: [],
|
|
144
132
|
},
|
|
145
133
|
rawContent: cardDetailsResponse.content || '',
|
|
146
|
-
|
|
134
|
+
adocContent: asciidocContent,
|
|
147
135
|
attachments: cardDetailsResponse.attachments,
|
|
148
136
|
path: cardDetailsResponse.path,
|
|
149
137
|
},
|
|
@@ -165,7 +153,7 @@ export async function getCardDetails(
|
|
|
165
153
|
data: {
|
|
166
154
|
...card[0],
|
|
167
155
|
rawContent: cardDetailsResponse.content || '',
|
|
168
|
-
|
|
156
|
+
adocContent: asciidocContent,
|
|
169
157
|
attachments: cardDetailsResponse.attachments,
|
|
170
158
|
path: cardDetailsResponse.path,
|
|
171
159
|
},
|
|
@@ -39,12 +39,20 @@ export const updateLinkSchema = z.object({
|
|
|
39
39
|
previousDirection: linkDirection,
|
|
40
40
|
previousDescription: z.string().optional(),
|
|
41
41
|
});
|
|
42
|
+
|
|
43
|
+
// Interpolated into the exported document's AsciiDoc header, where a line break
|
|
44
|
+
// starts a new attribute entry.
|
|
45
|
+
const headerValue = z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1)
|
|
48
|
+
.regex(/^[^\r\n]*$/, 'must not contain line breaks');
|
|
49
|
+
|
|
42
50
|
export const exportCardPdfSchema = z.object({
|
|
43
|
-
title:
|
|
44
|
-
name:
|
|
51
|
+
title: headerValue,
|
|
52
|
+
name: headerValue,
|
|
45
53
|
cardKey: z.string(),
|
|
46
54
|
exportChildCards: z.boolean(),
|
|
47
|
-
version:
|
|
55
|
+
version: headerValue.optional(),
|
|
48
56
|
});
|
|
49
57
|
|
|
50
58
|
export type ExportCardPdfRequestBody = z.infer<typeof exportCardPdfSchema>;
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { convert } from '@asciidoctor/core';
|
|
15
14
|
import type {
|
|
16
15
|
ExportPdfOptions,
|
|
17
16
|
MetadataContent,
|
|
@@ -51,13 +50,16 @@ export async function updateCard(
|
|
|
51
50
|
key: string,
|
|
52
51
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
52
|
body: any,
|
|
54
|
-
) {
|
|
53
|
+
): Promise<boolean> {
|
|
54
|
+
let written = false;
|
|
55
55
|
await commands.atomic(async () => {
|
|
56
56
|
if (body.state) {
|
|
57
57
|
await commands.transitionCmd.cardTransition(key, body.state);
|
|
58
|
+
written = true;
|
|
58
59
|
}
|
|
59
60
|
if (body.content != null) {
|
|
60
61
|
await commands.editCmd.editCardContent(key, body.content);
|
|
62
|
+
written = true;
|
|
61
63
|
}
|
|
62
64
|
if (body.metadata) {
|
|
63
65
|
for (const [metadataKey, metadataValue] of Object.entries(
|
|
@@ -68,15 +70,19 @@ export async function updateCard(
|
|
|
68
70
|
metadataKey,
|
|
69
71
|
metadataValue as MetadataContent,
|
|
70
72
|
);
|
|
73
|
+
written = true;
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
if (body.parent) {
|
|
74
77
|
await commands.moveCmd.moveCard(key, body.parent);
|
|
78
|
+
written = true;
|
|
75
79
|
}
|
|
76
80
|
if (body.index != null) {
|
|
77
81
|
await commands.moveCmd.rankByIndex(key, body.index);
|
|
82
|
+
written = true;
|
|
78
83
|
}
|
|
79
84
|
}, `Update card ${key}`);
|
|
85
|
+
return written;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
export async function deleteCard(commands: CommandManager, key: string) {
|
|
@@ -142,16 +148,6 @@ export async function removeAttachment(
|
|
|
142
148
|
return { message: 'Attachment removed successfully' };
|
|
143
149
|
}
|
|
144
150
|
|
|
145
|
-
export async function openAttachment(
|
|
146
|
-
commands: CommandManager,
|
|
147
|
-
key: string,
|
|
148
|
-
filename: string,
|
|
149
|
-
) {
|
|
150
|
-
validateAttachmentFileName(filename);
|
|
151
|
-
await commands.showCmd.openAttachment(key, filename);
|
|
152
|
-
return { message: 'Attachment opened successfully' };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
151
|
export async function parseContent(
|
|
156
152
|
commands: CommandManager,
|
|
157
153
|
key: string,
|
|
@@ -181,18 +177,7 @@ export async function parseContent(
|
|
|
181
177
|
'inject',
|
|
182
178
|
);
|
|
183
179
|
|
|
184
|
-
|
|
185
|
-
const parsedContent = (
|
|
186
|
-
await convert(asciidocContent, {
|
|
187
|
-
safe: 'safe',
|
|
188
|
-
attributes: {
|
|
189
|
-
imagesdir: `/api/projects/${projectPrefix}/cards/${key}/a`,
|
|
190
|
-
icons: 'font',
|
|
191
|
-
},
|
|
192
|
-
})
|
|
193
|
-
).toString();
|
|
194
|
-
|
|
195
|
-
return { parsedContent };
|
|
180
|
+
return { adocContent: asciidocContent };
|
|
196
181
|
});
|
|
197
182
|
}
|
|
198
183
|
|
|
@@ -0,0 +1,112 @@
|
|
|
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 { Hono } from 'hono';
|
|
15
|
+
import { disableSSG } from 'hono/ssg';
|
|
16
|
+
import { streamSSE, type SSEMessage } from 'hono/streaming';
|
|
17
|
+
import { isAtLeastRole, requireRole } from '../../middleware/auth.js';
|
|
18
|
+
import { zValidator } from '../../middleware/zvalidator.js';
|
|
19
|
+
import { UserRole } from '../../types.js';
|
|
20
|
+
import { computeLifetimeMs } from './lifetime.js';
|
|
21
|
+
import { presenceSchema } from './schema.js';
|
|
22
|
+
|
|
23
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
24
|
+
|
|
25
|
+
const router = new Hono();
|
|
26
|
+
|
|
27
|
+
router.get('/', disableSSG(), requireRole(UserRole.Reader), (c) => {
|
|
28
|
+
const events = c.get('events');
|
|
29
|
+
const user = c.get('user');
|
|
30
|
+
const capabilities = {
|
|
31
|
+
canSeeIdentity: isAtLeastRole(user.role, UserRole.Editor),
|
|
32
|
+
canDeclareEditing: isAtLeastRole(user.role, UserRole.Editor),
|
|
33
|
+
canSeePresence: isAtLeastRole(user.role, UserRole.Editor),
|
|
34
|
+
};
|
|
35
|
+
return streamSSE(c, async (stream) => {
|
|
36
|
+
if (stream.aborted) return;
|
|
37
|
+
let finish: () => void = () => {};
|
|
38
|
+
const closed = new Promise<void>((resolve) => {
|
|
39
|
+
finish = resolve;
|
|
40
|
+
});
|
|
41
|
+
let stopped = false;
|
|
42
|
+
let release = () => {};
|
|
43
|
+
const stop = () => {
|
|
44
|
+
if (stopped) return;
|
|
45
|
+
stopped = true;
|
|
46
|
+
release();
|
|
47
|
+
finish();
|
|
48
|
+
};
|
|
49
|
+
// Ends the stream after one last message. Deliberately does not call
|
|
50
|
+
// `release()`: a rotating connection has been retired, and disconnecting
|
|
51
|
+
// it would drop the presence entry that bridges the client's reconnect.
|
|
52
|
+
const terminate = (message: SSEMessage) => {
|
|
53
|
+
if (stopped) return;
|
|
54
|
+
stopped = true;
|
|
55
|
+
clearInterval(heartbeat);
|
|
56
|
+
clearTimeout(rotate);
|
|
57
|
+
void stream.writeSSE(message).then(finish);
|
|
58
|
+
};
|
|
59
|
+
// Hono swallows stream write errors; `onAbort` is the only teardown signal.
|
|
60
|
+
stream.onAbort(stop);
|
|
61
|
+
const connectionId = events.connect(user, capabilities, {
|
|
62
|
+
send: (message) => void stream.writeSSE(message),
|
|
63
|
+
close: stop,
|
|
64
|
+
terminate,
|
|
65
|
+
});
|
|
66
|
+
release = () => events.disconnect(connectionId);
|
|
67
|
+
const heartbeat = setInterval(
|
|
68
|
+
() => void stream.writeSSE({ event: 'hb', data: '' }),
|
|
69
|
+
HEARTBEAT_INTERVAL_MS,
|
|
70
|
+
);
|
|
71
|
+
const rotate = setTimeout(
|
|
72
|
+
() => {
|
|
73
|
+
events.retire(connectionId);
|
|
74
|
+
terminate({ event: 'rotating', data: '' });
|
|
75
|
+
},
|
|
76
|
+
computeLifetimeMs(c.get('tokenExp')),
|
|
77
|
+
);
|
|
78
|
+
try {
|
|
79
|
+
await closed;
|
|
80
|
+
} finally {
|
|
81
|
+
clearInterval(heartbeat);
|
|
82
|
+
clearTimeout(rotate);
|
|
83
|
+
stop();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
router.put(
|
|
89
|
+
'/presence',
|
|
90
|
+
requireRole(UserRole.Reader),
|
|
91
|
+
zValidator('json', presenceSchema),
|
|
92
|
+
(c) => {
|
|
93
|
+
const { connectionId, sequence, cardKey, mode } = c.req.valid('json');
|
|
94
|
+
if (cardKey && !c.get('commands').project.hasCard(cardKey)) {
|
|
95
|
+
return c.json({ error: 'Card not found' }, 404);
|
|
96
|
+
}
|
|
97
|
+
const events = c.get('events');
|
|
98
|
+
const known = events.update(
|
|
99
|
+
connectionId,
|
|
100
|
+
c.get('user').id,
|
|
101
|
+
sequence,
|
|
102
|
+
cardKey,
|
|
103
|
+
mode,
|
|
104
|
+
);
|
|
105
|
+
if (!known) {
|
|
106
|
+
return c.json({ error: 'Presence connection not found' }, 404);
|
|
107
|
+
}
|
|
108
|
+
return c.body(null, 204);
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
export default router;
|