@saluzi/saluzi-edu 0.2.13 → 0.2.15
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 +79 -72
- package/dist/rcs-web/assets/AdminPanel-CQU763nr.js +1 -0
- package/dist/rcs-web/assets/Dashboard-B1hQfqI9.js +1 -0
- package/dist/rcs-web/assets/Join-7hMl360t.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/Login-0VGZs85I.js → dist/rcs-web/assets/Login-BE8mNc0U.js} +1 -1
- package/dist/rcs-web/assets/SessionDetail-B5C0o1YT.js +55 -0
- package/dist/rcs-web/assets/SessionVisibilityBadge-CQN3bJW4.js +11 -0
- package/dist/rcs-web/assets/{Setup-C0cjxXMt.js → Setup-CqZ5gEBQ.js} +1 -1
- package/dist/rcs-web/assets/{ShareLanding-DUZrreJ7.js → ShareLanding-DBC9gbJo.js} +1 -1
- package/dist/rcs-web/assets/TeamCreate-DSYdTaPe.js +1 -0
- package/dist/rcs-web/assets/TeamDetail-CmJGsgOP.js +1 -0
- package/dist/rcs-web/assets/TeamList-BMS_Qq8U.js +1 -0
- package/dist/rcs-web/assets/{TeamSettings-CNVwYwcp.js → TeamSettings-C0sYgCFT.js} +1 -1
- package/dist/rcs-web/assets/UserSettings-CqzeJ5cc.js +1 -0
- package/{packages/remote-control-server/web/dist/assets/arc-CJxirXEQ.js → dist/rcs-web/assets/arc-DBPKxYzS.js} +1 -1
- package/dist/rcs-web/assets/{architectureDiagram-3BPJPVTR-BZvIHrLA.js → architectureDiagram-3BPJPVTR-D88rj_js.js} +1 -1
- package/dist/rcs-web/assets/{blockDiagram-GPEHLZMM-3p0y3Ija.js → blockDiagram-GPEHLZMM-xzgUpRMn.js} +1 -1
- package/dist/rcs-web/assets/{c4Diagram-AAUBKEIU-Bmz9A1wn.js → c4Diagram-AAUBKEIU-vpg_EoPb.js} +1 -1
- package/dist/rcs-web/assets/channel-Daz3yMSu.js +1 -0
- package/dist/rcs-web/assets/{chunk-2J33WTMH-B8gQz22j.js → chunk-2J33WTMH-DXWsXvhS.js} +1 -1
- package/dist/rcs-web/assets/{chunk-4BX2VUAB-CgLEZsTU.js → chunk-4BX2VUAB-BtTxwj9K.js} +1 -1
- package/dist/rcs-web/assets/{chunk-55IACEB6-0mdE_-IS.js → chunk-55IACEB6-D2-Q95qg.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/chunk-727SXJPM-DdtjnIHR.js → dist/rcs-web/assets/chunk-727SXJPM-Ce3e5x8u.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/chunk-AQP2D5EJ-BtwFPk50.js → dist/rcs-web/assets/chunk-AQP2D5EJ-C0UbKVtd.js} +1 -1
- package/dist/rcs-web/assets/{chunk-FMBD7UC4-BcO9mQ0P.js → chunk-FMBD7UC4-BCwppVqh.js} +1 -1
- package/dist/rcs-web/assets/{chunk-ND2GUHAM-BvRq13S_.js → chunk-ND2GUHAM-CnOBwWrm.js} +1 -1
- package/dist/rcs-web/assets/{chunk-QZHKN3VN-D7GHkPpq.js → chunk-QZHKN3VN-CDdzB71P.js} +1 -1
- package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-DufdcV1P.js +1 -0
- package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-DufdcV1P.js +1 -0
- package/dist/rcs-web/assets/{code-block-IT6T5CEO-B1p-vO-3.js → code-block-IT6T5CEO-C1_H_ztH.js} +1 -1
- package/dist/rcs-web/assets/copy-DXU1lQqc.js +6 -0
- package/{packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-L0rUZuH5.js → dist/rcs-web/assets/cose-bilkent-S5V4N54A-BC6nfjTF.js} +1 -1
- package/dist/rcs-web/assets/{dagre-BM42HDAG-CHGuio22.js → dagre-BM42HDAG-CEPR_pRx.js} +1 -1
- package/dist/rcs-web/assets/{diagram-2AECGRRQ-DIDRyQk8.js → diagram-2AECGRRQ-fVMGnSYa.js} +1 -1
- package/dist/rcs-web/assets/{diagram-5GNKFQAL-CNubKb6P.js → diagram-5GNKFQAL-BPzN2rbK.js} +1 -1
- package/dist/rcs-web/assets/{diagram-KO2AKTUF-CzQh664-.js → diagram-KO2AKTUF-Boneg9n-.js} +1 -1
- package/dist/rcs-web/assets/{diagram-LMA3HP47-CWBR7-VG.js → diagram-LMA3HP47-B8AtvjO9.js} +1 -1
- package/dist/rcs-web/assets/{diagram-OG6HWLK6-BMHdlbcZ.js → diagram-OG6HWLK6-DjY7A8jK.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-OgT8dC-K.js → dist/rcs-web/assets/erDiagram-TEJ5UH35-C1BEfKxk.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/flowDiagram-I6XJVG4X-DRrCY2oB.js → dist/rcs-web/assets/flowDiagram-I6XJVG4X-7TbI9MLV.js} +1 -1
- package/dist/rcs-web/assets/{ganttDiagram-6RSMTGT7-DctpzEKN.js → ganttDiagram-6RSMTGT7-CMAV0A01.js} +1 -1
- package/dist/rcs-web/assets/{gitGraphDiagram-PVQCEYII-5KpLszm9.js → gitGraphDiagram-PVQCEYII-DAlR8G81.js} +1 -1
- package/dist/rcs-web/assets/{infoDiagram-5YYISTIA-BAKD_Tt7.js → infoDiagram-5YYISTIA-0T0DAV4W.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-VHOPG4Nc.js → dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-BAw8txlx.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/journeyDiagram-JHISSGLW-BtGih-ik.js → dist/rcs-web/assets/journeyDiagram-JHISSGLW-DWvYKwz-.js} +1 -1
- package/dist/rcs-web/assets/{kanban-definition-UN3LZRKU-BsEGZsD1.js → kanban-definition-UN3LZRKU-Qzqw1H5B.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/linear-Dj636PNG.js → dist/rcs-web/assets/linear-D2KIJoCI.js} +1 -1
- package/dist/rcs-web/assets/main-J6cPs2y8.js +253 -0
- package/dist/rcs-web/assets/{mermaid-VLURNSYL-DutpHHm9.js → mermaid-VLURNSYL-CxWk1wHz.js} +4 -4
- package/dist/rcs-web/assets/{mermaid.core-XHHf0pen.js → mermaid.core-C48YvKu4.js} +5 -5
- package/dist/rcs-web/assets/{mindmap-definition-RKZ34NQL-BBa8V_3g.js → mindmap-definition-RKZ34NQL-BfmcTuCA.js} +1 -1
- package/dist/rcs-web/assets/{pieDiagram-4H26LBE5-BuNEdo9H.js → pieDiagram-4H26LBE5-DVNZpnfb.js} +1 -1
- package/dist/rcs-web/assets/{quadrantDiagram-W4KKPZXB-CsJgyQIN.js → quadrantDiagram-W4KKPZXB-COL55Z3i.js} +1 -1
- package/dist/rcs-web/assets/{requirementDiagram-4Y6WPE33-DqMJZ914.js → requirementDiagram-4Y6WPE33-CFczYQ0a.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-CVsz09q2.js → dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-BrQEv3WQ.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-BRp4Wddn.js → dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-CMxtYmQu.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-S-QqQr-D.js → dist/rcs-web/assets/stateDiagram-AJRCARHV-Dw9xJZ-A.js} +1 -1
- package/dist/rcs-web/assets/{stateDiagram-v2-BHNVJYJU-D77KfDr-.js → stateDiagram-v2-BHNVJYJU-Br0D-weX.js} +1 -1
- package/{packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-De8bFWnE.js → dist/rcs-web/assets/timeline-definition-PNZ67QCA-DuWDwYBt.js} +1 -1
- package/dist/rcs-web/assets/{vennDiagram-CIIHVFJN-Dikwrn1L.js → vennDiagram-CIIHVFJN--7IV65HQ.js} +1 -1
- package/dist/rcs-web/assets/{wardleyDiagram-YWT4CUSO-DUzpiV6H.js → wardleyDiagram-YWT4CUSO-CVTgWbvT.js} +1 -1
- package/dist/rcs-web/assets/{xychartDiagram-2RQKCTM6-OZNbB32D.js → xychartDiagram-2RQKCTM6-B9om4xAF.js} +1 -1
- package/dist/rcs-web/index.html +1 -1
- package/package.json +1 -1
- package/packages/remote-control-server/src/__tests__/auth-login.test.ts +21 -4
- package/packages/remote-control-server/src/__tests__/auth-security.test.ts +12 -6
- package/packages/remote-control-server/src/__tests__/data-layer-round2.test.ts +12 -11
- package/packages/remote-control-server/src/__tests__/db-isolation.test.ts +61 -0
- package/packages/remote-control-server/src/__tests__/low-priority-fixes.test.ts +178 -0
- package/packages/remote-control-server/src/__tests__/middleware.test.ts +4 -4
- package/packages/remote-control-server/src/__tests__/route-mounting.test.ts +26 -8
- package/packages/remote-control-server/src/__tests__/security-fixes.test.ts +32 -27
- package/packages/remote-control-server/src/__tests__/security-round3.test.ts +5 -1
- package/packages/remote-control-server/src/__tests__/team-filter-api.test.ts +280 -0
- package/packages/remote-control-server/src/__tests__/team-session-env.test.ts +169 -0
- package/packages/remote-control-server/src/__tests__/teams.test.ts +7 -0
- package/packages/remote-control-server/src/auth/csrf.ts +1 -3
- package/packages/remote-control-server/src/auth/middleware.ts +13 -4
- package/packages/remote-control-server/src/index.ts +11 -8
- package/packages/remote-control-server/src/routes/v1/environments.ts +12 -9
- package/packages/remote-control-server/src/routes/v1/sessions.ts +1 -1
- package/packages/remote-control-server/src/routes/web/auth-routes.ts +3 -1
- package/packages/remote-control-server/src/routes/web/auth.ts +2 -1
- package/packages/remote-control-server/src/routes/web/environments.ts +22 -4
- package/packages/remote-control-server/src/routes/web/sessions.ts +24 -12
- package/packages/remote-control-server/src/routes/web/shares.ts +11 -2
- package/packages/remote-control-server/src/routes/web/teams.ts +6 -9
- package/packages/remote-control-server/src/services/environment.ts +8 -3
- package/packages/remote-control-server/src/services/session.ts +56 -45
- package/packages/remote-control-server/src/store.ts +128 -7
- package/packages/remote-control-server/src/types/api.ts +8 -1
- package/packages/remote-control-server/web/dist/assets/AdminPanel-CQU763nr.js +1 -0
- package/packages/remote-control-server/web/dist/assets/Dashboard-B1hQfqI9.js +1 -0
- package/packages/remote-control-server/web/dist/assets/Join-7hMl360t.js +1 -0
- package/{dist/rcs-web/assets/Login-0VGZs85I.js → packages/remote-control-server/web/dist/assets/Login-BE8mNc0U.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/SessionDetail-B5C0o1YT.js +55 -0
- package/packages/remote-control-server/web/dist/assets/SessionVisibilityBadge-CQN3bJW4.js +11 -0
- package/packages/remote-control-server/web/dist/assets/{Setup-C0cjxXMt.js → Setup-CqZ5gEBQ.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{ShareLanding-DUZrreJ7.js → ShareLanding-DBC9gbJo.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/TeamCreate-DSYdTaPe.js +1 -0
- package/packages/remote-control-server/web/dist/assets/TeamDetail-CmJGsgOP.js +1 -0
- package/packages/remote-control-server/web/dist/assets/TeamList-BMS_Qq8U.js +1 -0
- package/packages/remote-control-server/web/dist/assets/{TeamSettings-CNVwYwcp.js → TeamSettings-C0sYgCFT.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/UserSettings-CqzeJ5cc.js +1 -0
- package/{dist/rcs-web/assets/arc-CJxirXEQ.js → packages/remote-control-server/web/dist/assets/arc-DBPKxYzS.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{architectureDiagram-3BPJPVTR-BZvIHrLA.js → architectureDiagram-3BPJPVTR-D88rj_js.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{blockDiagram-GPEHLZMM-3p0y3Ija.js → blockDiagram-GPEHLZMM-xzgUpRMn.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{c4Diagram-AAUBKEIU-Bmz9A1wn.js → c4Diagram-AAUBKEIU-vpg_EoPb.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/channel-Daz3yMSu.js +1 -0
- package/packages/remote-control-server/web/dist/assets/{chunk-2J33WTMH-B8gQz22j.js → chunk-2J33WTMH-DXWsXvhS.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-4BX2VUAB-CgLEZsTU.js → chunk-4BX2VUAB-BtTxwj9K.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-55IACEB6-0mdE_-IS.js → chunk-55IACEB6-D2-Q95qg.js} +1 -1
- package/{dist/rcs-web/assets/chunk-727SXJPM-DdtjnIHR.js → packages/remote-control-server/web/dist/assets/chunk-727SXJPM-Ce3e5x8u.js} +1 -1
- package/{dist/rcs-web/assets/chunk-AQP2D5EJ-BtwFPk50.js → packages/remote-control-server/web/dist/assets/chunk-AQP2D5EJ-C0UbKVtd.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-FMBD7UC4-BcO9mQ0P.js → chunk-FMBD7UC4-BCwppVqh.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-ND2GUHAM-BvRq13S_.js → chunk-ND2GUHAM-CnOBwWrm.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{chunk-QZHKN3VN-D7GHkPpq.js → chunk-QZHKN3VN-CDdzB71P.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-DufdcV1P.js +1 -0
- package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-DufdcV1P.js +1 -0
- package/packages/remote-control-server/web/dist/assets/{code-block-IT6T5CEO-B1p-vO-3.js → code-block-IT6T5CEO-C1_H_ztH.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/copy-DXU1lQqc.js +6 -0
- package/{dist/rcs-web/assets/cose-bilkent-S5V4N54A-L0rUZuH5.js → packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-BC6nfjTF.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{dagre-BM42HDAG-CHGuio22.js → dagre-BM42HDAG-CEPR_pRx.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{diagram-2AECGRRQ-DIDRyQk8.js → diagram-2AECGRRQ-fVMGnSYa.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{diagram-5GNKFQAL-CNubKb6P.js → diagram-5GNKFQAL-BPzN2rbK.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{diagram-KO2AKTUF-CzQh664-.js → diagram-KO2AKTUF-Boneg9n-.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{diagram-LMA3HP47-CWBR7-VG.js → diagram-LMA3HP47-B8AtvjO9.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{diagram-OG6HWLK6-BMHdlbcZ.js → diagram-OG6HWLK6-DjY7A8jK.js} +1 -1
- package/{dist/rcs-web/assets/erDiagram-TEJ5UH35-OgT8dC-K.js → packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-C1BEfKxk.js} +1 -1
- package/{dist/rcs-web/assets/flowDiagram-I6XJVG4X-DRrCY2oB.js → packages/remote-control-server/web/dist/assets/flowDiagram-I6XJVG4X-7TbI9MLV.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{ganttDiagram-6RSMTGT7-DctpzEKN.js → ganttDiagram-6RSMTGT7-CMAV0A01.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{gitGraphDiagram-PVQCEYII-5KpLszm9.js → gitGraphDiagram-PVQCEYII-DAlR8G81.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{infoDiagram-5YYISTIA-BAKD_Tt7.js → infoDiagram-5YYISTIA-0T0DAV4W.js} +1 -1
- package/{dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-VHOPG4Nc.js → packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-BAw8txlx.js} +1 -1
- package/{dist/rcs-web/assets/journeyDiagram-JHISSGLW-BtGih-ik.js → packages/remote-control-server/web/dist/assets/journeyDiagram-JHISSGLW-DWvYKwz-.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{kanban-definition-UN3LZRKU-BsEGZsD1.js → kanban-definition-UN3LZRKU-Qzqw1H5B.js} +1 -1
- package/{dist/rcs-web/assets/linear-Dj636PNG.js → packages/remote-control-server/web/dist/assets/linear-D2KIJoCI.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/main-J6cPs2y8.js +253 -0
- package/packages/remote-control-server/web/dist/assets/{mermaid-VLURNSYL-DutpHHm9.js → mermaid-VLURNSYL-CxWk1wHz.js} +4 -4
- package/packages/remote-control-server/web/dist/assets/{mermaid.core-XHHf0pen.js → mermaid.core-C48YvKu4.js} +5 -5
- package/packages/remote-control-server/web/dist/assets/{mindmap-definition-RKZ34NQL-BBa8V_3g.js → mindmap-definition-RKZ34NQL-BfmcTuCA.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{pieDiagram-4H26LBE5-BuNEdo9H.js → pieDiagram-4H26LBE5-DVNZpnfb.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{quadrantDiagram-W4KKPZXB-CsJgyQIN.js → quadrantDiagram-W4KKPZXB-COL55Z3i.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{requirementDiagram-4Y6WPE33-DqMJZ914.js → requirementDiagram-4Y6WPE33-CFczYQ0a.js} +1 -1
- package/{dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-CVsz09q2.js → packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-BrQEv3WQ.js} +1 -1
- package/{dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-BRp4Wddn.js → packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-CMxtYmQu.js} +1 -1
- package/{dist/rcs-web/assets/stateDiagram-AJRCARHV-S-QqQr-D.js → packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-Dw9xJZ-A.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{stateDiagram-v2-BHNVJYJU-D77KfDr-.js → stateDiagram-v2-BHNVJYJU-Br0D-weX.js} +1 -1
- package/{dist/rcs-web/assets/timeline-definition-PNZ67QCA-De8bFWnE.js → packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-DuWDwYBt.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{vennDiagram-CIIHVFJN-Dikwrn1L.js → vennDiagram-CIIHVFJN--7IV65HQ.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{wardleyDiagram-YWT4CUSO-DUzpiV6H.js → wardleyDiagram-YWT4CUSO-CVTgWbvT.js} +1 -1
- package/packages/remote-control-server/web/dist/assets/{xychartDiagram-2RQKCTM6-OZNbB32D.js → xychartDiagram-2RQKCTM6-B9om4xAF.js} +1 -1
- package/packages/remote-control-server/web/dist/index.html +1 -1
- package/dist/rcs-web/assets/Dashboard-YYwDrKTN.js +0 -1
- package/dist/rcs-web/assets/Join-DKCP5X6X.js +0 -1
- package/dist/rcs-web/assets/SessionDetail-DmK-DfBQ.js +0 -50
- package/dist/rcs-web/assets/TeamCreate-B8ZV6EdY.js +0 -1
- package/dist/rcs-web/assets/TeamDetail-DBAsJ__i.js +0 -11
- package/dist/rcs-web/assets/TeamList-C-Hte4nV.js +0 -1
- package/dist/rcs-web/assets/UserSettings-DRdz9kYE.js +0 -1
- package/dist/rcs-web/assets/channel-DaI_n8dp.js +0 -1
- package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-pYllpkOq.js +0 -1
- package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-pYllpkOq.js +0 -1
- package/dist/rcs-web/assets/main-CkqtCllF.js +0 -238
- package/dist/rcs-web/assets/useTeams-CqJOqEy1.js +0 -1
- package/dist/rcs-web/assets/workerType-DOQ0AHrq.js +0 -1
- package/packages/remote-control-server/web/dist/assets/Dashboard-YYwDrKTN.js +0 -1
- package/packages/remote-control-server/web/dist/assets/Join-DKCP5X6X.js +0 -1
- package/packages/remote-control-server/web/dist/assets/SessionDetail-DmK-DfBQ.js +0 -50
- package/packages/remote-control-server/web/dist/assets/TeamCreate-B8ZV6EdY.js +0 -1
- package/packages/remote-control-server/web/dist/assets/TeamDetail-DBAsJ__i.js +0 -11
- package/packages/remote-control-server/web/dist/assets/TeamList-C-Hte4nV.js +0 -1
- package/packages/remote-control-server/web/dist/assets/UserSettings-DRdz9kYE.js +0 -1
- package/packages/remote-control-server/web/dist/assets/channel-DaI_n8dp.js +0 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-pYllpkOq.js +0 -1
- package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-pYllpkOq.js +0 -1
- package/packages/remote-control-server/web/dist/assets/main-CkqtCllF.js +0 -238
- package/packages/remote-control-server/web/dist/assets/useTeams-CqJOqEy1.js +0 -1
- package/packages/remote-control-server/web/dist/assets/workerType-DOQ0AHrq.js +0 -1
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, mock } from 'bun:test'
|
|
2
|
+
import { mockConfigModule } from './helpers/mock-config'
|
|
3
|
+
|
|
4
|
+
mock.module('../config', () => mockConfigModule())
|
|
5
|
+
|
|
6
|
+
import { Hono } from 'hono'
|
|
7
|
+
import { Database } from 'bun:sqlite'
|
|
8
|
+
import { randomUUID } from 'node:crypto'
|
|
9
|
+
import { tmpdir } from 'node:os'
|
|
10
|
+
import { resolve } from 'node:path'
|
|
11
|
+
import { unlinkSync, existsSync } from 'node:fs'
|
|
12
|
+
import webSessions from '../routes/web/sessions'
|
|
13
|
+
import webEnvironments from '../routes/web/environments'
|
|
14
|
+
import { storeReset, storeCreateEnvironment, storeBindSession } from '../store'
|
|
15
|
+
import { initDatabase, resetDbSingleton, getDb } from '../db/sqlite'
|
|
16
|
+
import { issueSessionToken } from '../auth/session'
|
|
17
|
+
|
|
18
|
+
function freshDb(): Database {
|
|
19
|
+
try { resetDbSingleton() } catch {}
|
|
20
|
+
const path = resolve(tmpdir(), `rcs-team-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`)
|
|
21
|
+
return initDatabase(path)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createUser(db: Database, role = 'member'): { id: string; username: string } {
|
|
25
|
+
const id = `usr_${randomUUID().replace(/-/g, '')}`
|
|
26
|
+
const now = new Date().toISOString()
|
|
27
|
+
db.query(
|
|
28
|
+
`INSERT INTO users (id, username, password_hash, role, created_at) VALUES ($id, $u, 'h', $r, $now)`,
|
|
29
|
+
).run({ $id: id, $u: `user_${id.slice(0, 6)}`, $r: role, $now: now })
|
|
30
|
+
return { id, username: `user_${id.slice(0, 6)}` }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createTeam(db: Database, createdBy: string): string {
|
|
34
|
+
const id = `team_${randomUUID().replace(/-/g, '')}`
|
|
35
|
+
const now = new Date().toISOString()
|
|
36
|
+
db.query(
|
|
37
|
+
`INSERT INTO teams (id, name, slug, created_by, created_at, updated_at) VALUES ($id, $n, $s, $by, $now, $now)`,
|
|
38
|
+
).run({ $id: id, $n: 'Eng', $s: `eng-${id.slice(0, 6)}`, $by: createdBy, $now: now })
|
|
39
|
+
db.query(
|
|
40
|
+
`INSERT INTO team_members (team_id, user_id, role, added_at, added_by) VALUES ($t, $u, 'member', $now, $by)`,
|
|
41
|
+
).run({ $t: id, $u: createdBy, $now: now, $by: createdBy })
|
|
42
|
+
return id
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function linkEnvToTeam(db: Database, envId: string, teamId: string): void {
|
|
46
|
+
db.query(
|
|
47
|
+
`INSERT OR IGNORE INTO team_environments (team_id, environment_id) VALUES ($t, $e)`,
|
|
48
|
+
).run({ $t: teamId, $e: envId })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function authHeader(token: string): Record<string, string> {
|
|
52
|
+
return { Authorization: `Bearer ${token}` }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createApp(): Hono {
|
|
56
|
+
const app = new Hono()
|
|
57
|
+
app.route('/web', webSessions)
|
|
58
|
+
app.route('/web', webEnvironments)
|
|
59
|
+
return app
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('团队域 session 创建与 environment 可见性', () => {
|
|
63
|
+
let db: Database
|
|
64
|
+
let app: Hono
|
|
65
|
+
let user: { id: string; username: string }
|
|
66
|
+
let accessToken: string
|
|
67
|
+
let teamId: string
|
|
68
|
+
let envId: string
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
db = freshDb()
|
|
72
|
+
app = createApp()
|
|
73
|
+
user = createUser(db)
|
|
74
|
+
teamId = createTeam(db, user.id)
|
|
75
|
+
|
|
76
|
+
// Register an environment and link it to the team
|
|
77
|
+
const env = storeCreateEnvironment({ secret: 'secret', machineName: 'dev', username: user.id })
|
|
78
|
+
envId = env.id
|
|
79
|
+
linkEnvToTeam(db, envId, teamId)
|
|
80
|
+
|
|
81
|
+
// Issue a session token for the user
|
|
82
|
+
const { accessToken: at } = issueSessionToken(user.id, db)
|
|
83
|
+
accessToken = at
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('团队域新建 session 应设 visibility=team 并绑 team owner', async () => {
|
|
87
|
+
const res = await app.request('/web/sessions', {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: { 'Content-Type': 'application/json', ...authHeader(accessToken) },
|
|
90
|
+
body: JSON.stringify({
|
|
91
|
+
title: 'Team Session',
|
|
92
|
+
environment_id: envId,
|
|
93
|
+
team_id: teamId,
|
|
94
|
+
}),
|
|
95
|
+
})
|
|
96
|
+
expect(res.status).toBe(200)
|
|
97
|
+
const session = await res.json()
|
|
98
|
+
|
|
99
|
+
// Bug A: visibility 应为 'team',不是 'private'
|
|
100
|
+
expect(session.visibility).toBe('team')
|
|
101
|
+
|
|
102
|
+
// session_owners 应有 owner_type='team', owner_id=teamId
|
|
103
|
+
const owners = db
|
|
104
|
+
.query('SELECT owner_type, owner_id FROM session_owners WHERE session_id = $sid')
|
|
105
|
+
.all({ $sid: session.id }) as Array<{ owner_type: string; owner_id: string }>
|
|
106
|
+
const teamOwner = owners.find(o => o.owner_type === 'team')
|
|
107
|
+
expect(teamOwner).toBeDefined()
|
|
108
|
+
expect(teamOwner!.owner_id).toBe(teamId)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('团队域 session 应被 team 成员可见', async () => {
|
|
112
|
+
// Create a session in team context
|
|
113
|
+
const createRes = await app.request('/web/sessions', {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: { 'Content-Type': 'application/json', ...authHeader(accessToken) },
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
title: 'Team Session',
|
|
118
|
+
environment_id: envId,
|
|
119
|
+
team_id: teamId,
|
|
120
|
+
}),
|
|
121
|
+
})
|
|
122
|
+
const session = await createRes.json()
|
|
123
|
+
|
|
124
|
+
// Add another team member
|
|
125
|
+
const member2 = createUser(db)
|
|
126
|
+
db.query(
|
|
127
|
+
`INSERT INTO team_members (team_id, user_id, role, added_at, added_by) VALUES ($t, $u, 'member', $now, $by)`,
|
|
128
|
+
).run({ $t: teamId, $u: member2.id, $now: new Date().toISOString(), $by: user.id })
|
|
129
|
+
|
|
130
|
+
// Member2 should be able to resolve the session (not 403)
|
|
131
|
+
const { accessToken: at2 } = issueSessionToken(member2.id, db)
|
|
132
|
+
const getRes = await app.request(`/web/sessions/${session.id}`, {
|
|
133
|
+
headers: authHeader(at2),
|
|
134
|
+
})
|
|
135
|
+
expect(getRes.status).not.toBe(403)
|
|
136
|
+
// Should be 200 (session visible to team member)
|
|
137
|
+
expect(getRes.status).toBe(200)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test('团队域应能看到属于该 team 的 environment', async () => {
|
|
141
|
+
const res = await app.request('/web/environments', {
|
|
142
|
+
headers: authHeader(accessToken),
|
|
143
|
+
})
|
|
144
|
+
expect(res.status).toBe(200)
|
|
145
|
+
const envs = await res.json()
|
|
146
|
+
expect(Array.isArray(envs)).toBe(true)
|
|
147
|
+
expect(envs.length).toBeGreaterThanOrEqual(1)
|
|
148
|
+
|
|
149
|
+
// The team-linked env should have team_id and team_name
|
|
150
|
+
const teamEnv = envs.find((e: { team_id?: string }) => e.team_id === teamId)
|
|
151
|
+
expect(teamEnv).toBeDefined()
|
|
152
|
+
expect(teamEnv.team_name).toBe('Eng')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test('个人域不应看到 team 的 environment', async () => {
|
|
156
|
+
// Create a user with no team membership
|
|
157
|
+
const solo = createUser(db)
|
|
158
|
+
const { accessToken: at } = issueSessionToken(solo.id, db)
|
|
159
|
+
|
|
160
|
+
const res = await app.request('/web/environments', {
|
|
161
|
+
headers: authHeader(at),
|
|
162
|
+
})
|
|
163
|
+
expect(res.status).toBe(200)
|
|
164
|
+
const envs = await res.json()
|
|
165
|
+
// Solo user should NOT see the team's environment (only admin sees all)
|
|
166
|
+
const teamEnv = envs.find((e: { team_id?: string }) => e.team_id === teamId)
|
|
167
|
+
expect(teamEnv).toBeUndefined()
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -1479,6 +1479,13 @@ describe('POST /web/environments/:id/transfer', () => {
|
|
|
1479
1479
|
})
|
|
1480
1480
|
|
|
1481
1481
|
test('team admin → 200', async () => {
|
|
1482
|
+
// teamAdmin must also be admin/owner in the target team (security fix)
|
|
1483
|
+
addTeamMember(db, {
|
|
1484
|
+
teamId: targetTeam.id,
|
|
1485
|
+
userId: teamAdmin.id,
|
|
1486
|
+
role: 'admin',
|
|
1487
|
+
addedBy: owner.id,
|
|
1488
|
+
})
|
|
1482
1489
|
const res = await app.request(`/web/environments/${envId}/transfer`, {
|
|
1483
1490
|
method: 'POST',
|
|
1484
1491
|
headers: {
|
|
@@ -40,9 +40,7 @@ export async function csrfCheck(c: Context, next: Next) {
|
|
|
40
40
|
// Support ALLOWED_ORIGINS env variable (comma-separated)
|
|
41
41
|
const envOrigins =
|
|
42
42
|
(c.env?.ALLOWED_ORIGINS as string | undefined) ??
|
|
43
|
-
(typeof process !== 'undefined'
|
|
44
|
-
? process.env.ALLOWED_ORIGINS
|
|
45
|
-
: undefined)
|
|
43
|
+
(typeof process !== 'undefined' ? process.env.ALLOWED_ORIGINS : undefined)
|
|
46
44
|
if (envOrigins) {
|
|
47
45
|
for (const o of envOrigins.split(',')) {
|
|
48
46
|
const trimmed = o.trim()
|
|
@@ -62,8 +62,8 @@ export function extractWebSocketAuthToken(c: Context): string | undefined {
|
|
|
62
62
|
/**
|
|
63
63
|
* Unified authentication middleware — supports two modes:
|
|
64
64
|
*
|
|
65
|
-
* 1. **Token mode** (Web UI): Bearer token resolved via server-side lookup →
|
|
66
|
-
* 2. **API Key mode** (CLI bridge): Valid API key + X-Username header →
|
|
65
|
+
* 1. **Token mode** (Web UI): Bearer token resolved via server-side lookup → userId injected
|
|
66
|
+
* 2. **API Key mode** (CLI bridge): Valid API key + X-Username header → userId injected
|
|
67
67
|
*/
|
|
68
68
|
export async function apiKeyAuth(c: Context, next: Next) {
|
|
69
69
|
const token = extractBearerToken(c)
|
|
@@ -71,7 +71,7 @@ export async function apiKeyAuth(c: Context, next: Next) {
|
|
|
71
71
|
// Try token authentication (Web UI)
|
|
72
72
|
const tokenUsername = resolveToken(token)
|
|
73
73
|
if (tokenUsername) {
|
|
74
|
-
c.set('
|
|
74
|
+
c.set('userId', tokenUsername)
|
|
75
75
|
await next()
|
|
76
76
|
return
|
|
77
77
|
}
|
|
@@ -81,7 +81,7 @@ export async function apiKeyAuth(c: Context, next: Next) {
|
|
|
81
81
|
// Extract username from X-Username header or ?username= query param
|
|
82
82
|
const username = c.req.header('X-Username') || c.req.query('username')
|
|
83
83
|
if (username) {
|
|
84
|
-
c.set('
|
|
84
|
+
c.set('userId', username)
|
|
85
85
|
}
|
|
86
86
|
await next()
|
|
87
87
|
return
|
|
@@ -223,6 +223,15 @@ export async function sessionAuth(c: Context, next: Next) {
|
|
|
223
223
|
}
|
|
224
224
|
} catch (err) {
|
|
225
225
|
console.warn('[sessionAuth] DB resolution failed:', err)
|
|
226
|
+
return c.json(
|
|
227
|
+
{
|
|
228
|
+
error: {
|
|
229
|
+
type: 'service_unavailable',
|
|
230
|
+
message: 'Database unavailable — cannot resolve session token',
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
503,
|
|
234
|
+
)
|
|
226
235
|
}
|
|
227
236
|
}
|
|
228
237
|
|
|
@@ -66,14 +66,17 @@ try {
|
|
|
66
66
|
console.warn('[RCS] Failed to reap expired sessions:', err)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const reapInterval = setInterval(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
69
|
+
const reapInterval = setInterval(
|
|
70
|
+
() => {
|
|
71
|
+
try {
|
|
72
|
+
const db = getDb()
|
|
73
|
+
reapExpiredSessions(db)
|
|
74
|
+
} catch {
|
|
75
|
+
// ignore — DB may not be available
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
60 * 60 * 1000,
|
|
79
|
+
) // every hour
|
|
77
80
|
|
|
78
81
|
if (reapInterval.unref) {
|
|
79
82
|
reapInterval.unref()
|
|
@@ -27,14 +27,14 @@ async function bridgeAuth(c: Context, next: Next) {
|
|
|
27
27
|
if (bearer) {
|
|
28
28
|
// Admin API key
|
|
29
29
|
if (validateApiKey(bearer)) {
|
|
30
|
-
c.set('
|
|
30
|
+
c.set('userId', c.req.header('X-Username') || c.req.query('username'))
|
|
31
31
|
await next()
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
34
|
// In-memory token
|
|
35
35
|
const username = resolveToken(bearer)
|
|
36
36
|
if (username) {
|
|
37
|
-
c.set('
|
|
37
|
+
c.set('userId', username)
|
|
38
38
|
await next()
|
|
39
39
|
return
|
|
40
40
|
}
|
|
@@ -47,8 +47,9 @@ async function bridgeAuth(c: Context, next: Next) {
|
|
|
47
47
|
await next()
|
|
48
48
|
return
|
|
49
49
|
}
|
|
50
|
-
} catch {
|
|
51
|
-
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error('[bridge] DB error during session token resolution:', err)
|
|
52
|
+
return c.json({ error: 'Database unavailable' }, 503)
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
// Fall back to apiKeyAuth (handles X-Username + API key from body/header)
|
|
@@ -59,7 +60,7 @@ async function bridgeAuth(c: Context, next: Next) {
|
|
|
59
60
|
app.post('/bridge', acceptCliHeaders, bridgeAuth, async c => {
|
|
60
61
|
const body = await c.req.json()
|
|
61
62
|
const userId = c.get('userId') as string | undefined
|
|
62
|
-
const username =
|
|
63
|
+
const username = userId
|
|
63
64
|
const teamId = c.req.header('X-Team-Id')
|
|
64
65
|
|
|
65
66
|
// Validate team if X-Team-Id is provided
|
|
@@ -102,8 +103,9 @@ app.post('/bridge', acceptCliHeaders, bridgeAuth, async c => {
|
|
|
102
103
|
)
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
|
-
} catch {
|
|
106
|
-
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[bridge] DB error during team validation:', err)
|
|
108
|
+
return c.json({ error: 'Database unavailable' }, 503)
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -116,8 +118,9 @@ app.post('/bridge', acceptCliHeaders, bridgeAuth, async c => {
|
|
|
116
118
|
db.query(
|
|
117
119
|
'INSERT OR IGNORE INTO team_environments (team_id, environment_id) VALUES ($teamId, $envId)',
|
|
118
120
|
).run({ $teamId: teamId, $envId: result.environment_id })
|
|
119
|
-
} catch {
|
|
120
|
-
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.error('[bridge] DB error during team-environment link:', err)
|
|
123
|
+
return c.json({ error: 'Database unavailable' }, 503)
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -16,7 +16,7 @@ const app = new Hono()
|
|
|
16
16
|
/** POST /v1/sessions — Create session */
|
|
17
17
|
app.post('/', acceptCliHeaders, apiKeyAuth, async c => {
|
|
18
18
|
const body = await c.req.json()
|
|
19
|
-
const username = c.get('
|
|
19
|
+
const username = c.get('userId')
|
|
20
20
|
const session = createSession({ ...body, username })
|
|
21
21
|
|
|
22
22
|
// Create work item if environment is specified
|
|
@@ -452,7 +452,9 @@ app.post('/join', async c => {
|
|
|
452
452
|
// Re-check invitation state inside the transaction (may have changed).
|
|
453
453
|
// Only check uses >= max_uses — consumed_at is set conditionally below.
|
|
454
454
|
const currentInv = db
|
|
455
|
-
.query(
|
|
455
|
+
.query(
|
|
456
|
+
'SELECT uses, max_uses FROM invitations WHERE token_hash = $hash',
|
|
457
|
+
)
|
|
456
458
|
.get({ $hash: tokenHash }) as { uses: number; max_uses: number } | null
|
|
457
459
|
|
|
458
460
|
if (!currentInv || currentInv.uses >= currentInv.max_uses) {
|
|
@@ -4,14 +4,32 @@ import { config } from '../../config'
|
|
|
4
4
|
import { listActiveEnvironmentsResponse } from '../../services/environment'
|
|
5
5
|
import { listWebSessionsByOwnerUuid } from '../../services/session'
|
|
6
6
|
import { storeListActiveEnvironments } from '../../store'
|
|
7
|
+
import { getDb } from '../../db/sqlite'
|
|
7
8
|
|
|
8
9
|
const app = new Hono()
|
|
9
10
|
|
|
10
|
-
/** GET /web/environments — List active environments
|
|
11
|
+
/** GET /web/environments — List active environments.
|
|
12
|
+
* Admin sees all. Non-admin sees only environments linked to teams they
|
|
13
|
+
* belong to, plus environments with no team association (personal space). */
|
|
11
14
|
app.get('/environments', sessionAuth, async c => {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
+
const userId = c.get('userId') as string
|
|
16
|
+
const isAdmin = c.get('isAdmin') as boolean
|
|
17
|
+
|
|
18
|
+
const allEnvs = listActiveEnvironmentsResponse()
|
|
19
|
+
|
|
20
|
+
if (isAdmin) {
|
|
21
|
+
return c.json(allEnvs, 200)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Resolve the user's team memberships so we can filter environments.
|
|
25
|
+
const db = getDb()
|
|
26
|
+
const teamRows = db
|
|
27
|
+
.query('SELECT team_id FROM team_members WHERE user_id = $userId')
|
|
28
|
+
.all({ $userId: userId }) as Array<{ team_id: string }>
|
|
29
|
+
const teamIds = new Set(teamRows.map(r => r.team_id))
|
|
30
|
+
|
|
31
|
+
const filtered = allEnvs.filter(env => !env.team_id || teamIds.has(env.team_id))
|
|
32
|
+
return c.json(filtered, 200)
|
|
15
33
|
})
|
|
16
34
|
|
|
17
35
|
/** GET /web/server-info — Aggregate server status for Web UI identity panel */
|
|
@@ -32,18 +32,22 @@ const app = new Hono()
|
|
|
32
32
|
* X-User-Id header for a real user identity. If no valid identity is
|
|
33
33
|
* found, returns null — the session will be orphaned (admin-visible).
|
|
34
34
|
*/
|
|
35
|
-
function resolveBindUserId(
|
|
36
|
-
c
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
function resolveBindUserId(c: import('hono').Context): string | null {
|
|
36
|
+
const userId = c.get('userId')
|
|
37
|
+
if (!userId || userId === SYSTEM_USER_ID) {
|
|
38
|
+
// API key auth — only admin can use X-User-Id to bind on behalf of a user
|
|
39
|
+
const isAdmin = c.get('isAdmin')
|
|
40
|
+
if (!isAdmin) {
|
|
41
|
+
return null // non-admin API key cannot create sessions as another user
|
|
42
|
+
}
|
|
43
|
+
const headerUserId = c.req.header('X-User-Id')
|
|
44
|
+
if (headerUserId && headerUserId !== SYSTEM_USER_ID) {
|
|
45
|
+
console.log(`[audit] API key created session as user ${headerUserId}`)
|
|
46
|
+
return headerUserId
|
|
47
|
+
}
|
|
48
|
+
return null // API key without X-User-Id → orphan session (admin-visible)
|
|
45
49
|
}
|
|
46
|
-
return
|
|
50
|
+
return userId
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/** POST /web/sessions — Create a session from web UI */
|
|
@@ -81,14 +85,22 @@ app.post('/sessions', sessionAuth, async c => {
|
|
|
81
85
|
}
|
|
82
86
|
}
|
|
83
87
|
|
|
88
|
+
const teamId = typeof body.team_id === 'string' ? body.team_id : null
|
|
89
|
+
|
|
84
90
|
const session = createSession({
|
|
85
91
|
environment_id: body.environment_id || null,
|
|
86
92
|
title: body.title || 'New Session',
|
|
87
93
|
source: 'web',
|
|
88
94
|
permission_mode: body.permission_mode || 'default',
|
|
95
|
+
visibility: teamId ? 'team' : 'private',
|
|
89
96
|
})
|
|
90
97
|
|
|
91
|
-
//
|
|
98
|
+
// Bind team owner first (if creating in team context), then user owner.
|
|
99
|
+
// Team ownership makes the session visible to all team members; user
|
|
100
|
+
// ownership gives the creator explicit access.
|
|
101
|
+
if (teamId) {
|
|
102
|
+
storeBindSession(session.id, teamId, 'team')
|
|
103
|
+
}
|
|
92
104
|
if (bindUserId) {
|
|
93
105
|
storeBindSession(session.id, bindUserId)
|
|
94
106
|
}
|
|
@@ -14,7 +14,11 @@ interface SessionOwnerRow {
|
|
|
14
14
|
/**
|
|
15
15
|
* Check if a user is a member of a given team.
|
|
16
16
|
*/
|
|
17
|
-
function isTeamMember(
|
|
17
|
+
function isTeamMember(
|
|
18
|
+
userId: string,
|
|
19
|
+
teamId: string,
|
|
20
|
+
db: ReturnType<typeof getDb>,
|
|
21
|
+
): boolean {
|
|
18
22
|
const row = db
|
|
19
23
|
.query(
|
|
20
24
|
'SELECT user_id FROM team_members WHERE team_id = $teamId AND user_id = $userId',
|
|
@@ -379,7 +383,12 @@ app.get(
|
|
|
379
383
|
|
|
380
384
|
if (!isGrantee) {
|
|
381
385
|
return c.json(
|
|
382
|
-
{
|
|
386
|
+
{
|
|
387
|
+
error: {
|
|
388
|
+
type: 'forbidden',
|
|
389
|
+
message: 'Not authorized for this share',
|
|
390
|
+
},
|
|
391
|
+
},
|
|
383
392
|
403,
|
|
384
393
|
)
|
|
385
394
|
}
|
|
@@ -154,10 +154,7 @@ app.post('/teams', withAuth, async c => {
|
|
|
154
154
|
)
|
|
155
155
|
.get() as { id: string } | null
|
|
156
156
|
if (!adminUser) {
|
|
157
|
-
return c.json(
|
|
158
|
-
{ error: 'No admin user available to create team' },
|
|
159
|
-
400,
|
|
160
|
-
)
|
|
157
|
+
return c.json({ error: 'No admin user available to create team' }, 400)
|
|
161
158
|
}
|
|
162
159
|
createdBy = adminUser.id
|
|
163
160
|
}
|
|
@@ -826,11 +823,11 @@ app.post('/environments/:id/transfer', withAuth, async c => {
|
|
|
826
823
|
|
|
827
824
|
// Check caller is owner/admin of the target team
|
|
828
825
|
const targetRole = getTeamRole(toTeamId, user)
|
|
829
|
-
if (
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
)
|
|
826
|
+
if (
|
|
827
|
+
!user.isAdmin &&
|
|
828
|
+
(!targetRole || (targetRole !== 'owner' && targetRole !== 'admin'))
|
|
829
|
+
) {
|
|
830
|
+
return c.json({ error: 'Not authorized in target team' }, 403)
|
|
834
831
|
}
|
|
835
832
|
|
|
836
833
|
// Remove from source team
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
storeGetEnvironment,
|
|
6
6
|
storeUpdateEnvironment,
|
|
7
7
|
storeListActiveEnvironments,
|
|
8
|
+
storeListActiveEnvironmentsWithTeam,
|
|
8
9
|
storeListActiveEnvironmentsByUsername,
|
|
9
10
|
storeListAcpAgentsByChannelGroup,
|
|
10
11
|
storeListSessionsByEnvironment,
|
|
@@ -15,7 +16,9 @@ import type {
|
|
|
15
16
|
} from '../types/api'
|
|
16
17
|
import type { EnvironmentRecord } from '../store'
|
|
17
18
|
|
|
18
|
-
function toResponse(
|
|
19
|
+
function toResponse(
|
|
20
|
+
row: EnvironmentRecord & { teamId?: string | null; teamName?: string | null },
|
|
21
|
+
): EnvironmentResponse {
|
|
19
22
|
return {
|
|
20
23
|
id: row.id,
|
|
21
24
|
machine_name: row.machineName,
|
|
@@ -27,6 +30,8 @@ function toResponse(row: EnvironmentRecord): EnvironmentResponse {
|
|
|
27
30
|
worker_type: row.workerType,
|
|
28
31
|
channel_group_id: row.bridgeId,
|
|
29
32
|
capabilities: row.capabilities,
|
|
33
|
+
team_id: row.teamId ?? null,
|
|
34
|
+
team_name: row.teamName ?? null,
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -136,13 +141,13 @@ export function listActiveEnvironments() {
|
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
export function listActiveEnvironmentsResponse(): EnvironmentResponse[] {
|
|
139
|
-
return
|
|
144
|
+
return storeListActiveEnvironmentsWithTeam().map(r => toResponse(r))
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
export function listActiveEnvironmentsByUsername(
|
|
143
148
|
username: string,
|
|
144
149
|
): EnvironmentResponse[] {
|
|
145
|
-
return storeListActiveEnvironmentsByUsername(username).map(toResponse)
|
|
150
|
+
return storeListActiveEnvironmentsByUsername(username).map(r => toResponse(r))
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
export function reconnectEnvironment(envId: string) {
|