From c5632f6a37f502f26b5f0850aad5c7431b237ec3 Mon Sep 17 00:00:00 2001 From: tdv Date: Sun, 31 Aug 2025 22:42:08 +0300 Subject: [PATCH] first commit, i i have no idea what i have done --- .env | 16 + ...d6ab3d63-4ffd-4964-a3b7-ccd5e1d776eb.vsidx | Bin 0 -> 529042 bytes .vs/NewSmoop/v17/.wsuo | Bin 0 -> 15360 bytes .vs/NewSmoop/v17/DocumentLayout.json | 41 + .vs/ProjectSettings.json | 3 + .vs/VSWorkspaceState.json | 11 + .vs/slnx.sqlite | Bin 0 -> 106496 bytes certs/gen-wildcard-nipio.sh | 153 + docker-compose.yaml | 108 + management-ui/.dockerignore | 7 + management-ui/.gitignore | 24 + management-ui/.vscode/extensions.json | 3 + management-ui/Dockerfile | 16 + management-ui/README.md | 5 + management-ui/components.json | 20 + management-ui/index.html | 13 + management-ui/package-lock.json | 2771 +++++++++++++++++ management-ui/package.json | 37 + management-ui/public/vite.svg | 1 + management-ui/src/App.vue | 7 + management-ui/src/assets/vue.svg | 1 + management-ui/src/components/HelloWorld.vue | 41 + .../ui/alert-dialog/AlertDialog.vue | 14 + .../ui/alert-dialog/AlertDialogAction.vue | 17 + .../ui/alert-dialog/AlertDialogCancel.vue | 24 + .../ui/alert-dialog/AlertDialogContent.vue | 41 + .../alert-dialog/AlertDialogDescription.vue | 23 + .../ui/alert-dialog/AlertDialogFooter.vue | 22 + .../ui/alert-dialog/AlertDialogHeader.vue | 17 + .../ui/alert-dialog/AlertDialogTitle.vue | 20 + .../ui/alert-dialog/AlertDialogTrigger.vue | 11 + .../src/components/ui/alert-dialog/index.ts | 9 + .../src/components/ui/button/Button.vue | 27 + .../src/components/ui/button/index.ts | 36 + management-ui/src/components/ui/card/Card.vue | 22 + .../src/components/ui/card/CardAction.vue | 17 + .../src/components/ui/card/CardContent.vue | 17 + .../components/ui/card/CardDescription.vue | 17 + .../src/components/ui/card/CardFooter.vue | 17 + .../src/components/ui/card/CardHeader.vue | 17 + .../src/components/ui/card/CardTitle.vue | 17 + management-ui/src/components/ui/card/index.ts | 7 + .../src/components/ui/combobox/Combobox.vue | 17 + .../components/ui/combobox/ComboboxAnchor.vue | 23 + .../components/ui/combobox/ComboboxEmpty.vue | 21 + .../components/ui/combobox/ComboboxGroup.vue | 27 + .../components/ui/combobox/ComboboxInput.vue | 41 + .../components/ui/combobox/ComboboxItem.vue | 24 + .../ui/combobox/ComboboxItemIndicator.vue | 23 + .../components/ui/combobox/ComboboxList.vue | 29 + .../ui/combobox/ComboboxSeparator.vue | 21 + .../ui/combobox/ComboboxTrigger.vue | 24 + .../ui/combobox/ComboboxViewport.vue | 23 + .../src/components/ui/combobox/index.ts | 12 + .../src/components/ui/dialog/Dialog.vue | 17 + .../src/components/ui/dialog/DialogClose.vue | 14 + .../components/ui/dialog/DialogContent.vue | 46 + .../ui/dialog/DialogDescription.vue | 22 + .../src/components/ui/dialog/DialogFooter.vue | 15 + .../src/components/ui/dialog/DialogHeader.vue | 17 + .../components/ui/dialog/DialogOverlay.vue | 20 + .../ui/dialog/DialogScrollContent.vue | 56 + .../src/components/ui/dialog/DialogTitle.vue | 22 + .../components/ui/dialog/DialogTrigger.vue | 14 + .../src/components/ui/dialog/index.ts | 10 + .../ui/dropdown-menu/DropdownMenu.vue | 17 + .../DropdownMenuCheckboxItem.vue | 38 + .../ui/dropdown-menu/DropdownMenuContent.vue | 36 + .../ui/dropdown-menu/DropdownMenuGroup.vue | 14 + .../ui/dropdown-menu/DropdownMenuItem.vue | 30 + .../ui/dropdown-menu/DropdownMenuLabel.vue | 22 + .../dropdown-menu/DropdownMenuRadioGroup.vue | 22 + .../dropdown-menu/DropdownMenuRadioItem.vue | 39 + .../dropdown-menu/DropdownMenuSeparator.vue | 23 + .../ui/dropdown-menu/DropdownMenuShortcut.vue | 17 + .../ui/dropdown-menu/DropdownMenuSub.vue | 19 + .../dropdown-menu/DropdownMenuSubContent.vue | 28 + .../dropdown-menu/DropdownMenuSubTrigger.vue | 30 + .../ui/dropdown-menu/DropdownMenuTrigger.vue | 16 + .../src/components/ui/dropdown-menu/index.ts | 16 + .../src/components/ui/input/Input.vue | 33 + .../src/components/ui/input/index.ts | 1 + .../src/components/ui/label/Label.vue | 25 + .../src/components/ui/label/index.ts | 1 + .../components/ui/scroll-area/ScrollArea.vue | 33 + .../components/ui/scroll-area/ScrollBar.vue | 31 + .../src/components/ui/scroll-area/index.ts | 2 + .../src/components/ui/separator/Separator.vue | 28 + .../src/components/ui/separator/index.ts | 1 + .../src/components/ui/switch/Switch.vue | 38 + .../src/components/ui/switch/index.ts | 1 + .../src/components/ui/table/Table.vue | 16 + .../src/components/ui/table/TableBody.vue | 17 + .../src/components/ui/table/TableCaption.vue | 17 + .../src/components/ui/table/TableCell.vue | 22 + .../src/components/ui/table/TableEmpty.vue | 34 + .../src/components/ui/table/TableFooter.vue | 17 + .../src/components/ui/table/TableHead.vue | 17 + .../src/components/ui/table/TableHeader.vue | 17 + .../src/components/ui/table/TableRow.vue | 17 + .../src/components/ui/table/index.ts | 9 + .../src/components/ui/table/utils.ts | 9 + management-ui/src/components/ui/tabs/Tabs.vue | 23 + .../src/components/ui/tabs/TabsContent.vue | 20 + .../src/components/ui/tabs/TabsList.vue | 23 + .../src/components/ui/tabs/TabsTrigger.vue | 25 + management-ui/src/components/ui/tabs/index.ts | 4 + .../components/ui/tags-input/TagsInput.vue | 19 + .../ui/tags-input/TagsInputInput.vue | 16 + .../ui/tags-input/TagsInputItem.vue | 19 + .../ui/tags-input/TagsInputItemDelete.vue | 21 + .../ui/tags-input/TagsInputItemText.vue | 16 + .../src/components/ui/tags-input/index.ts | 5 + .../customcompometns/AdminDeviceDropdown.vue | 40 + .../customcompometns/AdminUserDropdonw.vue | 44 + .../src/customcompometns/Admincomponent.vue | 154 + .../src/customcompometns/AssignDevice.vue | 134 + .../src/customcompometns/CreateDevice.vue | 82 + .../src/customcompometns/CreateUser.vue | 67 + .../customcompometns/DataTableNoCheckbox.vue | 120 + .../customcompometns/DeleteDeviceDialog.vue | 47 + .../src/customcompometns/DeleteUserDialog.vue | 47 + .../src/customcompometns/DeviceComponent.vue | 22 + .../src/customcompometns/DevicesGrid.vue | 97 + .../src/customcompometns/EditDeviceDialog.vue | 71 + .../src/customcompometns/EditUserDialog.vue | 71 + management-ui/src/customcompometns/Navbar.vue | 57 + .../src/customcompometns/UserSettings.vue | 37 + management-ui/src/lib/api.ts | 28 + management-ui/src/lib/auth.ts | 61 + management-ui/src/lib/interfaces.ts | 11 + management-ui/src/lib/utils.ts | 6 + management-ui/src/main.ts | 6 + management-ui/src/pages/Admin.vue | 14 + management-ui/src/pages/Create.vue | 246 ++ management-ui/src/pages/DeviceView.vue | 13 + management-ui/src/pages/Devices.vue | 22 + management-ui/src/pages/Forbidden.vue | 12 + management-ui/src/pages/Login.vue | 82 + management-ui/src/pages/Settings.vue | 12 + management-ui/src/router.ts | 99 + management-ui/src/style.css | 123 + management-ui/src/vite-env.d.ts | 1 + management-ui/tsconfig.app.json | 22 + management-ui/tsconfig.json | 13 + management-ui/tsconfig.node.json | 25 + management-ui/vite.config.ts | 26 + nginx/dev.conf | 43 + readme.md | 38 + server/Dockerfile | 36 + server/cmd/api/main.go | 96 + server/go.mod | 67 + server/go.sum | 157 + server/internal/config/config.go | 205 ++ server/internal/crypto/argon2id.go | 102 + server/internal/crypto/jwt.go | 30 + server/internal/db/db.go | 20 + server/internal/dto/auth.go | 20 + server/internal/dto/device.go | 56 + server/internal/dto/record.go | 14 + server/internal/dto/user.go | 29 + server/internal/handlers/auth.go | 117 + server/internal/handlers/devices.go | 250 ++ server/internal/handlers/helpers.go | 78 + server/internal/handlers/livestream.go | 211 ++ server/internal/handlers/records.go | 123 + server/internal/handlers/users.go | 98 + server/internal/models/device.go | 12 + server/internal/models/record.go | 13 + server/internal/models/user.go | 20 + server/internal/router/router.go | 67 + server/internal/storage/minio.go | 60 + server/internal/vault/vault.go | 41 + traefik/.env | 1 + traefik/config/config.yaml | 19 + traefik/config/traefik.yaml | 37 + traefik/docker-compose.yaml | 49 + 177 files changed, 9173 insertions(+) create mode 100644 .env create mode 100644 .vs/NewSmoop/FileContentIndex/d6ab3d63-4ffd-4964-a3b7-ccd5e1d776eb.vsidx create mode 100644 .vs/NewSmoop/v17/.wsuo create mode 100644 .vs/NewSmoop/v17/DocumentLayout.json create mode 100644 .vs/ProjectSettings.json create mode 100644 .vs/VSWorkspaceState.json create mode 100644 .vs/slnx.sqlite create mode 100644 certs/gen-wildcard-nipio.sh create mode 100644 docker-compose.yaml create mode 100644 management-ui/.dockerignore create mode 100644 management-ui/.gitignore create mode 100644 management-ui/.vscode/extensions.json create mode 100644 management-ui/Dockerfile create mode 100644 management-ui/README.md create mode 100644 management-ui/components.json create mode 100644 management-ui/index.html create mode 100644 management-ui/package-lock.json create mode 100644 management-ui/package.json create mode 100644 management-ui/public/vite.svg create mode 100644 management-ui/src/App.vue create mode 100644 management-ui/src/assets/vue.svg create mode 100644 management-ui/src/components/HelloWorld.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialog.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogAction.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogCancel.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogContent.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogDescription.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogFooter.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogHeader.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogTitle.vue create mode 100644 management-ui/src/components/ui/alert-dialog/AlertDialogTrigger.vue create mode 100644 management-ui/src/components/ui/alert-dialog/index.ts create mode 100644 management-ui/src/components/ui/button/Button.vue create mode 100644 management-ui/src/components/ui/button/index.ts create mode 100644 management-ui/src/components/ui/card/Card.vue create mode 100644 management-ui/src/components/ui/card/CardAction.vue create mode 100644 management-ui/src/components/ui/card/CardContent.vue create mode 100644 management-ui/src/components/ui/card/CardDescription.vue create mode 100644 management-ui/src/components/ui/card/CardFooter.vue create mode 100644 management-ui/src/components/ui/card/CardHeader.vue create mode 100644 management-ui/src/components/ui/card/CardTitle.vue create mode 100644 management-ui/src/components/ui/card/index.ts create mode 100644 management-ui/src/components/ui/combobox/Combobox.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxAnchor.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxEmpty.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxGroup.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxInput.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxItem.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxItemIndicator.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxList.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxSeparator.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxTrigger.vue create mode 100644 management-ui/src/components/ui/combobox/ComboboxViewport.vue create mode 100644 management-ui/src/components/ui/combobox/index.ts create mode 100644 management-ui/src/components/ui/dialog/Dialog.vue create mode 100644 management-ui/src/components/ui/dialog/DialogClose.vue create mode 100644 management-ui/src/components/ui/dialog/DialogContent.vue create mode 100644 management-ui/src/components/ui/dialog/DialogDescription.vue create mode 100644 management-ui/src/components/ui/dialog/DialogFooter.vue create mode 100644 management-ui/src/components/ui/dialog/DialogHeader.vue create mode 100644 management-ui/src/components/ui/dialog/DialogOverlay.vue create mode 100644 management-ui/src/components/ui/dialog/DialogScrollContent.vue create mode 100644 management-ui/src/components/ui/dialog/DialogTitle.vue create mode 100644 management-ui/src/components/ui/dialog/DialogTrigger.vue create mode 100644 management-ui/src/components/ui/dialog/index.ts create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenu.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuContent.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuGroup.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuItem.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuLabel.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuSub.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue create mode 100644 management-ui/src/components/ui/dropdown-menu/index.ts create mode 100644 management-ui/src/components/ui/input/Input.vue create mode 100644 management-ui/src/components/ui/input/index.ts create mode 100644 management-ui/src/components/ui/label/Label.vue create mode 100644 management-ui/src/components/ui/label/index.ts create mode 100644 management-ui/src/components/ui/scroll-area/ScrollArea.vue create mode 100644 management-ui/src/components/ui/scroll-area/ScrollBar.vue create mode 100644 management-ui/src/components/ui/scroll-area/index.ts create mode 100644 management-ui/src/components/ui/separator/Separator.vue create mode 100644 management-ui/src/components/ui/separator/index.ts create mode 100644 management-ui/src/components/ui/switch/Switch.vue create mode 100644 management-ui/src/components/ui/switch/index.ts create mode 100644 management-ui/src/components/ui/table/Table.vue create mode 100644 management-ui/src/components/ui/table/TableBody.vue create mode 100644 management-ui/src/components/ui/table/TableCaption.vue create mode 100644 management-ui/src/components/ui/table/TableCell.vue create mode 100644 management-ui/src/components/ui/table/TableEmpty.vue create mode 100644 management-ui/src/components/ui/table/TableFooter.vue create mode 100644 management-ui/src/components/ui/table/TableHead.vue create mode 100644 management-ui/src/components/ui/table/TableHeader.vue create mode 100644 management-ui/src/components/ui/table/TableRow.vue create mode 100644 management-ui/src/components/ui/table/index.ts create mode 100644 management-ui/src/components/ui/table/utils.ts create mode 100644 management-ui/src/components/ui/tabs/Tabs.vue create mode 100644 management-ui/src/components/ui/tabs/TabsContent.vue create mode 100644 management-ui/src/components/ui/tabs/TabsList.vue create mode 100644 management-ui/src/components/ui/tabs/TabsTrigger.vue create mode 100644 management-ui/src/components/ui/tabs/index.ts create mode 100644 management-ui/src/components/ui/tags-input/TagsInput.vue create mode 100644 management-ui/src/components/ui/tags-input/TagsInputInput.vue create mode 100644 management-ui/src/components/ui/tags-input/TagsInputItem.vue create mode 100644 management-ui/src/components/ui/tags-input/TagsInputItemDelete.vue create mode 100644 management-ui/src/components/ui/tags-input/TagsInputItemText.vue create mode 100644 management-ui/src/components/ui/tags-input/index.ts create mode 100644 management-ui/src/customcompometns/AdminDeviceDropdown.vue create mode 100644 management-ui/src/customcompometns/AdminUserDropdonw.vue create mode 100644 management-ui/src/customcompometns/Admincomponent.vue create mode 100644 management-ui/src/customcompometns/AssignDevice.vue create mode 100644 management-ui/src/customcompometns/CreateDevice.vue create mode 100644 management-ui/src/customcompometns/CreateUser.vue create mode 100644 management-ui/src/customcompometns/DataTableNoCheckbox.vue create mode 100644 management-ui/src/customcompometns/DeleteDeviceDialog.vue create mode 100644 management-ui/src/customcompometns/DeleteUserDialog.vue create mode 100644 management-ui/src/customcompometns/DeviceComponent.vue create mode 100644 management-ui/src/customcompometns/DevicesGrid.vue create mode 100644 management-ui/src/customcompometns/EditDeviceDialog.vue create mode 100644 management-ui/src/customcompometns/EditUserDialog.vue create mode 100644 management-ui/src/customcompometns/Navbar.vue create mode 100644 management-ui/src/customcompometns/UserSettings.vue create mode 100644 management-ui/src/lib/api.ts create mode 100644 management-ui/src/lib/auth.ts create mode 100644 management-ui/src/lib/interfaces.ts create mode 100644 management-ui/src/lib/utils.ts create mode 100644 management-ui/src/main.ts create mode 100644 management-ui/src/pages/Admin.vue create mode 100644 management-ui/src/pages/Create.vue create mode 100644 management-ui/src/pages/DeviceView.vue create mode 100644 management-ui/src/pages/Devices.vue create mode 100644 management-ui/src/pages/Forbidden.vue create mode 100644 management-ui/src/pages/Login.vue create mode 100644 management-ui/src/pages/Settings.vue create mode 100644 management-ui/src/router.ts create mode 100644 management-ui/src/style.css create mode 100644 management-ui/src/vite-env.d.ts create mode 100644 management-ui/tsconfig.app.json create mode 100644 management-ui/tsconfig.json create mode 100644 management-ui/tsconfig.node.json create mode 100644 management-ui/vite.config.ts create mode 100644 nginx/dev.conf create mode 100644 readme.md create mode 100644 server/Dockerfile create mode 100644 server/cmd/api/main.go create mode 100644 server/go.mod create mode 100644 server/go.sum create mode 100644 server/internal/config/config.go create mode 100644 server/internal/crypto/argon2id.go create mode 100644 server/internal/crypto/jwt.go create mode 100644 server/internal/db/db.go create mode 100644 server/internal/dto/auth.go create mode 100644 server/internal/dto/device.go create mode 100644 server/internal/dto/record.go create mode 100644 server/internal/dto/user.go create mode 100644 server/internal/handlers/auth.go create mode 100644 server/internal/handlers/devices.go create mode 100644 server/internal/handlers/helpers.go create mode 100644 server/internal/handlers/livestream.go create mode 100644 server/internal/handlers/records.go create mode 100644 server/internal/handlers/users.go create mode 100644 server/internal/models/device.go create mode 100644 server/internal/models/record.go create mode 100644 server/internal/models/user.go create mode 100644 server/internal/router/router.go create mode 100644 server/internal/storage/minio.go create mode 100644 server/internal/vault/vault.go create mode 100644 traefik/.env create mode 100644 traefik/config/config.yaml create mode 100644 traefik/config/traefik.yaml create mode 100644 traefik/docker-compose.yaml diff --git a/.env b/.env new file mode 100644 index 0000000..91ddd77 --- /dev/null +++ b/.env @@ -0,0 +1,16 @@ +VAULT_ADDR=http://vault:8200 +VAULT_TOKEN=root +VAULT_KV_PATH=kv/data/snoop +CONFIG_MODE=dev +DB_DSN=postgres://snoop:example@postgres:5432/snoop?sslmode=disable +MINIO_ENDPOINT=minio:9000 +MINIO_ACCESS_KEY=minioadmin +MINIO_SECRET_KEY=minioadmin +MINIO_USE_SSL=false +MINIO_RECORDS_BUCKET=records +MINIO_LIVESTREAM_BUCKET=livestream +MINIO_PRESIGN_TTL_SECONDS=900 +JWT_SECRET=devsupersecret + +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin \ No newline at end of file diff --git a/.vs/NewSmoop/FileContentIndex/d6ab3d63-4ffd-4964-a3b7-ccd5e1d776eb.vsidx b/.vs/NewSmoop/FileContentIndex/d6ab3d63-4ffd-4964-a3b7-ccd5e1d776eb.vsidx new file mode 100644 index 0000000000000000000000000000000000000000..181079bed131610401b6b279c341162de62c9219 GIT binary patch literal 529042 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
RFva0~xko|9q5mWwVA9PX1kz2hF<4-^t-&YQ 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

