@juspay/shooter 1.0.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/.claude/hooks/notifier.cjs +1431 -0
- package/.claude/settings.json +162 -0
- package/README.md +515 -0
- package/bin/shooter.cjs +141 -0
- package/build/client/_app/immutable/assets/0.CM9Hl6d-.css +1 -0
- package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.br +0 -0
- package/build/client/_app/immutable/assets/0.CM9Hl6d-.css.gz +0 -0
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css +1 -0
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.br +1 -0
- package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.gz +0 -0
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css +1 -0
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css.br +0 -0
- package/build/client/_app/immutable/assets/3.C0uFg0IS.css.gz +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css +1 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.br +0 -0
- package/build/client/_app/immutable/assets/4.cJuCkJKZ.css.gz +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css +1 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.br +0 -0
- package/build/client/_app/immutable/assets/5.DRjApZQW.css.gz +0 -0
- package/build/client/_app/immutable/assets/6.AraZrY8I.css +1 -0
- package/build/client/_app/immutable/assets/6.AraZrY8I.css.br +0 -0
- package/build/client/_app/immutable/assets/6.AraZrY8I.css.gz +0 -0
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css +1 -0
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.br +0 -0
- package/build/client/_app/immutable/assets/7.BCJ1IuMx.css.gz +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css +1 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.br +0 -0
- package/build/client/_app/immutable/assets/ChatView.CsdBAOKx.css.gz +0 -0
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css +1 -0
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.br +0 -0
- package/build/client/_app/immutable/assets/markdown.B0b5w2tq.css.gz +0 -0
- package/build/client/_app/immutable/assets/xterm.DFuMZ0ql.css +1 -0
- package/build/client/_app/immutable/assets/xterm.DFuMZ0ql.css.br +0 -0
- package/build/client/_app/immutable/assets/xterm.DFuMZ0ql.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BNJphC1q.js +56 -0
- package/build/client/_app/immutable/chunks/BNJphC1q.js.br +0 -0
- package/build/client/_app/immutable/chunks/BNJphC1q.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BTGVxaYV.js +9 -0
- package/build/client/_app/immutable/chunks/BTGVxaYV.js.br +0 -0
- package/build/client/_app/immutable/chunks/BTGVxaYV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BlxrFPDK.js +1 -0
- package/build/client/_app/immutable/chunks/BlxrFPDK.js.br +0 -0
- package/build/client/_app/immutable/chunks/BlxrFPDK.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js +1 -0
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bvk7mfPM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CAokzuPQ.js +1 -0
- package/build/client/_app/immutable/chunks/CAokzuPQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CAokzuPQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js +1 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGLrx-H5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CgCpWzEA.js +1 -0
- package/build/client/_app/immutable/chunks/CgCpWzEA.js.br +0 -0
- package/build/client/_app/immutable/chunks/CgCpWzEA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js +6 -0
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cjwk_cGO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CtQ8EED1.js +11 -0
- package/build/client/_app/immutable/chunks/CtQ8EED1.js.br +0 -0
- package/build/client/_app/immutable/chunks/CtQ8EED1.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DERQCisl.js +1 -0
- package/build/client/_app/immutable/chunks/DERQCisl.js.br +0 -0
- package/build/client/_app/immutable/chunks/DERQCisl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DKrg8TQs.js +1 -0
- package/build/client/_app/immutable/chunks/DKrg8TQs.js.br +0 -0
- package/build/client/_app/immutable/chunks/DKrg8TQs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DLu6yJIZ.js +1 -0
- package/build/client/_app/immutable/chunks/DLu6yJIZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/DLu6yJIZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js +126 -0
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dkkpz_4D.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DoczjQhA.js +1 -0
- package/build/client/_app/immutable/chunks/DoczjQhA.js.br +0 -0
- package/build/client/_app/immutable/chunks/DoczjQhA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js +1 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js.br +0 -0
- package/build/client/_app/immutable/chunks/PPVm8Dsz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/RpcNruLP.js +2 -0
- package/build/client/_app/immutable/chunks/RpcNruLP.js.br +0 -0
- package/build/client/_app/immutable/chunks/RpcNruLP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/a-St0Zwo.js +1 -0
- package/build/client/_app/immutable/chunks/a-St0Zwo.js.br +0 -0
- package/build/client/_app/immutable/chunks/a-St0Zwo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/bo70OQUZ.js +1 -0
- package/build/client/_app/immutable/chunks/bo70OQUZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/bo70OQUZ.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js +2 -0
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js.br +0 -0
- package/build/client/_app/immutable/entry/app.QvGgdvTI.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BntDNRMC.js +1 -0
- package/build/client/_app/immutable/entry/start.BntDNRMC.js.br +0 -0
- package/build/client/_app/immutable/entry/start.BntDNRMC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js +1 -0
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CzkdvJ7j.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js +1 -0
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.MG1QhfrI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js +1 -0
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.B4MlOSh6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js +3 -0
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.DIwYkjDn.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js +1 -0
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.D-cIe70D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js +1 -0
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.D7zPRe3L.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js +2 -0
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BB7QE48r.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js +2 -0
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.D8mqsrZG.js.gz +0 -0
- package/build/client/_app/version.json +1 -0
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/client/app-icon.png +0 -0
- package/build/client/apple-touch-icon.png +0 -0
- package/build/client/favicon.png +0 -0
- package/build/client/favicon.svg +10 -0
- package/build/client/favicon.svg.br +0 -0
- package/build/client/favicon.svg.gz +0 -0
- package/build/client/manifest.webmanifest +1 -0
- package/build/client/pwa-192x192.png +0 -0
- package/build/client/pwa-512x512.png +0 -0
- package/build/client/registerSW.js +1 -0
- package/build/client/registerSW.js.br +0 -0
- package/build/client/registerSW.js.gz +0 -0
- package/build/client/sw.js +222 -0
- package/build/client/sw.js.br +0 -0
- package/build/client/sw.js.gz +0 -0
- package/build/client/workbox-5119daf5.js +3395 -0
- package/build/client/workbox-5119daf5.js.br +0 -0
- package/build/client/workbox-5119daf5.js.gz +0 -0
- package/build/env.js +94 -0
- package/build/handler.js +1494 -0
- package/build/index.js +345 -0
- package/build/pty-holder.cjs +510 -0
- package/build/server/chunks/0-q2IUp76Y.js +9 -0
- package/build/server/chunks/0-q2IUp76Y.js.map +1 -0
- package/build/server/chunks/1-CU50G5wZ.js +9 -0
- package/build/server/chunks/1-CU50G5wZ.js.map +1 -0
- package/build/server/chunks/2-D01t9s8T.js +9 -0
- package/build/server/chunks/2-D01t9s8T.js.map +1 -0
- package/build/server/chunks/3-5PUQ04wC.js +9 -0
- package/build/server/chunks/3-5PUQ04wC.js.map +1 -0
- package/build/server/chunks/4-e7gywnSG.js +9 -0
- package/build/server/chunks/4-e7gywnSG.js.map +1 -0
- package/build/server/chunks/5-CA1SA6KZ.js +9 -0
- package/build/server/chunks/5-CA1SA6KZ.js.map +1 -0
- package/build/server/chunks/6-71H221sV.js +9 -0
- package/build/server/chunks/6-71H221sV.js.map +1 -0
- package/build/server/chunks/7-Bo-vmdyz.js +9 -0
- package/build/server/chunks/7-Bo-vmdyz.js.map +1 -0
- package/build/server/chunks/_layout.svelte-SFHOxs74.js +132 -0
- package/build/server/chunks/_layout.svelte-SFHOxs74.js.map +1 -0
- package/build/server/chunks/_page.svelte-B4w-2wD-.js +120 -0
- package/build/server/chunks/_page.svelte-B4w-2wD-.js.map +1 -0
- package/build/server/chunks/_page.svelte-B_qAXjkh.js +213 -0
- package/build/server/chunks/_page.svelte-B_qAXjkh.js.map +1 -0
- package/build/server/chunks/_page.svelte-CsF1_TRG.js +50 -0
- package/build/server/chunks/_page.svelte-CsF1_TRG.js.map +1 -0
- package/build/server/chunks/_page.svelte-DJC6U-P0.js +68 -0
- package/build/server/chunks/_page.svelte-DJC6U-P0.js.map +1 -0
- package/build/server/chunks/_page.svelte-DQ6HBtsz.js +407 -0
- package/build/server/chunks/_page.svelte-DQ6HBtsz.js.map +1 -0
- package/build/server/chunks/_page.svelte-LbhhjP21.js +148 -0
- package/build/server/chunks/_page.svelte-LbhhjP21.js.map +1 -0
- package/build/server/chunks/_server.ts-BL2FGb5Z.js +387 -0
- package/build/server/chunks/_server.ts-BL2FGb5Z.js.map +1 -0
- package/build/server/chunks/_server.ts-BgdjBZco.js +47 -0
- package/build/server/chunks/_server.ts-BgdjBZco.js.map +1 -0
- package/build/server/chunks/_server.ts-BihKSdj_.js +59 -0
- package/build/server/chunks/_server.ts-BihKSdj_.js.map +1 -0
- package/build/server/chunks/_server.ts-BjOJsoy4.js +63 -0
- package/build/server/chunks/_server.ts-BjOJsoy4.js.map +1 -0
- package/build/server/chunks/_server.ts-C29xzfaw.js +77 -0
- package/build/server/chunks/_server.ts-C29xzfaw.js.map +1 -0
- package/build/server/chunks/_server.ts-CPa6DgIt.js +71 -0
- package/build/server/chunks/_server.ts-CPa6DgIt.js.map +1 -0
- package/build/server/chunks/_server.ts-CbDRDIoP.js +36 -0
- package/build/server/chunks/_server.ts-CbDRDIoP.js.map +1 -0
- package/build/server/chunks/_server.ts-Cl1OEWL4.js +54 -0
- package/build/server/chunks/_server.ts-Cl1OEWL4.js.map +1 -0
- package/build/server/chunks/_server.ts-ColfDHW8.js +60 -0
- package/build/server/chunks/_server.ts-ColfDHW8.js.map +1 -0
- package/build/server/chunks/_server.ts-Cv_OrRuL.js +494 -0
- package/build/server/chunks/_server.ts-Cv_OrRuL.js.map +1 -0
- package/build/server/chunks/_server.ts-D4MNi4cD.js +25 -0
- package/build/server/chunks/_server.ts-D4MNi4cD.js.map +1 -0
- package/build/server/chunks/_server.ts-DRVbgm6k.js +125 -0
- package/build/server/chunks/_server.ts-DRVbgm6k.js.map +1 -0
- package/build/server/chunks/_server.ts-DfajWaqh.js +39 -0
- package/build/server/chunks/_server.ts-DfajWaqh.js.map +1 -0
- package/build/server/chunks/_server.ts-y9-WYDMa.js +35 -0
- package/build/server/chunks/_server.ts-y9-WYDMa.js.map +1 -0
- package/build/server/chunks/auth-CEgFis71.js +32 -0
- package/build/server/chunks/auth-CEgFis71.js.map +1 -0
- package/build/server/chunks/client-CxCatAKr.js +255 -0
- package/build/server/chunks/client-CxCatAKr.js.map +1 -0
- package/build/server/chunks/error.svelte-BqdwMWdK.js +26 -0
- package/build/server/chunks/error.svelte-BqdwMWdK.js.map +1 -0
- package/build/server/chunks/exports-CJ0Q5XmL.js +4081 -0
- package/build/server/chunks/exports-CJ0Q5XmL.js.map +1 -0
- package/build/server/chunks/index2-DAxIoAO-.js +36 -0
- package/build/server/chunks/index2-DAxIoAO-.js.map +1 -0
- package/build/server/chunks/jsonl-parser-dmZU_Hyu.js +137 -0
- package/build/server/chunks/jsonl-parser-dmZU_Hyu.js.map +1 -0
- package/build/server/chunks/library-apns-BHxLmuIx.js +104 -0
- package/build/server/chunks/library-apns-BHxLmuIx.js.map +1 -0
- package/build/server/chunks/markdown-Bxrl3cCF.js +1241 -0
- package/build/server/chunks/markdown-Bxrl3cCF.js.map +1 -0
- package/build/server/chunks/pending-requests-D8UiTw7L.js +44 -0
- package/build/server/chunks/pending-requests-D8UiTw7L.js.map +1 -0
- package/build/server/chunks/pty-manager-C0FhBiVq.js +1697 -0
- package/build/server/chunks/pty-manager-C0FhBiVq.js.map +1 -0
- package/build/server/chunks/shared-server-BDY8jh20.js +200 -0
- package/build/server/chunks/shared-server-BDY8jh20.js.map +1 -0
- package/build/server/chunks/stores-D0HorpgL.js +36 -0
- package/build/server/chunks/stores-D0HorpgL.js.map +1 -0
- package/build/server/index.js +6466 -0
- package/build/server/index.js.map +1 -0
- package/build/server/manifest.js +184 -0
- package/build/server/manifest.js.map +1 -0
- package/build/shims.js +32 -0
- package/package.json +94 -0
- package/scripts/clipboard-shims/wl-paste +48 -0
- package/scripts/clipboard-shims/xclip +31 -0
- package/scripts/install.sh +477 -0
- package/scripts/setup-node-pty.sh +63 -0
- package/scripts/setup.cjs +571 -0
- package/scripts/test-runner.ts +243 -0
- package/scripts/vercel-env-commands.sh +60 -0
- package/server.ts +139 -0
- package/src/app.css +1835 -0
- package/src/app.d.ts +31 -0
- package/src/app.html +24 -0
- package/src/generated/types/APN.ts +305 -0
- package/src/generated/types/CLI.ts +52 -0
- package/src/generated/types/JWT.ts +92 -0
- package/src/generated/types/Terminal.ts +2736 -0
- package/src/generated/types/index.ts +6 -0
- package/src/lib/assets/icons/alert-triangle.svg +5 -0
- package/src/lib/assets/icons/bell.svg +4 -0
- package/src/lib/assets/icons/check-circle.svg +4 -0
- package/src/lib/assets/icons/file.svg +4 -0
- package/src/lib/assets/icons/folder.svg +3 -0
- package/src/lib/assets/icons/play.svg +3 -0
- package/src/lib/assets/icons/refresh.svg +4 -0
- package/src/lib/assets/icons/settings.svg +4 -0
- package/src/lib/assets/icons/terminal.svg +1 -0
- package/src/lib/assets/icons/tool.svg +3 -0
- package/src/lib/assets/icons/x-circle.svg +5 -0
- package/src/lib/modules/client/common/Card.svelte +26 -0
- package/src/lib/modules/client/common/EmptyState.svelte +36 -0
- package/src/lib/modules/client/common/Icon.svelte +61 -0
- package/src/lib/modules/client/common/StatusBadge.svelte +38 -0
- package/src/lib/modules/client/common/cache.ts +31 -0
- package/src/lib/modules/client/common/config-guard.ts +18 -0
- package/src/lib/modules/client/common/index.ts +12 -0
- package/src/lib/modules/client/common/markdown.ts +23 -0
- package/src/lib/modules/client/common/native-bridge.ts +50 -0
- package/src/lib/modules/client/common/time.ts +22 -0
- package/src/lib/modules/client/common/tool-title.ts +28 -0
- package/src/lib/modules/client/terminal/ChatView.svelte +400 -0
- package/src/lib/modules/client/terminal/CommandPalette.svelte +60 -0
- package/src/lib/modules/client/terminal/ConnectionStatus.svelte +99 -0
- package/src/lib/modules/client/terminal/LaunchSheet.svelte +294 -0
- package/src/lib/modules/client/terminal/QuickKeys.svelte +71 -0
- package/src/lib/modules/client/terminal/ShortcutsHelp.svelte +79 -0
- package/src/lib/modules/client/terminal/keyboard-shortcuts.ts +70 -0
- package/src/lib/modules/client/terminal/xterm-wrapper.ts +243 -0
- package/src/lib/modules/server/apn/library-apns.ts +137 -0
- package/src/lib/modules/server/apn/notification-history.ts +35 -0
- package/src/lib/modules/server/apn/notification-sessions.ts +117 -0
- package/src/lib/modules/server/apn/pending-requests.ts +65 -0
- package/src/lib/modules/server/apn/types.ts +51 -0
- package/src/lib/modules/server/auth.ts +34 -0
- package/src/lib/modules/server/cli/index.ts +79 -0
- package/src/lib/modules/server/cli/runner.ts +162 -0
- package/src/lib/modules/server/fcm/fcm-service.ts +72 -0
- package/src/lib/modules/server/sessions/jsonl-parser.ts +197 -0
- package/src/lib/modules/server/sessions/jsonl-reader.ts +301 -0
- package/src/lib/modules/server/sessions/opencode-reader.ts +264 -0
- package/src/lib/modules/server/sessions/types.ts +53 -0
- package/src/lib/modules/server/terminal/holder-client.ts +273 -0
- package/src/lib/modules/server/terminal/opencode-watcher.ts +661 -0
- package/src/lib/modules/server/terminal/pty-holder.cjs +510 -0
- package/src/lib/modules/server/terminal/pty-manager.ts +1012 -0
- package/src/lib/modules/server/terminal/session-watcher.ts +320 -0
- package/src/lib/modules/server/terminal/terminal-store.ts +198 -0
- package/src/lib/modules/server/ws/events-handler.ts +73 -0
- package/src/lib/modules/server/ws/keepalive.ts +108 -0
- package/src/lib/modules/server/ws/server.ts +93 -0
- package/src/lib/modules/server/ws/session-handler.ts +462 -0
- package/src/lib/modules/server/ws/terminal-handler.ts +197 -0
- package/src/lib/modules/server/ws/ticket-store.ts +58 -0
- package/src/lib/theme.css +529 -0
- package/src/lib/types/config.ts +6 -0
- package/src/routes/+layout.svelte +218 -0
- package/src/routes/+page.svelte +261 -0
- package/src/routes/api/debug/+server.ts +33 -0
- package/src/routes/api/device-token/+server.ts +85 -0
- package/src/routes/api/health/+server.ts +100 -0
- package/src/routes/api/notify/+server.ts +418 -0
- package/src/routes/api/qr-config/+server.ts +45 -0
- package/src/routes/api/response/+server.ts +73 -0
- package/src/routes/api/sessions/+server.ts +120 -0
- package/src/routes/api/terminals/+server.ts +141 -0
- package/src/routes/api/terminals/[id]/+server.ts +75 -0
- package/src/routes/api/terminals/[id]/paste-image/+server.ts +61 -0
- package/src/routes/api/terminals/[id]/resize/+server.ts +60 -0
- package/src/routes/api/webhook/+server.ts +42 -0
- package/src/routes/api/ws-status/+server.ts +23 -0
- package/src/routes/api/ws-ticket/+server.ts +86 -0
- package/src/routes/config/+page.svelte +600 -0
- package/src/routes/project/+page.svelte +274 -0
- package/src/routes/session/[id]/+page.svelte +434 -0
- package/src/routes/terminals/+page.svelte +618 -0
- package/src/routes/terminals/[id]/+page.svelte +968 -0
- package/svelte.config.js +18 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
2
|
+
import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
|
|
3
|
+
import { json } from '@sveltejs/kit';
|
|
4
|
+
import { realpathSync, statSync } from 'fs';
|
|
5
|
+
import { basename } from 'path';
|
|
6
|
+
|
|
7
|
+
import type { RequestHandler } from './$types';
|
|
8
|
+
|
|
9
|
+
const ALLOWED_COMMANDS = ['zsh', 'bash', 'sh', 'fish', 'claude', 'opencode'];
|
|
10
|
+
|
|
11
|
+
// GET /api/terminals — List all terminals (active + recently exited)
|
|
12
|
+
export const GET: RequestHandler = ({ request }) => {
|
|
13
|
+
const authError = validateAuth(request);
|
|
14
|
+
if (authError) {return authError;}
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const terminals = ptyManager.list().map(t => ({
|
|
18
|
+
args: t.args,
|
|
19
|
+
clientCount: t.clients.size,
|
|
20
|
+
command: t.command,
|
|
21
|
+
createdAt: t.createdAt.toISOString(),
|
|
22
|
+
currentCwd: t.currentCwd,
|
|
23
|
+
cwd: t.cwd,
|
|
24
|
+
exitCode: t.exitCode,
|
|
25
|
+
exitedAt: t.exitedAt?.toISOString() ?? null,
|
|
26
|
+
id: t.id,
|
|
27
|
+
isActive: t.isActive,
|
|
28
|
+
pid: t.pid,
|
|
29
|
+
status: t.status,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
return json({
|
|
33
|
+
count: terminals.length,
|
|
34
|
+
terminals,
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const err = error as Error;
|
|
39
|
+
return json({ details: err.message, error: 'Failed to list terminals' }, { status: 500 });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// POST /api/terminals — Create a new terminal session
|
|
44
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
45
|
+
const authError = validateAuth(request);
|
|
46
|
+
if (authError) {return authError;}
|
|
47
|
+
|
|
48
|
+
let body: { args?: string[]; cols?: number; command?: string; cwd?: string; rows?: number };
|
|
49
|
+
try {
|
|
50
|
+
body = (await request.json()) as {
|
|
51
|
+
args?: string[];
|
|
52
|
+
cols?: number;
|
|
53
|
+
command?: string;
|
|
54
|
+
cwd?: string;
|
|
55
|
+
rows?: number;
|
|
56
|
+
};
|
|
57
|
+
} catch {
|
|
58
|
+
return json({ error: 'Invalid JSON in request body' }, { status: 400 });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const { args, cols, command, cwd, rows } = body;
|
|
63
|
+
|
|
64
|
+
if (!command) {
|
|
65
|
+
return json({ error: 'command is required' }, { status: 400 });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Issue 4: Command allowlist — only allow known safe commands
|
|
69
|
+
const commandBasename = basename(command);
|
|
70
|
+
if (!ALLOWED_COMMANDS.includes(commandBasename)) {
|
|
71
|
+
return json(
|
|
72
|
+
{ error: `Command not allowed. Allowed: ${ALLOWED_COMMANDS.join(', ')}` },
|
|
73
|
+
{ status: 400 },
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!cwd) {
|
|
78
|
+
return json({ error: 'cwd is required' }, { status: 400 });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Issue 3: Validate cwd — resolve symlinks and restrict to home directory
|
|
82
|
+
let realCwd: string;
|
|
83
|
+
try {
|
|
84
|
+
realCwd = realpathSync(cwd);
|
|
85
|
+
} catch {
|
|
86
|
+
return json({ error: 'Invalid working directory' }, { status: 400 });
|
|
87
|
+
}
|
|
88
|
+
if (!statSync(realCwd).isDirectory()) {
|
|
89
|
+
return json({ error: 'Invalid working directory' }, { status: 400 });
|
|
90
|
+
}
|
|
91
|
+
const home = process.env.HOME || '';
|
|
92
|
+
if (home && !realCwd.startsWith(home)) {
|
|
93
|
+
return json({ error: 'Working directory must be under home directory' }, { status: 400 });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (args !== undefined && !Array.isArray(args)) {
|
|
97
|
+
return json({ error: 'args must be an array of strings' }, { status: 400 });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Issue 1: Validate every element in args is a string
|
|
101
|
+
if (args && !args.every((a: unknown) => typeof a === 'string')) {
|
|
102
|
+
return json({ error: 'All args must be strings' }, { status: 400 });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (cols !== undefined && (typeof cols !== 'number' || cols < 1)) {
|
|
106
|
+
return json({ error: 'cols must be a positive number' }, { status: 400 });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (rows !== undefined && (typeof rows !== 'number' || rows < 1)) {
|
|
110
|
+
return json({ error: 'rows must be a positive number' }, { status: 400 });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const terminal = await ptyManager.create(
|
|
114
|
+
command,
|
|
115
|
+
args ?? [],
|
|
116
|
+
realCwd,
|
|
117
|
+
cols ?? 80,
|
|
118
|
+
rows ?? 24,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
console.log(
|
|
122
|
+
`[terminals] Created terminal ${terminal.id} (pid=${terminal.pid}, command=${command})`
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return json(
|
|
126
|
+
{
|
|
127
|
+
command: terminal.command,
|
|
128
|
+
createdAt: terminal.createdAt,
|
|
129
|
+
cwd: terminal.cwd,
|
|
130
|
+
id: terminal.id,
|
|
131
|
+
pid: terminal.pid,
|
|
132
|
+
sessionWs: `/ws/session/${terminal.id}`,
|
|
133
|
+
ws: `/ws/terminal/${terminal.id}`,
|
|
134
|
+
},
|
|
135
|
+
{ status: 201 }
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const err = error as Error;
|
|
139
|
+
return json({ details: err.message, error: 'Failed to create terminal' }, { status: 500 });
|
|
140
|
+
}
|
|
141
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
2
|
+
import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
|
|
3
|
+
import { json } from '@sveltejs/kit';
|
|
4
|
+
|
|
5
|
+
import type { RequestHandler } from './$types';
|
|
6
|
+
|
|
7
|
+
// GET /api/terminals/:id — Get terminal details by ID
|
|
8
|
+
export const GET: RequestHandler = ({ params, request }) => {
|
|
9
|
+
const authError = validateAuth(request);
|
|
10
|
+
if (authError) {return authError;}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const terminal = ptyManager.get(params.id);
|
|
14
|
+
|
|
15
|
+
if (!terminal) {
|
|
16
|
+
return json({ error: 'Terminal not found' }, { status: 404 });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return json({
|
|
20
|
+
args: terminal.args,
|
|
21
|
+
clientCount: terminal.clients.size,
|
|
22
|
+
command: terminal.command,
|
|
23
|
+
createdAt: terminal.createdAt.toISOString(),
|
|
24
|
+
cwd: terminal.cwd,
|
|
25
|
+
exitCode: terminal.exitCode,
|
|
26
|
+
exitedAt: terminal.exitedAt?.toISOString() ?? null,
|
|
27
|
+
id: terminal.id,
|
|
28
|
+
lastOutput: terminal.scrollback.length > 0 ? terminal.scrollback[terminal.scrollback.length - 1] : null,
|
|
29
|
+
pid: terminal.pid,
|
|
30
|
+
sessionWs: `/ws/session/${terminal.id}`,
|
|
31
|
+
status: terminal.status,
|
|
32
|
+
timestamp: new Date().toISOString(),
|
|
33
|
+
ws: `/ws/terminal/${terminal.id}`,
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
const err = error as Error;
|
|
37
|
+
return json({ details: err.message, error: 'Failed to get terminal' }, { status: 500 });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// DELETE /api/terminals/:id — Kill terminal by ID (SIGTERM)
|
|
42
|
+
export const DELETE: RequestHandler = ({ params, request }) => {
|
|
43
|
+
const authError = validateAuth(request);
|
|
44
|
+
if (authError) {return authError;}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const terminal = ptyManager.get(params.id);
|
|
48
|
+
|
|
49
|
+
if (!terminal) {
|
|
50
|
+
return json({ error: 'Terminal not found' }, { status: 404 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (terminal.status === 'exited') {
|
|
54
|
+
ptyManager.remove(params.id);
|
|
55
|
+
console.log(`[terminals] Removed exited terminal ${params.id}`);
|
|
56
|
+
return json({
|
|
57
|
+
removed: true,
|
|
58
|
+
success: true,
|
|
59
|
+
timestamp: new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ptyManager.kill(params.id);
|
|
64
|
+
|
|
65
|
+
console.log(`[terminals] Killed terminal ${params.id} (pid=${terminal.pid})`);
|
|
66
|
+
|
|
67
|
+
return json({
|
|
68
|
+
success: true,
|
|
69
|
+
timestamp: new Date().toISOString(),
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const err = error as Error;
|
|
73
|
+
return json({ details: err.message, error: 'Failed to kill terminal' }, { status: 500 });
|
|
74
|
+
}
|
|
75
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
2
|
+
import { json } from '@sveltejs/kit';
|
|
3
|
+
import { mkdirSync, writeFileSync } from 'fs';
|
|
4
|
+
|
|
5
|
+
import type { RequestHandler } from './$types';
|
|
6
|
+
|
|
7
|
+
// POST /api/terminals/[id]/paste-image — Write clipboard image for a terminal
|
|
8
|
+
export const POST: RequestHandler = async ({ params, request }) => {
|
|
9
|
+
const authError = validateAuth(request);
|
|
10
|
+
if (authError) {
|
|
11
|
+
return authError;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const terminalId = params.id;
|
|
15
|
+
|
|
16
|
+
// Validate terminalId to prevent path traversal
|
|
17
|
+
if (!/^[A-Za-z0-9_-]+$/.test(terminalId)) {
|
|
18
|
+
return json({ error: 'Invalid terminal ID' }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let body: { image: string };
|
|
22
|
+
try {
|
|
23
|
+
body = (await request.json()) as { image: string };
|
|
24
|
+
} catch {
|
|
25
|
+
return json({ error: 'Invalid JSON' }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!body.image || typeof body.image !== 'string') {
|
|
29
|
+
return json({ error: 'image (base64) is required' }, { status: 400 });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Decode base64 image
|
|
33
|
+
let imageBuffer: Buffer;
|
|
34
|
+
try {
|
|
35
|
+
// Strip data URI prefix if present
|
|
36
|
+
const base64Data = body.image.replace(/^data:image\/\w+;base64,/, '');
|
|
37
|
+
|
|
38
|
+
// Validate base64 encoding via round-trip check
|
|
39
|
+
// Buffer.from silently ignores invalid chars, so verify re-encoding matches
|
|
40
|
+
const decoded = Buffer.from(base64Data, 'base64');
|
|
41
|
+
if (decoded.length === 0 || decoded.toString('base64') !== base64Data) {
|
|
42
|
+
return json({ error: 'Invalid base64 image data' }, { status: 400 });
|
|
43
|
+
}
|
|
44
|
+
imageBuffer = decoded;
|
|
45
|
+
} catch {
|
|
46
|
+
return json({ error: 'Invalid base64 image data' }, { status: 400 });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Write to per-terminal clipboard directory
|
|
50
|
+
const clipboardDir = `/tmp/shooter-clipboard-${terminalId}`;
|
|
51
|
+
const imagePath = `${clipboardDir}/image.png`;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
mkdirSync(clipboardDir, { recursive: true });
|
|
55
|
+
writeFileSync(imagePath, imageBuffer);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
return json({ details: (err as Error).message, error: 'Failed to write image' }, { status: 500 });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return json({ path: imagePath, size: imageBuffer.length });
|
|
61
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
2
|
+
import { ptyManager } from '$lib/modules/server/terminal/pty-manager.js';
|
|
3
|
+
import { json } from '@sveltejs/kit';
|
|
4
|
+
|
|
5
|
+
import type { RequestHandler } from './$types';
|
|
6
|
+
|
|
7
|
+
// POST /api/terminals/:id/resize — Resize terminal
|
|
8
|
+
export const POST: RequestHandler = async ({ params, request }) => {
|
|
9
|
+
const authError = validateAuth(request);
|
|
10
|
+
if (authError) {return authError;}
|
|
11
|
+
|
|
12
|
+
let body: { cols?: number; rows?: number };
|
|
13
|
+
try {
|
|
14
|
+
body = (await request.json()) as { cols?: number; rows?: number };
|
|
15
|
+
} catch {
|
|
16
|
+
return json({ error: 'Invalid JSON in request body' }, { status: 400 });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { cols, rows } = body;
|
|
21
|
+
|
|
22
|
+
if (cols === undefined || rows === undefined) {
|
|
23
|
+
return json({ error: 'cols and rows are required' }, { status: 400 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof cols !== 'number' || cols < 1) {
|
|
27
|
+
return json({ error: 'cols must be a positive number' }, { status: 400 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (typeof rows !== 'number' || rows < 1) {
|
|
31
|
+
return json({ error: 'rows must be a positive number' }, { status: 400 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (cols > 500 || rows > 200) {
|
|
35
|
+
return json({ error: 'cols must be <= 500 and rows must be <= 200' }, { status: 400 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const terminal = ptyManager.get(params.id);
|
|
39
|
+
|
|
40
|
+
if (!terminal) {
|
|
41
|
+
return json({ error: 'Terminal not found' }, { status: 404 });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (terminal.status === 'exited') {
|
|
45
|
+
return json({ error: 'Terminal already exited' }, { status: 409 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
ptyManager.resize(params.id, cols, rows);
|
|
49
|
+
|
|
50
|
+
console.log(`[terminals] Resized terminal ${params.id} to ${cols}x${rows}`);
|
|
51
|
+
|
|
52
|
+
return json({
|
|
53
|
+
success: true,
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const err = error as Error;
|
|
58
|
+
return json({ details: err.message, error: 'Failed to resize terminal' }, { status: 500 });
|
|
59
|
+
}
|
|
60
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
2
|
+
import { json } from '@sveltejs/kit';
|
|
3
|
+
|
|
4
|
+
import type { RequestHandler } from './$types';
|
|
5
|
+
|
|
6
|
+
type WebhookBody = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
9
|
+
try {
|
|
10
|
+
const authError = validateAuth(request);
|
|
11
|
+
if (authError) return authError;
|
|
12
|
+
|
|
13
|
+
const body = (await request.json()) as WebhookBody;
|
|
14
|
+
|
|
15
|
+
console.log('Webhook received:', {
|
|
16
|
+
body,
|
|
17
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// In a full implementation, this would forward to Claude Code
|
|
22
|
+
// For now, just log and acknowledge
|
|
23
|
+
|
|
24
|
+
return json({
|
|
25
|
+
data: body,
|
|
26
|
+
message: 'Webhook received successfully',
|
|
27
|
+
success: true,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
});
|
|
30
|
+
} catch (error) {
|
|
31
|
+
const err = error as Error;
|
|
32
|
+
console.error('Webhook error:', err);
|
|
33
|
+
return json(
|
|
34
|
+
{
|
|
35
|
+
details: err.message,
|
|
36
|
+
error: 'Failed to process webhook',
|
|
37
|
+
timestamp: new Date().toISOString(),
|
|
38
|
+
},
|
|
39
|
+
{ status: 500 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// GET /api/ws-status — Returns the number of connected WebSocket clients.
|
|
2
|
+
//
|
|
3
|
+
// Used by notifier.cjs to decide whether to send an APNs push notification
|
|
4
|
+
// or skip it (because a WebSocket client is already listening for events).
|
|
5
|
+
// If at least one client is connected to the /ws/events channel, the notifier
|
|
6
|
+
// skips the push notification to avoid double-prompting.
|
|
7
|
+
|
|
8
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
9
|
+
import { getConnectedClientCount } from '$lib/modules/server/ws/server';
|
|
10
|
+
import { json } from '@sveltejs/kit';
|
|
11
|
+
|
|
12
|
+
import type { RequestHandler } from './$types';
|
|
13
|
+
|
|
14
|
+
// ── Endpoint ────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export const GET: RequestHandler = ({ request }) => {
|
|
17
|
+
const authError = validateAuth(request);
|
|
18
|
+
if (authError) {return authError;}
|
|
19
|
+
|
|
20
|
+
return json({
|
|
21
|
+
connectedClients: getConnectedClientCount(),
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// POST /api/ws-ticket — Generate a short-lived WebSocket authentication ticket.
|
|
2
|
+
//
|
|
3
|
+
// The client presents its Bearer API_KEY here, and receives a single-use ticket
|
|
4
|
+
// (32-byte hex string, valid for 30 seconds). The ticket is then passed as a
|
|
5
|
+
// query parameter on the WebSocket upgrade URL, keeping the long-lived API_KEY
|
|
6
|
+
// out of URL strings (which appear in proxy logs and browser history).
|
|
7
|
+
//
|
|
8
|
+
// Rate limited to 10 requests per minute per API key.
|
|
9
|
+
|
|
10
|
+
import { env } from '$env/dynamic/private';
|
|
11
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
12
|
+
import { generateTicket } from '$lib/modules/server/ws/ticket-store';
|
|
13
|
+
import { json } from '@sveltejs/kit';
|
|
14
|
+
|
|
15
|
+
import type { RequestHandler } from './$types';
|
|
16
|
+
|
|
17
|
+
// ── Rate limiting ───────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const RATE_LIMIT_WINDOW_MS = 60_000; // 60 seconds
|
|
20
|
+
const RATE_LIMIT_MAX = 10; // max 10 requests per window
|
|
21
|
+
|
|
22
|
+
/** Maps API key -> array of request timestamps (epoch ms). */
|
|
23
|
+
const rateLimitMap = new Map<string, number[]>();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check and record a request for the given API key.
|
|
27
|
+
* Returns true if the request is within the rate limit, false if exceeded.
|
|
28
|
+
*/
|
|
29
|
+
function checkRateLimit(apiKey: string): boolean {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
const cutoff = now - RATE_LIMIT_WINDOW_MS;
|
|
32
|
+
|
|
33
|
+
let timestamps = rateLimitMap.get(apiKey);
|
|
34
|
+
if (!timestamps) {
|
|
35
|
+
timestamps = [];
|
|
36
|
+
rateLimitMap.set(apiKey, timestamps);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Prune timestamps outside the window
|
|
40
|
+
const recent = timestamps.filter((t) => t > cutoff);
|
|
41
|
+
rateLimitMap.set(apiKey, recent);
|
|
42
|
+
|
|
43
|
+
if (recent.length >= RATE_LIMIT_MAX) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
recent.push(now);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Cleanup stale rate limit entries every 5 minutes
|
|
52
|
+
setInterval(() => {
|
|
53
|
+
const cutoff = Date.now() - RATE_LIMIT_WINDOW_MS;
|
|
54
|
+
for (const [key, timestamps] of rateLimitMap) {
|
|
55
|
+
const recent = timestamps.filter((t) => t > cutoff);
|
|
56
|
+
if (recent.length === 0) {
|
|
57
|
+
rateLimitMap.delete(key);
|
|
58
|
+
} else {
|
|
59
|
+
rateLimitMap.set(key, recent);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}, 300_000).unref();
|
|
63
|
+
|
|
64
|
+
// ── Endpoint ────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
67
|
+
const authError = validateAuth(request);
|
|
68
|
+
if (authError) {return authError;}
|
|
69
|
+
|
|
70
|
+
// Extract the API key for rate limiting
|
|
71
|
+
const apiKey = request.headers.get('authorization')!.substring(7);
|
|
72
|
+
|
|
73
|
+
if (!checkRateLimit(apiKey)) {
|
|
74
|
+
return json(
|
|
75
|
+
{ error: 'Rate limit exceeded. Maximum 10 ticket requests per minute.' },
|
|
76
|
+
{ status: 429 }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const ticket = generateTicket();
|
|
81
|
+
|
|
82
|
+
return json({
|
|
83
|
+
expiresIn: 30,
|
|
84
|
+
ticket,
|
|
85
|
+
});
|
|
86
|
+
};
|