@saluzi/saluzi-edu 0.2.9 → 0.2.11
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/cli.js +336 -333
- package/dist/rcs-web/assets/Dashboard-BwotBWlJ.js +1 -0
- package/dist/rcs-web/assets/Join-4yrksbdX.js +1 -0
- package/dist/rcs-web/assets/Login-Cxm_yEeE.js +1 -0
- package/dist/rcs-web/assets/SessionDetail-DNdfPOYy.js +50 -0
- package/dist/rcs-web/assets/Setup-BiAAxugt.js +1 -0
- package/dist/rcs-web/assets/ShareLanding-CP3Z6ckr.js +1 -0
- package/dist/rcs-web/assets/TeamCreate-DgdCwRSb.js +1 -0
- package/dist/rcs-web/assets/TeamDetail-CkNTFoMB.js +11 -0
- package/dist/rcs-web/assets/TeamList-Bf0sQ5RQ.js +1 -0
- package/dist/rcs-web/assets/TeamSettings-BaNAfXU3.js +1 -0
- package/dist/rcs-web/assets/UserSettings-CsutQjxG.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/arc-D90597o-.js → dist/rcs-web/assets/arc-BxxmNQO6.js} +1 -1
- package/dist/rcs-web/assets/architectureDiagram-3BPJPVTR-DBjkRdbd.js +36 -0
- package/dist/rcs-web/assets/{blockDiagram-GPEHLZMM-DHd8IbOs.js → blockDiagram-GPEHLZMM-BF6COWlM.js} +2 -2
- package/dist/rcs-web/assets/{c4Diagram-AAUBKEIU-CjFkQJ9h.js → c4Diagram-AAUBKEIU-DHOf390v.js} +2 -2
- package/dist/rcs-web/assets/channel-JsvbMQjW.js +1 -0
- package/dist/rcs-web/assets/{chunk-2J33WTMH-C21XWnGZ.js → chunk-2J33WTMH-CeJN8yjM.js} +1 -1
- package/dist/rcs-web/assets/{chunk-4BX2VUAB-B3SXiJlt.js → chunk-4BX2VUAB-jP76_P2A.js} +1 -1
- package/dist/rcs-web/assets/{chunk-55IACEB6-DjImv45F.js → chunk-55IACEB6-BsFA6emB.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/chunk-727SXJPM-DtnaqsyD.js → dist/rcs-web/assets/chunk-727SXJPM-BF4HrQjZ.js} +1 -1
- package/dist/rcs-web/assets/{chunk-AQP2D5EJ-BVFgH5mS.js → chunk-AQP2D5EJ-DToJ74Kc.js} +1 -1
- package/dist/rcs-web/assets/{chunk-FMBD7UC4-Bl8jgb57.js → chunk-FMBD7UC4-DVTARgsa.js} +1 -1
- package/dist/rcs-web/assets/{chunk-ND2GUHAM-BpK111zh.js → chunk-ND2GUHAM-C50Gqv9c.js} +1 -1
- package/dist/rcs-web/assets/{chunk-QZHKN3VN-ruhIHBim.js → chunk-QZHKN3VN-EsIIh6zV.js} +1 -1
- package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-BXn8Uc4c.js +1 -0
- package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-BXn8Uc4c.js +1 -0
- package/dist/rcs-web/assets/code-block-IT6T5CEO-BnufHNTT.js +2 -0
- package/dist/rcs-web/assets/cose-bilkent-S5V4N54A-M6tNt14I.js +1 -0
- package/dist/rcs-web/assets/dagre-BM42HDAG-DaTm29qG.js +4 -0
- package/dist/rcs-web/assets/diagram-2AECGRRQ-fqoeOvq2.js +43 -0
- package/dist/rcs-web/assets/diagram-5GNKFQAL-6s2CiaGG.js +10 -0
- package/dist/rcs-web/assets/{diagram-KO2AKTUF-Uy3PDP5S.js → diagram-KO2AKTUF-C0aw_N95.js} +2 -2
- package/dist/rcs-web/assets/diagram-LMA3HP47-C8_Rv3rB.js +24 -0
- package/dist/rcs-web/assets/diagram-OG6HWLK6-Dae4u8Uc.js +24 -0
- package/{packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-sre_MTeK.js → dist/rcs-web/assets/erDiagram-TEJ5UH35-CosS5VWr.js} +13 -13
- package/dist/rcs-web/assets/{flowDiagram-I6XJVG4X-DwrWaLoe.js → flowDiagram-I6XJVG4X-D_Mxs46-.js} +2 -2
- package/dist/rcs-web/assets/ganttDiagram-6RSMTGT7-B0jy8Am_.js +292 -0
- package/dist/rcs-web/assets/gitGraphDiagram-PVQCEYII-CSdu2HVI.js +106 -0
- package/dist/rcs-web/assets/i18n-Dbt-vEQ3.js +1 -0
- package/dist/rcs-web/assets/infoDiagram-5YYISTIA-Dcbc9L1S.js +2 -0
- package/{packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-SO0nn3--.js → dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-BLMjFE4t.js} +2 -2
- package/dist/rcs-web/assets/{journeyDiagram-JHISSGLW-D4igFUd4.js → journeyDiagram-JHISSGLW-Dw4nh1xI.js} +2 -2
- package/dist/rcs-web/assets/{kanban-definition-UN3LZRKU-Cm_yKsZC.js → kanban-definition-UN3LZRKU-DkU3j_ly.js} +2 -2
- package/dist/rcs-web/assets/{linear-4IMGll_E.js → linear-N5Cify7M.js} +1 -1
- package/dist/rcs-web/assets/main-30YqUvTd.js +238 -0
- package/dist/rcs-web/assets/main-EP6Lac2m.css +1 -0
- package/{packages/remote-control-server/web/dist/assets/mermaid-VLURNSYL-D_yQ7nAd.js → dist/rcs-web/assets/mermaid-VLURNSYL-gfiJUpl_.js} +5 -5
- package/dist/rcs-web/assets/{mermaid.core-B1qOUdiU.js → mermaid.core-B_Xc65Bf.js} +5 -5
- package/dist/rcs-web/assets/{mindmap-definition-RKZ34NQL-yETom5qt.js → mindmap-definition-RKZ34NQL-DUavHfNu.js} +2 -2
- package/{packages/remote-control-server/web/dist/assets/pieDiagram-4H26LBE5-KIhqXGT6.js → dist/rcs-web/assets/pieDiagram-4H26LBE5-DSJaToTU.js} +2 -2
- package/{packages/remote-control-server/web/dist/assets/quadrantDiagram-W4KKPZXB-DISqmoA6.js → dist/rcs-web/assets/quadrantDiagram-W4KKPZXB-BFp6TnHj.js} +2 -2
- package/dist/rcs-web/assets/{requirementDiagram-4Y6WPE33-DsgT4ptk.js → requirementDiagram-4Y6WPE33-eil6F3-O.js} +2 -2
- package/{packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-Dn67kbn0.js → dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-DbGhoepK.js} +2 -2
- package/{packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-CtWaXZHl.js → dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-B0YrN1ud.js} +14 -14
- package/dist/rcs-web/assets/stateDiagram-AJRCARHV-CofFPj3N.js +1 -0
- package/dist/rcs-web/assets/stateDiagram-v2-BHNVJYJU-DPlcvPn1.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-Dk5WUenC.js → dist/rcs-web/assets/timeline-definition-PNZ67QCA-Bx4dusy8.js} +2 -2
- package/dist/rcs-web/assets/useTeams-MDI4djXg.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/vennDiagram-CIIHVFJN-TY8EiRrC.js → dist/rcs-web/assets/vennDiagram-CIIHVFJN-B_-3l-MF.js} +2 -2
- package/dist/rcs-web/assets/{wardleyDiagram-YWT4CUSO-BK3oi47X.js → wardleyDiagram-YWT4CUSO-DNSn-pGp.js} +2 -2
- package/dist/rcs-web/assets/workerType-OM09y7Lk.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/xychartDiagram-2RQKCTM6-DhsxqX67.js → dist/rcs-web/assets/xychartDiagram-2RQKCTM6-TqUbNp0j.js} +2 -2
- package/dist/rcs-web/index.html +2 -3
- package/package.json +2 -1
- package/packages/remote-control-server/src/__tests__/acp-ws-handler.test.ts +7 -19
- package/packages/remote-control-server/src/__tests__/auth-login.test.ts +561 -0
- package/packages/remote-control-server/src/__tests__/auth-security.test.ts +547 -0
- package/packages/remote-control-server/src/__tests__/auth.test.ts +7 -18
- package/packages/remote-control-server/src/__tests__/bind-replacement.test.ts +251 -0
- package/packages/remote-control-server/src/__tests__/cookie.test.ts +155 -0
- package/packages/remote-control-server/src/__tests__/data-layer-fixes.test.ts +147 -0
- package/packages/remote-control-server/src/__tests__/db.test.ts +582 -0
- package/packages/remote-control-server/src/__tests__/design-gaps.test.ts +440 -0
- package/packages/remote-control-server/src/__tests__/disconnect-monitor.test.ts +2 -18
- package/packages/remote-control-server/src/__tests__/helpers/db.ts +43 -0
- package/packages/remote-control-server/src/__tests__/helpers/fixtures.ts +94 -0
- package/packages/remote-control-server/src/__tests__/helpers/mock-config.ts +31 -0
- package/packages/remote-control-server/src/__tests__/middleware.test.ts +4 -112
- package/packages/remote-control-server/src/__tests__/password.test.ts +76 -0
- package/packages/remote-control-server/src/__tests__/phase4-cleanup.test.ts +290 -0
- package/packages/remote-control-server/src/__tests__/phase4-integration.test.ts +224 -0
- package/packages/remote-control-server/src/__tests__/routes.test.ts +292 -207
- package/packages/remote-control-server/src/__tests__/services.test.ts +2 -18
- package/packages/remote-control-server/src/__tests__/session-visibility.test.ts +452 -0
- package/packages/remote-control-server/src/__tests__/session.test.ts +216 -0
- package/packages/remote-control-server/src/__tests__/shares.test.ts +1419 -0
- package/packages/remote-control-server/src/__tests__/sse-writer.test.ts +2 -18
- package/packages/remote-control-server/src/__tests__/teams.test.ts +2089 -0
- package/packages/remote-control-server/src/__tests__/work-dispatch.test.ts +2 -18
- package/packages/remote-control-server/src/__tests__/worker-token-removal.test.ts +487 -0
- package/packages/remote-control-server/src/__tests__/ws-handler.test.ts +2 -18
- package/packages/remote-control-server/src/auth/constants.ts +13 -0
- package/packages/remote-control-server/src/auth/cookie.ts +75 -0
- package/packages/remote-control-server/src/auth/middleware.ts +75 -165
- package/packages/remote-control-server/src/auth/password.ts +116 -0
- package/packages/remote-control-server/src/auth/session.ts +130 -0
- package/packages/remote-control-server/src/db/schema.sql +116 -0
- package/packages/remote-control-server/src/db/sqlite.ts +99 -0
- package/packages/remote-control-server/src/routes/acp/index.ts +6 -11
- package/packages/remote-control-server/src/routes/v1/environments.ts +111 -8
- package/packages/remote-control-server/src/routes/web/auth-routes.ts +634 -0
- package/packages/remote-control-server/src/routes/web/control.ts +5 -5
- package/packages/remote-control-server/src/routes/web/environments.ts +5 -27
- package/packages/remote-control-server/src/routes/web/sessions.ts +19 -41
- package/packages/remote-control-server/src/routes/web/shares.ts +371 -0
- package/packages/remote-control-server/src/routes/web/teams.ts +850 -0
- package/packages/remote-control-server/src/services/environment.ts +1 -4
- package/packages/remote-control-server/src/services/session-visibility.ts +79 -0
- package/packages/remote-control-server/src/services/session.ts +0 -13
- package/packages/remote-control-server/src/store.ts +150 -56
- package/packages/remote-control-server/src/types/api.ts +2 -8
- package/packages/remote-control-server/web/dist/assets/Dashboard-BwotBWlJ.js +1 -0
- package/packages/remote-control-server/web/dist/assets/Join-4yrksbdX.js +1 -0
- package/packages/remote-control-server/web/dist/assets/Login-Cxm_yEeE.js +1 -0
- package/packages/remote-control-server/web/dist/assets/SessionDetail-DNdfPOYy.js +50 -0
- package/packages/remote-control-server/web/dist/assets/Setup-BiAAxugt.js +1 -0
- package/packages/remote-control-server/web/dist/assets/ShareLanding-CP3Z6ckr.js +1 -0
- package/packages/remote-control-server/web/dist/assets/TeamCreate-DgdCwRSb.js +1 -0
- package/packages/remote-control-server/web/dist/assets/TeamDetail-CkNTFoMB.js +11 -0
- package/packages/remote-control-server/web/dist/assets/TeamList-Bf0sQ5RQ.js +1 -0
- package/packages/remote-control-server/web/dist/assets/TeamSettings-BaNAfXU3.js +1 -0
- package/packages/remote-control-server/web/dist/assets/UserSettings-CsutQjxG.js +1 -0
- package/{dist/rcs-web/assets/arc-D90597o-.js → packages/remote-control-server/web/dist/assets/arc-BxxmNQO6.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/architectureDiagram-3BPJPVTR-DBjkRdbd.js +36 -0
- package/packages/remote-control-server/web/dist/assets/{blockDiagram-GPEHLZMM-DHd8IbOs.js → blockDiagram-GPEHLZMM-BF6COWlM.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{c4Diagram-AAUBKEIU-CjFkQJ9h.js → c4Diagram-AAUBKEIU-DHOf390v.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/channel-JsvbMQjW.js +1 -0
- package/packages/remote-control-server/web/dist/assets/{chunk-2J33WTMH-C21XWnGZ.js → chunk-2J33WTMH-CeJN8yjM.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-4BX2VUAB-B3SXiJlt.js → chunk-4BX2VUAB-jP76_P2A.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-55IACEB6-DjImv45F.js → chunk-55IACEB6-BsFA6emB.js} +1 -1
- package/{dist/rcs-web/assets/chunk-727SXJPM-DtnaqsyD.js → packages/remote-control-server/web/dist/assets/chunk-727SXJPM-BF4HrQjZ.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-AQP2D5EJ-BVFgH5mS.js → chunk-AQP2D5EJ-DToJ74Kc.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-FMBD7UC4-Bl8jgb57.js → chunk-FMBD7UC4-DVTARgsa.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-ND2GUHAM-BpK111zh.js → chunk-ND2GUHAM-C50Gqv9c.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-QZHKN3VN-ruhIHBim.js → chunk-QZHKN3VN-EsIIh6zV.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-BXn8Uc4c.js +1 -0
- package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-BXn8Uc4c.js +1 -0
- package/packages/remote-control-server/web/dist/assets/code-block-IT6T5CEO-BnufHNTT.js +2 -0
- package/packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-M6tNt14I.js +1 -0
- package/packages/remote-control-server/web/dist/assets/dagre-BM42HDAG-DaTm29qG.js +4 -0
- package/packages/remote-control-server/web/dist/assets/diagram-2AECGRRQ-fqoeOvq2.js +43 -0
- package/packages/remote-control-server/web/dist/assets/diagram-5GNKFQAL-6s2CiaGG.js +10 -0
- package/packages/remote-control-server/web/dist/assets/{diagram-KO2AKTUF-Uy3PDP5S.js → diagram-KO2AKTUF-C0aw_N95.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/diagram-LMA3HP47-C8_Rv3rB.js +24 -0
- package/packages/remote-control-server/web/dist/assets/diagram-OG6HWLK6-Dae4u8Uc.js +24 -0
- package/{dist/rcs-web/assets/erDiagram-TEJ5UH35-sre_MTeK.js → packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-CosS5VWr.js} +13 -13
- package/packages/remote-control-server/web/dist/assets/{flowDiagram-I6XJVG4X-DwrWaLoe.js → flowDiagram-I6XJVG4X-D_Mxs46-.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/ganttDiagram-6RSMTGT7-B0jy8Am_.js +292 -0
- package/packages/remote-control-server/web/dist/assets/gitGraphDiagram-PVQCEYII-CSdu2HVI.js +106 -0
- package/packages/remote-control-server/web/dist/assets/i18n-Dbt-vEQ3.js +1 -0
- package/packages/remote-control-server/web/dist/assets/infoDiagram-5YYISTIA-Dcbc9L1S.js +2 -0
- package/{dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-SO0nn3--.js → packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-BLMjFE4t.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{journeyDiagram-JHISSGLW-D4igFUd4.js → journeyDiagram-JHISSGLW-Dw4nh1xI.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{kanban-definition-UN3LZRKU-Cm_yKsZC.js → kanban-definition-UN3LZRKU-DkU3j_ly.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{linear-4IMGll_E.js → linear-N5Cify7M.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/main-30YqUvTd.js +238 -0
- package/packages/remote-control-server/web/dist/assets/main-EP6Lac2m.css +1 -0
- package/{dist/rcs-web/assets/mermaid-VLURNSYL-D_yQ7nAd.js → packages/remote-control-server/web/dist/assets/mermaid-VLURNSYL-gfiJUpl_.js} +5 -5
- package/packages/remote-control-server/web/dist/assets/{mermaid.core-B1qOUdiU.js → mermaid.core-B_Xc65Bf.js} +5 -5
- package/packages/remote-control-server/web/dist/assets/{mindmap-definition-RKZ34NQL-yETom5qt.js → mindmap-definition-RKZ34NQL-DUavHfNu.js} +2 -2
- package/{dist/rcs-web/assets/pieDiagram-4H26LBE5-KIhqXGT6.js → packages/remote-control-server/web/dist/assets/pieDiagram-4H26LBE5-DSJaToTU.js} +2 -2
- package/{dist/rcs-web/assets/quadrantDiagram-W4KKPZXB-DISqmoA6.js → packages/remote-control-server/web/dist/assets/quadrantDiagram-W4KKPZXB-BFp6TnHj.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{requirementDiagram-4Y6WPE33-DsgT4ptk.js → requirementDiagram-4Y6WPE33-eil6F3-O.js} +2 -2
- package/{dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-Dn67kbn0.js → packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-DbGhoepK.js} +2 -2
- package/{dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-CtWaXZHl.js → packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-B0YrN1ud.js} +14 -14
- package/packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-CofFPj3N.js +1 -0
- package/packages/remote-control-server/web/dist/assets/stateDiagram-v2-BHNVJYJU-DPlcvPn1.js +1 -0
- package/{dist/rcs-web/assets/timeline-definition-PNZ67QCA-Dk5WUenC.js → packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-Bx4dusy8.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/useTeams-MDI4djXg.js +1 -0
- package/{dist/rcs-web/assets/vennDiagram-CIIHVFJN-TY8EiRrC.js → packages/remote-control-server/web/dist/assets/vennDiagram-CIIHVFJN-B_-3l-MF.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/{wardleyDiagram-YWT4CUSO-BK3oi47X.js → wardleyDiagram-YWT4CUSO-DNSn-pGp.js} +2 -2
- package/packages/remote-control-server/web/dist/assets/workerType-OM09y7Lk.js +1 -0
- package/{dist/rcs-web/assets/xychartDiagram-2RQKCTM6-DhsxqX67.js → packages/remote-control-server/web/dist/assets/xychartDiagram-2RQKCTM6-TqUbNp0j.js} +2 -2
- package/packages/remote-control-server/web/dist/index.html +2 -3
- package/dist/rcs-web/assets/Dashboard-Cyj-Bb86.js +0 -2
- package/dist/rcs-web/assets/SessionDetail-SA9QBkyj.js +0 -51
- package/dist/rcs-web/assets/architectureDiagram-3BPJPVTR-CG9q9_bk.js +0 -36
- package/dist/rcs-web/assets/channel-CQFGglg-.js +0 -1
- package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-C5rBdShD.js +0 -1
- package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-C5rBdShD.js +0 -1
- package/dist/rcs-web/assets/code-block-IT6T5CEO-BLGFrBw3.js +0 -2
- package/dist/rcs-web/assets/cose-bilkent-S5V4N54A-DBKEXjqM.js +0 -1
- package/dist/rcs-web/assets/dagre-BM42HDAG-DB_SWlAe.js +0 -4
- package/dist/rcs-web/assets/diagram-2AECGRRQ-Gbm8G3Xz.js +0 -43
- package/dist/rcs-web/assets/diagram-5GNKFQAL-BBuWqfDc.js +0 -10
- package/dist/rcs-web/assets/diagram-LMA3HP47-DyUwNrlg.js +0 -24
- package/dist/rcs-web/assets/diagram-OG6HWLK6-BXpvRwkJ.js +0 -24
- package/dist/rcs-web/assets/ganttDiagram-6RSMTGT7-ByDYviDl.js +0 -292
- package/dist/rcs-web/assets/gitGraphDiagram-PVQCEYII-BUnOPw74.js +0 -106
- package/dist/rcs-web/assets/infoDiagram-5YYISTIA-B1RpwJHG.js +0 -2
- package/dist/rcs-web/assets/main-0A4a3FDU.css +0 -1
- package/dist/rcs-web/assets/main-Dom16YJk.js +0 -283
- package/dist/rcs-web/assets/qr-C0jP9HcD.js +0 -8
- package/dist/rcs-web/assets/qr-scanner-worker.min-D85Z9gVD.js +0 -98
- package/dist/rcs-web/assets/stateDiagram-AJRCARHV-D6xp64f3.js +0 -1
- package/dist/rcs-web/assets/stateDiagram-v2-BHNVJYJU-D76vZCyQ.js +0 -1
- package/packages/remote-control-server/web/dist/assets/Dashboard-Cyj-Bb86.js +0 -2
- package/packages/remote-control-server/web/dist/assets/SessionDetail-SA9QBkyj.js +0 -51
- package/packages/remote-control-server/web/dist/assets/architectureDiagram-3BPJPVTR-CG9q9_bk.js +0 -36
- package/packages/remote-control-server/web/dist/assets/channel-CQFGglg-.js +0 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-C5rBdShD.js +0 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-C5rBdShD.js +0 -1
- package/packages/remote-control-server/web/dist/assets/code-block-IT6T5CEO-BLGFrBw3.js +0 -2
- package/packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-DBKEXjqM.js +0 -1
- package/packages/remote-control-server/web/dist/assets/dagre-BM42HDAG-DB_SWlAe.js +0 -4
- package/packages/remote-control-server/web/dist/assets/diagram-2AECGRRQ-Gbm8G3Xz.js +0 -43
- package/packages/remote-control-server/web/dist/assets/diagram-5GNKFQAL-BBuWqfDc.js +0 -10
- package/packages/remote-control-server/web/dist/assets/diagram-LMA3HP47-DyUwNrlg.js +0 -24
- package/packages/remote-control-server/web/dist/assets/diagram-OG6HWLK6-BXpvRwkJ.js +0 -24
- package/packages/remote-control-server/web/dist/assets/ganttDiagram-6RSMTGT7-ByDYviDl.js +0 -292
- package/packages/remote-control-server/web/dist/assets/gitGraphDiagram-PVQCEYII-BUnOPw74.js +0 -106
- package/packages/remote-control-server/web/dist/assets/infoDiagram-5YYISTIA-B1RpwJHG.js +0 -2
- package/packages/remote-control-server/web/dist/assets/main-0A4a3FDU.css +0 -1
- package/packages/remote-control-server/web/dist/assets/main-Dom16YJk.js +0 -283
- package/packages/remote-control-server/web/dist/assets/qr-C0jP9HcD.js +0 -8
- package/packages/remote-control-server/web/dist/assets/qr-scanner-worker.min-D85Z9gVD.js +0 -98
- package/packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-D6xp64f3.js +0 -1
- package/packages/remote-control-server/web/dist/assets/stateDiagram-v2-BHNVJYJU-D76vZCyQ.js +0 -1
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { mockConfigModule } from './helpers/mock-config'
|
|
2
3
|
|
|
3
4
|
// Mock config before imports
|
|
4
|
-
|
|
5
|
-
port: 3000,
|
|
6
|
-
host: '0.0.0.0',
|
|
7
|
-
apiKeys: ['test-api-key'],
|
|
8
|
-
baseUrl: 'http://localhost:3000',
|
|
9
|
-
pollTimeout: 1, // Short timeout for tests
|
|
10
|
-
heartbeatInterval: 20,
|
|
11
|
-
jwtExpiresIn: 3600,
|
|
12
|
-
disconnectTimeout: 300,
|
|
13
|
-
webCorsOrigins: [],
|
|
14
|
-
wsIdleTimeout: 30,
|
|
15
|
-
wsKeepaliveInterval: 20,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
mock.module('../config', () => ({
|
|
19
|
-
config: mockConfig,
|
|
20
|
-
getBaseUrl: () => 'http://localhost:3000',
|
|
21
|
-
}))
|
|
5
|
+
mock.module('../config', () => mockConfigModule({ pollTimeout: 1 })) // Short timeout for tests
|
|
22
6
|
|
|
23
7
|
import {
|
|
24
8
|
storeReset,
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import { randomUUID } from 'node:crypto'
|
|
5
|
+
import { tmpdir } from 'node:os'
|
|
6
|
+
import { mockConfigModule } from './helpers/mock-config'
|
|
7
|
+
|
|
8
|
+
// Mock config before any module imports
|
|
9
|
+
mock.module('../config', () => mockConfigModule())
|
|
10
|
+
|
|
11
|
+
import { Hono } from 'hono'
|
|
12
|
+
import { Database } from 'bun:sqlite'
|
|
13
|
+
|
|
14
|
+
// Dynamic imports — modules may or may not exist
|
|
15
|
+
let migrateDatabase: ((db: Database) => void) | undefined
|
|
16
|
+
let initDatabase: ((path?: string) => Database) | undefined
|
|
17
|
+
let resetDbSingleton: (() => void) | undefined
|
|
18
|
+
let getDb: (() => Database) | undefined
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const dbMod = await import('../db/sqlite')
|
|
22
|
+
migrateDatabase = dbMod.migrateDatabase
|
|
23
|
+
getDb = dbMod.getDb
|
|
24
|
+
initDatabase = dbMod.initDatabase
|
|
25
|
+
resetDbSingleton = dbMod.resetDbSingleton
|
|
26
|
+
} catch {
|
|
27
|
+
// Module not implemented yet
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let authRoutes: Hono | undefined
|
|
31
|
+
try {
|
|
32
|
+
const mod = await import('../routes/web/auth-routes')
|
|
33
|
+
authRoutes = mod.default
|
|
34
|
+
} catch {
|
|
35
|
+
// Module not implemented yet
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let sessionRoutes: Hono | undefined
|
|
39
|
+
try {
|
|
40
|
+
const mod = await import('../routes/web/sessions')
|
|
41
|
+
sessionRoutes = mod.default
|
|
42
|
+
} catch {
|
|
43
|
+
// Module not implemented yet
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let controlRoutes: Hono | undefined
|
|
47
|
+
try {
|
|
48
|
+
const mod = await import('../routes/web/control')
|
|
49
|
+
controlRoutes = mod.default
|
|
50
|
+
} catch {
|
|
51
|
+
// Module not implemented yet
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Helpers
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
const PKG_ROOT = resolve(import.meta.dir, '../..')
|
|
59
|
+
const RCS_SRC = resolve(PKG_ROOT, 'src')
|
|
60
|
+
const BRIDGE_SRC = resolve(PKG_ROOT, '..', '..', '..', 'src', 'bridge')
|
|
61
|
+
const WEB_SRC = resolve(PKG_ROOT, 'web', 'src')
|
|
62
|
+
|
|
63
|
+
function readFileSafe(p: string): string | null {
|
|
64
|
+
try {
|
|
65
|
+
return readFileSync(p, 'utf-8')
|
|
66
|
+
} catch {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function freshDb(): Database {
|
|
72
|
+
if (resetDbSingleton) {
|
|
73
|
+
try {
|
|
74
|
+
resetDbSingleton()
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (initDatabase) {
|
|
80
|
+
const uniquePath = resolve(
|
|
81
|
+
tmpdir(),
|
|
82
|
+
`rcs-test-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
|
|
83
|
+
)
|
|
84
|
+
return initDatabase(uniquePath)
|
|
85
|
+
}
|
|
86
|
+
const db = new Database(':memory:')
|
|
87
|
+
db.exec('PRAGMA foreign_keys = ON')
|
|
88
|
+
if (migrateDatabase) migrateDatabase(db)
|
|
89
|
+
return db
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface TestUser {
|
|
93
|
+
id: string
|
|
94
|
+
username: string
|
|
95
|
+
role: string
|
|
96
|
+
accessToken: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createUserDirectly(
|
|
100
|
+
db: Database,
|
|
101
|
+
opts: { username: string; role: string },
|
|
102
|
+
): TestUser {
|
|
103
|
+
const id = `usr_${randomUUID().replace(/-/g, '')}`
|
|
104
|
+
const now = new Date().toISOString()
|
|
105
|
+
const passwordHash =
|
|
106
|
+
'$argon2id$v=19$m=65536,t=3,p=1$dGVzdHNhbHR0ZXN0$' +
|
|
107
|
+
'dGVzdGhhc2h0ZXN0aGFzaHRlc3RoYXNodGVzdGhhc2g'
|
|
108
|
+
try {
|
|
109
|
+
db.query(
|
|
110
|
+
`INSERT INTO users (id, username, password_hash, role, created_at)
|
|
111
|
+
VALUES ($id, $username, $hash, $role, $now)`,
|
|
112
|
+
).run({
|
|
113
|
+
$id: id,
|
|
114
|
+
$username: opts.username,
|
|
115
|
+
$hash: passwordHash,
|
|
116
|
+
$role: opts.role,
|
|
117
|
+
$now: now,
|
|
118
|
+
})
|
|
119
|
+
} catch {
|
|
120
|
+
// table may not exist
|
|
121
|
+
}
|
|
122
|
+
return { id, username: opts.username, role: opts.role, accessToken: '' }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function issueTokenForUser(db: Database, userId: string): string {
|
|
126
|
+
try {
|
|
127
|
+
const { issueSessionToken } = require('../auth/session') as {
|
|
128
|
+
issueSessionToken: (
|
|
129
|
+
userId: string,
|
|
130
|
+
db: Database,
|
|
131
|
+
) => { accessToken: string; refreshToken: string }
|
|
132
|
+
}
|
|
133
|
+
const { accessToken } = issueSessionToken(userId, db)
|
|
134
|
+
return accessToken
|
|
135
|
+
} catch {
|
|
136
|
+
const { createHash } =
|
|
137
|
+
require('node:crypto') as typeof import('node:crypto')
|
|
138
|
+
const token = `rct_${randomUUID().replace(/-/g, '')}`
|
|
139
|
+
const tokenHash = createHash('sha256').update(token).digest('hex')
|
|
140
|
+
const now = new Date().toISOString()
|
|
141
|
+
const expiresAt = new Date(Date.now() + 3600_000).toISOString()
|
|
142
|
+
try {
|
|
143
|
+
db.query(
|
|
144
|
+
`INSERT INTO session_tokens (token_hash, user_id, kind, expires_at, created_at)
|
|
145
|
+
VALUES ($hash, $userId, 'access', $exp, $now)`,
|
|
146
|
+
).run({ $hash: tokenHash, $userId: userId, $exp: expiresAt, $now: now })
|
|
147
|
+
} catch {
|
|
148
|
+
// table may not exist
|
|
149
|
+
}
|
|
150
|
+
return token
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function createAndLoginUser(
|
|
155
|
+
db: Database,
|
|
156
|
+
opts: { username: string; role: string },
|
|
157
|
+
): Promise<TestUser> {
|
|
158
|
+
const user = createUserDirectly(db, opts)
|
|
159
|
+
user.accessToken = issueTokenForUser(db, user.id)
|
|
160
|
+
return user
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function authHeader(token: string): Record<string, string> {
|
|
164
|
+
return { Authorization: `Bearer ${token}` }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function createApp(): Hono {
|
|
168
|
+
const app = new Hono()
|
|
169
|
+
if (authRoutes) app.route('/web/auth', authRoutes)
|
|
170
|
+
if (sessionRoutes) app.route('/web', sessionRoutes)
|
|
171
|
+
if (controlRoutes) app.route('/web', controlRoutes)
|
|
172
|
+
return app
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ===========================================================================
|
|
176
|
+
// TESTS — RCS_WORKER_TOKENS env removed
|
|
177
|
+
// ===========================================================================
|
|
178
|
+
|
|
179
|
+
describe('RCS_WORKER_TOKENS env removed', () => {
|
|
180
|
+
let db: Database
|
|
181
|
+
|
|
182
|
+
beforeEach(() => {
|
|
183
|
+
db = freshDb()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('EnvironmentRecord type does not contain workerTokenHash field', async () => {
|
|
187
|
+
// Read store.ts and check that EnvironmentRecord no longer has workerTokenHash
|
|
188
|
+
const storeContent = readFileSafe(resolve(RCS_SRC, 'store.ts'))
|
|
189
|
+
if (!storeContent) return
|
|
190
|
+
|
|
191
|
+
// Check the EnvironmentRecord interface/type definition
|
|
192
|
+
// After Phase 3, workerTokenHash should not appear in the type
|
|
193
|
+
const envRecordMatch = storeContent.match(
|
|
194
|
+
/(?:interface|type)\s+EnvironmentRecord[^}]*}/s,
|
|
195
|
+
)
|
|
196
|
+
if (envRecordMatch) {
|
|
197
|
+
expect(envRecordMatch[0]).not.toContain('workerTokenHash')
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('storeCreateEnvironment does not accept workerTokenHash parameter', async () => {
|
|
202
|
+
const storeContent = readFileSafe(resolve(RCS_SRC, 'store.ts'))
|
|
203
|
+
if (!storeContent) return
|
|
204
|
+
|
|
205
|
+
// After Phase 3, the StoreEnvironmentRequest type should not include workerTokenHash
|
|
206
|
+
const reqTypeMatch = storeContent.match(
|
|
207
|
+
/(?:interface|type)\s+Store\w*Request[^}]*}/s,
|
|
208
|
+
)
|
|
209
|
+
if (reqTypeMatch) {
|
|
210
|
+
expect(reqTypeMatch[0]).not.toContain('workerTokenHash')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Also check the function signature doesn't reference workerTokenHash
|
|
214
|
+
const createFnMatch = storeContent.match(
|
|
215
|
+
/function\s+storeCreateEnvironment[^{]*\{[^}]*\}/s,
|
|
216
|
+
)
|
|
217
|
+
if (createFnMatch) {
|
|
218
|
+
expect(createFnMatch[0]).not.toContain('workerTokenHash')
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// ===========================================================================
|
|
224
|
+
// TESTS — requireWorkerToken middleware removed
|
|
225
|
+
// ===========================================================================
|
|
226
|
+
|
|
227
|
+
describe('requireWorkerToken middleware removed', () => {
|
|
228
|
+
test('requireWorkerToken does not exist in auth/middleware.ts', () => {
|
|
229
|
+
const middlewareContent = readFileSafe(
|
|
230
|
+
resolve(RCS_SRC, 'auth', 'middleware.ts'),
|
|
231
|
+
)
|
|
232
|
+
if (!middlewareContent) return
|
|
233
|
+
// After Phase 3, requireWorkerToken should not be exported
|
|
234
|
+
const hasExport =
|
|
235
|
+
/export\s+(?:async\s+)?function\s+requireWorkerToken/.test(
|
|
236
|
+
middlewareContent,
|
|
237
|
+
)
|
|
238
|
+
expect(hasExport).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
test('verifyWorkerTokenForEnv does not exist in auth/middleware.ts', () => {
|
|
242
|
+
const middlewareContent = readFileSafe(
|
|
243
|
+
resolve(RCS_SRC, 'auth', 'middleware.ts'),
|
|
244
|
+
)
|
|
245
|
+
if (!middlewareContent) return
|
|
246
|
+
const hasExport =
|
|
247
|
+
/export\s+(?:async\s+)?function\s+verifyWorkerTokenForEnv/.test(
|
|
248
|
+
middlewareContent,
|
|
249
|
+
)
|
|
250
|
+
expect(hasExport).toBe(false)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
test('POST /web/sessions/:id/events does not require X-Worker-Token', async () => {
|
|
254
|
+
const app = createApp()
|
|
255
|
+
const db = freshDb()
|
|
256
|
+
const user = await createAndLoginUser(db, {
|
|
257
|
+
username: 'worker-test',
|
|
258
|
+
role: 'member',
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
|
|
262
|
+
try {
|
|
263
|
+
const now = new Date().toISOString()
|
|
264
|
+
db.query(
|
|
265
|
+
`INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
|
|
266
|
+
).run({ $id: sessionId, $now: now })
|
|
267
|
+
db.query(
|
|
268
|
+
`INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
|
|
269
|
+
).run({ $sid: sessionId, $uid: user.id })
|
|
270
|
+
} catch {
|
|
271
|
+
// tables may not exist
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Access without X-Worker-Token — should not get a worker_token_required error
|
|
275
|
+
const res = await app.request(`/web/sessions/${sessionId}/events`, {
|
|
276
|
+
method: 'POST',
|
|
277
|
+
headers: {
|
|
278
|
+
'Content-Type': 'application/json',
|
|
279
|
+
...authHeader(user.accessToken),
|
|
280
|
+
},
|
|
281
|
+
body: JSON.stringify({ type: 'message', content: 'test' }),
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
// Should NOT return worker_token_required error
|
|
285
|
+
if (res.status === 401) {
|
|
286
|
+
const body = (await res.json()) as Record<string, unknown>
|
|
287
|
+
const errorType =
|
|
288
|
+
(body.error as Record<string, unknown>)?.type || body.type
|
|
289
|
+
expect(errorType).not.toBe('worker_token_required')
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
test('POST /web/sessions/:id/control does not require X-Worker-Token', async () => {
|
|
294
|
+
const app = createApp()
|
|
295
|
+
const db = freshDb()
|
|
296
|
+
const user = await createAndLoginUser(db, {
|
|
297
|
+
username: 'control-test',
|
|
298
|
+
role: 'member',
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
|
|
302
|
+
try {
|
|
303
|
+
const now = new Date().toISOString()
|
|
304
|
+
db.query(
|
|
305
|
+
`INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
|
|
306
|
+
).run({ $id: sessionId, $now: now })
|
|
307
|
+
db.query(
|
|
308
|
+
`INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
|
|
309
|
+
).run({ $sid: sessionId, $uid: user.id })
|
|
310
|
+
} catch {
|
|
311
|
+
// tables may not exist
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const res = await app.request(`/web/sessions/${sessionId}/control`, {
|
|
315
|
+
method: 'POST',
|
|
316
|
+
headers: {
|
|
317
|
+
'Content-Type': 'application/json',
|
|
318
|
+
...authHeader(user.accessToken),
|
|
319
|
+
},
|
|
320
|
+
body: JSON.stringify({ action: 'interrupt' }),
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
if (res.status === 401) {
|
|
324
|
+
const body = (await res.json()) as Record<string, unknown>
|
|
325
|
+
const errorType =
|
|
326
|
+
(body.error as Record<string, unknown>)?.type || body.type
|
|
327
|
+
expect(errorType).not.toBe('worker_token_required')
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
test('POST /web/sessions/:id/interrupt does not require X-Worker-Token', async () => {
|
|
332
|
+
const app = createApp()
|
|
333
|
+
const db = freshDb()
|
|
334
|
+
const user = await createAndLoginUser(db, {
|
|
335
|
+
username: 'interrupt-test',
|
|
336
|
+
role: 'member',
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
|
|
340
|
+
try {
|
|
341
|
+
const now = new Date().toISOString()
|
|
342
|
+
db.query(
|
|
343
|
+
`INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
|
|
344
|
+
).run({ $id: sessionId, $now: now })
|
|
345
|
+
db.query(
|
|
346
|
+
`INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
|
|
347
|
+
).run({ $sid: sessionId, $uid: user.id })
|
|
348
|
+
} catch {
|
|
349
|
+
// tables may not exist
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const res = await app.request(`/web/sessions/${sessionId}/interrupt`, {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
headers: {
|
|
355
|
+
'Content-Type': 'application/json',
|
|
356
|
+
...authHeader(user.accessToken),
|
|
357
|
+
},
|
|
358
|
+
body: JSON.stringify({}),
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
if (res.status === 401) {
|
|
362
|
+
const body = (await res.json()) as Record<string, unknown>
|
|
363
|
+
const errorType =
|
|
364
|
+
(body.error as Record<string, unknown>)?.type || body.type
|
|
365
|
+
expect(errorType).not.toBe('worker_token_required')
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
test('existing routes do not call worker token verification', () => {
|
|
370
|
+
// Check sessions.ts does not import or use requireWorkerToken/verifyWorkerTokenForEnv
|
|
371
|
+
const sessionsContent = readFileSafe(
|
|
372
|
+
resolve(RCS_SRC, 'routes', 'web', 'sessions.ts'),
|
|
373
|
+
)
|
|
374
|
+
if (sessionsContent) {
|
|
375
|
+
expect(sessionsContent).not.toContain('requireWorkerToken')
|
|
376
|
+
expect(sessionsContent).not.toContain('verifyWorkerTokenForEnv')
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Check control.ts does not import or use them either
|
|
380
|
+
const controlContent = readFileSafe(
|
|
381
|
+
resolve(RCS_SRC, 'routes', 'web', 'control.ts'),
|
|
382
|
+
)
|
|
383
|
+
if (controlContent) {
|
|
384
|
+
expect(controlContent).not.toContain('requireWorkerToken')
|
|
385
|
+
expect(controlContent).not.toContain('verifyWorkerTokenForEnv')
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
// ===========================================================================
|
|
391
|
+
// TESTS — worker token related code removed
|
|
392
|
+
// ===========================================================================
|
|
393
|
+
|
|
394
|
+
describe('worker token related code removed', () => {
|
|
395
|
+
test('src/bridge/bridgeConfig.ts does not read RCS_WORKER_TOKENS', () => {
|
|
396
|
+
const bridgeConfigContent = readFileSafe(
|
|
397
|
+
resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
|
|
398
|
+
)
|
|
399
|
+
if (!bridgeConfigContent) return
|
|
400
|
+
expect(bridgeConfigContent).not.toContain('RCS_WORKER_TOKENS')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
test('src/bridge/bridgeConfig.ts does not fall back to SALUZI_BRIDGE_OAUTH_TOKEN as worker token', () => {
|
|
404
|
+
const bridgeConfigContent = readFileSafe(
|
|
405
|
+
resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
|
|
406
|
+
)
|
|
407
|
+
if (!bridgeConfigContent) return
|
|
408
|
+
// getWorkerToken() should be removed entirely, or at least not reference
|
|
409
|
+
// SALUZI_BRIDGE_OAUTH_TOKEN as a worker token fallback
|
|
410
|
+
const hasWorkerTokenFn = /function\s+getWorkerToken\b/.test(
|
|
411
|
+
bridgeConfigContent,
|
|
412
|
+
)
|
|
413
|
+
if (hasWorkerTokenFn) {
|
|
414
|
+
// If the function still exists, it should not reference SALUZI_BRIDGE_OAUTH_TOKEN
|
|
415
|
+
// as a worker token fallback
|
|
416
|
+
const fnBody = bridgeConfigContent.match(
|
|
417
|
+
/function\s+getWorkerToken\b[^}]*\}/s,
|
|
418
|
+
)
|
|
419
|
+
if (fnBody) {
|
|
420
|
+
expect(fnBody[0]).not.toContain('SALUZI_BRIDGE_OAUTH_TOKEN')
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
test('Web UI client.ts does not send X-Worker-Token header', () => {
|
|
426
|
+
const clientContent = readFileSafe(resolve(WEB_SRC, 'api', 'client.ts'))
|
|
427
|
+
if (!clientContent) return
|
|
428
|
+
expect(clientContent).not.toContain('X-Worker-Token')
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
test('Web UI no longer has WorkerTokenPrompt or it is not referenced', () => {
|
|
432
|
+
// Check if WorkerTokenPrompt.tsx exists and is referenced
|
|
433
|
+
const workerTokenPromptPath = resolve(
|
|
434
|
+
WEB_SRC,
|
|
435
|
+
'components',
|
|
436
|
+
'WorkerTokenPrompt.tsx',
|
|
437
|
+
)
|
|
438
|
+
const promptContent = readFileSafe(workerTokenPromptPath)
|
|
439
|
+
|
|
440
|
+
if (promptContent) {
|
|
441
|
+
// File exists — check it's not imported anywhere
|
|
442
|
+
const pagesDir = resolve(WEB_SRC, 'pages')
|
|
443
|
+
const dashboardContent = readFileSafe(resolve(pagesDir, 'Dashboard.tsx'))
|
|
444
|
+
const sessionDetailContent = readFileSafe(
|
|
445
|
+
resolve(pagesDir, 'SessionDetail.tsx'),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if (dashboardContent) {
|
|
449
|
+
expect(dashboardContent).not.toContain('WorkerTokenPrompt')
|
|
450
|
+
}
|
|
451
|
+
if (sessionDetailContent) {
|
|
452
|
+
expect(sessionDetailContent).not.toContain('WorkerTokenPrompt')
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// If file doesn't exist, test passes
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
test('useWorkerTokens hook is not imported in active components', () => {
|
|
459
|
+
// Check if useWorkerTokens is still referenced in active components
|
|
460
|
+
const dashboardContent = readFileSafe(
|
|
461
|
+
resolve(WEB_SRC, 'pages', 'Dashboard.tsx'),
|
|
462
|
+
)
|
|
463
|
+
const sessionDetailContent = readFileSafe(
|
|
464
|
+
resolve(WEB_SRC, 'pages', 'SessionDetail.tsx'),
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if (dashboardContent) {
|
|
468
|
+
expect(dashboardContent).not.toMatch(/import.*useWorkerTokens/)
|
|
469
|
+
}
|
|
470
|
+
if (sessionDetailContent) {
|
|
471
|
+
expect(sessionDetailContent).not.toMatch(/import.*useWorkerTokens/)
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
test('getWorkerToken and getWorkerTokenHash are removed from bridgeConfig', () => {
|
|
476
|
+
const bridgeConfigContent = readFileSafe(
|
|
477
|
+
resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
|
|
478
|
+
)
|
|
479
|
+
if (!bridgeConfigContent) return
|
|
480
|
+
expect(bridgeConfigContent).not.toMatch(
|
|
481
|
+
/export\s+function\s+getWorkerToken\b/,
|
|
482
|
+
)
|
|
483
|
+
expect(bridgeConfigContent).not.toMatch(
|
|
484
|
+
/export\s+function\s+getWorkerTokenHash\b/,
|
|
485
|
+
)
|
|
486
|
+
})
|
|
487
|
+
})
|
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { mockConfigModule } from './helpers/mock-config'
|
|
2
3
|
|
|
3
4
|
// Mock config before imports
|
|
4
|
-
|
|
5
|
-
port: 3000,
|
|
6
|
-
host: '0.0.0.0',
|
|
7
|
-
apiKeys: ['test-api-key'],
|
|
8
|
-
baseUrl: 'http://localhost:3000',
|
|
9
|
-
pollTimeout: 8,
|
|
10
|
-
heartbeatInterval: 20,
|
|
11
|
-
jwtExpiresIn: 3600,
|
|
12
|
-
disconnectTimeout: 300,
|
|
13
|
-
webCorsOrigins: [],
|
|
14
|
-
wsIdleTimeout: 30,
|
|
15
|
-
wsKeepaliveInterval: 20,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
mock.module('../config', () => ({
|
|
19
|
-
config: mockConfig,
|
|
20
|
-
getBaseUrl: () => 'http://localhost:3000',
|
|
21
|
-
}))
|
|
5
|
+
mock.module('../config', () => mockConfigModule())
|
|
22
6
|
|
|
23
7
|
import { storeReset } from '../store'
|
|
24
8
|
import {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity constants for the RCS auth system.
|
|
3
|
+
*
|
|
4
|
+
* SYSTEM_USER_ID is the identity assigned to API-key-authenticated operations.
|
|
5
|
+
* These operations are not attributable to any specific human user.
|
|
6
|
+
*
|
|
7
|
+
* ADMIN_FALLBACK_USER_ID is kept only for backward compatibility in code paths
|
|
8
|
+
* that previously relied on the literal string. New code should use SYSTEM_USER_ID.
|
|
9
|
+
*/
|
|
10
|
+
export const SYSTEM_USER_ID = '__system__'
|
|
11
|
+
|
|
12
|
+
/** @deprecated Use SYSTEM_USER_ID for new code. Kept for backward compat only. */
|
|
13
|
+
export const ADMIN_FALLBACK_USER_ID = '__admin__'
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sign cookie data with HMAC-SHA256.
|
|
5
|
+
* Returns `data.signature` format.
|
|
6
|
+
*
|
|
7
|
+
* Note: Currently unused by production code — token verification relies on
|
|
8
|
+
* database lookup. Kept as utility for potential future use.
|
|
9
|
+
*/
|
|
10
|
+
export function signCookie(data: string, secret: string): string {
|
|
11
|
+
const sig = createHmac('sha256', secret).update(data).digest('hex')
|
|
12
|
+
return `${data}.${sig}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse and verify a signed cookie value.
|
|
17
|
+
* Returns the original data if signature is valid, null otherwise.
|
|
18
|
+
*
|
|
19
|
+
* Note: Currently unused by production code — token verification relies on
|
|
20
|
+
* database lookup. Kept as utility for potential future use.
|
|
21
|
+
*/
|
|
22
|
+
export function parseCookie(
|
|
23
|
+
signedValue: string,
|
|
24
|
+
secret: string,
|
|
25
|
+
): string | null {
|
|
26
|
+
if (!signedValue) return null
|
|
27
|
+
|
|
28
|
+
const lastDot = signedValue.lastIndexOf('.')
|
|
29
|
+
if (lastDot === -1) return null
|
|
30
|
+
|
|
31
|
+
const data = signedValue.slice(0, lastDot)
|
|
32
|
+
const sig = signedValue.slice(lastDot + 1)
|
|
33
|
+
|
|
34
|
+
const expectedSig = createHmac('sha256', secret).update(data).digest('hex')
|
|
35
|
+
|
|
36
|
+
const a = Buffer.from(sig, 'utf8')
|
|
37
|
+
const b = Buffer.from(expectedSig, 'utf8')
|
|
38
|
+
if (a.length !== b.length) return null
|
|
39
|
+
if (!timingSafeEqual(a, b)) return null
|
|
40
|
+
|
|
41
|
+
return data
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build Set-Cookie header values for access and refresh tokens.
|
|
46
|
+
*
|
|
47
|
+
* Access token cookie: HttpOnly, Secure (default true), SameSite=Strict,
|
|
48
|
+
* Path=/, Max-Age=3600 (1 hour)
|
|
49
|
+
* Refresh token cookie: HttpOnly, Secure (default true), SameSite=Strict,
|
|
50
|
+
* Path=/, Max-Age=2592000 (30 days)
|
|
51
|
+
*/
|
|
52
|
+
export function buildSessionCookies(
|
|
53
|
+
accessToken: string,
|
|
54
|
+
refreshToken: string,
|
|
55
|
+
options?: { secure?: boolean },
|
|
56
|
+
): string[] {
|
|
57
|
+
const secure = options?.secure ?? true
|
|
58
|
+
const secureFlag = secure ? '; Secure' : ''
|
|
59
|
+
|
|
60
|
+
const accessCookie = `rcs_access=${accessToken}; Path=/; HttpOnly; SameSite=Strict; Max-Age=3600${secureFlag}`
|
|
61
|
+
|
|
62
|
+
const refreshCookie = `rcs_refresh=${refreshToken}; Path=/; HttpOnly; SameSite=Strict; Max-Age=2592000${secureFlag}`
|
|
63
|
+
|
|
64
|
+
return [accessCookie, refreshCookie]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build Set-Cookie headers to clear session cookies.
|
|
69
|
+
*/
|
|
70
|
+
export function buildClearCookies(): string[] {
|
|
71
|
+
return [
|
|
72
|
+
'rcs_access=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0',
|
|
73
|
+
'rcs_refresh=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0',
|
|
74
|
+
]
|
|
75
|
+
}
|