JAm#Ci=jSONqJ?wd5~4u| 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 z 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 J|MQ3Z_kR*lsE_~v literal 0 HcmV?d00001 diff --git a/.vs/NewSmoop/v17/.wsuo b/.vs/NewSmoop/v17/.wsuo new file mode 100644 index 0000000000000000000000000000000000000000..71878882a9ad890c7b2f691edefd3f2a2f61dcc7 GIT binary patch literal 15360 zcmeHOU2Ggz6~61zmQo6BQvwtSrnsfF88fs0>jvYs*V`0oC(S17Lbej)-C29oa%moqO-OKj)l#&b`;azwy9destyGzls1%i#x>4?R&&szVw*yUED9k`w(Z^ z&F$@Ne&Rb`M=(Sqa7LK;wZ*uY7wZTuagkFQ8IFoOcjo-geM-&z^S9sFzW?pmy~y|V z54)L9gQ6*36wiQq9dGmErG0AY{(Mx7?a=42cYp02djZK{Uk4<8(%leEw7?Puu!OPD zfrgi_UoauYV*cswm(QPm0+BT@;gr|}m8NK*|HKs^BRmwkQnD0jsQiho?s{AuAWuKvh zd%XHS7D$)hN8zm*0nTwR1=&7PtUh1fBw(20jN2(klDXzU;l9M_BQt-OGjb9no zjv+4WX@#4w-xKyvm!9dD&!5iulJNp)F2e>}!H;&|l4JE%#VTU9%oGxi4?zDZ)JI#1 z5TrkgHW+Bb8mv*%u?XAXMcT-lNZH$>IL4oH$@-#>{}l3a&Iq%p*#xx~Xj%xP+P!XR z%Jf()hKO7vWO`V579T0wgtN}Ir>Ole zAuroY$b;8)@Zd#IXA4T8Yht7gXqp0eaUPl_fn0Hs!!rZj(k&Bd(5Jp5ZGuzjK94i_ z&NdSYpvIoIK#{VyusTZJMOs7?Cq){?^jfFY@|9t^;e@3SP5axfBpF&t8&jE@by2voo_w(BVB!SZ4YQjnIA3f4_aApl_qJ=Ny(rzvU z|2zJm^b?~DCRvXrc_sKP70y7DnBKZVZFRK%aD_T_2WLehXWF7ckN-P~|?9e{8zBPS!-5a4g2L zl>WxQhdq;-)pZ%BM)*Ak#Z{?|9tT$Zk zEvx~n4i!>9gS0A;>%nv20ZIXFBYQ^%cI(d$z<>Vn58AFWKC=IB5Br~bfcjuiIBa>! zF6k4m|Lw=Wcy0Rc-+S|glXS@<0rj{n?-tmYiB!1Jupa!ZyOEsXK=#kjKI&{>y`e`{mcxy=b zKR5^6F`!0rc}j?14{1Ld(6U+Z6D9fDkoLhnNnOK?Y(T=fUI{TQdR20e)JpUPnnOJs zTaXG$hlPGL;7xKit`8|^u`iZmyKp~_J0a}Nu;?t_WoV++b@XC)-IMWQ?}yvF;hm4Y z7A;sS62uPWz3uKpL?YHB6MZk9nP)D7H=IWKr4 zXD}fB!vN1g+`B-2Pw*U4S@TI1E{Ff8?8L&~{AKvw7fNE{-goj(e)l&gul(ufH;(*t z365AvWa|%Iy?OQ6vp;#c_UaFg{VUhExU-K>A9x~BUrtn0YDQ7DYE4P1$%L|;uBnQa z%4XG6B2{0m)?(s2vBH#!-=%!KIG;(*Yszc}IW?`ID*1UWuc&HXoy)1kVsS2UxpQKn zVONWdreRNa3R4x^+_Y@Nt}GfCN-fjesMvz2{f+l?(_>ToT=Tsx;& z9OuT&XpnkN*O-`9N@rd6(W=S1Vd%P&%xGC9sp~l{Zm{O3<{hmN+d{~p2=&OeSoYs!0_)Syv- zt1W@&?Nx8TaSL^GN6*53UkUaCclhMl_ax3fOzbb#oLxljAhw&tDdB+H+2D&$PS0{ykTxqkSGYe>*V!=TX<1SKny< zqx0YY)BHER`466dj?RDSi`_owzvKg+{m|c`cNX0u>0DAhfYYF=frtEdxua>vwN6oQ z+J-|W@HiXQaimLWi7n?@f%0u(DX2gZZ(z}=Af_W+$HK7!Mf&HdHz)sa?Uz^X>b$&I z`s%;UKc0T$#^%O}`^;3W?^1xn(fp$fWHvaMYtaZKi)Z67pzow_Z8RDr$ zt?}K>7nJ{A_YeEsyzIy|~r}Ae31H$&sVa-;g(%BNyq) rII-sjv<+*#>}>OCSWeW?+pq5b-IXKneja2I+a1QP2KR{!y8iqJzFMMr literal 0 HcmV?d00001 diff --git a/.vs/NewSmoop/v17/DocumentLayout.json b/.vs/NewSmoop/v17/DocumentLayout.json new file mode 100644 index 0000000..31ebcaf --- /dev/null +++ b/.vs/NewSmoop/v17/DocumentLayout.json @@ -0,0 +1,41 @@ +{ + "Version": 1, + "WorkspaceRootPath": "D:\\sourses\\NewSmoop\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|D:\\sourses\\NewSmoop\\server\\cmd\\api\\main.go||{3B902123-F8A7-4915-9F01-361F908088D0}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:server\\cmd\\api\\main.go||{3B902123-F8A7-4915-9F01-361F908088D0}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 1, + "Children": [ + { + "$type": "Bookmark", + "Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}" + }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "main.go", + "DocumentMoniker": "D:\\sourses\\NewSmoop\\server\\cmd\\api\\main.go", + "RelativeDocumentMoniker": "server\\cmd\\api\\main.go", + "ToolTip": "D:\\sourses\\NewSmoop\\server\\cmd\\api\\main.go", + "RelativeToolTip": "server\\cmd\\api\\main.go", + "ViewState": "AgIAABIAAAAAAAAAAAAAAAsAAAABAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001001|", + "WhenOpened": "2025-08-19T11:50:45.464Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..866f1e1 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..7a60152 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,11 @@ +{ + "ExpandedNodes": [ + "", + "\\server", + "\\server\\cmd", + "\\server\\cmd\\api", + "\\server\\internal" + ], + "SelectedNode": "\\server\\cmd\\api\\main.go", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..9c6a30364cede686dbda4adf8f1179cae5ce3f34 GIT binary patch literal 106496 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 XZ**01K5ogVcWjb9S;Dt|kDK`4)z=0f literal 0 HcmV?d00001 diff --git a/certs/gen-wildcard-nipio.sh b/certs/gen-wildcard-nipio.sh new file mode 100644 index 0000000..937a37e --- /dev/null +++ b/certs/gen-wildcard-nipio.sh @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +set -euo pipefail + +# === Config (you can override via env or first arg) === +DOMAIN="${1:-${DOMAIN:-192.168.205.130.nip.io}}" +BITS="${BITS:-4096}" +DAYS_CA="${DAYS_CA:-3650}" # ~10 years for the CA +DAYS_CERT="${DAYS_CERT:-397}" # keep server cert within common client limits +WORKDIR="${WORKDIR:-ssl-${DOMAIN}}" + +echo ">> Generating Root CA and wildcard cert for domain: ${DOMAIN}" +echo ">> Work directory: ${WORKDIR}" +mkdir -p "${WORKDIR}" +cd "${WORKDIR}" + +# Tighten key perms by default +umask 077 + +# --- CA OpenSSL config --- +cat > ca.cnf <<'EOF' +[ ca ] +default_ca = CA_default + +[ CA_default ] +dir = . +new_certs_dir = $dir/newcerts +database = $dir/index.txt +serial = $dir/serial +private_key = $dir/ca.key.pem +certificate = $dir/ca.cert.pem +default_md = sha256 +policy = policy_any +copy_extensions = copy + +[ policy_any ] +commonName = supplied +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +emailAddress = optional + +[ req ] +default_bits = 4096 +prompt = no +default_md = sha256 +x509_extensions = v3_ca +distinguished_name = req_dn +string_mask = utf8only + +[ req_dn ] +CN = Example Local Root CA + +[ v3_ca ] +basicConstraints = critical, CA:true, pathlen:0 +keyUsage = critical, keyCertSign, cRLSign +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +EOF + +mkdir -p newcerts +: > index.txt +echo 1000 > serial + +# --- Generate CA key & self-signed cert (RSA-4096) --- +echo ">> Creating CA key..." +openssl genrsa -out ca.key.pem "${BITS}" >/dev/null 2>&1 +chmod 600 ca.key.pem + +echo ">> Creating CA certificate..." +openssl req -config ca.cnf -key ca.key.pem -new -x509 -days "${DAYS_CA}" -sha256 -out ca.cert.pem + +# --- Server/OpenSSL config with SAN --- +cat > server.cnf <> Creating server key..." +openssl genrsa -out "${DOMAIN}.key" "${BITS}" >/dev/null 2>&1 +chmod 600 "${DOMAIN}.key" + +echo ">> Creating CSR..." +openssl req -new -key "${DOMAIN}.key" -out "${DOMAIN}.csr.pem" -config server.cnf + +# --- Extensions for final certificate (keep SAN/EKU consistent) --- +cat > cert_ext.cnf <> Signing server certificate with CA..." +openssl x509 -req \ + -in "${DOMAIN}.csr.pem" \ + -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial \ + -out "${DOMAIN}.crt" \ + -days "${DAYS_CERT}" -sha256 \ + -extfile cert_ext.cnf -extensions v3_server + +# --- Produce handy bundles --- +# Leaf only (typical when client already trusts the CA) +cp "${DOMAIN}.crt" "${DOMAIN}.leaf.pem" + +# Leaf + CA (some servers/clients expect to see the issuing cert) +cat "${DOMAIN}.crt" ca.cert.pem > "${DOMAIN}.chain.pem" + +# Optional PFX for systems that prefer it (no password) +openssl pkcs12 -export -out "${DOMAIN}.pfx" -inkey "${DOMAIN}.key" -in "${DOMAIN}.crt" -certfile ca.cert.pem -passout pass: + +# --- Verify --- +echo ">> Verifying certificate chain..." +openssl verify -CAfile ca.cert.pem "${DOMAIN}.crt" + +echo +echo "=== Done ===" +echo "Files created in ${WORKDIR}:" +printf " - Root CA: ca.key.pem (KEEP SECRET), ca.cert.pem (import into trust store)\n" +printf " - Server key: %s.key (KEEP SECRET)\n" "${DOMAIN}" +printf " - Server cert: %s.crt\n" "${DOMAIN}" +printf " - Chain: %s.chain.pem (leaf + CA)\n" "${DOMAIN}" +printf " - CSR: %s.csr.pem\n" "${DOMAIN}" +printf " - PFX: %s.pfx (no password)\n" "${DOMAIN}" +echo +echo "Tip: import ca.cert.pem into your OS/browser trust store; point your server at:" +echo " key=${DOMAIN}.key" +echo " cert=${DOMAIN}.crt (or ${DOMAIN}.chain.pem if it wants a chain)" diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..0af55e9 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,108 @@ +services: + postgres: + image: postgres:16 + environment: + POSTGRES_PASSWORD: example + POSTGRES_DB: snoop + POSTGRES_USER: snoop + ports: + - "5432:5432" + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 10s + timeout: 5s + retries: 10 + networks: + - snoopBack + + minio: + image: minio/minio:latest + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/ready"] + interval: 5s + timeout: 3s + retries: 5 + volumes: + - miniodata:/data + ports: # console :9001 is handy during dev + - "9000:9000" + - "9001:9001" + networks: + - snoopBack + + vault: + image: hashicorp/vault:1.16 + environment: + VAULT_API_ADDR: http://0.0.0.0:8200 + VAULT_TOKEN: root + ports: + - 8200:8200 + cap_add: + - IPC_LOCK + volumes: + - vault-data:/vault/data + networks: + - snoopBack + + snoop-api: + build: + context: ./server + dockerfile: Dockerfile + args: + APP_DIR: ${API_APP_DIR:-./cmd/api} + environment: + VAULT_ADDR: "http://vault:8200" + VAULT_TOKEN: "root" + VAULT_KV_PATH: "kv/data/snoop" + MINIO_ENDPOINT: "http://minio:9000" + env_file: + - .env + depends_on: + postgres: + condition: service_healthy + minio: + condition: service_healthy + networks: + - snoopBack + - proxy + + + + web: + build: + context: ./management-ui + dockerfile: Dockerfile + environment: + VITE_API_URL: /api + networks: + - proxy + + nginx: + image: nginx:1.27-alpine + depends_on: + - web + - snoop-api + ports: + - "80:80" + volumes: + - ./nginx/dev.conf:/etc/nginx/conf.d/default.conf:ro,Z + networks: + - proxy + + + +volumes: + pgdata: + miniodata: + vault-data: + +networks: + proxy: + external: true + snoopBack: diff --git a/management-ui/.dockerignore b/management-ui/.dockerignore new file mode 100644 index 0000000..f8f04d0 --- /dev/null +++ b/management-ui/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.git +.gitignore +npm-debug.log +.vscode +.vs \ No newline at end of file diff --git a/management-ui/.gitignore b/management-ui/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/management-ui/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/management-ui/.vscode/extensions.json b/management-ui/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/management-ui/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/management-ui/Dockerfile b/management-ui/Dockerfile new file mode 100644 index 0000000..323da87 --- /dev/null +++ b/management-ui/Dockerfile @@ -0,0 +1,16 @@ +FROM node:22-alpine +WORKDIR /app + +# Install deps +COPY package*.json ./ +RUN npm ci + +# Copy source +COPY . . + +# Vite dev server +ENV HOST=0.0.0.0 +ENV PORT=5173 +EXPOSE 5173 +ENV VITE_API_URL=/api +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173"] \ No newline at end of file diff --git a/management-ui/README.md b/management-ui/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/management-ui/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/management-ui/package-lock.json b/management-ui/package-lock.json new file mode 100644 index 0000000..fed64d4 --- /dev/null +++ b/management-ui/package-lock.json @@ -0,0 +1,2771 @@ +{ + "name": "management-ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "management-ui", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.11", + "@tanstack/vue-table": "^8.21.3", + "@vueuse/core": "^13.5.0", + "axios": "^1.11.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-vue-next": "^0.525.0", + "reka-ui": "^2.4.0", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.11", + "tw-animate-css": "^1.3.6", + "uuid": "^11.1.0", + "vue": "^3.5.17", + "vue-router": "^4.5.1" + }, + "devDependencies": { + "@iconify-json/radix-icons": "^1.2.2", + "@iconify/vue": "^5.0.0", + "@types/node": "^24.1.0", + "@vitejs/plugin-vue": "^6.0.0", + "@vue/tsconfig": "^0.7.0", + "typescript": "~5.8.3", + "vite": "^7.0.4", + "vue-tsc": "^2.2.12" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@floating-ui/vue": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.7.tgz", + "integrity": "sha512-idmAtbAIigGXN2SI5gItiXYBYtNfDTP9yIiObxgu13dgtG7ARCHlNfnR29GxP4LI4o13oiwsJ8wVgghj1lNqcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.2", + "@floating-ui/utils": "^0.2.10", + "vue-demi": ">=0.13.0" + } + }, + "node_modules/@floating-ui/vue/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@iconify-json/radix-icons": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@iconify-json/radix-icons/-/radix-icons-1.2.2.tgz", + "integrity": "sha512-+PPmKWDP7pfJMcEc9Ty1zyo/zzq+9rfKW4EGb2HSZcPu1VUhothDLFzWvBqQNoFIOYCJ2nm0Vmf8kVyYhq9G0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/vue": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-5.0.0.tgz", + "integrity": "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "vue": ">=3" + } + }, + "node_modules/@internationalized/date": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz", + "integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@internationalized/number": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.4.tgz", + "integrity": "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "tailwindcss": "4.1.11" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/vue-table/-/vue-table-8.21.3.tgz", + "integrity": "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": ">=3.2" + } + }, + "node_modules/@tanstack/vue-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz", + "integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz", + "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.19" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "vue": "3.5.18" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", + "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.5.0.tgz", + "integrity": "sha512-wV7z0eUpifKmvmN78UBZX8T7lMW53Nrk6JP5+6hbzrB9+cJ3jr//hUlhl9TZO/03bUkMK6gGkQpqOPWoabr72g==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "13.5.0", + "@vueuse/shared": "13.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.5.0.tgz", + "integrity": "sha512-euhItU3b0SqXxSy8u1XHxUCdQ8M++bsRs+TYhOLDU/OykS7KvJnyIFfep0XM5WjIFry9uAPlVSjmVHiqeshmkw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.5.0.tgz", + "integrity": "sha512-K7GrQIxJ/ANtucxIXbQlUHdB0TPA8c+q5i+zbrjxuhJCnJ9GtBg75sBSnvmLSxHKPg2Yo8w62PWksl9kwH0Q8g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lucide-vue-next": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.525.0.tgz", + "integrity": "sha512-Xf8+x8B2DrnGDV/rxylS+KBp2FIe6ljwDn2JsGTZZvXIfhmm/q+nv8RuGO1OyoMjOVkkz7CqtUqJfwtFPRbB2w==", + "license": "ISC", + "peerDependencies": { + "vue": ">=3.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/reka-ui": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.4.0.tgz", + "integrity": "sha512-5WHLEquWI5W67NnjH9F+RhpzRAcwjAEQHtHZMa5wYdhClcYDr59q0RAAcymcnfndFqv0MiBxyFfzbGSRyIZG5g==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.13", + "@floating-ui/vue": "^1.1.6", + "@internationalized/date": "^3.5.0", + "@internationalized/number": "^3.5.0", + "@tanstack/vue-virtual": "^3.12.0", + "@vueuse/core": "^12.5.0", + "@vueuse/shared": "^12.5.0", + "aria-hidden": "^1.2.4", + "defu": "^6.1.4", + "ohash": "^2.0.11" + }, + "peerDependencies": { + "vue": ">= 3.2.0" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/reka-ui/node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.6.tgz", + "integrity": "sha512-9dy0R9UsYEGmgf26L8UcHiLmSFTHa9+D7+dAt/G/sF5dCnPePZbfgDYinc7/UzAM7g/baVrmS6m9yEpU46d+LA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vite": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + } + } +} diff --git a/management-ui/package.json b/management-ui/package.json new file mode 100644 index 0000000..6c5912e --- /dev/null +++ b/management-ui/package.json @@ -0,0 +1,37 @@ +{ + "name": "management-ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.11", + "@tanstack/vue-table": "^8.21.3", + "@vueuse/core": "^13.5.0", + "axios": "^1.11.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-vue-next": "^0.525.0", + "reka-ui": "^2.4.0", + "tailwind-merge": "^3.3.1", + "tailwindcss": "^4.1.11", + "tw-animate-css": "^1.3.6", + "uuid": "^11.1.0", + "vue": "^3.5.17", + "vue-router": "^4.5.1" + }, + "devDependencies": { + "@iconify-json/radix-icons": "^1.2.2", + "@iconify/vue": "^5.0.0", + "@types/node": "^24.1.0", + "@vitejs/plugin-vue": "^6.0.0", + "@vue/tsconfig": "^0.7.0", + "typescript": "~5.8.3", + "vite": "^7.0.4", + "vue-tsc": "^2.2.12" + } +} diff --git a/management-ui/public/vite.svg b/management-ui/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/management-ui/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/management-ui/src/App.vue b/management-ui/src/App.vue new file mode 100644 index 0000000..950cb89 --- /dev/null +++ b/management-ui/src/App.vue @@ -0,0 +1,7 @@ + + + + diff --git a/management-ui/src/assets/vue.svg b/management-ui/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/management-ui/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/management-ui/src/components/HelloWorld.vue b/management-ui/src/components/HelloWorld.vue new file mode 100644 index 0000000..b58e52b --- /dev/null +++ b/management-ui/src/components/HelloWorld.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialog.vue b/management-ui/src/components/ui/alert-dialog/AlertDialog.vue new file mode 100644 index 0000000..f4d8c9a --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialog.vue @@ -0,0 +1,14 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogAction.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogAction.vue new file mode 100644 index 0000000..a5586f2 --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogAction.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogCancel.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogCancel.vue new file mode 100644 index 0000000..853968f --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogCancel.vue @@ -0,0 +1,24 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogContent.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogContent.vue new file mode 100644 index 0000000..c049348 --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogContent.vue @@ -0,0 +1,41 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogDescription.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogDescription.vue new file mode 100644 index 0000000..d04253a --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogDescription.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogFooter.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogFooter.vue new file mode 100644 index 0000000..1b534ad --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogFooter.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogHeader.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogHeader.vue new file mode 100644 index 0000000..d57edb9 --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogHeader.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogTitle.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogTitle.vue new file mode 100644 index 0000000..240ea3f --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogTitle.vue @@ -0,0 +1,20 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/AlertDialogTrigger.vue b/management-ui/src/components/ui/alert-dialog/AlertDialogTrigger.vue new file mode 100644 index 0000000..f7822d8 --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/AlertDialogTrigger.vue @@ -0,0 +1,11 @@ + + + diff --git a/management-ui/src/components/ui/alert-dialog/index.ts b/management-ui/src/components/ui/alert-dialog/index.ts new file mode 100644 index 0000000..0e1a4a8 --- /dev/null +++ b/management-ui/src/components/ui/alert-dialog/index.ts @@ -0,0 +1,9 @@ +export { default as AlertDialog } from './AlertDialog.vue' +export { default as AlertDialogAction } from './AlertDialogAction.vue' +export { default as AlertDialogCancel } from './AlertDialogCancel.vue' +export { default as AlertDialogContent } from './AlertDialogContent.vue' +export { default as AlertDialogDescription } from './AlertDialogDescription.vue' +export { default as AlertDialogFooter } from './AlertDialogFooter.vue' +export { default as AlertDialogHeader } from './AlertDialogHeader.vue' +export { default as AlertDialogTitle } from './AlertDialogTitle.vue' +export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue' diff --git a/management-ui/src/components/ui/button/Button.vue b/management-ui/src/components/ui/button/Button.vue new file mode 100644 index 0000000..6498db9 --- /dev/null +++ b/management-ui/src/components/ui/button/Button.vue @@ -0,0 +1,27 @@ + + + diff --git a/management-ui/src/components/ui/button/index.ts b/management-ui/src/components/ui/button/index.ts new file mode 100644 index 0000000..23edd68 --- /dev/null +++ b/management-ui/src/components/ui/button/index.ts @@ -0,0 +1,36 @@ +import { cva, type VariantProps } from 'class-variance-authority' + +export { default as Button } from './Button.vue' + +export const buttonVariants = cva( + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive', + { + variants: { + variant: { + default: + 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: + 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: + 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +) + +export type ButtonVariants = VariantProps diff --git a/management-ui/src/components/ui/card/Card.vue b/management-ui/src/components/ui/card/Card.vue new file mode 100644 index 0000000..2b447cc --- /dev/null +++ b/management-ui/src/components/ui/card/Card.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/card/CardAction.vue b/management-ui/src/components/ui/card/CardAction.vue new file mode 100644 index 0000000..77da12e --- /dev/null +++ b/management-ui/src/components/ui/card/CardAction.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/CardContent.vue b/management-ui/src/components/ui/card/CardContent.vue new file mode 100644 index 0000000..6d330dc --- /dev/null +++ b/management-ui/src/components/ui/card/CardContent.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/CardDescription.vue b/management-ui/src/components/ui/card/CardDescription.vue new file mode 100644 index 0000000..fef88d3 --- /dev/null +++ b/management-ui/src/components/ui/card/CardDescription.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/CardFooter.vue b/management-ui/src/components/ui/card/CardFooter.vue new file mode 100644 index 0000000..5702cc1 --- /dev/null +++ b/management-ui/src/components/ui/card/CardFooter.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/CardHeader.vue b/management-ui/src/components/ui/card/CardHeader.vue new file mode 100644 index 0000000..ad8c305 --- /dev/null +++ b/management-ui/src/components/ui/card/CardHeader.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/CardTitle.vue b/management-ui/src/components/ui/card/CardTitle.vue new file mode 100644 index 0000000..1dc9bd3 --- /dev/null +++ b/management-ui/src/components/ui/card/CardTitle.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/card/index.ts b/management-ui/src/components/ui/card/index.ts new file mode 100644 index 0000000..91680e1 --- /dev/null +++ b/management-ui/src/components/ui/card/index.ts @@ -0,0 +1,7 @@ +export { default as Card } from './Card.vue' +export { default as CardAction } from './CardAction.vue' +export { default as CardContent } from './CardContent.vue' +export { default as CardDescription } from './CardDescription.vue' +export { default as CardFooter } from './CardFooter.vue' +export { default as CardHeader } from './CardHeader.vue' +export { default as CardTitle } from './CardTitle.vue' diff --git a/management-ui/src/components/ui/combobox/Combobox.vue b/management-ui/src/components/ui/combobox/Combobox.vue new file mode 100644 index 0000000..75d8632 --- /dev/null +++ b/management-ui/src/components/ui/combobox/Combobox.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxAnchor.vue b/management-ui/src/components/ui/combobox/ComboboxAnchor.vue new file mode 100644 index 0000000..fbde937 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxAnchor.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxEmpty.vue b/management-ui/src/components/ui/combobox/ComboboxEmpty.vue new file mode 100644 index 0000000..5765ee1 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxEmpty.vue @@ -0,0 +1,21 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxGroup.vue b/management-ui/src/components/ui/combobox/ComboboxGroup.vue new file mode 100644 index 0000000..b6e23fb --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxGroup.vue @@ -0,0 +1,27 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxInput.vue b/management-ui/src/components/ui/combobox/ComboboxInput.vue new file mode 100644 index 0000000..7854cf1 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxInput.vue @@ -0,0 +1,41 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxItem.vue b/management-ui/src/components/ui/combobox/ComboboxItem.vue new file mode 100644 index 0000000..b23357b --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxItem.vue @@ -0,0 +1,24 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxItemIndicator.vue b/management-ui/src/components/ui/combobox/ComboboxItemIndicator.vue new file mode 100644 index 0000000..d0858e8 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxItemIndicator.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxList.vue b/management-ui/src/components/ui/combobox/ComboboxList.vue new file mode 100644 index 0000000..c77fde5 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxList.vue @@ -0,0 +1,29 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxSeparator.vue b/management-ui/src/components/ui/combobox/ComboboxSeparator.vue new file mode 100644 index 0000000..561eb7d --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxSeparator.vue @@ -0,0 +1,21 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxTrigger.vue b/management-ui/src/components/ui/combobox/ComboboxTrigger.vue new file mode 100644 index 0000000..454f3f8 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxTrigger.vue @@ -0,0 +1,24 @@ + + + diff --git a/management-ui/src/components/ui/combobox/ComboboxViewport.vue b/management-ui/src/components/ui/combobox/ComboboxViewport.vue new file mode 100644 index 0000000..3abd323 --- /dev/null +++ b/management-ui/src/components/ui/combobox/ComboboxViewport.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/combobox/index.ts b/management-ui/src/components/ui/combobox/index.ts new file mode 100644 index 0000000..98fce96 --- /dev/null +++ b/management-ui/src/components/ui/combobox/index.ts @@ -0,0 +1,12 @@ +export { default as Combobox } from './Combobox.vue' +export { default as ComboboxAnchor } from './ComboboxAnchor.vue' +export { default as ComboboxEmpty } from './ComboboxEmpty.vue' +export { default as ComboboxGroup } from './ComboboxGroup.vue' +export { default as ComboboxInput } from './ComboboxInput.vue' +export { default as ComboboxItem } from './ComboboxItem.vue' +export { default as ComboboxItemIndicator } from './ComboboxItemIndicator.vue' +export { default as ComboboxList } from './ComboboxList.vue' +export { default as ComboboxSeparator } from './ComboboxSeparator.vue' +export { default as ComboboxViewport } from './ComboboxViewport.vue' + +export { ComboboxCancel, ComboboxTrigger } from 'reka-ui' diff --git a/management-ui/src/components/ui/dialog/Dialog.vue b/management-ui/src/components/ui/dialog/Dialog.vue new file mode 100644 index 0000000..d3ef205 --- /dev/null +++ b/management-ui/src/components/ui/dialog/Dialog.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogClose.vue b/management-ui/src/components/ui/dialog/DialogClose.vue new file mode 100644 index 0000000..ec17092 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogClose.vue @@ -0,0 +1,14 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogContent.vue b/management-ui/src/components/ui/dialog/DialogContent.vue new file mode 100644 index 0000000..e33fa3c --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogContent.vue @@ -0,0 +1,46 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogDescription.vue b/management-ui/src/components/ui/dialog/DialogDescription.vue new file mode 100644 index 0000000..74e1a44 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogDescription.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogFooter.vue b/management-ui/src/components/ui/dialog/DialogFooter.vue new file mode 100644 index 0000000..fd26d48 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogFooter.vue @@ -0,0 +1,15 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogHeader.vue b/management-ui/src/components/ui/dialog/DialogHeader.vue new file mode 100644 index 0000000..aa9ab52 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogHeader.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogOverlay.vue b/management-ui/src/components/ui/dialog/DialogOverlay.vue new file mode 100644 index 0000000..1ff5b60 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogOverlay.vue @@ -0,0 +1,20 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogScrollContent.vue b/management-ui/src/components/ui/dialog/DialogScrollContent.vue new file mode 100644 index 0000000..09a29d4 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogScrollContent.vue @@ -0,0 +1,56 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogTitle.vue b/management-ui/src/components/ui/dialog/DialogTitle.vue new file mode 100644 index 0000000..10c81db --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogTitle.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/dialog/DialogTrigger.vue b/management-ui/src/components/ui/dialog/DialogTrigger.vue new file mode 100644 index 0000000..8a64056 --- /dev/null +++ b/management-ui/src/components/ui/dialog/DialogTrigger.vue @@ -0,0 +1,14 @@ + + + diff --git a/management-ui/src/components/ui/dialog/index.ts b/management-ui/src/components/ui/dialog/index.ts new file mode 100644 index 0000000..6a679e7 --- /dev/null +++ b/management-ui/src/components/ui/dialog/index.ts @@ -0,0 +1,10 @@ +export { default as Dialog } from './Dialog.vue' +export { default as DialogClose } from './DialogClose.vue' +export { default as DialogContent } from './DialogContent.vue' +export { default as DialogDescription } from './DialogDescription.vue' +export { default as DialogFooter } from './DialogFooter.vue' +export { default as DialogHeader } from './DialogHeader.vue' +export { default as DialogOverlay } from './DialogOverlay.vue' +export { default as DialogScrollContent } from './DialogScrollContent.vue' +export { default as DialogTitle } from './DialogTitle.vue' +export { default as DialogTrigger } from './DialogTrigger.vue' diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenu.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenu.vue new file mode 100644 index 0000000..05336c0 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenu.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue new file mode 100644 index 0000000..8e5beb8 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue @@ -0,0 +1,38 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuContent.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuContent.vue new file mode 100644 index 0000000..7381315 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuContent.vue @@ -0,0 +1,36 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuGroup.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuGroup.vue new file mode 100644 index 0000000..c516269 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuGroup.vue @@ -0,0 +1,14 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuItem.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuItem.vue new file mode 100644 index 0000000..f3cf879 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuItem.vue @@ -0,0 +1,30 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuLabel.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuLabel.vue new file mode 100644 index 0000000..2612018 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuLabel.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue new file mode 100644 index 0000000..f3b0605 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue new file mode 100644 index 0000000..e3f9666 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue @@ -0,0 +1,39 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue new file mode 100644 index 0000000..1d9c347 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue new file mode 100644 index 0000000..c04fc8f --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuSub.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSub.vue new file mode 100644 index 0000000..e829f22 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSub.vue @@ -0,0 +1,19 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue new file mode 100644 index 0000000..9bdbeda --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue @@ -0,0 +1,28 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue new file mode 100644 index 0000000..989baa6 --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue @@ -0,0 +1,30 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue b/management-ui/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue new file mode 100644 index 0000000..891a9dc --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue @@ -0,0 +1,16 @@ + + + diff --git a/management-ui/src/components/ui/dropdown-menu/index.ts b/management-ui/src/components/ui/dropdown-menu/index.ts new file mode 100644 index 0000000..866da4e --- /dev/null +++ b/management-ui/src/components/ui/dropdown-menu/index.ts @@ -0,0 +1,16 @@ +export { default as DropdownMenu } from './DropdownMenu.vue' + +export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.vue' +export { default as DropdownMenuContent } from './DropdownMenuContent.vue' +export { default as DropdownMenuGroup } from './DropdownMenuGroup.vue' +export { default as DropdownMenuItem } from './DropdownMenuItem.vue' +export { default as DropdownMenuLabel } from './DropdownMenuLabel.vue' +export { default as DropdownMenuRadioGroup } from './DropdownMenuRadioGroup.vue' +export { default as DropdownMenuRadioItem } from './DropdownMenuRadioItem.vue' +export { default as DropdownMenuSeparator } from './DropdownMenuSeparator.vue' +export { default as DropdownMenuShortcut } from './DropdownMenuShortcut.vue' +export { default as DropdownMenuSub } from './DropdownMenuSub.vue' +export { default as DropdownMenuSubContent } from './DropdownMenuSubContent.vue' +export { default as DropdownMenuSubTrigger } from './DropdownMenuSubTrigger.vue' +export { default as DropdownMenuTrigger } from './DropdownMenuTrigger.vue' +export { DropdownMenuPortal } from 'reka-ui' diff --git a/management-ui/src/components/ui/input/Input.vue b/management-ui/src/components/ui/input/Input.vue new file mode 100644 index 0000000..d1a3642 --- /dev/null +++ b/management-ui/src/components/ui/input/Input.vue @@ -0,0 +1,33 @@ + + + diff --git a/management-ui/src/components/ui/input/index.ts b/management-ui/src/components/ui/input/index.ts new file mode 100644 index 0000000..a691dd6 --- /dev/null +++ b/management-ui/src/components/ui/input/index.ts @@ -0,0 +1 @@ +export { default as Input } from './Input.vue' diff --git a/management-ui/src/components/ui/label/Label.vue b/management-ui/src/components/ui/label/Label.vue new file mode 100644 index 0000000..e1ce62f --- /dev/null +++ b/management-ui/src/components/ui/label/Label.vue @@ -0,0 +1,25 @@ + + + diff --git a/management-ui/src/components/ui/label/index.ts b/management-ui/src/components/ui/label/index.ts new file mode 100644 index 0000000..572c2f0 --- /dev/null +++ b/management-ui/src/components/ui/label/index.ts @@ -0,0 +1 @@ +export { default as Label } from './Label.vue' diff --git a/management-ui/src/components/ui/scroll-area/ScrollArea.vue b/management-ui/src/components/ui/scroll-area/ScrollArea.vue new file mode 100644 index 0000000..9f6cbf5 --- /dev/null +++ b/management-ui/src/components/ui/scroll-area/ScrollArea.vue @@ -0,0 +1,33 @@ + + + diff --git a/management-ui/src/components/ui/scroll-area/ScrollBar.vue b/management-ui/src/components/ui/scroll-area/ScrollBar.vue new file mode 100644 index 0000000..f738101 --- /dev/null +++ b/management-ui/src/components/ui/scroll-area/ScrollBar.vue @@ -0,0 +1,31 @@ + + + diff --git a/management-ui/src/components/ui/scroll-area/index.ts b/management-ui/src/components/ui/scroll-area/index.ts new file mode 100644 index 0000000..448071d --- /dev/null +++ b/management-ui/src/components/ui/scroll-area/index.ts @@ -0,0 +1,2 @@ +export { default as ScrollArea } from './ScrollArea.vue' +export { default as ScrollBar } from './ScrollBar.vue' diff --git a/management-ui/src/components/ui/separator/Separator.vue b/management-ui/src/components/ui/separator/Separator.vue new file mode 100644 index 0000000..c8f0500 --- /dev/null +++ b/management-ui/src/components/ui/separator/Separator.vue @@ -0,0 +1,28 @@ + + + diff --git a/management-ui/src/components/ui/separator/index.ts b/management-ui/src/components/ui/separator/index.ts new file mode 100644 index 0000000..2287bcb --- /dev/null +++ b/management-ui/src/components/ui/separator/index.ts @@ -0,0 +1 @@ +export { default as Separator } from './Separator.vue' diff --git a/management-ui/src/components/ui/switch/Switch.vue b/management-ui/src/components/ui/switch/Switch.vue new file mode 100644 index 0000000..aac7adf --- /dev/null +++ b/management-ui/src/components/ui/switch/Switch.vue @@ -0,0 +1,38 @@ + + + diff --git a/management-ui/src/components/ui/switch/index.ts b/management-ui/src/components/ui/switch/index.ts new file mode 100644 index 0000000..87b4b17 --- /dev/null +++ b/management-ui/src/components/ui/switch/index.ts @@ -0,0 +1 @@ +export { default as Switch } from './Switch.vue' diff --git a/management-ui/src/components/ui/table/Table.vue b/management-ui/src/components/ui/table/Table.vue new file mode 100644 index 0000000..050f67a --- /dev/null +++ b/management-ui/src/components/ui/table/Table.vue @@ -0,0 +1,16 @@ + + + diff --git a/management-ui/src/components/ui/table/TableBody.vue b/management-ui/src/components/ui/table/TableBody.vue new file mode 100644 index 0000000..94c4f7f --- /dev/null +++ b/management-ui/src/components/ui/table/TableBody.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/TableCaption.vue b/management-ui/src/components/ui/table/TableCaption.vue new file mode 100644 index 0000000..7d0016c --- /dev/null +++ b/management-ui/src/components/ui/table/TableCaption.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/TableCell.vue b/management-ui/src/components/ui/table/TableCell.vue new file mode 100644 index 0000000..de766c6 --- /dev/null +++ b/management-ui/src/components/ui/table/TableCell.vue @@ -0,0 +1,22 @@ + + + diff --git a/management-ui/src/components/ui/table/TableEmpty.vue b/management-ui/src/components/ui/table/TableEmpty.vue new file mode 100644 index 0000000..42c0e24 --- /dev/null +++ b/management-ui/src/components/ui/table/TableEmpty.vue @@ -0,0 +1,34 @@ + + + diff --git a/management-ui/src/components/ui/table/TableFooter.vue b/management-ui/src/components/ui/table/TableFooter.vue new file mode 100644 index 0000000..5f90a2b --- /dev/null +++ b/management-ui/src/components/ui/table/TableFooter.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/TableHead.vue b/management-ui/src/components/ui/table/TableHead.vue new file mode 100644 index 0000000..8e32dd4 --- /dev/null +++ b/management-ui/src/components/ui/table/TableHead.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/TableHeader.vue b/management-ui/src/components/ui/table/TableHeader.vue new file mode 100644 index 0000000..69cf93d --- /dev/null +++ b/management-ui/src/components/ui/table/TableHeader.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/TableRow.vue b/management-ui/src/components/ui/table/TableRow.vue new file mode 100644 index 0000000..1a9cd78 --- /dev/null +++ b/management-ui/src/components/ui/table/TableRow.vue @@ -0,0 +1,17 @@ + + + diff --git a/management-ui/src/components/ui/table/index.ts b/management-ui/src/components/ui/table/index.ts new file mode 100644 index 0000000..67f382c --- /dev/null +++ b/management-ui/src/components/ui/table/index.ts @@ -0,0 +1,9 @@ +export { default as Table } from './Table.vue' +export { default as TableBody } from './TableBody.vue' +export { default as TableCaption } from './TableCaption.vue' +export { default as TableCell } from './TableCell.vue' +export { default as TableEmpty } from './TableEmpty.vue' +export { default as TableFooter } from './TableFooter.vue' +export { default as TableHead } from './TableHead.vue' +export { default as TableHeader } from './TableHeader.vue' +export { default as TableRow } from './TableRow.vue' diff --git a/management-ui/src/components/ui/table/utils.ts b/management-ui/src/components/ui/table/utils.ts new file mode 100644 index 0000000..8ed839b --- /dev/null +++ b/management-ui/src/components/ui/table/utils.ts @@ -0,0 +1,9 @@ +import type { Updater } from '@tanstack/vue-table' +import type { Ref } from 'vue' + +export function valueUpdater>(updaterOrValue: T, ref: Ref) { + ref.value + = typeof updaterOrValue === 'function' + ? updaterOrValue(ref.value) + : updaterOrValue +} diff --git a/management-ui/src/components/ui/tabs/Tabs.vue b/management-ui/src/components/ui/tabs/Tabs.vue new file mode 100644 index 0000000..f887a2c --- /dev/null +++ b/management-ui/src/components/ui/tabs/Tabs.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/tabs/TabsContent.vue b/management-ui/src/components/ui/tabs/TabsContent.vue new file mode 100644 index 0000000..80c78ed --- /dev/null +++ b/management-ui/src/components/ui/tabs/TabsContent.vue @@ -0,0 +1,20 @@ + + + diff --git a/management-ui/src/components/ui/tabs/TabsList.vue b/management-ui/src/components/ui/tabs/TabsList.vue new file mode 100644 index 0000000..bf8cb97 --- /dev/null +++ b/management-ui/src/components/ui/tabs/TabsList.vue @@ -0,0 +1,23 @@ + + + diff --git a/management-ui/src/components/ui/tabs/TabsTrigger.vue b/management-ui/src/components/ui/tabs/TabsTrigger.vue new file mode 100644 index 0000000..dd25c67 --- /dev/null +++ b/management-ui/src/components/ui/tabs/TabsTrigger.vue @@ -0,0 +1,25 @@ + + + diff --git a/management-ui/src/components/ui/tabs/index.ts b/management-ui/src/components/ui/tabs/index.ts new file mode 100644 index 0000000..d56c937 --- /dev/null +++ b/management-ui/src/components/ui/tabs/index.ts @@ -0,0 +1,4 @@ +export { default as Tabs } from './Tabs.vue' +export { default as TabsContent } from './TabsContent.vue' +export { default as TabsList } from './TabsList.vue' +export { default as TabsTrigger } from './TabsTrigger.vue' diff --git a/management-ui/src/components/ui/tags-input/TagsInput.vue b/management-ui/src/components/ui/tags-input/TagsInput.vue new file mode 100644 index 0000000..3d59bb8 --- /dev/null +++ b/management-ui/src/components/ui/tags-input/TagsInput.vue @@ -0,0 +1,19 @@ + + + diff --git a/management-ui/src/components/ui/tags-input/TagsInputInput.vue b/management-ui/src/components/ui/tags-input/TagsInputInput.vue new file mode 100644 index 0000000..f3c245b --- /dev/null +++ b/management-ui/src/components/ui/tags-input/TagsInputInput.vue @@ -0,0 +1,16 @@ + + + diff --git a/management-ui/src/components/ui/tags-input/TagsInputItem.vue b/management-ui/src/components/ui/tags-input/TagsInputItem.vue new file mode 100644 index 0000000..7ffd2b5 --- /dev/null +++ b/management-ui/src/components/ui/tags-input/TagsInputItem.vue @@ -0,0 +1,19 @@ + + + diff --git a/management-ui/src/components/ui/tags-input/TagsInputItemDelete.vue b/management-ui/src/components/ui/tags-input/TagsInputItemDelete.vue new file mode 100644 index 0000000..fcd3e7a --- /dev/null +++ b/management-ui/src/components/ui/tags-input/TagsInputItemDelete.vue @@ -0,0 +1,21 @@ + + + diff --git a/management-ui/src/components/ui/tags-input/TagsInputItemText.vue b/management-ui/src/components/ui/tags-input/TagsInputItemText.vue new file mode 100644 index 0000000..0b2d91a --- /dev/null +++ b/management-ui/src/components/ui/tags-input/TagsInputItemText.vue @@ -0,0 +1,16 @@ + + + diff --git a/management-ui/src/components/ui/tags-input/index.ts b/management-ui/src/components/ui/tags-input/index.ts new file mode 100644 index 0000000..4e921e4 --- /dev/null +++ b/management-ui/src/components/ui/tags-input/index.ts @@ -0,0 +1,5 @@ +export { default as TagsInput } from './TagsInput.vue' +export { default as TagsInputInput } from './TagsInputInput.vue' +export { default as TagsInputItem } from './TagsInputItem.vue' +export { default as TagsInputItemDelete } from './TagsInputItemDelete.vue' +export { default as TagsInputItemText } from './TagsInputItemText.vue' diff --git a/management-ui/src/customcompometns/AdminDeviceDropdown.vue b/management-ui/src/customcompometns/AdminDeviceDropdown.vue new file mode 100644 index 0000000..1537634 --- /dev/null +++ b/management-ui/src/customcompometns/AdminDeviceDropdown.vue @@ -0,0 +1,40 @@ + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/AdminUserDropdonw.vue b/management-ui/src/customcompometns/AdminUserDropdonw.vue new file mode 100644 index 0000000..4ad8cfc --- /dev/null +++ b/management-ui/src/customcompometns/AdminUserDropdonw.vue @@ -0,0 +1,44 @@ + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/Admincomponent.vue b/management-ui/src/customcompometns/Admincomponent.vue new file mode 100644 index 0000000..c75fd80 --- /dev/null +++ b/management-ui/src/customcompometns/Admincomponent.vue @@ -0,0 +1,154 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/AssignDevice.vue b/management-ui/src/customcompometns/AssignDevice.vue new file mode 100644 index 0000000..8090cc4 --- /dev/null +++ b/management-ui/src/customcompometns/AssignDevice.vue @@ -0,0 +1,134 @@ + + + diff --git a/management-ui/src/customcompometns/CreateDevice.vue b/management-ui/src/customcompometns/CreateDevice.vue new file mode 100644 index 0000000..fccd780 --- /dev/null +++ b/management-ui/src/customcompometns/CreateDevice.vue @@ -0,0 +1,82 @@ + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/CreateUser.vue b/management-ui/src/customcompometns/CreateUser.vue new file mode 100644 index 0000000..b9900f4 --- /dev/null +++ b/management-ui/src/customcompometns/CreateUser.vue @@ -0,0 +1,67 @@ + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/DataTableNoCheckbox.vue b/management-ui/src/customcompometns/DataTableNoCheckbox.vue new file mode 100644 index 0000000..1fe44d5 --- /dev/null +++ b/management-ui/src/customcompometns/DataTableNoCheckbox.vue @@ -0,0 +1,120 @@ + + + diff --git a/management-ui/src/customcompometns/DeleteDeviceDialog.vue b/management-ui/src/customcompometns/DeleteDeviceDialog.vue new file mode 100644 index 0000000..195e68e --- /dev/null +++ b/management-ui/src/customcompometns/DeleteDeviceDialog.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/DeleteUserDialog.vue b/management-ui/src/customcompometns/DeleteUserDialog.vue new file mode 100644 index 0000000..414a6ff --- /dev/null +++ b/management-ui/src/customcompometns/DeleteUserDialog.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/DeviceComponent.vue b/management-ui/src/customcompometns/DeviceComponent.vue new file mode 100644 index 0000000..a5d6645 --- /dev/null +++ b/management-ui/src/customcompometns/DeviceComponent.vue @@ -0,0 +1,22 @@ + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/DevicesGrid.vue b/management-ui/src/customcompometns/DevicesGrid.vue new file mode 100644 index 0000000..6d272b4 --- /dev/null +++ b/management-ui/src/customcompometns/DevicesGrid.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/EditDeviceDialog.vue b/management-ui/src/customcompometns/EditDeviceDialog.vue new file mode 100644 index 0000000..7cb6773 --- /dev/null +++ b/management-ui/src/customcompometns/EditDeviceDialog.vue @@ -0,0 +1,71 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/EditUserDialog.vue b/management-ui/src/customcompometns/EditUserDialog.vue new file mode 100644 index 0000000..82b0af4 --- /dev/null +++ b/management-ui/src/customcompometns/EditUserDialog.vue @@ -0,0 +1,71 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/Navbar.vue b/management-ui/src/customcompometns/Navbar.vue new file mode 100644 index 0000000..91e80ab --- /dev/null +++ b/management-ui/src/customcompometns/Navbar.vue @@ -0,0 +1,57 @@ + + + \ No newline at end of file diff --git a/management-ui/src/customcompometns/UserSettings.vue b/management-ui/src/customcompometns/UserSettings.vue new file mode 100644 index 0000000..490b77e --- /dev/null +++ b/management-ui/src/customcompometns/UserSettings.vue @@ -0,0 +1,37 @@ + + \ No newline at end of file diff --git a/management-ui/src/lib/api.ts b/management-ui/src/lib/api.ts new file mode 100644 index 0000000..53e6c88 --- /dev/null +++ b/management-ui/src/lib/api.ts @@ -0,0 +1,28 @@ +import axios from 'axios' +import { auth } from './auth' + +export const api = axios.create({ + baseURL: import.meta.env.VITE_API_URL || '/api', + timeout: 10_000, + headers: { 'Content-Type': 'application/json' }, + // withCredentials: false // bearer tokens don't need cookies +}) + +api.interceptors.request.use((config) => { + const t = auth.token.value + if (t) config.headers.Authorization = `Bearer ${t}` + return config +}) + +api.interceptors.response.use( + (res) => res, + (err) => { + if (err?.response?.status === 401) { + auth.clear() + if (window.location.pathname !== '/login') { + window.location.href = '/login' + } + } + return Promise.reject(err) + } +) \ No newline at end of file diff --git a/management-ui/src/lib/auth.ts b/management-ui/src/lib/auth.ts new file mode 100644 index 0000000..057be2f --- /dev/null +++ b/management-ui/src/lib/auth.ts @@ -0,0 +1,61 @@ +import { ref, computed } from 'vue' + +export type JwtPayload = { + sub?: string + exp?: number + iat?: number + role?: string | string[] + roles?: string[] + scope?: string + [k: string]: unknown +} + +function decodeJwt(token: string): JwtPayload | null { + try { + const [, payload] = token.split('.') + if (!payload) return null + // URL-safe Base64 → Base64 + const base64 = payload.replace(/-/g, '+').replace(/_/g, '/') + const json = decodeURIComponent( + atob(base64) + .split('') + .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)) + .join('') + ) + return JSON.parse(json) + } catch { + return null + } +} + +const token = ref(sessionStorage.getItem('accessToken')) +const payload = ref(token.value ? decodeJwt(token.value) : null) + +const isAuthenticated = computed(() => { + if (!token.value) return false + const exp = payload.value?.exp + return !exp || exp * 1000 > Date.now() +}) + +const roles = computed(() => { + const p = payload.value + if (!p) return [] + if (Array.isArray(p.roles)) return p.roles + if (typeof p.role === 'string') return [p.role] + if (typeof p.scope === 'string') return p.scope.split(' ') // e.g., "admin user" + return [] +}) + +function setToken(t: string) { + token.value = t + payload.value = decodeJwt(t) + sessionStorage.setItem('accessToken', t) +} + +function clear() { + token.value = null + payload.value = null + sessionStorage.removeItem('accessToken') +} + +export const auth = { token, payload, roles, isAuthenticated, setToken, clear } \ No newline at end of file diff --git a/management-ui/src/lib/interfaces.ts b/management-ui/src/lib/interfaces.ts new file mode 100644 index 0000000..4c1552b --- /dev/null +++ b/management-ui/src/lib/interfaces.ts @@ -0,0 +1,11 @@ +export interface Device { + guid: string + devicename: string + assigned_users: string +} + +export interface Users { + id: number, + username: string, + role: string +} \ No newline at end of file diff --git a/management-ui/src/lib/utils.ts b/management-ui/src/lib/utils.ts new file mode 100644 index 0000000..28f8215 --- /dev/null +++ b/management-ui/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from 'clsx' +import { twMerge } from 'tailwind-merge' + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/management-ui/src/main.ts b/management-ui/src/main.ts new file mode 100644 index 0000000..7b19912 --- /dev/null +++ b/management-ui/src/main.ts @@ -0,0 +1,6 @@ +import { createApp } from 'vue' +import './style.css' +import App from './App.vue' +import router from './router' + +createApp(App).use(router).mount('#app') diff --git a/management-ui/src/pages/Admin.vue b/management-ui/src/pages/Admin.vue new file mode 100644 index 0000000..06ebb8c --- /dev/null +++ b/management-ui/src/pages/Admin.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/management-ui/src/pages/Create.vue b/management-ui/src/pages/Create.vue new file mode 100644 index 0000000..cfdc099 --- /dev/null +++ b/management-ui/src/pages/Create.vue @@ -0,0 +1,246 @@ + + + \ No newline at end of file diff --git a/management-ui/src/pages/DeviceView.vue b/management-ui/src/pages/DeviceView.vue new file mode 100644 index 0000000..53c087b --- /dev/null +++ b/management-ui/src/pages/DeviceView.vue @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/management-ui/src/pages/Devices.vue b/management-ui/src/pages/Devices.vue new file mode 100644 index 0000000..a8de1df --- /dev/null +++ b/management-ui/src/pages/Devices.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/management-ui/src/pages/Forbidden.vue b/management-ui/src/pages/Forbidden.vue new file mode 100644 index 0000000..694cedc --- /dev/null +++ b/management-ui/src/pages/Forbidden.vue @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/management-ui/src/pages/Login.vue b/management-ui/src/pages/Login.vue new file mode 100644 index 0000000..25ed3af --- /dev/null +++ b/management-ui/src/pages/Login.vue @@ -0,0 +1,82 @@ + + + \ No newline at end of file diff --git a/management-ui/src/pages/Settings.vue b/management-ui/src/pages/Settings.vue new file mode 100644 index 0000000..be0a2a2 --- /dev/null +++ b/management-ui/src/pages/Settings.vue @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/management-ui/src/router.ts b/management-ui/src/router.ts new file mode 100644 index 0000000..0900530 --- /dev/null +++ b/management-ui/src/router.ts @@ -0,0 +1,99 @@ +import { createRouter, createWebHistory } from 'vue-router'; + +import Admin from '@/pages/Admin.vue'; +import Login from '@/pages/Login.vue'; +import Settings from '@/pages/Settings.vue'; +import Devices from '@/pages/Devices.vue'; +import DeviceView from './pages/DeviceView.vue'; +import Forbidden from './pages/Forbidden.vue'; +import Create from './pages/Create.vue'; +import { auth } from './lib/auth'; + + +declare module 'vue-router' { + interface RouteMeta { + requiresAuth?: boolean + roles?: string[] // allowed roles + } +} + +const routes = [ + { + path: '/login', + name: 'Login', + component: Login, + meta: { requiresAuth: false } + }, + { + path: '/', + redirect: '/devices', // Optional: Redirect the root path to /home + }, + { + path: '/devices', + name: 'Devices', + component: Devices, + meta: { requiresAuth: true } + }, + { + path: '/settings', + name: 'Settings', + component: Settings, + meta: { requiresAuth: true } + }, + { + path: '/admin', + name: 'Admin', + component: Admin, + meta: { requiresAuth: true, roles: ['admin'] } + }, + { + path: '/device/:guid', // ← new dynamic segment + name: 'DeviceView', + component: DeviceView, + props: true, // so `guid` shows up as a prop + meta: { requiresAuth: true } + }, + { + path: '/forbidden', + name: 'Forbidden', + component: Forbidden, + meta: { requiresAuth: false } + }, + { + path: '/create', + name: 'Create', + component: Create, + meta: { requiresAuth: true, roles: ['admin'] } + } +] + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes, +}) + +// Navigation guard for protected routes. +// For any route except "Login", we force a full page load so that the server-side session check occurs. +router.beforeEach((to, _from, next) => { + const requiresAuth = to.meta.requiresAuth !== false && to.name !== 'Login' + + // 1) Not authenticated → /login?redirect= + if (requiresAuth && !auth.isAuthenticated.value) { + return next({ name: 'Login', query: { redirect: to.fullPath } }) + } + + // 2) Role check (if any) + const allowed = to.meta.roles + if (allowed && allowed.length > 0) { + const hasRole = allowed.some((r) => auth.roles.value.includes(r)) + if (!hasRole) { + return next({ name: 'Forbidden' }) + } + } + + next() +}) + + +export default router + diff --git a/management-ui/src/style.css b/management-ui/src/style.css new file mode 100644 index 0000000..0a61557 --- /dev/null +++ b/management-ui/src/style.css @@ -0,0 +1,123 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/management-ui/src/vite-env.d.ts b/management-ui/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/management-ui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/management-ui/tsconfig.app.json b/management-ui/tsconfig.app.json new file mode 100644 index 0000000..31b4897 --- /dev/null +++ b/management-ui/tsconfig.app.json @@ -0,0 +1,22 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/management-ui/tsconfig.json b/management-ui/tsconfig.json new file mode 100644 index 0000000..856b926 --- /dev/null +++ b/management-ui/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/management-ui/tsconfig.node.json b/management-ui/tsconfig.node.json new file mode 100644 index 0000000..f85a399 --- /dev/null +++ b/management-ui/tsconfig.node.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/management-ui/vite.config.ts b/management-ui/vite.config.ts new file mode 100644 index 0000000..de2b9d5 --- /dev/null +++ b/management-ui/vite.config.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import path from 'node:path' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [vue(),tailwindcss()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + host: true, // listen on 0.0.0.0 inside container + port: 5173, + // Allow the container DNS name and local access + allowedHosts: ['localhost', '127.0.0.1', 'web', 'nginx'], + // Make HMR use the public port (80) when going through Nginx + hmr: { + clientPort: 80, // browser connects to ws://:80/… + protocol: 'ws', // dev over HTTP + host: 'localhost', // adjust if you open via a LAN IP + }, + }, +}) diff --git a/nginx/dev.conf b/nginx/dev.conf new file mode 100644 index 0000000..38d8882 --- /dev/null +++ b/nginx/dev.conf @@ -0,0 +1,43 @@ +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +# Helpful for larger uploads via API (tweak as you wish) +client_max_body_size 400m; + +server { + listen 80; + server_name _; + + # Common proxy settings + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # --- Frontend (Vite dev server) --- + # Pass EVERYTHING else to Vite dev server on `web:5173` + # Includes HMR websocket support. + location / { + proxy_pass http://web:5173; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + } + + # --- API --- + # Strip the /api prefix (so backend sees /foo instead of /api/foo) + location /api/ { + proxy_pass http://snoop-api:8080/; # note the trailing slash -> strips /api + proxy_http_version 1.1; + + # WebSocket/SSE-friendly defaults for API (covers /api/ws etc.) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5094417 --- /dev/null +++ b/readme.md @@ -0,0 +1,38 @@ +# Vault setup + +```bash +export VAULT_ADDR=http://localhost:8200 +export VAULT_TOKEN=root + +# Enable KV v2 (if not already): vault secrets enable -path=kv kv-v2 +vault secrets enable -path=kv kv-v2 + +# Put secrets (example) +vault kv put kv/snoop \ + db_dsn="postgres://snoop:snoop@postgres:5432/snoop?sslmode=disable" \ + minio_endpoint="minio:9000" \ + minio_access_key="minioadmin" \ + minio_secret_key="minioadmin" \ + minio_use_ssl="false" \ + jwt_secret="supersecretjwt" \ + minio_records_bucket="records" \ + minio_livestream_bucket="livestream" \ + minio_presign_ttl_seconds="900" +``` + +Unseal Key: gQrvvJBaGR4CpmkoVq93tWqk5dTpioMAHtNHKMWNlH0= + +Root Token: root + + +{ + "db_dsn": "postgres://snoop:snoop@postgres:5432/snoop?sslmode=disable", + "minio_endpoint": "minio:9000", + "minio_access_key": "minioadmin", + "minio_secret_key": "minioadmin", + "minio_use_ssl": "false", + "jwt_secret": "supersecretjwt", + "minio_records_bucket": "records", + "minio_livestream_bucket": "livestream", + "minio_presign_ttl_seconds": "900" +} \ No newline at end of file diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..9e8f101 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,36 @@ +######################## +# 1) Build stage +######################## +FROM --platform=$BUILDPLATFORM golang:1.23-alpine AS build +WORKDIR /src +ENV CGO_ENABLED=0 + +# CA certs for TLS to Vault/MinIO +RUN apk add --no-cache ca-certificates && update-ca-certificates + +# Cache deps +COPY go.mod go.sum ./ +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +# Copy source +COPY . . + +# Pick your entrypoint package; default assumes ./cmd/api/main.go +# You can override APP_DIR build-arg from compose if needed. +ARG APP_DIR=./cmd/api +ARG TARGETOS TARGETARCH +RUN --mount=type=cache,target=/root/.cache/go-build \ + GOOS=$TARGETOS GOARCH=$TARGETARCH \ + go build -trimpath -ldflags="-s -w -buildid=" -o /out/snoop-api $APP_DIR + +######################## +# 2) Minimal runtime +######################## +FROM gcr.io/distroless/static:nonroot +# Copy CA bundle for HTTPS calls +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=build /out/snoop-api /snoop-api +USER nonroot:nonroot +EXPOSE 8080 +ENTRYPOINT ["/snoop-api"] \ No newline at end of file diff --git a/server/cmd/api/main.go b/server/cmd/api/main.go new file mode 100644 index 0000000..696fe6c --- /dev/null +++ b/server/cmd/api/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "log" + "net/http" + "os" + "time" + + "smoop-api/internal/config" + "smoop-api/internal/crypto" + "smoop-api/internal/db" + "smoop-api/internal/models" + "smoop-api/internal/router" + "smoop-api/internal/storage" + + "gorm.io/gorm" +) + +func main() { + // 1) Load config from Vault + // useDev := os.Getenv("CONFIG_MODE") == "dev" + var err error + var cfg *config.Config + // if useDev { + // cfg, err = config.LoadDev() + // } else { + // cfg, err = config.Load() + // } + // if err != nil { + // log.Printf("config: %v", err) + // } + cfg, err = config.LoadDev() + if err != nil { + log.Fatalf("config: %v", err) + } + // 2) DB + gormDB, err := db.Open(cfg.DB.DSN) + if err != nil { + log.Fatalf("db: %v", err) + } + if err := db.AutoMigrate(gormDB); err != nil { + log.Fatalf("migrate: %v", err) + } + + if err := seedAdmin(gormDB); err != nil { + log.Fatalf("seed admin: %v", err) + } + + // 3) MinIO + minioClient, err := storage.NewMinio(cfg.MinIO) + if err != nil { + log.Fatalf("minio: %v", err) + } + if err := storage.EnsureBuckets(minioClient, cfg.MinIO); err != nil { + log.Fatalf("ensure buckets: %v", err) + } + + // 4) Router + engine := router.Build(gormDB, minioClient, cfg) + + srv := &http.Server{ + Addr: ":8080", + Handler: engine, + ReadHeaderTimeout: 10 * time.Second, + } + log.Println("listening on :8080") + log.Fatal(srv.ListenAndServe()) +} + +func seedAdmin(g *gorm.DB) error { + password := os.Getenv("ADMIN_PASSWORD") + if password == "" { + return nil // nothing to do + } + username := os.Getenv("ADMIN_USERNAME") + if username == "" { + username = "admin" + } + var count int64 + if err := g.Model(&models.User{}).Where("username = ?", username).Count(&count).Error; err != nil { + return err + } + if count > 0 { + return nil + } + hash, err := crypto.Hash(password, crypto.DefaultArgon2) + if err != nil { + return err + } + u := models.User{Username: username, Password: hash, Role: models.RoleAdmin} + if err := g.Create(&u).Error; err != nil { + return err + } + log.Printf("created admin user %s", username) + return nil +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..3e4fc2b --- /dev/null +++ b/server/go.mod @@ -0,0 +1,67 @@ +module smoop-api + +go 1.23.0 + +require github.com/gin-gonic/gin v1.10.1 + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/minio/crc64nvme v1.0.2 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/philhofer/fwd v1.2.0 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/tinylib/msgp v1.3.0 // indirect + golang.org/x/sync v0.15.0 // indirect + golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect +) + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/gorilla/websocket v1.5.3 + github.com/hashicorp/vault-client-go v0.4.3 + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/minio/minio-go/v7 v7.0.95 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/postgres v1.6.0 + gorm.io/gorm v1.30.1 +) diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..20988c2 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,157 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= +github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= +github.com/hashicorp/vault-client-go v0.4.3 h1:zG7STGVgn/VK6rnZc0k8PGbfv2x/sJExRKHSUg3ljWc= +github.com/hashicorp/vault-client-go v0.4.3/go.mod h1:4tDw7Uhq5XOxS1fO+oMtotHL7j4sB9cp0T7U6m4FzDY= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg= +github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU= +github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= +github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= +github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4= +gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/server/internal/config/config.go b/server/internal/config/config.go new file mode 100644 index 0000000..fa0be6c --- /dev/null +++ b/server/internal/config/config.go @@ -0,0 +1,205 @@ +package config + +import ( + "fmt" + "os" + "strconv" + "strings" + "time" + + "smoop-api/internal/vault" +) + +type Config struct { + DB struct { + DSN string + } + MinIO struct { + Endpoint string + AccessKey string + SecretKey string + UseSSL bool + RecordsBucket string + LivestreamBucket string + PresignTTL time.Duration + } + JWTSecret []byte +} + +func Load() (*Config, error) { + addr := os.Getenv("VAULT_ADDR") + token := os.Getenv("VAULT_TOKEN") + + // New style: explicit KV v2 mount + key + mount := os.Getenv("VAULT_KV_MOUNT") // e.g. "kv" or "secret" + key := os.Getenv("VAULT_KV_KEY") // e.g. "snoop" + + // Back-compat: allow legacy VAULT_KV_PATH like "kv/data/snoop" and derive mount+key + if (mount == "" || key == "") && os.Getenv("VAULT_KV_PATH") != "" { + legacy := strings.Trim(os.Getenv("VAULT_KV_PATH"), "/") + parts := strings.Split(legacy, "/") + if len(parts) >= 2 { + mount = parts[0] + key = parts[len(parts)-1] + } + } + + if addr == "" || token == "" || mount == "" || key == "" { + return nil, fmt.Errorf("VAULT_ADDR, VAULT_TOKEN, VAULT_KV_MOUNT and VAULT_KV_KEY must be set (or provide legacy VAULT_KV_PATH)") + } + + raw, err := vault.ReadKVv2(addr, token, mount, key) + if err != nil { + return nil, err + } + + getStr := func(k string) (string, error) { + v, ok := raw[k].(string) + if !ok || v == "" { + return "", fmt.Errorf("missing secret key: %s", k) + } + return v, nil + } + getBool := func(k string) (bool, error) { + v, ok := raw[k] + if !ok { + return false, fmt.Errorf("missing secret key: %s", k) + } + switch t := v.(type) { + case bool: + return t, nil + case string: + if t == "true" || t == "1" { + return true, nil + } + return false, nil + default: + return false, fmt.Errorf("invalid bool for key %s", k) + } + } + + dbDSN, err := getStr("db_dsn") + if err != nil { + return nil, err + } + endpoint, err := getStr("minio_endpoint") + if err != nil { + return nil, err + } + ak, err := getStr("minio_access_key") + if err != nil { + return nil, err + } + sk, err := getStr("minio_secret_key") + if err != nil { + return nil, err + } + useSSL, err := getBool("minio_use_ssl") + if err != nil { + return nil, err + } + jwt, err := getStr("jwt_secret") + if err != nil { + return nil, err + } + + recordsBucket := "records" + if v, ok := raw["minio_records_bucket"].(string); ok && v != "" { + recordsBucket = v + } + liveBucket := "livestream" + if v, ok := raw["minio_livestream_bucket"].(string); ok && v != "" { + liveBucket = v + } + presignTTL := 15 * time.Minute + if v, ok := raw["minio_presign_ttl_seconds"].(string); ok && v != "" { + var sec int + fmt.Sscanf(v, "%d", &sec) + if sec > 0 { + presignTTL = time.Duration(sec) * time.Second + } + } + + cfg := &Config{} + cfg.DB.DSN = dbDSN + cfg.MinIO.Endpoint = endpoint + cfg.MinIO.AccessKey = ak + cfg.MinIO.SecretKey = sk + cfg.MinIO.UseSSL = useSSL + cfg.MinIO.RecordsBucket = recordsBucket + cfg.MinIO.LivestreamBucket = liveBucket + cfg.MinIO.PresignTTL = presignTTL + cfg.JWTSecret = []byte(jwt) + return cfg, nil +} + +func LoadDev() (*Config, error) { + getRequired := func(k string) (string, error) { + v := os.Getenv(k) + if v == "" { + return "", fmt.Errorf("missing required env %s", k) + } + return v, nil + } + getBoolEnv := func(k string, def bool) bool { + v := strings.ToLower(strings.TrimSpace(os.Getenv(k))) + if v == "true" || v == "1" || v == "yes" { + return true + } + if v == "false" || v == "0" || v == "no" { + return false + } + return def + } + getIntEnv := func(k string, def int) int { + if v := strings.TrimSpace(os.Getenv(k)); v != "" { + if n, err := strconv.Atoi(v); err == nil { + return n + } + } + return def + } + dbDSN, err := getRequired("DB_DSN") + if err != nil { + return nil, err + } + endpoint, err := getRequired("MINIO_ENDPOINT") + if err != nil { + return nil, err + } + ak, err := getRequired("MINIO_ACCESS_KEY") + if err != nil { + return nil, err + } + sk, err := getRequired("MINIO_SECRET_KEY") + if err != nil { + return nil, err + } + jwt, err := getRequired("JWT_SECRET") + if err != nil { + return nil, err + } + + useSSL := getBoolEnv("MINIO_USE_SSL", false) + recordsBucket := os.Getenv("MINIO_RECORDS_BUCKET") + if recordsBucket == "" { + recordsBucket = "records" + } + liveBucket := os.Getenv("MINIO_LIVESTREAM_BUCKET") + if liveBucket == "" { + liveBucket = "livestream" + } + presignTTL := time.Duration(getIntEnv("MINIO_PRESIGN_TTL_SECONDS", 900)) * time.Second + + cfg := &Config{} + cfg.DB.DSN = dbDSN + cfg.MinIO.Endpoint = endpoint + cfg.MinIO.AccessKey = ak + cfg.MinIO.SecretKey = sk + cfg.MinIO.UseSSL = useSSL + cfg.MinIO.RecordsBucket = recordsBucket + cfg.MinIO.LivestreamBucket = liveBucket + cfg.MinIO.PresignTTL = presignTTL + cfg.JWTSecret = []byte(jwt) + return cfg, nil +} diff --git a/server/internal/crypto/argon2id.go b/server/internal/crypto/argon2id.go new file mode 100644 index 0000000..5bcb16e --- /dev/null +++ b/server/internal/crypto/argon2id.go @@ -0,0 +1,102 @@ +package crypto + +import ( + "crypto/rand" + "crypto/subtle" + "encoding/base64" + "errors" + "fmt" + "strconv" + "strings" + + "golang.org/x/crypto/argon2" +) + +type Argon2Params struct { + Memory uint32 // KiB + Time uint32 + Threads uint8 + SaltLen uint32 + KeyLen uint32 +} + +var DefaultArgon2 = Argon2Params{ + Memory: 7168, // 7 MiB + Time: 5, + Threads: 1, + SaltLen: 16, + KeyLen: 32, +} + +// Hash returns PHC string: $argon2id$v=19$m=...,t=...,p=...$$ +func Hash(password string, p Argon2Params) (string, error) { + salt := make([]byte, p.SaltLen) + if _, err := rand.Read(salt); err != nil { + return "", err + } + sum := argon2.IDKey([]byte(password), salt, p.Time, p.Memory, p.Threads, p.KeyLen) + return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s", + p.Memory, p.Time, p.Threads, + base64.RawStdEncoding.EncodeToString(salt), + base64.RawStdEncoding.EncodeToString(sum), + ), nil +} + +func Verify(password, phc string) (bool, error) { + parts := strings.Split(phc, "$") + // Expect: ["", "argon2id", "v=19", "m=...,t=...,p=...", "", ""] + if len(parts) != 6 || parts[1] != "argon2id" || parts[2] != "v=19" { + return false, errors.New("invalid PHC header") + } + + // parse params + var mem, iters uint64 + var threads uint64 + for _, kv := range strings.Split(parts[3], ",") { + if strings.HasPrefix(kv, "m=") { + v := strings.TrimPrefix(kv, "m=") + x, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return false, fmt.Errorf("mem: %w", err) + } + mem = x + } else if strings.HasPrefix(kv, "t=") { + v := strings.TrimPrefix(kv, "t=") + x, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return false, fmt.Errorf("time: %w", err) + } + iters = x + } else if strings.HasPrefix(kv, "p=") { + v := strings.TrimPrefix(kv, "p=") + x, err := strconv.ParseUint(v, 10, 8) + if err != nil { + return false, fmt.Errorf("threads: %w", err) + } + threads = x + } + } + if mem == 0 || iters == 0 || threads == 0 { + return false, errors.New("invalid PHC params") + } + + salt, err := b64DecodeFlex(parts[4]) + if err != nil { + return false, fmt.Errorf("salt decode: %w", err) + } + + want, err := b64DecodeFlex(parts[5]) + if err != nil { + return false, fmt.Errorf("hash decode: %w", err) + } + + got := argon2.IDKey([]byte(password), salt, uint32(iters), uint32(mem), uint8(threads), uint32(len(want))) + return subtle.ConstantTimeCompare(got, want) == 1, nil +} + +func b64DecodeFlex(s string) ([]byte, error) { + if b, err := base64.RawStdEncoding.DecodeString(s); err == nil { + return b, nil + } + return base64.StdEncoding.DecodeString(s) // padded fallback +} diff --git a/server/internal/crypto/jwt.go b/server/internal/crypto/jwt.go new file mode 100644 index 0000000..04e822b --- /dev/null +++ b/server/internal/crypto/jwt.go @@ -0,0 +1,30 @@ +package crypto + +import ( + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type JWTManager struct { + secret []byte +} + +func NewJWT(secret []byte) *JWTManager { + return &JWTManager{secret: secret} +} + +func (j *JWTManager) Generate(userID uint, username, role string) (string, error) { + t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": userID, + "name": username, + "role": role, + "iat": time.Now().Unix(), + "exp": time.Now().Add(24 * time.Hour).Unix(), + }) + return t.SignedString(j.secret) +} + +func (j *JWTManager) Parse(tok string) (*jwt.Token, error) { + return jwt.Parse(tok, func(t *jwt.Token) (interface{}, error) { return j.secret, nil }) +} diff --git a/server/internal/db/db.go b/server/internal/db/db.go new file mode 100644 index 0000000..5204c51 --- /dev/null +++ b/server/internal/db/db.go @@ -0,0 +1,20 @@ +package db + +import ( + "gorm.io/driver/postgres" + "gorm.io/gorm" + + "smoop-api/internal/models" +) + +func Open(dsn string) (*gorm.DB, error) { + return gorm.Open(postgres.Open(dsn), &gorm.Config{}) +} + +func AutoMigrate(db *gorm.DB) error { + return db.AutoMigrate( + &models.User{}, + &models.Device{}, + &models.Record{}, + ) +} diff --git a/server/internal/dto/auth.go b/server/internal/dto/auth.go new file mode 100644 index 0000000..4fbe3e4 --- /dev/null +++ b/server/internal/dto/auth.go @@ -0,0 +1,20 @@ +package dto + +type AuthDto struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` +} + +type AccessTokenDto struct { + AccessToken string `json:"accessToken"` +} + +type ChangePasswordDto struct { + UserID uint `json:"userId,omitempty"` + OldPassword string `json:"oldPassword"` + NewPassword string `json:"newPassword" binding:"required"` +} + +type CheckTokenResultDto struct { + IsValid bool `json:"isValid"` +} diff --git a/server/internal/dto/device.go b/server/internal/dto/device.go new file mode 100644 index 0000000..ce77fa4 --- /dev/null +++ b/server/internal/dto/device.go @@ -0,0 +1,56 @@ +package dto + +import "smoop-api/internal/models" + +type DeviceDto struct { + GUID string `json:"guid"` + Name string `json:"name"` + Users []UserDto `json:"users,omitempty"` +} + +type DeviceListDto struct { + Devices []DeviceDto `json:"devices"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Total int64 `json:"total"` +} + +type CreateDeviceDto struct { + GUID string `json:"guid" binding:"required"` + Name string `json:"name" binding:"required"` + UserIDs []uint `json:"userIds"` +} + +type RenameDeviceDto struct { + Name string `json:"name" binding:"required"` +} + +type EditDeviceToUserRelationDto struct { + UserID uint `json:"userId"` + UserIDs []uint `json:"userIds"` +} + +// Remove users: accept one or many (kept separate for clarity of intent) +type RemoveDeviceUsersDto struct { + UserID uint `json:"userId"` + UserIDs []uint `json:"userIds"` +} + +// Replace users: set entire list (may be empty to clear all) +type SetDeviceUsersDto struct { + UserIDs []uint `json:"userIds"` +} + +func MapDevice(d models.Device) DeviceDto { + out := DeviceDto{ + GUID: d.GUID, + Name: d.Name, + } + if len(d.Users) > 0 { + out.Users = make([]UserDto, 0, len(d.Users)) + for _, u := range d.Users { + out.Users = append(out.Users, MapUser(u)) + } + } + return out +} diff --git a/server/internal/dto/record.go b/server/internal/dto/record.go new file mode 100644 index 0000000..04b4f9e --- /dev/null +++ b/server/internal/dto/record.go @@ -0,0 +1,14 @@ +package dto + +type RecordDto struct { + ID uint `json:"id"` + StartedAt int64 `json:"startedAt"` + StoppedAt int64 `json:"stoppedAt"` +} + +type RecordListDto struct { + Records []RecordDto `json:"records"` + Offset int `json:"offset"` + Limit int `json:"limit"` + Total int64 `json:"total"` +} diff --git a/server/internal/dto/user.go b/server/internal/dto/user.go new file mode 100644 index 0000000..a140e44 --- /dev/null +++ b/server/internal/dto/user.go @@ -0,0 +1,29 @@ +package dto + +import "smoop-api/internal/models" + +type UserDto struct { + ID uint `json:"id"` + Username string `json:"username"` + Role models.Role `json:"role"` +} + +type UserRoleDto struct { + Role models.Role `json:"role" binding:"required,oneof=admin user"` +} + +// CreateUserDto is used by POST /users/create to create a new user with a role. +// Role must be one of: "admin" | "user". +type CreateUserDto struct { + Username string `json:"username" binding:"required"` + Password string `json:"password" binding:"required"` + Role string `json:"role" binding:"required,oneof=admin user"` +} + +func MapUser(u models.User) UserDto { + return UserDto{ + ID: u.ID, + Username: u.Username, + Role: u.Role, + } +} diff --git a/server/internal/handlers/auth.go b/server/internal/handlers/auth.go new file mode 100644 index 0000000..22b4067 --- /dev/null +++ b/server/internal/handlers/auth.go @@ -0,0 +1,117 @@ +package handlers + +import ( + "log" + "net/http" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "smoop-api/internal/crypto" + "smoop-api/internal/dto" + "smoop-api/internal/models" +) + +type AuthHandler struct { + db *gorm.DB + jwtMgr *crypto.JWTManager +} + +func NewAuthHandler(db *gorm.DB, jwt *crypto.JWTManager) *AuthHandler { + return &AuthHandler{db: db, jwtMgr: jwt} +} + +func (h *AuthHandler) SignUp(c *gin.Context) { + var req dto.AuthDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + hash, err := crypto.Hash(req.Password, crypto.DefaultArgon2) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "hash failed"}) + return + } + u := models.User{Username: req.Username, Password: hash, Role: models.RoleUser} + if err := h.db.Create(&u).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "username exists"}) + return + } + tok, _ := h.jwtMgr.Generate(u.ID, u.Username, string(u.Role)) + c.JSON(http.StatusCreated, dto.AccessTokenDto{AccessToken: tok}) +} + +func (h *AuthHandler) SignIn(c *gin.Context) { + var req dto.AuthDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var u models.User + if err := h.db.Where("username = ?", req.Username).First(&u).Error; err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials - login"}) + return + } + ok, verr := crypto.Verify(req.Password, u.Password) + if verr != nil { + log.Printf("verify error: %v", verr) // keep log-only in prod + } + if !ok { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials - password"}) + return + } + tok, _ := h.jwtMgr.Generate(u.ID, u.Username, string(u.Role)) + c.JSON(http.StatusCreated, dto.AccessTokenDto{AccessToken: tok}) +} + +func (h *AuthHandler) ChangePassword(c *gin.Context) { + var req dto.ChangePasswordDto + if err := c.ShouldBindJSON(&req); err != nil || req.NewPassword == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) + return + } + claims := MustClaims(c) + currentUID := ClaimUserID(claims) + isAdmin := ClaimRole(claims) == "admin" + + targetID := currentUID + if req.UserID != 0 { + if !isAdmin && req.UserID != currentUID { + c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"}) + return + } + targetID = req.UserID + } + + var u models.User + if err := h.db.First(&u, targetID).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) + return + } + + if !isAdmin { + ok, _ := crypto.Verify(req.OldPassword, u.Password) + if !ok { + c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid old password"}) + return + } + } + + hash, _ := crypto.Hash(req.NewPassword, crypto.DefaultArgon2) + u.Password = hash + if err := h.db.Save(&u).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "update failed"}) + return + } + c.Status(http.StatusCreated) +} + +func (h *AuthHandler) CheckToken(c *gin.Context) { + var req dto.AccessTokenDto + if err := c.ShouldBindJSON(&req); err != nil || req.AccessToken == "" { + c.JSON(http.StatusBadRequest, gin.H{"isValid": false}) + return + } + _, err := h.jwtMgr.Parse(req.AccessToken) + c.JSON(http.StatusCreated, gin.H{"isValid": err == nil}) +} diff --git a/server/internal/handlers/devices.go b/server/internal/handlers/devices.go new file mode 100644 index 0000000..da95db0 --- /dev/null +++ b/server/internal/handlers/devices.go @@ -0,0 +1,250 @@ +package handlers + +import ( + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "smoop-api/internal/dto" + "smoop-api/internal/models" +) + +type DevicesHandler struct { + db *gorm.DB +} + +func NewDevicesHandler(db *gorm.DB) *DevicesHandler { return &DevicesHandler{db: db} } + +func (h *DevicesHandler) List(c *gin.Context) { + offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0")) + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50")) + if limit <= 0 || limit > 200 { + limit = 50 + } + + var total int64 + h.db.Model(&models.Device{}).Count(&total) + + var devs []models.Device + if err := h.db.Preload("Users").Offset(offset).Limit(limit).Find(&devs).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"}) + return + } + + out := make([]dto.DeviceDto, 0, len(devs)) + for _, d := range devs { + out = append(out, dto.MapDevice(d)) + } + c.JSON(http.StatusOK, dto.DeviceListDto{Devices: out, Offset: offset, Limit: limit, Total: total}) +} + +func (h *DevicesHandler) Create(c *gin.Context) { + var req dto.CreateDeviceDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + d := models.Device{GUID: req.GUID, Name: req.Name} + if err := h.db.Create(&d).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "device exists?"}) + return + } + // Optional initial user assignments + if len(req.UserIDs) > 0 { + users, err := h.fetchUsers(req.UserIDs) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if err := h.db.Model(&d).Association("Users").Append(&users); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"}) + return + } + } + // Return with users + var withUsers models.Device + if err := h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error; err != nil { + c.JSON(http.StatusCreated, dto.DeviceDto{GUID: d.GUID, Name: d.Name}) + return + } + c.JSON(http.StatusCreated, dto.MapDevice(withUsers)) +} + +func (h *DevicesHandler) Rename(c *gin.Context) { + guid := c.Param("guid") + var d models.Device + if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "device not found"}) + return + } + var req dto.RenameDeviceDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + d.Name = req.Name + if err := h.db.Save(&d).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"}) + return + } + c.JSON(http.StatusCreated, dto.DeviceDto{GUID: d.GUID, Name: d.Name}) +} + +func (h *DevicesHandler) AddToUser(c *gin.Context) { + guid := c.Param("guid") + var d models.Device + if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "device not found"}) + return + } + var req dto.EditDeviceToUserRelationDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + ids := req.UserIDs + if req.UserID != 0 { + ids = append(ids, req.UserID) + } + if len(ids) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "userIds or userId required"}) + return + } + + users, err := h.fetchUsers(ids) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if err := h.db.Model(&d).Association("Users").Append(&users); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"}) + return + } + var withUsers models.Device + _ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error + c.JSON(http.StatusCreated, dto.MapDevice(withUsers)) +} + +// SetUsers replaces the users of a device with the provided list. +// Passing an empty list clears all assignments (covers the "no user assigned" case). +func (h *DevicesHandler) SetUsers(c *gin.Context) { + guid := c.Param("guid") + var d models.Device + if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "device not found"}) + return + } + var req dto.SetDeviceUsersDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + // Load users (if any) + users := []models.User{} + if len(req.UserIDs) > 0 { + found, err := h.fetchUsers(req.UserIDs) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + users = found + } + // Replace association: Clear() then Append(new) + if err := h.db.Model(&d).Association("Users").Clear(); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "clear failed"}) + return + } + if len(users) > 0 { + if err := h.db.Model(&d).Association("Users").Append(&users); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "link failed"}) + return + } + } + var withUsers models.Device + _ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error + c.JSON(http.StatusCreated, dto.MapDevice(withUsers)) +} + +func (h *DevicesHandler) RemoveFromUser(c *gin.Context) { + guid := c.Param("guid") + var d models.Device + if err := h.db.Where("guid = ?", guid).First(&d).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "device not found"}) + return + } + var req dto.RemoveDeviceUsersDto + _ = c.ShouldBindJSON(&req) // ignore error; we support query fallback + + ids := make([]uint, 0, len(req.UserIDs)+1) + if req.UserID != 0 { + ids = append(ids, req.UserID) + } + if len(req.UserIDs) > 0 { + ids = append(ids, req.UserIDs...) + } + // query fallback + if len(ids) == 0 { + if q := strings.TrimSpace(c.Query("userId")); q != "" { + if n, err := strconv.Atoi(q); err == nil && n > 0 { + ids = append(ids, uint(n)) + } + } + if q := strings.TrimSpace(c.Query("userIds")); q != "" { + for _, p := range strings.Split(q, ",") { + p = strings.TrimSpace(p) + if n, err := strconv.Atoi(p); err == nil && n > 0 { + ids = append(ids, uint(n)) + } + } + } + } + if len(ids) == 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": "userIds or userId required"}) + return + } + + users, err := h.fetchUsers(ids) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if len(users) == 0 { + c.JSON(http.StatusOK, dto.DeviceDto{GUID: d.GUID, Name: d.Name}) + return + } + if err := h.db.Model(&d).Association("Users").Delete(&users); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "unlink failed"}) + return + } + var withUsers models.Device + _ = h.db.Preload("Users").Where("guid = ?", d.GUID).First(&withUsers).Error + c.JSON(http.StatusOK, dto.MapDevice(withUsers)) +} + +func (h *DevicesHandler) fetchUsers(ids []uint) ([]models.User, error) { + unique := make(map[uint]struct{}, len(ids)) + clean := make([]uint, 0, len(ids)) + for _, id := range ids { + if id != 0 { + if _, ok := unique[id]; !ok { + unique[id] = struct{}{} + clean = append(clean, id) + } + } + } + if len(clean) == 0 { + return nil, nil + } + var users []models.User + if err := h.db.Find(&users, clean).Error; err != nil { + return nil, err + } + if len(users) != len(clean) { + return nil, fmt.Errorf("some users not found") + } + return users, nil +} diff --git a/server/internal/handlers/helpers.go b/server/internal/handlers/helpers.go new file mode 100644 index 0000000..7ae9e13 --- /dev/null +++ b/server/internal/handlers/helpers.go @@ -0,0 +1,78 @@ +package handlers + +import ( + "net/http" + "smoop-api/internal/crypto" + "strings" + + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" +) + +func Auth(jwtMgr *crypto.JWTManager) gin.HandlerFunc { + return func(c *gin.Context) { + h := c.GetHeader("Authorization") + if !strings.HasPrefix(h, "Bearer ") { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing bearer token"}) + return + } + tok := strings.TrimPrefix(h, "Bearer ") + token, err := jwtMgr.Parse(tok) + if err != nil || !token.Valid { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid token"}) + return + } + claims, _ := token.Claims.(jwt.MapClaims) + c.Set("claims", claims) + c.Next() + } +} + +func RequireRole(role string) gin.HandlerFunc { + return func(c *gin.Context) { + claims := MustClaims(c) + if ClaimRole(claims) != role { + c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "forbidden"}) + return + } + c.Next() + } +} + +// helpers used by handlers +func MustClaims(c *gin.Context) map[string]interface{} { + val, ok := c.Get("claims") + if !ok { + return jwt.MapClaims{} + } + switch t := val.(type) { + case jwt.MapClaims: + return t + case map[string]interface{}: + return jwt.MapClaims(t) + default: + return jwt.MapClaims{} + } +} +func ClaimUserID(claims map[string]interface{}) uint { + if claims == nil { + return 0 + } + if v, ok := claims["sub"]; ok { + switch n := v.(type) { + case float64: + return uint(n) + case int: + return uint(n) + case int64: + return uint(n) + } + } + return 0 +} +func ClaimRole(claims map[string]interface{}) string { + if r, ok := claims["role"].(string); ok { + return r + } + return "" +} diff --git a/server/internal/handlers/livestream.go b/server/internal/handlers/livestream.go new file mode 100644 index 0000000..ada050b --- /dev/null +++ b/server/internal/handlers/livestream.go @@ -0,0 +1,211 @@ +package handlers + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "github.com/minio/minio-go/v7" +) + +// --- Minimal in-file hub (per-GUID), fanout to viewers and upload to MinIO --- + +type stream struct { + GUID string + writer io.WriteCloser + active bool + object string + mu sync.RWMutex + view map[*viewer]struct{} +} + +// Close implements io.WriteCloser. +func (s *stream) Close() error { + panic("unimplemented") +} + +func (s *stream) Write(p []byte) (int, error) { + s.mu.RLock() + defer s.mu.RUnlock() + if !s.active { + return 0, fmt.Errorf("not active") + } + for v := range s.view { + select { + case v.out <- p: + default: + } + } + return s.writer.Write(p) +} + +type viewer struct{ out chan []byte } + +type liveHub struct { + mu sync.RWMutex + streams map[string]*stream + minio *minio.Client + bucket string +} + +func newLiveHub(mc *minio.Client, bucket string) *liveHub { + return &liveHub{streams: map[string]*stream{}, minio: mc, bucket: bucket} +} + +func (h *liveHub) start(guid string) (io.WriteCloser, string, error) { + h.mu.Lock() + defer h.mu.Unlock() + + s, ok := h.streams[guid] + if !ok { + s = &stream{GUID: guid, view: map[*viewer]struct{}{}} + h.streams[guid] = s + } + if s.active { + return nil, "", fmt.Errorf("already active") + } + key := fmt.Sprintf("%s/live_%d.raw", guid, time.Now().Unix()) + + pr, pw := io.Pipe() + s.writer = pw + s.object = key + s.active = true + + go func(reader io.Reader, bucket, object string) { + // naive buffering for simplicity + buf := new(bytes.Buffer) + _, _ = io.Copy(buf, reader) + _, _ = h.minio.PutObject(context.Background(), bucket, object, bytes.NewReader(buf.Bytes()), int64(buf.Len()), minio.PutObjectOptions{ + ContentType: "application/octet-stream", + }) + }(pr, h.bucket, key) + + return s, key, nil +} + +func (h *liveHub) stop(guid string) (string, error) { + h.mu.Lock() + defer h.mu.Unlock() + + s, ok := h.streams[guid] + if !ok || !s.active { + return "", fmt.Errorf("not active") + } + _ = s.writer.Close() + s.active = false + return s.object, nil +} + +func (h *liveHub) join(guid string) *viewer { + h.mu.Lock() + defer h.mu.Unlock() + + s, ok := h.streams[guid] + if !ok { + s = &stream{GUID: guid, view: map[*viewer]struct{}{}} + h.streams[guid] = s + } + v := &viewer{out: make(chan []byte, 64)} + s.view[v] = struct{}{} + return v +} + +func (h *liveHub) leave(guid string, v *viewer) { + h.mu.Lock() + defer h.mu.Unlock() + + if s, ok := h.streams[guid]; ok { + delete(s.view, v) + close(v.out) + } +} + +// --- Gin handler --- + +type LivestreamHandler struct { + hub *liveHub +} + +func NewLivestreamHandler(minio *minio.Client, bucket string) *LivestreamHandler { + return &LivestreamHandler{hub: newLiveHub(minio, bucket)} +} + +var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} + +type wsMsg struct { + Type string `json:"type"` // "start", "stop" + Role string `json:"role"` // "device" or "viewer" + GUID string `json:"guid"` // required + Format string `json:"format"` // optional +} + +func (h *LivestreamHandler) Upgrade(c *gin.Context) { + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + defer conn.Close() + + for { + var m wsMsg + if err := conn.ReadJSON(&m); err != nil { + return + } + if m.GUID == "" { + _ = conn.WriteJSON(gin.H{"error": "guid required"}) + continue + } + switch m.Role { + case "device": + switch m.Type { + case "start": + w, object, err := h.hub.start(m.GUID) + if err != nil { + _ = conn.WriteJSON(gin.H{"error": err.Error()}) + continue + } + _ = conn.WriteJSON(gin.H{"ok": true, "object": object}) + // read binary frames until stop/close + for { + mt, data, err := conn.ReadMessage() + if err != nil { + h.hub.stop(m.GUID) + return + } + if mt == websocket.BinaryMessage { + _, _ = w.Write(data) + } else if mt == websocket.TextMessage { + // best-effort: if a text control announcing stop arrives + var ctrl wsMsg + if err := conn.ReadJSON(&ctrl); err == nil && ctrl.Type == "stop" { + h.hub.stop(m.GUID) + return + } + } + } + case "stop": + _, _ = h.hub.stop(m.GUID) + _ = conn.WriteJSON(gin.H{"ok": true}) + default: + _ = conn.WriteJSON(gin.H{"error": "unknown type"}) + } + case "viewer": + v := h.hub.join(m.GUID) + defer h.hub.leave(m.GUID, v) + _ = conn.WriteJSON(gin.H{"ok": true}) + for frame := range v.out { + if err := conn.WriteMessage(websocket.BinaryMessage, frame); err != nil { + return + } + } + default: + _ = conn.WriteJSON(gin.H{"error": "unknown role"}) + } + } +} diff --git a/server/internal/handlers/records.go b/server/internal/handlers/records.go new file mode 100644 index 0000000..6110f64 --- /dev/null +++ b/server/internal/handlers/records.go @@ -0,0 +1,123 @@ +package handlers + +import ( + "context" + "fmt" + "mime/multipart" + "net/http" + "path" + "strconv" + "time" + + "github.com/gin-gonic/gin" + "github.com/minio/minio-go/v7" + "gorm.io/gorm" + + "smoop-api/internal/dto" + "smoop-api/internal/models" +) + +type RecordsHandler struct { + db *gorm.DB + minio *minio.Client + recordsBucket string + presignTTL time.Duration +} + +func NewRecordsHandler(db *gorm.DB, mc *minio.Client, bucket string, ttl time.Duration) *RecordsHandler { + return &RecordsHandler{db: db, minio: mc, recordsBucket: bucket, presignTTL: ttl} +} + +func (h *RecordsHandler) Upload(c *gin.Context) { + file, err := c.FormFile("file") + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "file required"}) + return + } + guid := c.PostForm("guid") + if guid == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "guid required"}) + return + } + startedAt, _ := strconv.ParseInt(c.PostForm("startedAt"), 10, 64) + stoppedAt, _ := strconv.ParseInt(c.PostForm("stoppedAt"), 10, 64) + + var dev models.Device + if err := h.db.Where("guid = ?", guid).First(&dev).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "device not found"}) + return + } + + objKey := fmt.Sprintf("%s/%d_%s", guid, time.Now().UnixNano(), path.Base(file.Filename)) + if err := h.putFile(c, file, h.recordsBucket, objKey); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "upload failed"}) + return + } + + rec := models.Record{ + DeviceGUID: dev.GUID, + StartedAt: startedAt, + StoppedAt: stoppedAt, + ObjectKey: objKey, + } + if err := h.db.Create(&rec).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "db save failed"}) + return + } + c.JSON(http.StatusCreated, dto.RecordDto{ID: rec.ID, StartedAt: rec.StartedAt, StoppedAt: rec.StoppedAt}) +} + +func (h *RecordsHandler) putFile(c *gin.Context, fh *multipart.FileHeader, bucket, object string) error { + src, err := fh.Open() + if err != nil { + return err + } + defer src.Close() + _, err = h.minio.PutObject(c, bucket, object, src, fh.Size, minio.PutObjectOptions{ + ContentType: fh.Header.Get("Content-Type"), + ContentDisposition: "attachment; filename=" + fh.Filename, + }) + return err +} + +func (h *RecordsHandler) List(c *gin.Context) { + guid := c.Query("guid") + if guid == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "guid is required"}) + return + } + offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0")) + limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50")) + if limit <= 0 || limit > 200 { + limit = 50 + } + + var total int64 + h.db.Model(&models.Record{}).Where("device_guid = ?", guid).Count(&total) + + var recs []models.Record + if err := h.db.Where("device_guid = ?", guid).Order("id desc").Offset(offset).Limit(limit).Find(&recs).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"}) + return + } + out := make([]dto.RecordDto, 0, len(recs)) + for _, r := range recs { + out = append(out, dto.RecordDto{ID: r.ID, StartedAt: r.StartedAt, StoppedAt: r.StoppedAt}) + } + c.JSON(http.StatusOK, dto.RecordListDto{Records: out, Offset: offset, Limit: limit, Total: total}) +} + +func (h *RecordsHandler) File(c *gin.Context) { + id, _ := strconv.Atoi(c.Param("id")) + var rec models.Record + if err := h.db.First(&rec, id).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) + return + } + u, err := h.minio.PresignedGetObject(context.Background(), h.recordsBucket, rec.ObjectKey, h.presignTTL, nil) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "presign failed"}) + return + } + c.Redirect(http.StatusFound, u.String()) +} diff --git a/server/internal/handlers/users.go b/server/internal/handlers/users.go new file mode 100644 index 0000000..bfb7b92 --- /dev/null +++ b/server/internal/handlers/users.go @@ -0,0 +1,98 @@ +package handlers + +import ( + "net/http" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "smoop-api/internal/crypto" + "smoop-api/internal/dto" + "smoop-api/internal/models" +) + +type UsersHandler struct { + db *gorm.DB +} + +func NewUsersHandler(db *gorm.DB) *UsersHandler { return &UsersHandler{db: db} } + +func (h *UsersHandler) Profile(c *gin.Context) { + claims := MustClaims(c) + uid := ClaimUserID(claims) + + var u models.User + if err := h.db.First(&u, uid).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) + return + } + c.JSON(http.StatusOK, dto.MapUser(u)) +} + +func (h *UsersHandler) SetRole(c *gin.Context) { + idStr := c.Param("id") + uid, _ := strconv.Atoi(idStr) + + var u models.User + if err := h.db.First(&u, uid).Error; err != nil { + c.JSON(http.StatusNotFound, gin.H{"error": "user not found"}) + return + } + var req dto.UserRoleDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + u.Role = req.Role + if err := h.db.Save(&u).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "save failed"}) + return + } + c.JSON(http.StatusCreated, dto.MapUser(u)) +} + +func (h *UsersHandler) List(c *gin.Context) { + var users []models.User + if err := h.db.Order("id asc").Find(&users).Error; err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"}) + return + } + out := make([]dto.UserDto, 0, len(users)) + for _, u := range users { + out = append(out, dto.MapUser(u)) + } + c.JSON(http.StatusOK, out) +} + +// POST /users/create (admin) — create user with given role +func (h *UsersHandler) Create(c *gin.Context) { + var req dto.CreateUserDto + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + role := models.Role(strings.ToLower(req.Role)) + if role != models.RoleAdmin && role != models.RoleUser { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid role"}) + return + } + hash, err := crypto.Hash(req.Password, crypto.DefaultArgon2) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "hash error"}) + return + } + u := models.User{Username: req.Username, Password: hash, Role: role} + if err := h.db.Create(&u).Error; err != nil { + // hint duplicate username + e := strings.ToLower(err.Error()) + if strings.Contains(e, "duplicate") || strings.Contains(e, "unique") || strings.Contains(e, "exists") { + c.JSON(http.StatusBadRequest, gin.H{"error": "username already exists"}) + return + } + c.JSON(http.StatusInternalServerError, gin.H{"error": "create failed"}) + return + } + c.JSON(http.StatusCreated, dto.MapUser(u)) +} diff --git a/server/internal/models/device.go b/server/internal/models/device.go new file mode 100644 index 0000000..7f12013 --- /dev/null +++ b/server/internal/models/device.go @@ -0,0 +1,12 @@ +package models + +import "time" + +type Device struct { + GUID string `gorm:"primaryKey"` + Name string `gorm:"size:255;not null"` + Users []User `gorm:"many2many:user_devices;joinForeignKey:ID;joinReferences:GUID"` + Records []Record `gorm:"foreignKey:DeviceGUID;references:GUID;constraint:OnDelete:CASCADE"` + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/server/internal/models/record.go b/server/internal/models/record.go new file mode 100644 index 0000000..ebe7c40 --- /dev/null +++ b/server/internal/models/record.go @@ -0,0 +1,13 @@ +package models + +import "time" + +type Record struct { + ID uint `gorm:"primaryKey"` + DeviceGUID string `gorm:"index;size:64;not null"` + StartedAt int64 `gorm:"not null"` + StoppedAt int64 `gorm:"not null"` + ObjectKey string `gorm:"size:512;not null"` + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/server/internal/models/user.go b/server/internal/models/user.go new file mode 100644 index 0000000..ea79792 --- /dev/null +++ b/server/internal/models/user.go @@ -0,0 +1,20 @@ +package models + +import "time" + +type Role string + +const ( + RoleAdmin Role = "admin" + RoleUser Role = "user" +) + +type User struct { + ID uint `gorm:"primaryKey"` + Username string `gorm:"uniqueIndex;size:255;not null"` + Password string `gorm:"not null"` + Role Role `gorm:"type:varchar(16);not null;default:'user'"` + Devices []Device `gorm:"many2many:user_devices;joinForeignKey:ID;joinReferences:GUID"` + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/server/internal/router/router.go b/server/internal/router/router.go new file mode 100644 index 0000000..99c2702 --- /dev/null +++ b/server/internal/router/router.go @@ -0,0 +1,67 @@ +package router + +import ( + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/minio/minio-go/v7" + "gorm.io/gorm" + + "smoop-api/internal/config" + "smoop-api/internal/crypto" + "smoop-api/internal/handlers" +) + +func Build(db *gorm.DB, minio *minio.Client, cfg *config.Config) *gin.Engine { + r := gin.Default() + + jwtMgr := crypto.NewJWT(cfg.JWTSecret) + + // --- Handlers + authH := handlers.NewAuthHandler(db, jwtMgr) + usersH := handlers.NewUsersHandler(db) + devH := handlers.NewDevicesHandler(db) + recH := handlers.NewRecordsHandler(db, minio, cfg.MinIO.RecordsBucket, cfg.MinIO.PresignTTL) + liveH := handlers.NewLivestreamHandler(minio, cfg.MinIO.LivestreamBucket) + + // --- Public auth + r.POST("/auth/signup", authH.SignUp) + r.POST("/auth/signin", authH.SignIn) + r.POST("/auth/check_token", authH.CheckToken) + + // Protected + authMW := handlers.Auth(jwtMgr) + adminOnly := handlers.RequireRole("admin") + + r.POST("/auth/change_password", authMW, authH.ChangePassword) + + r.GET("/users/profile", authMW, usersH.Profile) + r.POST("/users/:id/set_role", authMW, adminOnly, usersH.SetRole) + r.GET("/users", authMW, adminOnly, usersH.List) + r.POST("/users/create", authMW, adminOnly, usersH.Create) + + r.GET("/devices", authMW, devH.List) + r.POST("/devices/create", authMW, devH.Create) + r.POST("/devices/:guid/rename", authMW, devH.Rename) + r.POST("/devices/:guid/add_to_user", authMW, devH.AddToUser) + r.POST("/devices/:guid/set_users", authMW, adminOnly, devH.SetUsers) + r.POST("/devices/:guid/remove_from_user", authMW, devH.RemoveFromUser) + + r.POST("/records/upload", authMW, recH.Upload) + r.GET("/records", authMW, recH.List) + r.GET("/records/:id/file", authMW, recH.File) + + // WebSocket livestream + r.GET("/livestream", authMW, liveH.Upgrade) + + // health + r.GET("/healthz", func(c *gin.Context) { c.String(http.StatusOK, "ok") }) + + // sensible defaults + r.MaxMultipartMemory = 64 << 20 // 64 MiB + _ = time.Now() // appease linters + return r +} + +// --- JWT middleware & helpers (kept here to avoid new dirs) --- diff --git a/server/internal/storage/minio.go b/server/internal/storage/minio.go new file mode 100644 index 0000000..34e9ca1 --- /dev/null +++ b/server/internal/storage/minio.go @@ -0,0 +1,60 @@ +package storage + +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" +) + +type MinIOConfig struct { + Endpoint string + AccessKey string + SecretKey string + UseSSL bool + RecordsBucket string + LivestreamBucket string + PresignTTL time.Duration +} + +func cleanEndpoint(ep string) string { + ep = strings.TrimSpace(ep) + if ep == "" { + return ep + } + // If a scheme is present, strip it. + if u, err := url.Parse(ep); err == nil && u.Host != "" { + ep = u.Host // drops scheme and any path/query + } + // Remove any trailing slash that might remain. + ep = strings.TrimSuffix(ep, "/") + return ep +} + +func NewMinio(c MinIOConfig) (*minio.Client, error) { + endpoint := cleanEndpoint(c.Endpoint) + return minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(c.AccessKey, c.SecretKey, ""), + Secure: c.UseSSL, + }) +} + +func EnsureBuckets(mc *minio.Client, c MinIOConfig) error { + ctx := context.Background() + for _, b := range []string{c.RecordsBucket, c.LivestreamBucket} { + exists, err := mc.BucketExists(ctx, b) + if err != nil { + return err + } + if !exists { + if err := mc.MakeBucket(ctx, b, minio.MakeBucketOptions{}); err != nil { + return fmt.Errorf("make bucket %s: %w", b, err) + } + } + } + return nil +} diff --git a/server/internal/vault/vault.go b/server/internal/vault/vault.go new file mode 100644 index 0000000..d3131ff --- /dev/null +++ b/server/internal/vault/vault.go @@ -0,0 +1,41 @@ +package vault + +import ( + "context" + "fmt" + "time" + + vault "github.com/hashicorp/vault-client-go" +) + +func ReadKVv2(addr, token, mountPath, key string) (map[string]any, error) { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + client, err := vault.New( + vault.WithAddress(addr), + vault.WithRequestTimeout(30*time.Second), + ) + if err != nil { + return nil, fmt.Errorf("vault new: %w", err) + } + if err := client.SetToken(token); err != nil { + return nil, fmt.Errorf("set token: %w", err) + } + + resp, err := client.Secrets.KvV2Read(ctx, key, vault.WithMountPath(mountPath)) + if err != nil { + return nil, err + } + if resp == nil || resp.Data.Data == nil { + return nil, fmt.Errorf("vault: empty response for %s/%s", mountPath, key) + } + return resp.Data.Data, nil +} + +// tiny typed error +type ErrNotFound string + +func (e ErrNotFound) Error() string { + return "vault: secret not found at " + string(e) +} diff --git a/traefik/.env b/traefik/.env new file mode 100644 index 0000000..0db0d97 --- /dev/null +++ b/traefik/.env @@ -0,0 +1 @@ +TRAEFIK_DASHBOARD_CREDENTIALS=admin:$$2y$$05$$PWLMUBMexlqDK6yXZvKWFeFaMULMlVb4Fp7.XUvFpvVhuqxeVLeMq \ No newline at end of file diff --git a/traefik/config/config.yaml b/traefik/config/config.yaml new file mode 100644 index 0000000..2bb9d51 --- /dev/null +++ b/traefik/config/config.yaml @@ -0,0 +1,19 @@ +http: + middlewares: + default-security-headers: + headers: + customBrowserXSSValue: 0 # X-XSS-Protection=1; mode=block + contentTypeNosniff: true # X-Content-Type-Options=nosniff + forceSTSHeader: true # Add the Strict-Transport-Security header even when the connection is HTTP + frameDeny: false # X-Frame-Options=deny + referrerPolicy: "strict-origin-when-cross-origin" + stsIncludeSubdomains: true # Add includeSubdomains to the Strict-Transport-Security header + stsPreload: true # Add preload flag appended to the Strict-Transport-Security header + stsSeconds: 3153600 # Set the max-age of the Strict-Transport-Security header (63072000 = 2 years) + contentSecurityPolicy: "default-src 'self'" + customRequestHeaders: + X-Forwarded-Proto: https + https-redirectscheme: + redirectScheme: + scheme: https + permanent: true \ No newline at end of file diff --git a/traefik/config/traefik.yaml b/traefik/config/traefik.yaml new file mode 100644 index 0000000..7b30fbc --- /dev/null +++ b/traefik/config/traefik.yaml @@ -0,0 +1,37 @@ +api: + dashboard: true + debug: true +entryPoints: + http: + address: ":80" + http: + # middlewares: # uncomment if using CrowdSec - see my video + # - crowdsec-bouncer@file + redirections: + entryPoint: + to: https + scheme: https + https: + address: ":443" + # http: + # middlewares: # uncomment if using CrowdSec - see my video + # - crowdsec-bouncer@file + # tcp: + # address: ":10000" + # apis: + # address: ":33073" +serversTransport: + insecureSkipVerify: true +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + exposedByDefault: false + file: + filename: /config.yaml # example provided gives A+ rating https://www.ssllabs.com/ssltest/ + + +log: + level: "INFO" + filePath: "/var/log/traefik/traefik.log" +accessLog: + filePath: "/var/log/traefik/access.log" \ No newline at end of file diff --git a/traefik/docker-compose.yaml b/traefik/docker-compose.yaml new file mode 100644 index 0000000..2c70916 --- /dev/null +++ b/traefik/docker-compose.yaml @@ -0,0 +1,49 @@ +services: + traefik: + image: traefik:latest # or pin a specific v3.x version + container_name: traefik + restart: unless-stopped + security_opt: + - no-new-privileges:true + env_file: + - .env # should define TRAEFIK_DASHBOARD_CREDENTIALS + networks: + - proxy + ports: + - "80:80" + - "443:443" + environment: + - TRAEFIK_DASHBOARD_CREDENTIALS=${TRAEFIK_DASHBOARD_CREDENTIALS} + volumes: + - /etc/localtime:/etc/localtime:ro + - ${XDG_RUNTIME_DIR}/podman/podman.sock:/var/run/docker.sock:Z + # Static (static) config + - ./config/traefik.yaml:/traefik.yaml:ro,Z + # Dynamic (file provider) config + - ./config/config.yaml:/config.yaml:ro,Z + # Your self-signed wildcard cert + - ./certs/wildcard.192.168.205.130.nip.io.crt:/certs/wildcard.192.168.205.130.nip.io.crt:ro + - ./certs/wildcard.192.168.205.130.nip.io.key:/certs/wildcard.192.168.205.130.nip.io.key:ro + # Logs + - ./logs:/var/log/traefik:Z + labels: + - "traefik.enable=true" + - "traefik.http.routers.traefik.entrypoints=http" # expose dashboard on HTTP :contentReference[oaicite:2]{index=2} + - "traefik.http.routers.traefik.rule=Host(`traefik.192.168.205.130.nip.io`)" + - "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_DASHBOARD_CREDENTIALS}" + # Redirect HTTP → HTTPS + - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https" + - "traefik.http.routers.traefik.middlewares=traefik-https-redirect" + # Secure (HTTPS) dashboard + - "traefik.http.routers.traefik-secure.entrypoints=https" + - "traefik.http.routers.traefik-secure.rule=Host(`traefik.192.168.205.130.nip.io`)" + - "traefik.http.routers.traefik-secure.middlewares=traefik-auth" + - "traefik.http.routers.traefik-secure.tls=true" + - "traefik.http.routers.traefik-secure.tls.domains[0].main=192.168.205.130.nip.io" + - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.192.168.205.130.nip.io" + - "traefik.http.routers.traefik-secure.service=api@internal" + +networks: + proxy: + external: true +