From 834ecacdb9275b2f8c273b53f021bf33744f947f Mon Sep 17 00:00:00 2001 From: chiyi Date: Thu, 1 Oct 2026 01:58:47 +0800 Subject: [PATCH] cyi-box: Memphis-style toolbox admin platform Go(fun)+SQLite backend, Vue 3 frontend. Modules: dashboard, pay gateway (Epay-compatible + CDK stock), license/card system (per-app appid, domain binding), AI memory bank with built-in MCP server (/mcp, FTS5 search, signed read links), server SSH monitor + terminal + SFTP, proxy pool with protocol-level alive checks, DB manager (mysql/pg/sqlite + SQL autocomplete + import/export), disk share, vault (AES-GCM + TOTP), bookmarks, dev tools. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .gitignore | 10 + LICENSE | 21 + Makefile | 24 + README.md | 209 ++ backend/cmd/gen/main.go | 34 + backend/cmd/server/main.go | 129 + backend/go.mod | 85 + backend/go.sum | 263 ++ backend/internal/config/config.go | 90 + backend/internal/db/db.go | 31 + backend/internal/db/embed.go | 8 + backend/internal/db/models.go | 220 ++ backend/internal/db/querier.go | 136 + backend/internal/db/queries.sql | 427 +++ backend/internal/db/queries.sql.go | 3102 ++++++++++++++++++ backend/internal/db/schema.sql | 228 ++ backend/internal/dbx/dbx.go | 400 +++ backend/internal/dto/dto.go | 755 +++++ backend/internal/memx/memx.go | 276 ++ backend/internal/payx/upstream.go | 612 ++++ backend/internal/proxyx/proxyx.go | 432 +++ backend/internal/service/auth.go | 123 + backend/internal/service/auth_guard.go | 152 + backend/internal/service/bookmark.go | 91 + backend/internal/service/dashboard.go | 114 + backend/internal/service/dbm.go | 242 ++ backend/internal/service/dbm_routes.go | 394 +++ backend/internal/service/disk.go | 163 + backend/internal/service/disk_routes.go | 167 + backend/internal/service/license.go | 203 ++ backend/internal/service/license_routes.go | 82 + backend/internal/service/memory.go | 122 + backend/internal/service/memory_routes.go | 124 + backend/internal/service/pay.go | 867 +++++ backend/internal/service/pay_routes.go | 189 ++ backend/internal/service/proxy.go | 416 +++ backend/internal/service/proxy_routes.go | 105 + backend/internal/service/server.go | 569 ++++ backend/internal/service/setting.go | 39 + backend/internal/service/tool.go | 90 + backend/internal/service/user.go | 147 + backend/internal/service/vault.go | 188 ++ backend/internal/service/webreq.go | 105 + backend/internal/sshx/sshx.go | 295 ++ backend/internal/store/bookmark.go | 73 + backend/internal/store/dbconn.go | 91 + backend/internal/store/disk.go | 148 + backend/internal/store/mem.go | 272 ++ backend/internal/store/proxy.go | 362 ++ backend/internal/store/server.go | 95 + backend/internal/store/setting.go | 20 + backend/internal/store/store.go | 371 +++ backend/internal/store/vault.go | 93 + backend/internal/vault/vault.go | 142 + backend/main | 0 backend/sqlc.yaml | 14 + docs/screenshots/dashboard.png | Bin 0 -> 136502 bytes docs/screenshots/dbm.png | Bin 0 -> 66060 bytes docs/screenshots/licenses.png | Bin 0 -> 176636 bytes docs/screenshots/memories.png | Bin 0 -> 81676 bytes docs/screenshots/pay.png | Bin 0 -> 153685 bytes docs/screenshots/proxies.png | Bin 0 -> 138673 bytes docs/screenshots/servers.png | Bin 0 -> 63762 bytes docs/screenshots/tools-json.png | Bin 0 -> 63510 bytes frontend/index.html | 14 + frontend/package-lock.json | 2439 ++++++++++++++ frontend/package.json | 34 + frontend/src/App.vue | 46 + frontend/src/api/authSvc.ts | 24 + frontend/src/api/bookmarkIdDto.ts | 3 + frontend/src/api/bookmarkSvc.ts | 21 + frontend/src/api/bookmarkView.ts | 7 + frontend/src/api/changePasswordDto.ts | 4 + frontend/src/api/client.ts | 665 ++++ frontend/src/api/createShareDto.ts | 4 + frontend/src/api/dashboardSvc.ts | 12 + frontend/src/api/dbColumnView.ts | 6 + frontend/src/api/dbDatabasesView.ts | 3 + frontend/src/api/dbMgrSvc.ts | 38 + frontend/src/api/dbQueryDto.ts | 6 + frontend/src/api/dbQueryView.ts | 9 + frontend/src/api/dbScopeDto.ts | 4 + frontend/src/api/dbTableView.ts | 4 + frontend/src/api/dbTablesView.ts | 6 + frontend/src/api/dbconnIdDto.ts | 3 + frontend/src/api/dbconnView.ts | 11 + frontend/src/api/deleteToolDto.ts | 3 + frontend/src/api/deleteUserDto.ts | 3 + frontend/src/api/diskMountView.ts | 8 + frontend/src/api/diskSvc.ts | 28 + frontend/src/api/fileView.ts | 7 + frontend/src/api/fun.ts | 43 + frontend/src/api/idDto.ts | 3 + frontend/src/api/licenseAppSaveDto.ts | 5 + frontend/src/api/licenseAppView.ts | 8 + frontend/src/api/licenseAppsView.ts | 4 + frontend/src/api/licenseCardView.ts | 10 + frontend/src/api/licenseGenDto.ts | 5 + frontend/src/api/licenseGenView.ts | 3 + frontend/src/api/licenseIdDto.ts | 3 + frontend/src/api/licenseListDto.ts | 6 + frontend/src/api/licensePageView.ts | 5 + frontend/src/api/licenseStatsDto.ts | 3 + frontend/src/api/licenseStatsView.ts | 6 + frontend/src/api/licenseSvc.ts | 40 + frontend/src/api/listUsersDto.ts | 5 + frontend/src/api/loginDto.ts | 4 + frontend/src/api/memoryKeyDto.ts | 3 + frontend/src/api/memoryListDto.ts | 6 + frontend/src/api/memoryListView.ts | 4 + frontend/src/api/memoryMcpView.ts | 4 + frontend/src/api/memoryMetaView.ts | 8 + frontend/src/api/memoryProjectView.ts | 4 + frontend/src/api/memoryProjectsView.ts | 4 + frontend/src/api/memorySaveDto.ts | 6 + frontend/src/api/memorySaveView.ts | 5 + frontend/src/api/memorySvc.ts | 38 + frontend/src/api/memoryView.ts | 11 + frontend/src/api/okView.ts | 3 + frontend/src/api/payCashierDto.ts | 3 + frontend/src/api/payCashierStatusView.ts | 4 + frontend/src/api/payCashierView.ts | 13 + frontend/src/api/payOrderListDto.ts | 6 + frontend/src/api/payOrderPageView.ts | 5 + frontend/src/api/payOrderView.ts | 14 + frontend/src/api/payStatsView.ts | 9 + frontend/src/api/paySvc.ts | 42 + frontend/src/api/payTestPayDto.ts | 3 + frontend/src/api/payTestPayView.ts | 7 + frontend/src/api/payTrendPoint.ts | 5 + frontend/src/api/payUpstreamDto.ts | 4 + frontend/src/api/payUpstreamStatusView.ts | 18 + frontend/src/api/proxyKeySaveDto.ts | 4 + frontend/src/api/proxyKeyView.ts | 12 + frontend/src/api/proxyListDto.ts | 8 + frontend/src/api/proxyListView.ts | 5 + frontend/src/api/proxyRegionView.ts | 8 + frontend/src/api/proxyStatsView.ts | 9 + frontend/src/api/proxySvc.ts | 44 + frontend/src/api/proxySyncView.ts | 13 + frontend/src/api/proxyView.ts | 20 + frontend/src/api/recentMemoryView.ts | 5 + frontend/src/api/recentOrderView.ts | 6 + frontend/src/api/saveBookmarkDto.ts | 7 + frontend/src/api/saveDbconnDto.ts | 11 + frontend/src/api/saveServerDto.ts | 10 + frontend/src/api/saveToolDto.ts | 9 + frontend/src/api/saveUserDto.ts | 7 + frontend/src/api/saveVaultDto.ts | 9 + frontend/src/api/secretView.ts | 3 + frontend/src/api/serverIdDto.ts | 3 + frontend/src/api/serverMetricsView.ts | 25 + frontend/src/api/serverSvc.ts | 40 + frontend/src/api/serverView.ts | 11 + frontend/src/api/sessionView.ts | 5 + frontend/src/api/settingDto.ts | 4 + frontend/src/api/settingSvc.ts | 17 + frontend/src/api/settingView.ts | 3 + frontend/src/api/sftpEntryView.ts | 7 + frontend/src/api/sftpListView.ts | 5 + frontend/src/api/sftpPathDto.ts | 4 + frontend/src/api/sftpRenameDto.ts | 5 + frontend/src/api/shareView.ts | 9 + frontend/src/api/statsView.ts | 30 + frontend/src/api/toolSvc.ts | 24 + frontend/src/api/toolView.ts | 9 + frontend/src/api/totpView.ts | 4 + frontend/src/api/trendPoint.ts | 4 + frontend/src/api/ts/authSvc.ts | 24 + frontend/src/api/ts/bookmarkIdDto.ts | 3 + frontend/src/api/ts/bookmarkSvc.ts | 21 + frontend/src/api/ts/bookmarkView.ts | 7 + frontend/src/api/ts/changePasswordDto.ts | 4 + frontend/src/api/ts/client.ts | 665 ++++ frontend/src/api/ts/createShareDto.ts | 4 + frontend/src/api/ts/dashboardSvc.ts | 12 + frontend/src/api/ts/dbColumnView.ts | 6 + frontend/src/api/ts/dbDatabasesView.ts | 3 + frontend/src/api/ts/dbMgrSvc.ts | 38 + frontend/src/api/ts/dbQueryDto.ts | 6 + frontend/src/api/ts/dbQueryView.ts | 9 + frontend/src/api/ts/dbScopeDto.ts | 4 + frontend/src/api/ts/dbTableView.ts | 4 + frontend/src/api/ts/dbTablesView.ts | 6 + frontend/src/api/ts/dbconnIdDto.ts | 3 + frontend/src/api/ts/dbconnView.ts | 11 + frontend/src/api/ts/deleteToolDto.ts | 3 + frontend/src/api/ts/deleteUserDto.ts | 3 + frontend/src/api/ts/diskMountView.ts | 8 + frontend/src/api/ts/diskSvc.ts | 28 + frontend/src/api/ts/fileView.ts | 7 + frontend/src/api/ts/fun.ts | 43 + frontend/src/api/ts/idDto.ts | 3 + frontend/src/api/ts/licenseAppSaveDto.ts | 5 + frontend/src/api/ts/licenseAppView.ts | 8 + frontend/src/api/ts/licenseAppsView.ts | 4 + frontend/src/api/ts/licenseCardView.ts | 10 + frontend/src/api/ts/licenseGenDto.ts | 5 + frontend/src/api/ts/licenseGenView.ts | 3 + frontend/src/api/ts/licenseIdDto.ts | 3 + frontend/src/api/ts/licenseListDto.ts | 6 + frontend/src/api/ts/licensePageView.ts | 5 + frontend/src/api/ts/licenseStatsDto.ts | 3 + frontend/src/api/ts/licenseStatsView.ts | 6 + frontend/src/api/ts/licenseSvc.ts | 40 + frontend/src/api/ts/listUsersDto.ts | 5 + frontend/src/api/ts/loginDto.ts | 4 + frontend/src/api/ts/memoryKeyDto.ts | 3 + frontend/src/api/ts/memoryListDto.ts | 6 + frontend/src/api/ts/memoryListView.ts | 4 + frontend/src/api/ts/memoryMcpView.ts | 4 + frontend/src/api/ts/memoryMetaView.ts | 8 + frontend/src/api/ts/memoryProjectView.ts | 4 + frontend/src/api/ts/memoryProjectsView.ts | 4 + frontend/src/api/ts/memorySaveDto.ts | 6 + frontend/src/api/ts/memorySaveView.ts | 5 + frontend/src/api/ts/memorySvc.ts | 38 + frontend/src/api/ts/memoryView.ts | 11 + frontend/src/api/ts/okView.ts | 3 + frontend/src/api/ts/payCashierDto.ts | 3 + frontend/src/api/ts/payCashierStatusView.ts | 4 + frontend/src/api/ts/payCashierView.ts | 13 + frontend/src/api/ts/payOrderListDto.ts | 6 + frontend/src/api/ts/payOrderPageView.ts | 5 + frontend/src/api/ts/payOrderView.ts | 14 + frontend/src/api/ts/payStatsView.ts | 9 + frontend/src/api/ts/paySvc.ts | 42 + frontend/src/api/ts/payTestPayDto.ts | 3 + frontend/src/api/ts/payTestPayView.ts | 7 + frontend/src/api/ts/payTrendPoint.ts | 5 + frontend/src/api/ts/payUpstreamDto.ts | 4 + frontend/src/api/ts/payUpstreamStatusView.ts | 18 + frontend/src/api/ts/proxyKeySaveDto.ts | 4 + frontend/src/api/ts/proxyKeyView.ts | 12 + frontend/src/api/ts/proxyListDto.ts | 8 + frontend/src/api/ts/proxyListView.ts | 5 + frontend/src/api/ts/proxyRegionView.ts | 8 + frontend/src/api/ts/proxyStatsView.ts | 9 + frontend/src/api/ts/proxySvc.ts | 44 + frontend/src/api/ts/proxySyncView.ts | 13 + frontend/src/api/ts/proxyView.ts | 20 + frontend/src/api/ts/recentMemoryView.ts | 5 + frontend/src/api/ts/recentOrderView.ts | 6 + frontend/src/api/ts/saveBookmarkDto.ts | 7 + frontend/src/api/ts/saveDbconnDto.ts | 11 + frontend/src/api/ts/saveServerDto.ts | 10 + frontend/src/api/ts/saveToolDto.ts | 9 + frontend/src/api/ts/saveUserDto.ts | 7 + frontend/src/api/ts/saveVaultDto.ts | 9 + frontend/src/api/ts/secretView.ts | 3 + frontend/src/api/ts/serverIdDto.ts | 3 + frontend/src/api/ts/serverMetricsView.ts | 25 + frontend/src/api/ts/serverSvc.ts | 40 + frontend/src/api/ts/serverView.ts | 11 + frontend/src/api/ts/sessionView.ts | 5 + frontend/src/api/ts/settingDto.ts | 4 + frontend/src/api/ts/settingSvc.ts | 17 + frontend/src/api/ts/settingView.ts | 3 + frontend/src/api/ts/sftpEntryView.ts | 7 + frontend/src/api/ts/sftpListView.ts | 5 + frontend/src/api/ts/sftpPathDto.ts | 4 + frontend/src/api/ts/sftpRenameDto.ts | 5 + frontend/src/api/ts/shareView.ts | 9 + frontend/src/api/ts/statsView.ts | 30 + frontend/src/api/ts/toolSvc.ts | 24 + frontend/src/api/ts/toolView.ts | 9 + frontend/src/api/ts/totpView.ts | 4 + frontend/src/api/ts/trendPoint.ts | 4 + frontend/src/api/ts/userPageView.ts | 5 + frontend/src/api/ts/userSvc.ts | 23 + frontend/src/api/ts/userView.ts | 7 + frontend/src/api/ts/vaultIdDto.ts | 3 + frontend/src/api/ts/vaultItemView.ts | 10 + frontend/src/api/ts/vaultSvc.ts | 29 + frontend/src/api/ts/webFetchDto.ts | 6 + frontend/src/api/ts/webFetchView.ts | 7 + frontend/src/api/ts/webSvc.ts | 13 + frontend/src/api/userPageView.ts | 5 + frontend/src/api/userSvc.ts | 23 + frontend/src/api/userView.ts | 7 + frontend/src/api/vaultIdDto.ts | 3 + frontend/src/api/vaultItemView.ts | 10 + frontend/src/api/vaultSvc.ts | 29 + frontend/src/api/webFetchDto.ts | 6 + frontend/src/api/webFetchView.ts | 7 + frontend/src/api/webSvc.ts | 13 + frontend/src/layouts/AdminLayout.vue | 207 ++ frontend/src/lib/api.ts | 38 + frontend/src/lib/icons.ts | 56 + frontend/src/lib/kit.ts | 44 + frontend/src/main.ts | 10 + frontend/src/router/index.ts | 187 ++ frontend/src/stores/auth.ts | 44 + frontend/src/stores/bookmarks.ts | 20 + frontend/src/stores/servers.ts | 23 + frontend/src/style.css | 639 ++++ frontend/src/views/Bookmarks.vue | 175 + frontend/src/views/Cashier.vue | 149 + frontend/src/views/Dashboard.vue | 281 ++ frontend/src/views/DbManager.vue | 608 ++++ frontend/src/views/Disk.vue | 247 ++ frontend/src/views/DiskShares.vue | 108 + frontend/src/views/LicenseApps.vue | 174 + frontend/src/views/Licenses.vue | 249 ++ frontend/src/views/Login.vue | 107 + frontend/src/views/Memories.vue | 504 +++ frontend/src/views/PayConfig.vue | 356 ++ frontend/src/views/PayOrders.vue | 209 ++ frontend/src/views/Proxies.vue | 338 ++ frontend/src/views/ProxyExtract.vue | 185 ++ frontend/src/views/ServerDetail.vue | 537 +++ frontend/src/views/Servers.vue | 213 ++ frontend/src/views/Settings.vue | 64 + frontend/src/views/Vault.vue | 311 ++ frontend/src/views/tools/ColorImage.vue | 260 ++ frontend/src/views/tools/ColorTool.vue | 265 ++ frontend/src/views/tools/GradientTool.vue | 156 + frontend/src/views/tools/JsonTool.vue | 135 + frontend/src/views/tools/MarkdownTool.vue | 124 + frontend/src/views/tools/PostmanTool.vue | 274 ++ frontend/src/views/tools/TimestampTool.vue | 232 ++ frontend/src/vite-env.d.ts | 7 + frontend/tsconfig.app.json | 21 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 11 + frontend/vite.config.ts | 19 + 326 files changed, 27813 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 backend/cmd/gen/main.go create mode 100644 backend/cmd/server/main.go create mode 100644 backend/go.mod create mode 100644 backend/go.sum create mode 100644 backend/internal/config/config.go create mode 100644 backend/internal/db/db.go create mode 100644 backend/internal/db/embed.go create mode 100644 backend/internal/db/models.go create mode 100644 backend/internal/db/querier.go create mode 100644 backend/internal/db/queries.sql create mode 100644 backend/internal/db/queries.sql.go create mode 100644 backend/internal/db/schema.sql create mode 100644 backend/internal/dbx/dbx.go create mode 100644 backend/internal/dto/dto.go create mode 100644 backend/internal/memx/memx.go create mode 100644 backend/internal/payx/upstream.go create mode 100644 backend/internal/proxyx/proxyx.go create mode 100644 backend/internal/service/auth.go create mode 100644 backend/internal/service/auth_guard.go create mode 100644 backend/internal/service/bookmark.go create mode 100644 backend/internal/service/dashboard.go create mode 100644 backend/internal/service/dbm.go create mode 100644 backend/internal/service/dbm_routes.go create mode 100644 backend/internal/service/disk.go create mode 100644 backend/internal/service/disk_routes.go create mode 100644 backend/internal/service/license.go create mode 100644 backend/internal/service/license_routes.go create mode 100644 backend/internal/service/memory.go create mode 100644 backend/internal/service/memory_routes.go create mode 100644 backend/internal/service/pay.go create mode 100644 backend/internal/service/pay_routes.go create mode 100644 backend/internal/service/proxy.go create mode 100644 backend/internal/service/proxy_routes.go create mode 100644 backend/internal/service/server.go create mode 100644 backend/internal/service/setting.go create mode 100644 backend/internal/service/tool.go create mode 100644 backend/internal/service/user.go create mode 100644 backend/internal/service/vault.go create mode 100644 backend/internal/service/webreq.go create mode 100644 backend/internal/sshx/sshx.go create mode 100644 backend/internal/store/bookmark.go create mode 100644 backend/internal/store/dbconn.go create mode 100644 backend/internal/store/disk.go create mode 100644 backend/internal/store/mem.go create mode 100644 backend/internal/store/proxy.go create mode 100644 backend/internal/store/server.go create mode 100644 backend/internal/store/setting.go create mode 100644 backend/internal/store/store.go create mode 100644 backend/internal/store/vault.go create mode 100644 backend/internal/vault/vault.go create mode 100644 backend/main create mode 100644 backend/sqlc.yaml create mode 100644 docs/screenshots/dashboard.png create mode 100644 docs/screenshots/dbm.png create mode 100644 docs/screenshots/licenses.png create mode 100644 docs/screenshots/memories.png create mode 100644 docs/screenshots/pay.png create mode 100644 docs/screenshots/proxies.png create mode 100644 docs/screenshots/servers.png create mode 100644 docs/screenshots/tools-json.png create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/api/authSvc.ts create mode 100644 frontend/src/api/bookmarkIdDto.ts create mode 100644 frontend/src/api/bookmarkSvc.ts create mode 100644 frontend/src/api/bookmarkView.ts create mode 100644 frontend/src/api/changePasswordDto.ts create mode 100644 frontend/src/api/client.ts create mode 100644 frontend/src/api/createShareDto.ts create mode 100644 frontend/src/api/dashboardSvc.ts create mode 100644 frontend/src/api/dbColumnView.ts create mode 100644 frontend/src/api/dbDatabasesView.ts create mode 100644 frontend/src/api/dbMgrSvc.ts create mode 100644 frontend/src/api/dbQueryDto.ts create mode 100644 frontend/src/api/dbQueryView.ts create mode 100644 frontend/src/api/dbScopeDto.ts create mode 100644 frontend/src/api/dbTableView.ts create mode 100644 frontend/src/api/dbTablesView.ts create mode 100644 frontend/src/api/dbconnIdDto.ts create mode 100644 frontend/src/api/dbconnView.ts create mode 100644 frontend/src/api/deleteToolDto.ts create mode 100644 frontend/src/api/deleteUserDto.ts create mode 100644 frontend/src/api/diskMountView.ts create mode 100644 frontend/src/api/diskSvc.ts create mode 100644 frontend/src/api/fileView.ts create mode 100644 frontend/src/api/fun.ts create mode 100644 frontend/src/api/idDto.ts create mode 100644 frontend/src/api/licenseAppSaveDto.ts create mode 100644 frontend/src/api/licenseAppView.ts create mode 100644 frontend/src/api/licenseAppsView.ts create mode 100644 frontend/src/api/licenseCardView.ts create mode 100644 frontend/src/api/licenseGenDto.ts create mode 100644 frontend/src/api/licenseGenView.ts create mode 100644 frontend/src/api/licenseIdDto.ts create mode 100644 frontend/src/api/licenseListDto.ts create mode 100644 frontend/src/api/licensePageView.ts create mode 100644 frontend/src/api/licenseStatsDto.ts create mode 100644 frontend/src/api/licenseStatsView.ts create mode 100644 frontend/src/api/licenseSvc.ts create mode 100644 frontend/src/api/listUsersDto.ts create mode 100644 frontend/src/api/loginDto.ts create mode 100644 frontend/src/api/memoryKeyDto.ts create mode 100644 frontend/src/api/memoryListDto.ts create mode 100644 frontend/src/api/memoryListView.ts create mode 100644 frontend/src/api/memoryMcpView.ts create mode 100644 frontend/src/api/memoryMetaView.ts create mode 100644 frontend/src/api/memoryProjectView.ts create mode 100644 frontend/src/api/memoryProjectsView.ts create mode 100644 frontend/src/api/memorySaveDto.ts create mode 100644 frontend/src/api/memorySaveView.ts create mode 100644 frontend/src/api/memorySvc.ts create mode 100644 frontend/src/api/memoryView.ts create mode 100644 frontend/src/api/okView.ts create mode 100644 frontend/src/api/payCashierDto.ts create mode 100644 frontend/src/api/payCashierStatusView.ts create mode 100644 frontend/src/api/payCashierView.ts create mode 100644 frontend/src/api/payOrderListDto.ts create mode 100644 frontend/src/api/payOrderPageView.ts create mode 100644 frontend/src/api/payOrderView.ts create mode 100644 frontend/src/api/payStatsView.ts create mode 100644 frontend/src/api/paySvc.ts create mode 100644 frontend/src/api/payTestPayDto.ts create mode 100644 frontend/src/api/payTestPayView.ts create mode 100644 frontend/src/api/payTrendPoint.ts create mode 100644 frontend/src/api/payUpstreamDto.ts create mode 100644 frontend/src/api/payUpstreamStatusView.ts create mode 100644 frontend/src/api/proxyKeySaveDto.ts create mode 100644 frontend/src/api/proxyKeyView.ts create mode 100644 frontend/src/api/proxyListDto.ts create mode 100644 frontend/src/api/proxyListView.ts create mode 100644 frontend/src/api/proxyRegionView.ts create mode 100644 frontend/src/api/proxyStatsView.ts create mode 100644 frontend/src/api/proxySvc.ts create mode 100644 frontend/src/api/proxySyncView.ts create mode 100644 frontend/src/api/proxyView.ts create mode 100644 frontend/src/api/recentMemoryView.ts create mode 100644 frontend/src/api/recentOrderView.ts create mode 100644 frontend/src/api/saveBookmarkDto.ts create mode 100644 frontend/src/api/saveDbconnDto.ts create mode 100644 frontend/src/api/saveServerDto.ts create mode 100644 frontend/src/api/saveToolDto.ts create mode 100644 frontend/src/api/saveUserDto.ts create mode 100644 frontend/src/api/saveVaultDto.ts create mode 100644 frontend/src/api/secretView.ts create mode 100644 frontend/src/api/serverIdDto.ts create mode 100644 frontend/src/api/serverMetricsView.ts create mode 100644 frontend/src/api/serverSvc.ts create mode 100644 frontend/src/api/serverView.ts create mode 100644 frontend/src/api/sessionView.ts create mode 100644 frontend/src/api/settingDto.ts create mode 100644 frontend/src/api/settingSvc.ts create mode 100644 frontend/src/api/settingView.ts create mode 100644 frontend/src/api/sftpEntryView.ts create mode 100644 frontend/src/api/sftpListView.ts create mode 100644 frontend/src/api/sftpPathDto.ts create mode 100644 frontend/src/api/sftpRenameDto.ts create mode 100644 frontend/src/api/shareView.ts create mode 100644 frontend/src/api/statsView.ts create mode 100644 frontend/src/api/toolSvc.ts create mode 100644 frontend/src/api/toolView.ts create mode 100644 frontend/src/api/totpView.ts create mode 100644 frontend/src/api/trendPoint.ts create mode 100644 frontend/src/api/ts/authSvc.ts create mode 100644 frontend/src/api/ts/bookmarkIdDto.ts create mode 100644 frontend/src/api/ts/bookmarkSvc.ts create mode 100644 frontend/src/api/ts/bookmarkView.ts create mode 100644 frontend/src/api/ts/changePasswordDto.ts create mode 100644 frontend/src/api/ts/client.ts create mode 100644 frontend/src/api/ts/createShareDto.ts create mode 100644 frontend/src/api/ts/dashboardSvc.ts create mode 100644 frontend/src/api/ts/dbColumnView.ts create mode 100644 frontend/src/api/ts/dbDatabasesView.ts create mode 100644 frontend/src/api/ts/dbMgrSvc.ts create mode 100644 frontend/src/api/ts/dbQueryDto.ts create mode 100644 frontend/src/api/ts/dbQueryView.ts create mode 100644 frontend/src/api/ts/dbScopeDto.ts create mode 100644 frontend/src/api/ts/dbTableView.ts create mode 100644 frontend/src/api/ts/dbTablesView.ts create mode 100644 frontend/src/api/ts/dbconnIdDto.ts create mode 100644 frontend/src/api/ts/dbconnView.ts create mode 100644 frontend/src/api/ts/deleteToolDto.ts create mode 100644 frontend/src/api/ts/deleteUserDto.ts create mode 100644 frontend/src/api/ts/diskMountView.ts create mode 100644 frontend/src/api/ts/diskSvc.ts create mode 100644 frontend/src/api/ts/fileView.ts create mode 100644 frontend/src/api/ts/fun.ts create mode 100644 frontend/src/api/ts/idDto.ts create mode 100644 frontend/src/api/ts/licenseAppSaveDto.ts create mode 100644 frontend/src/api/ts/licenseAppView.ts create mode 100644 frontend/src/api/ts/licenseAppsView.ts create mode 100644 frontend/src/api/ts/licenseCardView.ts create mode 100644 frontend/src/api/ts/licenseGenDto.ts create mode 100644 frontend/src/api/ts/licenseGenView.ts create mode 100644 frontend/src/api/ts/licenseIdDto.ts create mode 100644 frontend/src/api/ts/licenseListDto.ts create mode 100644 frontend/src/api/ts/licensePageView.ts create mode 100644 frontend/src/api/ts/licenseStatsDto.ts create mode 100644 frontend/src/api/ts/licenseStatsView.ts create mode 100644 frontend/src/api/ts/licenseSvc.ts create mode 100644 frontend/src/api/ts/listUsersDto.ts create mode 100644 frontend/src/api/ts/loginDto.ts create mode 100644 frontend/src/api/ts/memoryKeyDto.ts create mode 100644 frontend/src/api/ts/memoryListDto.ts create mode 100644 frontend/src/api/ts/memoryListView.ts create mode 100644 frontend/src/api/ts/memoryMcpView.ts create mode 100644 frontend/src/api/ts/memoryMetaView.ts create mode 100644 frontend/src/api/ts/memoryProjectView.ts create mode 100644 frontend/src/api/ts/memoryProjectsView.ts create mode 100644 frontend/src/api/ts/memorySaveDto.ts create mode 100644 frontend/src/api/ts/memorySaveView.ts create mode 100644 frontend/src/api/ts/memorySvc.ts create mode 100644 frontend/src/api/ts/memoryView.ts create mode 100644 frontend/src/api/ts/okView.ts create mode 100644 frontend/src/api/ts/payCashierDto.ts create mode 100644 frontend/src/api/ts/payCashierStatusView.ts create mode 100644 frontend/src/api/ts/payCashierView.ts create mode 100644 frontend/src/api/ts/payOrderListDto.ts create mode 100644 frontend/src/api/ts/payOrderPageView.ts create mode 100644 frontend/src/api/ts/payOrderView.ts create mode 100644 frontend/src/api/ts/payStatsView.ts create mode 100644 frontend/src/api/ts/paySvc.ts create mode 100644 frontend/src/api/ts/payTestPayDto.ts create mode 100644 frontend/src/api/ts/payTestPayView.ts create mode 100644 frontend/src/api/ts/payTrendPoint.ts create mode 100644 frontend/src/api/ts/payUpstreamDto.ts create mode 100644 frontend/src/api/ts/payUpstreamStatusView.ts create mode 100644 frontend/src/api/ts/proxyKeySaveDto.ts create mode 100644 frontend/src/api/ts/proxyKeyView.ts create mode 100644 frontend/src/api/ts/proxyListDto.ts create mode 100644 frontend/src/api/ts/proxyListView.ts create mode 100644 frontend/src/api/ts/proxyRegionView.ts create mode 100644 frontend/src/api/ts/proxyStatsView.ts create mode 100644 frontend/src/api/ts/proxySvc.ts create mode 100644 frontend/src/api/ts/proxySyncView.ts create mode 100644 frontend/src/api/ts/proxyView.ts create mode 100644 frontend/src/api/ts/recentMemoryView.ts create mode 100644 frontend/src/api/ts/recentOrderView.ts create mode 100644 frontend/src/api/ts/saveBookmarkDto.ts create mode 100644 frontend/src/api/ts/saveDbconnDto.ts create mode 100644 frontend/src/api/ts/saveServerDto.ts create mode 100644 frontend/src/api/ts/saveToolDto.ts create mode 100644 frontend/src/api/ts/saveUserDto.ts create mode 100644 frontend/src/api/ts/saveVaultDto.ts create mode 100644 frontend/src/api/ts/secretView.ts create mode 100644 frontend/src/api/ts/serverIdDto.ts create mode 100644 frontend/src/api/ts/serverMetricsView.ts create mode 100644 frontend/src/api/ts/serverSvc.ts create mode 100644 frontend/src/api/ts/serverView.ts create mode 100644 frontend/src/api/ts/sessionView.ts create mode 100644 frontend/src/api/ts/settingDto.ts create mode 100644 frontend/src/api/ts/settingSvc.ts create mode 100644 frontend/src/api/ts/settingView.ts create mode 100644 frontend/src/api/ts/sftpEntryView.ts create mode 100644 frontend/src/api/ts/sftpListView.ts create mode 100644 frontend/src/api/ts/sftpPathDto.ts create mode 100644 frontend/src/api/ts/sftpRenameDto.ts create mode 100644 frontend/src/api/ts/shareView.ts create mode 100644 frontend/src/api/ts/statsView.ts create mode 100644 frontend/src/api/ts/toolSvc.ts create mode 100644 frontend/src/api/ts/toolView.ts create mode 100644 frontend/src/api/ts/totpView.ts create mode 100644 frontend/src/api/ts/trendPoint.ts create mode 100644 frontend/src/api/ts/userPageView.ts create mode 100644 frontend/src/api/ts/userSvc.ts create mode 100644 frontend/src/api/ts/userView.ts create mode 100644 frontend/src/api/ts/vaultIdDto.ts create mode 100644 frontend/src/api/ts/vaultItemView.ts create mode 100644 frontend/src/api/ts/vaultSvc.ts create mode 100644 frontend/src/api/ts/webFetchDto.ts create mode 100644 frontend/src/api/ts/webFetchView.ts create mode 100644 frontend/src/api/ts/webSvc.ts create mode 100644 frontend/src/api/userPageView.ts create mode 100644 frontend/src/api/userSvc.ts create mode 100644 frontend/src/api/userView.ts create mode 100644 frontend/src/api/vaultIdDto.ts create mode 100644 frontend/src/api/vaultItemView.ts create mode 100644 frontend/src/api/vaultSvc.ts create mode 100644 frontend/src/api/webFetchDto.ts create mode 100644 frontend/src/api/webFetchView.ts create mode 100644 frontend/src/api/webSvc.ts create mode 100644 frontend/src/layouts/AdminLayout.vue create mode 100644 frontend/src/lib/api.ts create mode 100644 frontend/src/lib/icons.ts create mode 100644 frontend/src/lib/kit.ts create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/router/index.ts create mode 100644 frontend/src/stores/auth.ts create mode 100644 frontend/src/stores/bookmarks.ts create mode 100644 frontend/src/stores/servers.ts create mode 100644 frontend/src/style.css create mode 100644 frontend/src/views/Bookmarks.vue create mode 100644 frontend/src/views/Cashier.vue create mode 100644 frontend/src/views/Dashboard.vue create mode 100644 frontend/src/views/DbManager.vue create mode 100644 frontend/src/views/Disk.vue create mode 100644 frontend/src/views/DiskShares.vue create mode 100644 frontend/src/views/LicenseApps.vue create mode 100644 frontend/src/views/Licenses.vue create mode 100644 frontend/src/views/Login.vue create mode 100644 frontend/src/views/Memories.vue create mode 100644 frontend/src/views/PayConfig.vue create mode 100644 frontend/src/views/PayOrders.vue create mode 100644 frontend/src/views/Proxies.vue create mode 100644 frontend/src/views/ProxyExtract.vue create mode 100644 frontend/src/views/ServerDetail.vue create mode 100644 frontend/src/views/Servers.vue create mode 100644 frontend/src/views/Settings.vue create mode 100644 frontend/src/views/Vault.vue create mode 100644 frontend/src/views/tools/ColorImage.vue create mode 100644 frontend/src/views/tools/ColorTool.vue create mode 100644 frontend/src/views/tools/GradientTool.vue create mode 100644 frontend/src/views/tools/JsonTool.vue create mode 100644 frontend/src/views/tools/MarkdownTool.vue create mode 100644 frontend/src/views/tools/PostmanTool.vue create mode 100644 frontend/src/views/tools/TimestampTool.vue create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..776db54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +data/ +*.log +*.db +*.db-shm +*.db-wal +.env +.env.* +backend/cyibox diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..41ebd4f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 cyi-cc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d3873f --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +NODE_BIN := $(HOME)/.local/opt/node-v22.23.2-linux-x64/bin +export PATH := $(NODE_BIN):$(PATH) + +.PHONY: dev backend frontend gen build + +# 前后端一起起:后端 :8890,前端 :5173 +dev: + (cd backend && go run ./cmd/server) & (cd frontend && npm run dev) + +backend: + cd backend && go run ./cmd/server + +frontend: + cd frontend && npm run dev + +# 改了 backend 服务/DTO 后跑这个:sqlc 重新生成 internal/db,再生成 frontend/src/api +gen: + cd backend && go tool sqlc generate && go run ./cmd/gen + # 用 cp 覆盖而非 mv:内容重写才能触发 vite watcher,mv(rename) 会让 dev server 继续喂过期模块 + cp -f frontend/src/api/ts/* frontend/src/api/ && rm -rf frontend/src/api/ts + +build: + cd backend && go build ./... + cd frontend && npm run build diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d3d745 --- /dev/null +++ b/README.md @@ -0,0 +1,209 @@ +# 池易工作箱 · cyi-box + +孟菲斯/新粗野主义风格的个人工作箱 + 管理后台。单二进制 + SQLite 就能跑:支付网关、授权卡密系统、内置 MCP Server 的 AI 共享记忆库、服务器监控、代理池、数据库管理器、网盘、密码箱、书签导航和一套开发者小工具。 + +后端 Go + [fun](https://github.com/cyi-cc/fun) 单端点 RPC + sqlc/SQLite,前端 Vue 3 + Vite + Naive UI + ECharts + CodeMirror。 + +![License](https://img.shields.io/badge/license-MIT-blue) + +## 截图 + +| 仪表盘 | 代理池 | +|---|---| +| ![仪表盘](docs/screenshots/dashboard.png) | ![代理池](docs/screenshots/proxies.png) | +| **订单管理** | **授权卡密** | +| ![订单](docs/screenshots/pay.png) | ![卡密](docs/screenshots/licenses.png) | +| **记忆库** | **数据库管理** | +| ![记忆库](docs/screenshots/memories.png) | ![数据库](docs/screenshots/dbm.png) | + +## 功能一览 + +- **仪表盘**:营收/订单/会话 KPI、模块宫格快捷入口、近 14 天营收趋势(金额+单数双轴)、最近成交与记忆动态 +- **支付网关**:Epay 兼容协议(`submit.php` / `mapi.php` / `api.php`)、商户 PID/Key、商品+CDK 库存(锁定分配、裸 hex 卡密)、二维码收银台、订单状态实时推送(`fun.Stream`)、异步通知重试 +- **授权系统**:项目管理(每个项目独立 `appid`)+ 卡密批量生成;`GET /v1/license` 公开校验——首次激活绑域名、异域名拒绝、0 小时=永久、项目停用全卡失效 +- **记忆库 / MCP Server**:`POST /mcp` Streamable HTTP MCP 端点(Bearer 鉴权),`memory_save/get/search/list/delete/projects` 六个工具 + `memory://` resources + `save_context` prompt;FTS5 trigram 全文搜索(中文子串可命中);`GET /m/?sig=` HMAC 签名阅读链接;Devin/Codex/Claude 共用一座库 +- **服务器管理**:SSH 密码/密钥登录(AES-GCM 加密存储)、CPU/内存/磁盘/网速实时监控、WebSocket 终端(xterm.js)、SFTP 文件管理 +- **代理池**:双数据源每日同步、协议级握手测活(SOCKS4/5、HTTP,裸 TCP 不算数)、大洲→地区分组、提取密钥 `pk_xxx` + `GET /v1/proxy` 随机活代理 API +- **数据库管理器**:MySQL / PostgreSQL / SQLite 连接(密码加密存储)、库表列结构 → CodeMirror SQL 补全、Ctrl+Enter 执行、JSON/CSV/XLSX 导入导出 +- **网盘**:拖拽上传、限时/永久分享链接(`GET /d/` 免登录)、下载计数 +- **密码箱**:AES-GCM 加密存储、TOTP/otpauth 解析与验证码 +- **开发者工具**:JSON 格式化、时间戳转换、Markdown 编辑器、接口调试(服务端代发绕 CORS)、取色板、图片拾色、渐变图鉴 + +## 快速开始 + +环境:Go 1.24+(含 `go tool sqlc`)、Node 20+。 + +```bash +make dev # 后端 :8890 + 前端 :5173(vite 把 /api 代理到后端) +``` + +浏览器开 `http://127.0.0.1:5173`,默认账号 `admin / admin123`(首次启动自动建库建号,用 `CYIBOX_ADMIN_PASSWORD` 覆盖)。 + +生产部署: + +```bash +make build # go build + vite build → backend/cyibox + frontend/dist +``` + +`cyibox` 单二进制即可跑(数据库首启自动创建于 `./data/cyibox.db`);前端 dist 用任意静态服务/反代托管,把 `/api`、`/mcp`、`/m`、`/v1`、`/d` 等前缀转发到后端即可。 + +## 环境变量 + +| 变量 | 默认 | 说明 | +|---|---|---| +| `CYIBOX_PORT` | `8890` | 后端监听端口 | +| `CYIBOX_DB` | `./data/cyibox.db` | SQLite 路径(自动创建) | +| `CYIBOX_ADMIN_NAME` | `admin` | 初始管理员名 | +| `CYIBOX_ADMIN_PASSWORD` | `admin123` | 初始管理员密码(**上线前必改**) | +| `CYIBOX_CORS` | 空 | 跨域白名单(逗号分隔) | +| `CYIBOX_VAULT_KEY` | `/vault.key` | AES-GCM 密钥文件(自动生成) | +| `CYIBOX_DISK_DIR` | `/files` | 网盘文件目录 | + +## MCP 接入(AI 记忆库) + +「记忆库 → 接入 MCP」页面拿端点和令牌,配置到客户端: + +```json +{ + "mcpServers": { + "memory": { + "url": "http://<你的域名或IP>:8890/mcp", + "transport": "http", + "headers": { "Authorization": "Bearer <令牌>" } + } + } +} +``` + +工具:`memory_save`(key 必填,`project/topic` 命名)· `memory_get` · `memory_search`(FTS5)· `memory_list` · `memory_projects` · `memory_delete`;资源 `memory://`;prompt `save_context`。 + +## 开发 + +```bash +make gen # sqlc 重新生成 internal/db + TS 客户端(改了 service/DTO/SQL 后跑) +make build # go build + vite build +``` + +## 结构 + +``` +backend/ Go 后端(module github.com/cyi-cc/cyi-box/backend) + sqlc.yaml sqlc 配置(engine sqlite) + cmd/server/ 服务入口 + cmd/gen/ TS 客户端生成(BindServiceForGen,不碰数据库) + internal/config/ 环境变量配置 + internal/db/ schema.sql + queries.sql + sqlc 生成物(勿手改 *.sql.go / models.go) + internal/store/ 数据门面层:委托 db.Queries + 会话续期/种子等定制逻辑 + internal/sshx/ SSH 拨号 + 指标采集(/proc 两采样) + internal/vault/ AES-GCM 字段加密 + TOTP/otpauth 解析 + internal/dto/ RPC DTO(定宽整型/指针可空,见 fun 文档) + internal/service/ AuthGuard 策略表 + 各 Svc + SFTP/终端自定义路由 +frontend/ Vue 3 + Vite + Naive UI + Pinia + vue-router + src/api/ fun GenTs 生成产物(勿手改,跑 make gen 重新生成) + src/lib/api.ts client 封装:token 注入 state、4011 统一跳登录 + src/layouts/ 后台布局(左侧导航:通用工具静态组 + 书签组) + src/views/ 登录/仪表盘/用户/密码箱/书签/服务器,tools/ 下为内置工具页 +``` + +## 启动 + +```bash +make dev # 后端 :8890 + 前端 :5173(vite 代理 /api → /cell) +``` + +首次启动自动建库并种子 `admin / admin123`(用 `CYIBOX_ADMIN_PASSWORD` 改掉)。 +可选环境变量:`CYIBOX_PORT`、`CYIBOX_DB`、`CYIBOX_ADMIN_NAME`、`CYIBOX_CORS`。 + +## 常用 + +```bash +make gen # sqlc 重新生成 internal/db + 重新生成 TS 客户端 +make build # go build + vite build +``` + +## 数据层约定 + +- 表结构与查询写在 `internal/db/schema.sql` / `queries.sql`,`go tool sqlc generate` 生成类型安全代码; +- `internal/store` 是门面层:服务层统一调它,sqlc 表达不了的(会话滑动续期、映射)在这里实现; +- 注意:sqlc 的 sqlite 解析器对 `.sql` 注释里的 `/`、`:` 有兼容问题,注释只写 `-- name:` 行; +- 建表语句以 `schema.sql` 为准,运行时经 `db.SchemaDDL` 自动迁移。 + +## 服务器管理 + +- 密钥/私钥 AES-GCM 加密存 `servers.secret_enc`(复用 vault.key,可用 `CYIBOX_VAULT_KEY` 指定); +- 指标:SSH 跑一段 shell 两采样 `/proc/stat` + `/proc/net/dev` + `df`,得 CPU%/内存/磁盘/网速率; +- SFTP:RPC 列目录/建目录/删/改名,上传 `POST /api/files/upload`、下载 `GET /api/files/download`; +- 终端:`GET /api/ws/terminal` 升级 WebSocket → SSH PTY(xterm.js),query `token` 鉴权(仅 admin)。 + +## 通用工具 + +侧边栏「通用工具」组内置 6 个小工具: + +- `/tools/json` JSON 格式化/压缩/校验,左右分栏 + 语法高亮 +- `/tools/timestamp` 时间戳转换:实时钟、单位自动识别(s/ms/µs/ns)、多时区、相对时间、批量转换 +- `/tools/markdown` Markdown 编辑 + 实时预览(markdown-it,html 关闭防 XSS) +- `/tools/postman` 接口调试:`WebSvc.Fetch` 服务端代发绕开 CORS,30s 超时/5 次重定向/响应 2MB 截断,历史存 localStorage +- `/tools/color` 取色板:HSL 色球(角度=色相/半径=饱和/明度滑杆)、图片上传拾色 + 主色提取、明度阶梯、配色方案 +- `/tools/gradient` 渐变图鉴:预设 + 随机 + 自定义,一键复制 CSS + +## 网盘 + +- 文件落盘 `/files/`(`CYIBOX_DISK_DIR` 可改),`files` 表记录原始名/大小/mime; +- 上传 `POST /disk/upload`(multipart,登录用户)、属主下载 `GET /disk/download?id=&token=`; +- 分享:`shares` 表存 code + 到期时间(0=永久),公开下载 `GET /d/` 免登录, + 过期返回 410 页面,命中计 downloads;删文件连带删分享与磁盘文件; +- 前端 `/disk`:拖拽/点选上传、文件行内分享弹窗(永久/10分~7天/自定义分钟)、链接列表显示剩余时间。 + +## 数据库管理器 + +- 支持 mysql / postgres / sqlite 连接,密码 AES-GCM 存 `dbconns.password_enc`; +- 库/表/列 introspection 喂给 CodeMirror sql 补全;Ctrl+Enter 执行(选中片段优先); +- 查询 20s 超时、行数封顶(默认 500,上限 10000);按首关键字判定是否结果集语句; +- 导出 `POST /api/dbm/export`:json/csv/xlsx(结果集最大 5 万行); +- 导入 `POST /api/dbm/import`:json 对象数组 / csv / xlsx,首行表头与表列按名交集,逐行插入、坏行跳过计数,上限 1 万行。 + +## 代理池 + +- 数据源两个,任一失败不致命(失败源名下的代理本轮不删): + - `GET https://proxy5.net/api/free-proxies.php?v=<30min窗口>`(页面 JS 的接口,直连会被 + Cloudflare 拦:`proxyx.Fetch` 带浏览器 UA/Referer/gzip,403 时先拉列表页热 cookie 再重试); + - `GET https://rola-ip.co/proxy-api/api/v1/proxies?page=N&pageSize=500`(`proxyx.FetchRola`, + 页面前端走的公开前缀,免登录,500/页翻到底,大洲由内置国家码映射补); +- 多源按 ip:port 合并(协议并集),`proxies.source` 记录归属源;删除规则=没出现在任何成功源里才删; +- `proxy_regions`(code 如 SG/NL + 中文名 + 大洲)与 `proxies`(region_id 外键,ip:port 唯一); +- 同步:启动时若无数据或上次超过 20h 补一次,之后每日 00:00(本地时区)全量 diff—— + 源上消失即删、已存在刷新指标复活、新增入库;`proxy_syncs` 记录 added/removed/updated 与明细 + (各最多 200 个地址); +- 测活:每小时一轮,对距上次检测 >55min 的存活代理做**协议级握手**(SOCKS5 greeting+CONNECT / + SOCKS4 / HTTP GET 转发——裸 TCP 连通不算数,防透明网关误判),并发 128、4s 超时; + 失败标 alive=0,死记录 7 天后物理清除; +- 页面 `/proxies`:左栏大洲→地区分类(存活数),右侧筛选/搜索/分页列表,底部同步记录可展开变更明细; +- `ProxySvc`:List/Regions/Stats 登录即可,Syncs/SyncNow/CheckNow 仅 admin。 + +## 代理提取 API + +- `/proxies/extract` 页生成密钥(`pk_xxx`,存 `proxy_keys` 表,绑定地区或「全部」),删除即吊销; +- `GET /v1/proxy?key=<密钥>` 免登录:随机抽 15 个存活候选**并行**协议级探活(3s 超时), + 先活先回一行 `ip:port` 纯文本;死的顺手标 `alive=0`,最多 4 批 / 20s 总超时,全死 503; +- 每次成功提取 `used_count+1`、`last_used_at` 更新。 + +## 系统设置 + +- `/settings`(admin):修改密码。`SettingSvc`(Get 登录可读 / Set 仅 admin,`settings` KV 表) + 已就位,暂无前端消费方,后续站点级开关可挂在这。 + +## 加一个新工具页 + +1. `frontend/src/views/tools/` 下写页面,路由加在 `router/index.ts` 的 children 里; +2. 侧边栏 `AdminLayout.vue` 的「通用工具」组里加一项; +3. 需要后端时:`service/` 里建 Svc(嵌 `fun.Ctx`,方法 `DTO → (View, error)`), + 在 `auth_guard.go` 的策略表登记端点,`cmd/server` 与 `cmd/gen` 各加一行注册, + 然后 `make gen`。注意:svc 结构体字段必须全部导出(fun autowired 反射赋值,未导出字段会 panic), + 包级单例放普通变量即可。 + +## 鉴权约定 + +- 前端 token 存 localStorage,请求拦截器写 `state.token`; +- `AuthGuard` 按「服务.方法」策略表鉴权(缺省拒绝),会话存 SQLite,24h 滑动过期(闲置 2h 后的下次请求自动续回 24h); +- 业务错误 `fun.Error(code, msg)`;`4011` = 登录失效,前端统一跳登录页; +- SFTP/终端等自定义路由不走 AuthGuard,`routeAdmin` 手动校验 query token。 diff --git a/backend/cmd/gen/main.go b/backend/cmd/gen/main.go new file mode 100644 index 0000000..583850e --- /dev/null +++ b/backend/cmd/gen/main.go @@ -0,0 +1,34 @@ +// Command gen 生成前端 TS 客户端:BindServiceForGen 只反射方法签名, +// 不装配数据库等基础设施,离线可用。用法:cd backend && go run ./cmd/gen +package main + +import ( + "log" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/service" +) + +func main() { + f := fun.GetFun() + f.BindServiceForGen(&service.AuthSvc{}) + f.BindServiceForGen(&service.UserSvc{}) + f.BindServiceForGen(&service.DashboardSvc{}) + f.BindServiceForGen(&service.ToolSvc{}) + f.BindServiceForGen(&service.VaultSvc{}) + f.BindServiceForGen(&service.BookmarkSvc{}) + f.BindServiceForGen(&service.ServerSvc{}) + f.BindServiceForGen(&service.DbMgrSvc{}) + f.BindServiceForGen(&service.WebSvc{}) + f.BindServiceForGen(&service.DiskSvc{}) + f.BindServiceForGen(&service.ProxySvc{}) + f.BindServiceForGen(&service.SettingSvc{}) + f.BindServiceForGen(&service.PaySvc{}) + f.BindServiceForGen(&service.LicenseSvc{}) + f.BindServiceForGen(&service.MemorySvc{}) + + fun.SetOutput("../frontend/src/api") + fun.GenCode(fun.GenTs{}) + log.Println("gen: TS client written to frontend/src/api/ts") +} diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go new file mode 100644 index 0000000..8eddcd2 --- /dev/null +++ b/backend/cmd/server/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "context" + "log" + "os" + "os/signal" + "syscall" + "time" + + "github.com/cyi-cc/fun" + "golang.org/x/crypto/bcrypt" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/payx" + "github.com/cyi-cc/cyi-box/backend/internal/service" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// mustWired 装配失败即退出:基础设施缺一不可,粘性错误由 fun 容器返回 +func mustWired[T any](v *T, err error) *T { + if err != nil { + log.Fatalf("boot: wired %T failed: %v", v, err) + } + return v +} + +func main() { + cfg := mustWired(fun.Wired[config.Config]()) + st := mustWired(fun.Wired[store.Store]()) + + // 首启种子:空库写 admin + 示例工具 + if hash, err := bcrypt.GenerateFromPassword([]byte(cfg.AdminPassword), bcrypt.DefaultCost); err == nil { + if err := st.SeedAdmin(string(hash)); err != nil { + log.Fatalf("boot: seed admin failed: %v", err) + } + } + if err := st.SeedTools(); err != nil { + log.Fatalf("boot: seed tools failed: %v", err) + } + + f := fun.GetFun() + if len(cfg.CorsOrigins) > 0 { + f.CORS(cfg.CorsOrigins...) + } + if err := f.BindGuard(&service.AuthGuard{}); err != nil { + log.Fatalf("boot: bind guard failed: %v", err) + } + for _, err := range []error{ + f.BindService(&service.AuthSvc{}), + f.BindService(&service.UserSvc{}), + f.BindService(&service.DashboardSvc{}), + f.BindService(&service.ToolSvc{}), + f.BindService(&service.VaultSvc{}), + f.BindService(&service.BookmarkSvc{}), + f.BindService(&service.ServerSvc{}), + f.BindService(&service.DbMgrSvc{}), + f.BindService(&service.WebSvc{}), + f.BindService(&service.DiskSvc{}), + f.BindService(&service.ProxySvc{}), + f.BindService(&service.SettingSvc{}), + f.BindService(&service.PaySvc{}), + f.BindService(&service.LicenseSvc{}), + f.BindService(&service.MemorySvc{}), + } { + if err != nil { + log.Fatalf("boot: bind service failed: %v", err) + } + } + + // 支付:上游客户端 + 订单轮询 + 商户通知(单例) + up := mustWired(fun.Wired[payx.Client]()) + mustWired(fun.Wired[service.PayNotifier]()) + mustWired(fun.Wired[service.PayPoller]()) + service.BootPay(st, up, cfg) + + // SFTP 上传放宽请求体上限(默认 4MB → 512MB) + f.SetBodyLimit(512 << 20) + for _, err := range []error{ + f.BindRoute("GET", "/files/download", service.SftpDownloadHandler(st, cfg)), + f.BindRoute("POST", "/files/upload", service.SftpUploadHandler(st, cfg)), + f.BindRoute("GET", "/ws/terminal", service.TerminalHandler(st, cfg)), + f.BindRoute("POST", "/dbm/export", service.DbExportHandler(st, cfg)), + f.BindRoute("POST", "/dbm/import", service.DbImportHandler(st, cfg)), + f.BindRoute("POST", "/disk/upload", service.DiskUploadHandler(st, cfg)), + f.BindRoute("GET", "/disk/download", service.DiskDownloadHandler(st, cfg)), + f.BindRoute("GET", "/d/*", service.ShareDownloadHandler(st, cfg)), + f.BindRoute("GET", "/v1/proxy", service.ProxyExtractHandler(st)), + f.BindRoute("GET", "/v1/license", service.LicenseVerifyHandler(st)), + f.BindRoute("POST", "/v1/license", service.LicenseVerifyHandler(st)), + // 共享记忆库:MCP 协议端点 + 公开阅读链接 + f.BindRoute("POST", "/mcp", service.McpHandler(st)), + f.BindRoute("GET", "/m", service.MemoryReadHandler(st)), + f.BindRoute("GET", "/m/*", service.MemoryReadHandler(st)), + // 易支付协议端点(GET/POST 同路由) + f.BindRoute("GET", "/submit.php", service.PaySubmitHandler(st, up)), + f.BindRoute("POST", "/submit.php", service.PaySubmitHandler(st, up)), + f.BindRoute("GET", "/mapi.php", service.PayMapiHandler(st, up)), + f.BindRoute("POST", "/mapi.php", service.PayMapiHandler(st, up)), + f.BindRoute("GET", "/api.php", service.PayApiHandler(st)), + f.BindRoute("POST", "/api.php", service.PayApiHandler(st)), + f.BindRoute("GET", "/healthz", func(c *fun.RouteCtx) error { + c.RequestCtx.WriteString("ok") + return nil + }), + } { + if err != nil { + log.Fatalf("boot: bind route failed: %v", err) + } + } + + // 代理池后台调度:每小时测活、每日 00:00 全量同步 + schedCtx, stopSched := context.WithCancel(context.Background()) + service.StartProxyScheduler(schedCtx, st) + + // 优雅停机:SIGINT/SIGTERM → 等在途请求收尾 + go func() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) + <-ch + stopSched() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + _ = f.Shutdown(ctx) + }() + + log.Printf("cyi-box backend listening on :%d, db=%s", cfg.Port, cfg.DBPath) + f.Start(cfg.Port) +} diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..a445734 --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,85 @@ +module github.com/cyi-cc/cyi-box/backend + +go 1.26.1 + +require ( + github.com/cyi-cc/fun v1.4.2 + github.com/fasthttp/websocket v1.5.12 + github.com/go-sql-driver/mysql v1.10.1 + github.com/jackc/pgx/v5 v5.11.0 + github.com/mark3labs/mcp-go v1.1.1 + github.com/pkg/sftp v1.13.11 + github.com/valyala/fasthttp v1.73.0 + github.com/xuri/excelize/v2 v2.11.0 + golang.org/x/crypto v0.54.0 + modernc.org/sqlite v1.59.0 +) + +require ( + cel.dev/expr v0.25.1 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/andybalholm/brotli v1.2.2 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/cubicdaiya/gonp v1.0.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/structtag v1.2.0 // indirect + github.com/google/cel-go v0.28.0 // indirect + github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/klauspost/compress v1.19.1 // indirect + github.com/kr/fs v0.1.0 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect + github.com/ncruces/go-sqlite3 v0.32.0 // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/ncruces/julianday v1.0.0 // indirect + github.com/pganalyze/pg_query_go/v6 v6.2.2 // indirect + github.com/pingcap/errors v0.11.5-0.20250523034308-74f78ae071ee // indirect + github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 // indirect + github.com/pingcap/log v1.1.0 // indirect + github.com/pingcap/tidb/pkg/parser v0.0.0-20260418072757-ce92298d1124 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/richardlehane/mscfb v1.0.7 // indirect + github.com/richardlehane/msoleps v1.0.6 // indirect + github.com/riza-io/grpc-go v0.2.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect + github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/sqlc-dev/doubleclick v1.0.0 // indirect + github.com/sqlc-dev/sqlc v1.31.1 // indirect + github.com/tetratelabs/wazero v1.11.0 // indirect + github.com/tiendc/go-deepcopy v1.7.2 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 // indirect + github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect + google.golang.org/grpc v1.80.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.75.7 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.12.1 // indirect +) + +tool github.com/sqlc-dev/sqlc/cmd/sqlc diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..828f840 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,263 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM= +github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cubicdaiya/gonp v1.0.4 h1:ky2uIAJh81WiLcGKBVD5R7KsM/36W6IqqTy6Bo6rGws= +github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYYACpOI0I= +github.com/cyi-cc/fun v1.4.2 h1:sMhBlUtXAWp57mwx4MnCodL1oPc9ejz+E5ukRIAZvD4= +github.com/cyi-cc/fun v1.4.2/go.mod h1:+093hp81tF4m0LZX6FNIfL+N9vsUardWCJwjkMVB5N8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +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/fasthttp/websocket v1.5.12 h1:e4RGPpWW2HTbL3zV0Y/t7g0ub294LkiuXXUuTOUInlE= +github.com/fasthttp/websocket v1.5.12/go.mod h1:I+liyL7/4moHojiOgUOIKEWm9EIxHqxZChS+aMFltyg= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.10.1 h1:arlSnNLq6a5yxGxV7qg9lF4j0C+KwD6NbQyKr9QL6ME= +github.com/go-sql-driver/mysql v1.10.1/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc= +github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= +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/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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.11.0 h1:IzBBtyK9AHqf98cctWFifYSci2hgQR/cd56wB4p+ogg= +github.com/jackc/pgx/v5 v5.11.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +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/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mark3labs/mcp-go v1.1.1 h1:PMZjyayCF01Y4R2kQXgDtsmxVLOdq1Mol4CnzzTYSEo= +github.com/mark3labs/mcp-go v1.1.1/go.mod h1:r2fW4o3wsoJ7IMsx1Wuq5xeP8PRGXPDfNveoGAYbb/s= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/ncruces/go-sqlite3 v0.32.0 h1:hNBUXp88LrfQCsuyXLqWTbTUG35sUuktDsqhhgHvU20= +github.com/ncruces/go-sqlite3 v0.32.0/go.mod h1:MIWTK60ONDl0oVY073zYvJP21C3Dly6P9bxVpgkLwdQ= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= +github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= +github.com/pganalyze/pg_query_go/v6 v6.2.2 h1:O0L6zMC226R82RF3X5n0Ki6HjytDsoAzuzp4ATVAHNo= +github.com/pganalyze/pg_query_go/v6 v6.2.2/go.mod h1:Cn6+j4870kJz3iYNsb0VsNG04vpSWgEvBwc590J4qD0= +github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pingcap/errors v0.11.5-0.20250523034308-74f78ae071ee h1:/IDPbpzkzA97t1/Z1+C3KlxbevjMeaI6BQYxvivu4u8= +github.com/pingcap/errors v0.11.5-0.20250523034308-74f78ae071ee/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg= +github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 h1:tdMsjOqUR7YXHoBitzdebTvOjs/swniBTOLy5XiMtuE= +github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86/go.mod h1:exzhVYca3WRtd6gclGNErRWb1qEgff3LYta0LvRmON4= +github.com/pingcap/log v1.1.0 h1:ELiPxACz7vdo1qAvvaWJg1NrYFoY6gqAh/+Uo6aXdD8= +github.com/pingcap/log v1.1.0/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4= +github.com/pingcap/tidb/pkg/parser v0.0.0-20260418072757-ce92298d1124 h1:zYmP5fBH+i2yhhU6f5uOol6zxHtR2/sD47BsJLfy0oU= +github.com/pingcap/tidb/pkg/parser v0.0.0-20260418072757-ce92298d1124/go.mod h1:zDLDsfNBU5+L6T4J9/OgWAHc/WZvMUjbpgHqQ/t3yKo= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.11 h1:0N92SLTB8JqASJB14ZLHHzFnBV8mG9zw4K7jghEFWuE= +github.com/pkg/sftp v1.13.11/go.mod h1:uNkH9roSXglNJqM+glJJi+TQXQUm0fXFWqCFmT8hsN0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/richardlehane/mscfb v1.0.7 h1:oeoiM0WE79vHwE8RpIYYvIAc8ajTH2mb6UZm55/+EB0= +github.com/richardlehane/mscfb v1.0.7/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo= +github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg= +github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ= +github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc= +github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/sqlc-dev/doubleclick v1.0.0 h1:2/OApfQ2eLgcfa/Fqs8WSMA6atH0G8j9hHbQIgMfAXI= +github.com/sqlc-dev/doubleclick v1.0.0/go.mod h1:ODHRroSrk/rr5neRHlWMSRijqOak8YmNaO3VAZCNl5Y= +github.com/sqlc-dev/sqlc v1.31.1 h1:+V+BjBJfFNPX/RFfL8eiZD9jk9lVJUEGGllWvnYNqbc= +github.com/sqlc-dev/sqlc v1.31.1/go.mod h1:6ZPww/Jd3G6MzJeW6NrqizjL+52vYNaaXP9yMeJ/Nao= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= +github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= +github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44= +github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.73.0 h1:ocTOORnBWtJ+P8t/6wAjdkchMzdfHmWx2VD/DPbgZ7s= +github.com/valyala/fasthttp v1.73.0/go.mod h1:EtXQDHaR+5P18p8wqDRFpUhxr108Ga9mXvVJXHRrN2k= +github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo= +github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM= +github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4= +github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.11.0 h1:HxaEFl6sRN2+8J5a8HaKq+0M4FsjBGMnWWtjOCPSG88= +github.com/xuri/excelize/v2 v2.11.0/go.mod h1:jxFLbzaIwGQ5ufFNvYfUOHqXhfPaNmP14KWfmNz2Uak= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= +golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +golang.org/x/image v0.38.0 h1:5l+q+Y9JDC7mBOMjo4/aPhMDcxEptsX+Tt3GgRQRPuE= +golang.org/x/image v0.38.0/go.mod h1:/3f6vaXC+6CEanU4KJxbcUZyEePbyKbaLoDOe4ehFYY= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= +google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= +modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8= +modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w= +modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc= +modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew= +modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g= +modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= +modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A= +modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go new file mode 100644 index 0000000..fd0378b --- /dev/null +++ b/backend/internal/config/config.go @@ -0,0 +1,90 @@ +// Package config 进程配置:全部走环境变量,零配置文件。 +package config + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/cyi-cc/cyi-box/backend/internal/vault" +) + +type Config struct { + Port uint16 // CYIBOX_PORT,默认 8890 + DBPath string // CYIBOX_DB,默认 ./data/cyibox.db + AdminName string // CYIBOX_ADMIN_NAME,默认 admin(首启种子账号) + AdminPassword string // CYIBOX_ADMIN_PASSWORD,默认 admin123(首启种子密码) + CorsOrigins []string // CYIBOX_CORS 逗号分隔白名单;开发走 vite 代理可留空 + VaultKey []byte // 密码箱字段加密密钥(env CYIBOX_VAULT_KEY 或自动生成落盘) + DiskDir string // 网盘文件落盘目录,默认 /files +} + +func (c *Config) New() error { + c.Port = uint16(envInt("CYIBOX_PORT", 8890)) + c.DBPath = envStr("CYIBOX_DB", "./data/cyibox.db") + c.AdminName = envStr("CYIBOX_ADMIN_NAME", "admin") + c.AdminPassword = envStr("CYIBOX_ADMIN_PASSWORD", "admin123") + if v := strings.TrimSpace(os.Getenv("CYIBOX_CORS")); v != "" { + for _, o := range strings.Split(v, ",") { + if o = strings.TrimSpace(o); o != "" { + c.CorsOrigins = append(c.CorsOrigins, o) + } + } + } + if c.Port == 0 { + return fmt.Errorf("config: invalid CYIBOX_PORT") + } + if dir := filepath.Dir(c.DBPath); dir != "." { + if err := os.MkdirAll(dir, 0o755); err != nil { + return fmt.Errorf("config: create db dir failed: %w", err) + } + } + key, err := loadVaultKey(filepath.Dir(c.DBPath)) + if err != nil { + return err + } + c.VaultKey = key + c.DiskDir = envStr("CYIBOX_DISK_DIR", filepath.Join(filepath.Dir(c.DBPath), "files")) + if err := os.MkdirAll(c.DiskDir, 0o755); err != nil { + return fmt.Errorf("config: create disk dir failed: %w", err) + } + return nil +} + +// loadVaultKey CYIBOX_VAULT_KEY 优先;否则 /vault.key 存在即读, +// 不存在则随机生成并以 0600 落盘——密钥独立于 db 文件,db 被拷走也解不开密文 +func loadVaultKey(dir string) ([]byte, error) { + if v := strings.TrimSpace(os.Getenv("CYIBOX_VAULT_KEY")); v != "" { + return vault.DeriveKey(v), nil + } + path := filepath.Join(dir, "vault.key") + if raw, err := os.ReadFile(path); err == nil { + return vault.DeriveKey(string(raw)), nil + } + material, err := vault.RandomKey() + if err != nil { + return nil, fmt.Errorf("config: generate vault key failed: %w", err) + } + if err := os.WriteFile(path, []byte(material), 0o600); err != nil { + return nil, fmt.Errorf("config: write vault key failed: %w", err) + } + return vault.DeriveKey(material), nil +} + +func envStr(key, def string) string { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + return v + } + return def +} + +func envInt(key string, def int) int { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + if n, err := strconv.Atoi(v); err == nil { + return n + } + } + return def +} diff --git a/backend/internal/db/db.go b/backend/internal/db/db.go new file mode 100644 index 0000000..f43598b --- /dev/null +++ b/backend/internal/db/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package db + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/internal/db/embed.go b/backend/internal/db/embed.go new file mode 100644 index 0000000..ffbe779 --- /dev/null +++ b/backend/internal/db/embed.go @@ -0,0 +1,8 @@ +// Package db sqlc 生成层。models.go / queries.sql.go 由 `go tool sqlc generate` 产出, +// 手写部分只有本文件——把 schema.sql 嵌进来供 store 运行时迁移。 +package db + +import _ "embed" + +//go:embed schema.sql +var SchemaDDL string diff --git a/backend/internal/db/models.go b/backend/internal/db/models.go new file mode 100644 index 0000000..e0a35df --- /dev/null +++ b/backend/internal/db/models.go @@ -0,0 +1,220 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package db + +type Bookmark struct { + ID int64 + UserID int64 + Title string + Url string + Icon string + Sort int64 + CreatedAt int64 +} + +type Dbconn struct { + ID int64 + Name string + Engine string + Host string + Port int64 + Username string + PasswordEnc string + Database string + Params string + CreatedAt int64 + UpdatedAt int64 +} + +type File struct { + ID int64 + UserID int64 + Name string + Stored string + Size int64 + Mime string + CreatedAt int64 +} + +type LicenseApp struct { + ID int64 + Appid string + Secret string + Name string + Enabled int64 + CreatedAt int64 +} + +type LicenseCard struct { + ID int64 + AppID int64 + Card string + Hours int64 + Domain string + ActivatedAt int64 + ExpiresAt int64 + CreatedAt int64 +} + +type Memory struct { + K string + Content string + Project string + Tags string + WrittenBy string + Revision int64 + CreatedAt int64 + UpdatedAt int64 +} + +type PayOrder struct { + ID int64 + TradeNo string + OutTradeNo string + Channel string + Subject string + Money int64 + Fee int64 + Status int64 + NotifyUrl string + ReturnUrl string + ClientIp string + Notified int64 + NotifyAttempts int64 + PaidAt int64 + ExpiredAt int64 + UpstreamTradeNo string + GoodsKey string + Quantity int64 + UnitPrice int64 + Payurl string + Qrcode string + BuyerContact string + QueryPwd string + Cards string + CreatedAt int64 +} + +type Proxy struct { + ID int64 + Ip string + Port int64 + Protocols string + Anonymity string + Source string + RegionID int64 + Latency int64 + Speed int64 + Uptime int64 + Alive int64 + FailCount int64 + LastCheckedAt int64 + LastAliveAt int64 + LastSeenAt int64 + CreatedAt int64 +} + +type ProxyKey struct { + ID int64 + Kkey string + Name string + RegionID int64 + Enabled int64 + UsedCount int64 + LastUsedAt int64 + CreatedAt int64 +} + +type ProxyRegion struct { + ID int64 + Code string + Name string + ZhName string + Region string + Slug string +} + +type ProxySync struct { + ID int64 + StartedAt int64 + FinishedAt int64 + Status string + Source string + Total int64 + Added int64 + Removed int64 + Updated int64 + Detail string + Error string +} + +type Server struct { + ID int64 + Name string + Host string + Port int64 + Username string + AuthType string + SecretEnc string + Note string + CreatedAt int64 + UpdatedAt int64 +} + +type Session struct { + Token string + UserID int64 + CreatedAt int64 + ExpiresAt int64 +} + +type Setting struct { + K string + V string +} + +type Share struct { + ID int64 + Code string + FileID int64 + UserID int64 + FileName string + ExpiresAt int64 + Downloads int64 + CreatedAt int64 +} + +type Tool struct { + ID int64 + Name string + Icon string + Url string + Description string + Sort int64 + Enabled bool +} + +type User struct { + ID int64 + Name string + PasswordHash string + Role string + Status string + CreatedAt int64 + UpdatedAt int64 +} + +type Vault struct { + ID int64 + UserID int64 + Title string + Username string + PasswordEnc string + Url string + Note string + TotpEnc string + CreatedAt int64 + UpdatedAt int64 +} diff --git a/backend/internal/db/querier.go b/backend/internal/db/querier.go new file mode 100644 index 0000000..7dc6061 --- /dev/null +++ b/backend/internal/db/querier.go @@ -0,0 +1,136 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package db + +import ( + "context" + "database/sql" +) + +type Querier interface { + ActivateLicenseCard(ctx context.Context, arg ActivateLicenseCardParams) error + CountActiveSessions(ctx context.Context, expiresAt int64) (int64, error) + CountActiveUsers(ctx context.Context) (int64, error) + CountAdmins(ctx context.Context) (int64, error) + CountAllUsers(ctx context.Context) (int64, error) + CountBookmarksByUser(ctx context.Context, userID int64) (int64, error) + CountDbconns(ctx context.Context) (int64, error) + CountFilesAll(ctx context.Context) (int64, error) + CountLicenseApps(ctx context.Context) (int64, error) + CountLicenseCards(ctx context.Context, arg CountLicenseCardsParams) (int64, error) + CountMemories(ctx context.Context) (int64, error) + CountPayOrders(ctx context.Context, arg CountPayOrdersParams) (int64, error) + CountProxies(ctx context.Context) (int64, error) + CountProxiesAll(ctx context.Context) (int64, error) + CountProxiesFiltered(ctx context.Context, arg CountProxiesFilteredParams) (int64, error) + CountServers(ctx context.Context) (int64, error) + CountTodayLogins(ctx context.Context) (int64, error) + CountTools(ctx context.Context) (int64, error) + CountUsers(ctx context.Context, keyword sql.NullString) (int64, error) + CountVaultByUser(ctx context.Context, userID int64) (int64, error) + CreateLicenseApp(ctx context.Context, arg CreateLicenseAppParams) (int64, error) + CreateLicenseCard(ctx context.Context, arg CreateLicenseCardParams) error + CreatePayOrder(ctx context.Context, arg CreatePayOrderParams) (int64, error) + DeleteBookmark(ctx context.Context, arg DeleteBookmarkParams) (int64, error) + DeleteDbconn(ctx context.Context, id int64) (int64, error) + DeleteDeadProxies(ctx context.Context, lastCheckedAt int64) (int64, error) + DeleteFile(ctx context.Context, arg DeleteFileParams) (int64, error) + DeleteLicenseApp(ctx context.Context, id int64) error + DeleteLicenseCard(ctx context.Context, id int64) error + DeleteMemory(ctx context.Context, k string) (int64, error) + DeleteOtherSessions(ctx context.Context, arg DeleteOtherSessionsParams) error + DeleteProxy(ctx context.Context, id int64) error + DeleteProxyApiKey(ctx context.Context, id int64) (int64, error) + DeleteServer(ctx context.Context, id int64) (int64, error) + DeleteSession(ctx context.Context, token string) error + DeleteShare(ctx context.Context, arg DeleteShareParams) (int64, error) + DeleteSharesByFile(ctx context.Context, fileID int64) error + DeleteTool(ctx context.Context, id int64) error + DeleteUser(ctx context.Context, id int64) error + DeleteVault(ctx context.Context, arg DeleteVaultParams) (int64, error) + FinishProxySync(ctx context.Context, arg FinishProxySyncParams) error + GetDbconn(ctx context.Context, id int64) (Dbconn, error) + GetFile(ctx context.Context, id int64) (File, error) + GetLicenseAppByAppid(ctx context.Context, appid string) (LicenseApp, error) + GetLicenseCard(ctx context.Context, arg GetLicenseCardParams) (LicenseCard, error) + GetMemory(ctx context.Context, k string) (Memory, error) + GetPayOrder(ctx context.Context, id int64) (PayOrder, error) + GetPayOrderByOutNo(ctx context.Context, outTradeNo string) (PayOrder, error) + GetPayOrderByTradeNo(ctx context.Context, tradeNo string) (PayOrder, error) + GetProxyApiKey(ctx context.Context, kkey string) (ProxyKey, error) + GetProxyRegionByCode(ctx context.Context, code string) (ProxyRegion, error) + GetServer(ctx context.Context, id int64) (Server, error) + GetSessionUser(ctx context.Context, token string) (GetSessionUserRow, error) + GetSetting(ctx context.Context, k string) (string, error) + GetShareByCode(ctx context.Context, code string) (GetShareByCodeRow, error) + GetUserByID(ctx context.Context, id int64) (User, error) + GetUserByName(ctx context.Context, name string) (User, error) + GetVault(ctx context.Context, arg GetVaultParams) (Vault, error) + IncPayNotifyAttempts(ctx context.Context, id int64) error + IncShareDownloads(ctx context.Context, code string) error + InsertBookmark(ctx context.Context, arg InsertBookmarkParams) (int64, error) + InsertDbconn(ctx context.Context, arg InsertDbconnParams) (int64, error) + InsertFile(ctx context.Context, arg InsertFileParams) (int64, error) + InsertProxy(ctx context.Context, arg InsertProxyParams) (int64, error) + InsertProxyApiKey(ctx context.Context, arg InsertProxyApiKeyParams) (int64, error) + InsertProxySync(ctx context.Context, arg InsertProxySyncParams) (int64, error) + InsertServer(ctx context.Context, arg InsertServerParams) (int64, error) + InsertSession(ctx context.Context, arg InsertSessionParams) error + InsertShare(ctx context.Context, arg InsertShareParams) (int64, error) + InsertTool(ctx context.Context, arg InsertToolParams) (int64, error) + InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) + InsertVault(ctx context.Context, arg InsertVaultParams) (int64, error) + LastProxyCheckAt(ctx context.Context) (interface{}, error) + LicenseCardStats(ctx context.Context, arg LicenseCardStatsParams) (LicenseCardStatsRow, error) + LicenseCardStatsAll(ctx context.Context, expiresAt int64) (LicenseCardStatsAllRow, error) + ListBookmarks(ctx context.Context, userID int64) ([]Bookmark, error) + ListDbconns(ctx context.Context) ([]Dbconn, error) + ListEnabledTools(ctx context.Context) ([]Tool, error) + ListFiles(ctx context.Context, userID int64) ([]File, error) + ListLicenseApps(ctx context.Context) ([]ListLicenseAppsRow, error) + ListLicenseCards(ctx context.Context, arg ListLicenseCardsParams) ([]LicenseCard, error) + ListMemories(ctx context.Context, arg ListMemoriesParams) ([]Memory, error) + ListPayOrders(ctx context.Context, arg ListPayOrdersParams) ([]PayOrder, error) + ListPendingPayOrders(ctx context.Context) ([]PayOrder, error) + ListProxiesDue(ctx context.Context, lastCheckedAt int64) ([]ListProxiesDueRow, error) + ListProxiesFiltered(ctx context.Context, arg ListProxiesFilteredParams) ([]ListProxiesFilteredRow, error) + ListProxyApiKeys(ctx context.Context) ([]ListProxyApiKeysRow, error) + ListProxyKeys(ctx context.Context) ([]ListProxyKeysRow, error) + ListProxyRegions(ctx context.Context) ([]ListProxyRegionsRow, error) + ListProxySyncs(ctx context.Context, limit int64) ([]ProxySync, error) + ListServers(ctx context.Context) ([]Server, error) + ListShares(ctx context.Context, userID int64) ([]Share, error) + ListTools(ctx context.Context) ([]Tool, error) + ListUsers(ctx context.Context, arg ListUsersParams) ([]User, error) + ListVault(ctx context.Context, arg ListVaultParams) ([]Vault, error) + LoginTrend(ctx context.Context, createdAt int64) ([]LoginTrendRow, error) + MarkProxyAlive(ctx context.Context, arg MarkProxyAliveParams) error + MarkProxyFailed(ctx context.Context, arg MarkProxyFailedParams) error + MemoryProjects(ctx context.Context) ([]MemoryProjectsRow, error) + PayDailyPaid(ctx context.Context, paidAt int64) ([]PayDailyPaidRow, error) + PayOrderStats(ctx context.Context, createdAt int64) (PayOrderStatsRow, error) + RandomProxies(ctx context.Context, arg RandomProxiesParams) ([]RandomProxiesRow, error) + RecentPaidOrders(ctx context.Context, limit int64) ([]RecentPaidOrdersRow, error) + SetPayNotified(ctx context.Context, id int64) error + SetPayOrderQrcode(ctx context.Context, arg SetPayOrderQrcodeParams) error + SetPayOrderStatus(ctx context.Context, arg SetPayOrderStatusParams) error + SetPayOrderUpstream(ctx context.Context, arg SetPayOrderUpstreamParams) error + TouchProxyApiKey(ctx context.Context, arg TouchProxyApiKeyParams) error + TouchSession(ctx context.Context, arg TouchSessionParams) error + UpdateBookmark(ctx context.Context, arg UpdateBookmarkParams) (int64, error) + UpdateDbconn(ctx context.Context, arg UpdateDbconnParams) (int64, error) + UpdateLicenseApp(ctx context.Context, arg UpdateLicenseAppParams) error + UpdateProxySeen(ctx context.Context, arg UpdateProxySeenParams) error + UpdateServer(ctx context.Context, arg UpdateServerParams) (int64, error) + UpdateTool(ctx context.Context, arg UpdateToolParams) error + UpdateUser(ctx context.Context, arg UpdateUserParams) error + UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error + UpdateVault(ctx context.Context, arg UpdateVaultParams) (int64, error) + UpsertMemory(ctx context.Context, arg UpsertMemoryParams) error + UpsertProxyRegion(ctx context.Context, arg UpsertProxyRegionParams) error + UpsertSetting(ctx context.Context, arg UpsertSettingParams) error +} + +var _ Querier = (*Queries)(nil) diff --git a/backend/internal/db/queries.sql b/backend/internal/db/queries.sql new file mode 100644 index 0000000..0fb4942 --- /dev/null +++ b/backend/internal/db/queries.sql @@ -0,0 +1,427 @@ +-- name: GetUserByName :one +SELECT * FROM users WHERE name = ? LIMIT 1; + +-- name: GetUserByID :one +SELECT * FROM users WHERE id = ?; + +-- name: ListUsers :many +SELECT * FROM users +WHERE name LIKE '%' || sqlc.arg(keyword) || '%' +ORDER BY id LIMIT ? OFFSET ?; + +-- name: CountUsers :one +SELECT COUNT(*) FROM users WHERE name LIKE '%' || sqlc.arg(keyword) || '%'; + +-- name: CountAllUsers :one +SELECT COUNT(*) FROM users; + +-- name: CountActiveUsers :one +SELECT COUNT(*) FROM users WHERE status = 'active'; + +-- name: CountAdmins :one +SELECT COUNT(*) FROM users WHERE role = 'admin' AND status = 'active'; + +-- name: InsertUser :execlastid +INSERT INTO users(name, password_hash, role, status, created_at, updated_at) +VALUES(?, ?, ?, 'active', ?, ?); + +-- name: UpdateUser :exec +UPDATE users SET name = ?, role = ?, status = ?, updated_at = ? WHERE id = ?; + +-- name: UpdateUserPassword :exec +UPDATE users SET name = ?, role = ?, status = ?, password_hash = ?, updated_at = ? WHERE id = ?; + +-- name: DeleteUser :exec +DELETE FROM users WHERE id = ?; + +-- name: InsertSession :exec +INSERT INTO sessions(token, user_id, created_at, expires_at) VALUES(?, ?, ?, ?); + +-- name: GetSessionUser :one +SELECT u.id, u.name, u.password_hash, u.role, u.status, u.created_at, u.updated_at, s.expires_at +FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ?; + +-- name: DeleteSession :exec +DELETE FROM sessions WHERE token = ?; + +-- name: DeleteOtherSessions :exec +DELETE FROM sessions WHERE user_id = ? AND token != ?; + +-- name: TouchSession :exec +UPDATE sessions SET expires_at = ? WHERE token = ?; + +-- name: CountActiveSessions :one +SELECT COUNT(*) FROM sessions WHERE expires_at > ?; + +-- name: CountTodayLogins :one +SELECT COUNT(*) FROM sessions +WHERE date(created_at, 'unixepoch', 'localtime') = date('now', 'localtime'); + +-- name: LoginTrend :many +SELECT date(created_at, 'unixepoch', 'localtime') AS d, COUNT(*) AS c +FROM sessions WHERE created_at >= ? GROUP BY d; + +-- name: ListTools :many +SELECT * FROM tools ORDER BY sort, id; + +-- name: ListEnabledTools :many +SELECT * FROM tools WHERE enabled = 1 ORDER BY sort, id; + +-- name: InsertTool :execlastid +INSERT INTO tools(name, icon, url, description, sort, enabled) VALUES(?, ?, ?, ?, ?, ?); + +-- name: UpdateTool :exec +UPDATE tools SET name = ?, icon = ?, url = ?, description = ?, sort = ?, enabled = ? WHERE id = ?; + +-- name: DeleteTool :exec +DELETE FROM tools WHERE id = ?; + +-- name: CountTools :one +SELECT COUNT(*) FROM tools WHERE enabled = 1; + +-- name: ListVault :many +SELECT * FROM vault WHERE user_id = ? + AND (title LIKE '%' || sqlc.arg(keyword) || '%' + OR username LIKE '%' || sqlc.arg(keyword) || '%' + OR url LIKE '%' || sqlc.arg(keyword) || '%') +ORDER BY updated_at DESC; + +-- name: GetVault :one +SELECT * FROM vault WHERE user_id = ? AND id = ?; + +-- name: InsertVault :execlastid +INSERT INTO vault(user_id, title, username, password_enc, url, note, totp_enc, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: UpdateVault :execrows +UPDATE vault SET title = ?, username = ?, password_enc = ?, url = ?, note = ?, totp_enc = ?, updated_at = ? +WHERE id = ? AND user_id = ?; + +-- name: DeleteVault :execrows +DELETE FROM vault WHERE user_id = ? AND id = ?; + +-- name: ListBookmarks :many +SELECT * FROM bookmarks WHERE user_id = ? ORDER BY sort, id; + +-- name: InsertBookmark :execlastid +INSERT INTO bookmarks(user_id, title, url, icon, sort, created_at) VALUES(?, ?, ?, ?, ?, ?); + +-- name: UpdateBookmark :execrows +UPDATE bookmarks SET title = ?, url = ?, icon = ?, sort = ? WHERE id = ? AND user_id = ?; + +-- name: DeleteBookmark :execrows +DELETE FROM bookmarks WHERE user_id = ? AND id = ?; + +-- name: ListServers :many +SELECT * FROM servers ORDER BY id; + +-- name: GetServer :one +SELECT * FROM servers WHERE id = ?; + +-- name: InsertServer :execlastid +INSERT INTO servers(name, host, port, username, auth_type, secret_enc, note, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: UpdateServer :execrows +UPDATE servers SET name = ?, host = ?, port = ?, username = ?, auth_type = ?, secret_enc = ?, note = ?, updated_at = ? +WHERE id = ?; + +-- name: DeleteServer :execrows +DELETE FROM servers WHERE id = ?; + +-- name: CountServers :one +SELECT COUNT(*) FROM servers; + +-- name: ListDbconns :many +SELECT * FROM dbconns ORDER BY id; + +-- name: GetDbconn :one +SELECT * FROM dbconns WHERE id = ?; + +-- name: InsertDbconn :execlastid +INSERT INTO dbconns(name, engine, host, port, username, password_enc, database, params, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: UpdateDbconn :execrows +UPDATE dbconns SET name = ?, engine = ?, host = ?, port = ?, username = ?, password_enc = ?, database = ?, params = ?, updated_at = ? +WHERE id = ?; + +-- name: DeleteDbconn :execrows +DELETE FROM dbconns WHERE id = ?; + +-- name: InsertFile :execlastid +INSERT INTO files(user_id, name, stored, size, mime, created_at) VALUES(?, ?, ?, ?, ?, ?); + +-- name: ListFiles :many +SELECT * FROM files WHERE user_id = ? ORDER BY id DESC; + +-- name: GetFile :one +SELECT * FROM files WHERE id = ?; + +-- name: DeleteFile :execrows +DELETE FROM files WHERE id = ? AND user_id = ?; + +-- name: InsertShare :execlastid +INSERT INTO shares(code, file_id, user_id, file_name, expires_at, created_at) VALUES(?, ?, ?, ?, ?, ?); + +-- name: ListShares :many +SELECT * FROM shares WHERE user_id = ? ORDER BY id DESC; + +-- name: GetShareByCode :one +SELECT sh.id, sh.code, sh.file_id, sh.user_id, sh.file_name, sh.expires_at, sh.downloads, sh.created_at, + f.name, f.stored, f.size, f.mime +FROM shares sh JOIN files f ON f.id = sh.file_id WHERE sh.code = ?; + +-- name: DeleteShare :execrows +DELETE FROM shares WHERE id = ? AND user_id = ?; + +-- name: DeleteSharesByFile :exec +DELETE FROM shares WHERE file_id = ?; + +-- name: IncShareDownloads :exec +UPDATE shares SET downloads = downloads + 1 WHERE code = ?; + +-- name: UpsertProxyRegion :exec +INSERT INTO proxy_regions(code, name, zh_name, region, slug) VALUES(?, ?, ?, ?, ?) +ON CONFLICT(code) DO UPDATE SET name = excluded.name, zh_name = excluded.zh_name, + region = excluded.region, slug = excluded.slug; + +-- name: GetProxyRegionByCode :one +SELECT * FROM proxy_regions WHERE code = ?; + +-- name: ListProxyRegions :many +SELECT r.*, (SELECT COUNT(*) FROM proxies p WHERE p.region_id = r.id AND p.alive = 1) AS alive_count +FROM proxy_regions r ORDER BY alive_count DESC, r.code; + +-- name: ListProxyKeys :many +SELECT id, ip, port, source FROM proxies; + +-- name: InsertProxy :execlastid +INSERT INTO proxies(ip, port, protocols, anonymity, source, region_id, latency, speed, uptime, alive, last_seen_at, created_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?); + +-- name: UpdateProxySeen :exec +UPDATE proxies SET protocols = ?, anonymity = ?, region_id = ?, latency = ?, speed = ?, uptime = ?, last_seen_at = ?, alive = 1, fail_count = 0 +WHERE id = ?; + +-- name: DeleteProxy :exec +DELETE FROM proxies WHERE id = ?; + +-- name: ListProxiesDue :many +SELECT id, ip, port, protocols, fail_count FROM proxies WHERE alive = 1 AND last_checked_at < ? ORDER BY last_checked_at LIMIT 20000; + +-- name: MarkProxyAlive :exec +UPDATE proxies SET last_checked_at = ?, alive = 1, fail_count = 0, last_alive_at = ?, latency = ? WHERE id = ?; + +-- name: MarkProxyFailed :exec +UPDATE proxies SET last_checked_at = ?, alive = 0, fail_count = ? WHERE id = ?; + +-- name: DeleteDeadProxies :execrows +DELETE FROM proxies WHERE alive = 0 AND last_checked_at < ?; + +-- name: CountProxies :one +SELECT COUNT(*) FROM proxies WHERE alive = 1; + +-- name: CountProxiesFiltered :one +SELECT COUNT(*) FROM proxies p WHERE (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR p.ip LIKE '%' || ? || '%') AND (? < 0 OR p.alive = ?); + +-- name: ListProxiesFiltered :many +SELECT p.*, r.code AS region_code, r.name AS region_name, r.zh_name AS region_zh_name +FROM proxies p JOIN proxy_regions r ON r.id = p.region_id +WHERE (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR p.ip LIKE '%' || ? || '%') AND (? < 0 OR p.alive = ?) +ORDER BY p.alive DESC, p.latency LIMIT ? OFFSET ?; + +-- name: InsertProxySync :execlastid +INSERT INTO proxy_syncs(started_at, source) VALUES(?, ?); + +-- name: FinishProxySync :exec +UPDATE proxy_syncs SET finished_at = ?, status = ?, total = ?, added = ?, removed = ?, updated = ?, detail = ?, error = ? WHERE id = ?; + +-- name: ListProxySyncs :many +SELECT * FROM proxy_syncs ORDER BY id DESC LIMIT ?; + +-- name: LastProxyCheckAt :one +SELECT COALESCE(MAX(last_checked_at), 0) FROM proxies; + +-- name: InsertProxyApiKey :execlastid +INSERT INTO proxy_keys(kkey, name, region_id, created_at) VALUES(?, ?, ?, ?); + +-- name: ListProxyApiKeys :many +SELECT k.*, r.code AS region_code, r.zh_name AS region_zh_name, r.name AS region_name +FROM proxy_keys k LEFT JOIN proxy_regions r ON r.id = k.region_id +ORDER BY k.id DESC; + +-- name: GetProxyApiKey :one +SELECT * FROM proxy_keys WHERE kkey = ? AND enabled = 1; + +-- name: TouchProxyApiKey :exec +UPDATE proxy_keys SET used_count = used_count + 1, last_used_at = ? WHERE id = ?; + +-- name: DeleteProxyApiKey :execrows +DELETE FROM proxy_keys WHERE id = ?; + +-- name: RandomProxies :many +SELECT p.id, p.ip, p.port, p.protocols, p.fail_count, r.code AS region_code, r.zh_name AS region_zh_name +FROM proxies p JOIN proxy_regions r ON r.id = p.region_id +WHERE p.alive = 1 AND (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR r.code = ?) +ORDER BY RANDOM() LIMIT ?; + +-- name: GetSetting :one +SELECT v FROM settings WHERE k = ?; + +-- name: UpsertSetting :exec +INSERT INTO settings (k, v) VALUES (?, ?) ON CONFLICT(k) DO UPDATE SET v = excluded.v; + +-- name: CreatePayOrder :execlastid +INSERT INTO pay_orders (trade_no, out_trade_no, channel, subject, money, fee, notify_url, return_url, client_ip, expired_at, created_at) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + +-- name: GetPayOrderByTradeNo :one +SELECT * FROM pay_orders WHERE trade_no = ?; + +-- name: GetPayOrderByOutNo :one +SELECT * FROM pay_orders WHERE out_trade_no = ?; + +-- name: SetPayOrderUpstream :exec +UPDATE pay_orders SET upstream_trade_no = ?, goods_key = ?, quantity = ?, unit_price = ?, payurl = ?, buyer_contact = ?, query_pwd = ? WHERE id = ?; + +-- name: SetPayOrderQrcode :exec +UPDATE pay_orders SET qrcode = ? WHERE id = ?; + +-- name: SetPayOrderStatus :exec +UPDATE pay_orders SET status = ?, paid_at = ?, cards = ? WHERE id = ?; + +-- name: ListPendingPayOrders :many +SELECT * FROM pay_orders WHERE status = 0 AND upstream_trade_no != '' ORDER BY id LIMIT 200; + +-- name: ListPayOrders :many +SELECT * FROM pay_orders +WHERE (? = -1 OR status = ?) AND (? = '' OR trade_no LIKE '%' || ? || '%' OR out_trade_no LIKE '%' || ? || '%') +ORDER BY id DESC LIMIT ? OFFSET ?; + +-- name: CountPayOrders :one +SELECT COUNT(*) FROM pay_orders +WHERE (? = -1 OR status = ?) AND (? = '' OR trade_no LIKE '%' || ? || '%' OR out_trade_no LIKE '%' || ? || '%'); + +-- name: PayOrderStats :one +SELECT COUNT(*) AS cnt, COALESCE(SUM(CASE WHEN status = 1 THEN money ELSE 0 END), 0) AS money, + COALESCE(SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END), 0) AS pending, + COALESCE(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS paid +FROM pay_orders WHERE created_at >= ?; + +-- name: IncPayNotifyAttempts :exec +UPDATE pay_orders SET notify_attempts = notify_attempts + 1 WHERE id = ?; + +-- name: SetPayNotified :exec +UPDATE pay_orders SET notified = 1 WHERE id = ?; + +-- name: GetPayOrder :one +SELECT * FROM pay_orders WHERE id = ?; + +-- name: CreateLicenseApp :execlastid +INSERT INTO license_apps (appid, secret, name, created_at) VALUES (?, ?, ?, ?); + +-- name: GetLicenseAppByAppid :one +SELECT * FROM license_apps WHERE appid = ?; + +-- name: ListLicenseApps :many +SELECT a.*, (SELECT COUNT(*) FROM license_cards c WHERE c.app_id = a.id) AS card_count +FROM license_apps a ORDER BY a.id DESC; + +-- name: UpdateLicenseApp :exec +UPDATE license_apps SET name = ?, enabled = ? WHERE id = ?; + +-- name: DeleteLicenseApp :exec +DELETE FROM license_apps WHERE id = ?; + +-- name: CreateLicenseCard :exec +INSERT INTO license_cards (app_id, card, hours, created_at) VALUES (?, ?, ?, ?); + +-- name: GetLicenseCard :one +SELECT * FROM license_cards WHERE app_id = ? AND card = ?; + +-- name: ActivateLicenseCard :exec +UPDATE license_cards SET domain = ?, activated_at = ?, expires_at = ? WHERE id = ?; + +-- name: ListLicenseCards :many +SELECT * FROM license_cards +WHERE app_id = ? AND (? = '' OR card LIKE '%' || ? || '%' OR domain LIKE '%' || ? || '%') +ORDER BY id DESC LIMIT ? OFFSET ?; + +-- name: CountLicenseCards :one +SELECT COUNT(*) FROM license_cards +WHERE app_id = ? AND (? = '' OR card LIKE '%' || ? || '%' OR domain LIKE '%' || ? || '%'); + +-- name: LicenseCardStats :one +SELECT COUNT(*) AS total, + COALESCE(SUM(CASE WHEN activated_at = 0 THEN 1 ELSE 0 END), 0) AS unused, + COALESCE(SUM(CASE WHEN activated_at > 0 AND (expires_at = 0 OR expires_at > ?) THEN 1 ELSE 0 END), 0) AS active, + COALESCE(SUM(CASE WHEN activated_at > 0 AND expires_at > 0 AND expires_at <= ? THEN 1 ELSE 0 END), 0) AS expired +FROM license_cards WHERE app_id = ?; + +-- name: DeleteLicenseCard :exec +DELETE FROM license_cards WHERE id = ?; + +-- name: UpsertMemory :exec +INSERT INTO memories (k, content, project, tags, written_by, revision, created_at, updated_at) +VALUES (?, ?, ?, ?, ?, 1, ?, ?) +ON CONFLICT(k) DO UPDATE SET + content = excluded.content, + project = excluded.project, + tags = excluded.tags, + written_by = excluded.written_by, + revision = memories.revision + 1, + updated_at = excluded.updated_at; + +-- name: GetMemory :one +SELECT * FROM memories WHERE k = ?; + +-- name: DeleteMemory :execrows +DELETE FROM memories WHERE k = ?; + +-- name: ListMemories :many +SELECT * FROM memories +WHERE (? = '' OR project = ?) AND (? = '' OR tags LIKE '%"' || ? || '"%') +ORDER BY updated_at DESC LIMIT ?; + +-- name: MemoryProjects :many +SELECT project AS name, COUNT(*) AS cnt FROM memories +WHERE project != '' GROUP BY project ORDER BY MAX(updated_at) DESC; + +-- name: CountMemories :one +SELECT COUNT(*) FROM memories; + +-- name: PayDailyPaid :many +SELECT CAST(strftime('%Y-%m-%d', paid_at, 'unixepoch', 'localtime') AS TEXT) AS day, + COALESCE(SUM(money), 0) AS money, COUNT(*) AS cnt +FROM pay_orders WHERE status = 1 AND paid_at >= ? GROUP BY day; + +-- name: RecentPaidOrders :many +SELECT trade_no, subject, money, paid_at FROM pay_orders WHERE status = 1 ORDER BY paid_at DESC LIMIT ?; + +-- name: LicenseCardStatsAll :one +SELECT COUNT(*) AS total, + COALESCE(SUM(CASE WHEN activated_at = 0 THEN 1 ELSE 0 END), 0) AS unused, + COALESCE(SUM(CASE WHEN activated_at > 0 AND (expires_at = 0 OR expires_at > ?) THEN 1 ELSE 0 END), 0) AS active +FROM license_cards; + +-- name: CountLicenseApps :one +SELECT COUNT(*) FROM license_apps; + +-- name: CountProxiesAll :one +SELECT COUNT(*) FROM proxies; + +-- name: CountBookmarksByUser :one +SELECT COUNT(*) FROM bookmarks WHERE user_id = ?; + +-- name: CountVaultByUser :one +SELECT COUNT(*) FROM vault WHERE user_id = ?; + +-- name: CountDbconns :one +SELECT COUNT(*) FROM dbconns; + +-- name: CountFilesAll :one +SELECT COUNT(*) FROM files; diff --git a/backend/internal/db/queries.sql.go b/backend/internal/db/queries.sql.go new file mode 100644 index 0000000..2f8655b --- /dev/null +++ b/backend/internal/db/queries.sql.go @@ -0,0 +1,3102 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: queries.sql + +package db + +import ( + "context" + "database/sql" +) + +const activateLicenseCard = `-- name: ActivateLicenseCard :exec +UPDATE license_cards SET domain = ?, activated_at = ?, expires_at = ? WHERE id = ? +` + +type ActivateLicenseCardParams struct { + Domain string + ActivatedAt int64 + ExpiresAt int64 + ID int64 +} + +func (q *Queries) ActivateLicenseCard(ctx context.Context, arg ActivateLicenseCardParams) error { + _, err := q.db.ExecContext(ctx, activateLicenseCard, + arg.Domain, + arg.ActivatedAt, + arg.ExpiresAt, + arg.ID, + ) + return err +} + +const countActiveSessions = `-- name: CountActiveSessions :one +SELECT COUNT(*) FROM sessions WHERE expires_at > ? +` + +func (q *Queries) CountActiveSessions(ctx context.Context, expiresAt int64) (int64, error) { + row := q.db.QueryRowContext(ctx, countActiveSessions, expiresAt) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countActiveUsers = `-- name: CountActiveUsers :one +SELECT COUNT(*) FROM users WHERE status = 'active' +` + +func (q *Queries) CountActiveUsers(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countActiveUsers) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countAdmins = `-- name: CountAdmins :one +SELECT COUNT(*) FROM users WHERE role = 'admin' AND status = 'active' +` + +func (q *Queries) CountAdmins(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countAdmins) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countAllUsers = `-- name: CountAllUsers :one +SELECT COUNT(*) FROM users +` + +func (q *Queries) CountAllUsers(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countAllUsers) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countBookmarksByUser = `-- name: CountBookmarksByUser :one +SELECT COUNT(*) FROM bookmarks WHERE user_id = ? +` + +func (q *Queries) CountBookmarksByUser(ctx context.Context, userID int64) (int64, error) { + row := q.db.QueryRowContext(ctx, countBookmarksByUser, userID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countDbconns = `-- name: CountDbconns :one +SELECT COUNT(*) FROM dbconns +` + +func (q *Queries) CountDbconns(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countDbconns) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countFilesAll = `-- name: CountFilesAll :one +SELECT COUNT(*) FROM files +` + +func (q *Queries) CountFilesAll(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countFilesAll) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countLicenseApps = `-- name: CountLicenseApps :one +SELECT COUNT(*) FROM license_apps +` + +func (q *Queries) CountLicenseApps(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countLicenseApps) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countLicenseCards = `-- name: CountLicenseCards :one +SELECT COUNT(*) FROM license_cards +WHERE app_id = ? AND (? = '' OR card LIKE '%' || ? || '%' OR domain LIKE '%' || ? || '%') +` + +type CountLicenseCardsParams struct { + AppID int64 + Column2 interface{} + Column3 sql.NullString + Column4 sql.NullString +} + +func (q *Queries) CountLicenseCards(ctx context.Context, arg CountLicenseCardsParams) (int64, error) { + row := q.db.QueryRowContext(ctx, countLicenseCards, + arg.AppID, + arg.Column2, + arg.Column3, + arg.Column4, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countMemories = `-- name: CountMemories :one +SELECT COUNT(*) FROM memories +` + +func (q *Queries) CountMemories(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countMemories) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countPayOrders = `-- name: CountPayOrders :one +SELECT COUNT(*) FROM pay_orders +WHERE (? = -1 OR status = ?) AND (? = '' OR trade_no LIKE '%' || ? || '%' OR out_trade_no LIKE '%' || ? || '%') +` + +type CountPayOrdersParams struct { + Column1 interface{} + Status int64 + Column3 interface{} + Column4 sql.NullString + Column5 sql.NullString +} + +func (q *Queries) CountPayOrders(ctx context.Context, arg CountPayOrdersParams) (int64, error) { + row := q.db.QueryRowContext(ctx, countPayOrders, + arg.Column1, + arg.Status, + arg.Column3, + arg.Column4, + arg.Column5, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countProxies = `-- name: CountProxies :one +SELECT COUNT(*) FROM proxies WHERE alive = 1 +` + +func (q *Queries) CountProxies(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countProxies) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countProxiesAll = `-- name: CountProxiesAll :one +SELECT COUNT(*) FROM proxies +` + +func (q *Queries) CountProxiesAll(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countProxiesAll) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countProxiesFiltered = `-- name: CountProxiesFiltered :one +SELECT COUNT(*) FROM proxies p WHERE (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR p.ip LIKE '%' || ? || '%') AND (? < 0 OR p.alive = ?) +` + +type CountProxiesFilteredParams struct { + Column1 interface{} + RegionID int64 + Column3 interface{} + Column4 sql.NullString + Column5 interface{} + Column6 sql.NullString + Column7 interface{} + Alive int64 +} + +func (q *Queries) CountProxiesFiltered(ctx context.Context, arg CountProxiesFilteredParams) (int64, error) { + row := q.db.QueryRowContext(ctx, countProxiesFiltered, + arg.Column1, + arg.RegionID, + arg.Column3, + arg.Column4, + arg.Column5, + arg.Column6, + arg.Column7, + arg.Alive, + ) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countServers = `-- name: CountServers :one +SELECT COUNT(*) FROM servers +` + +func (q *Queries) CountServers(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countServers) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countTodayLogins = `-- name: CountTodayLogins :one +SELECT COUNT(*) FROM sessions +WHERE date(created_at, 'unixepoch', 'localtime') = date('now', 'localtime') +` + +func (q *Queries) CountTodayLogins(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countTodayLogins) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countTools = `-- name: CountTools :one +SELECT COUNT(*) FROM tools WHERE enabled = 1 +` + +func (q *Queries) CountTools(ctx context.Context) (int64, error) { + row := q.db.QueryRowContext(ctx, countTools) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countUsers = `-- name: CountUsers :one +SELECT COUNT(*) FROM users WHERE name LIKE '%' || ?1 || '%' +` + +func (q *Queries) CountUsers(ctx context.Context, keyword sql.NullString) (int64, error) { + row := q.db.QueryRowContext(ctx, countUsers, keyword) + var count int64 + err := row.Scan(&count) + return count, err +} + +const countVaultByUser = `-- name: CountVaultByUser :one +SELECT COUNT(*) FROM vault WHERE user_id = ? +` + +func (q *Queries) CountVaultByUser(ctx context.Context, userID int64) (int64, error) { + row := q.db.QueryRowContext(ctx, countVaultByUser, userID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const createLicenseApp = `-- name: CreateLicenseApp :execlastid +INSERT INTO license_apps (appid, secret, name, created_at) VALUES (?, ?, ?, ?) +` + +type CreateLicenseAppParams struct { + Appid string + Secret string + Name string + CreatedAt int64 +} + +func (q *Queries) CreateLicenseApp(ctx context.Context, arg CreateLicenseAppParams) (int64, error) { + result, err := q.db.ExecContext(ctx, createLicenseApp, + arg.Appid, + arg.Secret, + arg.Name, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const createLicenseCard = `-- name: CreateLicenseCard :exec +INSERT INTO license_cards (app_id, card, hours, created_at) VALUES (?, ?, ?, ?) +` + +type CreateLicenseCardParams struct { + AppID int64 + Card string + Hours int64 + CreatedAt int64 +} + +func (q *Queries) CreateLicenseCard(ctx context.Context, arg CreateLicenseCardParams) error { + _, err := q.db.ExecContext(ctx, createLicenseCard, + arg.AppID, + arg.Card, + arg.Hours, + arg.CreatedAt, + ) + return err +} + +const createPayOrder = `-- name: CreatePayOrder :execlastid +INSERT INTO pay_orders (trade_no, out_trade_no, channel, subject, money, fee, notify_url, return_url, client_ip, expired_at, created_at) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type CreatePayOrderParams struct { + TradeNo string + OutTradeNo string + Channel string + Subject string + Money int64 + Fee int64 + NotifyUrl string + ReturnUrl string + ClientIp string + ExpiredAt int64 + CreatedAt int64 +} + +func (q *Queries) CreatePayOrder(ctx context.Context, arg CreatePayOrderParams) (int64, error) { + result, err := q.db.ExecContext(ctx, createPayOrder, + arg.TradeNo, + arg.OutTradeNo, + arg.Channel, + arg.Subject, + arg.Money, + arg.Fee, + arg.NotifyUrl, + arg.ReturnUrl, + arg.ClientIp, + arg.ExpiredAt, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const deleteBookmark = `-- name: DeleteBookmark :execrows +DELETE FROM bookmarks WHERE user_id = ? AND id = ? +` + +type DeleteBookmarkParams struct { + UserID int64 + ID int64 +} + +func (q *Queries) DeleteBookmark(ctx context.Context, arg DeleteBookmarkParams) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteBookmark, arg.UserID, arg.ID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteDbconn = `-- name: DeleteDbconn :execrows +DELETE FROM dbconns WHERE id = ? +` + +func (q *Queries) DeleteDbconn(ctx context.Context, id int64) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteDbconn, id) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteDeadProxies = `-- name: DeleteDeadProxies :execrows +DELETE FROM proxies WHERE alive = 0 AND last_checked_at < ? +` + +func (q *Queries) DeleteDeadProxies(ctx context.Context, lastCheckedAt int64) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteDeadProxies, lastCheckedAt) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteFile = `-- name: DeleteFile :execrows +DELETE FROM files WHERE id = ? AND user_id = ? +` + +type DeleteFileParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) DeleteFile(ctx context.Context, arg DeleteFileParams) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteFile, arg.ID, arg.UserID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteLicenseApp = `-- name: DeleteLicenseApp :exec +DELETE FROM license_apps WHERE id = ? +` + +func (q *Queries) DeleteLicenseApp(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteLicenseApp, id) + return err +} + +const deleteLicenseCard = `-- name: DeleteLicenseCard :exec +DELETE FROM license_cards WHERE id = ? +` + +func (q *Queries) DeleteLicenseCard(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteLicenseCard, id) + return err +} + +const deleteMemory = `-- name: DeleteMemory :execrows +DELETE FROM memories WHERE k = ? +` + +func (q *Queries) DeleteMemory(ctx context.Context, k string) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteMemory, k) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteOtherSessions = `-- name: DeleteOtherSessions :exec +DELETE FROM sessions WHERE user_id = ? AND token != ? +` + +type DeleteOtherSessionsParams struct { + UserID int64 + Token string +} + +func (q *Queries) DeleteOtherSessions(ctx context.Context, arg DeleteOtherSessionsParams) error { + _, err := q.db.ExecContext(ctx, deleteOtherSessions, arg.UserID, arg.Token) + return err +} + +const deleteProxy = `-- name: DeleteProxy :exec +DELETE FROM proxies WHERE id = ? +` + +func (q *Queries) DeleteProxy(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteProxy, id) + return err +} + +const deleteProxyApiKey = `-- name: DeleteProxyApiKey :execrows +DELETE FROM proxy_keys WHERE id = ? +` + +func (q *Queries) DeleteProxyApiKey(ctx context.Context, id int64) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteProxyApiKey, id) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteServer = `-- name: DeleteServer :execrows +DELETE FROM servers WHERE id = ? +` + +func (q *Queries) DeleteServer(ctx context.Context, id int64) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteServer, id) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteSession = `-- name: DeleteSession :exec +DELETE FROM sessions WHERE token = ? +` + +func (q *Queries) DeleteSession(ctx context.Context, token string) error { + _, err := q.db.ExecContext(ctx, deleteSession, token) + return err +} + +const deleteShare = `-- name: DeleteShare :execrows +DELETE FROM shares WHERE id = ? AND user_id = ? +` + +type DeleteShareParams struct { + ID int64 + UserID int64 +} + +func (q *Queries) DeleteShare(ctx context.Context, arg DeleteShareParams) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteShare, arg.ID, arg.UserID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const deleteSharesByFile = `-- name: DeleteSharesByFile :exec +DELETE FROM shares WHERE file_id = ? +` + +func (q *Queries) DeleteSharesByFile(ctx context.Context, fileID int64) error { + _, err := q.db.ExecContext(ctx, deleteSharesByFile, fileID) + return err +} + +const deleteTool = `-- name: DeleteTool :exec +DELETE FROM tools WHERE id = ? +` + +func (q *Queries) DeleteTool(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteTool, id) + return err +} + +const deleteUser = `-- name: DeleteUser :exec +DELETE FROM users WHERE id = ? +` + +func (q *Queries) DeleteUser(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteUser, id) + return err +} + +const deleteVault = `-- name: DeleteVault :execrows +DELETE FROM vault WHERE user_id = ? AND id = ? +` + +type DeleteVaultParams struct { + UserID int64 + ID int64 +} + +func (q *Queries) DeleteVault(ctx context.Context, arg DeleteVaultParams) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteVault, arg.UserID, arg.ID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const finishProxySync = `-- name: FinishProxySync :exec +UPDATE proxy_syncs SET finished_at = ?, status = ?, total = ?, added = ?, removed = ?, updated = ?, detail = ?, error = ? WHERE id = ? +` + +type FinishProxySyncParams struct { + FinishedAt int64 + Status string + Total int64 + Added int64 + Removed int64 + Updated int64 + Detail string + Error string + ID int64 +} + +func (q *Queries) FinishProxySync(ctx context.Context, arg FinishProxySyncParams) error { + _, err := q.db.ExecContext(ctx, finishProxySync, + arg.FinishedAt, + arg.Status, + arg.Total, + arg.Added, + arg.Removed, + arg.Updated, + arg.Detail, + arg.Error, + arg.ID, + ) + return err +} + +const getDbconn = `-- name: GetDbconn :one +SELECT id, name, engine, host, port, username, password_enc, "database", params, created_at, updated_at FROM dbconns WHERE id = ? +` + +func (q *Queries) GetDbconn(ctx context.Context, id int64) (Dbconn, error) { + row := q.db.QueryRowContext(ctx, getDbconn, id) + var i Dbconn + err := row.Scan( + &i.ID, + &i.Name, + &i.Engine, + &i.Host, + &i.Port, + &i.Username, + &i.PasswordEnc, + &i.Database, + &i.Params, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getFile = `-- name: GetFile :one +SELECT id, user_id, name, stored, size, mime, created_at FROM files WHERE id = ? +` + +func (q *Queries) GetFile(ctx context.Context, id int64) (File, error) { + row := q.db.QueryRowContext(ctx, getFile, id) + var i File + err := row.Scan( + &i.ID, + &i.UserID, + &i.Name, + &i.Stored, + &i.Size, + &i.Mime, + &i.CreatedAt, + ) + return i, err +} + +const getLicenseAppByAppid = `-- name: GetLicenseAppByAppid :one +SELECT id, appid, secret, name, enabled, created_at FROM license_apps WHERE appid = ? +` + +func (q *Queries) GetLicenseAppByAppid(ctx context.Context, appid string) (LicenseApp, error) { + row := q.db.QueryRowContext(ctx, getLicenseAppByAppid, appid) + var i LicenseApp + err := row.Scan( + &i.ID, + &i.Appid, + &i.Secret, + &i.Name, + &i.Enabled, + &i.CreatedAt, + ) + return i, err +} + +const getLicenseCard = `-- name: GetLicenseCard :one +SELECT id, app_id, card, hours, domain, activated_at, expires_at, created_at FROM license_cards WHERE app_id = ? AND card = ? +` + +type GetLicenseCardParams struct { + AppID int64 + Card string +} + +func (q *Queries) GetLicenseCard(ctx context.Context, arg GetLicenseCardParams) (LicenseCard, error) { + row := q.db.QueryRowContext(ctx, getLicenseCard, arg.AppID, arg.Card) + var i LicenseCard + err := row.Scan( + &i.ID, + &i.AppID, + &i.Card, + &i.Hours, + &i.Domain, + &i.ActivatedAt, + &i.ExpiresAt, + &i.CreatedAt, + ) + return i, err +} + +const getMemory = `-- name: GetMemory :one +SELECT k, content, project, tags, written_by, revision, created_at, updated_at FROM memories WHERE k = ? +` + +func (q *Queries) GetMemory(ctx context.Context, k string) (Memory, error) { + row := q.db.QueryRowContext(ctx, getMemory, k) + var i Memory + err := row.Scan( + &i.K, + &i.Content, + &i.Project, + &i.Tags, + &i.WrittenBy, + &i.Revision, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getPayOrder = `-- name: GetPayOrder :one +SELECT id, trade_no, out_trade_no, channel, subject, money, fee, status, notify_url, return_url, client_ip, notified, notify_attempts, paid_at, expired_at, upstream_trade_no, goods_key, quantity, unit_price, payurl, qrcode, buyer_contact, query_pwd, cards, created_at FROM pay_orders WHERE id = ? +` + +func (q *Queries) GetPayOrder(ctx context.Context, id int64) (PayOrder, error) { + row := q.db.QueryRowContext(ctx, getPayOrder, id) + var i PayOrder + err := row.Scan( + &i.ID, + &i.TradeNo, + &i.OutTradeNo, + &i.Channel, + &i.Subject, + &i.Money, + &i.Fee, + &i.Status, + &i.NotifyUrl, + &i.ReturnUrl, + &i.ClientIp, + &i.Notified, + &i.NotifyAttempts, + &i.PaidAt, + &i.ExpiredAt, + &i.UpstreamTradeNo, + &i.GoodsKey, + &i.Quantity, + &i.UnitPrice, + &i.Payurl, + &i.Qrcode, + &i.BuyerContact, + &i.QueryPwd, + &i.Cards, + &i.CreatedAt, + ) + return i, err +} + +const getPayOrderByOutNo = `-- name: GetPayOrderByOutNo :one +SELECT id, trade_no, out_trade_no, channel, subject, money, fee, status, notify_url, return_url, client_ip, notified, notify_attempts, paid_at, expired_at, upstream_trade_no, goods_key, quantity, unit_price, payurl, qrcode, buyer_contact, query_pwd, cards, created_at FROM pay_orders WHERE out_trade_no = ? +` + +func (q *Queries) GetPayOrderByOutNo(ctx context.Context, outTradeNo string) (PayOrder, error) { + row := q.db.QueryRowContext(ctx, getPayOrderByOutNo, outTradeNo) + var i PayOrder + err := row.Scan( + &i.ID, + &i.TradeNo, + &i.OutTradeNo, + &i.Channel, + &i.Subject, + &i.Money, + &i.Fee, + &i.Status, + &i.NotifyUrl, + &i.ReturnUrl, + &i.ClientIp, + &i.Notified, + &i.NotifyAttempts, + &i.PaidAt, + &i.ExpiredAt, + &i.UpstreamTradeNo, + &i.GoodsKey, + &i.Quantity, + &i.UnitPrice, + &i.Payurl, + &i.Qrcode, + &i.BuyerContact, + &i.QueryPwd, + &i.Cards, + &i.CreatedAt, + ) + return i, err +} + +const getPayOrderByTradeNo = `-- name: GetPayOrderByTradeNo :one +SELECT id, trade_no, out_trade_no, channel, subject, money, fee, status, notify_url, return_url, client_ip, notified, notify_attempts, paid_at, expired_at, upstream_trade_no, goods_key, quantity, unit_price, payurl, qrcode, buyer_contact, query_pwd, cards, created_at FROM pay_orders WHERE trade_no = ? +` + +func (q *Queries) GetPayOrderByTradeNo(ctx context.Context, tradeNo string) (PayOrder, error) { + row := q.db.QueryRowContext(ctx, getPayOrderByTradeNo, tradeNo) + var i PayOrder + err := row.Scan( + &i.ID, + &i.TradeNo, + &i.OutTradeNo, + &i.Channel, + &i.Subject, + &i.Money, + &i.Fee, + &i.Status, + &i.NotifyUrl, + &i.ReturnUrl, + &i.ClientIp, + &i.Notified, + &i.NotifyAttempts, + &i.PaidAt, + &i.ExpiredAt, + &i.UpstreamTradeNo, + &i.GoodsKey, + &i.Quantity, + &i.UnitPrice, + &i.Payurl, + &i.Qrcode, + &i.BuyerContact, + &i.QueryPwd, + &i.Cards, + &i.CreatedAt, + ) + return i, err +} + +const getProxyApiKey = `-- name: GetProxyApiKey :one +SELECT id, kkey, name, region_id, enabled, used_count, last_used_at, created_at FROM proxy_keys WHERE kkey = ? AND enabled = 1 +` + +func (q *Queries) GetProxyApiKey(ctx context.Context, kkey string) (ProxyKey, error) { + row := q.db.QueryRowContext(ctx, getProxyApiKey, kkey) + var i ProxyKey + err := row.Scan( + &i.ID, + &i.Kkey, + &i.Name, + &i.RegionID, + &i.Enabled, + &i.UsedCount, + &i.LastUsedAt, + &i.CreatedAt, + ) + return i, err +} + +const getProxyRegionByCode = `-- name: GetProxyRegionByCode :one +SELECT id, code, name, zh_name, region, slug FROM proxy_regions WHERE code = ? +` + +func (q *Queries) GetProxyRegionByCode(ctx context.Context, code string) (ProxyRegion, error) { + row := q.db.QueryRowContext(ctx, getProxyRegionByCode, code) + var i ProxyRegion + err := row.Scan( + &i.ID, + &i.Code, + &i.Name, + &i.ZhName, + &i.Region, + &i.Slug, + ) + return i, err +} + +const getServer = `-- name: GetServer :one +SELECT id, name, host, port, username, auth_type, secret_enc, note, created_at, updated_at FROM servers WHERE id = ? +` + +func (q *Queries) GetServer(ctx context.Context, id int64) (Server, error) { + row := q.db.QueryRowContext(ctx, getServer, id) + var i Server + err := row.Scan( + &i.ID, + &i.Name, + &i.Host, + &i.Port, + &i.Username, + &i.AuthType, + &i.SecretEnc, + &i.Note, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getSessionUser = `-- name: GetSessionUser :one +SELECT u.id, u.name, u.password_hash, u.role, u.status, u.created_at, u.updated_at, s.expires_at +FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? +` + +type GetSessionUserRow struct { + ID int64 + Name string + PasswordHash string + Role string + Status string + CreatedAt int64 + UpdatedAt int64 + ExpiresAt int64 +} + +func (q *Queries) GetSessionUser(ctx context.Context, token string) (GetSessionUserRow, error) { + row := q.db.QueryRowContext(ctx, getSessionUser, token) + var i GetSessionUserRow + err := row.Scan( + &i.ID, + &i.Name, + &i.PasswordHash, + &i.Role, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + &i.ExpiresAt, + ) + return i, err +} + +const getSetting = `-- name: GetSetting :one +SELECT v FROM settings WHERE k = ? +` + +func (q *Queries) GetSetting(ctx context.Context, k string) (string, error) { + row := q.db.QueryRowContext(ctx, getSetting, k) + var v string + err := row.Scan(&v) + return v, err +} + +const getShareByCode = `-- name: GetShareByCode :one +SELECT sh.id, sh.code, sh.file_id, sh.user_id, sh.file_name, sh.expires_at, sh.downloads, sh.created_at, + f.name, f.stored, f.size, f.mime +FROM shares sh JOIN files f ON f.id = sh.file_id WHERE sh.code = ? +` + +type GetShareByCodeRow struct { + ID int64 + Code string + FileID int64 + UserID int64 + FileName string + ExpiresAt int64 + Downloads int64 + CreatedAt int64 + Name string + Stored string + Size int64 + Mime string +} + +func (q *Queries) GetShareByCode(ctx context.Context, code string) (GetShareByCodeRow, error) { + row := q.db.QueryRowContext(ctx, getShareByCode, code) + var i GetShareByCodeRow + err := row.Scan( + &i.ID, + &i.Code, + &i.FileID, + &i.UserID, + &i.FileName, + &i.ExpiresAt, + &i.Downloads, + &i.CreatedAt, + &i.Name, + &i.Stored, + &i.Size, + &i.Mime, + ) + return i, err +} + +const getUserByID = `-- name: GetUserByID :one +SELECT id, name, password_hash, role, status, created_at, updated_at FROM users WHERE id = ? +` + +func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByID, id) + var i User + err := row.Scan( + &i.ID, + &i.Name, + &i.PasswordHash, + &i.Role, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getUserByName = `-- name: GetUserByName :one +SELECT id, name, password_hash, role, status, created_at, updated_at FROM users WHERE name = ? LIMIT 1 +` + +func (q *Queries) GetUserByName(ctx context.Context, name string) (User, error) { + row := q.db.QueryRowContext(ctx, getUserByName, name) + var i User + err := row.Scan( + &i.ID, + &i.Name, + &i.PasswordHash, + &i.Role, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getVault = `-- name: GetVault :one +SELECT id, user_id, title, username, password_enc, url, note, totp_enc, created_at, updated_at FROM vault WHERE user_id = ? AND id = ? +` + +type GetVaultParams struct { + UserID int64 + ID int64 +} + +func (q *Queries) GetVault(ctx context.Context, arg GetVaultParams) (Vault, error) { + row := q.db.QueryRowContext(ctx, getVault, arg.UserID, arg.ID) + var i Vault + err := row.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Username, + &i.PasswordEnc, + &i.Url, + &i.Note, + &i.TotpEnc, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const incPayNotifyAttempts = `-- name: IncPayNotifyAttempts :exec +UPDATE pay_orders SET notify_attempts = notify_attempts + 1 WHERE id = ? +` + +func (q *Queries) IncPayNotifyAttempts(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, incPayNotifyAttempts, id) + return err +} + +const incShareDownloads = `-- name: IncShareDownloads :exec +UPDATE shares SET downloads = downloads + 1 WHERE code = ? +` + +func (q *Queries) IncShareDownloads(ctx context.Context, code string) error { + _, err := q.db.ExecContext(ctx, incShareDownloads, code) + return err +} + +const insertBookmark = `-- name: InsertBookmark :execlastid +INSERT INTO bookmarks(user_id, title, url, icon, sort, created_at) VALUES(?, ?, ?, ?, ?, ?) +` + +type InsertBookmarkParams struct { + UserID int64 + Title string + Url string + Icon string + Sort int64 + CreatedAt int64 +} + +func (q *Queries) InsertBookmark(ctx context.Context, arg InsertBookmarkParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertBookmark, + arg.UserID, + arg.Title, + arg.Url, + arg.Icon, + arg.Sort, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertDbconn = `-- name: InsertDbconn :execlastid +INSERT INTO dbconns(name, engine, host, port, username, password_enc, database, params, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type InsertDbconnParams struct { + Name string + Engine string + Host string + Port int64 + Username string + PasswordEnc string + Database string + Params string + CreatedAt int64 + UpdatedAt int64 +} + +func (q *Queries) InsertDbconn(ctx context.Context, arg InsertDbconnParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertDbconn, + arg.Name, + arg.Engine, + arg.Host, + arg.Port, + arg.Username, + arg.PasswordEnc, + arg.Database, + arg.Params, + arg.CreatedAt, + arg.UpdatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertFile = `-- name: InsertFile :execlastid +INSERT INTO files(user_id, name, stored, size, mime, created_at) VALUES(?, ?, ?, ?, ?, ?) +` + +type InsertFileParams struct { + UserID int64 + Name string + Stored string + Size int64 + Mime string + CreatedAt int64 +} + +func (q *Queries) InsertFile(ctx context.Context, arg InsertFileParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertFile, + arg.UserID, + arg.Name, + arg.Stored, + arg.Size, + arg.Mime, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertProxy = `-- name: InsertProxy :execlastid +INSERT INTO proxies(ip, port, protocols, anonymity, source, region_id, latency, speed, uptime, alive, last_seen_at, created_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?) +` + +type InsertProxyParams struct { + Ip string + Port int64 + Protocols string + Anonymity string + Source string + RegionID int64 + Latency int64 + Speed int64 + Uptime int64 + LastSeenAt int64 + CreatedAt int64 +} + +func (q *Queries) InsertProxy(ctx context.Context, arg InsertProxyParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertProxy, + arg.Ip, + arg.Port, + arg.Protocols, + arg.Anonymity, + arg.Source, + arg.RegionID, + arg.Latency, + arg.Speed, + arg.Uptime, + arg.LastSeenAt, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertProxyApiKey = `-- name: InsertProxyApiKey :execlastid +INSERT INTO proxy_keys(kkey, name, region_id, created_at) VALUES(?, ?, ?, ?) +` + +type InsertProxyApiKeyParams struct { + Kkey string + Name string + RegionID int64 + CreatedAt int64 +} + +func (q *Queries) InsertProxyApiKey(ctx context.Context, arg InsertProxyApiKeyParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertProxyApiKey, + arg.Kkey, + arg.Name, + arg.RegionID, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertProxySync = `-- name: InsertProxySync :execlastid +INSERT INTO proxy_syncs(started_at, source) VALUES(?, ?) +` + +type InsertProxySyncParams struct { + StartedAt int64 + Source string +} + +func (q *Queries) InsertProxySync(ctx context.Context, arg InsertProxySyncParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertProxySync, arg.StartedAt, arg.Source) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertServer = `-- name: InsertServer :execlastid +INSERT INTO servers(name, host, port, username, auth_type, secret_enc, note, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type InsertServerParams struct { + Name string + Host string + Port int64 + Username string + AuthType string + SecretEnc string + Note string + CreatedAt int64 + UpdatedAt int64 +} + +func (q *Queries) InsertServer(ctx context.Context, arg InsertServerParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertServer, + arg.Name, + arg.Host, + arg.Port, + arg.Username, + arg.AuthType, + arg.SecretEnc, + arg.Note, + arg.CreatedAt, + arg.UpdatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertSession = `-- name: InsertSession :exec +INSERT INTO sessions(token, user_id, created_at, expires_at) VALUES(?, ?, ?, ?) +` + +type InsertSessionParams struct { + Token string + UserID int64 + CreatedAt int64 + ExpiresAt int64 +} + +func (q *Queries) InsertSession(ctx context.Context, arg InsertSessionParams) error { + _, err := q.db.ExecContext(ctx, insertSession, + arg.Token, + arg.UserID, + arg.CreatedAt, + arg.ExpiresAt, + ) + return err +} + +const insertShare = `-- name: InsertShare :execlastid +INSERT INTO shares(code, file_id, user_id, file_name, expires_at, created_at) VALUES(?, ?, ?, ?, ?, ?) +` + +type InsertShareParams struct { + Code string + FileID int64 + UserID int64 + FileName string + ExpiresAt int64 + CreatedAt int64 +} + +func (q *Queries) InsertShare(ctx context.Context, arg InsertShareParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertShare, + arg.Code, + arg.FileID, + arg.UserID, + arg.FileName, + arg.ExpiresAt, + arg.CreatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertTool = `-- name: InsertTool :execlastid +INSERT INTO tools(name, icon, url, description, sort, enabled) VALUES(?, ?, ?, ?, ?, ?) +` + +type InsertToolParams struct { + Name string + Icon string + Url string + Description string + Sort int64 + Enabled bool +} + +func (q *Queries) InsertTool(ctx context.Context, arg InsertToolParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertTool, + arg.Name, + arg.Icon, + arg.Url, + arg.Description, + arg.Sort, + arg.Enabled, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertUser = `-- name: InsertUser :execlastid +INSERT INTO users(name, password_hash, role, status, created_at, updated_at) +VALUES(?, ?, ?, 'active', ?, ?) +` + +type InsertUserParams struct { + Name string + PasswordHash string + Role string + CreatedAt int64 + UpdatedAt int64 +} + +func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertUser, + arg.Name, + arg.PasswordHash, + arg.Role, + arg.CreatedAt, + arg.UpdatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const insertVault = `-- name: InsertVault :execlastid +INSERT INTO vault(user_id, title, username, password_enc, url, note, totp_enc, created_at, updated_at) +VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) +` + +type InsertVaultParams struct { + UserID int64 + Title string + Username string + PasswordEnc string + Url string + Note string + TotpEnc string + CreatedAt int64 + UpdatedAt int64 +} + +func (q *Queries) InsertVault(ctx context.Context, arg InsertVaultParams) (int64, error) { + result, err := q.db.ExecContext(ctx, insertVault, + arg.UserID, + arg.Title, + arg.Username, + arg.PasswordEnc, + arg.Url, + arg.Note, + arg.TotpEnc, + arg.CreatedAt, + arg.UpdatedAt, + ) + if err != nil { + return 0, err + } + return result.LastInsertId() +} + +const lastProxyCheckAt = `-- name: LastProxyCheckAt :one +SELECT COALESCE(MAX(last_checked_at), 0) FROM proxies +` + +func (q *Queries) LastProxyCheckAt(ctx context.Context) (interface{}, error) { + row := q.db.QueryRowContext(ctx, lastProxyCheckAt) + var coalesce interface{} + err := row.Scan(&coalesce) + return coalesce, err +} + +const licenseCardStats = `-- name: LicenseCardStats :one +SELECT COUNT(*) AS total, + COALESCE(SUM(CASE WHEN activated_at = 0 THEN 1 ELSE 0 END), 0) AS unused, + COALESCE(SUM(CASE WHEN activated_at > 0 AND (expires_at = 0 OR expires_at > ?) THEN 1 ELSE 0 END), 0) AS active, + COALESCE(SUM(CASE WHEN activated_at > 0 AND expires_at > 0 AND expires_at <= ? THEN 1 ELSE 0 END), 0) AS expired +FROM license_cards WHERE app_id = ? +` + +type LicenseCardStatsParams struct { + ExpiresAt int64 + ExpiresAt_2 int64 + AppID int64 +} + +type LicenseCardStatsRow struct { + Total int64 + Unused interface{} + Active interface{} + Expired interface{} +} + +func (q *Queries) LicenseCardStats(ctx context.Context, arg LicenseCardStatsParams) (LicenseCardStatsRow, error) { + row := q.db.QueryRowContext(ctx, licenseCardStats, arg.ExpiresAt, arg.ExpiresAt_2, arg.AppID) + var i LicenseCardStatsRow + err := row.Scan( + &i.Total, + &i.Unused, + &i.Active, + &i.Expired, + ) + return i, err +} + +const licenseCardStatsAll = `-- name: LicenseCardStatsAll :one +SELECT COUNT(*) AS total, + COALESCE(SUM(CASE WHEN activated_at = 0 THEN 1 ELSE 0 END), 0) AS unused, + COALESCE(SUM(CASE WHEN activated_at > 0 AND (expires_at = 0 OR expires_at > ?) THEN 1 ELSE 0 END), 0) AS active +FROM license_cards +` + +type LicenseCardStatsAllRow struct { + Total int64 + Unused interface{} + Active interface{} +} + +func (q *Queries) LicenseCardStatsAll(ctx context.Context, expiresAt int64) (LicenseCardStatsAllRow, error) { + row := q.db.QueryRowContext(ctx, licenseCardStatsAll, expiresAt) + var i LicenseCardStatsAllRow + err := row.Scan(&i.Total, &i.Unused, &i.Active) + return i, err +} + +const listBookmarks = `-- name: ListBookmarks :many +SELECT id, user_id, title, url, icon, sort, created_at FROM bookmarks WHERE user_id = ? ORDER BY sort, id +` + +func (q *Queries) ListBookmarks(ctx context.Context, userID int64) ([]Bookmark, error) { + rows, err := q.db.QueryContext(ctx, listBookmarks, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Bookmark{} + for rows.Next() { + var i Bookmark + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Url, + &i.Icon, + &i.Sort, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listDbconns = `-- name: ListDbconns :many +SELECT id, name, engine, host, port, username, password_enc, "database", params, created_at, updated_at FROM dbconns ORDER BY id +` + +func (q *Queries) ListDbconns(ctx context.Context) ([]Dbconn, error) { + rows, err := q.db.QueryContext(ctx, listDbconns) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Dbconn{} + for rows.Next() { + var i Dbconn + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Engine, + &i.Host, + &i.Port, + &i.Username, + &i.PasswordEnc, + &i.Database, + &i.Params, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listEnabledTools = `-- name: ListEnabledTools :many +SELECT id, name, icon, url, description, sort, enabled FROM tools WHERE enabled = 1 ORDER BY sort, id +` + +func (q *Queries) ListEnabledTools(ctx context.Context) ([]Tool, error) { + rows, err := q.db.QueryContext(ctx, listEnabledTools) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Tool{} + for rows.Next() { + var i Tool + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Icon, + &i.Url, + &i.Description, + &i.Sort, + &i.Enabled, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listFiles = `-- name: ListFiles :many +SELECT id, user_id, name, stored, size, mime, created_at FROM files WHERE user_id = ? ORDER BY id DESC +` + +func (q *Queries) ListFiles(ctx context.Context, userID int64) ([]File, error) { + rows, err := q.db.QueryContext(ctx, listFiles, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []File{} + for rows.Next() { + var i File + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.Name, + &i.Stored, + &i.Size, + &i.Mime, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listLicenseApps = `-- name: ListLicenseApps :many +SELECT a.id, a.appid, a.secret, a.name, a.enabled, a.created_at, (SELECT COUNT(*) FROM license_cards c WHERE c.app_id = a.id) AS card_count +FROM license_apps a ORDER BY a.id DESC +` + +type ListLicenseAppsRow struct { + ID int64 + Appid string + Secret string + Name string + Enabled int64 + CreatedAt int64 + CardCount int64 +} + +func (q *Queries) ListLicenseApps(ctx context.Context) ([]ListLicenseAppsRow, error) { + rows, err := q.db.QueryContext(ctx, listLicenseApps) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListLicenseAppsRow{} + for rows.Next() { + var i ListLicenseAppsRow + if err := rows.Scan( + &i.ID, + &i.Appid, + &i.Secret, + &i.Name, + &i.Enabled, + &i.CreatedAt, + &i.CardCount, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listLicenseCards = `-- name: ListLicenseCards :many +SELECT id, app_id, card, hours, domain, activated_at, expires_at, created_at FROM license_cards +WHERE app_id = ? AND (? = '' OR card LIKE '%' || ? || '%' OR domain LIKE '%' || ? || '%') +ORDER BY id DESC LIMIT ? OFFSET ? +` + +type ListLicenseCardsParams struct { + AppID int64 + Column2 interface{} + Column3 sql.NullString + Column4 sql.NullString + Limit int64 + Offset int64 +} + +func (q *Queries) ListLicenseCards(ctx context.Context, arg ListLicenseCardsParams) ([]LicenseCard, error) { + rows, err := q.db.QueryContext(ctx, listLicenseCards, + arg.AppID, + arg.Column2, + arg.Column3, + arg.Column4, + arg.Limit, + arg.Offset, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []LicenseCard{} + for rows.Next() { + var i LicenseCard + if err := rows.Scan( + &i.ID, + &i.AppID, + &i.Card, + &i.Hours, + &i.Domain, + &i.ActivatedAt, + &i.ExpiresAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listMemories = `-- name: ListMemories :many +SELECT k, content, project, tags, written_by, revision, created_at, updated_at FROM memories +WHERE (? = '' OR project = ?) AND (? = '' OR tags LIKE '%"' || ? || '"%') +ORDER BY updated_at DESC LIMIT ? +` + +type ListMemoriesParams struct { + Column1 interface{} + Project string + Column3 interface{} + Column4 sql.NullString + Limit int64 +} + +func (q *Queries) ListMemories(ctx context.Context, arg ListMemoriesParams) ([]Memory, error) { + rows, err := q.db.QueryContext(ctx, listMemories, + arg.Column1, + arg.Project, + arg.Column3, + arg.Column4, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Memory{} + for rows.Next() { + var i Memory + if err := rows.Scan( + &i.K, + &i.Content, + &i.Project, + &i.Tags, + &i.WrittenBy, + &i.Revision, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listPayOrders = `-- name: ListPayOrders :many +SELECT id, trade_no, out_trade_no, channel, subject, money, fee, status, notify_url, return_url, client_ip, notified, notify_attempts, paid_at, expired_at, upstream_trade_no, goods_key, quantity, unit_price, payurl, qrcode, buyer_contact, query_pwd, cards, created_at FROM pay_orders +WHERE (? = -1 OR status = ?) AND (? = '' OR trade_no LIKE '%' || ? || '%' OR out_trade_no LIKE '%' || ? || '%') +ORDER BY id DESC LIMIT ? OFFSET ? +` + +type ListPayOrdersParams struct { + Column1 interface{} + Status int64 + Column3 interface{} + Column4 sql.NullString + Column5 sql.NullString + Limit int64 + Offset int64 +} + +func (q *Queries) ListPayOrders(ctx context.Context, arg ListPayOrdersParams) ([]PayOrder, error) { + rows, err := q.db.QueryContext(ctx, listPayOrders, + arg.Column1, + arg.Status, + arg.Column3, + arg.Column4, + arg.Column5, + arg.Limit, + arg.Offset, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []PayOrder{} + for rows.Next() { + var i PayOrder + if err := rows.Scan( + &i.ID, + &i.TradeNo, + &i.OutTradeNo, + &i.Channel, + &i.Subject, + &i.Money, + &i.Fee, + &i.Status, + &i.NotifyUrl, + &i.ReturnUrl, + &i.ClientIp, + &i.Notified, + &i.NotifyAttempts, + &i.PaidAt, + &i.ExpiredAt, + &i.UpstreamTradeNo, + &i.GoodsKey, + &i.Quantity, + &i.UnitPrice, + &i.Payurl, + &i.Qrcode, + &i.BuyerContact, + &i.QueryPwd, + &i.Cards, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listPendingPayOrders = `-- name: ListPendingPayOrders :many +SELECT id, trade_no, out_trade_no, channel, subject, money, fee, status, notify_url, return_url, client_ip, notified, notify_attempts, paid_at, expired_at, upstream_trade_no, goods_key, quantity, unit_price, payurl, qrcode, buyer_contact, query_pwd, cards, created_at FROM pay_orders WHERE status = 0 AND upstream_trade_no != '' ORDER BY id LIMIT 200 +` + +func (q *Queries) ListPendingPayOrders(ctx context.Context) ([]PayOrder, error) { + rows, err := q.db.QueryContext(ctx, listPendingPayOrders) + if err != nil { + return nil, err + } + defer rows.Close() + items := []PayOrder{} + for rows.Next() { + var i PayOrder + if err := rows.Scan( + &i.ID, + &i.TradeNo, + &i.OutTradeNo, + &i.Channel, + &i.Subject, + &i.Money, + &i.Fee, + &i.Status, + &i.NotifyUrl, + &i.ReturnUrl, + &i.ClientIp, + &i.Notified, + &i.NotifyAttempts, + &i.PaidAt, + &i.ExpiredAt, + &i.UpstreamTradeNo, + &i.GoodsKey, + &i.Quantity, + &i.UnitPrice, + &i.Payurl, + &i.Qrcode, + &i.BuyerContact, + &i.QueryPwd, + &i.Cards, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxiesDue = `-- name: ListProxiesDue :many +SELECT id, ip, port, protocols, fail_count FROM proxies WHERE alive = 1 AND last_checked_at < ? ORDER BY last_checked_at LIMIT 20000 +` + +type ListProxiesDueRow struct { + ID int64 + Ip string + Port int64 + Protocols string + FailCount int64 +} + +func (q *Queries) ListProxiesDue(ctx context.Context, lastCheckedAt int64) ([]ListProxiesDueRow, error) { + rows, err := q.db.QueryContext(ctx, listProxiesDue, lastCheckedAt) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListProxiesDueRow{} + for rows.Next() { + var i ListProxiesDueRow + if err := rows.Scan( + &i.ID, + &i.Ip, + &i.Port, + &i.Protocols, + &i.FailCount, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxiesFiltered = `-- name: ListProxiesFiltered :many +SELECT p.id, p.ip, p.port, p.protocols, p.anonymity, p.source, p.region_id, p.latency, p.speed, p.uptime, p.alive, p.fail_count, p.last_checked_at, p.last_alive_at, p.last_seen_at, p.created_at, r.code AS region_code, r.name AS region_name, r.zh_name AS region_zh_name +FROM proxies p JOIN proxy_regions r ON r.id = p.region_id +WHERE (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR p.ip LIKE '%' || ? || '%') AND (? < 0 OR p.alive = ?) +ORDER BY p.alive DESC, p.latency LIMIT ? OFFSET ? +` + +type ListProxiesFilteredParams struct { + Column1 interface{} + RegionID int64 + Column3 interface{} + Column4 sql.NullString + Column5 interface{} + Column6 sql.NullString + Column7 interface{} + Alive int64 + Limit int64 + Offset int64 +} + +type ListProxiesFilteredRow struct { + ID int64 + Ip string + Port int64 + Protocols string + Anonymity string + Source string + RegionID int64 + Latency int64 + Speed int64 + Uptime int64 + Alive int64 + FailCount int64 + LastCheckedAt int64 + LastAliveAt int64 + LastSeenAt int64 + CreatedAt int64 + RegionCode string + RegionName string + RegionZhName string +} + +func (q *Queries) ListProxiesFiltered(ctx context.Context, arg ListProxiesFilteredParams) ([]ListProxiesFilteredRow, error) { + rows, err := q.db.QueryContext(ctx, listProxiesFiltered, + arg.Column1, + arg.RegionID, + arg.Column3, + arg.Column4, + arg.Column5, + arg.Column6, + arg.Column7, + arg.Alive, + arg.Limit, + arg.Offset, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListProxiesFilteredRow{} + for rows.Next() { + var i ListProxiesFilteredRow + if err := rows.Scan( + &i.ID, + &i.Ip, + &i.Port, + &i.Protocols, + &i.Anonymity, + &i.Source, + &i.RegionID, + &i.Latency, + &i.Speed, + &i.Uptime, + &i.Alive, + &i.FailCount, + &i.LastCheckedAt, + &i.LastAliveAt, + &i.LastSeenAt, + &i.CreatedAt, + &i.RegionCode, + &i.RegionName, + &i.RegionZhName, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxyApiKeys = `-- name: ListProxyApiKeys :many +SELECT k.id, k.kkey, k.name, k.region_id, k.enabled, k.used_count, k.last_used_at, k.created_at, r.code AS region_code, r.zh_name AS region_zh_name, r.name AS region_name +FROM proxy_keys k LEFT JOIN proxy_regions r ON r.id = k.region_id +ORDER BY k.id DESC +` + +type ListProxyApiKeysRow struct { + ID int64 + Kkey string + Name string + RegionID int64 + Enabled int64 + UsedCount int64 + LastUsedAt int64 + CreatedAt int64 + RegionCode sql.NullString + RegionZhName sql.NullString + RegionName sql.NullString +} + +func (q *Queries) ListProxyApiKeys(ctx context.Context) ([]ListProxyApiKeysRow, error) { + rows, err := q.db.QueryContext(ctx, listProxyApiKeys) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListProxyApiKeysRow{} + for rows.Next() { + var i ListProxyApiKeysRow + if err := rows.Scan( + &i.ID, + &i.Kkey, + &i.Name, + &i.RegionID, + &i.Enabled, + &i.UsedCount, + &i.LastUsedAt, + &i.CreatedAt, + &i.RegionCode, + &i.RegionZhName, + &i.RegionName, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxyKeys = `-- name: ListProxyKeys :many +SELECT id, ip, port, source FROM proxies +` + +type ListProxyKeysRow struct { + ID int64 + Ip string + Port int64 + Source string +} + +func (q *Queries) ListProxyKeys(ctx context.Context) ([]ListProxyKeysRow, error) { + rows, err := q.db.QueryContext(ctx, listProxyKeys) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListProxyKeysRow{} + for rows.Next() { + var i ListProxyKeysRow + if err := rows.Scan( + &i.ID, + &i.Ip, + &i.Port, + &i.Source, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxyRegions = `-- name: ListProxyRegions :many +SELECT r.id, r.code, r.name, r.zh_name, r.region, r.slug, (SELECT COUNT(*) FROM proxies p WHERE p.region_id = r.id AND p.alive = 1) AS alive_count +FROM proxy_regions r ORDER BY alive_count DESC, r.code +` + +type ListProxyRegionsRow struct { + ID int64 + Code string + Name string + ZhName string + Region string + Slug string + AliveCount int64 +} + +func (q *Queries) ListProxyRegions(ctx context.Context) ([]ListProxyRegionsRow, error) { + rows, err := q.db.QueryContext(ctx, listProxyRegions) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ListProxyRegionsRow{} + for rows.Next() { + var i ListProxyRegionsRow + if err := rows.Scan( + &i.ID, + &i.Code, + &i.Name, + &i.ZhName, + &i.Region, + &i.Slug, + &i.AliveCount, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listProxySyncs = `-- name: ListProxySyncs :many +SELECT id, started_at, finished_at, status, source, total, added, removed, updated, detail, error FROM proxy_syncs ORDER BY id DESC LIMIT ? +` + +func (q *Queries) ListProxySyncs(ctx context.Context, limit int64) ([]ProxySync, error) { + rows, err := q.db.QueryContext(ctx, listProxySyncs, limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []ProxySync{} + for rows.Next() { + var i ProxySync + if err := rows.Scan( + &i.ID, + &i.StartedAt, + &i.FinishedAt, + &i.Status, + &i.Source, + &i.Total, + &i.Added, + &i.Removed, + &i.Updated, + &i.Detail, + &i.Error, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listServers = `-- name: ListServers :many +SELECT id, name, host, port, username, auth_type, secret_enc, note, created_at, updated_at FROM servers ORDER BY id +` + +func (q *Queries) ListServers(ctx context.Context) ([]Server, error) { + rows, err := q.db.QueryContext(ctx, listServers) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Server{} + for rows.Next() { + var i Server + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Host, + &i.Port, + &i.Username, + &i.AuthType, + &i.SecretEnc, + &i.Note, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listShares = `-- name: ListShares :many +SELECT id, code, file_id, user_id, file_name, expires_at, downloads, created_at FROM shares WHERE user_id = ? ORDER BY id DESC +` + +func (q *Queries) ListShares(ctx context.Context, userID int64) ([]Share, error) { + rows, err := q.db.QueryContext(ctx, listShares, userID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Share{} + for rows.Next() { + var i Share + if err := rows.Scan( + &i.ID, + &i.Code, + &i.FileID, + &i.UserID, + &i.FileName, + &i.ExpiresAt, + &i.Downloads, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTools = `-- name: ListTools :many +SELECT id, name, icon, url, description, sort, enabled FROM tools ORDER BY sort, id +` + +func (q *Queries) ListTools(ctx context.Context) ([]Tool, error) { + rows, err := q.db.QueryContext(ctx, listTools) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Tool{} + for rows.Next() { + var i Tool + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Icon, + &i.Url, + &i.Description, + &i.Sort, + &i.Enabled, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUsers = `-- name: ListUsers :many +SELECT id, name, password_hash, role, status, created_at, updated_at FROM users +WHERE name LIKE '%' || ?3 || '%' +ORDER BY id LIMIT ? OFFSET ? +` + +type ListUsersParams struct { + Keyword sql.NullString + Limit int64 + Offset int64 +} + +func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]User, error) { + rows, err := q.db.QueryContext(ctx, listUsers, arg.Keyword, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + items := []User{} + for rows.Next() { + var i User + if err := rows.Scan( + &i.ID, + &i.Name, + &i.PasswordHash, + &i.Role, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listVault = `-- name: ListVault :many +SELECT id, user_id, title, username, password_enc, url, note, totp_enc, created_at, updated_at FROM vault WHERE user_id = ? + AND (title LIKE '%' || ?2 || '%' + OR username LIKE '%' || ?2 || '%' + OR url LIKE '%' || ?2 || '%') +ORDER BY updated_at DESC +` + +type ListVaultParams struct { + UserID int64 + Keyword sql.NullString +} + +func (q *Queries) ListVault(ctx context.Context, arg ListVaultParams) ([]Vault, error) { + rows, err := q.db.QueryContext(ctx, listVault, arg.UserID, arg.Keyword) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Vault{} + for rows.Next() { + var i Vault + if err := rows.Scan( + &i.ID, + &i.UserID, + &i.Title, + &i.Username, + &i.PasswordEnc, + &i.Url, + &i.Note, + &i.TotpEnc, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const loginTrend = `-- name: LoginTrend :many +SELECT date(created_at, 'unixepoch', 'localtime') AS d, COUNT(*) AS c +FROM sessions WHERE created_at >= ? GROUP BY d +` + +type LoginTrendRow struct { + D interface{} + C int64 +} + +func (q *Queries) LoginTrend(ctx context.Context, createdAt int64) ([]LoginTrendRow, error) { + rows, err := q.db.QueryContext(ctx, loginTrend, createdAt) + if err != nil { + return nil, err + } + defer rows.Close() + items := []LoginTrendRow{} + for rows.Next() { + var i LoginTrendRow + if err := rows.Scan(&i.D, &i.C); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const markProxyAlive = `-- name: MarkProxyAlive :exec +UPDATE proxies SET last_checked_at = ?, alive = 1, fail_count = 0, last_alive_at = ?, latency = ? WHERE id = ? +` + +type MarkProxyAliveParams struct { + LastCheckedAt int64 + LastAliveAt int64 + Latency int64 + ID int64 +} + +func (q *Queries) MarkProxyAlive(ctx context.Context, arg MarkProxyAliveParams) error { + _, err := q.db.ExecContext(ctx, markProxyAlive, + arg.LastCheckedAt, + arg.LastAliveAt, + arg.Latency, + arg.ID, + ) + return err +} + +const markProxyFailed = `-- name: MarkProxyFailed :exec +UPDATE proxies SET last_checked_at = ?, alive = 0, fail_count = ? WHERE id = ? +` + +type MarkProxyFailedParams struct { + LastCheckedAt int64 + FailCount int64 + ID int64 +} + +func (q *Queries) MarkProxyFailed(ctx context.Context, arg MarkProxyFailedParams) error { + _, err := q.db.ExecContext(ctx, markProxyFailed, arg.LastCheckedAt, arg.FailCount, arg.ID) + return err +} + +const memoryProjects = `-- name: MemoryProjects :many +SELECT project AS name, COUNT(*) AS cnt FROM memories +WHERE project != '' GROUP BY project ORDER BY MAX(updated_at) DESC +` + +type MemoryProjectsRow struct { + Name string + Cnt int64 +} + +func (q *Queries) MemoryProjects(ctx context.Context) ([]MemoryProjectsRow, error) { + rows, err := q.db.QueryContext(ctx, memoryProjects) + if err != nil { + return nil, err + } + defer rows.Close() + items := []MemoryProjectsRow{} + for rows.Next() { + var i MemoryProjectsRow + if err := rows.Scan(&i.Name, &i.Cnt); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const payDailyPaid = `-- name: PayDailyPaid :many +SELECT CAST(strftime('%Y-%m-%d', paid_at, 'unixepoch', 'localtime') AS TEXT) AS day, + COALESCE(SUM(money), 0) AS money, COUNT(*) AS cnt +FROM pay_orders WHERE status = 1 AND paid_at >= ? GROUP BY day +` + +type PayDailyPaidRow struct { + Day string + Money interface{} + Cnt int64 +} + +func (q *Queries) PayDailyPaid(ctx context.Context, paidAt int64) ([]PayDailyPaidRow, error) { + rows, err := q.db.QueryContext(ctx, payDailyPaid, paidAt) + if err != nil { + return nil, err + } + defer rows.Close() + items := []PayDailyPaidRow{} + for rows.Next() { + var i PayDailyPaidRow + if err := rows.Scan(&i.Day, &i.Money, &i.Cnt); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const payOrderStats = `-- name: PayOrderStats :one +SELECT COUNT(*) AS cnt, COALESCE(SUM(CASE WHEN status = 1 THEN money ELSE 0 END), 0) AS money, + COALESCE(SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END), 0) AS pending, + COALESCE(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS paid +FROM pay_orders WHERE created_at >= ? +` + +type PayOrderStatsRow struct { + Cnt int64 + Money interface{} + Pending interface{} + Paid interface{} +} + +func (q *Queries) PayOrderStats(ctx context.Context, createdAt int64) (PayOrderStatsRow, error) { + row := q.db.QueryRowContext(ctx, payOrderStats, createdAt) + var i PayOrderStatsRow + err := row.Scan( + &i.Cnt, + &i.Money, + &i.Pending, + &i.Paid, + ) + return i, err +} + +const randomProxies = `-- name: RandomProxies :many +SELECT p.id, p.ip, p.port, p.protocols, p.fail_count, r.code AS region_code, r.zh_name AS region_zh_name +FROM proxies p JOIN proxy_regions r ON r.id = p.region_id +WHERE p.alive = 1 AND (? = 0 OR p.region_id = ?) AND (? = '' OR p.protocols LIKE '%' || ? || '%') +AND (? = '' OR r.code = ?) +ORDER BY RANDOM() LIMIT ? +` + +type RandomProxiesParams struct { + Column1 interface{} + RegionID int64 + Column3 interface{} + Column4 sql.NullString + Column5 interface{} + Code string + Limit int64 +} + +type RandomProxiesRow struct { + ID int64 + Ip string + Port int64 + Protocols string + FailCount int64 + RegionCode string + RegionZhName string +} + +func (q *Queries) RandomProxies(ctx context.Context, arg RandomProxiesParams) ([]RandomProxiesRow, error) { + rows, err := q.db.QueryContext(ctx, randomProxies, + arg.Column1, + arg.RegionID, + arg.Column3, + arg.Column4, + arg.Column5, + arg.Code, + arg.Limit, + ) + if err != nil { + return nil, err + } + defer rows.Close() + items := []RandomProxiesRow{} + for rows.Next() { + var i RandomProxiesRow + if err := rows.Scan( + &i.ID, + &i.Ip, + &i.Port, + &i.Protocols, + &i.FailCount, + &i.RegionCode, + &i.RegionZhName, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const recentPaidOrders = `-- name: RecentPaidOrders :many +SELECT trade_no, subject, money, paid_at FROM pay_orders WHERE status = 1 ORDER BY paid_at DESC LIMIT ? +` + +type RecentPaidOrdersRow struct { + TradeNo string + Subject string + Money int64 + PaidAt int64 +} + +func (q *Queries) RecentPaidOrders(ctx context.Context, limit int64) ([]RecentPaidOrdersRow, error) { + rows, err := q.db.QueryContext(ctx, recentPaidOrders, limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []RecentPaidOrdersRow{} + for rows.Next() { + var i RecentPaidOrdersRow + if err := rows.Scan( + &i.TradeNo, + &i.Subject, + &i.Money, + &i.PaidAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const setPayNotified = `-- name: SetPayNotified :exec +UPDATE pay_orders SET notified = 1 WHERE id = ? +` + +func (q *Queries) SetPayNotified(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, setPayNotified, id) + return err +} + +const setPayOrderQrcode = `-- name: SetPayOrderQrcode :exec +UPDATE pay_orders SET qrcode = ? WHERE id = ? +` + +type SetPayOrderQrcodeParams struct { + Qrcode string + ID int64 +} + +func (q *Queries) SetPayOrderQrcode(ctx context.Context, arg SetPayOrderQrcodeParams) error { + _, err := q.db.ExecContext(ctx, setPayOrderQrcode, arg.Qrcode, arg.ID) + return err +} + +const setPayOrderStatus = `-- name: SetPayOrderStatus :exec +UPDATE pay_orders SET status = ?, paid_at = ?, cards = ? WHERE id = ? +` + +type SetPayOrderStatusParams struct { + Status int64 + PaidAt int64 + Cards string + ID int64 +} + +func (q *Queries) SetPayOrderStatus(ctx context.Context, arg SetPayOrderStatusParams) error { + _, err := q.db.ExecContext(ctx, setPayOrderStatus, + arg.Status, + arg.PaidAt, + arg.Cards, + arg.ID, + ) + return err +} + +const setPayOrderUpstream = `-- name: SetPayOrderUpstream :exec +UPDATE pay_orders SET upstream_trade_no = ?, goods_key = ?, quantity = ?, unit_price = ?, payurl = ?, buyer_contact = ?, query_pwd = ? WHERE id = ? +` + +type SetPayOrderUpstreamParams struct { + UpstreamTradeNo string + GoodsKey string + Quantity int64 + UnitPrice int64 + Payurl string + BuyerContact string + QueryPwd string + ID int64 +} + +func (q *Queries) SetPayOrderUpstream(ctx context.Context, arg SetPayOrderUpstreamParams) error { + _, err := q.db.ExecContext(ctx, setPayOrderUpstream, + arg.UpstreamTradeNo, + arg.GoodsKey, + arg.Quantity, + arg.UnitPrice, + arg.Payurl, + arg.BuyerContact, + arg.QueryPwd, + arg.ID, + ) + return err +} + +const touchProxyApiKey = `-- name: TouchProxyApiKey :exec +UPDATE proxy_keys SET used_count = used_count + 1, last_used_at = ? WHERE id = ? +` + +type TouchProxyApiKeyParams struct { + LastUsedAt int64 + ID int64 +} + +func (q *Queries) TouchProxyApiKey(ctx context.Context, arg TouchProxyApiKeyParams) error { + _, err := q.db.ExecContext(ctx, touchProxyApiKey, arg.LastUsedAt, arg.ID) + return err +} + +const touchSession = `-- name: TouchSession :exec +UPDATE sessions SET expires_at = ? WHERE token = ? +` + +type TouchSessionParams struct { + ExpiresAt int64 + Token string +} + +func (q *Queries) TouchSession(ctx context.Context, arg TouchSessionParams) error { + _, err := q.db.ExecContext(ctx, touchSession, arg.ExpiresAt, arg.Token) + return err +} + +const updateBookmark = `-- name: UpdateBookmark :execrows +UPDATE bookmarks SET title = ?, url = ?, icon = ?, sort = ? WHERE id = ? AND user_id = ? +` + +type UpdateBookmarkParams struct { + Title string + Url string + Icon string + Sort int64 + ID int64 + UserID int64 +} + +func (q *Queries) UpdateBookmark(ctx context.Context, arg UpdateBookmarkParams) (int64, error) { + result, err := q.db.ExecContext(ctx, updateBookmark, + arg.Title, + arg.Url, + arg.Icon, + arg.Sort, + arg.ID, + arg.UserID, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const updateDbconn = `-- name: UpdateDbconn :execrows +UPDATE dbconns SET name = ?, engine = ?, host = ?, port = ?, username = ?, password_enc = ?, database = ?, params = ?, updated_at = ? +WHERE id = ? +` + +type UpdateDbconnParams struct { + Name string + Engine string + Host string + Port int64 + Username string + PasswordEnc string + Database string + Params string + UpdatedAt int64 + ID int64 +} + +func (q *Queries) UpdateDbconn(ctx context.Context, arg UpdateDbconnParams) (int64, error) { + result, err := q.db.ExecContext(ctx, updateDbconn, + arg.Name, + arg.Engine, + arg.Host, + arg.Port, + arg.Username, + arg.PasswordEnc, + arg.Database, + arg.Params, + arg.UpdatedAt, + arg.ID, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const updateLicenseApp = `-- name: UpdateLicenseApp :exec +UPDATE license_apps SET name = ?, enabled = ? WHERE id = ? +` + +type UpdateLicenseAppParams struct { + Name string + Enabled int64 + ID int64 +} + +func (q *Queries) UpdateLicenseApp(ctx context.Context, arg UpdateLicenseAppParams) error { + _, err := q.db.ExecContext(ctx, updateLicenseApp, arg.Name, arg.Enabled, arg.ID) + return err +} + +const updateProxySeen = `-- name: UpdateProxySeen :exec +UPDATE proxies SET protocols = ?, anonymity = ?, region_id = ?, latency = ?, speed = ?, uptime = ?, last_seen_at = ?, alive = 1, fail_count = 0 +WHERE id = ? +` + +type UpdateProxySeenParams struct { + Protocols string + Anonymity string + RegionID int64 + Latency int64 + Speed int64 + Uptime int64 + LastSeenAt int64 + ID int64 +} + +func (q *Queries) UpdateProxySeen(ctx context.Context, arg UpdateProxySeenParams) error { + _, err := q.db.ExecContext(ctx, updateProxySeen, + arg.Protocols, + arg.Anonymity, + arg.RegionID, + arg.Latency, + arg.Speed, + arg.Uptime, + arg.LastSeenAt, + arg.ID, + ) + return err +} + +const updateServer = `-- name: UpdateServer :execrows +UPDATE servers SET name = ?, host = ?, port = ?, username = ?, auth_type = ?, secret_enc = ?, note = ?, updated_at = ? +WHERE id = ? +` + +type UpdateServerParams struct { + Name string + Host string + Port int64 + Username string + AuthType string + SecretEnc string + Note string + UpdatedAt int64 + ID int64 +} + +func (q *Queries) UpdateServer(ctx context.Context, arg UpdateServerParams) (int64, error) { + result, err := q.db.ExecContext(ctx, updateServer, + arg.Name, + arg.Host, + arg.Port, + arg.Username, + arg.AuthType, + arg.SecretEnc, + arg.Note, + arg.UpdatedAt, + arg.ID, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const updateTool = `-- name: UpdateTool :exec +UPDATE tools SET name = ?, icon = ?, url = ?, description = ?, sort = ?, enabled = ? WHERE id = ? +` + +type UpdateToolParams struct { + Name string + Icon string + Url string + Description string + Sort int64 + Enabled bool + ID int64 +} + +func (q *Queries) UpdateTool(ctx context.Context, arg UpdateToolParams) error { + _, err := q.db.ExecContext(ctx, updateTool, + arg.Name, + arg.Icon, + arg.Url, + arg.Description, + arg.Sort, + arg.Enabled, + arg.ID, + ) + return err +} + +const updateUser = `-- name: UpdateUser :exec +UPDATE users SET name = ?, role = ?, status = ?, updated_at = ? WHERE id = ? +` + +type UpdateUserParams struct { + Name string + Role string + Status string + UpdatedAt int64 + ID int64 +} + +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error { + _, err := q.db.ExecContext(ctx, updateUser, + arg.Name, + arg.Role, + arg.Status, + arg.UpdatedAt, + arg.ID, + ) + return err +} + +const updateUserPassword = `-- name: UpdateUserPassword :exec +UPDATE users SET name = ?, role = ?, status = ?, password_hash = ?, updated_at = ? WHERE id = ? +` + +type UpdateUserPasswordParams struct { + Name string + Role string + Status string + PasswordHash string + UpdatedAt int64 + ID int64 +} + +func (q *Queries) UpdateUserPassword(ctx context.Context, arg UpdateUserPasswordParams) error { + _, err := q.db.ExecContext(ctx, updateUserPassword, + arg.Name, + arg.Role, + arg.Status, + arg.PasswordHash, + arg.UpdatedAt, + arg.ID, + ) + return err +} + +const updateVault = `-- name: UpdateVault :execrows +UPDATE vault SET title = ?, username = ?, password_enc = ?, url = ?, note = ?, totp_enc = ?, updated_at = ? +WHERE id = ? AND user_id = ? +` + +type UpdateVaultParams struct { + Title string + Username string + PasswordEnc string + Url string + Note string + TotpEnc string + UpdatedAt int64 + ID int64 + UserID int64 +} + +func (q *Queries) UpdateVault(ctx context.Context, arg UpdateVaultParams) (int64, error) { + result, err := q.db.ExecContext(ctx, updateVault, + arg.Title, + arg.Username, + arg.PasswordEnc, + arg.Url, + arg.Note, + arg.TotpEnc, + arg.UpdatedAt, + arg.ID, + arg.UserID, + ) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + +const upsertMemory = `-- name: UpsertMemory :exec +INSERT INTO memories (k, content, project, tags, written_by, revision, created_at, updated_at) +VALUES (?, ?, ?, ?, ?, 1, ?, ?) +ON CONFLICT(k) DO UPDATE SET + content = excluded.content, + project = excluded.project, + tags = excluded.tags, + written_by = excluded.written_by, + revision = memories.revision + 1, + updated_at = excluded.updated_at +` + +type UpsertMemoryParams struct { + K string + Content string + Project string + Tags string + WrittenBy string + CreatedAt int64 + UpdatedAt int64 +} + +func (q *Queries) UpsertMemory(ctx context.Context, arg UpsertMemoryParams) error { + _, err := q.db.ExecContext(ctx, upsertMemory, + arg.K, + arg.Content, + arg.Project, + arg.Tags, + arg.WrittenBy, + arg.CreatedAt, + arg.UpdatedAt, + ) + return err +} + +const upsertProxyRegion = `-- name: UpsertProxyRegion :exec +INSERT INTO proxy_regions(code, name, zh_name, region, slug) VALUES(?, ?, ?, ?, ?) +ON CONFLICT(code) DO UPDATE SET name = excluded.name, zh_name = excluded.zh_name, + region = excluded.region, slug = excluded.slug +` + +type UpsertProxyRegionParams struct { + Code string + Name string + ZhName string + Region string + Slug string +} + +func (q *Queries) UpsertProxyRegion(ctx context.Context, arg UpsertProxyRegionParams) error { + _, err := q.db.ExecContext(ctx, upsertProxyRegion, + arg.Code, + arg.Name, + arg.ZhName, + arg.Region, + arg.Slug, + ) + return err +} + +const upsertSetting = `-- name: UpsertSetting :exec +INSERT INTO settings (k, v) VALUES (?, ?) ON CONFLICT(k) DO UPDATE SET v = excluded.v +` + +type UpsertSettingParams struct { + K string + V string +} + +func (q *Queries) UpsertSetting(ctx context.Context, arg UpsertSettingParams) error { + _, err := q.db.ExecContext(ctx, upsertSetting, arg.K, arg.V) + return err +} diff --git a/backend/internal/db/schema.sql b/backend/internal/db/schema.sql new file mode 100644 index 0000000..51d5fd5 --- /dev/null +++ b/backend/internal/db/schema.sql @@ -0,0 +1,228 @@ +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'user', + status TEXT NOT NULL DEFAULT 'active', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS sessions ( + token TEXT PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at INTEGER NOT NULL, + expires_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id); +CREATE INDEX IF NOT EXISTS idx_sessions_expiry ON sessions(expires_at); + +CREATE TABLE IF NOT EXISTS tools ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + icon TEXT NOT NULL DEFAULT '', + url TEXT NOT NULL DEFAULT '', + description TEXT NOT NULL DEFAULT '', + sort INTEGER NOT NULL DEFAULT 0, + enabled INTEGER NOT NULL DEFAULT 1 +); + +CREATE TABLE IF NOT EXISTS vault ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + title TEXT NOT NULL, + username TEXT NOT NULL DEFAULT '', + password_enc TEXT NOT NULL DEFAULT '', + url TEXT NOT NULL DEFAULT '', + note TEXT NOT NULL DEFAULT '', + totp_enc TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_vault_user ON vault(user_id); + +CREATE TABLE IF NOT EXISTS bookmarks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + title TEXT NOT NULL, + url TEXT NOT NULL, + icon TEXT NOT NULL DEFAULT '', + sort INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_bookmarks_user ON bookmarks(user_id); + +CREATE TABLE IF NOT EXISTS servers ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + host TEXT NOT NULL, + port INTEGER NOT NULL DEFAULT 22, + username TEXT NOT NULL, + auth_type TEXT NOT NULL DEFAULT 'password', + secret_enc TEXT NOT NULL DEFAULT '', + note TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + name TEXT NOT NULL, + stored TEXT NOT NULL, + size INTEGER NOT NULL DEFAULT 0, + mime TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_files_user ON files(user_id); + +CREATE TABLE IF NOT EXISTS shares ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT NOT NULL UNIQUE, + file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + file_name TEXT NOT NULL DEFAULT '', + expires_at INTEGER NOT NULL DEFAULT 0, + downloads INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_shares_user ON shares(user_id); +CREATE INDEX IF NOT EXISTS idx_shares_file ON shares(file_id); + +CREATE TABLE IF NOT EXISTS dbconns ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + engine TEXT NOT NULL DEFAULT 'mysql', + host TEXT NOT NULL DEFAULT '', + port INTEGER NOT NULL DEFAULT 0, + username TEXT NOT NULL DEFAULT '', + password_enc TEXT NOT NULL DEFAULT '', + database TEXT NOT NULL DEFAULT '', + params TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS proxy_regions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT NOT NULL UNIQUE, + name TEXT NOT NULL DEFAULT '', + zh_name TEXT NOT NULL DEFAULT '', + region TEXT NOT NULL DEFAULT '', + slug TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS proxies ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + ip TEXT NOT NULL, + port INTEGER NOT NULL, + protocols TEXT NOT NULL DEFAULT '', + anonymity TEXT NOT NULL DEFAULT '', + source TEXT NOT NULL DEFAULT '', + region_id INTEGER NOT NULL REFERENCES proxy_regions(id) ON DELETE CASCADE, + latency INTEGER NOT NULL DEFAULT 0, + speed INTEGER NOT NULL DEFAULT 0, + uptime INTEGER NOT NULL DEFAULT 0, + alive INTEGER NOT NULL DEFAULT 1, + fail_count INTEGER NOT NULL DEFAULT 0, + last_checked_at INTEGER NOT NULL DEFAULT 0, + last_alive_at INTEGER NOT NULL DEFAULT 0, + last_seen_at INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS idx_proxies_addr ON proxies(ip, port); +CREATE INDEX IF NOT EXISTS idx_proxies_region ON proxies(region_id); +CREATE INDEX IF NOT EXISTS idx_proxies_alive ON proxies(alive); +CREATE INDEX IF NOT EXISTS idx_proxies_check ON proxies(alive, last_checked_at); + +CREATE TABLE IF NOT EXISTS proxy_syncs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + started_at INTEGER NOT NULL, + finished_at INTEGER NOT NULL DEFAULT 0, + status TEXT NOT NULL DEFAULT '', + source TEXT NOT NULL DEFAULT '', + total INTEGER NOT NULL DEFAULT 0, + added INTEGER NOT NULL DEFAULT 0, + removed INTEGER NOT NULL DEFAULT 0, + updated INTEGER NOT NULL DEFAULT 0, + detail TEXT NOT NULL DEFAULT '', + error TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS proxy_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + kkey TEXT NOT NULL UNIQUE, + name TEXT NOT NULL DEFAULT '', + region_id INTEGER NOT NULL DEFAULT 0, + enabled INTEGER NOT NULL DEFAULT 1, + used_count INTEGER NOT NULL DEFAULT 0, + last_used_at INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS settings ( + k TEXT PRIMARY KEY, + v TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS pay_orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + trade_no TEXT NOT NULL UNIQUE, + out_trade_no TEXT NOT NULL UNIQUE, + channel TEXT NOT NULL DEFAULT 'alipay', + subject TEXT NOT NULL DEFAULT '', + money INTEGER NOT NULL, + fee INTEGER NOT NULL DEFAULT 0, + status INTEGER NOT NULL DEFAULT 0, -- 0 待支付 1 已支付 3 已关闭 + notify_url TEXT NOT NULL DEFAULT '', + return_url TEXT NOT NULL DEFAULT '', + client_ip TEXT NOT NULL DEFAULT '', + notified INTEGER NOT NULL DEFAULT 0, + notify_attempts INTEGER NOT NULL DEFAULT 0, + paid_at INTEGER NOT NULL DEFAULT 0, + expired_at INTEGER NOT NULL DEFAULT 0, + upstream_trade_no TEXT NOT NULL DEFAULT '', + goods_key TEXT NOT NULL DEFAULT '', + quantity INTEGER NOT NULL DEFAULT 0, + unit_price INTEGER NOT NULL DEFAULT 0, + payurl TEXT NOT NULL DEFAULT '', + qrcode TEXT NOT NULL DEFAULT '', + buyer_contact TEXT NOT NULL DEFAULT '', + query_pwd TEXT NOT NULL DEFAULT '', + cards TEXT NOT NULL DEFAULT '', + created_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_pay_orders_status ON pay_orders(status, created_at); + +CREATE TABLE IF NOT EXISTS license_apps ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + appid TEXT NOT NULL UNIQUE, + secret TEXT NOT NULL, + name TEXT NOT NULL DEFAULT '', + enabled INTEGER NOT NULL DEFAULT 1, + created_at INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS license_cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + app_id INTEGER NOT NULL DEFAULT 0, + card TEXT NOT NULL UNIQUE, + hours INTEGER NOT NULL DEFAULT 24, + domain TEXT NOT NULL DEFAULT '', + activated_at INTEGER NOT NULL DEFAULT 0, + expires_at INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL +); + +-- agent 共享记忆库:key 形态 "/",跨会话/跨 agent 持久事实 +CREATE TABLE IF NOT EXISTS memories ( + k TEXT PRIMARY KEY, + content TEXT NOT NULL, + project TEXT NOT NULL DEFAULT '', + tags TEXT NOT NULL DEFAULT '[]', + written_by TEXT NOT NULL DEFAULT '', + revision INTEGER NOT NULL DEFAULT 1, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project, updated_at); diff --git a/backend/internal/dbx/dbx.go b/backend/internal/dbx/dbx.go new file mode 100644 index 0000000..4e6d5ac --- /dev/null +++ b/backend/internal/dbx/dbx.go @@ -0,0 +1,400 @@ +// Package dbx 通用数据库连接层:按 engine 生成 DSN、库/表/列 introspection、 +// 受限执行 SQL(超时 + 行数上限)并把结果序列化为可 JSON 化结构。 +// 支持 mysql / postgres / sqlite。 +package dbx + +import ( + "context" + "database/sql" + "database/sql/driver" + "encoding/base64" + "errors" + "fmt" + "net/url" + "strconv" + "strings" + "time" + "unicode/utf8" + + _ "github.com/go-sql-driver/mysql" + _ "github.com/jackc/pgx/v5/stdlib" + _ "modernc.org/sqlite" +) + +// Conn 已解密的连接信息(service 层组装) +type Conn struct { + Engine string + Host string + Port int64 + Username string + Password string + Database string // mysql/pg:默认库;sqlite:文件路径 + Params string // 附加 DSN 参数,形如 "charset=utf8mb4&sslmode=disable" +} + +func (c Conn) driverName() (string, error) { + switch c.Engine { + case "mysql": + return "mysql", nil + case "postgres", "postgresql", "pg": + return "pgx", nil + case "sqlite", "sqlite3": + return "sqlite", nil + } + return "", fmt.Errorf("dbx: 不支持的引擎 %q", c.Engine) +} + +// dsn 按引擎拼连接串;db 为空用连接默认库 +func (c Conn) dsn(db string) string { + if db == "" { + db = c.Database + } + switch c.Engine { + case "mysql": + params := "parseTime=true&timeout=6s" + if c.Params != "" { + params = c.Params + if !strings.Contains(params, "parseTime") { + params += "&parseTime=true" + } + if !strings.Contains(params, "timeout") { + params += "&timeout=6s" + } + } + host := c.Host + if c.Port > 0 { + host += ":" + strconv.FormatInt(c.Port, 10) + } + return fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", c.Username, c.Password, host, db, params) + case "postgres", "postgresql", "pg": + u := &url.URL{Scheme: "postgres", Host: c.Host, User: url.UserPassword(c.Username, c.Password), Path: "/" + db} + if c.Port > 0 { + u.Host += ":" + strconv.FormatInt(c.Port, 10) + } + q := u.Query() + if c.Params != "" { + if extra, err := url.ParseQuery(c.Params); err == nil { + for k, vs := range extra { + for _, v := range vs { + q.Add(k, v) + } + } + } + } + if q.Get("sslmode") == "" { + q.Set("sslmode", "disable") + } + u.RawQuery = q.Encode() + return u.String() + default: // sqlite:host/port/user 不用,database 是文件路径 + path := db + if path == "" { + path = c.Database + } + return "file:" + path + } +} + +// Open 建连并 ping 验证 +func Open(c Conn, db string) (*sql.DB, error) { + driver, err := c.driverName() + if err != nil { + return nil, err + } + d, err := sql.Open(driver, c.dsn(db)) + if err != nil { + return nil, err + } + d.SetMaxOpenConns(2) + ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) + defer cancel() + if err := d.PingContext(ctx); err != nil { + d.Close() + return nil, fmt.Errorf("dbx: 连接失败: %w", err) + } + return d, nil +} + +// ---- introspection ---- + +// Databases 列出可访问的库;sqlite 恒为 main +func Databases(d *sql.DB, engine string) ([]string, error) { + var q string + switch engine { + case "mysql": + q = `SELECT schema_name FROM information_schema.schemata + WHERE schema_name NOT IN ('mysql','sys','information_schema','performance_schema') ORDER BY schema_name` + case "postgres", "postgresql", "pg": + q = `SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname` + default: // sqlite + return []string{"main"}, nil + } + return queryStrings(d, q) +} + +type Table struct { + Name string + Type string // table | view +} + +type Column struct { + Table string + Name string + Type string + Pk bool +} + +// TablesAndColumns 某库下的表/视图与列(sqlite 用 sqlite_master+pragma) +func TablesAndColumns(d *sql.DB, engine, database string) ([]Table, []Column, error) { + if engine == "sqlite" || engine == "sqlite3" { + return sqliteSchema(d) + } + var tq, cq string + if engine == "mysql" { + tq = `SELECT table_name, table_type FROM information_schema.tables + WHERE table_schema = ? ORDER BY table_name` + cq = `SELECT table_name, column_name, column_type, + (column_key = 'PRI') AS pk FROM information_schema.columns + WHERE table_schema = ? ORDER BY table_name, ordinal_position` + } else { // postgres:只取 public 及其余非系统 schema 的用户表 + tq = `SELECT table_name, table_type FROM information_schema.tables + WHERE table_schema NOT IN ('pg_catalog','information_schema') + ORDER BY table_name` + cq = `SELECT c.table_name, c.column_name, c.data_type, + EXISTS ( + SELECT 1 FROM pg_constraint con + JOIN pg_class rel ON rel.oid = con.conrelid + JOIN pg_namespace n ON n.oid = rel.relnamespace + JOIN pg_attribute att ON att.attrelid = con.conrelid + AND att.attnum = ANY(con.conkey) + WHERE con.contype = 'p' + AND n.nspname = c.table_schema + AND rel.relname = c.table_name + AND att.attname = c.column_name + ) AS pk + FROM information_schema.columns c + WHERE c.table_schema NOT IN ('pg_catalog','information_schema') + ORDER BY c.table_name, c.ordinal_position` + } + tables := []Table{} + rows, err := d.Query(tq) + if err != nil { + return nil, nil, err + } + for rows.Next() { + var t Table + var ty string + if err := rows.Scan(&t.Name, &ty); err != nil { + rows.Close() + return nil, nil, err + } + t.Type = "table" + if strings.Contains(strings.ToLower(ty), "view") { + t.Type = "view" + } + tables = append(tables, t) + } + rows.Close() + + cols := []Column{} + rows2, err := d.Query(cq) + if err != nil { + return tables, cols, nil // 列拿不到不致命:补全降级 + } + defer rows2.Close() + for rows2.Next() { + var c Column + var pk any + if err := rows2.Scan(&c.Table, &c.Name, &c.Type, &pk); err == nil { + if b, ok := pk.(int64); ok { + c.Pk = b != 0 + } else if b, ok := pk.(bool); ok { + c.Pk = b + } + cols = append(cols, c) + } + } + return tables, cols, nil +} + +func sqliteSchema(d *sql.DB) ([]Table, []Column, error) { + tables := []Table{} + rows, err := d.Query( + `SELECT name, type FROM sqlite_master WHERE type IN ('table','view') + AND name NOT LIKE 'sqlite_%' ORDER BY name`) + if err != nil { + return nil, nil, err + } + defer rows.Close() + for rows.Next() { + var t Table + if err := rows.Scan(&t.Name, &t.Type); err != nil { + return nil, nil, err + } + tables = append(tables, t) + } + rows.Close() + + cols := []Column{} + for _, t := range tables { + if t.Type != "table" { + continue + } + pr, err := d.Query(`SELECT name, type, pk FROM pragma_table_info(?)`, t.Name) + if err != nil { + continue + } + for pr.Next() { + var c Column + var pk int64 + if err := pr.Scan(&c.Name, &c.Type, &pk); err == nil { + c.Table = t.Name + c.Pk = pk != 0 + cols = append(cols, c) + } + } + pr.Close() + } + return tables, cols, nil +} + +func queryStrings(d *sql.DB, q string, args ...any) ([]string, error) { + rows, err := d.Query(q, args...) + if err != nil { + return nil, err + } + defer rows.Close() + out := []string{} + for rows.Next() { + var s string + if err := rows.Scan(&s); err != nil { + return nil, err + } + out = append(out, s) + } + return out, rows.Err() +} + +// ---- 查询执行 ---- + +type Result struct { + IsSelect bool + Columns []string + Rows [][]any + RowCount int64 + AffectedRows int64 + Truncated bool + ElapsedMs int64 +} + +const maxRowsCap = 10000 + +// returnsRows 按首个关键字预判语句是否产生结果集; +// 不能只靠 QueryContext 试探——sqlite 会把 INSERT 当空结果集执行掉,拿不到 affected +func returnsRows(sqlText string) bool { + s := strings.TrimLeft(sqlText, " \t\r\n(") + for strings.HasPrefix(s, "--") { + if i := strings.IndexByte(s, '\n'); i >= 0 { + s = strings.TrimLeft(s[i+1:], " \t\r\n(") + } else { + return true + } + } + if i := strings.IndexAny(s, " \t\r\n("); i > 0 { + s = s[:i] + } + switch strings.ToUpper(s) { + case "SELECT", "WITH", "SHOW", "PRAGMA", "EXPLAIN", "VALUES", "TABLE", "DESC", "DESCRIBE": + return true + } + return false +} + +// Query 执行单条 SQL:有结果集走行收集(cap 截断),否则回 affected rows +func Query(ctx context.Context, d *sql.DB, sqlText string, maxRows int64) (*Result, error) { + if maxRows <= 0 || maxRows > maxRowsCap { + maxRows = 500 + } + start := time.Now() + if !returnsRows(sqlText) { + res, err := d.ExecContext(ctx, sqlText) + if err != nil { + return nil, err + } + aff, _ := res.RowsAffected() + return &Result{IsSelect: false, AffectedRows: aff, + ElapsedMs: time.Since(start).Milliseconds(), + Columns: []string{}, Rows: [][]any{}}, nil + } + r := &Result{Columns: []string{}, Rows: [][]any{}} + + rows, err := d.QueryContext(ctx, sqlText) + if err != nil { + return nil, err + } + defer rows.Close() + + cols, err := rows.Columns() + if err != nil { + return nil, err + } + r.IsSelect = true + r.Columns = cols + for rows.Next() { + vals := make([]any, len(cols)) + ptrs := make([]any, len(cols)) + for i := range vals { + ptrs[i] = &vals[i] + } + if err := rows.Scan(ptrs...); err != nil { + return nil, err + } + row := make([]any, len(cols)) + for i, v := range vals { + row[i] = cellJSON(v) + } + r.Rows = append(r.Rows, row) + if int64(len(r.Rows)) >= maxRows { + r.Truncated = true + break + } + } + r.RowCount = int64(len(r.Rows)) + r.ElapsedMs = time.Since(start).Milliseconds() + return r, nil +} + +// cellJSON 把驱动返回值规整为 JSON 友好类型 +func cellJSON(v any) any { + switch t := v.(type) { + case nil: + return nil + case []byte: + if utf8.Valid(t) { + return string(t) + } + return "0x" + base64.StdEncoding.EncodeToString(t) + case time.Time: + return t.Format("2006-01-02 15:04:05") + case driver.Valuer: // 防御:部分驱动包一层 + vv, _ := t.Value() + return cellJSON(vv) + default: + return v + } +} + +// Exec 明确写路径(删改/DDL),回受影响行数 +func Exec(ctx context.Context, d *sql.DB, sqlText string) (*Result, error) { + start := time.Now() + res, err := d.ExecContext(ctx, sqlText) + if err != nil { + return nil, err + } + aff, _ := res.RowsAffected() + return &Result{IsSelect: false, AffectedRows: aff, + ElapsedMs: time.Since(start).Milliseconds(), + Columns: []string{}, Rows: [][]any{}}, nil +} + +var ErrNoPK = errors.New("dbx: no primary key") diff --git a/backend/internal/dto/dto.go b/backend/internal/dto/dto.go new file mode 100644 index 0000000..9d43703 --- /dev/null +++ b/backend/internal/dto/dto.go @@ -0,0 +1,755 @@ +// Package dto RPC 线协议类型。遵守 fun DTO 约束:定宽整型、无 map/any, +// 可空字段一律 *T,响应键自动转 camelCase。 +package dto + +type OkView struct { + Message string +} + +// ---- auth ---- + +type LoginDto struct { + Name string + Password string +} + +type UserView struct { + Id int64 + Name string + Role string + Status string + CreatedAt int64 +} + +type SessionView struct { + User *UserView + Token *string +} + +type ChangePasswordDto struct { + OldPassword string + NewPassword string +} + +// ---- user admin ---- + +type ListUsersDto struct { + Page int64 + PageSize int64 + Keyword *string +} + +type UserPageView struct { + Total int64 + Items []UserView +} + +// SaveUserDto 创建/更新一体:Id 为空为新建;Password 更新时可空(不填不改密码) +type SaveUserDto struct { + Id *int64 + Name string + Password *string + Role string + Status *string +} + +type DeleteUserDto struct { + Id int64 +} + +// ---- dashboard ---- + +type TrendPoint struct { + Day string + Count int64 +} + +type StatsView struct { + TotalUsers int64 + ActiveSessions int64 + TodayLogins int64 + UptimeSeconds int64 + LoginTrend []TrendPoint + + Servers int64 + Proxies int64 + AliveProxies int64 + TodayOrders int64 + TodayMoney int64 + TotalMoney int64 + PendingOrders int64 + LicenseApps int64 + LicenseCards int64 + LicenseActive int64 + Memories int64 + MemoryProjects int64 + Bookmarks int64 + VaultItems int64 + Dbconns int64 + Files int64 + + PayTrend []PayTrendPoint + RecentOrders []RecentOrderView + RecentMemories []RecentMemoryView +} + +type PayTrendPoint struct { + Day string + Money int64 + Count int64 +} + +type RecentOrderView struct { + TradeNo string + Subject string + Money int64 + PaidAt int64 +} + +type RecentMemoryView struct { + Key string + Project string + UpdatedAt int64 +} + +// ---- tools ---- + +type ToolView struct { + Id int64 + Name string + Icon string + Url string + Description string + Sort int64 + Enabled bool +} + +type SaveToolDto struct { + Id *int64 + Name string + Icon *string + Url string + Description *string + Sort *int64 + Enabled *bool +} + +type DeleteToolDto struct { + Id int64 +} + +// ---- vault 密码箱 ---- + +// VaultItemView 列表视图:密文字段不下发,只给 hasXxx 标记 +type VaultItemView struct { + Id int64 + Title string + Username *string + Url *string + Note *string + HasPassword bool + HasTotp bool + UpdatedAt int64 +} + +// SaveVaultDto Password/TotpSecret 更新时传空指针表示不变; +// TotpSecret 支持裸 base32 或 otpauth://totp/... 链接;传空字符串表示清除 +type SaveVaultDto struct { + Id *int64 + Title string + Username *string + Password *string + Url *string + Note *string + TotpSecret *string +} + +type VaultIdDto struct { + Id int64 +} + +type SecretView struct { + Value string +} + +type TotpView struct { + Code string + SecondsLeft int64 +} + +// ---- bookmarks 书签 ---- + +type BookmarkView struct { + Id int64 + Title string + Url string + Icon string + Sort int64 +} + +type SaveBookmarkDto struct { + Id *int64 + Title string + Url string + Icon *string + Sort *int64 +} + +type BookmarkIdDto struct { + Id int64 +} + +// ---- servers 服务器管理 ---- + +// ServerView 列表视图:secret 密文不下发,只给 hasSecret 标记 +type ServerView struct { + Id int64 + Name string + Host string + Port int64 + Username string + AuthType string + HasSecret bool + Note *string + CreatedAt int64 +} + +// SaveServerDto Secret 为密码或 PEM 私钥(按 AuthType);nil = 不变,"" = 清除 +type SaveServerDto struct { + Id *int64 + Name string + Host string + Port *int64 + Username string + AuthType string + Secret *string + Note *string +} + +type ServerIdDto struct { + Id int64 +} + +type DiskMountView struct { + Mount string + Fs string + Total int64 + Used int64 + Avail int64 + Percent int64 +} + +// ServerMetricsView 百分比直接给 0-100 整数;字节量单位 byte;速率单位 byte/s +type ServerMetricsView struct { + Hostname string + Os string + Kernel string + UptimeSeconds int64 + CpuPercent int64 + Load1 string + Load5 string + Load15 string + MemTotal int64 + MemUsed int64 + MemAvail int64 + SwapTotal int64 + SwapUsed int64 + DiskTotal int64 + DiskUsed int64 + DiskAvail int64 + NetRxRate int64 + NetTxRate int64 + NetRxTotal int64 + NetTxTotal int64 + Disks []DiskMountView + CollectedAt int64 +} + +// ---- sftp ---- + +type SftpPathDto struct { + Id int64 + Path string +} + +type SftpEntryView struct { + Name string + Size int64 + Mode string + IsDir bool + ModTime int64 +} + +type SftpListView struct { + Path string + Entries []SftpEntryView +} + +// SftpRenameDto Path 为原完整路径,NewName 为同目录新名字 +type SftpRenameDto struct { + Id int64 + Path string + NewName string +} + +// ---- dbm 通用数据库管理 ---- + +// DbconnView 连接视图:密码密文不下发 +type DbconnView struct { + Id int64 + Name string + Engine string + Host string + Port int64 + Username string + Database string + HasPassword bool + CreatedAt int64 +} + +// SaveDbconnDto Password 为 nil=不变、""=清除;sqlite 时 Host 可空、Database 为文件路径 +type SaveDbconnDto struct { + Id *int64 + Name string + Engine string + Host *string + Port *int64 + Username *string + Password *string + Database *string + Params *string +} + +type DbconnIdDto struct { + Id int64 +} + +// DbScopeDto 带可选库的查询范围 +type DbScopeDto struct { + Id int64 + Database *string +} + +type DbDatabasesView struct { + Names []string +} + +type DbTableView struct { + Name string + Type string +} + +type DbColumnView struct { + Table string + Name string + Type string + Pk bool +} + +type DbTablesView struct { + Tables []DbTableView + Columns []DbColumnView +} + +// DbQueryDto Sql 单条语句;MaxRows 服务端封顶 +type DbQueryDto struct { + Id int64 + Database *string + Sql string + MaxRows *int64 +} + +// DbQueryView RowsJson 为 [][]any 的 JSON 串(DTO 不支持嵌套数组/any,前端自行 parse) +type DbQueryView struct { + IsSelect bool + Columns []string + RowsJson string + RowCount int64 + AffectedRows int64 + Truncated bool + ElapsedMs int64 +} + +type DbImportView struct { + Inserted int64 + Skipped int64 +} + +// ---- webreq HTTP 代理 ---- + +// WebFetchDto HeadersJson 为 {"k":"v"} 对象 JSON 串 +type WebFetchDto struct { + Method string + Url string + HeadersJson string + Body string +} + +type WebFetchView struct { + Status int64 + HeadersJson string + Body string + ElapsedMs int64 + Truncated bool +} + +// ---- disk 网盘 ---- + +type FileView struct { + Id int64 + Name string + Size int64 + Mime string + CreatedAt int64 +} + +// ShareView expiresAt=0 表示永久有效;expired 由服务端算好给前端直接显示 +type ShareView struct { + Id int64 + Code string + FileName string + ExpiresAt int64 + Expired bool + Downloads int64 + CreatedAt int64 +} + +// CreateShareDto ExpireMinutes:0=永久,>0=从现在起 N 分钟 +type CreateShareDto struct { + FileId int64 + ExpireMinutes int64 +} + +type IdDto struct { + Id int64 +} + +// ---- proxy 代理池 ---- + +type ProxyRegionView struct { + Id int64 + Code string + Name string + ZhName string + Region string + AliveCount int64 +} + +type ProxyView struct { + Id int64 + Ip string + Port int64 + Address string + Protocols string + Anonymity string + RegionId int64 + RegionCode string + RegionName string + RegionZhName string + Latency int64 + Speed int64 + Uptime int64 + Alive bool + FailCount int64 + LastCheckedAt int64 + LastAliveAt int64 + LastSeenAt int64 +} + +type ProxyListDto struct { + RegionId int64 + Protocol string + Keyword string + Alive int64 + Page int64 + PageSize int64 +} + +type ProxyListView struct { + Total int64 + Items []ProxyView +} + +type ProxySyncView struct { + Id int64 + StartedAt int64 + FinishedAt int64 + Status string + Source string + Total int64 + Added int64 + Removed int64 + Updated int64 + DetailJson string + Error string +} + +type ProxyStatsView struct { + Total int64 + Regions int64 + LastSyncAt int64 + LastCheckAt int64 + LastStatus string + Checking bool + Syncing bool +} + +// ---- 代理提取密钥 ---- + +type ProxyKeyView struct { + Id int64 + Key string + Name string + RegionId int64 + RegionCode string + RegionZhName string + Enabled bool + UsedCount int64 + LastUsedAt int64 + CreatedAt int64 +} + +// ProxyKeySaveDto regionId=0 表示全部地区;key 由服务端生成 +type ProxyKeySaveDto struct { + Name string + RegionId int64 +} + +// ---- settings ---- + +type SettingDto struct { + Key string + Value string +} + +type SettingView struct { + Value string +} + +// ---- pay ---- + +type PayOrderListDto struct { + Page int64 + PageSize int64 + Status *int64 + Kw *string +} + +type PayOrderView struct { + Id int64 + TradeNo string + OutTradeNo string + Channel string + Subject string + Money int64 + Status int64 + Notified int64 + Payurl string + PaidAt int64 + ExpiredAt int64 + CreatedAt int64 +} + +type PayOrderPageView struct { + Total int64 + Items []PayOrderView +} + +type PayStatsView struct { + TodayCount int64 + TodayMoney int64 + TotalCount int64 + TotalMoney int64 + PendingCount int64 + TodayPaidCount int64 + TotalPaidCount int64 +} + +type PayUpstreamDto struct { + Username string + Password string +} + +type PayUpstreamStatusView struct { + Configured int64 + Username string + Nickname string + Shop string + TokenAge int64 + GoodsId int64 + GoodsKey string + GoodsName string + UnitPrice int64 + StockCount int64 + TargetMoney int64 + WalletOk int64 + WalletAvail int64 + WalletFrozen int64 + Ensuring int64 + LastError string +} + +type PayTestPayDto struct { + Money string +} + +type PayTestPayView struct { + TradeNo string + Payurl string + Qrcode string + Money int64 + Cashier string +} + +type PayCashierDto struct { + TradeNo string +} + +type PayCashierView struct { + TradeNo string + OutTradeNo string + Subject string + Money int64 + Channel string + Status int64 + Payurl string + Qrcode string + Cards string + CreatedAt int64 + ExpiredAt int64 +} + +type PayCashierStatusView struct { + Status int64 + ReturnUrl string +} + +type LicenseListDto struct { + AppId int64 + Page int64 + PageSize int64 + Kw *string +} + +type LicenseCardView struct { + Id int64 + Card string + Hours int64 + Domain string + Status int64 + ActivatedAt int64 + ExpiresAt int64 + CreatedAt int64 +} + +type LicensePageView struct { + Total int64 + Items []LicenseCardView +} + +type LicenseStatsView struct { + Total int64 + Unused int64 + Active int64 + Expired int64 +} + +type LicenseGenDto struct { + AppId int64 + Count int64 + Hours int64 +} + +type LicenseGenView struct { + Cards []string +} + +type LicenseIdDto struct { + Id int64 +} + +type LicenseStatsDto struct { + AppId int64 +} + +type LicenseAppView struct { + Id int64 + Appid string + Name string + Enabled int64 + CardCount int64 + CreatedAt int64 +} + +type LicenseAppSaveDto struct { + Id int64 + Name string + Enabled *int64 +} + +type LicenseAppsView struct { + Items []LicenseAppView +} + +type MemoryListDto struct { + Project string + Tag string + Query string + Limit int64 +} + +type MemoryMetaView struct { + Key string + Project string + Tags []string + WrittenBy string + Revision int64 + UpdatedAt int64 +} + +type MemoryListView struct { + Items []MemoryMetaView +} + +type MemoryView struct { + Key string + Url string + Content string + Project string + Tags []string + WrittenBy string + Revision int64 + CreatedAt int64 + UpdatedAt int64 +} + +type MemoryKeyDto struct { + Key string +} + +type MemorySaveDto struct { + Key string + Content string + Project string + Tags []string +} + +type MemorySaveView struct { + Key string + Url string + Revision int64 +} + +type MemoryProjectView struct { + Name string + Count int64 +} + +type MemoryProjectsView struct { + Items []MemoryProjectView +} + +type MemoryMcpView struct { + Url string + Token string +} diff --git a/backend/internal/memx/memx.go b/backend/internal/memx/memx.go new file mode 100644 index 0000000..808ed2e --- /dev/null +++ b/backend/internal/memx/memx.go @@ -0,0 +1,276 @@ +// Package memx 共享记忆库的对外协议面:Streamable HTTP MCP server(/mcp), +// 工具与 git.cyi.cc/chiyi/memory-mcp 完全同构,agent 改指 cyi-box 地址即可无缝迁移。 +package memx + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + "strings" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +const maxContentLen = 64 * 1024 + +// 高置信度密钥格式拦截——记忆库绝不允许落凭证 +var secretPatterns = []*regexp.Regexp{ + regexp.MustCompile(`AKIA[0-9A-Z]{16}`), + regexp.MustCompile(`-----BEGIN [A-Z0-9 ]*PRIVATE KEY`), + regexp.MustCompile(`\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}`), + regexp.MustCompile(`github_pat_[A-Za-z0-9_]{20,}`), + regexp.MustCompile(`\bsk-[A-Za-z0-9_-]{20,}`), + regexp.MustCompile(`\bxox[baprs]-[A-Za-z0-9-]{10,}`), + regexp.MustCompile(`(?i)\b(password|passwd|api[_-]?key|secret)\s*[:=]\s*["']?[^\s"'$]{8,}`), +} + +// LintContent 内容体检:超长与疑似凭证直接拒(管理页与 MCP 共用) +func LintContent(content string) error { + if len(content) > maxContentLen { + return fmt.Errorf("content too large (%d bytes, max %d)", len(content), maxContentLen) + } + for _, p := range secretPatterns { + if loc := p.FindStringIndex(content); loc != nil { + return fmt.Errorf("content rejected: looks like it contains a credential near offset %d; store secrets in a secrets manager, not here", loc[0]) + } + } + return nil +} + +func toJSON(v any) *mcp.CallToolResult { + b, _ := json.MarshalIndent(v, "", " ") + return mcp.NewToolResultText(string(b)) +} + +func errResult(err error) *mcp.CallToolResult { + return mcp.NewToolResultError(err.Error()) +} + +type ctxKeyBase struct{} + +// WithBase 把当前请求推导出的站点基址注入 ctx,memory_save 用它拼 /m/ 链接 +func WithBase(ctx context.Context, base string) context.Context { + return context.WithValue(ctx, ctxKeyBase{}, base) +} + +func baseFrom(ctx context.Context) string { + if v, ok := ctx.Value(ctxKeyBase{}).(string); ok { + return v + } + return "" +} + +// keyFrom 容忍传整段分享链接:带 "/m/" 时只取其后段 +func keyFrom(ctx context.Context, key string) string { + if base := baseFrom(ctx); base != "" { + key = strings.TrimPrefix(key, base+"/m/") + } + if i := strings.LastIndex(key, "/m/"); i >= 0 { + key = key[i+3:] + } + return key +} + +// NewMCPServer 建 Streamable HTTP MCP 服务 +func NewMCPServer(st *store.Store) *server.MCPServer { + s := server.NewMCPServer("cyi-box-memory", "1.0.0", + server.WithToolCapabilities(true), + server.WithPromptCapabilities(true), + server.WithResourceCapabilities(false, true), + ) + + s.AddTool(mcp.NewTool("memory_save", + mcp.WithDescription(`Save a durable, reusable fact to the shared memory bank so future sessions (possibly other agents) can recall it. + +WHEN TO CALL: when you learn non-obvious, persistent facts — project architecture, stack/framework, server addresses and how to reach them, build/test/deploy commands, repo conventions, recurring pitfalls, external service endpoints. + +DO NOT SAVE: task progress or TODO state, anything derivable by reading the repo (file lists, function names), secrets/credentials/tokens (rejected server-side), one-off command output. + +Content should be concise markdown. Re-saving the same key overwrites it (revision+1), so update facts in place rather than duplicating.`), + mcp.WithString("key", mcp.Required(), mcp.Description(`Stable identifier like "vivid/deploy" — use "project/topic" naming.`)), + mcp.WithString("content", mcp.Required(), mcp.Description("Markdown body of the memory")), + mcp.WithString("project", mcp.Description("Project slug, e.g. repo name — groups memories for memory_list")), + mcp.WithArray("tags", mcp.Items(map[string]any{"type": "string"}), mcp.Description(`e.g. ["server","stack","gotcha","command"]`)), + mcp.WithString("written_by", mcp.Description("Which agent is writing: devin / codex / claude / human / ...")), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + content, err := r.RequireString("content") + if err != nil { + return errResult(err), nil + } + key, err := r.RequireString("key") + if err != nil { + return errResult(err), nil + } + key = strings.TrimSpace(key) + if key == "" { + return errResult(fmt.Errorf("key 不能为空,形如 project/topic")), nil + } + if err := LintContent(content); err != nil { + return errResult(err), nil + } + project := strings.TrimSpace(r.GetString("project", "")) + m := &store.Memory{ + Key: key, + Content: content, + Project: project, + Tags: r.GetStringSlice("tags", nil), + WrittenBy: r.GetString("written_by", ""), + } + if err := st.SaveMemory(m); err != nil { + return errResult(err), nil + } + url, _ := st.SignMemoryReadURL(baseFrom(ctx), m.Key) + return toJSON(map[string]any{ + "ok": true, "key": m.Key, "revision": m.Revision, + "url": url, "resource": "memory://" + m.Key, + }), nil + }) + + s.AddTool(mcp.NewTool("memory_get", + mcp.WithDescription("Read one memory by exact key. Use keys from memory_list/memory_search results or a shared URL like {base}/m/{key}."), + mcp.WithString("key", mcp.Required(), mcp.Description(`e.g. "vivid/deploy"`)), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + key, err := r.RequireString("key") + if err != nil { + return errResult(err), nil + } + key = keyFrom(ctx, key) + m, err := st.GetMemory(key) + if err != nil { + return errResult(err), nil + } + if m == nil { + return mcp.NewToolResultErrorf("no memory with key %q", key), nil + } + return toJSON(m), nil + }) + + s.AddTool(mcp.NewTool("memory_search", + mcp.WithDescription("Keyword-search the memory bank. All terms must match (AND). Returns full memories ordered by recency."), + mcp.WithString("query", mcp.Required(), mcp.Description("Space-separated keywords")), + mcp.WithString("project", mcp.Description("Restrict to one project")), + mcp.WithNumber("limit", mcp.Description("Max results, default 20")), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + query, err := r.RequireString("query") + if err != nil { + return errResult(err), nil + } + ms, err := st.SearchMemories(query, r.GetString("project", ""), int64(r.GetInt("limit", 20))) + if err != nil { + return errResult(err), nil + } + return toJSON(ms), nil + }) + + s.AddTool(mcp.NewTool("memory_list", + mcp.WithDescription("List memory keys (metadata only). CALL THIS at the start of a session to see what is already known about the current project before doing exploration work."), + mcp.WithString("project", mcp.Description("Restrict to one project")), + mcp.WithString("tag", mcp.Description(`Restrict to one tag, e.g. "server"`)), + mcp.WithNumber("limit", mcp.Description("Max results, default 20")), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + ms, err := st.ListMemories(r.GetString("project", ""), r.GetString("tag", ""), int64(r.GetInt("limit", 20))) + if err != nil { + return errResult(err), nil + } + type summary struct { + Key string `json:"key"` + Project string `json:"project,omitempty"` + Tags []string `json:"tags,omitempty"` + WrittenBy string `json:"written_by,omitempty"` + UpdatedAt int64 `json:"updated_at"` + } + out := make([]summary, 0, len(ms)) + for _, m := range ms { + out = append(out, summary{m.Key, m.Project, m.Tags, m.WrittenBy, m.UpdatedAt}) + } + return toJSON(out), nil + }) + + s.AddTool(mcp.NewTool("memory_projects", + mcp.WithDescription("List project groups in the memory bank with memory counts. Use to discover which projects have memories."), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + ps, err := st.MemoryProjects() + if err != nil { + return errResult(err), nil + } + type proj struct { + Name string `json:"name"` + Count int64 `json:"count"` + } + out := make([]proj, 0, len(ps)) + for _, p := range ps { + out = append(out, proj{p.Name, p.Count}) + } + return toJSON(out), nil + }) + + s.AddTool(mcp.NewTool("memory_delete", + mcp.WithDescription("Permanently delete a memory by key. Use when a fact is stale or was saved by mistake."), + mcp.WithString("key", mcp.Required()), + ), func(ctx context.Context, r mcp.CallToolRequest) (*mcp.CallToolResult, error) { + key, err := r.RequireString("key") + if err != nil { + return errResult(err), nil + } + ok, err := st.DeleteMemory(keyFrom(ctx, key)) + if err != nil { + return errResult(err), nil + } + return toJSON(map[string]any{"ok": ok, "key": key}), nil + }) + + s.AddPrompt(mcp.NewPrompt("save_context", + mcp.WithPromptDescription("Collect this project's durable context (repo, stack, servers, commands, gotchas) and save it to the shared memory bank."), + ), func(ctx context.Context, r mcp.GetPromptRequest) (*mcp.GetPromptResult, error) { + return mcp.NewGetPromptResult("Save project context to shared memory", []mcp.PromptMessage{ + mcp.NewPromptMessage(mcp.RoleUser, mcp.NewTextContent(`Collect this project's durable context and save it with the memory_save tool: + +1. Gather: git remote URL, default branch, current commit; language/framework/build system; external servers or endpoints the project talks to; non-obvious build/test/deploy commands; known pitfalls. +2. Do NOT include secrets, tokens, or task progress. +3. Call memory_save once per coherent topic using keys like "/" (e.g. "vivid/deploy", "vivid/stack"), with project and tags set. +4. Reply with the list of saved keys and their URLs.`)), + }), nil + }) + + // memory:// 资源:客户端可直接把记忆钉进上下文,省一轮 tools/call + s.AddResourceTemplate( + mcp.NewResourceTemplate("memory:/{/key*}", "memory", + mcp.WithTemplateDescription("Read a memory by key (e.g. memory://vivid/deploy)"), + mcp.WithTemplateMIMEType("text/markdown"), + ), + func(ctx context.Context, r mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { + var parts []string + switch v := r.Params.Arguments["key"].(type) { + case []string: + parts = v + case []any: + for _, s := range v { + if str, ok := s.(string); ok { + parts = append(parts, str) + } + } + case string: + parts = []string{v} + } + key := strings.Join(parts, "/") + m, err := st.GetMemory(key) + if err != nil { + return nil, err + } + if m == nil { + return nil, fmt.Errorf("no memory with key %q", key) + } + return []mcp.ResourceContents{mcp.TextResourceContents{ + URI: r.Params.URI, + MIMEType: "text/markdown", + Text: m.Content, + }}, nil + }, + ) + + return s +} diff --git a/backend/internal/payx/upstream.go b/backend/internal/payx/upstream.go new file mode 100644 index 0000000..18ae2e4 --- /dev/null +++ b/backend/internal/payx/upstream.go @@ -0,0 +1,612 @@ +// Package payx catfk.com 上游发卡平台对接:商户接口(merchantApi)走 Merchant-Token 鉴权, +// 买家接口(shopApi)无鉴权。提供登录、分类/商品自检、卡密库存维护、下单与支付状态查询。 +package payx + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/json" + "fmt" + "io" + "math" + "net/http" + "net/http/cookiejar" + "net/url" + "regexp" + "strings" + "sync" + "time" +) + +// apiError 上游业务错误。 +type apiError struct{ msg string } + +func (e *apiError) Error() string { return e.msg } + +// envelope 上游统一响应信封 {code,msg,data};code==1 为成功。 +type envelope struct { + Code int64 `json:"code"` + Msg string `json:"msg"` + Data json.RawMessage `json:"data"` +} + +// Client 上游平台客户端(fun.Wired 单例)。 +type Client struct { + base string + httpCli *http.Client + + mu sync.Mutex + token string + tokenAt int64 + username string + password string + shop string + nickname string + channelID int64 + channelAt int64 + + // 库存维护锁:确保同一时刻只有一个补库存流程(全程带锁)。 + stockMu sync.Mutex + ensuring chan struct{} +} + +func (c *Client) New() error { + c.base = "https://catfk.com" + c.httpCli = &http.Client{Timeout: 30 * time.Second} + return nil +} + +// Username 已配置的上游账号。 +func (c *Client) Username() string { c.mu.Lock(); defer c.mu.Unlock(); return c.username } + +// Shop 当前店铺买家链接 token。 +func (c *Client) Shop() string { c.mu.Lock(); defer c.mu.Unlock(); return c.shop } + +// Nickname 商户昵称。 +func (c *Client) Nickname() string { c.mu.Lock(); defer c.mu.Unlock(); return c.nickname } + +// TokenAge 当前令牌已存活秒数(0=未登录)。 +func (c *Client) TokenAge() int64 { + c.mu.Lock() + defer c.mu.Unlock() + if c.token == "" { + return 0 + } + return time.Now().Unix() - c.tokenAt +} + +// SetCredentials 设定/更新上游账号凭据并立即登录验证。 +func (c *Client) SetCredentials(username, password string) error { + c.mu.Lock() + c.username, c.password = username, password + c.token, c.tokenAt = "", 0 + c.mu.Unlock() + _, err := c.login() + return err +} + +// SetCredentialsLazy 仅落凭据不登录(启动时装配,首个请求时惰性登录)。 +func (c *Client) SetCredentialsLazy(username, password string) { + c.mu.Lock() + c.username, c.password = username, password + c.mu.Unlock() +} + +// ClearCredentials 清除凭据(账号删除时)。 +func (c *Client) ClearCredentials() { + c.mu.Lock() + c.username, c.password, c.token = "", "", "" + c.mu.Unlock() +} + +func (c *Client) login() (string, error) { + c.mu.Lock() + user, pass := c.username, c.password + c.mu.Unlock() + if user == "" || pass == "" { + return "", &apiError{"上游账号未配置"} + } + var out struct { + MerchantToken string `json:"merchant_token"` + } + if err := c.post(context.Background(), "/merchantApi/user/login", + map[string]any{"username": user, "password": pass}, "", &out); err != nil { + return "", err + } + if out.MerchantToken == "" { + return "", &apiError{"上游登录未返回令牌"} + } + c.mu.Lock() + c.token, c.tokenAt = out.MerchantToken, time.Now().Unix() + c.mu.Unlock() + return out.MerchantToken, nil +} + +// ensureSession 返回有效令牌:无令牌或调用方要求重登时重新登录。 +func (c *Client) ensureSession() (string, error) { + c.mu.Lock() + tok := c.token + c.mu.Unlock() + if tok != "" { + return tok, nil + } + return c.login() +} + +// merchantPost 商户接口调用:带 Merchant-Token;失败原样透出,不自动重登。 +func (c *Client) merchantPost(ctx context.Context, path string, body, out any) error { + tok, err := c.ensureSession() + if err != nil { + return err + } + return c.postOnce(ctx, path, body, tok, out) +} + +// post 通用调用:merchantToken 为空=买家接口;data 解码到 out。 +func (c *Client) post(ctx context.Context, path string, body any, merchantToken string, out any) error { + return c.postOnce(ctx, path, body, merchantToken, out) +} + +func (c *Client) postOnce(ctx context.Context, path string, body any, merchantToken string, out any) error { + raw, err := c.postRawOnce(ctx, path, body, merchantToken) + if err != nil { + return err + } + trimmed := bytes.TrimSpace(raw) + // 令牌失效/被风控时上游会回 HTML 登录页,统一归为鉴权类错误触发重登 + if len(trimmed) > 0 && trimmed[0] == '<' { + return &apiError{"上游返回登录页(token 已失效)"} + } + var env envelope + if err := json.Unmarshal(trimmed, &env); err != nil { + return fmt.Errorf("上游响应解析失败: %w", err) + } + if env.Code != 1 { + return &apiError{env.Msg} + } + if out != nil && len(env.Data) > 0 { + return json.Unmarshal(env.Data, out) + } + return nil +} + +func (c *Client) postRawOnce(ctx context.Context, path string, body any, merchantToken string) ([]byte, error) { + raw, err := json.Marshal(body) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.base+path, bytes.NewReader(raw)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + if merchantToken != "" { + req.Header.Set("merchant-token", merchantToken) + } + resp, err := c.httpCli.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + data, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20)) + if err != nil { + return nil, err + } + // 5xx/HTML 网关故障单独报错;200 + HTML = 令牌失效被踢回登录页 + if resp.StatusCode >= 500 { + return nil, &apiError{fmt.Sprintf("上游网关故障(HTTP %d)", resp.StatusCode)} + } + return data, nil +} + +// postRaw 返回原始响应体(code!=1 也是合法应答的接口用)。 +func (c *Client) postRaw(ctx context.Context, path string, body any) ([]byte, error) { + return c.postRawOnce(ctx, path, body, "") +} + +// ---------- 商户接口 ---------- + +// Userinfo 拉取商户资料,顺带刷新昵称与店铺公开 token。 +func (c *Client) Userinfo(ctx context.Context) (nickname, shop string, err error) { + var out struct { + Nickname string `json:"nickname"` + LinkWebsite string `json:"link_website"` + } + if err := c.merchantPost(ctx, "/merchantApi/user/userinfo", map[string]any{}, &out); err != nil { + return "", "", err + } + // link_website 可能是旧值,以 shop/getLink 返回的 /shop/ 为准 + shop = out.LinkWebsite + var link struct { + Link string `json:"link"` + } + if err := c.merchantPost(ctx, "/merchantApi/shop/getLink", map[string]any{}, &link); err == nil && link.Link != "" { + if u, perr := url.Parse(link.Link); perr == nil && strings.HasPrefix(u.Path, "/shop/") { + if seg := strings.Trim(u.Path[len("/shop/"):], "/"); seg != "" && !strings.Contains(seg, "/") { + shop = seg + } + } + } + c.mu.Lock() + if c.shop != shop { + c.channelID, c.channelAt = 0, 0 + } + c.nickname, c.shop = out.Nickname, shop + c.mu.Unlock() + return out.Nickname, shop, nil +} + +// MerchantGoods 商户商品列表项。 +type MerchantGoods struct { + ID int64 `json:"id"` + GoodsKey string `json:"goods_key"` + Name string `json:"name"` + Price int64 `json:"-"` // 分 + PriceYuan float64 `json:"price"` + Status int64 `json:"status"` + Stock int64 `json:"-"` + Extend struct { + StockCount int64 `json:"stock_count"` + } `json:"extend"` +} + +// GoodsList 商户商品列表(卡密类)。 +func (c *Client) GoodsList(ctx context.Context, keywords string, current int64) ([]MerchantGoods, int64, error) { + var out struct { + Total int64 `json:"total"` + List []MerchantGoods `json:"list"` + } + body := map[string]any{ + "current": current, "pageSize": 20, "goods_type": "card", + "status": 999, "name": keywords, "is_proxy": "0", + } + if err := c.merchantPost(ctx, "/merchantApi/Goods/list", body, &out); err != nil { + return nil, 0, err + } + for i := range out.List { + out.List[i].Price = yuanFen(out.List[i].PriceYuan) + out.List[i].Stock = out.List[i].Extend.StockCount + } + return out.List, out.Total, nil +} + +// GoodsInfo 商品详情:售价(分)、名称、库存张数。 +func (c *Client) GoodsInfo(ctx context.Context, goodsID int64) (priceFen int64, name string, stock int64, err error) { + var out struct { + Name string `json:"name"` + Price float64 `json:"price"` + Extend struct { + StockCount int64 `json:"stock_count"` + } `json:"extend"` + } + if err := c.merchantPost(ctx, "/merchantApi/Goods/info", map[string]any{"id": fmt.Sprint(goodsID)}, &out); err != nil { + return 0, "", 0, err + } + return yuanFen(out.Price), out.Name, out.Extend.StockCount, nil +} + +// CategoryListAll 全部卡密分类(value=分类 ID)。 +func (c *Client) CategoryListAll(ctx context.Context) ([]Category, error) { + var out []struct { + Value int64 `json:"value"` + Label string `json:"label"` + } + if err := c.merchantPost(ctx, "/merchantApi/GoodsCategory/listAll", map[string]any{"goods_type": "card"}, &out); err != nil { + return nil, err + } + cats := make([]Category, 0, len(out)) + for _, v := range out { + cats = append(cats, Category{ID: v.Value, Name: v.Label}) + } + return cats, nil +} + +// Category 卡密分类。 +type Category struct { + ID int64 + Name string +} + +// CategoryAdd 新建卡密分类(响应 data 为 null,需再查 listAll 拿 ID)。 +func (c *Client) CategoryAdd(ctx context.Context, name string) error { + return c.merchantPost(ctx, "/merchantApi/GoodsCategory/update", map[string]any{ + "id": 0, "name": name, "image": "", "sort": 0, "goods_type": "card", + }, nil) +} + +// GoodsAdd 新建卡密商品,返回商品 ID 与 goods_key。 +func (c *Client) GoodsAdd(ctx context.Context, name string, categoryID int64, priceYuan float64) (int64, string, error) { + var out struct { + ID int64 `json:"id"` + GoodsKey string `json:"goods_key"` + } + body := map[string]any{ + "goods_type": "card", "id": 0, "name": name, "image": "", "category_id": categoryID, + "price": priceYuan, "market_price": 0, "description": "", "sort": 0, "coupon_status": 1, + "status": 1, "fee_payer": -1, "show": 1, "contact_format": "any", "agent_status": 0, + "agent_price1": 0, "agent_price2": 0, "agent_price3": 0, "agent_price_limit": 0, + "description_sync": 0, "name_sync": 0, "parent_id": 0, "cost_price": 0, + "add_type": 1, "add_rate": 0, "add_price": 0, + "extend": map[string]any{ + "instructions": "


", "stock_notice": 0, "lock_card": 0, + "limit_count": 1, "limit_count_max": 0, "show_stock_type": 0, + "send_order": 0, "query_password_status": 0, + }, + } + if err := c.merchantPost(ctx, "/merchantApi/Goods/update", body, &out); err != nil { + return 0, "", err + } + return out.ID, out.GoodsKey, nil +} + +// CardAdd 导入卡密(一行一张)。 +func (c *Client) CardAdd(ctx context.Context, goodsID int64, content string) error { + return c.merchantPost(ctx, "/merchantApi/GoodsCardStorage/add", map[string]any{ + "goods_id": goodsID, "content": content, "first": 0, "remove_repeat": 0, + }, nil) +} + +// cardAddBatch 上游单次导入上限(1w 张/批)。 +const cardAddBatch = int64(10000) + +// CardAddN 批量导入 n 张随机卡密,每次 1 万张分批。 +func (c *Client) CardAddN(ctx context.Context, goodsID, n int64) error { + if n <= 0 || n > 1_000_000 { + return fmt.Errorf("补库存数量超出限制: %d", n) + } + for left := n; left > 0; { + b := left + if b > cardAddBatch { + b = cardAddBatch + } + if err := c.CardAdd(ctx, goodsID, RandCards(b)); err != nil { + return err + } + left -= b + } + return nil +} + +// StockLock 获取库存全局锁(调用方负责释放),确保补库存/下单串行。 +func (c *Client) StockLock() func() { + c.stockMu.Lock() + return c.stockMu.Unlock +} + +// EnsureStock 补库存到 target 张(调用前需持有 StockLock)。 +func (c *Client) EnsureStock(ctx context.Context, goodsID, target int64) (int64, error) { + _, _, stock, err := c.GoodsInfo(ctx, goodsID) + if err != nil { + return 0, err + } + if stock < target { + if err := c.CardAddN(ctx, goodsID, target-stock); err != nil { + return stock, err + } + if _, _, stock, err = c.GoodsInfo(ctx, goodsID); err != nil { + return target, nil + } + } + return stock, nil +} + +// ---------- 买家接口(无鉴权) ---------- + +// Channel 支付通道。 +type Channel struct { + ID int64 `json:"id"` + Name string `json:"name"` + ShowName string `json:"show_name"` + Status int64 `json:"status"` + CustomStatus int64 `json:"custom_status"` +} + +// AlipayChannelID 店铺可用的支付宝支付通道 id,缓存 10 分钟。 +func (c *Client) AlipayChannelID(ctx context.Context, shop string) (int64, error) { + c.mu.Lock() + if c.channelID > 0 && time.Now().Unix()-c.channelAt < 600 { + id := c.channelID + c.mu.Unlock() + return id, nil + } + c.mu.Unlock() + var list []Channel + if err := c.post(ctx, "/shopApi/Shop/getUserChannel", map[string]any{"token": shop}, "", &list); err != nil { + if strings.Contains(err.Error(), "Attempt to read property") { + return 0, &apiError{"上游店铺支付通道未开通"} + } + return 0, err + } + var pick int64 + for _, ch := range list { + if ch.Status != 1 || ch.CustomStatus != 1 { + continue + } + if pick == 0 { + pick = ch.ID + } + if strings.Contains(ch.Name, "支付宝") || strings.Contains(ch.ShowName, "支付宝") { + pick = ch.ID + break + } + } + if pick == 0 { + return 0, &apiError{"上游没有可用的支付宝支付通道"} + } + c.mu.Lock() + c.channelID, c.channelAt = pick, time.Now().Unix() + c.mu.Unlock() + return pick, nil +} + +// PayOrderResult 上游下单结果。 +type PayOrderResult struct { + TradeNo string + TotalAmount int64 // 分 + PayURL string +} + +// CreateOrder 上游买家下单(微信支付通道),返回平台订单号与收银台地址。 +func (c *Client) CreateOrder(ctx context.Context, goodsKey string, quantity, channelID int64, contact, queryPwd string) (PayOrderResult, error) { + var raw struct { + TradeNo string `json:"trade_no"` + TotalAmount float64 `json:"total_amount"` + PayURL string `json:"payurl"` + } + err := c.post(ctx, "/shopApi/Pay/order", map[string]any{ + "goods_key": goodsKey, "quantity": quantity, "coupon_code": "", + "channel_id": channelID, "contact": contact, "query_password": queryPwd, + "select_cards_ids": []any{}, + "extend": map[string]any{"juuid": randHex(12)}, + }, "", &raw) + if err != nil { + return PayOrderResult{}, err + } + return PayOrderResult{TradeNo: raw.TradeNo, TotalAmount: yuanFen(raw.TotalAmount), PayURL: raw.PayURL}, nil +} + +// FetchQRCode 提取支付宝扫码付二维码内容。 +// 流程:GET payurl 收银台(自动提交表单)→ POST openapi.alipay.com 网关 → +// 302 到 excashier 收银页 → 页面内嵌 。 +func (c *Client) FetchQRCode(ctx context.Context, tradeNo string) (string, error) { + jar, _ := cookiejar.New(nil) + cli := &http.Client{Timeout: 30 * time.Second, Jar: jar} + ua := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36" + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, + c.base+"/shopApi/Pay/payment?trade_no="+tradeNo, nil) + if err != nil { + return "", err + } + req.Header.Set("User-Agent", ua) + resp, err := cli.Do(req) + if err != nil { + return "", fmt.Errorf("获取收银台页面失败: %w", err) + } + body, err := io.ReadAll(io.LimitReader(resp.Body, 2<<20)) + resp.Body.Close() + if err != nil { + return "", err + } + action := formActionRe.FindSubmatch(body) + if len(action) < 2 { + return "", fmt.Errorf("收银台页面未找到支付表单") + } + form := url.Values{} + for _, m := range formFieldRe.FindAllSubmatch(body, -1) { + form.Set(string(m[1]), string(m[2])) + } + req2, err := http.NewRequestWithContext(ctx, http.MethodPost, string(action[1]), + strings.NewReader(form.Encode())) + if err != nil { + return "", err + } + req2.Header.Set("User-Agent", ua) + req2.Header.Set("Content-Type", "application/x-www-form-urlencoded") + resp2, err := cli.Do(req2) + if err != nil { + return "", fmt.Errorf("收银台跳转失败: %w", err) + } + body2, err := io.ReadAll(io.LimitReader(resp2.Body, 2<<20)) + resp2.Body.Close() + if err != nil { + return "", err + } + m := qrInputRe.FindSubmatch(body2) + if len(m) < 2 { + return "", fmt.Errorf("收银台页面未找到二维码") + } + return string(m[1]), nil +} + +var ( + formActionRe = regexp.MustCompile(`action='([^']+)'`) + formFieldRe = regexp.MustCompile(`name='([^']+)' value='([^']*)'`) + qrInputRe = regexp.MustCompile(`name="qrCode"\s+[^>]*value="([^"]+)"`) +) + +// OrderPaid 上游订单是否已支付(Pay/query:未支付 code=0,已支付 code=1)。 +func (c *Client) OrderPaid(ctx context.Context, tradeNo string) (bool, error) { + raw, err := c.postRaw(ctx, "/shopApi/Pay/query", map[string]any{"trade_no": tradeNo}) + if err != nil { + return false, err + } + var env envelope + if err := json.Unmarshal(raw, &env); err != nil { + return false, err + } + return env.Code == 1, nil +} + +// OrderCards 已支付订单交付的卡密列表。 +func (c *Client) OrderCards(ctx context.Context, tradeNo string) ([]string, error) { + var out struct { + Status int64 `json:"status"` + Response struct { + Cards []string `json:"cards"` + } `json:"response"` + } + if err := c.post(ctx, "/shopApi/Order/info", map[string]any{"trade_no": tradeNo, "dump": 1}, "", &out); err != nil { + return nil, err + } + return out.Response.Cards, nil +} + +// WalletInfo 商户钱包(可提现/冻结,分)。 +func (c *Client) WalletInfo(ctx context.Context) (availableFen, frozenFen int64, err error) { + var out struct { + Platform struct { + AvailableMoney float64 `json:"available_money"` + FreezeMoney float64 `json:"freeze_money"` + } `json:"platform"` + } + if err := c.merchantPost(ctx, "/merchantApi/wallet/info", map[string]any{}, &out); err != nil { + return 0, 0, err + } + return yuanFen(out.Platform.AvailableMoney), yuanFen(out.Platform.FreezeMoney), nil +} + +// ---------- 工具 ---------- + +func yuanFen(y float64) int64 { return int64(math.Round(y * 100)) } + +// RandContact 随机买家联系邮箱。 +func RandContact() string { return "p" + randHex(10) + "@outlook.com" } + +// RandQueryPwd 随机订单查询密码。 +func RandQueryPwd() string { return randHex(8) } + +// RandCards 生成 n 张随机唯一卡密(一行一张)。 +func RandCards(n int64) string { + var b strings.Builder + seen := make(map[string]struct{}, n) + for i := int64(0); i < n; { + card := randHex(16) + if _, ok := seen[card]; ok { + continue + } + seen[card] = struct{}{} + if i > 0 { + b.WriteByte('\n') + } + b.WriteString(card) + i++ + } + return b.String() +} + +func randHex(n int) string { + raw := make([]byte, n) + if _, err := rand.Read(raw); err != nil { + for i := range raw { + raw[i] = byte(time.Now().UnixNano() >> (i % 8)) + } + } + const digits = "0123456789abcdef" + b := make([]byte, n) + for i := range b { + b[i] = digits[raw[i]%16] + } + return string(b) +} diff --git a/backend/internal/proxyx/proxyx.go b/backend/internal/proxyx/proxyx.go new file mode 100644 index 0000000..284928a --- /dev/null +++ b/backend/internal/proxyx/proxyx.go @@ -0,0 +1,432 @@ +package proxyx + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "net/http/cookiejar" + "strings" + "time" +) + +const ( + SourceURL = "https://proxy5.net/api/free-proxies.php" + pageURL = "https://proxy5.net/cn/free-proxy" + // version window used by the site's own JS: floor(now / 30min) + versionWindowSec = 1800 + ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36" +) + +type Remote struct { + IP string `json:"ip_address"` + Port int `json:"port"` + Protocols []string `json:"protocols"` + Anonymity string `json:"anonymity"` + Country string `json:"country"` + CountryCode string `json:"country_code"` + CountrySlug string `json:"country_slug"` + Region string `json:"region"` + Latency int `json:"latency"` + Speed int `json:"download_speed"` + Uptime int `json:"uptime"` + Working bool `json:"is_working"` +} + +type payload struct { + CreatedAt string `json:"created_at_utc"` + Count int `json:"count"` + Results []Remote `json:"results"` +} + +var client = newClient() + +func newClient() *http.Client { + jar, _ := cookiejar.New(nil) + return &http.Client{ + Jar: jar, + Timeout: 90 * time.Second, + } +} + +func version() string { + return fmt.Sprint(time.Now().Unix() / versionWindowSec) +} + +func get(ctx context.Context, url string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", ua) + req.Header.Set("Accept", "application/json") + req.Header.Set("Referer", pageURL) + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<16)) + return nil, fmt.Errorf("HTTP %d", resp.StatusCode) + } + return io.ReadAll(io.LimitReader(resp.Body, 64<<20)) +} + +// Fetch pulls the full proxy catalog. If the API answers with a Cloudflare +// challenge it warms up the cookie jar by loading the listing page once, then retries. +func Fetch(ctx context.Context) ([]Remote, string, error) { + u := SourceURL + "?v=" + version() + body, err := get(ctx, u) + var p payload + if err == nil { + err = json.Unmarshal(body, &p) + } + if err != nil { + if _, warmErr := get(ctx, pageURL); warmErr == nil { + if body, err = get(ctx, u); err == nil { + err = json.Unmarshal(body, &p) + } + } + } + if err != nil { + return nil, "", err + } + out := p.Results[:0] + for _, r := range p.Results { + if r.IP != "" && r.Port > 0 { + out = append(out, r) + } + } + return out, p.CreatedAt, nil +} + +// ---- rola-ip 源 ---- + +const rolaURL = "https://rola-ip.co/proxy-api/api/v1/proxies" +const rolaReferer = "https://rola-ip.co/zh/tools/free-proxy-list/" +const rolaPageSize = 500 +const rolaMaxPages = 40 + +type rolaRow struct { + IP string `json:"ip"` + Port int `json:"port"` + Protocols []string `json:"protocols"` + Code string `json:"code"` + Country string `json:"country"` + Anonymity string `json:"anonymity"` + Latency *float64 `json:"latency"` + Speed *float64 `json:"speed"` + Uptime *float64 `json:"uptime"` +} + +type rolaPayload struct { + Data []rolaRow `json:"data"` + Pagination struct { + Page int `json:"page"` + Total int `json:"total"` + TotalPages int `json:"totalPages"` + } `json:"pagination"` +} + +func rolaAnonymity(a string) string { + switch strings.ToLower(a) { + case "elite": + return "Elite" + case "anonymous": + return "Anonymous" + case "transparent": + return "Transparent" + } + return a +} + +// FetchRola 拉取 rola-ip 免费代理列表:公开前缀 /proxy-api,500/页翻到底。 +func FetchRola(ctx context.Context) ([]Remote, error) { + out := []Remote{} + for page := 1; page <= rolaMaxPages; page++ { + u := fmt.Sprintf("%s?page=%d&pageSize=%d", rolaURL, page, rolaPageSize) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) + if err != nil { + return nil, err + } + req.Header.Set("User-Agent", ua) + req.Header.Set("Accept", "application/json") + req.Header.Set("Referer", rolaReferer) + resp, err := client.Do(req) + if err != nil { + return nil, err + } + body, err := io.ReadAll(io.LimitReader(resp.Body, 16<<20)) + resp.Body.Close() + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP %d", resp.StatusCode) + } + var p rolaPayload + if err := json.Unmarshal(body, &p); err != nil { + return nil, err + } + for _, r := range p.Data { + if r.IP == "" || r.Port <= 0 { + continue + } + protos := make([]string, 0, len(r.Protocols)) + for _, pr := range r.Protocols { + protos = append(protos, strings.ToUpper(pr)) + } + var lat, spd, upt int + if r.Latency != nil { + lat = int(*r.Latency) + } + if r.Speed != nil { + spd = int(*r.Speed) + } + if r.Uptime != nil { + upt = int(*r.Uptime) + } + out = append(out, Remote{ + IP: r.IP, Port: r.Port, Protocols: protos, + Anonymity: rolaAnonymity(r.Anonymity), + Country: r.Country, CountryCode: strings.ToUpper(r.Code), + Region: Continent(r.Code), + Latency: lat, Speed: spd, Uptime: upt, + }) + } + if page >= p.Pagination.TotalPages || len(p.Data) == 0 { + break + } + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(300 * time.Millisecond): + } + } + return out, nil +} + +// Continent 国家码 → 大洲(与 proxy5 的 region 字段取值对齐) +func Continent(code string) string { + if c, ok := continents[strings.ToUpper(code)]; ok { + return c + } + return "Other" +} + +var continents = map[string]string{ + // Asia + "AF": "Asia", "AM": "Asia", "AZ": "Asia", "BH": "Asia", "BD": "Asia", + "BN": "Asia", "KH": "Asia", "CN": "Asia", "HK": "Asia", "MO": "Asia", + "TW": "Asia", "CY": "Asia", "GE": "Asia", "IN": "Asia", "ID": "Asia", + "IR": "Asia", "IQ": "Asia", "IL": "Asia", "JP": "Asia", "JO": "Asia", + "KZ": "Asia", "KW": "Asia", "KG": "Asia", "LA": "Asia", "LB": "Asia", + "MY": "Asia", "MV": "Asia", "MN": "Asia", "MM": "Asia", "NP": "Asia", + "KP": "Asia", "OM": "Asia", "PK": "Asia", "PS": "Asia", "PH": "Asia", + "QA": "Asia", "SA": "Asia", "SG": "Asia", "KR": "Asia", "LK": "Asia", + "SY": "Asia", "TJ": "Asia", "TH": "Asia", "TL": "Asia", "TR": "Asia", + "TM": "Asia", "AE": "Asia", "UZ": "Asia", "VN": "Asia", "YE": "Asia", + // Europe + "AL": "Europe", "AD": "Europe", "AT": "Europe", "BY": "Europe", "BE": "Europe", + "BA": "Europe", "BG": "Europe", "HR": "Europe", "CZ": "Europe", "DK": "Europe", + "EE": "Europe", "FI": "Europe", "FR": "Europe", "DE": "Europe", "GR": "Europe", + "HU": "Europe", "IS": "Europe", "IE": "Europe", "IT": "Europe", "LV": "Europe", + "LI": "Europe", "LT": "Europe", "LU": "Europe", "MT": "Europe", "MD": "Europe", + "MC": "Europe", "ME": "Europe", "NL": "Europe", "MK": "Europe", "NO": "Europe", + "PL": "Europe", "PT": "Europe", "RO": "Europe", "RU": "Europe", "SM": "Europe", + "RS": "Europe", "SK": "Europe", "SI": "Europe", "ES": "Europe", "SE": "Europe", + "CH": "Europe", "UA": "Europe", "GB": "Europe", "XK": "Europe", "VA": "Europe", + // Americas + "AG": "Americas", "AR": "Americas", "BS": "Americas", "BB": "Americas", "BZ": "Americas", + "BO": "Americas", "BR": "Americas", "CA": "Americas", "CL": "Americas", "CO": "Americas", + "CR": "Americas", "CU": "Americas", "DM": "Americas", "DO": "Americas", "EC": "Americas", + "SV": "Americas", "GD": "Americas", "GT": "Americas", "GY": "Americas", "HT": "Americas", + "HN": "Americas", "JM": "Americas", "MX": "Americas", "NI": "Americas", "PA": "Americas", + "PY": "Americas", "PE": "Americas", "PR": "Americas", "KN": "Americas", "LC": "Americas", + "VC": "Americas", "SR": "Americas", "TT": "Americas", "US": "Americas", "UY": "Americas", + "VE": "Americas", "VG": "Americas", "AI": "Americas", "AW": "Americas", "BM": "Americas", + // Africa + "DZ": "Africa", "AO": "Africa", "BJ": "Africa", "BW": "Africa", "BF": "Africa", + "BI": "Africa", "CM": "Africa", "CF": "Africa", "TD": "Africa", "KM": "Africa", + "CG": "Africa", "CD": "Africa", "CI": "Africa", "DJ": "Africa", "EG": "Africa", + "GQ": "Africa", "ER": "Africa", "SZ": "Africa", "ET": "Africa", "GA": "Africa", + "GM": "Africa", "GH": "Africa", "GN": "Africa", "GW": "Africa", "KE": "Africa", + "LS": "Africa", "LR": "Africa", "LY": "Africa", "MG": "Africa", "MW": "Africa", + "ML": "Africa", "MR": "Africa", "MU": "Africa", "MA": "Africa", "MZ": "Africa", + "NA": "Africa", "NE": "Africa", "NG": "Africa", "RW": "Africa", "ST": "Africa", + "SN": "Africa", "SC": "Africa", "SL": "Africa", "SO": "Africa", "ZA": "Africa", + "SS": "Africa", "SD": "Africa", "TZ": "Africa", "TG": "Africa", "TN": "Africa", + "UG": "Africa", "ZM": "Africa", "ZW": "Africa", + // Pacific + "AU": "Pacific", "FJ": "Pacific", "KI": "Pacific", "MH": "Pacific", "FM": "Pacific", + "NR": "Pacific", "NZ": "Pacific", "PW": "Pacific", "PG": "Pacific", "WS": "Pacific", + "SB": "Pacific", "TO": "Pacific", "TV": "Pacific", "VU": "Pacific", "GU": "Pacific", +} + +// probeTarget 测活拨测目标:公网稳定 HTTP 站点 +const probeHost = "www.gstatic.com" +const probePort = 80 + +// Check 协议感知测活:按 protocols 依次尝试 SOCKS5/SOCKS4/HTTP 握手, +// 纯 TCP 连通不算数——必须说出协议正确的应答才算活。返回拨测耗时。 +func Check(ctx context.Context, ip string, port int, protocols string, timeout time.Duration) (int, error) { + probes := []func(context.Context, string, int, time.Duration) error{ + probeSOCKS5, probeSOCKS4, probeHTTP, + } + upper := strings.ToUpper(protocols) + order := []int{} + for i, name := range []string{"SOCKS5", "SOCKS4", "HTTP"} { + if strings.Contains(upper, name) { + order = append(order, i) + } + } + if len(order) == 0 { + order = []int{0, 1, 2} + } + var lastErr error + for _, i := range order { + start := time.Now() + if err := probes[i](ctx, ip, port, timeout); err == nil { + return int(time.Since(start).Milliseconds()), nil + } else { + lastErr = err + } + } + return 0, lastErr +} + +func dial(ctx context.Context, ip string, port int, timeout time.Duration) (net.Conn, error) { + d := net.Dialer{Timeout: timeout} + return d.DialContext(ctx, "tcp", net.JoinHostPort(ip, fmt.Sprint(port))) +} + +func probeSOCKS5(ctx context.Context, ip string, port int, timeout time.Duration) error { + conn, err := dial(ctx, ip, port, timeout) + if err != nil { + return err + } + defer conn.Close() + conn.SetDeadline(time.Now().Add(timeout)) + // greeting: VER=5, 1 method, no-auth + if _, err := conn.Write([]byte{0x05, 0x01, 0x00}); err != nil { + return err + } + buf := make([]byte, 2) + if _, err := io.ReadFull(conn, buf); err != nil { + return err + } + if buf[0] != 0x05 || buf[1] == 0xff { + return fmt.Errorf("socks5 greeting rejected: %x", buf) + } + // CONNECT domain:port + req := append([]byte{0x05, 0x01, 0x00, 0x03, byte(len(probeHost))}, []byte(probeHost)...) + req = append(req, byte(probePort>>8), byte(probePort)) + if _, err := conn.Write(req); err != nil { + return err + } + // reply: VER REP RSV ATYP ... — 收到合法的 socks5 应答头即算活 + head := make([]byte, 5) + if _, err := io.ReadFull(conn, head); err != nil { + return err + } + if head[0] != 0x05 { + return fmt.Errorf("bad socks5 reply: %x", head[0]) + } + return nil +} + +func probeSOCKS4(ctx context.Context, ip string, port int, timeout time.Duration) error { + conn, err := dial(ctx, ip, port, timeout) + if err != nil { + return err + } + defer conn.Close() + conn.SetDeadline(time.Now().Add(timeout)) + // CONNECT 1.1.1.1:80 + req := []byte{0x04, 0x01, 0x00, 0x50, 1, 1, 1, 1, 0x00} + if _, err := conn.Write(req); err != nil { + return err + } + buf := make([]byte, 8) + if _, err := io.ReadFull(conn, buf); err != nil { + return err + } + if buf[0] != 0x00 && buf[0] != 0x04 { + return fmt.Errorf("bad socks4 reply: %x", buf[0]) + } + return nil +} + +func probeHTTP(ctx context.Context, ip string, port int, timeout time.Duration) error { + conn, err := dial(ctx, ip, port, timeout) + if err != nil { + return err + } + defer conn.Close() + conn.SetDeadline(time.Now().Add(timeout)) + req := fmt.Sprintf("GET http://%s/generate_204 HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", probeHost, probeHost) + if _, err := conn.Write([]byte(req)); err != nil { + return err + } + buf := make([]byte, 5) + if _, err := io.ReadFull(conn, buf); err != nil { + return err + } + if string(buf) != "HTTP/" { + return fmt.Errorf("bad http reply: %q", string(buf)) + } + return nil +} + +// ZhName maps an ISO-like country code to a Chinese display name. +func ZhName(code string) string { + if n, ok := zhNames[strings.ToUpper(code)]; ok { + return n + } + return "" +} + +var zhNames = map[string]string{ + "AF": "阿富汗", "AL": "阿尔巴尼亚", "DZ": "阿尔及利亚", "AD": "安道尔", "AO": "安哥拉", + "AG": "安提瓜和巴布达", "AR": "阿根廷", "AM": "亚美尼亚", "AU": "澳大利亚", "AT": "奥地利", + "AZ": "阿塞拜疆", "BS": "巴哈马", "BH": "巴林", "BD": "孟加拉国", "BB": "巴巴多斯", + "BY": "白俄罗斯", "BE": "比利时", "BZ": "伯利兹", "BJ": "贝宁", "BO": "玻利维亚", + "BA": "波黑", "BW": "博茨瓦纳", "BR": "巴西", "BN": "文莱", "BG": "保加利亚", + "BF": "布基纳法索", "BI": "布隆迪", "KH": "柬埔寨", "CM": "喀麦隆", "CA": "加拿大", + "CF": "中非", "TD": "乍得", "CL": "智利", "CN": "中国", "HK": "中国香港", + "MO": "中国澳门", "TW": "中国台湾", "CO": "哥伦比亚", "KM": "科摩罗", "CG": "刚果", + "CD": "刚果(金)", "CR": "哥斯达黎加", "CI": "科特迪瓦", "HR": "克罗地亚", "CU": "古巴", + "CY": "塞浦路斯", "CZ": "捷克", "DK": "丹麦", "DJ": "吉布提", "DM": "多米尼克", + "DO": "多米尼加", "EC": "厄瓜多尔", "EG": "埃及", "SV": "萨尔瓦多", "GQ": "赤道几内亚", + "ER": "厄立特里亚", "EE": "爱沙尼亚", "SZ": "斯威士兰", "ET": "埃塞俄比亚", "FJ": "斐济", + "FI": "芬兰", "FR": "法国", "GA": "加蓬", "GM": "冈比亚", "GE": "格鲁吉亚", + "DE": "德国", "GH": "加纳", "GR": "希腊", "GD": "格林纳达", "GT": "危地马拉", + "GN": "几内亚", "GW": "几内亚比绍", "GY": "圭亚那", "HT": "海地", "HN": "洪都拉斯", + "HU": "匈牙利", "IS": "冰岛", "IN": "印度", "ID": "印度尼西亚", "IR": "伊朗", + "IQ": "伊拉克", "IE": "爱尔兰", "IL": "以色列", "IT": "意大利", "JM": "牙买加", + "JP": "日本", "JO": "约旦", "KZ": "哈萨克斯坦", "KE": "肯尼亚", "KI": "基里巴斯", + "KP": "朝鲜", "KR": "韩国", "KW": "科威特", "KG": "吉尔吉斯斯坦", "LA": "老挝", + "LV": "拉脱维亚", "LB": "黎巴嫩", "LS": "莱索托", "LR": "利比里亚", "LY": "利比亚", + "LI": "列支敦士登", "LT": "立陶宛", "LU": "卢森堡", "MG": "马达加斯加", "MW": "马拉维", + "MY": "马来西亚", "MV": "马尔代夫", "ML": "马里", "MT": "马耳他", "MH": "马绍尔群岛", + "MR": "毛里塔尼亚", "MU": "毛里求斯", "MX": "墨西哥", "FM": "密克罗尼西亚", "MD": "摩尔多瓦", + "MC": "摩纳哥", "MN": "蒙古", "ME": "黑山", "MA": "摩洛哥", "MZ": "莫桑比克", + "MM": "缅甸", "NA": "纳米比亚", "NR": "瑙鲁", "NP": "尼泊尔", "NL": "荷兰", + "NZ": "新西兰", "NI": "尼加拉瓜", "NE": "尼日尔", "NG": "尼日利亚", "MK": "北马其顿", + "NO": "挪威", "OM": "阿曼", "PK": "巴基斯坦", "PW": "帕劳", "PA": "巴拿马", + "PG": "巴布亚新几内亚", "PY": "巴拉圭", "PE": "秘鲁", "PH": "菲律宾", "PL": "波兰", + "PT": "葡萄牙", "QA": "卡塔尔", "RO": "罗马尼亚", "RU": "俄罗斯", "RW": "卢旺达", + "WS": "萨摩亚", "SM": "圣马力诺", "ST": "圣多美和普林西比", "SA": "沙特阿拉伯", "SN": "塞内加尔", + "RS": "塞尔维亚", "SC": "塞舌尔", "SL": "塞拉利昂", "SG": "新加坡", "SK": "斯洛伐克", + "SI": "斯洛文尼亚", "SB": "所罗门群岛", "SO": "索马里", "ZA": "南非", "SS": "南苏丹", + "ES": "西班牙", "LK": "斯里兰卡", "SD": "苏丹", "SR": "苏里南", "SE": "瑞典", + "CH": "瑞士", "SY": "叙利亚", "TJ": "塔吉克斯坦", "TZ": "坦桑尼亚", "TH": "泰国", + "TL": "东帝汶", "TG": "多哥", "TO": "汤加", "TT": "特立尼达和多巴哥", "TN": "突尼斯", + "TR": "土耳其", "TM": "土库曼斯坦", "TV": "图瓦卢", "UG": "乌干达", "UA": "乌克兰", + "AE": "阿联酋", "GB": "英国", "US": "美国", "UY": "乌拉圭", "UZ": "乌兹别克斯坦", + "VU": "瓦努阿图", "VE": "委内瑞拉", "VN": "越南", "YE": "也门", "ZM": "赞比亚", + "ZW": "津巴布韦", "PS": "巴勒斯坦", "XK": "科索沃", "PR": "波多黎各", "VG": "英属维尔京群岛", +} diff --git a/backend/internal/service/auth.go b/backend/internal/service/auth.go new file mode 100644 index 0000000..8ef57bd --- /dev/null +++ b/backend/internal/service/auth.go @@ -0,0 +1,123 @@ +package service + +import ( + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "log" + "strings" + "time" + + "github.com/cyi-cc/fun" + "golang.org/x/crypto/bcrypt" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// 登录态:24h 滑动过期;距上次续期超过 2h 的请求自动把有效期顺延回 24h +const sessionTTL = 24 * time.Hour +const sessionRenewAfter = 2 * time.Hour + +type AuthSvc struct { + fun.Ctx + Store *store.Store +} + +// Login 用户名+密码 → 签发会话 token(前端存 localStorage,随 state 透传) +func (s *AuthSvc) Login(d dto.LoginDto) (dto.SessionView, error) { + name := strings.TrimSpace(d.Name) + if name == "" || d.Password == "" { + return dto.SessionView{}, fun.Error(4001, "请输入用户名和密码") + } + user, err := s.Store.GetUserByName(name) + if errors.Is(err, store.ErrNotFound) { + return dto.SessionView{}, fun.Error(4001, "用户名或密码错误") + } + if err != nil { + return dto.SessionView{}, fmt.Errorf("auth: query user failed: %w", err) + } + if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(d.Password)) != nil { + return dto.SessionView{}, fun.Error(4001, "用户名或密码错误") + } + if user.Status != "active" { + return dto.SessionView{}, fun.Error(4003, "账号已被禁用") + } + token, err := newToken() + if err != nil { + return dto.SessionView{}, err + } + if err := s.Store.CreateSession(token, user.Id, sessionTTL); err != nil { + return dto.SessionView{}, fmt.Errorf("auth: create session failed: %w", err) + } + log.Printf("auth: user %q logged in from %s", user.Name, s.Ip) + view := userView(user) + return dto.SessionView{User: &view, Token: &token}, nil +} + +// Me 返回当前登录用户(不重复签发 token) +func (s *AuthSvc) Me() (dto.SessionView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.SessionView{}, err + } + view := userView(su.User) + return dto.SessionView{User: &view}, nil +} + +// Logout 幂等:删掉当前会话即可 +func (s *AuthSvc) Logout() (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{Message: "已退出"}, nil + } + if err := s.Store.DeleteSession(su.Token); err != nil { + return dto.OkView{}, fmt.Errorf("auth: logout failed: %w", err) + } + return dto.OkView{Message: "已退出"}, nil +} + +// ChangePassword 校验旧密码 → 改密 → 吊销其他会话(当前会话保留) +func (s *AuthSvc) ChangePassword(d dto.ChangePasswordDto) (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{}, err + } + if len(d.NewPassword) < 6 { + return dto.OkView{}, fun.Error(4001, "新密码至少 6 位") + } + if bcrypt.CompareHashAndPassword([]byte(su.User.PasswordHash), []byte(d.OldPassword)) != nil { + return dto.OkView{}, fun.Error(4001, "旧密码错误") + } + hash, err := bcrypt.GenerateFromPassword([]byte(d.NewPassword), bcrypt.DefaultCost) + if err != nil { + return dto.OkView{}, err + } + h := string(hash) + if err := s.Store.UpdateUser(su.User.Id, su.User.Name, su.User.Role, su.User.Status, &h); err != nil { + return dto.OkView{}, fmt.Errorf("auth: update password failed: %w", err) + } + if err := s.Store.DeleteOtherSessions(su.User.Id, su.Token); err != nil { + return dto.OkView{}, fmt.Errorf("auth: revoke sessions failed: %w", err) + } + return dto.OkView{Message: "密码已修改"}, nil +} + +func newToken() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return hex.EncodeToString(b), nil +} + +func userView(u store.User) dto.UserView { + return dto.UserView{ + Id: u.Id, + Name: u.Name, + Role: u.Role, + Status: u.Status, + CreatedAt: u.CreatedAt, + } +} diff --git a/backend/internal/service/auth_guard.go b/backend/internal/service/auth_guard.go new file mode 100644 index 0000000..e7742eb --- /dev/null +++ b/backend/internal/service/auth_guard.go @@ -0,0 +1,152 @@ +package service + +import ( + "errors" + + "github.com/cyi-cc/fun" + "github.com/valyala/fasthttp" + + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// 认证统一业务错误码:前端响应拦截器据此清理登录态并跳登录页 +const authErrCode = 4011 + +type authPolicy uint8 + +const ( + policyDeny authPolicy = iota + policyPublic + policyAuthenticated + policyAdmin +) + +// endpointPolicies 显式端点策略表:缺省拒绝,新端点必须登记 +var endpointPolicies = map[string]authPolicy{ + "AuthSvc.Login": policyPublic, + "AuthSvc.Me": policyAuthenticated, + "AuthSvc.Logout": policyAuthenticated, + "AuthSvc.ChangePassword": policyAuthenticated, + "UserSvc.ListUsers": policyAdmin, + "UserSvc.SaveUser": policyAdmin, + "UserSvc.DeleteUser": policyAdmin, + "DashboardSvc.Stats": policyAuthenticated, + "ToolSvc.List": policyAuthenticated, + "ToolSvc.AdminList": policyAdmin, + "ToolSvc.SaveTool": policyAdmin, + "ToolSvc.DeleteTool": policyAdmin, + "VaultSvc.List": policyAuthenticated, + "VaultSvc.Save": policyAuthenticated, + "VaultSvc.Reveal": policyAuthenticated, + "VaultSvc.Totp": policyAuthenticated, + "VaultSvc.Delete": policyAuthenticated, + "BookmarkSvc.List": policyAuthenticated, + "BookmarkSvc.Save": policyAuthenticated, + "BookmarkSvc.Delete": policyAuthenticated, + "ServerSvc.List": policyAdmin, + "ServerSvc.Save": policyAdmin, + "ServerSvc.Delete": policyAdmin, + "ServerSvc.Metrics": policyAdmin, + "ServerSvc.SftpList": policyAdmin, + "ServerSvc.SftpMkdir": policyAdmin, + "ServerSvc.SftpRemove": policyAdmin, + "ServerSvc.SftpRename": policyAdmin, + "DbMgrSvc.List": policyAdmin, + "DbMgrSvc.Save": policyAdmin, + "DbMgrSvc.Delete": policyAdmin, + "DbMgrSvc.Test": policyAdmin, + "DbMgrSvc.Databases": policyAdmin, + "DbMgrSvc.Tables": policyAdmin, + "DbMgrSvc.Query": policyAdmin, + "WebSvc.Fetch": policyAuthenticated, + "DiskSvc.List": policyAuthenticated, + "DiskSvc.DeleteFile": policyAuthenticated, + "DiskSvc.CreateShare": policyAuthenticated, + "DiskSvc.ListShares": policyAuthenticated, + "DiskSvc.DeleteShare": policyAuthenticated, + "ProxySvc.List": policyAuthenticated, + "ProxySvc.Regions": policyAuthenticated, + "ProxySvc.Syncs": policyAdmin, + "ProxySvc.Stats": policyAuthenticated, + "ProxySvc.SyncNow": policyAdmin, + "ProxySvc.CheckNow": policyAdmin, + "ProxySvc.ListKeys": policyAdmin, + "ProxySvc.CreateKey": policyAdmin, + "ProxySvc.DeleteKey": policyAdmin, + "SettingSvc.Get": policyAuthenticated, + "SettingSvc.Set": policyAdmin, + "PaySvc.List": policyAdmin, + "PaySvc.Stats": policyAdmin, + "PaySvc.UpstreamStatus": policyAdmin, + "PaySvc.SaveUpstream": policyAdmin, + "PaySvc.TestPay": policyAdmin, + "PaySvc.CashierInfo": policyPublic, + "PaySvc.CashierStatus": policyPublic, + "PaySvc.Watch": policyPublic, + "LicenseSvc.Apps": policyAdmin, + "LicenseSvc.SaveApp": policyAdmin, + "LicenseSvc.DeleteApp": policyAdmin, + "LicenseSvc.List": policyAdmin, + "LicenseSvc.Stats": policyAdmin, + "LicenseSvc.Generate": policyAdmin, + "LicenseSvc.Delete": policyAdmin, + "MemorySvc.Projects": policyAdmin, + "MemorySvc.List": policyAdmin, + "MemorySvc.Get": policyAdmin, + "MemorySvc.Save": policyAdmin, + "MemorySvc.Delete": policyAdmin, + "MemorySvc.McpInfo": policyAdmin, + "MemorySvc.ResetToken": policyAdmin, +} + +// AuthGuard 唯一 RPC 鉴权边界:token 由前端请求拦截器写入 ctx.State["token"], +// SQLite 会话表校验有效性,命中后用户经 SetUserValue 传给服务层 +type AuthGuard struct { + Store *store.Store +} + +func (g *AuthGuard) Guard(ctx fun.Ctx) error { + policy := endpointPolicies[ctx.ServiceName+"."+ctx.MethodName] + switch policy { + case policyPublic: + return nil + case policyDeny: + return fun.Error(4003, "接口未登记鉴权策略") + } + if ctx.RequestCtx == nil { + return errors.New("auth guard: missing request context") + } + token := ctx.State["token"] + if token == "" { + return fun.Error(authErrCode, "请先登录") + } + su, err := g.Store.GetSessionUser(token, sessionTTL, sessionRenewAfter) + if errors.Is(err, store.ErrNotFound) { + return fun.Error(authErrCode, "登录已失效,请重新登录") + } + if err != nil { + return err + } + if su.User.Status != "active" { + return fun.Error(4003, "账号已被禁用") + } + if policy == policyAdmin && su.User.Role != "admin" { + return fun.Error(4003, "仅管理员可访问") + } + ctx.RequestCtx.SetUserValue(sessionKey{}, su) + return nil +} + +type sessionKey struct{} + +// sessionFrom 取 Guard 校验过的会话;策略表保证进入服务层时必然已设置 +func sessionFrom(ctx *fasthttp.RequestCtx) (store.SessionUser, error) { + if ctx == nil { + return store.SessionUser{}, errors.New("auth session: missing request context") + } + su, ok := ctx.UserValue(sessionKey{}).(store.SessionUser) + if !ok || su.User.Id == 0 { + return store.SessionUser{}, errors.New("auth session: validated session missing") + } + return su, nil +} diff --git a/backend/internal/service/bookmark.go b/backend/internal/service/bookmark.go new file mode 100644 index 0000000..cd42d29 --- /dev/null +++ b/backend/internal/service/bookmark.go @@ -0,0 +1,91 @@ +package service + +import ( + "errors" + "fmt" + "net/url" + "strings" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// BookmarkSvc 个人书签:列表驱动侧边栏快捷入口与书签页 +type BookmarkSvc struct { + fun.Ctx + Store *store.Store +} + +func (s *BookmarkSvc) List() ([]dto.BookmarkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return nil, err + } + items, err := s.Store.ListBookmarks(su.User.Id) + if err != nil { + return nil, fmt.Errorf("bookmark: list failed: %w", err) + } + views := make([]dto.BookmarkView, 0, len(items)) + for _, b := range items { + views = append(views, bookmarkView(b)) + } + return views, nil +} + +func (s *BookmarkSvc) Save(d dto.SaveBookmarkDto) (dto.BookmarkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.BookmarkView{}, err + } + title := strings.TrimSpace(d.Title) + raw := strings.TrimSpace(d.Url) + if title == "" || len(title) > 64 { + return dto.BookmarkView{}, fun.Error(4001, "名称必填且不超过 64 字符") + } + if raw != "" && !strings.Contains(raw, "://") { + raw = "https://" + raw // 裸域名自动补协议 + } + u, err := url.Parse(raw) + if err != nil || u.Scheme == "" || u.Host == "" || + (u.Scheme != "http" && u.Scheme != "https") { + return dto.BookmarkView{}, fun.Error(4001, "网址无效,需 http(s) 地址") + } + + b := store.Bookmark{UserID: su.User.Id, Title: title, Url: raw} + if d.Id != nil { + b.Id = *d.Id + } + if d.Icon != nil { + b.Icon = strings.TrimSpace(*d.Icon) + } + if d.Sort != nil { + b.Sort = *d.Sort + } + if err := s.Store.SaveBookmark(&b); err != nil { + if errors.Is(err, store.ErrNotFound) { + return dto.BookmarkView{}, fun.Error(4001, "书签不存在") + } + return dto.BookmarkView{}, fmt.Errorf("bookmark: save failed: %w", err) + } + return bookmarkView(b), nil +} + +func (s *BookmarkSvc) Delete(d dto.BookmarkIdDto) (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{}, err + } + if err := s.Store.DeleteBookmark(su.User.Id, d.Id); err != nil { + if errors.Is(err, store.ErrNotFound) { + return dto.OkView{}, fun.Error(4001, "书签不存在") + } + return dto.OkView{}, fmt.Errorf("bookmark: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +func bookmarkView(b store.Bookmark) dto.BookmarkView { + return dto.BookmarkView{Id: b.Id, Title: b.Title, Url: b.Url, Icon: b.Icon, Sort: b.Sort} +} diff --git a/backend/internal/service/dashboard.go b/backend/internal/service/dashboard.go new file mode 100644 index 0000000..4e59fe4 --- /dev/null +++ b/backend/internal/service/dashboard.go @@ -0,0 +1,114 @@ +package service + +import ( + "context" + "fmt" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/db" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +var bootTime = time.Now() + +type DashboardSvc struct { + fun.Ctx + Store *store.Store +} + +// Stats 仪表盘聚合:会话概况 + 各业务模块计数 + 收入趋势 + 最近动态 +func (s *DashboardSvc) Stats() (dto.StatsView, error) { + ctx := context.Background() + q := s.Store.Q() + now := time.Now() + view := dto.StatsView{UptimeSeconds: int64(time.Since(bootTime).Seconds())} + + // 会话概况 + var err error + if view.TotalUsers, err = s.Store.CountUsers(); err != nil { + return view, fmt.Errorf("dashboard: count users failed: %w", err) + } + if view.ActiveSessions, err = s.Store.CountActiveSessions(); err != nil { + return view, fmt.Errorf("dashboard: count sessions failed: %w", err) + } + if view.TodayLogins, err = s.Store.CountTodayLogins(); err != nil { + return view, fmt.Errorf("dashboard: count today logins failed: %w", err) + } + const days = 14 + trend, err := s.Store.LoginTrend(days) + if err != nil { + return view, fmt.Errorf("dashboard: login trend failed: %w", err) + } + view.LoginTrend = make([]dto.TrendPoint, 0, days) + for i := days - 1; i >= 0; i-- { + day := now.AddDate(0, 0, -int(i)).Format("2006-01-02") + view.LoginTrend = append(view.LoginTrend, dto.TrendPoint{Day: day, Count: trend[day]}) + } + + // 业务模块计数 + view.Servers, _ = s.Store.CountServers() + view.Proxies, _ = q.CountProxiesAll(ctx) + view.AliveProxies, _ = s.Store.CountAliveProxies() + view.LicenseApps, _ = q.CountLicenseApps(ctx) + view.Dbconns, _ = q.CountDbconns(ctx) + view.Files, _ = q.CountFilesAll(ctx) + view.Memories, _ = q.CountMemories(ctx) + if ps, err := s.Store.MemoryProjects(); err == nil { + view.MemoryProjects = int64(len(ps)) + } + if ls, err := q.LicenseCardStatsAll(ctx, now.Unix()); err == nil { + view.LicenseCards, view.LicenseActive = ls.Total, anyI64(ls.Active) + } + if su, err := sessionFrom(s.RequestCtx); err == nil { + view.Bookmarks, _ = q.CountBookmarksByUser(ctx, su.User.Id) + view.VaultItems, _ = q.CountVaultByUser(ctx, su.User.Id) + } + + // 支付:今日 + 累计 + 14 天收入趋势 + 最近成交 + dayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix() + if st, err := q.PayOrderStats(ctx, dayStart); err == nil { + view.TodayOrders, view.TodayMoney = st.Cnt, anyI64(st.Money) + } + if st, err := q.PayOrderStats(ctx, 0); err == nil { + view.TotalMoney, view.PendingOrders = anyI64(st.Money), anyI64(st.Pending) + } + daily := map[string]db.PayDailyPaidRow{} + if rows, err := q.PayDailyPaid(ctx, dayStart-int64((days-1)*86400)); err == nil { + for _, r := range rows { + daily[r.Day] = r + } + } + view.PayTrend = make([]dto.PayTrendPoint, 0, days) + for i := days - 1; i >= 0; i-- { + t := now.AddDate(0, 0, -int(i)) + p := dto.PayTrendPoint{Day: t.Format("2006-01-02")} + if r, ok := daily[p.Day]; ok { + p.Money, p.Count = anyI64(r.Money), r.Cnt + } + view.PayTrend = append(view.PayTrend, p) + } + if rows, err := q.RecentPaidOrders(ctx, 6); err == nil { + view.RecentOrders = make([]dto.RecentOrderView, 0, len(rows)) + for _, r := range rows { + subject := r.Subject + if subject == "" { + subject = r.TradeNo + } + view.RecentOrders = append(view.RecentOrders, dto.RecentOrderView{ + TradeNo: r.TradeNo, Subject: subject, Money: r.Money, PaidAt: r.PaidAt, + }) + } + } + if ms, err := s.Store.ListMemories("", "", 5); err == nil { + view.RecentMemories = make([]dto.RecentMemoryView, 0, len(ms)) + for _, m := range ms { + view.RecentMemories = append(view.RecentMemories, dto.RecentMemoryView{ + Key: m.Key, Project: m.Project, UpdatedAt: m.UpdatedAt, + }) + } + } + return view, nil +} diff --git a/backend/internal/service/dbm.go b/backend/internal/service/dbm.go new file mode 100644 index 0000000..df078ff --- /dev/null +++ b/backend/internal/service/dbm.go @@ -0,0 +1,242 @@ +package service + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/dbx" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" + "github.com/cyi-cc/cyi-box/backend/internal/vault" +) + +// DbMgrSvc 通用数据库管理器:连接密码 AES-GCM 落库,全部端点仅管理员; +// 查询固定 20s 超时 + 行数封顶,防止一条烂 SQL 拖死进程 +type DbMgrSvc struct { + fun.Ctx + Store *store.Store + Cfg *config.Config `fun:"auto"` +} + +func (s *DbMgrSvc) List() ([]dto.DbconnView, error) { + items, err := s.Store.ListDbconns() + if err != nil { + return nil, fmt.Errorf("dbm: list failed: %w", err) + } + views := make([]dto.DbconnView, 0, len(items)) + for _, e := range items { + views = append(views, dbconnView(e)) + } + return views, nil +} + +func (s *DbMgrSvc) Save(d dto.SaveDbconnDto) (dto.DbconnView, error) { + name := strings.TrimSpace(d.Name) + engine := strings.TrimSpace(d.Engine) + switch engine { + case "mysql", "postgres", "sqlite": + default: + return dto.DbconnView{}, fun.Error(4001, "引擎只支持 mysql / postgres / sqlite") + } + if name == "" || len(name) > 64 { + return dto.DbconnView{}, fun.Error(4001, "名称必填且不超过 64 字符") + } + + e := store.Dbconn{Name: name, Engine: engine} + if d.Id != nil { + old, err := s.Store.GetDbconn(*d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.DbconnView{}, fun.Error(4001, "连接不存在") + } + if err != nil { + return dto.DbconnView{}, err + } + e = old + e.Name, e.Engine = name, engine + } + if d.Host != nil { + e.Host = strings.TrimSpace(*d.Host) + } + if d.Port != nil { + e.Port = *d.Port + } + if d.Username != nil { + e.Username = strings.TrimSpace(*d.Username) + } + if d.Database != nil { + e.Database = strings.TrimSpace(*d.Database) + } + if d.Params != nil { + e.Params = strings.TrimSpace(*d.Params) + } + if d.Password != nil { + pw := *d.Password + if pw == "" { + e.PasswordEnc = "" + } else { + enc, err := vault.Encrypt(s.Cfg.VaultKey, pw) + if err != nil { + return dto.DbconnView{}, err + } + e.PasswordEnc = enc + } + } + if e.Engine != "sqlite" && e.Host == "" { + return dto.DbconnView{}, fun.Error(4001, "主机地址必填") + } + if e.Engine == "sqlite" && e.Database == "" { + return dto.DbconnView{}, fun.Error(4001, "sqlite 需在「数据库」填文件路径") + } + if err := s.Store.SaveDbconn(&e); err != nil { + return dto.DbconnView{}, fmt.Errorf("dbm: save failed: %w", err) + } + return dbconnView(e), nil +} + +func (s *DbMgrSvc) Delete(d dto.DbconnIdDto) (dto.OkView, error) { + if err := s.Store.DeleteDbconn(d.Id); err != nil { + if errors.Is(err, store.ErrNotFound) { + return dto.OkView{}, fun.Error(4001, "连接不存在") + } + return dto.OkView{}, fmt.Errorf("dbm: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +// openConn 解密码 + 建连 + ping +func openConn(st *store.Store, key []byte, id int64, database string) (*sql.DB, store.Dbconn, error) { + e, err := st.GetDbconn(id) + if errors.Is(err, store.ErrNotFound) { + return nil, e, fun.Error(4001, "连接不存在") + } + if err != nil { + return nil, e, err + } + pw := "" + if e.PasswordEnc != "" { + pw, err = vault.Decrypt(key, e.PasswordEnc) + if err != nil { + return nil, e, fmt.Errorf("dbm: decrypt password failed: %w", err) + } + } + d, err := dbx.Open(dbx.Conn{ + Engine: e.Engine, Host: e.Host, Port: e.Port, Username: e.Username, + Password: pw, Database: e.Database, Params: e.Params, + }, database) + return d, e, err +} + +func (s *DbMgrSvc) open(id int64, database string) (*sql.DB, store.Dbconn, error) { + return openConn(s.Store, s.Cfg.VaultKey, id, database) +} + +// Test 连通性测试 +func (s *DbMgrSvc) Test(d dto.DbconnIdDto) (dto.OkView, error) { + c, _, err := s.open(d.Id, "") + if err != nil { + return dto.OkView{}, fun.Error(4001, err.Error()) + } + defer c.Close() + return dto.OkView{Message: "连接成功"}, nil +} + +func (s *DbMgrSvc) Databases(d dto.DbScopeDto) (dto.DbDatabasesView, error) { + c, e, err := s.open(d.Id, "") + if err != nil { + return dto.DbDatabasesView{}, err + } + defer c.Close() + names, err := dbx.Databases(c, e.Engine) + if err != nil { + return dto.DbDatabasesView{}, fmt.Errorf("dbm: list databases failed: %w", err) + } + return dto.DbDatabasesView{Names: names}, nil +} + +func (s *DbMgrSvc) Tables(d dto.DbScopeDto) (dto.DbTablesView, error) { + db := "" + if d.Database != nil { + db = *d.Database + } + c, e, err := s.open(d.Id, db) + if err != nil { + return dto.DbTablesView{}, err + } + defer c.Close() + tables, cols, err := dbx.TablesAndColumns(c, e.Engine, db) + if err != nil { + return dto.DbTablesView{}, fmt.Errorf("dbm: list tables failed: %w", err) + } + v := dto.DbTablesView{Tables: []dto.DbTableView{}, Columns: []dto.DbColumnView{}} + for _, t := range tables { + v.Tables = append(v.Tables, dto.DbTableView{Name: t.Name, Type: t.Type}) + } + for _, cl := range cols { + v.Columns = append(v.Columns, dto.DbColumnView{ + Table: cl.Table, Name: cl.Name, Type: cl.Type, Pk: cl.Pk, + }) + } + return v, nil +} + +const dbQueryTimeout = 20 * time.Second + +// Query 执行单条 SQL:SELECT 回结果集(JSON 行串),其余回 affected rows +func (s *DbMgrSvc) Query(d dto.DbQueryDto) (dto.DbQueryView, error) { + sqlText := strings.TrimSpace(d.Sql) + if sqlText == "" { + return dto.DbQueryView{}, fun.Error(4001, "SQL 不能为空") + } + db := "" + if d.Database != nil { + db = *d.Database + } + c, _, err := s.open(d.Id, db) + if err != nil { + return dto.DbQueryView{}, err + } + defer c.Close() + + maxRows := int64(500) + if d.MaxRows != nil && *d.MaxRows > 0 { + maxRows = *d.MaxRows + } + ctx, cancel := context.WithTimeout(context.Background(), dbQueryTimeout) + defer cancel() + res, err := dbx.Query(ctx, c, sqlText, maxRows) + if err != nil { + return dto.DbQueryView{}, fun.Error(4002, "执行失败:"+err.Error()) + } + rowsJson, _ := json.Marshal(res.Rows) + return dto.DbQueryView{ + IsSelect: res.IsSelect, + Columns: res.Columns, + RowsJson: string(rowsJson), + RowCount: res.RowCount, + AffectedRows: res.AffectedRows, + Truncated: res.Truncated, + ElapsedMs: res.ElapsedMs, + }, nil +} + +func dbconnView(e store.Dbconn) dto.DbconnView { + return dto.DbconnView{ + Id: e.Id, + Name: e.Name, + Engine: e.Engine, + Host: e.Host, + Port: e.Port, + Username: e.Username, + Database: e.Database, + HasPassword: e.PasswordEnc != "", + CreatedAt: e.CreatedAt, + } +} diff --git a/backend/internal/service/dbm_routes.go b/backend/internal/service/dbm_routes.go new file mode 100644 index 0000000..3f55ea7 --- /dev/null +++ b/backend/internal/service/dbm_routes.go @@ -0,0 +1,394 @@ +package service + +import ( + "bytes" + "context" + "database/sql" + "encoding/csv" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "time" + + "github.com/cyi-cc/fun" + "github.com/xuri/excelize/v2" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/dbx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +const exportRowCap = 50000 + +// DbExportHandler POST /dbm/export 表单字段:id, db, sql, format(json|csv|xlsx) +func DbExportHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + if err := routeAdmin(st, ctx); err != nil { + ctx.SetBodyString(`{"status":2,"msg":"未授权"}`) + return nil + } + id, _ := strconv.ParseInt(string(ctx.QueryArgs().Peek("id")), 10, 64) + if id == 0 { + id, _ = strconv.ParseInt(rc.Data["id"], 10, 64) + } + dbName := rc.Data["db"] + sqlText := strings.TrimSpace(rc.Data["sql"]) + format := rc.Data["format"] + if format == "" { + format = "json" + } + if id == 0 || sqlText == "" { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"参数不完整"}`) + return nil + } + c, _, err := openConn(st, cfg.VaultKey, id, dbName) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString("连接失败:" + err.Error()) + return nil + } + defer c.Close() + + qctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + res, err := dbx.Query(qctx, c, sqlText, exportRowCap) + if err != nil { + ctx.SetStatusCode(500) + ctx.SetBodyString("执行失败:" + err.Error()) + return nil + } + if !res.IsSelect { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"非查询语句没有可导出的结果集"}`) + return nil + } + + var body []byte + var ext, mime string + switch format { + case "xlsx": + body, err = toXLSX(res) + ext, mime = "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + case "csv": + body, err = toCSV(res) + ext, mime = "csv", "text/csv; charset=utf-8" + default: + ext, mime = "json", "application/json" + body, err = toJSONRows(res) + } + if err != nil { + ctx.SetStatusCode(500) + ctx.SetBodyString("导出失败:" + err.Error()) + return nil + } + fname := fmt.Sprintf("export_%d.%s", time.Now().Unix(), ext) + ctx.Response.Header.Set("Content-Disposition", "attachment; filename=\""+fname+"\"") + ctx.SetContentType(mime) + ctx.SetBody(body) + return nil + } +} + +func toJSONRows(res *dbx.Result) ([]byte, error) { + list := make([]map[string]any, 0, len(res.Rows)) + for _, row := range res.Rows { + obj := map[string]any{} + for i, col := range res.Columns { + obj[col] = row[i] + } + list = append(list, obj) + } + return json.MarshalIndent(list, "", " ") +} + +func toCSV(res *dbx.Result) ([]byte, error) { + var buf bytes.Buffer + buf.Write([]byte{0xEF, 0xBB, 0xBF}) // BOM,Excel 打开不乱码 + w := csv.NewWriter(&buf) + if err := w.Write(res.Columns); err != nil { + return nil, err + } + for _, row := range res.Rows { + rec := make([]string, len(row)) + for i, v := range row { + rec[i] = cellText(v) + } + if err := w.Write(rec); err != nil { + return nil, err + } + } + w.Flush() + return buf.Bytes(), w.Error() +} + +func toXLSX(res *dbx.Result) ([]byte, error) { + f := excelize.NewFile() + defer f.Close() + sheet := "Sheet1" + for i, c := range res.Columns { + cell, _ := excelize.CoordinatesToCellName(i+1, 1) + if err := f.SetCellValue(sheet, cell, c); err != nil { + return nil, err + } + } + for ri, row := range res.Rows { + for ci, v := range row { + cell, _ := excelize.CoordinatesToCellName(ci+1, ri+2) + if v == nil { + continue + } + if err := f.SetCellValue(sheet, cell, v); err != nil { + return nil, err + } + } + } + buf, err := f.WriteToBuffer() + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func cellText(v any) string { + switch t := v.(type) { + case nil: + return "" + case string: + return t + case float64: + return strconv.FormatFloat(t, 'f', -1, 64) + case bool: + if t { + return "1" + } + return "0" + default: + return fmt.Sprint(t) + } +} + +// DbImportHandler POST /dbm/import multipart:file + 字段 id/db/table; +// 按扩展名解析 json(对象数组) / csv / xlsx(首行为表头),列名与表列交集后批量插入 +func DbImportHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + if err := routeAdmin(st, ctx); err != nil { + ctx.SetBodyString(`{"status":2,"msg":"未授权"}`) + return nil + } + id, _ := strconv.ParseInt(string(ctx.QueryArgs().Peek("id")), 10, 64) + dbName := string(ctx.QueryArgs().Peek("db")) + table := strings.TrimSpace(string(ctx.QueryArgs().Peek("table"))) + if id == 0 || table == "" { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"参数不完整"}`) + return nil + } + fh, err := ctx.FormFile("file") + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"缺少文件"}`) + return nil + } + src, err := fh.Open() + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"读取上传失败"}`) + return nil + } + defer src.Close() + raw, err := io.ReadAll(io.LimitReader(src, 64<<20)) + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"读取文件失败"}`) + return nil + } + rows, err := parseImport(strings.ToLower(fh.Filename), raw) + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"` + err.Error() + `"}`) + return nil + } + if len(rows) == 0 { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"文件里没有数据行"}`) + return nil + } + + c, e, err := openConn(st, cfg.VaultKey, id, dbName) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString(`{"status":2,"msg":"连接失败"}`) + return nil + } + defer c.Close() + + // 表名校验:必须在 introspection 列表里,顺便拿到列清单做交集 + tables, cols, err := dbx.TablesAndColumns(c, e.Engine, dbName) + if err != nil { + ctx.SetStatusCode(500) + ctx.SetBodyString(`{"status":2,"msg":"读表结构失败"}`) + return nil + } + validTable := false + tableCols := map[string]bool{} + for _, t := range tables { + if t.Name == table { + validTable = true + } + } + for _, cl := range cols { + if cl.Table == table { + tableCols[cl.Name] = true + } + } + if !validTable { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"表不存在"}`) + return nil + } + + // 列名 = 文件表头 ∩ 表列 + fileCols := orderedKeys(rows) + useCols := []string{} + for _, k := range fileCols { + if tableCols[k] { + useCols = append(useCols, k) + } + } + if len(useCols) == 0 { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"文件列名与表列没有交集"}`) + return nil + } + + inserted, skipped, err := bulkInsert(c, e.Engine, table, useCols, rows) + if err != nil { + ctx.SetStatusCode(500) + ctx.SetBodyString(`{"status":2,"msg":"导入失败:` + err.Error() + `"}`) + return nil + } + ctx.SetContentType("application/json") + fmt.Fprintf(ctx, `{"status":0,"inserted":%d,"skipped":%d}`, inserted, skipped) + return nil + } +} + +// bulkInsert 逐行插入(不开事务:PG 事务内单行失败会整批回滚, +// 逐行保证「能导多少导多少」,跳过坏行计数返回)。单次最多 1 万行。 +func bulkInsert(d *sql.DB, engine, table string, cols []string, rows []map[string]any) (int64, int64, error) { + if len(rows) > 10000 { + rows = rows[:10000] + } + quote := func(s string) string { return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` } + if engine == "mysql" { + quote = func(s string) string { return "`" + strings.ReplaceAll(s, "`", "``") + "`" } + } + quoted := make([]string, len(cols)) + placeholders := make([]string, len(cols)) + for i, c := range cols { + quoted[i] = quote(c) + if engine == "postgres" { + placeholders[i] = "$" + strconv.Itoa(i+1) + } else { + placeholders[i] = "?" + } + } + stmt := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", + quote(table), strings.Join(quoted, ","), strings.Join(placeholders, ",")) + ps, err := d.Prepare(stmt) + if err != nil { + return 0, 0, err + } + defer ps.Close() + var inserted, skipped int64 + for _, r := range rows { + vals := make([]any, len(cols)) + for i, c := range cols { + vals[i] = r[c] + } + if _, err := ps.Exec(vals...); err != nil { + skipped++ + continue + } + inserted++ + } + return inserted, skipped, nil +} + +// parseImport 按扩展名分派:.json 对象数组 / .xlsx 首行表头 / 其余按 CSV(兼容 BOM) +func parseImport(filename string, raw []byte) ([]map[string]any, error) { + switch { + case strings.HasSuffix(filename, ".json"): + var rows []map[string]any + if err := json.Unmarshal(raw, &rows); err != nil { + return nil, fmt.Errorf("JSON 解析失败:文件应为对象数组 [{...}, ...]") + } + return rows, nil + case strings.HasSuffix(filename, ".xlsx"): + f, err := excelize.OpenReader(bytes.NewReader(raw)) + if err != nil { + return nil, fmt.Errorf("Excel 解析失败") + } + defer f.Close() + sheets := f.GetSheetList() + if len(sheets) == 0 { + return nil, fmt.Errorf("Excel 文件没有工作表") + } + recs, err := f.GetRows(sheets[0]) + if err != nil { + return nil, fmt.Errorf("读取工作表失败") + } + return rowsFromTable(recs), nil + default: + r := csv.NewReader(bytes.NewReader(bytes.TrimPrefix(raw, []byte{0xEF, 0xBB, 0xBF}))) + r.FieldsPerRecord = -1 // 容忍长短不一的行 + recs, err := r.ReadAll() + if err != nil { + return nil, fmt.Errorf("CSV 解析失败") + } + return rowsFromTable(recs), nil + } +} + +// rowsFromTable 二维表(首行表头)→ 对象数组 +func rowsFromTable(recs [][]string) []map[string]any { + if len(recs) < 2 { + return nil + } + head := recs[0] + out := []map[string]any{} + for _, rec := range recs[1:] { + m := map[string]any{} + for i, h := range head { + h = strings.TrimSpace(h) + if h == "" { + continue + } + if i < len(rec) { + m[h] = rec[i] + } + } + out = append(out, m) + } + return out +} + +// orderedKeys 取文件列名(按首行出现顺序) +func orderedKeys(rows []map[string]any) []string { + seen := map[string]bool{} + out := []string{} + for _, r := range rows { + for k := range r { + if !seen[k] { + seen[k] = true + out = append(out, k) + } + } + } + return out +} diff --git a/backend/internal/service/disk.go b/backend/internal/service/disk.go new file mode 100644 index 0000000..8775124 --- /dev/null +++ b/backend/internal/service/disk.go @@ -0,0 +1,163 @@ +package service + +import ( + "crypto/rand" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +const shareCodeLen = 10 +const shareAlphabet = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789" + +// DiskSvc 网盘:文件列表/删除/分享链接管理(按用户隔离,属主校验) +type DiskSvc struct { + fun.Ctx + Store *store.Store + Cfg *config.Config +} + +func shareCode() (string, error) { + buf := make([]byte, shareCodeLen) + if _, err := rand.Read(buf); err != nil { + return "", err + } + for i := range buf { + buf[i] = shareAlphabet[int(buf[i])%len(shareAlphabet)] + } + return string(buf), nil +} + +func fileViews(files []store.File) []dto.FileView { + out := make([]dto.FileView, 0, len(files)) + for _, f := range files { + out = append(out, dto.FileView{ + Id: f.Id, Name: f.Name, Size: f.Size, Mime: f.Mime, CreatedAt: f.CreatedAt, + }) + } + return out +} + +func shareViews(shares []store.Share) []dto.ShareView { + now := time.Now().Unix() + out := make([]dto.ShareView, 0, len(shares)) + for _, s := range shares { + out = append(out, dto.ShareView{ + Id: s.Id, Code: s.Code, FileName: s.FileName, + ExpiresAt: s.ExpiresAt, Expired: s.ExpiresAt > 0 && now > s.ExpiresAt, + Downloads: s.Downloads, CreatedAt: s.CreatedAt, + }) + } + return out +} + +func (s *DiskSvc) List() ([]dto.FileView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return nil, err + } + files, err := s.Store.ListFiles(su.User.Id) + if err != nil { + return nil, fmt.Errorf("disk: list failed: %w", err) + } + return fileViews(files), nil +} + +// DeleteFile 删文件:库记录 + 磁盘文件 + 关联分享一起清 +func (s *DiskSvc) DeleteFile(d dto.IdDto) (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{}, err + } + f, err := s.Store.GetFile(d.Id) + if errors.Is(err, store.ErrNotFound) || f.UserID != su.User.Id { + return dto.OkView{}, fun.Error(4004, "文件不存在") + } + if err != nil { + return dto.OkView{}, err + } + if _, err := s.Store.DeleteFile(d.Id, su.User.Id); err != nil { + return dto.OkView{}, fmt.Errorf("disk: delete failed: %w", err) + } + _ = s.Store.DeleteSharesByFile(d.Id) + _ = os.Remove(filepath.Join(s.Cfg.DiskDir, f.Stored)) + return dto.OkView{Message: "已删除"}, nil +} + +// CreateShare ExpireMinutes=0 永久;>0 限时分享 +func (s *DiskSvc) CreateShare(d dto.CreateShareDto) (dto.ShareView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.ShareView{}, err + } + f, err := s.Store.GetFile(d.FileId) + if errors.Is(err, store.ErrNotFound) || f.UserID != su.User.Id { + return dto.ShareView{}, fun.Error(4004, "文件不存在") + } + if err != nil { + return dto.ShareView{}, err + } + if d.ExpireMinutes < 0 || d.ExpireMinutes > 60*24*365 { + return dto.ShareView{}, fun.Error(4001, "有效期不合法") + } + code, err := shareCode() + if err != nil { + return dto.ShareView{}, err + } + var expires int64 + if d.ExpireMinutes > 0 { + expires = time.Now().Add(time.Duration(d.ExpireMinutes) * time.Minute).Unix() + } + sh := store.Share{ + Code: code, FileID: f.Id, UserID: su.User.Id, + FileName: f.Name, ExpiresAt: expires, + } + if err := s.Store.InsertShare(&sh); err != nil { + return dto.ShareView{}, fmt.Errorf("disk: create share failed: %w", err) + } + return shareViews([]store.Share{sh})[0], nil +} + +func (s *DiskSvc) ListShares() ([]dto.ShareView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return nil, err + } + shares, err := s.Store.ListShares(su.User.Id) + if err != nil { + return nil, fmt.Errorf("disk: list shares failed: %w", err) + } + return shareViews(shares), nil +} + +func (s *DiskSvc) DeleteShare(d dto.IdDto) (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{}, err + } + n, err := s.Store.DeleteShare(d.Id, su.User.Id) + if err != nil { + return dto.OkView{}, fmt.Errorf("disk: delete share failed: %w", err) + } + if n == 0 { + return dto.OkView{}, fun.Error(4004, "分享不存在") + } + return dto.OkView{Message: "已删除"}, nil +} + +// safeStored 落盘文件名清洗:只留安全字符,防路径穿越 +func safeStored(name string) string { + name = strings.ReplaceAll(name, "/", "_") + name = strings.ReplaceAll(name, "\\", "_") + name = strings.ReplaceAll(name, "..", "_") + return name +} diff --git a/backend/internal/service/disk_routes.go b/backend/internal/service/disk_routes.go new file mode 100644 index 0000000..d36b609 --- /dev/null +++ b/backend/internal/service/disk_routes.go @@ -0,0 +1,167 @@ +package service + +import ( + "errors" + "fmt" + "io" + "mime" + "net/url" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/cyi-cc/fun" + "github.com/valyala/fasthttp" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// routeUser 自定义路由的登录用户校验(区别于 routeAdmin:任何登录用户可用) +func routeUser(st *store.Store, ctx *fasthttp.RequestCtx) (store.SessionUser, error) { + token := string(ctx.QueryArgs().Peek("token")) + if token == "" { + ctx.SetStatusCode(fasthttp.StatusUnauthorized) + return store.SessionUser{}, errors.New("missing token") + } + su, err := st.GetSessionUser(token, sessionTTL, sessionRenewAfter) + if err != nil || su.User.Status != "active" { + ctx.SetStatusCode(fasthttp.StatusUnauthorized) + return store.SessionUser{}, errors.New("invalid token") + } + return su, nil +} + +func routeJSONErr(ctx *fasthttp.RequestCtx, code int, msg string) { + ctx.SetStatusCode(code) + ctx.SetContentType("application/json") + ctx.SetBodyString(`{"status":2,"msg":"` + msg + `"}`) +} + +// DiskUploadHandler POST /disk/upload?token= multipart 字段 file +func DiskUploadHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + su, err := routeUser(st, ctx) + if err != nil { + routeJSONErr(ctx, 401, "未登录或登录已失效") + return nil + } + fh, err := ctx.FormFile("file") + if err != nil { + routeJSONErr(ctx, 400, "缺少文件") + return nil + } + src, err := fh.Open() + if err != nil { + routeJSONErr(ctx, 400, "读取上传失败") + return nil + } + defer src.Close() + + // 落盘名 = 时间戳+随机后缀(不沿用原名,天然免冲突与穿越) + stored := fmt.Sprintf("%d_%s", time.Now().UnixNano(), safeStored(filepath.Base(fh.Filename))) + dstPath := filepath.Join(cfg.DiskDir, stored) + dst, err := os.Create(dstPath) + if err != nil { + routeJSONErr(ctx, 500, "写入失败") + return nil + } + size, err := io.Copy(dst, src) + dst.Close() + if err != nil { + _ = os.Remove(dstPath) + routeJSONErr(ctx, 500, "保存失败") + return nil + } + + mimeType := fh.Header.Get("Content-Type") + f := store.File{ + UserID: su.User.Id, Name: filepath.Base(fh.Filename), + Stored: stored, Size: size, Mime: mimeType, + } + if err := st.InsertFile(&f); err != nil { + _ = os.Remove(dstPath) + routeJSONErr(ctx, 500, "记录失败") + return nil + } + ctx.SetContentType("application/json") + fmt.Fprintf(ctx, `{"status":0,"id":%d,"name":%q,"size":%d}`, f.Id, f.Name, f.Size) + return nil + } +} + +// serveDiskFile 共用:按原始文件名附件回传 +func serveDiskFile(ctx *fasthttp.RequestCtx, dir, stored, name, mimeType string, size int64) { + path := filepath.Join(dir, stored) + f, err := os.Open(path) + if err != nil { + ctx.SetStatusCode(404) + ctx.SetBodyString("文件已不存在") + return + } + defer f.Close() + // RFC 5987:中文文件名走 filename*,再补一份 filename 兜底 + disp := "attachment; filename*=UTF-8''" + url.PathEscape(name) + ctx.Response.Header.Set("Content-Disposition", disp) + if mimeType != "" { + ctx.SetContentType(mimeType) + } else if mt := mime.TypeByExtension(filepath.Ext(name)); mt != "" { + ctx.SetContentType(mt) + } else { + ctx.SetContentType("application/octet-stream") + } + ctx.Response.Header.Set("Content-Length", strconv.FormatInt(size, 10)) + _, _ = io.Copy(ctx, f) +} + +// DiskDownloadHandler GET /disk/download?id=&token= 属主下载 +func DiskDownloadHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + su, err := routeUser(st, ctx) + if err != nil { + routeJSONErr(ctx, 401, "未登录或登录已失效") + return nil + } + id, _ := strconv.ParseInt(string(ctx.QueryArgs().Peek("id")), 10, 64) + f, err := st.GetFile(id) + if err != nil || f.UserID != su.User.Id { + ctx.SetStatusCode(404) + ctx.SetBodyString("文件不存在") + return nil + } + serveDiskFile(ctx, cfg.DiskDir, f.Stored, f.Name, f.Mime, f.Size) + return nil + } +} + +// ShareDownloadHandler GET /d/* 公开分享下载:code 即 wildcard 路径,过期返回 410 +func ShareDownloadHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + code := strings.Trim(rc.Wildcard, "/") + if code == "" { + ctx.SetStatusCode(404) + ctx.SetBodyString("链接无效") + return nil + } + v, err := st.GetShareFile(code) + if err != nil { + ctx.SetStatusCode(404) + ctx.SetBodyString("分享链接不存在或已删除") + return nil + } + if v.ExpiresAt > 0 && time.Now().Unix() > v.ExpiresAt { + ctx.SetStatusCode(410) + ctx.SetContentType("text/html; charset=utf-8") + ctx.SetBodyString(`

链接已过期

这个分享已经过了有效期,找文件的主人再要一个新链接吧

`) + return nil + } + _ = st.IncShareDownloads(code) + serveDiskFile(ctx, cfg.DiskDir, v.Stored, v.FileName, v.Mime, v.Size) + return nil + } +} diff --git a/backend/internal/service/license.go b/backend/internal/service/license.go new file mode 100644 index 0000000..d6e7bab --- /dev/null +++ b/backend/internal/service/license.go @@ -0,0 +1,203 @@ +// license.go 授权系统管理:项目(appid/secret)+ 激活卡密(绑域名、按时长计时)。 +// 对外校验走 /v1/license,仅供其他项目客户端调用。 +package service + +import ( + "context" + "crypto/rand" + "database/sql" + "encoding/hex" + "fmt" + "strings" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/db" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// LicenseSvc 授权管理(仅管理员)。 +type LicenseSvc struct { + fun.Ctx + Store *store.Store +} + +const ( + licenseUnused = 0 // 未使用 + licenseActive = 1 // 已激活(有效期内) + licenseExpired = 2 // 已过期 +) + +// ---- 项目管理 ---- + +func (s *LicenseSvc) Apps() (dto.LicenseAppsView, error) { + rows, err := s.Store.Q().ListLicenseApps(context.Background()) + if err != nil { + return dto.LicenseAppsView{}, err + } + items := make([]dto.LicenseAppView, 0, len(rows)) + for _, r := range rows { + items = append(items, dto.LicenseAppView{ + Id: r.ID, Appid: r.Appid, Name: r.Name, + Enabled: r.Enabled, CardCount: r.CardCount, CreatedAt: r.CreatedAt, + }) + } + return dto.LicenseAppsView{Items: items}, nil +} + +// SaveApp id=0 新建(自动生成 appid+secret);否则改名/开关/重置 secret。 +func (s *LicenseSvc) SaveApp(d dto.LicenseAppSaveDto) (dto.LicenseAppView, error) { + ctx := context.Background() + if d.Id == 0 { + name := strings.TrimSpace(d.Name) + if name == "" || len(name) > 50 { + return dto.LicenseAppView{}, fun.Error(4001, "项目名必填且不超过 50 字") + } + appid := "app-" + randLowerHex(4) + id, err := s.Store.Q().CreateLicenseApp(ctx, db.CreateLicenseAppParams{ + Appid: appid, Secret: "", Name: name, CreatedAt: time.Now().Unix(), + }) + if err != nil { + return dto.LicenseAppView{}, fmt.Errorf("创建项目失败: %w", err) + } + return dto.LicenseAppView{Id: id, Appid: appid, Name: name, Enabled: 1}, nil + } + name := strings.TrimSpace(d.Name) + if name == "" || len(name) > 50 { + return dto.LicenseAppView{}, fun.Error(4001, "项目名必填且不超过 50 字") + } + enabled := int64(1) + if d.Enabled != nil { + enabled = *d.Enabled + } + if err := s.Store.Q().UpdateLicenseApp(ctx, db.UpdateLicenseAppParams{ + Name: name, Enabled: enabled, ID: d.Id, + }); err != nil { + return dto.LicenseAppView{}, err + } + return dto.LicenseAppView{Id: d.Id, Name: name, Enabled: enabled}, nil +} + +func (s *LicenseSvc) DeleteApp(d dto.LicenseIdDto) (dto.OkView, error) { + if err := s.Store.Q().DeleteLicenseApp(context.Background(), d.Id); err != nil { + return dto.OkView{}, err + } + return dto.OkView{Message: "已删除"}, nil +} + +// ---- 卡密管理 ---- + +func licenseStatus(c db.LicenseCard, now int64) int64 { + if c.ActivatedAt == 0 { + return licenseUnused + } + if c.ExpiresAt > 0 && c.ExpiresAt <= now { // expires_at=0 表示永久 + return licenseExpired + } + return licenseActive +} + +func licenseView(c db.LicenseCard, now int64) dto.LicenseCardView { + return dto.LicenseCardView{ + Id: c.ID, Card: c.Card, Hours: c.Hours, Domain: c.Domain, + Status: licenseStatus(c, now), + ActivatedAt: c.ActivatedAt, ExpiresAt: c.ExpiresAt, CreatedAt: c.CreatedAt, + } +} + +func (s *LicenseSvc) List(d dto.LicenseListDto) (dto.LicensePageView, error) { + kw := sql.NullString{} + if d.Kw != nil { + kw = sql.NullString{String: strings.TrimSpace(*d.Kw), Valid: true} + } + page := max(d.Page, 1) + size := min(max(d.PageSize, 1), 200) + ctx := context.Background() + total, err := s.Store.Q().CountLicenseCards(ctx, db.CountLicenseCardsParams{ + AppID: d.AppId, Column2: kw.String, Column3: kw, Column4: kw, + }) + if err != nil { + return dto.LicensePageView{}, err + } + rows, err := s.Store.Q().ListLicenseCards(ctx, db.ListLicenseCardsParams{ + AppID: d.AppId, Column2: kw.String, Column3: kw, Column4: kw, + Limit: size, Offset: (page - 1) * size, + }) + if err != nil { + return dto.LicensePageView{}, err + } + now := time.Now().Unix() + items := make([]dto.LicenseCardView, 0, len(rows)) + for _, r := range rows { + items = append(items, licenseView(r, now)) + } + return dto.LicensePageView{Total: total, Items: items}, nil +} + +func (s *LicenseSvc) Stats(d dto.LicenseStatsDto) (dto.LicenseStatsView, error) { + now := time.Now().Unix() + r, err := s.Store.Q().LicenseCardStats(context.Background(), db.LicenseCardStatsParams{ + ExpiresAt: now, ExpiresAt_2: now, AppID: d.AppId, + }) + if err != nil { + return dto.LicenseStatsView{}, err + } + return dto.LicenseStatsView{Total: r.Total, Unused: anyI64(r.Unused), Active: anyI64(r.Active), Expired: anyI64(r.Expired)}, nil +} + +// Generate 批量生成卡密:大写分组格式(XXXX-XXXX-XXXX-XXXX),一次最多 500。 +func (s *LicenseSvc) Generate(d dto.LicenseGenDto) (dto.LicenseGenView, error) { + if d.AppId <= 0 { + return dto.LicenseGenView{}, fun.Error(4001, "请先选择项目") + } + count := min(max(d.Count, 1), 500) + hours := min(max(d.Hours, 0), 24*3650) // 0 = 永久 + ctx := context.Background() + now := time.Now().Unix() + cards := make([]string, 0, count) + for i := int64(0); i < count; i++ { + card := genLicenseCard() + if err := s.Store.Q().CreateLicenseCard(ctx, db.CreateLicenseCardParams{ + AppID: d.AppId, Card: card, Hours: hours, CreatedAt: now, + }); err != nil { + return dto.LicenseGenView{}, fmt.Errorf("生成卡密失败(%d/%d): %w", i, count, err) + } + cards = append(cards, card) + } + return dto.LicenseGenView{Cards: cards}, nil +} + +func (s *LicenseSvc) Delete(d dto.LicenseIdDto) (dto.OkView, error) { + if err := s.Store.Q().DeleteLicenseCard(context.Background(), d.Id); err != nil { + return dto.OkView{}, err + } + return dto.OkView{Message: "已删除"}, nil +} + +// genLicenseCard XXXX-XXXX-XXXX-XXXX 大写卡密,排除易混淆字符(0/O、1/I/L)。 +func genLicenseCard() string { + const chars = "23456789ABCDEFGHJKMNPQRSTUVWXYZ" + var b [16]byte + if _, err := rand.Read(b[:]); err != nil { + panic(err) + } + var sb strings.Builder + for i, v := range b { + if i > 0 && i%4 == 0 { + sb.WriteByte('-') + } + sb.WriteByte(chars[int(v)%len(chars)]) + } + return sb.String() +} + +// randLowerHex n 字节随机小写 hex。 +func randLowerHex(n int) string { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + panic(err) + } + return hex.EncodeToString(b) +} diff --git a/backend/internal/service/license_routes.go b/backend/internal/service/license_routes.go new file mode 100644 index 0000000..c9727fb --- /dev/null +++ b/backend/internal/service/license_routes.go @@ -0,0 +1,82 @@ +// license_routes.go 卡密公开校验端点:GET/POST /v1/license?appid=xx&card=xx&domain=xx +// 首次调用激活并绑定域名(开始计时),之后仅限同域名校验;供其他项目做网络验证。 +package service + +import ( + "context" + "strings" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/db" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +func LicenseVerifyHandler(st *store.Store) fun.RouteHandler { + return func(c *fun.RouteCtx) error { + appid := strings.TrimSpace(c.Param("appid")) + card := strings.ToUpper(strings.TrimSpace(c.Param("card"))) + domain := normalizeDomain(c.Param("domain")) + if appid == "" || card == "" || domain == "" { + return licenseJSON(c, 0, "缺少参数:appid / card / domain", nil) + } + ctx := context.Background() + app, err := st.Q().GetLicenseAppByAppid(ctx, appid) + if err != nil || app.Enabled != 1 { + return licenseJSON(c, 0, "项目不存在或已停用", nil) + } + lc, err := st.Q().GetLicenseCard(ctx, db.GetLicenseCardParams{AppID: app.ID, Card: card}) + if err != nil { + return licenseJSON(c, 0, "卡密无效", nil) + } + now := time.Now().Unix() + if lc.ActivatedAt == 0 { + // 首激:绑域名 + 计时开始;hours=0 为永久卡(expires_at=0 永不过期) + expires := int64(0) + if lc.Hours > 0 { + expires = now + lc.Hours*3600 + } + if err := st.Q().ActivateLicenseCard(ctx, db.ActivateLicenseCardParams{ + Domain: domain, ActivatedAt: now, ExpiresAt: expires, ID: lc.ID, + }); err != nil { + return licenseJSON(c, 0, "激活失败", nil) + } + return licenseJSON(c, 1, "激活成功", map[string]any{ + "domain": domain, "hours": lc.Hours, "expires_at": expires, + }) + } + if lc.Domain != domain { + return licenseJSON(c, 0, "卡密已绑定其他域名", nil) + } + if lc.ExpiresAt > 0 && lc.ExpiresAt <= now { + return licenseJSON(c, 0, "卡密已过期", map[string]any{"expires_at": lc.ExpiresAt}) + } + return licenseJSON(c, 1, "验证通过", map[string]any{ + "domain": lc.Domain, "hours": lc.Hours, "expires_at": lc.ExpiresAt, + }) + } +} + +// normalizeDomain 取纯主机名:去协议/路径/端口/末尾点,小写。 +func normalizeDomain(raw string) string { + d := strings.ToLower(strings.TrimSpace(raw)) + if i := strings.Index(d, "://"); i >= 0 { + d = d[i+3:] + } + if i := strings.IndexAny(d, "/?#"); i >= 0 { + d = d[:i] + } + if i := strings.Index(d, ":"); i >= 0 { + d = d[:i] + } + return strings.TrimSuffix(d, ".") +} + +func licenseJSON(c *fun.RouteCtx, code int, msg string, extra map[string]any) error { + obj := map[string]any{"code": code, "msg": msg} + for k, v := range extra { + obj[k] = v + } + return epayJSON(c, obj) +} diff --git a/backend/internal/service/memory.go b/backend/internal/service/memory.go new file mode 100644 index 0000000..2b8acce --- /dev/null +++ b/backend/internal/service/memory.go @@ -0,0 +1,122 @@ +// memory.go 共享记忆库管理(仅管理员):项目分组、搜索、读写删除、MCP 接入信息。 +// 对外协议端点见 memory_routes.go(POST /mcp + GET /m/)。 +package service + +import ( + "strings" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/memx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// MemorySvc 记忆库管理(仅管理员)。 +type MemorySvc struct { + fun.Ctx + Store *store.Store +} + +func memoryMeta(m store.Memory) dto.MemoryMetaView { + return dto.MemoryMetaView{ + Key: m.Key, Project: m.Project, Tags: m.Tags, + WrittenBy: m.WrittenBy, Revision: m.Revision, UpdatedAt: m.UpdatedAt, + } +} + +func (s *MemorySvc) Projects() (dto.MemoryProjectsView, error) { + rows, err := s.Store.MemoryProjects() + if err != nil { + return dto.MemoryProjectsView{}, err + } + items := make([]dto.MemoryProjectView, 0, len(rows)) + for _, r := range rows { + items = append(items, dto.MemoryProjectView{Name: r.Name, Count: r.Count}) + } + return dto.MemoryProjectsView{Items: items}, nil +} + +// List query 非空走全文 AND 搜索;否则按 project/tag 过滤列元数据 +func (s *MemorySvc) List(d dto.MemoryListDto) (dto.MemoryListView, error) { + var ms []store.Memory + var err error + if q := strings.TrimSpace(d.Query); q != "" { + ms, err = s.Store.SearchMemories(q, strings.TrimSpace(d.Project), d.Limit) + } else { + ms, err = s.Store.ListMemories(strings.TrimSpace(d.Project), strings.TrimSpace(d.Tag), d.Limit) + } + if err != nil { + return dto.MemoryListView{}, err + } + items := make([]dto.MemoryMetaView, 0, len(ms)) + for _, m := range ms { + items = append(items, memoryMeta(m)) + } + return dto.MemoryListView{Items: items}, nil +} + +func (s *MemorySvc) Get(d dto.MemoryKeyDto) (dto.MemoryView, error) { + m, err := s.Store.GetMemory(strings.TrimSpace(d.Key)) + if err != nil { + return dto.MemoryView{}, err + } + if m == nil { + return dto.MemoryView{}, fun.Error(4004, "记忆不存在: "+d.Key) + } + readURL, _ := s.Store.SignMemoryReadURL(requestBase(s.Ctx.RequestCtx), m.Key) + return dto.MemoryView{ + Key: m.Key, Url: readURL, Content: m.Content, Project: m.Project, Tags: m.Tags, + WrittenBy: m.WrittenBy, Revision: m.Revision, + CreatedAt: m.CreatedAt, UpdatedAt: m.UpdatedAt, + }, nil +} + +func (s *MemorySvc) Save(d dto.MemorySaveDto) (dto.MemorySaveView, error) { + if err := memx.LintContent(d.Content); err != nil { + return dto.MemorySaveView{}, fun.Error(4002, err.Error()) + } + project := strings.TrimSpace(d.Project) + key := strings.TrimSpace(d.Key) + if key == "" { + return dto.MemorySaveView{}, fun.Error(4001, "key 必填,形如 project/topic") + } + m := &store.Memory{ + Key: key, Content: d.Content, Project: project, + Tags: d.Tags, WrittenBy: "admin", + } + if err := s.Store.SaveMemory(m); err != nil { + return dto.MemorySaveView{}, err + } + readURL, _ := s.Store.SignMemoryReadURL(requestBase(s.Ctx.RequestCtx), m.Key) + return dto.MemorySaveView{ + Key: m.Key, Revision: m.Revision, Url: readURL, + }, nil +} + +func (s *MemorySvc) Delete(d dto.MemoryKeyDto) (dto.OkView, error) { + if _, err := s.Store.DeleteMemory(strings.TrimSpace(d.Key)); err != nil { + return dto.OkView{}, err + } + return dto.OkView{Message: "已删除"}, nil +} + +// McpInfo 返回对外 MCP 端点与 Bearer 令牌(缺省懒生成一个) +func (s *MemorySvc) McpInfo() (dto.MemoryMcpView, error) { + tok, err := s.Store.McpToken() + if err != nil { + return dto.MemoryMcpView{}, err + } + return dto.MemoryMcpView{ + Url: requestBase(s.Ctx.RequestCtx) + "/mcp", + Token: tok, + }, nil +} + +func (s *MemorySvc) ResetToken() (dto.MemoryMcpView, error) { + if _, err := s.Store.ResetMcpToken(); err != nil { + return dto.MemoryMcpView{}, err + } + return s.McpInfo() +} + diff --git a/backend/internal/service/memory_routes.go b/backend/internal/service/memory_routes.go new file mode 100644 index 0000000..c685506 --- /dev/null +++ b/backend/internal/service/memory_routes.go @@ -0,0 +1,124 @@ +// memory_routes.go 记忆库对外协议端点: +// POST /mcp Streamable HTTP MCP(Bearer 令牌鉴权,令牌见 MemorySvc.McpInfo) +// GET /m 阅读链接索引(Bearer 鉴权——key 可枚举,不能裸奔) +// GET /m/* 记忆阅读链接(?sig= 签名鉴权,HMAC(key, 令牌) 签发,令牌重置即吊销) +package service + +import ( + "context" + "crypto/subtle" + "net/http" + + "github.com/valyala/fasthttp" + + "github.com/cyi-cc/fun" + "github.com/mark3labs/mcp-go/server" + "github.com/valyala/fasthttp/fasthttpadaptor" + + "github.com/cyi-cc/cyi-box/backend/internal/memx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// requestBase 按当前请求 Host + X-Forwarded-Proto 推导站点基址, +// 反代换域名后生成的 /m/ 链接与 MCP 接入地址始终可点。 +func requestBase(ctx *fasthttp.RequestCtx) string { + proto := string(ctx.Request.Header.Peek("X-Forwarded-Proto")) + if proto == "" { + proto = "http" + } + return proto + "://" + string(ctx.Host()) +} + +// McpHandler POST /mcp:Authorization Bearer 校验后交给 Streamable HTTP 服务。 +// 令牌存 settings(mcp.token),首次访问懒生成。 +func McpHandler(st *store.Store) fun.RouteHandler { + mcpSrv := server.NewStreamableHTTPServer( + memx.NewMCPServer(st), + server.WithStateLess(true), + server.WithHTTPContextFunc(func(ctx context.Context, r *http.Request) context.Context { + proto := r.Header.Get("X-Forwarded-Proto") + if proto == "" { + proto = "http" + if r.TLS != nil { + proto = "https" + } + } + return memx.WithBase(ctx, proto+"://"+r.Host) + }), + ) + h := fasthttpadaptor.NewFastHTTPHandler(mcpSrv) + return func(c *fun.RouteCtx) error { + tok, err := st.McpToken() + if err != nil { + c.RequestCtx.SetStatusCode(500) + c.RequestCtx.WriteString(err.Error()) + return nil + } + auth := string(c.RequestCtx.Request.Header.Peek("Authorization")) + if subtle.ConstantTimeCompare([]byte(auth), []byte("Bearer "+tok)) != 1 { + c.RequestCtx.SetStatusCode(401) + c.RequestCtx.WriteString("unauthorized") + return nil + } + h(c.RequestCtx) + return nil + } +} + +// MemoryReadHandler GET /m/*:输出 markdown 原文;裸 /m/ 列出全部阅读链接。 +func MemoryReadHandler(st *store.Store) fun.RouteHandler { + return func(c *fun.RouteCtx) error { + key := c.Wildcard + base := requestBase(c.RequestCtx) + if key == "" { + // 索引收进 Bearer 鉴权:key 形如 project/topic 可枚举,裸索引等于全库暴露 + tok, err := st.McpToken() + if err != nil { + c.RequestCtx.SetStatusCode(500) + c.RequestCtx.WriteString(err.Error()) + return nil + } + auth := string(c.RequestCtx.Request.Header.Peek("Authorization")) + if subtle.ConstantTimeCompare([]byte(auth), []byte("Bearer "+tok)) != 1 { + c.RequestCtx.SetStatusCode(401) + c.RequestCtx.WriteString("unauthorized") + return nil + } + ms, err := st.ListMemories("", "", 200) + if err != nil { + c.RequestCtx.SetStatusCode(500) + c.RequestCtx.WriteString(err.Error()) + return nil + } + c.RequestCtx.SetContentType("text/plain; charset=utf-8") + for _, m := range ms { + readURL, _ := st.SignMemoryReadURL(base, m.Key) + line := readURL + if m.Project != "" { + line += "\t[" + m.Project + "]" + } + c.RequestCtx.WriteString(line + "\n") + } + return nil + } + if !st.CheckMemoryReadSig(key, string(c.RequestCtx.QueryArgs().Peek("sig"))) { + c.RequestCtx.SetStatusCode(403) + c.RequestCtx.WriteString("bad or missing sig") + return nil + } + m, err := st.GetMemory(key) + if err != nil { + c.RequestCtx.SetStatusCode(500) + c.RequestCtx.WriteString(err.Error()) + return nil + } + if m == nil { + c.RequestCtx.SetStatusCode(404) + c.RequestCtx.WriteString("not found") + return nil + } + c.RequestCtx.SetContentType("text/markdown; charset=utf-8") + c.RequestCtx.WriteString(m.Content) + return nil + } +} diff --git a/backend/internal/service/pay.go b/backend/internal/service/pay.go new file mode 100644 index 0000000..8facf24 --- /dev/null +++ b/backend/internal/service/pay.go @@ -0,0 +1,867 @@ +// pay.go 支付核心:上游 catfk 账号托管、分类/商品自检、卡密库存保障(全程带锁)、 +// 订单 RPC、支付轮询与商户异步通知。易支付协议端点在 pay_routes.go。 +package service + +import ( + "context" + "crypto/md5" + "crypto/rand" + "database/sql" + "encoding/hex" + "fmt" + "io" + "log" + "net/http" + "net/url" + "sort" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/db" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/payx" + "github.com/cyi-cc/cyi-box/backend/internal/store" + "github.com/cyi-cc/cyi-box/backend/internal/vault" +) + +// ---- 常量与 settings 键 ---- + +const ( + payStatusPending = int64(0) + payStatusPaid = int64(1) + payStatusClosed = int64(3) + + payUnitFen = int64(1) // 上游商品单价 0.01 元 = 1 分 + payTargetFen = int64(100000) // 库存目标 1000 元 = 100000 分 = 10w 张卡 + payCatName = "CDK" + payGoodsName = "CDK0.01元" + payMaxFen = int64(200000) // 单笔上限 2000 元 +) + +const ( + keyPayPid = "pay_pid" + keyPayKey = "pay_key" + keyUpUsername = "up_username" + keyUpPassword = "up_password" // AES 加密存储 + keyUpNickname = "up_nickname" + keyUpShop = "up_shop" + keyUpGoodsID = "up_goods_id" + keyUpGoodsKey = "up_goods_key" + keyUpGoodsName = "up_goods_name" + keyUpLastErr = "up_last_error" +) + +// ---- 易支付签名与金额工具 ---- + +// epaySign 标准 MD5 签名:参数名 ASCII 升序拼 k=v&…(忽略 sign/sign_type 与空值),末尾直接拼接商户密钥。 +func epaySign(params map[string]string, key string) string { + keys := make([]string, 0, len(params)) + for k, v := range params { + if k == "sign" || k == "sign_type" || v == "" { + continue + } + keys = append(keys, k) + } + sort.Strings(keys) + var b strings.Builder + for _, k := range keys { + if b.Len() > 0 { + b.WriteByte('&') + } + b.WriteString(k) + b.WriteByte('=') + b.WriteString(params[k]) + } + b.WriteString(key) + sum := md5.Sum([]byte(b.String())) + return hex.EncodeToString(sum[:]) +} + +func epayVerify(params map[string]string, key, sign string) bool { + if params["sign_type"] != "MD5" { + return false + } + return sign != "" && epaySign(params, key) == sign +} + +// fenToYuan 分转元字符串,如 199 -> "1.99"。 +func fenToYuan(fen int64) string { + sign := "" + if fen < 0 { + sign, fen = "-", -fen + } + return fmt.Sprintf("%s%d.%02d", sign, fen/100, fen%100) +} + +// yuanToFen 元字符串转分(最多两位小数)。 +func yuanToFen(s string) (int64, error) { + s = strings.TrimSpace(s) + if s == "" { + return 0, fmt.Errorf("金额不能为空") + } + if strings.HasPrefix(s, "-") { + return 0, fmt.Errorf("金额不能为负") + } + parts := strings.SplitN(s, ".", 2) + whole, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64) + if err != nil { + return 0, fmt.Errorf("金额格式错误") + } + fen := whole * 100 + if len(parts) == 2 { + frac := parts[1] + if len(frac) > 2 { + frac = frac[:2] + } + frac += strings.Repeat("0", 2-len(frac)) + f, err := strconv.ParseInt(frac, 10, 64) + if err != nil { + return 0, fmt.Errorf("金额格式错误") + } + fen += f + } + return fen, nil +} + +// genTradeNo 平台订单号:P + 时间戳 + 16 位随机 hex(不可枚举)。 +func genTradeNo() string { + b := make([]byte, 8) + _, _ = rand.Read(b) + return "P" + strconv.FormatInt(time.Now().Unix(), 10) + hex.EncodeToString(b) +} + +// ---- 商户凭证 ---- + +func payMerchant(st *store.Store) (pid, key string, err error) { + if pid, err = st.GetSetting(keyPayPid); err != nil { + return "", "", err + } + if key, err = st.GetSetting(keyPayKey); err != nil { + return "", "", err + } + return pid, key, nil +} + +func strSetting(st *store.Store, key string) string { + v, _ := st.GetSetting(key) + return v +} + +func intSetting(st *store.Store, key string) int64 { + v, _ := strconv.ParseInt(strSetting(st, key), 10, 64) + return v +} + +func anyI64(v any) int64 { + switch t := v.(type) { + case int64: + return t + case float64: + return int64(t) + case []byte: + n, _ := strconv.ParseInt(string(t), 10, 64) + return n + case string: + n, _ := strconv.ParseInt(t, 10, 64) + return n + } + return 0 +} + +// ---- PaySvc 管理端 RPC ---- + +type PaySvc struct { + fun.Ctx + Store *store.Store + Up *payx.Client + Cfg *config.Config +} + +// List 订单分页。 +func (s *PaySvc) List(d dto.PayOrderListDto) (dto.PayOrderPageView, error) { + page, size := d.Page, d.PageSize + if page < 1 { + page = 1 + } + if size < 1 || size > 100 { + size = 20 + } + status := int64(-1) + if d.Status != nil { + status = *d.Status + } + kw := sql.NullString{} + if d.Kw != nil { + kw = sql.NullString{String: strings.TrimSpace(*d.Kw), Valid: true} + } + ctx := context.Background() + arg := db.ListPayOrdersParams{ + Column1: status, Status: status, + Column3: kw.String, Column4: kw, Column5: kw, + Limit: size, Offset: (page - 1) * size, + } + rows, err := s.Store.Q().ListPayOrders(ctx, arg) + if err != nil { + return dto.PayOrderPageView{}, fun.Error(5000, "查询订单失败") + } + total, err := s.Store.Q().CountPayOrders(ctx, db.CountPayOrdersParams{ + Column1: arg.Column1, Status: arg.Status, + Column3: arg.Column3, Column4: arg.Column4, Column5: arg.Column5, + }) + if err != nil { + return dto.PayOrderPageView{}, fun.Error(5000, "统计订单失败") + } + items := make([]dto.PayOrderView, 0, len(rows)) + for _, r := range rows { + items = append(items, toPayOrderView(r)) + } + return dto.PayOrderPageView{Total: total, Items: items}, nil +} + +// Stats 今日与累计统计。 +func (s *PaySvc) Stats() (dto.PayStatsView, error) { + now := time.Now() + dayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix() + ctx := context.Background() + today, err := s.Store.Q().PayOrderStats(ctx, dayStart) + if err != nil { + return dto.PayStatsView{}, fun.Error(5000, "统计失败") + } + total, err := s.Store.Q().PayOrderStats(ctx, 0) + if err != nil { + return dto.PayStatsView{}, fun.Error(5000, "统计失败") + } + return dto.PayStatsView{ + TodayCount: today.Cnt, TodayMoney: anyI64(today.Money), TodayPaidCount: anyI64(today.Paid), + TotalCount: total.Cnt, TotalMoney: anyI64(total.Money), PendingCount: anyI64(total.Pending), + TotalPaidCount: anyI64(total.Paid), + }, nil +} + +// UpstreamStatus 上游账号与商品状态(管理页展示)。 +func (s *PaySvc) UpstreamStatus() (dto.PayUpstreamStatusView, error) { + v := dto.PayUpstreamStatusView{ + Username: strSetting(s.Store, keyUpUsername), + Nickname: strSetting(s.Store, keyUpNickname), + Shop: strSetting(s.Store, keyUpShop), + GoodsName: strSetting(s.Store, keyUpGoodsName), + LastError: strSetting(s.Store, keyUpLastErr), + TokenAge: s.Up.TokenAge(), + GoodsId: intSetting(s.Store, keyUpGoodsID), + GoodsKey: strSetting(s.Store, keyUpGoodsKey), + UnitPrice: payUnitFen, + TargetMoney: payTargetFen, + } + if payEnsuring.Load() { + v.Ensuring = 1 + } + if v.Username != "" { + v.Configured = 1 + } + if v.GoodsId > 0 && v.Configured == 1 { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + if _, _, stock, err := s.Up.GoodsInfo(ctx, v.GoodsId); err == nil { + v.StockCount = stock + } + if avail, frozen, err := s.Up.WalletInfo(ctx); err == nil { + v.WalletOk, v.WalletAvail, v.WalletFrozen = 1, avail, frozen + } + } + return v, nil +} + +// SaveUpstream 保存上游账号并登录验证;成功后异步触发分类/商品自检与库存补齐。 +func (s *PaySvc) SaveUpstream(d dto.PayUpstreamDto) (dto.PayUpstreamStatusView, error) { + username := strings.TrimSpace(d.Username) + if username == "" { + return dto.PayUpstreamStatusView{}, fun.Error(4001, "上游账号不能为空") + } + password := d.Password + if password == "" { + u, p, err := s.decryptUpPassword() + if err != nil || u != username || p == "" { + return dto.PayUpstreamStatusView{}, fun.Error(4001, "请输入上游密码") + } + password = p + } + if err := s.Up.SetCredentials(username, password); err != nil { + return dto.PayUpstreamStatusView{}, fun.Error(4002, "上游登录失败:"+err.Error()) + } + nickname, shop, err := s.Up.Userinfo(context.Background()) + if err != nil { + return dto.PayUpstreamStatusView{}, fun.Error(5000, "获取商户信息失败:"+err.Error()) + } + enc, err := vault.Encrypt(s.Cfg.VaultKey, password) + if err != nil { + return dto.PayUpstreamStatusView{}, fun.Error(5000, "凭据加密失败") + } + old := strSetting(s.Store, keyUpUsername) + sets := map[string]string{ + keyUpUsername: username, keyUpPassword: enc, + keyUpNickname: nickname, keyUpShop: shop, + } + if old != "" && old != username { + sets[keyUpGoodsID], sets[keyUpGoodsKey], sets[keyUpGoodsName] = "", "", "" + } + for k, v := range sets { + if err := s.Store.SetSetting(k, v); err != nil { + return dto.PayUpstreamStatusView{}, fun.Error(5000, "保存配置失败") + } + } + ensureGoodsAsync(s.Store, s.Up) + return s.UpstreamStatus() +} + +// TestPay 创建一笔真实订单走完整上游流程(收银台二维码 + 轮询结算)。 +func (s *PaySvc) TestPay(d dto.PayTestPayDto) (dto.PayTestPayView, error) { + money, err := yuanToFen(d.Money) + if err != nil || money <= 0 || money > payMaxFen { + return dto.PayTestPayView{}, fun.Error(4001, "金额非法(0-2000 元)") + } + order, err := createPayOrder(s.Store, s.Up, createPayOrderIn{ + Channel: "alipay", Subject: "测试支付", Money: money, + }) + if err != nil { + return dto.PayTestPayView{}, fun.Error(5000, err.Error()) + } + return dto.PayTestPayView{ + TradeNo: order.TradeNo, Payurl: order.Payurl, Qrcode: order.Qrcode, + Money: order.Money, Cashier: "/cashier/" + order.TradeNo, + }, nil +} + +// CashierInfo 收银台公开信息(trade_no 不可枚举即凭证)。 +func (s *PaySvc) CashierInfo(d dto.PayCashierDto) (dto.PayCashierView, error) { + o, err := s.Store.Q().GetPayOrderByTradeNo(context.Background(), strings.TrimSpace(d.TradeNo)) + if err != nil { + return dto.PayCashierView{}, fun.Error(4004, "订单不存在") + } + lazyCloseExpired(s.Store, &o) + return dto.PayCashierView{ + TradeNo: o.TradeNo, OutTradeNo: o.OutTradeNo, Subject: o.Subject, + Money: o.Money, Channel: o.Channel, Status: o.Status, + Payurl: o.Payurl, Qrcode: o.Qrcode, Cards: o.Cards, + CreatedAt: o.CreatedAt, ExpiredAt: o.ExpiredAt, + }, nil +} + +// Watch 收银台状态流:服务端推送订单状态变化,终态(已支付/已关闭)自动关闭。 +func (s *PaySvc) Watch(d dto.PayCashierDto) (*fun.Stream[dto.PayCashierStatusView], error) { + tradeNo := strings.TrimSpace(d.TradeNo) + o, err := s.Store.Q().GetPayOrderByTradeNo(context.Background(), tradeNo) + if err != nil { + return nil, fun.Error(4004, "订单不存在") + } + st := &fun.Stream[dto.PayCashierStatusView]{} + go s.watchLoop(st, tradeNo, o) + return st, nil +} + +func (s *PaySvc) watchLoop(st *fun.Stream[dto.PayCashierStatusView], tradeNo string, first db.PayOrder) { + defer st.Close() + last := first.Status + push := func(status int64, o db.PayOrder) bool { + msg := dto.PayCashierStatusView{Status: status} + if status == payStatusPaid && o.ReturnUrl != "" { + if pid, key, err := payMerchant(s.Store); err == nil { + msg.ReturnUrl = buildSignedURL(o.ReturnUrl, notifyParams(o, pid), key) + } + } + return st.Send(msg) == nil + } + if !push(last, first) || last != payStatusPending { + return + } + deadline := time.Now().Add(40 * time.Minute) + t := time.NewTicker(2 * time.Second) + defer t.Stop() + for time.Now().Before(deadline) { + <-t.C + o, err := s.Store.Q().GetPayOrderByTradeNo(context.Background(), tradeNo) + if err != nil { + return + } + lazyCloseExpired(s.Store, &o) + if o.Status != last { + if !push(o.Status, o) { + return + } + last = o.Status + if o.Status != payStatusPending { + return + } + } + } +} + +// CashierStatus 收银台轮询:已支付且配置了 return_url 时返回签名回跳地址。 +func (s *PaySvc) CashierStatus(d dto.PayCashierDto) (dto.PayCashierStatusView, error) { + o, err := s.Store.Q().GetPayOrderByTradeNo(context.Background(), strings.TrimSpace(d.TradeNo)) + if err != nil { + return dto.PayCashierStatusView{}, fun.Error(4004, "订单不存在") + } + res := dto.PayCashierStatusView{Status: o.Status} + if o.Status == payStatusPaid && o.ReturnUrl != "" { + pid, key, err := payMerchant(s.Store) + if err == nil { + res.ReturnUrl = buildSignedURL(o.ReturnUrl, notifyParams(o, pid), key) + } + } + return res, nil +} + +func toPayOrderView(r db.PayOrder) dto.PayOrderView { + return dto.PayOrderView{ + Id: r.ID, TradeNo: r.TradeNo, OutTradeNo: r.OutTradeNo, Channel: r.Channel, + Subject: r.Subject, Money: r.Money, Status: r.Status, Notified: r.Notified, + Payurl: r.Payurl, PaidAt: r.PaidAt, ExpiredAt: r.ExpiredAt, CreatedAt: r.CreatedAt, + } +} + +func lazyCloseExpired(st *store.Store, o *db.PayOrder) { + if o.Status == payStatusPending && o.ExpiredAt > 0 && o.ExpiredAt < time.Now().Unix() { + _ = st.Q().SetPayOrderStatus(context.Background(), db.SetPayOrderStatusParams{ + Status: payStatusClosed, PaidAt: o.PaidAt, Cards: o.Cards, ID: o.ID, + }) + o.Status = payStatusClosed + } +} + +func (s *PaySvc) decryptUpPassword() (string, string, error) { + u := strSetting(s.Store, keyUpUsername) + enc := strSetting(s.Store, keyUpPassword) + if u == "" || enc == "" { + return "", "", fmt.Errorf("未保存上游凭据") + } + p, err := vault.Decrypt(s.Cfg.VaultKey, enc) + return u, p, err +} + +// ---- 商品自检 + 库存保障 ---- + +var payEnsuring atomic.Bool + +// ensureGoodsAsync 后台幂等确保分类「CDK」与商品「CDK0.01元」存在并补齐库存。 +func ensureGoodsAsync(st *store.Store, up *payx.Client) { + if !payEnsuring.CompareAndSwap(false, true) { + return + } + go func() { + defer payEnsuring.Store(false) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer cancel() + if err := ensureGoods(ctx, st, up); err != nil { + _ = st.SetSetting(keyUpLastErr, err.Error()) + log.Printf("[pay] 上游商品自检失败: %v", err) + return + } + _ = st.SetSetting(keyUpLastErr, "") + }() +} + +// ensureGoods 幂等自检:分类 CDK → 商品 CDK0.01元(¥0.01)→ 库存补到 1000 元(10w 张,1w/批)。 +func ensureGoods(ctx context.Context, st *store.Store, up *payx.Client) error { + goodsID := intSetting(st, keyUpGoodsID) + goodsKey := strSetting(st, keyUpGoodsKey) + target := payTargetFen / payUnitFen // 10w 张 + if goodsID > 0 && goodsKey != "" { + return ensureStockTo(ctx, up, goodsID, target) + } + list, total, err := up.GoodsList(ctx, "", 1) + if err != nil { + return err + } + for page := int64(1); ; page++ { + for _, it := range list { + if it.Name == payGoodsName && it.Price == payUnitFen && it.Status == 1 { + goodsID, goodsKey = it.ID, it.GoodsKey + } + } + if goodsID != 0 || page*20 >= total { + break + } + list, _, err = up.GoodsList(ctx, "", page+1) + if err != nil { + return err + } + } + if goodsID == 0 { + var catID int64 + cats, err := up.CategoryListAll(ctx) + if err != nil { + return err + } + for _, cat := range cats { + if cat.Name == payCatName { + catID = cat.ID + break + } + } + if catID == 0 { + if err := up.CategoryAdd(ctx, payCatName); err != nil { + return err + } + cats, err = up.CategoryListAll(ctx) + if err != nil { + return err + } + for _, cat := range cats { + if cat.Name == payCatName { + catID = cat.ID + break + } + } + } + if catID == 0 { + return fmt.Errorf("上游分类「%s」创建失败", payCatName) + } + if goodsID, goodsKey, err = up.GoodsAdd(ctx, payGoodsName, catID, 0.01); err != nil { + return err + } + if goodsID <= 0 || goodsKey == "" { + return fmt.Errorf("上游商品创建未返回有效标识") + } + log.Printf("[pay] 已创建上游商品「%s」id=%d", payGoodsName, goodsID) + } + for k, v := range map[string]string{ + keyUpGoodsID: strconv.FormatInt(goodsID, 10), + keyUpGoodsKey: goodsKey, + keyUpGoodsName: payGoodsName, + } { + if err := st.SetSetting(k, v); err != nil { + return err + } + } + return ensureStockTo(ctx, up, goodsID, target) +} + +// ensureStockTo 全程带锁把库存补到 target 张。 +func ensureStockTo(ctx context.Context, up *payx.Client, goodsID, target int64) error { + unlock := up.StockLock() + defer unlock() + _, err := up.EnsureStock(ctx, goodsID, target) + return err +} + +// ---- 下单 ---- + +type createPayOrderIn struct { + OutTradeNo string + Channel string + Subject string + Money int64 // 分 + NotifyURL string + ReturnURL string + ClientIP string +} + +// createPayOrder 本地建单 → 库存保障(带锁)→ 上游下单 → 写回 payurl/二维码。 +// 幂等:同 out_trade_no 复用未支付订单。 +func createPayOrder(st *store.Store, up *payx.Client, in createPayOrderIn) (db.PayOrder, error) { + now := time.Now().Unix() + tradeNo := genTradeNo() + outNo := in.OutTradeNo + if outNo == "" { + outNo = tradeNo // 内部测试单:商户单号=平台单号 + } + if exist, err := st.Q().GetPayOrderByOutNo(context.Background(), outNo); err == nil { + switch { + case exist.Money != in.Money: + return db.PayOrder{}, fmt.Errorf("商户订单号重复且金额不一致") + case exist.Status == payStatusPaid: + return db.PayOrder{}, fmt.Errorf("该订单已支付") + case exist.Status != payStatusPending || (exist.ExpiredAt > 0 && exist.ExpiredAt < now): + return db.PayOrder{}, fmt.Errorf("订单已关闭,请更换订单号") + default: + return exist, nil + } + } + goodsID := intSetting(st, keyUpGoodsID) + goodsKey := strSetting(st, keyUpGoodsKey) + if goodsID <= 0 || goodsKey == "" { + return db.PayOrder{}, fmt.Errorf("上游商品未就绪:请先在支付配置页保存上游账号") + } + if in.Money%payUnitFen != 0 || in.Money/payUnitFen <= 0 { + return db.PayOrder{}, fmt.Errorf("金额必须是 ¥0.01 的整数倍") + } + quantity := in.Money / payUnitFen + id, err := st.Q().CreatePayOrder(context.Background(), db.CreatePayOrderParams{ + TradeNo: tradeNo, OutTradeNo: outNo, Channel: in.Channel, + Subject: in.Subject, Money: in.Money, Fee: 0, + NotifyUrl: in.NotifyURL, ReturnUrl: in.ReturnURL, ClientIp: in.ClientIP, + ExpiredAt: now + 30*60, CreatedAt: now, + }) + if err != nil { + return db.PayOrder{}, fmt.Errorf("创建订单失败") + } + o, err := st.Q().GetPayOrder(context.Background(), id) + if err != nil { + return db.PayOrder{}, fmt.Errorf("创建订单失败") + } + if err := placeUpstream(st, up, &o, goodsID, goodsKey, quantity); err != nil { + if o.UpstreamTradeNo == "" { + _ = st.Q().SetPayOrderStatus(context.Background(), db.SetPayOrderStatusParams{ + Status: payStatusClosed, PaidAt: 0, Cards: "", ID: o.ID, + }) + } + return db.PayOrder{}, err + } + return o, nil +} + +// placeUpstream 库存保障(全程带锁)→ 上游下单 → 回写。 +func placeUpstream(st *store.Store, up *payx.Client, o *db.PayOrder, goodsID int64, goodsKey string, quantity int64) error { + ctx := context.Background() + shop := strSetting(st, keyUpShop) + if shop == "" { + return fmt.Errorf("上游店铺未配置") + } + channelID, err := up.AlipayChannelID(ctx, shop) + if err != nil { + return fmt.Errorf("上游支付通道不可用:%v", err) + } + contact, queryPwd := payx.RandContact(), payx.RandQueryPwd() + + unlock := up.StockLock() + res, err := placeUnderLock(ctx, up, goodsID, goodsKey, quantity, channelID, contact, queryPwd) + unlock() + if err != nil { + return err + } + o.Payurl = res.PayURL + o.UpstreamTradeNo = res.TradeNo + if res.TotalAmount != o.Money { + log.Printf("[pay] 订单 %s 上游金额 %s 与本地 %s 不一致", o.TradeNo, fenToYuan(res.TotalAmount), fenToYuan(o.Money)) + } + if err := st.Q().SetPayOrderUpstream(ctx, db.SetPayOrderUpstreamParams{ + UpstreamTradeNo: res.TradeNo, GoodsKey: goodsKey, Quantity: quantity, + UnitPrice: payUnitFen, Payurl: res.PayURL, BuyerContact: contact, + QueryPwd: queryPwd, ID: o.ID, + }); err != nil { + return fmt.Errorf("回写上游订单失败") + } + if qr, qrErr := up.FetchQRCode(ctx, res.TradeNo); qrErr == nil && qr != "" { + _ = st.Q().SetPayOrderQrcode(ctx, db.SetPayOrderQrcodeParams{Qrcode: qr, ID: o.ID}) + o.Qrcode = qr + } else if qrErr != nil { + log.Printf("[pay] 订单 %s 取二维码失败: %v(payurl 仍可用)", o.TradeNo, qrErr) + } + return nil +} + +// placeUnderLock 持锁执行:库存不足先补齐(补到 max(10w, 当前+需求))再下单。 +func placeUnderLock(ctx context.Context, up *payx.Client, goodsID int64, goodsKey string, quantity, channelID int64, contact, queryPwd string) (payx.PayOrderResult, error) { + target := payTargetFen / payUnitFen + _, _, stock, err := up.GoodsInfo(ctx, goodsID) + if err != nil { + return payx.PayOrderResult{}, fmt.Errorf("上游库存查询失败:%v", err) + } + if stock < quantity { + goal := target + if stock+quantity > goal { + goal = stock + quantity + } + log.Printf("[pay] 库存 %d 不足订单需求 %d,补到 %d", stock, quantity, goal) + if _, err := up.EnsureStock(ctx, goodsID, goal); err != nil { + return payx.PayOrderResult{}, fmt.Errorf("上游库存补充失败:%v", err) + } + } + res, err := up.CreateOrder(ctx, goodsKey, quantity, channelID, contact, queryPwd) + if err != nil && strings.Contains(err.Error(), "库存") { + if _, serr := up.EnsureStock(ctx, goodsID, target+quantity); serr != nil { + return payx.PayOrderResult{}, fmt.Errorf("上游库存补充失败:%v", serr) + } + res, err = up.CreateOrder(ctx, goodsKey, quantity, channelID, contact, queryPwd) + } + if err != nil { + return payx.PayOrderResult{}, fmt.Errorf("上游下单失败:%v", err) + } + return res, nil +} + +// ---- 支付轮询 ---- + +// PayPoller 待支付订单轮询器(fun.Wired 单例,5s 一扫)。 +type PayPoller struct { + Store *store.Store `fun:"auto"` + Up *payx.Client `fun:"auto"` + Notify *PayNotifier `fun:"auto"` +} + +func (p *PayPoller) New() { + go p.loop() +} + +func (p *PayPoller) loop() { + t := time.NewTicker(5 * time.Second) + defer t.Stop() + for range t.C { + p.sweep() + } +} + +func (p *PayPoller) sweep() { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + rows, err := p.Store.Q().ListPendingPayOrders(ctx) + if err != nil { + return + } + now := time.Now().Unix() + for _, o := range rows { + if o.ExpiredAt > 0 && o.ExpiredAt < now { + _ = p.Store.Q().SetPayOrderStatus(ctx, db.SetPayOrderStatusParams{ + Status: payStatusClosed, PaidAt: 0, Cards: "", ID: o.ID, + }) + continue + } + paid, err := p.Up.OrderPaid(ctx, o.UpstreamTradeNo) + if err != nil || !paid { + continue + } + cards, _ := p.Up.OrderCards(ctx, o.UpstreamTradeNo) + _ = p.Store.Q().SetPayOrderStatus(ctx, db.SetPayOrderStatusParams{ + Status: payStatusPaid, PaidAt: now, Cards: strings.Join(cards, "\n"), ID: o.ID, + }) + log.Printf("[pay] 订单 %s 已支付", o.TradeNo) + p.Notify.OnPaid(o.TradeNo) + // 库存被消耗:异步补回目标水位 + if g := intSetting(p.Store, keyUpGoodsID); g > 0 { + ensureStockAsync(p.Store, p.Up, g, payTargetFen/payUnitFen) + } + } +} + +var stockFilling atomic.Bool + +func ensureStockAsync(st *store.Store, up *payx.Client, goodsID, target int64) { + if !stockFilling.CompareAndSwap(false, true) { + return + } + go func() { + defer stockFilling.Store(false) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer cancel() + if err := ensureStockTo(ctx, up, goodsID, target); err != nil { + log.Printf("[pay] 库存回充失败: %v", err) + } + }() +} + +// ---- 商户异步通知 ---- + +// PayNotifier 商户异步通知器(fun.Wired 单例)。 +type PayNotifier struct { + Store *store.Store `fun:"auto"` +} + +var notifyBackoff = []time.Duration{0, 15 * time.Second, time.Minute, 5 * time.Minute, 15 * time.Minute} + +// notifyParams 通知/回跳参数(未签名)。 +func notifyParams(o db.PayOrder, pid string) map[string]string { + return map[string]string{ + "pid": pid, + "trade_no": o.TradeNo, + "out_trade_no": o.OutTradeNo, + "type": o.Channel, + "name": o.Subject, + "money": fenToYuan(o.Money), + "trade_status": "TRADE_SUCCESS", + } +} + +// buildSignedURL 签名参数拼接到回调地址。 +func buildSignedURL(base string, params map[string]string, key string) string { + u, err := url.Parse(base) + if err != nil { + return base + } + q := u.Query() + for k, v := range params { + q.Set(k, v) + } + q.Set("sign_type", "MD5") + q.Set("sign", epaySign(params, key)) + u.RawQuery = q.Encode() + return u.String() +} + +// OnPaid 异步投递商户 notify_url(退避:立即/15s/1m/5m/15m)。 +func (n *PayNotifier) OnPaid(tradeNo string) { + go n.deliver(tradeNo) +} + +func (n *PayNotifier) deliver(tradeNo string) { + for i, delay := range notifyBackoff { + if delay > 0 { + time.Sleep(delay) + } + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + o, err := n.Store.Q().GetPayOrderByTradeNo(ctx, tradeNo) + cancel() + if err != nil || o.Status != payStatusPaid || o.Notified == 1 || o.NotifyUrl == "" { + return + } + if n.attempt(o) { + _ = n.Store.Q().SetPayNotified(context.Background(), o.ID) + return + } + _ = n.Store.Q().IncPayNotifyAttempts(context.Background(), o.ID) + log.Printf("[pay] %s 第 %d 次通知未确认", tradeNo, i+1) + } +} + +func (n *PayNotifier) attempt(o db.PayOrder) bool { + pid, key, err := payMerchant(n.Store) + if err != nil { + return false + } + params := notifyParams(o, pid) + params["sign"] = epaySign(params, key) + params["sign_type"] = "MD5" + form := url.Values{} + for k, v := range params { + form.Set(k, v) + } + cli := &http.Client{Timeout: 10 * time.Second} + if resp, err := cli.Post(o.NotifyUrl, "application/x-www-form-urlencoded", + strings.NewReader(form.Encode())); err == nil && notifyRespOK(resp) { + return true + } + sep := "?" + if strings.Contains(o.NotifyUrl, "?") { + sep = "&" + } + if resp, err := cli.Get(o.NotifyUrl + sep + form.Encode()); err == nil { + return notifyRespOK(resp) + } + return false +} + +func notifyRespOK(resp *http.Response) bool { + defer resp.Body.Close() + body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) + return resp.StatusCode == 200 && strings.Contains(strings.ToLower(string(body)), "success") +} + +// BootPay 启动时装配上游凭据(若有)并触发商品自检。 +func BootPay(st *store.Store, up *payx.Client, cfg *config.Config) { + username := strSetting(st, keyUpUsername) + enc := strSetting(st, keyUpPassword) + if username == "" || enc == "" { + return + } + pass, err := vault.Decrypt(cfg.VaultKey, enc) + if err != nil || pass == "" { + log.Printf("[pay] 上游凭据解密失败: %v", err) + return + } + up.SetCredentialsLazy(username, pass) + ensureGoodsAsync(st, up) +} diff --git a/backend/internal/service/pay_routes.go b/backend/internal/service/pay_routes.go new file mode 100644 index 0000000..f16c6cf --- /dev/null +++ b/backend/internal/service/pay_routes.go @@ -0,0 +1,189 @@ +// pay_routes.go 易支付兼容协议端点:/submit.php(跳收银台)、/mapi.php(JSON下单)、/api.php(查单)。 +package service + +import ( + "context" + "encoding/json" + "strings" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/payx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// paySubmitHandler GET/POST /submit.php:验签建单后 302 到收银台。 +func PaySubmitHandler(st *store.Store, up *payx.Client) fun.RouteHandler { + return func(c *fun.RouteCtx) error { + order, err := epayCreateOrder(c, st, up) + if err != nil { + return epayFail(c, err) + } + // 当码支付:优先跳二维码支付链接(qr.alipay.com),退化到本地收银台 + target := order.Qrcode + if target == "" { + target = "/cashier/" + order.TradeNo + } + c.RequestCtx.Redirect(target, 302) + return nil + } +} + +// payMapiHandler GET/POST /mapi.php:验签建单,JSON 返回 payurl/qrcode。 +func PayMapiHandler(st *store.Store, up *payx.Client) fun.RouteHandler { + return func(c *fun.RouteCtx) error { + order, err := epayCreateOrder(c, st, up) + if err != nil { + return epayFail(c, err) + } + payurl := order.Qrcode + if payurl == "" { + payurl = order.Payurl + } + return epayJSON(c, map[string]any{ + "code": 1, "msg": "success", + "trade_no": order.TradeNo, + "out_trade_no": order.OutTradeNo, + "type": order.Channel, + "name": order.Subject, + "money": fenToYuan(order.Money), + "payurl": payurl, + "qrcode": order.Qrcode, + "urlscheme": order.Qrcode, + }) + } +} + +// PayApiHandler GET/POST /api.php:act=order 查单(pid+key 验签)。 +func PayApiHandler(st *store.Store) fun.RouteHandler { + return func(c *fun.RouteCtx) error { + act := strings.TrimSpace(c.Param("act")) + pidStr := strings.TrimSpace(c.Param("pid")) + key := strings.TrimSpace(c.Param("key")) + merPid, merKey, err := payMerchant(st) + if err != nil || pidStr != merPid || key != merKey { + return epayJSON(c, map[string]any{"code": -1, "msg": "商户校验失败"}) + } + if act != "order" { + return epayJSON(c, map[string]any{"code": -1, "msg": "不支持的 act"}) + } + tradeNo := strings.TrimSpace(c.Param("trade_no")) + outNo := strings.TrimSpace(c.Param("out_trade_no")) + var order struct { + TradeNo string + OutTradeNo string + Subject string + Money int64 + Status int64 + } + if tradeNo != "" { + r, err := st.Q().GetPayOrderByTradeNo(context.Background(), tradeNo) + if err != nil { + return epayJSON(c, map[string]any{"code": -1, "msg": "订单不存在"}) + } + order.TradeNo, order.OutTradeNo, order.Subject = r.TradeNo, r.OutTradeNo, r.Subject + order.Money, order.Status = r.Money, r.Status + } else if outNo != "" { + r, err := st.Q().GetPayOrderByOutNo(context.Background(), outNo) + if err != nil { + return epayJSON(c, map[string]any{"code": -1, "msg": "订单不存在"}) + } + order.TradeNo, order.OutTradeNo, order.Subject = r.TradeNo, r.OutTradeNo, r.Subject + order.Money, order.Status = r.Money, r.Status + } else { + return epayJSON(c, map[string]any{"code": -1, "msg": "请传入 out_trade_no 或 trade_no"}) + } + status := int64(0) + if order.Status == payStatusPaid { + status = 1 + } + return epayJSON(c, map[string]any{ + "code": 1, "msg": "查询订单号成功", + "trade_no": order.TradeNo, + "out_trade_no": order.OutTradeNo, + "name": order.Subject, + "money": fenToYuan(order.Money), + "status": status, + }) + } +} + +// epayOrderResult 建单结果(submit/mapi 共用)。 +type epayOrderResult struct { + TradeNo string + OutTradeNo string + Channel string + Subject string + Money int64 + Payurl string + Qrcode string +} + +// epayCreateOrder submit/mapi 共用下单逻辑:参数校验 → 验签 → 幂等建单。 +func epayCreateOrder(c *fun.RouteCtx, st *store.Store, up *payx.Client) (epayOrderResult, error) { + var out epayOrderResult + pid := strings.TrimSpace(c.Param("pid")) + merPid, merKey, err := payMerchant(st) + if err != nil || merPid == "" { + return out, errParam("商户未配置") + } + if pid == "" || pid != merPid { + return out, errParam("PID 参数错误") + } + channel := strings.TrimSpace(c.Param("type")) + if channel == "" { + channel = "alipay" + } + if channel != "alipay" { + return out, errParam("当前仅支持支付宝支付(alipay)") + } + outTradeNo := strings.TrimSpace(c.Param("out_trade_no")) + if outTradeNo == "" || len(outTradeNo) > 64 { + return out, errParam("商户订单号 out_trade_no 需为 1-64 位") + } + subject := strings.TrimSpace(c.Param("name")) + if subject == "" || len(subject) > 100 { + return out, errParam("商品名称 name 需为 1-100 个字符") + } + money, err := yuanToFen(c.Param("money")) + if err != nil || money <= 0 || money > payMaxFen { + return out, errParam("订单金额非法(0-2000 元)") + } + notifyURL := strings.TrimSpace(c.Param("notify_url")) + returnURL := strings.TrimSpace(c.Param("return_url")) + for _, u := range []string{notifyURL, returnURL} { + if u != "" && !strings.HasPrefix(u, "http://") && !strings.HasPrefix(u, "https://") { + return out, errParam("notify_url / return_url 须为合法 URL") + } + } + if !epayVerify(c.Data, merKey, strings.TrimSpace(c.Param("sign"))) { + return out, errParam("签名验证失败") + } + order, err := createPayOrder(st, up, createPayOrderIn{ + OutTradeNo: outTradeNo, Channel: channel, Subject: subject, + Money: money, NotifyURL: notifyURL, ReturnURL: returnURL, + ClientIP: c.RequestCtx.RemoteIP().String(), + }) + if err != nil { + return out, err + } + out.TradeNo, out.OutTradeNo, out.Channel = order.TradeNo, order.OutTradeNo, order.Channel + out.Subject, out.Money, out.Payurl, out.Qrcode = order.Subject, order.Money, order.Payurl, order.Qrcode + return out, nil +} + +func epayJSON(c *fun.RouteCtx, obj map[string]any) error { + raw, err := json.Marshal(obj) + if err != nil { + return err + } + c.RequestCtx.Response.Header.Set("Content-Type", "application/json; charset=utf-8") + c.RequestCtx.Write(raw) + return nil +} + +func epayFail(c *fun.RouteCtx, err error) error { + return epayJSON(c, map[string]any{"code": -1, "msg": err.Error()}) +} + +func errParam(msg string) error { return fun.Error(4001, msg) } diff --git a/backend/internal/service/proxy.go b/backend/internal/service/proxy.go new file mode 100644 index 0000000..dbf3f0e --- /dev/null +++ b/backend/internal/service/proxy.go @@ -0,0 +1,416 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "log" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/proxyx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// 代理池后台任务参数 +const ( + proxyCheckInterval = time.Hour // 测活节奏:约每小时一轮 + proxyCheckStale = 55 * time.Minute // 距上次检测超过该时长才重测 + proxyCheckTimeout = 4 * time.Second // 单次 TCP 拨测超时 + proxyCheckWorkers = 128 // 测活并发 + proxyPurgeAge = 7 * 24 * time.Hour // 死代理记录保留时长 + syncDetailCap = 200 // 同步详情里记录的地址上限 +) + +var ( + proxySyncing atomic.Bool + proxyChecking atomic.Bool + proxyManualSem = make(chan struct{}, 1) // 手动触发同步信号 +) + +// ProxySvc 代理池:地区分组浏览 + 同步记录;抓取/测活由后台调度器跑 +type ProxySvc struct { + fun.Ctx + Store *store.Store +} + +func proxyView(p store.Proxy) dto.ProxyView { + return dto.ProxyView{ + Id: p.Id, Ip: p.Ip, Port: p.Port, Address: fmt.Sprintf("%s:%d", p.Ip, p.Port), + Protocols: p.Protocols, Anonymity: p.Anonymity, RegionId: p.RegionID, + RegionCode: p.RegionCode, RegionName: p.RegionName, RegionZhName: p.RegionZhName, + Latency: p.Latency, Speed: p.Speed, Uptime: p.Uptime, Alive: p.Alive, + FailCount: p.FailCount, LastCheckedAt: p.LastCheckedAt, + LastAliveAt: p.LastAliveAt, LastSeenAt: p.LastSeenAt, + } +} + +func (s *ProxySvc) List(d dto.ProxyListDto) (dto.ProxyListView, error) { + alive := d.Alive + if alive != 0 && alive != 1 { + alive = -1 + } + items, total, err := s.Store.ListProxies(store.ProxyFilter{ + RegionID: d.RegionId, Protocol: strings.ToUpper(strings.TrimSpace(d.Protocol)), + Keyword: strings.TrimSpace(d.Keyword), Alive: alive, + Page: d.Page, PageSize: d.PageSize, + }) + if err != nil { + return dto.ProxyListView{}, err + } + out := dto.ProxyListView{Total: total, Items: make([]dto.ProxyView, 0, len(items))} + for _, p := range items { + out.Items = append(out.Items, proxyView(p)) + } + return out, nil +} + +func (s *ProxySvc) Regions() ([]dto.ProxyRegionView, error) { + regions, err := s.Store.ListRegions() + if err != nil { + return nil, err + } + out := make([]dto.ProxyRegionView, 0, len(regions)) + for _, r := range regions { + out = append(out, dto.ProxyRegionView{ + Id: r.Id, Code: r.Code, Name: r.Name, ZhName: r.ZhName, + Region: r.Region, AliveCount: r.AliveCount, + }) + } + return out, nil +} + +func (s *ProxySvc) Syncs() ([]dto.ProxySyncView, error) { + syncs, err := s.Store.ListProxySyncs(30) + if err != nil { + return nil, err + } + out := make([]dto.ProxySyncView, 0, len(syncs)) + for _, v := range syncs { + out = append(out, dto.ProxySyncView{ + Id: v.Id, StartedAt: v.StartedAt, FinishedAt: v.FinishedAt, Status: v.Status, + Source: v.Source, Total: v.Total, Added: v.Added, Removed: v.Removed, + Updated: v.Updated, DetailJson: v.Detail, Error: v.Error, + }) + } + return out, nil +} + +func (s *ProxySvc) Stats() (dto.ProxyStatsView, error) { + total, err := s.Store.CountAliveProxies() + if err != nil { + return dto.ProxyStatsView{}, err + } + regions, err := s.Store.ListRegions() + if err != nil { + return dto.ProxyStatsView{}, err + } + v := dto.ProxyStatsView{ + Total: total, Regions: int64(len(regions)), + Syncing: proxySyncing.Load(), Checking: proxyChecking.Load(), + LastCheckAt: s.Store.LastProxyCheckAt(), + } + if syncs, err := s.Store.ListProxySyncs(1); err == nil && len(syncs) > 0 { + v.LastSyncAt = syncs[0].FinishedAt + v.LastStatus = syncs[0].Status + } + return v, nil +} + +// SyncNow 手动触发一次同步(后台执行,不等结果) +func (s *ProxySvc) SyncNow() (dto.OkView, error) { + select { + case proxyManualSem <- struct{}{}: + default: + } + return dto.OkView{Message: "ok"}, nil +} + +// CheckNow 手动触发一轮测活(后台执行) +func (s *ProxySvc) CheckNow() (dto.OkView, error) { + go runProxyCheck(context.Background(), s.Store) + return dto.OkView{Message: "ok"}, nil +} + +// ---- 同步与测活 ---- + +type syncDetail struct { + Added []string `json:"added,omitempty"` + Removed []string `json:"removed,omitempty"` +} + +// runProxySync 全量同步:拉取源数据 → 区域 upsert → 代理按 ip:port diff。 +// 源上消失的代理直接删除;已存在的刷新指标并复活。 +func runProxySync(ctx context.Context, st *store.Store) { + if !proxySyncing.CompareAndSwap(false, true) { + return + } + defer proxySyncing.Store(false) + + now := time.Now() + sync := &store.ProxySync{Source: "proxy5", StartedAt: now.Unix()} + syncID, err := st.BeginProxySync(sync.Source, sync.StartedAt) + if err != nil { + log.Printf("proxy: begin sync failed: %v", err) + return + } + sync.Id = syncID + finish := func(status string, detail syncDetail, err error) { + sync.FinishedAt = time.Now().Unix() + sync.Status = status + if b, e := json.Marshal(detail); e == nil { + sync.Detail = string(b) + } + if err != nil { + sync.Error = err.Error() + } + if e := st.FinishProxySync(sync); e != nil { + log.Printf("proxy: finish sync failed: %v", e) + } + } + + // 多源抓取:任一源失败不致命,只影响该源名下代理的删除判定 + type srcData struct { + name string + remotes []proxyx.Remote + } + sources := []srcData{} + failed := map[string]error{} + if p5, _, err := proxyx.Fetch(ctx); err != nil { + failed["proxy5"] = err + } else { + sources = append(sources, srcData{"proxy5", p5}) + } + if rl, err := proxyx.FetchRola(ctx); err != nil { + failed["rola"] = err + } else { + sources = append(sources, srcData{"rola", rl}) + } + if len(sources) == 0 { + err := fmt.Errorf("all sources failed: proxy5=%v rola=%v", failed["proxy5"], failed["rola"]) + finish("error", syncDetail{}, err) + log.Printf("proxy: %v", err) + return + } + + // 按 ip:port 合并各源记录:首个来源保留字段,协议取并集 + type merged struct { + r proxyx.Remote + source string + } + pool := map[string]*merged{} + order := []string{} + for _, src := range sources { + for _, r := range src.remotes { + key := fmt.Sprintf("%s:%d", r.IP, r.Port) + if m, ok := pool[key]; ok { + seen := map[string]bool{} + for _, p := range m.r.Protocols { + seen[p] = true + } + for _, p := range r.Protocols { + if !seen[p] { + m.r.Protocols = append(m.r.Protocols, p) + } + } + continue + } + pool[key] = &merged{r: r, source: src.name} + order = append(order, key) + } + } + sync.Total = int64(len(order)) + seenKeys := map[string]bool{} + for k := range pool { + seenKeys[k] = true + } + + keys, err := st.ProxyKeys() + if err != nil { + finish("error", syncDetail{}, err) + return + } + + var detail syncDetail + regionIDs := map[string]int64{} + for _, key := range order { + m := pool[key] + r := m.r + code := strings.ToUpper(strings.TrimSpace(r.CountryCode)) + if code == "" { + code = "UN" + } + regionID, ok := regionIDs[code] + if !ok { + regionID, err = st.UpsertRegion(code, r.Country, proxyx.ZhName(code), r.Region, r.CountrySlug) + if err != nil { + continue + } + regionIDs[code] = regionID + } + p := &store.Proxy{ + Ip: r.IP, Port: int64(r.Port), Protocols: strings.Join(r.Protocols, ","), + Anonymity: r.Anonymity, Source: m.source, RegionID: regionID, + Latency: int64(r.Latency), Speed: int64(r.Speed), Uptime: int64(r.Uptime), + LastSeenAt: now.Unix(), CreatedAt: now.Unix(), + } + if k, exists := keys[key]; exists { + delete(keys, key) + if err := st.UpdateProxySeen(k.Id, p); err == nil { + sync.Updated++ + } + continue + } + if err := st.InsertProxy(p); err == nil { + sync.Added++ + if len(detail.Added) < syncDetailCap { + detail.Added = append(detail.Added, key) + } + } + } + // 删除:出现在任一成功源里的都保留;只属于失败源的也保留(可能还在源上) + for key, k := range keys { + if seenKeys[key] { + continue + } + if _, srcFailed := failed[k.Source]; srcFailed { + continue + } + if err := st.DeleteProxy(k.Id); err == nil { + sync.Removed++ + if len(detail.Removed) < syncDetailCap { + detail.Removed = append(detail.Removed, key) + } + } + } + var syncErr error + if len(failed) > 0 { + syncErr = fmt.Errorf("partial: %v", failed) + } + finish("ok", detail, syncErr) + log.Printf("proxy: sync done total=%d added=%d updated=%d removed=%d failed=%v", + sync.Total, sync.Added, sync.Updated, sync.Removed, failed) +} + +// runProxyCheck 测活一轮:取距上次检测超过阈值的存活代理,并发 TCP 拨测; +// 连不上即标记为死(alive=0),死记录超过保留期后物理清除。 +func runProxyCheck(ctx context.Context, st *store.Store) { + if !proxyChecking.CompareAndSwap(false, true) { + return + } + defer proxyChecking.Store(false) + + due, err := st.DueProxies(time.Now().Add(-proxyCheckStale).Unix()) + if err != nil { + log.Printf("proxy: due list failed: %v", err) + return + } + if len(due) == 0 { + return + } + now := time.Now().Unix() + sem := make(chan struct{}, proxyCheckWorkers) + var wg sync.WaitGroup + for _, p := range due { + wg.Add(1) + go func(p store.DueProxy) { + defer wg.Done() + sem <- struct{}{} + defer func() { <-sem }() + latency, err := proxyx.Check(ctx, p.Ip, int(p.Port), p.Protocols, proxyCheckTimeout) + if err != nil { + _ = st.MarkProxyFailed(p.Id, now, p.FailCount+1) + return + } + _ = st.MarkProxyAlive(p.Id, now, now, int64(latency)) + }(p) + } + wg.Wait() + if n, err := st.PurgeDeadProxies(time.Now().Add(-proxyPurgeAge).Unix()); err == nil && n > 0 { + log.Printf("proxy: purged %d dead records", n) + } + log.Printf("proxy: checked %d proxies", len(due)) +} + +// StartProxyScheduler 调度器:启动补一次同步(数据为空时)→ 每小时测活 → 每日 00:00 同步。 +func StartProxyScheduler(ctx context.Context, st *store.Store) { + if syncs, err := st.ListProxySyncs(1); err == nil { + if len(syncs) == 0 || time.Since(time.Unix(syncs[0].StartedAt, 0)) > 20*time.Hour { + go runProxySync(ctx, st) + } + } + go func() { + checkTick := time.NewTicker(proxyCheckInterval) + defer checkTick.Stop() + for { + next := nextMidnight() + syncTimer := time.NewTimer(time.Until(next)) + select { + case <-ctx.Done(): + syncTimer.Stop() + return + case <-checkTick.C: + runProxyCheck(ctx, st) + case <-proxyManualSem: + go runProxySync(ctx, st) + case <-syncTimer.C: + runProxySync(ctx, st) + } + } + }() + log.Printf("proxy: scheduler started (check hourly, sync daily 00:00)") +} + +func nextMidnight() time.Time { + now := time.Now() + return time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location()) +} + +// ---- 提取密钥管理(admin) ---- + +func proxyKeyView(k store.ProxyApiKey) dto.ProxyKeyView { + return dto.ProxyKeyView{ + Id: k.Id, Key: k.Key, Name: k.Name, RegionId: k.RegionID, + RegionCode: k.RegionCode, RegionZhName: k.RegionZhName, + Enabled: k.Enabled, UsedCount: k.UsedCount, + LastUsedAt: k.LastUsedAt, CreatedAt: k.CreatedAt, + } +} + +func (s *ProxySvc) ListKeys() ([]dto.ProxyKeyView, error) { + keys, err := s.Store.ListProxyApiKeys() + if err != nil { + return nil, err + } + out := make([]dto.ProxyKeyView, 0, len(keys)) + for _, k := range keys { + out = append(out, proxyKeyView(k)) + } + return out, nil +} + +// CreateKey 生成一个提取密钥:绑定地区(0=全部),key 随机生成落库 +func (s *ProxySvc) CreateKey(d dto.ProxyKeySaveDto) (dto.ProxyKeyView, error) { + key, err := shareCode() + if err != nil { + return dto.ProxyKeyView{}, err + } + key = "pk_" + key + key // 20 位随机串,够长防穷举 + id, err := s.Store.InsertProxyApiKey(key, strings.TrimSpace(d.Name), d.RegionId) + if err != nil { + return dto.ProxyKeyView{}, err + } + return dto.ProxyKeyView{Id: id, Key: key, Name: d.Name, RegionId: d.RegionId, Enabled: true}, nil +} + +func (s *ProxySvc) DeleteKey(d dto.IdDto) (dto.OkView, error) { + if err := s.Store.DeleteProxyApiKey(d.Id); err != nil { + return dto.OkView{}, err + } + return dto.OkView{Message: "ok"}, nil +} diff --git a/backend/internal/service/proxy_routes.go b/backend/internal/service/proxy_routes.go new file mode 100644 index 0000000..33d2082 --- /dev/null +++ b/backend/internal/service/proxy_routes.go @@ -0,0 +1,105 @@ +package service + +import ( + "context" + "fmt" + "strings" + "sync" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/proxyx" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +const ( + extractBatchSize = 15 // 每批并行探测的候选数 + extractBatches = 4 // 最多取几批 + extractProbeTO = 3 * time.Second // 单代理探测超时 + extractDeadline = 20 * time.Second // 整个请求的最长耗时 +) + +type extractResult struct { + addr string + lat int + ok bool +} + +// ProxyExtractHandler GET /v1/proxy?key=xxx +// 公开提取接口:key 落库校验,地区绑在 key 上(0=全部)。 +// 并行探测一批候选,谁先探活返回谁,纯文本 "ip:port";两批全死返回 503。 +func ProxyExtractHandler(st *store.Store) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + reqCtx := rc.RequestCtx + key := strings.TrimSpace(string(reqCtx.QueryArgs().Peek("key"))) + if key == "" { + routeJSONErr(reqCtx, 401, "missing key") + return nil + } + k, err := st.GetProxyApiKey(key) + if err != nil { + routeJSONErr(reqCtx, 401, "invalid key") + return nil + } + + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(extractDeadline)) + defer cancel() + + for b := 0; b < extractBatches; b++ { + items, err := st.RandomProxies(k.RegionID, "", "", extractBatchSize) + if err != nil { + routeJSONErr(reqCtx, 500, "query failed") + return nil + } + if len(items) == 0 { + break + } + if p, _, ok := probeBatch(ctx, st, items); ok { + st.TouchProxyApiKey(k.Id) + reqCtx.SetContentType("text/plain; charset=utf-8") + reqCtx.SetBodyString(fmt.Sprintf("%s:%d\n", p.Ip, p.Port)) + return nil + } + } + routeJSONErr(reqCtx, 503, "no alive proxy") + return nil + } +} + +// probeBatch 并行探测一批候选:先活先赢,取消其余;死代理顺手标记移出池。 +func probeBatch(ctx context.Context, st *store.Store, items []store.RandomProxy) (store.RandomProxy, int, bool) { + type hit struct { + p store.RandomProxy + lat int + } + hits := make(chan hit, 1) + var wg sync.WaitGroup + for _, p := range items { + wg.Add(1) + go func(p store.RandomProxy) { + defer wg.Done() + lat, err := proxyx.Check(ctx, p.Ip, int(p.Port), p.Protocols, extractProbeTO) + if err != nil { + _ = st.MarkProxyFailed(p.Id, time.Now().Unix(), p.FailCount+1) + return + } + select { + case hits <- hit{p, lat}: + case <-ctx.Done(): + } + }(p) + } + done := make(chan struct{}) + go func() { wg.Wait(); close(done) }() + select { + case h := <-hits: + now := time.Now().Unix() + _ = st.MarkProxyAlive(h.p.Id, now, now, int64(h.lat)) + return h.p, h.lat, true + case <-done: + return store.RandomProxy{}, 0, false + case <-ctx.Done(): + return store.RandomProxy{}, 0, false + } +} diff --git a/backend/internal/service/server.go b/backend/internal/service/server.go new file mode 100644 index 0000000..133b54d --- /dev/null +++ b/backend/internal/service/server.go @@ -0,0 +1,569 @@ +package service + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "path" + "strconv" + "strings" + "time" + + "github.com/cyi-cc/fun" + "github.com/fasthttp/websocket" + "github.com/pkg/sftp" + "github.com/valyala/fasthttp" + "golang.org/x/crypto/ssh" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/sshx" + "github.com/cyi-cc/cyi-box/backend/internal/store" + "github.com/cyi-cc/cyi-box/backend/internal/vault" +) + +// ServerSvc 服务器管理:连接密钥加密落库,所有 SSH/SFTP 端点仅管理员。 +// 指标采集与文件操作每次新拨号——管理场景连接低频,免去连接池维护成本。 +type ServerSvc struct { + fun.Ctx + Store *store.Store + Cfg *config.Config `fun:"auto"` +} + +func (s *ServerSvc) List() ([]dto.ServerView, error) { + items, err := s.Store.ListServers() + if err != nil { + return nil, fmt.Errorf("servers: list failed: %w", err) + } + views := make([]dto.ServerView, 0, len(items)) + for _, e := range items { + views = append(views, serverView(e)) + } + return views, nil +} + +func (s *ServerSvc) Save(d dto.SaveServerDto) (dto.ServerView, error) { + name := strings.TrimSpace(d.Name) + host := strings.TrimSpace(d.Host) + username := strings.TrimSpace(d.Username) + if name == "" || len(name) > 64 { + return dto.ServerView{}, fun.Error(4001, "名称必填且不超过 64 字符") + } + if host == "" || len(host) > 255 { + return dto.ServerView{}, fun.Error(4001, "主机地址必填") + } + if username == "" { + return dto.ServerView{}, fun.Error(4001, "用户名必填") + } + authType := "password" + if d.AuthType == "key" { + authType = "key" + } + port := int64(22) + if d.Port != nil { + if *d.Port < 1 || *d.Port > 65535 { + return dto.ServerView{}, fun.Error(4001, "端口范围 1-65535") + } + port = *d.Port + } + + e := store.Server{Name: name, Host: host, Port: port, Username: username, AuthType: authType} + if d.Id != nil { + old, err := s.Store.GetServer(*d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.ServerView{}, fun.Error(4001, "服务器不存在") + } + if err != nil { + return dto.ServerView{}, err + } + e = old + e.Name, e.Host, e.Port, e.Username, e.AuthType = name, host, port, username, authType + } + if d.Note != nil { + e.Note = strings.TrimSpace(*d.Note) + } + if d.Secret != nil { + secret := strings.TrimSpace(*d.Secret) + if secret == "" { + e.SecretEnc = "" + } else { + enc, err := vault.Encrypt(s.Cfg.VaultKey, secret) + if err != nil { + return dto.ServerView{}, err + } + e.SecretEnc = enc + } + } + if e.SecretEnc == "" { + return dto.ServerView{}, fun.Error(4001, "请填写密码或私钥") + } + if err := s.Store.SaveServer(&e); err != nil { + return dto.ServerView{}, fmt.Errorf("servers: save failed: %w", err) + } + return serverView(e), nil +} + +func (s *ServerSvc) Delete(d dto.ServerIdDto) (dto.OkView, error) { + if err := s.Store.DeleteServer(d.Id); err != nil { + if errors.Is(err, store.ErrNotFound) { + return dto.OkView{}, fun.Error(4001, "服务器不存在") + } + return dto.OkView{}, fmt.Errorf("servers: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +// Metrics SSH 拨号 + 采集;拨不通直接返回带因的错误,前端即「离线」 +func (s *ServerSvc) Metrics(d dto.ServerIdDto) (dto.ServerMetricsView, error) { + client, err := s.dial(d.Id) + if err != nil { + return dto.ServerMetricsView{}, err + } + defer client.Close() + m, err := sshx.CollectMetrics(client) + if err != nil { + return dto.ServerMetricsView{}, err + } + v := dto.ServerMetricsView{ + Hostname: m.Hostname, + Os: m.OS, + Kernel: m.Kernel, + UptimeSeconds: m.UptimeSeconds, + CpuPercent: m.CpuPercent, + Load1: m.Load1, + Load5: m.Load5, + Load15: m.Load15, + MemTotal: m.MemTotal, + MemUsed: m.MemUsed, + MemAvail: m.MemAvail, + SwapTotal: m.SwapTotal, + SwapUsed: m.SwapUsed, + DiskTotal: m.DiskTotal, + DiskUsed: m.DiskUsed, + DiskAvail: m.DiskAvail, + NetRxRate: m.NetRxRate, + NetTxRate: m.NetTxRate, + NetRxTotal: m.NetRxTotal, + NetTxTotal: m.NetTxTotal, + CollectedAt: time.Now().Unix(), + } + for _, dsk := range m.Disks { + v.Disks = append(v.Disks, dto.DiskMountView{ + Mount: dsk.Mount, Fs: dsk.Fs, Total: dsk.Total, + Used: dsk.Used, Avail: dsk.Avail, Percent: dsk.Percent, + }) + } + return v, nil +} + +func (s *ServerSvc) dial(id int64) (*ssh.Client, error) { + e, err := s.Store.GetServer(id) + if errors.Is(err, store.ErrNotFound) { + return nil, fun.Error(4001, "服务器不存在") + } + if err != nil { + return nil, err + } + return dialServer(e, s.Cfg.VaultKey) +} + +// dialServer 解密 secret 并拨号;route 处理器与 RPC 共用 +func dialServer(e store.Server, key []byte) (*ssh.Client, error) { + if e.SecretEnc == "" { + return nil, fun.Error(4001, "该服务器未配置密码/私钥") + } + secret, err := vault.Decrypt(key, e.SecretEnc) + if err != nil { + return nil, fmt.Errorf("servers: decrypt secret failed: %w", err) + } + return sshx.Dial(e.Host, e.Port, e.Username, e.AuthType, secret) +} + +func serverView(e store.Server) dto.ServerView { + v := dto.ServerView{ + Id: e.Id, + Name: e.Name, + Host: e.Host, + Port: e.Port, + Username: e.Username, + AuthType: e.AuthType, + HasSecret: e.SecretEnc != "", + CreatedAt: e.CreatedAt, + } + if e.Note != "" { + v.Note = &e.Note + } + return v +} + +// ---- SFTP RPC ---- + +// cleanPath 规整远程路径:缺省 "/",防穿越由 SSH 权限兜底 +func cleanPath(p string) string { + p = strings.TrimSpace(p) + if p == "" { + return "/" + } + return path.Clean(p) +} + +func (s *ServerSvc) sftpClient(id int64) (*sftp.Client, *ssh.Client, error) { + c, err := s.dial(id) + if err != nil { + return nil, nil, err + } + fc, err := sftp.NewClient(c) + if err != nil { + c.Close() + return nil, nil, fmt.Errorf("sftp: 初始化失败: %w", err) + } + return fc, c, nil +} + +func (s *ServerSvc) SftpList(d dto.SftpPathDto) (dto.SftpListView, error) { + fc, c, err := s.sftpClient(d.Id) + if err != nil { + return dto.SftpListView{}, err + } + defer fc.Close() + defer c.Close() + p := cleanPath(d.Path) + fis, err := fc.ReadDir(p) + if err != nil { + return dto.SftpListView{}, fun.Error(4001, "无法读取目录:"+err.Error()) + } + v := dto.SftpListView{Path: p, Entries: []dto.SftpEntryView{}} + for _, fi := range fis { + v.Entries = append(v.Entries, dto.SftpEntryView{ + Name: fi.Name(), + Size: fi.Size(), + Mode: fi.Mode().String(), + IsDir: fi.IsDir(), + ModTime: fi.ModTime().Unix(), + }) + } + return v, nil +} + +func (s *ServerSvc) SftpMkdir(d dto.SftpPathDto) (dto.OkView, error) { + fc, c, err := s.sftpClient(d.Id) + if err != nil { + return dto.OkView{}, err + } + defer fc.Close() + defer c.Close() + if err := fc.Mkdir(cleanPath(d.Path)); err != nil { + return dto.OkView{}, fun.Error(4001, "创建目录失败:"+err.Error()) + } + return dto.OkView{Message: "已创建"}, nil +} + +// SftpRemove 删文件或空目录;非空目录拒绝递归删除,避免误删整棵目录树 +func (s *ServerSvc) SftpRemove(d dto.SftpPathDto) (dto.OkView, error) { + fc, c, err := s.sftpClient(d.Id) + if err != nil { + return dto.OkView{}, err + } + defer fc.Close() + defer c.Close() + p := cleanPath(d.Path) + fi, err := fc.Stat(p) + if err != nil { + return dto.OkView{}, fun.Error(4001, "路径不存在") + } + if fi.IsDir() { + err = fc.RemoveDirectory(p) + } else { + err = fc.Remove(p) + } + if err != nil { + return dto.OkView{}, fun.Error(4001, "删除失败:"+err.Error()+"(目录需为空)") + } + return dto.OkView{Message: "已删除"}, nil +} + +func (s *ServerSvc) SftpRename(d dto.SftpRenameDto) (dto.OkView, error) { + newName := strings.TrimSpace(d.NewName) + if newName == "" || strings.ContainsAny(newName, "/\\") { + return dto.OkView{}, fun.Error(4001, "新名字无效") + } + fc, c, err := s.sftpClient(d.Id) + if err != nil { + return dto.OkView{}, err + } + defer fc.Close() + defer c.Close() + old := cleanPath(d.Path) + if err := fc.PosixRename(old, path.Join(path.Dir(old), newName)); err != nil { + return dto.OkView{}, fun.Error(4001, "重命名失败:"+err.Error()) + } + return dto.OkView{Message: "已重命名"}, nil +} + +// ---- 自定义路由:上传 / 下载 / WS 终端 ---- + +// routeAdmin 自定义路由不走 AuthGuard,手动校验 query token 且要求管理员 +func routeAdmin(st *store.Store, ctx *fasthttp.RequestCtx) error { + token := string(ctx.QueryArgs().Peek("token")) + if token == "" { + ctx.SetStatusCode(fasthttp.StatusUnauthorized) + return errors.New("missing token") + } + su, err := st.GetSessionUser(token, sessionTTL, sessionRenewAfter) + if err != nil { + ctx.SetStatusCode(fasthttp.StatusUnauthorized) + return errors.New("invalid token") + } + if su.User.Role != "admin" || su.User.Status != "active" { + ctx.SetStatusCode(fasthttp.StatusForbidden) + return errors.New("admin only") + } + return nil +} + +func routeServer(st *store.Store, ctx *fasthttp.RequestCtx) (store.Server, error) { + id, _ := strconv.ParseInt(string(ctx.QueryArgs().Peek("id")), 10, 64) + e, err := st.GetServer(id) + if err != nil { + ctx.SetStatusCode(fasthttp.StatusNotFound) + ctx.SetBodyString("server not found") + } + return e, err +} + +// SftpDownloadHandler GET /files/download?id=&path=&token= 流式回传文件 +func SftpDownloadHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + if err := routeAdmin(st, ctx); err != nil { + return nil + } + e, err := routeServer(st, ctx) + if err != nil { + return nil + } + client, err := dialServer(e, cfg.VaultKey) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString("SSH 连接失败:" + err.Error()) + return nil + } + fc, err := sftp.NewClient(client) + if err != nil { + client.Close() + ctx.SetStatusCode(502) + ctx.SetBodyString("SFTP 初始化失败") + return nil + } + p := cleanPath(string(ctx.QueryArgs().Peek("path"))) + f, err := fc.Open(p) + if err != nil { + fc.Close() + client.Close() + ctx.SetStatusCode(404) + ctx.SetBodyString("文件不可读:" + err.Error()) + return nil + } + if fi, err := f.Stat(); err == nil { + ctx.Response.Header.Set("Content-Length", strconv.FormatInt(fi.Size(), 10)) + } + name := path.Base(p) + ctx.Response.Header.Set("Content-Disposition", "attachment; filename*=UTF-8''"+urlPathEscape(name)) + ctx.SetContentType("application/octet-stream") + // 流式回传:连接生命周期交给 StreamWriter 收尾 + ctx.SetBodyStreamWriter(func(w *bufio.Writer) { + _, _ = io.Copy(w, f) + f.Close() + fc.Close() + client.Close() + }) + return nil + } +} + +func urlPathEscape(s string) string { + var b strings.Builder + for _, c := range []byte(s) { + if c < 0x20 || c > 0x7e || strings.ContainsRune("\"%;+", rune(c)) { + fmt.Fprintf(&b, "%%%02X", c) + } else { + b.WriteByte(c) + } + } + return b.String() +} + +// SftpUploadHandler POST /files/upload?id=&dir=&token= multipart 字段 file +func SftpUploadHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + if err := routeAdmin(st, ctx); err != nil { + ctx.SetBodyString(`{"status":2,"msg":"未授权"}`) + return nil + } + e, err := routeServer(st, ctx) + if err != nil { + return nil + } + fh, err := ctx.FormFile("file") + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"缺少文件"}`) + return nil + } + src, err := fh.Open() + if err != nil { + ctx.SetStatusCode(400) + ctx.SetBodyString(`{"status":2,"msg":"读取上传失败"}`) + return nil + } + defer src.Close() + + client, err := dialServer(e, cfg.VaultKey) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString(`{"status":2,"msg":"SSH 连接失败"}`) + return nil + } + defer client.Close() + fc, err := sftp.NewClient(client) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString(`{"status":2,"msg":"SFTP 初始化失败"}`) + return nil + } + defer fc.Close() + + dir := cleanPath(string(ctx.QueryArgs().Peek("dir"))) + dst := path.Join(dir, path.Base(fh.Filename)) + w, err := fc.Create(dst) + if err != nil { + ctx.SetStatusCode(500) + ctx.SetBodyString(`{"status":2,"msg":"创建远端文件失败:` + err.Error() + `"}`) + return nil + } + if _, err := io.Copy(w, src); err != nil { + w.Close() + ctx.SetStatusCode(500) + ctx.SetBodyString(`{"status":2,"msg":"写入失败:` + err.Error() + `"}`) + return nil + } + w.Close() + ctx.SetContentType("application/json") + ctx.SetBodyString(`{"status":0,"msg":"ok"}`) + return nil + } +} + +// TerminalHandler GET /ws/terminal?id=&token= 升级 WebSocket → SSH PTY。 +// 协议:客户端→服务端首字节 0x01 为 JSON 控制帧 {"cols":N,"rows":N}(resize), +// 其余原样写入 shell stdin;服务端→客户端全部原样回显。 +func TerminalHandler(st *store.Store, cfg *config.Config) fun.RouteHandler { + upgrader := websocket.FastHTTPUpgrader{ + CheckOrigin: func(*fasthttp.RequestCtx) bool { return true }, + } + return func(rc *fun.RouteCtx) error { + ctx := rc.RequestCtx + if err := routeAdmin(st, ctx); err != nil { + ctx.SetBodyString("unauthorized") + return nil + } + e, err := routeServer(st, ctx) + if err != nil { + return nil + } + client, err := dialServer(e, cfg.VaultKey) + if err != nil { + ctx.SetStatusCode(502) + ctx.SetBodyString("SSH 连接失败:" + err.Error()) + return nil + } + // Upgrade 内部 hijack 连接后异步跑回调;ssh.Client 生命周期归 runTerm + if err := upgrader.Upgrade(ctx, func(ws *websocket.Conn) { + runTerm(ws, client) + }); err != nil { + client.Close() + return nil + } + return nil + } +} + +type termResize struct { + Cols int `json:"cols"` + Rows int `json:"rows"` +} + +func runTerm(ws *websocket.Conn, client *ssh.Client) { + defer ws.Close() + defer client.Close() + sess, err := client.NewSession() + if err != nil { + _ = ws.WriteMessage(websocket.TextMessage, []byte("\r\nopen session failed: "+err.Error()+"\r\n")) + return + } + defer sess.Close() + modes := ssh.TerminalModes{ + ssh.ECHO: 1, + ssh.TTY_OP_ISPEED: 14400, + ssh.TTY_OP_OSPEED: 14400, + } + if err := sess.RequestPty("xterm-256color", 24, 80, modes); err != nil { + _ = ws.WriteMessage(websocket.TextMessage, []byte("\r\npty failed: "+err.Error()+"\r\n")) + return + } + stdin, err := sess.StdinPipe() + if err != nil { + return + } + stdout, err := sess.StdoutPipe() + if err != nil { + return + } + sess.Stderr = sess.Stdout + if err := sess.Shell(); err != nil { + _ = ws.WriteMessage(websocket.TextMessage, []byte("\r\nshell failed: "+err.Error()+"\r\n")) + return + } + + done := make(chan struct{}) + // ws → stdin + go func() { + defer close(done) + for { + _, data, err := ws.ReadMessage() + if err != nil { + _ = sess.Close() + return + } + if len(data) > 0 && data[0] == 0x01 { + var r termResize + if json.Unmarshal(data[1:], &r) == nil && r.Cols > 0 && r.Rows > 0 { + _ = sess.WindowChange(r.Rows, r.Cols) + } + continue + } + if _, err := stdin.Write(data); err != nil { + return + } + } + }() + + // stdout → ws + buf := make([]byte, 32*1024) + for { + n, err := stdout.Read(buf) + if n > 0 { + if werr := ws.WriteMessage(websocket.BinaryMessage, buf[:n]); werr != nil { + break + } + } + if err != nil { + break + } + } + _ = sess.Close() + <-done +} diff --git a/backend/internal/service/setting.go b/backend/internal/service/setting.go new file mode 100644 index 0000000..46c84de --- /dev/null +++ b/backend/internal/service/setting.go @@ -0,0 +1,39 @@ +package service + +import ( + "strings" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// SettingSvc 站点级 KV 设置:读取对所有登录用户开放,写入仅管理员 +type SettingSvc struct { + fun.Ctx + Store *store.Store +} + +func (s *SettingSvc) Get(d dto.SettingDto) (dto.SettingView, error) { + key := strings.TrimSpace(d.Key) + if key == "" || len(key) > 64 { + return dto.SettingView{}, fun.Error(4001, "key 必填且不超过 64 字符") + } + v, err := s.Store.GetSetting(key) + if err != nil { + return dto.SettingView{}, err + } + return dto.SettingView{Value: v}, nil +} + +func (s *SettingSvc) Set(d dto.SettingDto) (dto.OkView, error) { + key := strings.TrimSpace(d.Key) + if key == "" || len(key) > 64 || len(d.Value) > 2000 { + return dto.OkView{}, fun.Error(4001, "key 必填且不超过 64 字符,value 不超过 2000 字符") + } + if err := s.Store.SetSetting(key, strings.TrimSpace(d.Value)); err != nil { + return dto.OkView{}, err + } + return dto.OkView{Message: "已保存"}, nil +} diff --git a/backend/internal/service/tool.go b/backend/internal/service/tool.go new file mode 100644 index 0000000..06555c5 --- /dev/null +++ b/backend/internal/service/tool.go @@ -0,0 +1,90 @@ +package service + +import ( + "fmt" + "strings" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// ToolSvc 工具箱注册表:List 登录用户可见(驱动侧边导航),管理端点为 admin +type ToolSvc struct { + fun.Ctx + Store *store.Store +} + +func (s *ToolSvc) List() ([]dto.ToolView, error) { + tools, err := s.Store.ListTools(true) + if err != nil { + return nil, fmt.Errorf("tool: list failed: %w", err) + } + return toolViews(tools), nil +} + +func (s *ToolSvc) AdminList() ([]dto.ToolView, error) { + tools, err := s.Store.ListTools(false) + if err != nil { + return nil, fmt.Errorf("tool: list failed: %w", err) + } + return toolViews(tools), nil +} + +func (s *ToolSvc) SaveTool(d dto.SaveToolDto) (dto.ToolView, error) { + name := strings.TrimSpace(d.Name) + url := strings.TrimSpace(d.Url) + if name == "" || len(name) > 32 { + return dto.ToolView{}, fun.Error(4001, "工具名必填且不超过 32 字符") + } + t := store.Tool{ + Name: name, + Url: url, + Sort: 0, + Enabled: true, + } + if d.Id != nil { + t.Id = *d.Id + } + if d.Icon != nil { + t.Icon = strings.TrimSpace(*d.Icon) + } + if d.Description != nil { + t.Description = strings.TrimSpace(*d.Description) + } + if d.Sort != nil { + t.Sort = *d.Sort + } + if d.Enabled != nil { + t.Enabled = *d.Enabled + } + if err := s.Store.SaveTool(&t); err != nil { + return dto.ToolView{}, fmt.Errorf("tool: save failed: %w", err) + } + views := toolViews([]store.Tool{t}) + return views[0], nil +} + +func (s *ToolSvc) DeleteTool(d dto.DeleteToolDto) (dto.OkView, error) { + if err := s.Store.DeleteTool(d.Id); err != nil { + return dto.OkView{}, fmt.Errorf("tool: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +func toolViews(tools []store.Tool) []dto.ToolView { + views := make([]dto.ToolView, 0, len(tools)) + for _, t := range tools { + views = append(views, dto.ToolView{ + Id: t.Id, + Name: t.Name, + Icon: t.Icon, + Url: t.Url, + Description: t.Description, + Sort: t.Sort, + Enabled: t.Enabled, + }) + } + return views +} diff --git a/backend/internal/service/user.go b/backend/internal/service/user.go new file mode 100644 index 0000000..b61397c --- /dev/null +++ b/backend/internal/service/user.go @@ -0,0 +1,147 @@ +package service + +import ( + "errors" + "fmt" + "strings" + + "github.com/cyi-cc/fun" + "golang.org/x/crypto/bcrypt" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" +) + +// UserSvc 用户管理:全部 policyAdmin, Guard 已保证仅管理员可达 +type UserSvc struct { + fun.Ctx + Store *store.Store +} + +func (s *UserSvc) ListUsers(d dto.ListUsersDto) (dto.UserPageView, error) { + page := max(d.Page, 1) + pageSize := min(max(d.PageSize, 1), 100) + keyword := "" + if d.Keyword != nil { + keyword = strings.TrimSpace(*d.Keyword) + } + users, total, err := s.Store.ListUsers(keyword, page, pageSize) + if err != nil { + return dto.UserPageView{}, fmt.Errorf("user: list failed: %w", err) + } + items := make([]dto.UserView, 0, len(users)) + for _, u := range users { + items = append(items, userView(u)) + } + return dto.UserPageView{Total: total, Items: items}, nil +} + +// SaveUser Id 为空创建(密码必填),非空更新(密码留空不变) +func (s *UserSvc) SaveUser(d dto.SaveUserDto) (dto.UserView, error) { + name := strings.TrimSpace(d.Name) + if name == "" || len(name) > 32 { + return dto.UserView{}, fun.Error(4001, "用户名必填且不超过 32 字符") + } + if d.Role != "admin" && d.Role != "user" { + return dto.UserView{}, fun.Error(4001, "角色无效") + } + + if d.Id == nil { + if d.Password == nil || len(*d.Password) < 6 { + return dto.UserView{}, fun.Error(4001, "密码至少 6 位") + } + if _, err := s.Store.GetUserByName(name); err == nil { + return dto.UserView{}, fun.Error(4001, "用户名已存在") + } else if !errors.Is(err, store.ErrNotFound) { + return dto.UserView{}, err + } + hash, err := bcrypt.GenerateFromPassword([]byte(*d.Password), bcrypt.DefaultCost) + if err != nil { + return dto.UserView{}, err + } + u, err := s.Store.CreateUser(name, string(hash), d.Role) + if err != nil { + return dto.UserView{}, fmt.Errorf("user: create failed: %w", err) + } + return userView(u), nil + } + + u, err := s.Store.GetUserByID(*d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.UserView{}, fun.Error(4001, "用户不存在") + } + if err != nil { + return dto.UserView{}, err + } + if existing, err := s.Store.GetUserByName(name); err == nil && existing.Id != u.Id { + return dto.UserView{}, fun.Error(4001, "用户名已存在") + } + status := u.Status + if d.Status != nil { + if *d.Status != "active" && *d.Status != "disabled" { + return dto.UserView{}, fun.Error(4001, "状态无效") + } + status = *d.Status + } + if err := s.guardLastAdmin(u, d.Role, status); err != nil { + return dto.UserView{}, err + } + var hash *string + if d.Password != nil && *d.Password != "" { + if len(*d.Password) < 6 { + return dto.UserView{}, fun.Error(4001, "密码至少 6 位") + } + h, err := bcrypt.GenerateFromPassword([]byte(*d.Password), bcrypt.DefaultCost) + if err != nil { + return dto.UserView{}, err + } + hs := string(h) + hash = &hs + } + if err := s.Store.UpdateUser(u.Id, name, d.Role, status, hash); err != nil { + return dto.UserView{}, fmt.Errorf("user: update failed: %w", err) + } + u, err = s.Store.GetUserByID(u.Id) + if err != nil { + return dto.UserView{}, err + } + return userView(u), nil +} + +func (s *UserSvc) DeleteUser(d dto.DeleteUserDto) (dto.OkView, error) { + su, err := sessionFrom(s.RequestCtx) + if err != nil { + return dto.OkView{}, err + } + if d.Id == su.User.Id { + return dto.OkView{}, fun.Error(4001, "不能删除当前登录账号") + } + u, err := s.Store.GetUserByID(d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.OkView{}, fun.Error(4001, "用户不存在") + } + if err != nil { + return dto.OkView{}, err + } + if err := s.guardLastAdmin(u, "user", "disabled"); err != nil { + return dto.OkView{}, err + } + if err := s.Store.DeleteUser(d.Id); err != nil { + return dto.OkView{}, fmt.Errorf("user: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +// guardLastAdmin 阻止把最后一个可用管理员降级/禁用/删除 +func (s *UserSvc) guardLastAdmin(u store.User, newRole, newStatus string) error { + if u.Role == "admin" && u.Status == "active" && (newRole != "admin" || newStatus != "active") { + n, err := s.Store.CountAdmins() + if err != nil { + return err + } + if n <= 1 { + return fun.Error(4001, "至少需要保留一个可用管理员") + } + } + return nil +} diff --git a/backend/internal/service/vault.go b/backend/internal/service/vault.go new file mode 100644 index 0000000..7275eba --- /dev/null +++ b/backend/internal/service/vault.go @@ -0,0 +1,188 @@ +package service + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/cyi-cc/fun" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/dto" + "github.com/cyi-cc/cyi-box/backend/internal/store" + "github.com/cyi-cc/cyi-box/backend/internal/vault" +) + +// VaultSvc 密码箱:密文只在服务端出现;列表只回元数据, +// 密码经 Reveal 显式取回,2FA 只回当前验证码不回密钥。 +type VaultSvc struct { + fun.Ctx + Store *store.Store + Cfg *config.Config `fun:"auto"` +} + +func (s *VaultSvc) owner() (store.SessionUser, error) { + return sessionFrom(s.RequestCtx) +} + +func (s *VaultSvc) List() ([]dto.VaultItemView, error) { + su, err := s.owner() + if err != nil { + return nil, err + } + items, err := s.Store.ListVault(su.User.Id, "") + if err != nil { + return nil, fmt.Errorf("vault: list failed: %w", err) + } + views := make([]dto.VaultItemView, 0, len(items)) + for _, e := range items { + views = append(views, vaultView(e)) + } + return views, nil +} + +func (s *VaultSvc) Save(d dto.SaveVaultDto) (dto.VaultItemView, error) { + su, err := s.owner() + if err != nil { + return dto.VaultItemView{}, err + } + title := strings.TrimSpace(d.Title) + if title == "" || len(title) > 64 { + return dto.VaultItemView{}, fun.Error(4001, "标题必填且不超过 64 字符") + } + + e := store.VaultEntry{UserID: su.User.Id, Title: title} + if d.Id != nil { + old, err := s.Store.GetVault(su.User.Id, *d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.VaultItemView{}, fun.Error(4001, "条目不存在") + } + if err != nil { + return dto.VaultItemView{}, err + } + e = old + e.Title = title + } + if d.Username != nil { + e.Username = strings.TrimSpace(*d.Username) + } + if d.Url != nil { + e.Url = strings.TrimSpace(*d.Url) + } + if d.Note != nil { + e.Note = strings.TrimSpace(*d.Note) + } + + if d.Password != nil { + enc, err := vault.Encrypt(s.Cfg.VaultKey, *d.Password) + if err != nil { + return dto.VaultItemView{}, err + } + e.PasswordEnc = enc + } + if d.TotpSecret != nil { + secret := strings.TrimSpace(*d.TotpSecret) + if secret == "" { + e.TotpEnc = "" // 空字符串 = 清除 2FA + } else { + parsed, err := vault.ParseSecret(secret) + if err != nil { + return dto.VaultItemView{}, fun.Error(4001, "2FA 密钥无效:支持 base32 或 otpauth:// 链接") + } + enc, err := vault.Encrypt(s.Cfg.VaultKey, parsed) + if err != nil { + return dto.VaultItemView{}, err + } + e.TotpEnc = enc + } + } + if err := s.Store.SaveVault(&e); err != nil { + return dto.VaultItemView{}, fmt.Errorf("vault: save failed: %w", err) + } + return vaultView(e), nil +} + +// Reveal 显式取回明文密码(前端点「查看」才调用,列表永不携带) +func (s *VaultSvc) Reveal(d dto.VaultIdDto) (dto.SecretView, error) { + su, err := s.owner() + if err != nil { + return dto.SecretView{}, err + } + e, err := s.Store.GetVault(su.User.Id, d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.SecretView{}, fun.Error(4001, "条目不存在") + } + if err != nil { + return dto.SecretView{}, err + } + if e.PasswordEnc == "" { + return dto.SecretView{}, fun.Error(4001, "该条目没有密码") + } + pt, err := vault.Decrypt(s.Cfg.VaultKey, e.PasswordEnc) + if err != nil { + return dto.SecretView{}, fmt.Errorf("vault: decrypt failed: %w", err) + } + return dto.SecretView{Value: pt}, nil +} + +// Totp 返回当前 6 位验证码与剩余秒数;密钥不出服务端 +func (s *VaultSvc) Totp(d dto.VaultIdDto) (dto.TotpView, error) { + su, err := s.owner() + if err != nil { + return dto.TotpView{}, err + } + e, err := s.Store.GetVault(su.User.Id, d.Id) + if errors.Is(err, store.ErrNotFound) { + return dto.TotpView{}, fun.Error(4001, "条目不存在") + } + if err != nil { + return dto.TotpView{}, err + } + if e.TotpEnc == "" { + return dto.TotpView{}, fun.Error(4001, "该条目未设置 2FA") + } + secret, err := vault.Decrypt(s.Cfg.VaultKey, e.TotpEnc) + if err != nil { + return dto.TotpView{}, fmt.Errorf("vault: decrypt totp failed: %w", err) + } + code, left, err := vault.TOTP(secret, time.Now()) + if err != nil { + return dto.TotpView{}, fun.Error(4001, "2FA 密钥无效") + } + return dto.TotpView{Code: code, SecondsLeft: left}, nil +} + +func (s *VaultSvc) Delete(d dto.VaultIdDto) (dto.OkView, error) { + su, err := s.owner() + if err != nil { + return dto.OkView{}, err + } + if err := s.Store.DeleteVault(su.User.Id, d.Id); err != nil { + if errors.Is(err, store.ErrNotFound) { + return dto.OkView{}, fun.Error(4001, "条目不存在") + } + return dto.OkView{}, fmt.Errorf("vault: delete failed: %w", err) + } + return dto.OkView{Message: "已删除"}, nil +} + +func vaultView(e store.VaultEntry) dto.VaultItemView { + v := dto.VaultItemView{ + Id: e.Id, + Title: e.Title, + HasPassword: e.PasswordEnc != "", + HasTotp: e.TotpEnc != "", + UpdatedAt: e.UpdatedAt, + } + if e.Username != "" { + v.Username = &e.Username + } + if e.Url != "" { + v.Url = &e.Url + } + if e.Note != "" { + v.Note = &e.Note + } + return v +} diff --git a/backend/internal/service/webreq.go b/backend/internal/service/webreq.go new file mode 100644 index 0000000..249c710 --- /dev/null +++ b/backend/internal/service/webreq.go @@ -0,0 +1,105 @@ +package service + +import ( + "encoding/json" + "strings" + "time" + + "github.com/cyi-cc/fun" + "github.com/valyala/fasthttp" + + "github.com/cyi-cc/cyi-box/backend/internal/dto" +) + +const ( + webReqTimeout = 30 * time.Second + webRespMaxBody = 2 << 20 // 响应体截断上限 2MB + webReqMaxBody = 1 << 20 // 请求体上限 1MB + webMaxRedirect = 5 + webMaxHeaderPairs = 64 +) + +// fun autowired 只处理导出字段,client 用包级单例复用 +var webClient = &fasthttp.Client{ + Name: "cyi-box-webreq", + MaxConnsPerHost: 16, + ReadTimeout: webReqTimeout, + WriteTimeout: webReqTimeout, + MaxConnWaitTimeout: 5 * time.Second, +} + +// WebSvc HTTP 请求代理(Postman 工具):服务端代发以绕开浏览器 CORS。 +// 只放行 http/https;headersJson 为 {"k":"v"} 对象。 +type WebSvc struct { + fun.Ctx +} + +func (s *WebSvc) Fetch(d dto.WebFetchDto) (dto.WebFetchView, error) { + method := strings.ToUpper(strings.TrimSpace(d.Method)) + switch method { + case "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS": + default: + return dto.WebFetchView{}, fun.Error(4001, "不支持的请求方法: "+method) + } + url := strings.TrimSpace(d.Url) + if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { + return dto.WebFetchView{}, fun.Error(4001, "URL 必须以 http:// 或 https:// 开头") + } + if len(d.Body) > webReqMaxBody { + return dto.WebFetchView{}, fun.Error(4001, "请求体超过 1MB 上限") + } + + var headers map[string]string + if strings.TrimSpace(d.HeadersJson) != "" { + if err := json.Unmarshal([]byte(d.HeadersJson), &headers); err != nil { + return dto.WebFetchView{}, fun.Error(4001, "请求头 JSON 解析失败: "+err.Error()) + } + if len(headers) > webMaxHeaderPairs { + return dto.WebFetchView{}, fun.Error(4001, "请求头数量超过上限") + } + } + + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + req.Header.SetMethod(method) + req.SetRequestURI(url) + for k, v := range headers { + k = strings.TrimSpace(k) + if k == "" || strings.EqualFold(k, "Host") || strings.EqualFold(k, "Content-Length") { + continue + } + req.Header.Set(k, v) + } + if d.Body != "" { + req.SetBodyString(d.Body) + } + + start := time.Now() + err := webClient.DoRedirects(req, resp, webMaxRedirect) + elapsed := time.Since(start).Milliseconds() + if err != nil { + return dto.WebFetchView{}, fun.Error(4002, "请求失败: "+err.Error()) + } + + view := dto.WebFetchView{ + Status: int64(resp.StatusCode()), + ElapsedMs: elapsed, + } + respHeaders := map[string]string{} + resp.Header.VisitAll(func(k, v []byte) { + respHeaders[string(k)] = string(v) + }) + if b, e := json.Marshal(respHeaders); e == nil { + view.HeadersJson = string(b) + } + body := resp.Body() + if len(body) > webRespMaxBody { + body = body[:webRespMaxBody] + view.Truncated = true + } + view.Body = string(body) + return view, nil +} diff --git a/backend/internal/sshx/sshx.go b/backend/internal/sshx/sshx.go new file mode 100644 index 0000000..46284e8 --- /dev/null +++ b/backend/internal/sshx/sshx.go @@ -0,0 +1,295 @@ +// Package sshx SSH 连接与指标采集:Dial 负责建连(密码/私钥), +// CollectMetrics 用一个 shell 脚本两采样算出 CPU% 与网卡速率。 +package sshx + +import ( + "bytes" + "fmt" + "net" + "strconv" + "strings" + "time" + + "golang.org/x/crypto/ssh" +) + +// Dial 建立 SSH 连接。authType: "password" | "key";secret 为明文密码或 PEM 私钥。 +// hostKey 校验用 InsecureIgnoreHostKey——自管工具箱场景,目标机由管理员自己登记。 +func Dial(host string, port int64, username, authType, secret string) (*ssh.Client, error) { + cfg := &ssh.ClientConfig{ + User: username, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Timeout: 8 * time.Second, + } + switch authType { + case "key": + signer, err := ssh.ParsePrivateKey([]byte(secret)) + if err != nil { + return nil, fmt.Errorf("sshx: 私钥无效(暂不支持加密私钥): %w", err) + } + cfg.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} + default: + cfg.Auth = []ssh.AuthMethod{ssh.Password(secret), ssh.KeyboardInteractive(func(_ string, _ string, questions []string, _ []bool) ([]string, error) { + ans := make([]string, len(questions)) + for i := range ans { + ans[i] = secret + } + return ans, nil + })} + } + addr := net.JoinHostPort(host, strconv.FormatInt(port, 10)) + conn, err := net.DialTimeout("tcp", addr, 8*time.Second) + if err != nil { + return nil, fmt.Errorf("sshx: 连接 %s 失败: %w", addr, err) + } + c, chans, reqs, err := ssh.NewClientConn(conn, addr, cfg) + if err != nil { + conn.Close() + return nil, fmt.Errorf("sshx: SSH 握手失败: %w", err) + } + return ssh.NewClient(c, chans, reqs), nil +} + +// Metrics 一次采集的全部指标(字节/百分比原始值,展示层格式化) +type Metrics struct { + Hostname string + OS string + Kernel string + UptimeSeconds int64 + CpuPercent int64 + Load1 string + Load5 string + Load15 string + MemTotal int64 + MemAvail int64 + MemUsed int64 + SwapTotal int64 + SwapUsed int64 + DiskTotal int64 + DiskUsed int64 + DiskAvail int64 + NetRxRate int64 + NetTxRate int64 + NetRxTotal int64 + NetTxTotal int64 + Disks []DiskMount +} + +type DiskMount struct { + Mount string + Fs string + Total int64 + Used int64 + Avail int64 + Percent int64 +} + +// 采集脚本:两帧 /proc/stat + /proc/net/dev 中间 sleep 0.5s 算速率, +// 其余一次性读取;==NAME== 段落标记用于切分解析 +const metricsScript = ` +echo ==CPU1==; grep '^cpu ' /proc/stat +echo ==NET1==; cat /proc/net/dev 2>/dev/null +sleep 0.5 +echo ==CPU2==; grep '^cpu ' /proc/stat +echo ==NET2==; cat /proc/net/dev 2>/dev/null +echo ==MEM==; grep -E '^(MemTotal|MemAvailable|MemFree|SwapTotal|SwapFree)' /proc/meminfo +echo ==DISKS==; df -B1 -x tmpfs -x devtmpfs -x squashfs -x overlay -x efivarfs 2>/dev/null | tail -n +2 +echo ==LOAD==; cat /proc/loadavg 2>/dev/null +echo ==UP==; cat /proc/uptime 2>/dev/null +echo ==OS==; (. /etc/os-release 2>/dev/null; echo "$PRETTY_NAME"); uname -r +echo ==HOST==; hostname +` + +// CollectMetrics 跑采集脚本并解析;整体超时 20s +func CollectMetrics(c *ssh.Client) (Metrics, error) { + type result struct { + out []byte + err error + } + ch := make(chan result, 1) + go func() { + sess, err := c.NewSession() + if err != nil { + ch <- result{nil, fmt.Errorf("sshx: 建立会话失败: %w", err)} + return + } + defer sess.Close() + // 脚本走 stdin 喂给 sh -s:内容含单引号,拼进 sh -c '...' 会被顶断(exit 2) + sess.Stdin = strings.NewReader(metricsScript) + out, err := sess.Output("sh -s") + ch <- result{out, err} + }() + var out []byte + select { + case r := <-ch: + if r.err != nil { + return Metrics{}, fmt.Errorf("sshx: 采集命令失败: %w", r.err) + } + out = r.out + case <-time.After(20 * time.Second): + return Metrics{}, fmt.Errorf("sshx: 采集超时") + } + return parseMetrics(string(out)), nil +} + +func parseMetrics(text string) Metrics { + sections := map[string]string{} + cur := "" + for _, line := range strings.Split(text, "\n") { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, "==") && strings.HasSuffix(trimmed, "==") { + cur = strings.Trim(trimmed, "=") + continue + } + if cur != "" { + sections[cur] += line + "\n" + } + } + + m := Metrics{Disks: []DiskMount{}} + c1, c2 := cpuFields(sections["CPU1"]), cpuFields(sections["CPU2"]) + if len(c1) >= 8 && len(c2) >= 8 { + m.CpuPercent = cpuPercent(c1, c2) + } + n1, n2 := netTotals(sections["NET1"]), netTotals(sections["NET2"]) + // 脚本固定 sleep 0.5s → 速率 = 差值 × 2 + m.NetRxRate = max64(n2[0]-n1[0], 0) * 2 + m.NetTxRate = max64(n2[1]-n1[1], 0) * 2 + m.NetRxTotal, m.NetTxTotal = n2[0], n2[1] + parseMem(&m, sections["MEM"]) + parseDisks(&m, sections["DISKS"]) + if l := strings.Fields(sections["LOAD"]); len(l) >= 3 { + m.Load1, m.Load5, m.Load15 = l[0], l[1], l[2] + } + if f := strings.Fields(sections["UP"]); len(f) > 0 { + if v, err := strconv.ParseFloat(f[0], 64); err == nil { + m.UptimeSeconds = int64(v) + } + } + if lines := strings.Split(strings.TrimSpace(sections["OS"]), "\n"); len(lines) > 0 { + m.OS = strings.TrimSpace(lines[0]) + if len(lines) > 1 { + m.Kernel = strings.TrimSpace(lines[1]) + } + } + m.Hostname = strings.TrimSpace(sections["HOST"]) + return m +} + +// cpuFields /proc/stat 的 cpu 行 → [user nice system idle iowait irq softirq steal] +func cpuFields(s string) []int64 { + for _, line := range strings.Split(s, "\n") { + if !strings.HasPrefix(strings.TrimSpace(line), "cpu ") { + continue + } + f := strings.Fields(line)[1:] + vals := make([]int64, len(f)) + for i, v := range f { + vals[i], _ = strconv.ParseInt(v, 10, 64) + } + return vals + } + return nil +} + +func cpuPercent(a, b []int64) int64 { + total := func(v []int64) int64 { + var s int64 + for _, x := range v { + s += x + } + return s + } + idle := func(v []int64) int64 { return v[3] + v[4] } // idle + iowait + dt, di := total(b)-total(a), idle(b)-idle(a) + if dt <= 0 { + return 0 + } + return (dt - di) * 100 / dt +} + +// netTotals /proc/net/dev → [rx_bytes合计, tx_bytes合计](跳过 lo) +func netTotals(s string) [2]int64 { + var rx, tx int64 + for _, line := range strings.Split(s, "\n") { + i := strings.Index(line, ":") + if i < 0 { + continue + } + if strings.TrimSpace(line[:i]) == "lo" { + continue + } + f := strings.Fields(line[i+1:]) + if len(f) < 9 { + continue + } + r, _ := strconv.ParseInt(f[0], 10, 64) + t, _ := strconv.ParseInt(f[8], 10, 64) + rx, tx = rx+r, tx+t + } + return [2]int64{rx, tx} +} + +func parseMem(m *Metrics, s string) { + kv := map[string]int64{} + for _, line := range strings.Split(s, "\n") { + f := strings.Fields(line) + if len(f) >= 2 { + v, _ := strconv.ParseInt(f[1], 10, 64) + kv[strings.TrimSuffix(f[0], ":")] = v * 1024 // kB → byte + } + } + m.MemTotal = kv["MemTotal"] + m.MemAvail = kv["MemAvailable"] + if m.MemAvail == 0 { + m.MemAvail = kv["MemFree"] + } + m.MemUsed = max64(m.MemTotal-m.MemAvail, 0) + m.SwapTotal = kv["SwapTotal"] + m.SwapUsed = m.SwapTotal - kv["SwapFree"] +} + +func parseDisks(m *Metrics, s string) { + for _, line := range strings.Split(s, "\n") { + f := strings.Fields(line) + if len(f) < 6 { + continue + } + d := DiskMount{Fs: f[0], Mount: f[5]} + d.Total, _ = strconv.ParseInt(f[1], 10, 64) + d.Used, _ = strconv.ParseInt(f[2], 10, 64) + d.Avail, _ = strconv.ParseInt(f[3], 10, 64) + d.Percent, _ = strconv.ParseInt(strings.TrimSuffix(f[4], "%"), 10, 64) + m.Disks = append(m.Disks, d) + if d.Mount == "/" { + m.DiskTotal, m.DiskUsed, m.DiskAvail = d.Total, d.Used, d.Avail + } + } + // 没有独立 / 挂载(罕见)时取第一块盘作概览 + if m.DiskTotal == 0 && len(m.Disks) > 0 { + m.DiskTotal, m.DiskUsed, m.DiskAvail = m.Disks[0].Total, m.Disks[0].Used, m.Disks[0].Avail + } +} + +func max64(a, b int64) int64 { + if a > b { + return a + } + return b +} + +// RunScript 终端之外保留:跑一次命令返回输出(SFTP 预热/调试用) +func RunScript(c *ssh.Client, script string) ([]byte, error) { + sess, err := c.NewSession() + if err != nil { + return nil, err + } + defer sess.Close() + var buf bytes.Buffer + sess.Stdout = &buf + sess.Stderr = &buf + if err := sess.Run(script); err != nil { + return buf.Bytes(), err + } + return buf.Bytes(), nil +} diff --git a/backend/internal/store/bookmark.go b/backend/internal/store/bookmark.go new file mode 100644 index 0000000..042f589 --- /dev/null +++ b/backend/internal/store/bookmark.go @@ -0,0 +1,73 @@ +package store + +import ( + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +type Bookmark struct { + Id int64 + UserID int64 + Title string + Url string + Icon string + Sort int64 + CreatedAt int64 +} + +func bookmarkFromRow(b db.Bookmark) Bookmark { + return Bookmark{ + Id: b.ID, UserID: b.UserID, Title: b.Title, Url: b.Url, + Icon: b.Icon, Sort: b.Sort, CreatedAt: b.CreatedAt, + } +} + +func (s *Store) ListBookmarks(userID int64) ([]Bookmark, error) { + rows, err := s.q.ListBookmarks(storeCtx, userID) + if err != nil { + return nil, err + } + items := make([]Bookmark, 0, len(rows)) + for _, r := range rows { + items = append(items, bookmarkFromRow(r)) + } + return items, nil +} + +func (s *Store) SaveBookmark(b *Bookmark) error { + if b.Id > 0 { + n, err := s.q.UpdateBookmark(storeCtx, db.UpdateBookmarkParams{ + Title: b.Title, Url: b.Url, Icon: b.Icon, Sort: b.Sort, + ID: b.Id, UserID: b.UserID, + }) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil + } + now := time.Now().Unix() + id, err := s.q.InsertBookmark(storeCtx, db.InsertBookmarkParams{ + UserID: b.UserID, Title: b.Title, Url: b.Url, Icon: b.Icon, + Sort: b.Sort, CreatedAt: now, + }) + if err != nil { + return err + } + b.Id, b.CreatedAt = id, now + return nil +} + +func (s *Store) DeleteBookmark(userID, id int64) error { + n, err := s.q.DeleteBookmark(storeCtx, db.DeleteBookmarkParams{UserID: userID, ID: id}) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil +} diff --git a/backend/internal/store/dbconn.go b/backend/internal/store/dbconn.go new file mode 100644 index 0000000..094d6e8 --- /dev/null +++ b/backend/internal/store/dbconn.go @@ -0,0 +1,91 @@ +package store + +import ( + "database/sql" + "errors" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// Dbconn 数据库连接条目:password_enc 为 AES-GCM 密文;sqlite 时 database 存文件路径 +type Dbconn struct { + Id int64 + Name string + Engine string // mysql | postgres | sqlite + Host string + Port int64 + Username string + PasswordEnc string + Database string + Params string // DSN 附加参数,如 mysql 的 charset、pg 的 sslmode + CreatedAt int64 + UpdatedAt int64 +} + +func dbconnFromRow(e db.Dbconn) Dbconn { + return Dbconn{ + Id: e.ID, Name: e.Name, Engine: e.Engine, Host: e.Host, Port: e.Port, + Username: e.Username, PasswordEnc: e.PasswordEnc, Database: e.Database, + Params: e.Params, CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, + } +} + +func (s *Store) ListDbconns() ([]Dbconn, error) { + rows, err := s.q.ListDbconns(storeCtx) + if err != nil { + return nil, err + } + items := make([]Dbconn, 0, len(rows)) + for _, r := range rows { + items = append(items, dbconnFromRow(r)) + } + return items, nil +} + +func (s *Store) GetDbconn(id int64) (Dbconn, error) { + e, err := s.q.GetDbconn(storeCtx, id) + if errors.Is(err, sql.ErrNoRows) { + return Dbconn{}, ErrNotFound + } + return dbconnFromRow(e), err +} + +func (s *Store) SaveDbconn(e *Dbconn) error { + now := time.Now().Unix() + if e.Id > 0 { + n, err := s.q.UpdateDbconn(storeCtx, db.UpdateDbconnParams{ + Name: e.Name, Engine: e.Engine, Host: e.Host, Port: e.Port, + Username: e.Username, PasswordEnc: e.PasswordEnc, + Database: e.Database, Params: e.Params, UpdatedAt: now, ID: e.Id, + }) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil + } + id, err := s.q.InsertDbconn(storeCtx, db.InsertDbconnParams{ + Name: e.Name, Engine: e.Engine, Host: e.Host, Port: e.Port, + Username: e.Username, PasswordEnc: e.PasswordEnc, + Database: e.Database, Params: e.Params, CreatedAt: now, UpdatedAt: now, + }) + if err != nil { + return err + } + e.Id, e.CreatedAt, e.UpdatedAt = id, now, now + return nil +} + +func (s *Store) DeleteDbconn(id int64) error { + n, err := s.q.DeleteDbconn(storeCtx, id) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil +} diff --git a/backend/internal/store/disk.go b/backend/internal/store/disk.go new file mode 100644 index 0000000..0083567 --- /dev/null +++ b/backend/internal/store/disk.go @@ -0,0 +1,148 @@ +package store + +import ( + "database/sql" + "errors" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// ---- disk 网盘 ---- + +type File struct { + Id int64 + UserID int64 + Name string // 原始文件名 + Stored string // 落盘文件名(随机) + Size int64 + Mime string + CreatedAt int64 +} + +type Share struct { + Id int64 + Code string + FileID int64 + UserID int64 + FileName string + ExpiresAt int64 // 0 = 永久 + Downloads int64 + CreatedAt int64 +} + +func fileFromRow(f db.File) File { + return File{ + Id: f.ID, UserID: f.UserID, Name: f.Name, Stored: f.Stored, + Size: f.Size, Mime: f.Mime, CreatedAt: f.CreatedAt, + } +} + +func shareFromRow(s db.Share) Share { + return Share{ + Id: s.ID, Code: s.Code, FileID: s.FileID, UserID: s.UserID, + FileName: s.FileName, ExpiresAt: s.ExpiresAt, + Downloads: s.Downloads, CreatedAt: s.CreatedAt, + } +} + +func (s *Store) InsertFile(f *File) error { + id, err := s.q.InsertFile(storeCtx, db.InsertFileParams{ + UserID: f.UserID, Name: f.Name, Stored: f.Stored, + Size: f.Size, Mime: f.Mime, CreatedAt: time.Now().Unix(), + }) + if err != nil { + return err + } + f.Id = id + return nil +} + +func (s *Store) ListFiles(userID int64) ([]File, error) { + rows, err := s.q.ListFiles(storeCtx, userID) + if err != nil { + return nil, err + } + files := make([]File, 0, len(rows)) + for _, r := range rows { + files = append(files, fileFromRow(r)) + } + return files, nil +} + +func (s *Store) GetFile(id int64) (File, error) { + f, err := s.q.GetFile(storeCtx, id) + if errors.Is(err, sql.ErrNoRows) { + return File{}, ErrNotFound + } + return fileFromRow(f), err +} + +// DeleteFile 仅限属主;返回被删行数(0 = 不存在或非属主) +func (s *Store) DeleteFile(id, userID int64) (int64, error) { + return s.q.DeleteFile(storeCtx, db.DeleteFileParams{ID: id, UserID: userID}) +} + +func (s *Store) InsertShare(sh *Share) error { + sh.CreatedAt = time.Now().Unix() + id, err := s.q.InsertShare(storeCtx, db.InsertShareParams{ + Code: sh.Code, FileID: sh.FileID, UserID: sh.UserID, + FileName: sh.FileName, ExpiresAt: sh.ExpiresAt, + CreatedAt: sh.CreatedAt, + }) + if err != nil { + return err + } + sh.Id = id + return nil +} + +func (s *Store) ListShares(userID int64) ([]Share, error) { + rows, err := s.q.ListShares(storeCtx, userID) + if err != nil { + return nil, err + } + shares := make([]Share, 0, len(rows)) + for _, r := range rows { + shares = append(shares, shareFromRow(r)) + } + return shares, nil +} + +// ShareFileView 公开下载需要的联表视图 +type ShareFileView struct { + Share + Stored string + Size int64 + Mime string +} + +func (s *Store) GetShareFile(code string) (ShareFileView, error) { + r, err := s.q.GetShareByCode(storeCtx, code) + if errors.Is(err, sql.ErrNoRows) { + return ShareFileView{}, ErrNotFound + } + if err != nil { + return ShareFileView{}, err + } + return ShareFileView{ + Share: Share{ + Id: r.ID, Code: r.Code, FileID: r.FileID, UserID: r.UserID, + FileName: r.FileName, ExpiresAt: r.ExpiresAt, + Downloads: r.Downloads, CreatedAt: r.CreatedAt, + }, + Stored: r.Stored, Size: r.Size, Mime: r.Mime, + }, nil +} + +func (s *Store) DeleteShare(id, userID int64) (int64, error) { + return s.q.DeleteShare(storeCtx, db.DeleteShareParams{ID: id, UserID: userID}) +} + +func (s *Store) DeleteSharesByFile(fileID int64) error { + return s.q.DeleteSharesByFile(storeCtx, fileID) +} + +func (s *Store) IncShareDownloads(code string) error { + return s.q.IncShareDownloads(storeCtx, code) +} diff --git a/backend/internal/store/mem.go b/backend/internal/store/mem.go new file mode 100644 index 0000000..fec9d4d --- /dev/null +++ b/backend/internal/store/mem.go @@ -0,0 +1,272 @@ +// mem.go 共享记忆库门面:key 形态 "/",写覆盖即 revision+1。 +// 同一套数据同时服务后台管理页(MemorySvc)与对外 MCP 协议端点(internal/memx)。 +package store + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "database/sql" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// memFTSDDL 全文索引:外挂内容表(不复制数据)+ 增删改触发器同步。 +// trigram 分词器:中英文都能做子串匹配(代价是 <3 字符的词无法索引,搜索侧兜底) +const memFTSDDL = ` +CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5( + k, content, project, tags, + content='memories', content_rowid='rowid', + tokenize='trigram' +); +CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN + INSERT INTO memories_fts(rowid, k, content, project, tags) + VALUES (new.rowid, new.k, new.content, new.project, new.tags); +END; +CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN + INSERT INTO memories_fts(memories_fts, rowid, k, content, project, tags) + VALUES ('delete', old.rowid, old.k, old.content, old.project, old.tags); +END; +CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN + INSERT INTO memories_fts(memories_fts, rowid, k, content, project, tags) + VALUES ('delete', old.rowid, old.k, old.content, old.project, old.tags); + INSERT INTO memories_fts(rowid, k, content, project, tags) + VALUES (new.rowid, new.k, new.content, new.project, new.tags); +END;` + +// Memory 一条持久记忆;Tags 经 JSON 数组落库 +type Memory struct { + Key string + Content string + Project string + Tags []string + WrittenBy string + Revision int64 + CreatedAt int64 + UpdatedAt int64 +} + +func memoryFromRow(r db.Memory) Memory { + m := Memory{ + Key: r.K, Content: r.Content, Project: r.Project, WrittenBy: r.WrittenBy, + Revision: r.Revision, CreatedAt: r.CreatedAt, UpdatedAt: r.UpdatedAt, + } + _ = json.Unmarshal([]byte(r.Tags), &m.Tags) + if m.Tags == nil { + m.Tags = []string{} + } + return m +} + +// SaveMemory 覆盖式写入:同 key 保留 created_at、revision 自增 +func (s *Store) SaveMemory(m *Memory) error { + now := time.Now().Unix() + tags, _ := json.Marshal(m.Tags) + if err := s.q.UpsertMemory(storeCtx, db.UpsertMemoryParams{ + K: m.Key, Content: m.Content, Project: m.Project, Tags: string(tags), + WrittenBy: m.WrittenBy, CreatedAt: now, UpdatedAt: now, + }); err != nil { + return err + } + row, err := s.q.GetMemory(storeCtx, m.Key) + if err != nil { + return err + } + m.Revision, m.CreatedAt, m.UpdatedAt = row.Revision, row.CreatedAt, row.UpdatedAt + return nil +} + +func (s *Store) GetMemory(key string) (*Memory, error) { + row, err := s.q.GetMemory(storeCtx, key) + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + if err != nil { + return nil, err + } + m := memoryFromRow(row) + return &m, nil +} + +func (s *Store) DeleteMemory(key string) (bool, error) { + n, err := s.q.DeleteMemory(storeCtx, key) + return n > 0, err +} + +// ListMemories project/tag 可空过滤,limit<=0 取默认 50,上限 500 +func (s *Store) ListMemories(project, tag string, limit int64) ([]Memory, error) { + if limit <= 0 { + limit = 50 + } + if limit > 500 { + limit = 500 + } + rows, err := s.q.ListMemories(storeCtx, db.ListMemoriesParams{ + Column1: project, Project: project, + Column3: tag, Column4: optStr(tag), Limit: limit, + }) + if err != nil { + return nil, err + } + out := make([]Memory, 0, len(rows)) + for _, r := range rows { + out = append(out, memoryFromRow(r)) + } + return out, nil +} + +// SearchMemories 每个空白分隔的词都要命中(AND 语义)。 +// ≥3 字符的词走 FTS5 trigram 索引(按相关度排序);出现短词或索引异常时退回内存 LIKE。 +func (s *Store) SearchMemories(query, project string, limit int64) ([]Memory, error) { + terms := strings.Fields(query) + if len(terms) == 0 { + return s.ListMemories(project, "", limit) + } + if limit <= 0 { + limit = 50 + } + canFTS := true + for _, t := range terms { + if len([]rune(t)) < 3 { + canFTS = false + break + } + } + if canFTS { + if ms, err := s.searchMemoriesFTS(terms, project, limit); err == nil { + return ms, nil + } + } + return s.searchMemoriesLike(terms, project, limit) +} + +// searchMemoriesFTS 索引命中:词项加引号消除 MATCH 语法注入面 +func (s *Store) searchMemoriesFTS(terms []string, project string, limit int64) ([]Memory, error) { + var b strings.Builder + for i, t := range terms { + if i > 0 { + b.WriteString(" AND ") + } + b.WriteString(`"` + strings.ReplaceAll(t, `"`, `""`) + `"`) + } + rows, err := s.DB.Query(`SELECT m.k, m.content, m.project, m.tags, m.written_by, m.revision, m.created_at, m.updated_at + FROM memories_fts f JOIN memories m ON m.rowid = f.rowid + WHERE f MATCH ? AND (? = '' OR m.project = ?) + ORDER BY f.rank LIMIT ?`, b.String(), project, project, limit) + if err != nil { + return nil, err + } + defer rows.Close() + var out []Memory + for rows.Next() { + var m Memory + var tags string + if err := rows.Scan(&m.Key, &m.Content, &m.Project, &tags, &m.WrittenBy, &m.Revision, &m.CreatedAt, &m.UpdatedAt); err != nil { + return nil, err + } + _ = json.Unmarshal([]byte(tags), &m.Tags) + if m.Tags == nil { + m.Tags = []string{} + } + out = append(out, m) + } + return out, rows.Err() +} + +func (s *Store) searchMemoriesLike(terms []string, project string, limit int64) ([]Memory, error) { + all, err := s.ListMemories(project, "", 500) + if err != nil { + return nil, err + } + var out []Memory + for _, m := range all { + hay := strings.ToLower(m.Key + "\n" + m.Project + "\n" + m.Content) + ok := true + for _, t := range terms { + if !strings.Contains(hay, strings.ToLower(t)) { + ok = false + break + } + } + if ok { + out = append(out, m) + } + if int64(len(out)) >= limit { + break + } + } + return out, nil +} + +type MemoryProject struct { + Name string + Count int64 +} + +func (s *Store) MemoryProjects() ([]MemoryProject, error) { + rows, err := s.q.MemoryProjects(storeCtx) + if err != nil { + return nil, err + } + out := make([]MemoryProject, 0, len(rows)) + for _, r := range rows { + out = append(out, MemoryProject{Name: r.Name, Count: r.Cnt}) + } + return out, nil +} + +const mcpTokenKey = "mcp.token" + +// McpToken 读取对外 MCP 端点的 Bearer 令牌;不存在则生成一个并落库 +func (s *Store) McpToken() (string, error) { + if v, err := s.GetSetting(mcpTokenKey); err == nil && v != "" { + return v, nil + } + b := make([]byte, 24) + if _, err := rand.Read(b); err != nil { + return "", fmt.Errorf("store: gen mcp token failed: %w", err) + } + tok := hex.EncodeToString(b) + if err := s.SetSetting(mcpTokenKey, tok); err != nil { + return "", err + } + return tok, nil +} + +// SignMemoryReadURL 签发 /m/ 阅读链接:?sig=HMAC(token, "read:"+key)[:24] +// 与 MCP 共用同一令牌——重置令牌即吊销全部已发链接 +func (s *Store) SignMemoryReadURL(base, key string) (string, error) { + tok, err := s.McpToken() + if err != nil { + return "", err + } + return base + "/m/" + key + "?sig=" + memReadSig(tok, key), nil +} + +// CheckMemoryReadSig 校验阅读链接签名 +func (s *Store) CheckMemoryReadSig(key, sig string) bool { + tok, err := s.McpToken() + if err != nil || sig == "" { + return false + } + return hmac.Equal([]byte(sig), []byte(memReadSig(tok, key))) +} + +func memReadSig(token, key string) string { + h := hmac.New(sha256.New, []byte(token)) + h.Write([]byte("read:" + key)) + return hex.EncodeToString(h.Sum(nil))[:24] +} + +func (s *Store) ResetMcpToken() (string, error) { + if err := s.SetSetting(mcpTokenKey, ""); err != nil { + return "", err + } + return s.McpToken() +} diff --git a/backend/internal/store/proxy.go b/backend/internal/store/proxy.go new file mode 100644 index 0000000..468932e --- /dev/null +++ b/backend/internal/store/proxy.go @@ -0,0 +1,362 @@ +package store + +import ( + "database/sql" + "errors" + "strconv" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// ---- proxies ---- + +type ProxyRegion struct { + Id int64 + Code string + Name string + ZhName string + Region string + Slug string + AliveCount int64 +} + +type Proxy struct { + Id int64 + Ip string + Port int64 + Protocols string + Anonymity string + Source string + RegionID int64 + RegionCode string + RegionName string + RegionZhName string + Latency int64 + Speed int64 + Uptime int64 + Alive bool + FailCount int64 + LastCheckedAt int64 + LastAliveAt int64 + LastSeenAt int64 + CreatedAt int64 +} + +type ProxySync struct { + Id int64 + StartedAt int64 + FinishedAt int64 + Status string + Source string + Total int64 + Added int64 + Removed int64 + Updated int64 + Detail string + Error string +} + +type ProxyFilter struct { + RegionID int64 + Protocol string + Keyword string + Alive int64 + Page int64 + PageSize int64 +} + +// UpsertRegion returns the region id for code, creating/updating the row. +func (s *Store) UpsertRegion(code, name, zhName, region, slug string) (int64, error) { + if err := s.q.UpsertProxyRegion(storeCtx, db.UpsertProxyRegionParams{ + Code: code, Name: name, ZhName: zhName, Region: region, Slug: slug, + }); err != nil { + return 0, err + } + r, err := s.q.GetProxyRegionByCode(storeCtx, code) + if err != nil { + return 0, err + } + return r.ID, nil +} + +func (s *Store) ListRegions() ([]ProxyRegion, error) { + rows, err := s.q.ListProxyRegions(storeCtx) + if err != nil { + return nil, err + } + out := make([]ProxyRegion, 0, len(rows)) + for _, r := range rows { + out = append(out, ProxyRegion{ + Id: r.ID, Code: r.Code, Name: r.Name, ZhName: r.ZhName, + Region: r.Region, Slug: r.Slug, AliveCount: r.AliveCount, + }) + } + return out, nil +} + +// ProxyKey 同步 diff 用的键信息:来源决定「消失即删」的作用域 +type ProxyKey struct { + Id int64 + Source string +} + +// ProxyKeys maps "ip:port" to ProxyKey, used by sync diffing. +func (s *Store) ProxyKeys() (map[string]ProxyKey, error) { + rows, err := s.q.ListProxyKeys(storeCtx) + if err != nil { + return nil, err + } + m := make(map[string]ProxyKey, len(rows)) + for _, r := range rows { + m[r.Ip+":"+strconv.FormatInt(r.Port, 10)] = ProxyKey{Id: r.ID, Source: r.Source} + } + return m, nil +} + +func (s *Store) InsertProxy(p *Proxy) error { + id, err := s.q.InsertProxy(storeCtx, db.InsertProxyParams{ + Ip: p.Ip, Port: p.Port, Protocols: p.Protocols, Anonymity: p.Anonymity, + Source: p.Source, + RegionID: p.RegionID, Latency: p.Latency, Speed: p.Speed, Uptime: p.Uptime, + LastSeenAt: p.LastSeenAt, CreatedAt: p.CreatedAt, + }) + if err != nil { + return err + } + p.Id = id + return nil +} + +func (s *Store) UpdateProxySeen(id int64, p *Proxy) error { + return s.q.UpdateProxySeen(storeCtx, db.UpdateProxySeenParams{ + Protocols: p.Protocols, Anonymity: p.Anonymity, RegionID: p.RegionID, + Latency: p.Latency, Speed: p.Speed, Uptime: p.Uptime, + LastSeenAt: p.LastSeenAt, ID: id, + }) +} + +func (s *Store) DeleteProxy(id int64) error { + return s.q.DeleteProxy(storeCtx, id) +} + +type DueProxy struct { + Id int64 + Ip string + Port int64 + Protocols string + FailCount int64 +} + +func (s *Store) DueProxies(checkedBefore int64) ([]DueProxy, error) { + rows, err := s.q.ListProxiesDue(storeCtx, checkedBefore) + if err != nil { + return nil, err + } + out := make([]DueProxy, 0, len(rows)) + for _, r := range rows { + out = append(out, DueProxy{Id: r.ID, Ip: r.Ip, Port: r.Port, Protocols: r.Protocols, FailCount: r.FailCount}) + } + return out, nil +} + +func (s *Store) MarkProxyAlive(id, checkedAt, aliveAt, latency int64) error { + return s.q.MarkProxyAlive(storeCtx, db.MarkProxyAliveParams{ + LastCheckedAt: checkedAt, LastAliveAt: aliveAt, Latency: latency, ID: id, + }) +} + +func (s *Store) MarkProxyFailed(id, checkedAt, failCount int64) error { + return s.q.MarkProxyFailed(storeCtx, db.MarkProxyFailedParams{ + LastCheckedAt: checkedAt, FailCount: failCount, ID: id, + }) +} + +func (s *Store) PurgeDeadProxies(before int64) (int64, error) { + return s.q.DeleteDeadProxies(storeCtx, before) +} + +func (s *Store) ListProxies(f ProxyFilter) ([]Proxy, int64, error) { + if f.Page < 1 { + f.Page = 1 + } + if f.PageSize <= 0 || f.PageSize > 200 { + f.PageSize = 50 + } + total, err := s.q.CountProxiesFiltered(storeCtx, db.CountProxiesFilteredParams{ + Column1: f.RegionID, RegionID: f.RegionID, + Column3: f.Protocol, Column4: optStr(f.Protocol), + Column5: f.Keyword, Column6: optStr(f.Keyword), + Column7: f.Alive, Alive: f.Alive, + }) + if err != nil { + return nil, 0, err + } + rows, err := s.q.ListProxiesFiltered(storeCtx, db.ListProxiesFilteredParams{ + Column1: f.RegionID, RegionID: f.RegionID, + Column3: f.Protocol, Column4: optStr(f.Protocol), + Column5: f.Keyword, Column6: optStr(f.Keyword), + Column7: f.Alive, Alive: f.Alive, + Limit: f.PageSize, Offset: (f.Page - 1) * f.PageSize, + }) + if err != nil { + return nil, 0, err + } + out := make([]Proxy, 0, len(rows)) + for _, r := range rows { + out = append(out, Proxy{ + Id: r.ID, Ip: r.Ip, Port: r.Port, Protocols: r.Protocols, Anonymity: r.Anonymity, + RegionID: r.RegionID, RegionCode: r.RegionCode, RegionName: r.RegionName, + RegionZhName: r.RegionZhName, Latency: r.Latency, Speed: r.Speed, Uptime: r.Uptime, + Alive: r.Alive == 1, FailCount: r.FailCount, LastCheckedAt: r.LastCheckedAt, + LastAliveAt: r.LastAliveAt, LastSeenAt: r.LastSeenAt, CreatedAt: r.CreatedAt, + }) + } + return out, total, nil +} + +func (s *Store) CountAliveProxies() (int64, error) { + return s.q.CountProxies(storeCtx) +} + +func (s *Store) LastProxyCheckAt() int64 { + v, err := s.q.LastProxyCheckAt(storeCtx) + if err != nil { + return 0 + } + if n, ok := v.(int64); ok { + return n + } + return 0 +} + +func (s *Store) BeginProxySync(source string, startedAt int64) (int64, error) { + return s.q.InsertProxySync(storeCtx, db.InsertProxySyncParams{ + StartedAt: startedAt, Source: source, + }) +} + +func (s *Store) FinishProxySync(sync *ProxySync) error { + return s.q.FinishProxySync(storeCtx, db.FinishProxySyncParams{ + FinishedAt: sync.FinishedAt, Status: sync.Status, Total: sync.Total, + Added: sync.Added, Removed: sync.Removed, Updated: sync.Updated, + Detail: sync.Detail, Error: sync.Error, ID: sync.Id, + }) +} + +func (s *Store) ListProxySyncs(limit int64) ([]ProxySync, error) { + if limit <= 0 { + limit = 20 + } + rows, err := s.q.ListProxySyncs(storeCtx, limit) + if err != nil { + return nil, err + } + out := make([]ProxySync, 0, len(rows)) + for _, r := range rows { + out = append(out, ProxySync{ + Id: r.ID, StartedAt: r.StartedAt, FinishedAt: r.FinishedAt, Status: r.Status, + Source: r.Source, Total: r.Total, Added: r.Added, Removed: r.Removed, + Updated: r.Updated, Detail: r.Detail, Error: r.Error, + }) + } + return out, nil +} + +// ---- 提取密钥 ---- + +type ProxyApiKey struct { + Id int64 + Key string + Name string + RegionID int64 + RegionCode string + RegionZhName string + Enabled bool + UsedCount int64 + LastUsedAt int64 + CreatedAt int64 +} + +func (s *Store) InsertProxyApiKey(key, name string, regionID int64) (int64, error) { + return s.q.InsertProxyApiKey(storeCtx, db.InsertProxyApiKeyParams{ + Kkey: key, Name: name, RegionID: regionID, CreatedAt: nowUnix(), + }) +} + +func (s *Store) ListProxyApiKeys() ([]ProxyApiKey, error) { + rows, err := s.q.ListProxyApiKeys(storeCtx) + if err != nil { + return nil, err + } + out := make([]ProxyApiKey, 0, len(rows)) + for _, r := range rows { + out = append(out, ProxyApiKey{ + Id: r.ID, Key: r.Kkey, Name: r.Name, RegionID: r.RegionID, + RegionCode: r.RegionCode.String, RegionZhName: r.RegionZhName.String, + Enabled: r.Enabled == 1, UsedCount: r.UsedCount, + LastUsedAt: r.LastUsedAt, CreatedAt: r.CreatedAt, + }) + } + return out, nil +} + +func (s *Store) GetProxyApiKey(key string) (ProxyApiKey, error) { + r, err := s.q.GetProxyApiKey(storeCtx, key) + if errors.Is(err, sql.ErrNoRows) { + return ProxyApiKey{}, ErrNotFound + } + if err != nil { + return ProxyApiKey{}, err + } + return ProxyApiKey{ + Id: r.ID, Key: r.Kkey, Name: r.Name, RegionID: r.RegionID, + Enabled: r.Enabled == 1, UsedCount: r.UsedCount, + LastUsedAt: r.LastUsedAt, CreatedAt: r.CreatedAt, + }, nil +} + +func (s *Store) TouchProxyApiKey(id int64) { + _ = s.q.TouchProxyApiKey(storeCtx, db.TouchProxyApiKeyParams{ + LastUsedAt: nowUnix(), ID: id, + }) +} + +func (s *Store) DeleteProxyApiKey(id int64) error { + _, err := s.q.DeleteProxyApiKey(storeCtx, id) + return err +} + +func nowUnix() int64 { return time.Now().Unix() } + +type RandomProxy struct { + Id int64 + Ip string + Port int64 + Protocols string + FailCount int64 + RegionCode string + RegionZhName string +} + +// RandomProxies 随机取存活代理:regionID=0 全部地区,code 可按国家码二次过滤 +func (s *Store) RandomProxies(regionID int64, protocol, code string, limit int64) ([]RandomProxy, error) { + rows, err := s.q.RandomProxies(storeCtx, db.RandomProxiesParams{ + Column1: regionID, RegionID: regionID, + Column3: protocol, Column4: optStr(protocol), + Column5: code, Code: code, + Limit: limit, + }) + if err != nil { + return nil, err + } + out := make([]RandomProxy, 0, len(rows)) + for _, r := range rows { + out = append(out, RandomProxy{ + Id: r.ID, Ip: r.Ip, Port: r.Port, Protocols: r.Protocols, + FailCount: r.FailCount, + RegionCode: r.RegionCode, RegionZhName: r.RegionZhName, + }) + } + return out, nil +} diff --git a/backend/internal/store/server.go b/backend/internal/store/server.go new file mode 100644 index 0000000..afeb795 --- /dev/null +++ b/backend/internal/store/server.go @@ -0,0 +1,95 @@ +package store + +import ( + "database/sql" + "errors" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// Server SSH 服务器条目:secret_enc 为 AES-GCM 密文,存密码或 PEM 私钥, +// 明文只在连接的瞬间存在于内存,不落盘不下发 +type Server struct { + Id int64 + Name string + Host string + Port int64 + Username string + AuthType string // password | key + SecretEnc string + Note string + CreatedAt int64 + UpdatedAt int64 +} + +func serverFromRow(e db.Server) Server { + return Server{ + Id: e.ID, Name: e.Name, Host: e.Host, Port: e.Port, Username: e.Username, + AuthType: e.AuthType, SecretEnc: e.SecretEnc, Note: e.Note, + CreatedAt: e.CreatedAt, UpdatedAt: e.UpdatedAt, + } +} + +func (s *Store) ListServers() ([]Server, error) { + rows, err := s.q.ListServers(storeCtx) + if err != nil { + return nil, err + } + items := make([]Server, 0, len(rows)) + for _, r := range rows { + items = append(items, serverFromRow(r)) + } + return items, nil +} + +func (s *Store) GetServer(id int64) (Server, error) { + e, err := s.q.GetServer(storeCtx, id) + if errors.Is(err, sql.ErrNoRows) { + return Server{}, ErrNotFound + } + return serverFromRow(e), err +} + +func (s *Store) SaveServer(e *Server) error { + now := time.Now().Unix() + if e.Id > 0 { + n, err := s.q.UpdateServer(storeCtx, db.UpdateServerParams{ + Name: e.Name, Host: e.Host, Port: e.Port, Username: e.Username, + AuthType: e.AuthType, SecretEnc: e.SecretEnc, Note: e.Note, + UpdatedAt: now, ID: e.Id, + }) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil + } + id, err := s.q.InsertServer(storeCtx, db.InsertServerParams{ + Name: e.Name, Host: e.Host, Port: e.Port, Username: e.Username, + AuthType: e.AuthType, SecretEnc: e.SecretEnc, Note: e.Note, + CreatedAt: now, UpdatedAt: now, + }) + if err != nil { + return err + } + e.Id, e.CreatedAt, e.UpdatedAt = id, now, now + return nil +} + +func (s *Store) DeleteServer(id int64) error { + n, err := s.q.DeleteServer(storeCtx, id) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil +} + +func (s *Store) CountServers() (int64, error) { + return s.q.CountServers(storeCtx) +} diff --git a/backend/internal/store/setting.go b/backend/internal/store/setting.go new file mode 100644 index 0000000..a42491a --- /dev/null +++ b/backend/internal/store/setting.go @@ -0,0 +1,20 @@ +package store + +import ( + "database/sql" + "errors" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +func (s *Store) GetSetting(key string) (string, error) { + v, err := s.q.GetSetting(storeCtx, key) + if errors.Is(err, sql.ErrNoRows) { + return "", nil + } + return v, err +} + +func (s *Store) SetSetting(key, value string) error { + return s.q.UpsertSetting(storeCtx, db.UpsertSettingParams{K: key, V: value}) +} diff --git a/backend/internal/store/store.go b/backend/internal/store/store.go new file mode 100644 index 0000000..5f9d1ed --- /dev/null +++ b/backend/internal/store/store.go @@ -0,0 +1,371 @@ +// Package store 数据门面层:CRUD 委托给 sqlc 生成的 internal/db.Queries(类型安全), +// 只保留 sqlc 表达不了的逻辑——会话滑动续期、聚合映射、种子数据,以及对服务层稳定的 +// 领域结构体(Id 命名、Enabled bool)与 ErrNotFound 语义。 +package store + +import ( + "context" + "database/sql" + "errors" + "fmt" + "log" + "strings" + "time" + + _ "modernc.org/sqlite" + + "github.com/cyi-cc/cyi-box/backend/internal/config" + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +type Store struct { + Cfg *config.Config `fun:"auto"` + DB *sql.DB + q *db.Queries +} + +// Q 暴露 sqlc 查询器:新代码可直接走类型安全 API,门面方法逐步收敛 +func (s *Store) Q() *db.Queries { return s.q } + +func (s *Store) New() error { + dsn := fmt.Sprintf("file:%s?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)", + s.Cfg.DBPath) + conn, err := sql.Open("sqlite", dsn) + if err != nil { + return fmt.Errorf("store: open sqlite failed: %w", err) + } + // SQLite 单写者:串行化所有连接,避免 database is locked + conn.SetMaxOpenConns(1) + s.DB = conn + s.q = db.New(conn) + if err := s.migrate(); err != nil { + return err + } + return nil +} + +// migrate 表结构唯一来源是 internal/db/schema.sql(sqlc 与运行时共用); +// 列级增量(老库补列)用探测式 ALTER,CREATE TABLE IF NOT EXISTS 覆盖不到。 +func (s *Store) migrate() error { + if _, err := s.DB.Exec(db.SchemaDDL); err != nil { + return fmt.Errorf("store: migrate failed: %w", err) + } + // proxies.source 是后加的列:老库探测缺失则 ALTER(COUNT 空表也返回行,不误判) + var dummy int64 + if err := s.DB.QueryRow("SELECT COUNT(source) FROM proxies").Scan(&dummy); err != nil { + if _, err := s.DB.Exec("ALTER TABLE proxies ADD COLUMN source TEXT NOT NULL DEFAULT ''"); err != nil { + return fmt.Errorf("store: migrate proxies.source failed: %w", err) + } + } + if err := s.DB.QueryRow("SELECT COUNT(app_id) FROM license_cards").Scan(&dummy); err != nil { + if _, err := s.DB.Exec("ALTER TABLE license_cards ADD COLUMN app_id INTEGER NOT NULL DEFAULT 0"); err != nil { + return fmt.Errorf("store: migrate license_cards.app_id failed: %w", err) + } + } + if _, err := s.DB.Exec("CREATE INDEX IF NOT EXISTS idx_license_cards_app ON license_cards(app_id)"); err != nil { + return fmt.Errorf("store: migrate license_cards index failed: %w", err) + } + // 记忆库全文索引:FTS5 外挂内容表 + 触发器同步(放这里而非 schema.sql, + // sqlc 的 sqlite 解析器不认 CREATE VIRTUAL TABLE/TRIGGER) + if _, err := s.DB.Exec(memFTSDDL); err != nil { + return fmt.Errorf("store: migrate memories_fts failed: %w", err) + } + // rebuild 幂等:给触发器建成前已存在的行补索引 + if _, err := s.DB.Exec(`INSERT INTO memories_fts(memories_fts) VALUES('rebuild')`); err != nil { + return fmt.Errorf("store: rebuild memories_fts failed: %w", err) + } + return nil +} + +var storeCtx = context.Background() + +func optStr(s string) sql.NullString { + return sql.NullString{String: s, Valid: true} +} + +// SeedAdmin 空库时写入首个管理员;已有用户则跳过 +func (s *Store) SeedAdmin(passwordHash string) error { + n, err := s.q.CountAllUsers(storeCtx) + if err != nil { + return fmt.Errorf("store: count users failed: %w", err) + } + if n > 0 { + return nil + } + now := time.Now().Unix() + _, err = s.q.InsertUser(storeCtx, db.InsertUserParams{ + Name: s.Cfg.AdminName, PasswordHash: passwordHash, Role: "admin", + CreatedAt: now, UpdatedAt: now, + }) + if err != nil { + return fmt.Errorf("store: seed admin failed: %w", err) + } + log.Printf("store: seeded admin account %q", s.Cfg.AdminName) + return nil +} + +// SeedTools 空表时写入两条示例工具,演示工具箱菜单的数据驱动渲染 +func (s *Store) SeedTools() error { + tools, err := s.q.ListTools(storeCtx) + if err != nil { + return fmt.Errorf("store: count tools failed: %w", err) + } + if len(tools) > 0 { + return nil + } + for _, t := range []db.InsertToolParams{ + {Name: "JSON 格式化", Icon: "code-outline", Url: "/tools/json", Description: "粘贴即格式化与校验 JSON", Sort: 1, Enabled: true}, + {Name: "时间戳转换", Icon: "time-outline", Url: "/tools/timestamp", Description: "Unix 时间戳与日期互转", Sort: 2, Enabled: true}, + } { + if _, err := s.q.InsertTool(storeCtx, t); err != nil { + return err + } + } + return nil +} + +// ---- users ---- + +type User struct { + Id int64 + Name string + PasswordHash string + Role string + Status string + CreatedAt int64 + UpdatedAt int64 +} + +var ErrNotFound = errors.New("record not found") + +func userFromRow(u db.User) User { + return User{ + Id: u.ID, Name: u.Name, PasswordHash: u.PasswordHash, Role: u.Role, + Status: u.Status, CreatedAt: u.CreatedAt, UpdatedAt: u.UpdatedAt, + } +} + +func (s *Store) GetUserByName(name string) (User, error) { + u, err := s.q.GetUserByName(storeCtx, name) + if errors.Is(err, sql.ErrNoRows) { + return User{}, ErrNotFound + } + return userFromRow(u), err +} + +func (s *Store) GetUserByID(id int64) (User, error) { + u, err := s.q.GetUserByID(storeCtx, id) + if errors.Is(err, sql.ErrNoRows) { + return User{}, ErrNotFound + } + return userFromRow(u), err +} + +func (s *Store) ListUsers(keyword string, page, pageSize int64) ([]User, int64, error) { + total, err := s.q.CountUsers(storeCtx, optStr(keyword)) + if err != nil { + return nil, 0, err + } + rows, err := s.q.ListUsers(storeCtx, db.ListUsersParams{ + Keyword: optStr(keyword), + Limit: pageSize, + Offset: (page - 1) * pageSize, + }) + if err != nil { + return nil, 0, err + } + users := make([]User, 0, len(rows)) + for _, r := range rows { + users = append(users, userFromRow(r)) + } + return users, total, nil +} + +func (s *Store) CreateUser(name, passwordHash, role string) (User, error) { + now := time.Now().Unix() + id, err := s.q.InsertUser(storeCtx, db.InsertUserParams{ + Name: name, PasswordHash: passwordHash, Role: role, CreatedAt: now, UpdatedAt: now, + }) + if err != nil { + return User{}, err + } + return s.GetUserByID(id) +} + +func (s *Store) UpdateUser(id int64, name, role, status string, passwordHash *string) error { + now := time.Now().Unix() + if passwordHash != nil { + return s.q.UpdateUserPassword(storeCtx, db.UpdateUserPasswordParams{ + Name: name, Role: role, Status: status, PasswordHash: *passwordHash, + UpdatedAt: now, ID: id, + }) + } + return s.q.UpdateUser(storeCtx, db.UpdateUserParams{ + Name: name, Role: role, Status: status, UpdatedAt: now, ID: id, + }) +} + +func (s *Store) DeleteUser(id int64) error { + return s.q.DeleteUser(storeCtx, id) +} + +func (s *Store) CountUsers() (int64, error) { + return s.q.CountAllUsers(storeCtx) +} + +func (s *Store) CountActiveUsers() (int64, error) { + return s.q.CountActiveUsers(storeCtx) +} + +func (s *Store) CountAdmins() (int64, error) { + return s.q.CountAdmins(storeCtx) +} + +// ---- sessions ---- + +func (s *Store) CreateSession(token string, userID int64, ttl time.Duration) error { + now := time.Now() + return s.q.InsertSession(storeCtx, db.InsertSessionParams{ + Token: token, UserID: userID, + CreatedAt: now.Unix(), ExpiresAt: now.Add(ttl).Unix(), + }) +} + +// SessionUser 会话命中后的用户视图:登录态校验只走这一处,过期/禁用统一判负 +type SessionUser struct { + User User + Token string +} + +// GetSessionUser 校验 token 对应的会话与用户。 +// 滑动续期:会话本身 ttl 过期即失效;存活但距签发/上次续期超过 renewAfter 时, +// 顺手把 expires_at 顺延回 now+ttl——活跃用户不掉线,闲置 ttl 时长强制下线。 +func (s *Store) GetSessionUser(token string, ttl, renewAfter time.Duration) (SessionUser, error) { + row, err := s.q.GetSessionUser(storeCtx, token) + if errors.Is(err, sql.ErrNoRows) { + return SessionUser{}, ErrNotFound + } + if err != nil { + return SessionUser{}, err + } + su := SessionUser{ + Token: token, + User: User{ + Id: row.ID, Name: row.Name, PasswordHash: row.PasswordHash, + Role: row.Role, Status: row.Status, + CreatedAt: row.CreatedAt, UpdatedAt: row.UpdatedAt, + }, + } + now := time.Now() + if now.Unix() > row.ExpiresAt { + _ = s.q.DeleteSession(storeCtx, token) + return SessionUser{}, ErrNotFound + } + lastRefresh := time.Unix(row.ExpiresAt, 0).Add(-ttl) + if now.Sub(lastRefresh) >= renewAfter { + _ = s.q.TouchSession(storeCtx, db.TouchSessionParams{ + ExpiresAt: now.Add(ttl).Unix(), Token: token, + }) + } + return su, nil +} + +func (s *Store) DeleteSession(token string) error { + return s.q.DeleteSession(storeCtx, token) +} + +// DeleteOtherSessions 改密后吊销除当前外的全部会话 +func (s *Store) DeleteOtherSessions(userID int64, keepToken string) error { + return s.q.DeleteOtherSessions(storeCtx, db.DeleteOtherSessionsParams{ + UserID: userID, Token: keepToken, + }) +} + +func (s *Store) CountActiveSessions() (int64, error) { + return s.q.CountActiveSessions(storeCtx, time.Now().Unix()) +} + +func (s *Store) CountTodayLogins() (int64, error) { + return s.q.CountTodayLogins(storeCtx) +} + +// LoginTrend 最近 days 天每日登录数(按会话创建数统计),缺口补零由调用方做 +func (s *Store) LoginTrend(days int64) (map[string]int64, error) { + rows, err := s.q.LoginTrend(storeCtx, time.Now().AddDate(0, 0, int(-days)).Unix()) + if err != nil { + return nil, err + } + m := map[string]int64{} + for _, r := range rows { + if d, ok := r.D.(string); ok { + m[d] = r.C + } + } + return m, nil +} + +// ---- tools ---- + +type Tool struct { + Id int64 + Name string + Icon string + Url string + Description string + Sort int64 + Enabled bool +} + +func toolFromRow(t db.Tool) Tool { + return Tool{ + Id: t.ID, Name: t.Name, Icon: t.Icon, Url: t.Url, + Description: t.Description, Sort: t.Sort, Enabled: t.Enabled, + } +} + +func (s *Store) ListTools(enabledOnly bool) ([]Tool, error) { + var rows []db.Tool + var err error + if enabledOnly { + rows, err = s.q.ListEnabledTools(storeCtx) + } else { + rows, err = s.q.ListTools(storeCtx) + } + if err != nil { + return nil, err + } + tools := make([]Tool, 0, len(rows)) + for _, r := range rows { + tools = append(tools, toolFromRow(r)) + } + return tools, nil +} + +func (s *Store) SaveTool(t *Tool) error { + if t.Id > 0 { + return s.q.UpdateTool(storeCtx, db.UpdateToolParams{ + Name: t.Name, Icon: t.Icon, Url: t.Url, Description: t.Description, + Sort: t.Sort, Enabled: t.Enabled, ID: t.Id, + }) + } + id, err := s.q.InsertTool(storeCtx, db.InsertToolParams{ + Name: t.Name, Icon: t.Icon, Url: t.Url, Description: t.Description, + Sort: t.Sort, Enabled: t.Enabled, + }) + if err != nil { + return err + } + t.Id = id + return nil +} + +func (s *Store) DeleteTool(id int64) error { + return s.q.DeleteTool(storeCtx, id) +} + +func (s *Store) CountTools() (int64, error) { + return s.q.CountTools(storeCtx) +} + +// NormalizeName 服务层共用的名字规整 +func NormalizeName(s string) string { return strings.TrimSpace(s) } diff --git a/backend/internal/store/vault.go b/backend/internal/store/vault.go new file mode 100644 index 0000000..593298e --- /dev/null +++ b/backend/internal/store/vault.go @@ -0,0 +1,93 @@ +package store + +import ( + "database/sql" + "errors" + "time" + + "github.com/cyi-cc/cyi-box/backend/internal/db" +) + +// VaultEntry 密码箱条目:password_enc / totp_enc 为 AES-GCM 密文,离开 store 前必须解密 +type VaultEntry struct { + Id int64 + UserID int64 + Title string + Username string + PasswordEnc string + Url string + Note string + TotpEnc string + CreatedAt int64 + UpdatedAt int64 +} + +func vaultFromRow(v db.Vault) VaultEntry { + return VaultEntry{ + Id: v.ID, UserID: v.UserID, Title: v.Title, Username: v.Username, + PasswordEnc: v.PasswordEnc, Url: v.Url, Note: v.Note, TotpEnc: v.TotpEnc, + CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, + } +} + +func (s *Store) ListVault(userID int64, keyword string) ([]VaultEntry, error) { + rows, err := s.q.ListVault(storeCtx, db.ListVaultParams{ + UserID: userID, + Keyword: optStr(keyword), + }) + if err != nil { + return nil, err + } + items := make([]VaultEntry, 0, len(rows)) + for _, r := range rows { + items = append(items, vaultFromRow(r)) + } + return items, nil +} + +func (s *Store) GetVault(userID, id int64) (VaultEntry, error) { + v, err := s.q.GetVault(storeCtx, db.GetVaultParams{UserID: userID, ID: id}) + if errors.Is(err, sql.ErrNoRows) { + return VaultEntry{}, ErrNotFound + } + return vaultFromRow(v), err +} + +func (s *Store) SaveVault(e *VaultEntry) error { + now := time.Now().Unix() + if e.Id > 0 { + n, err := s.q.UpdateVault(storeCtx, db.UpdateVaultParams{ + Title: e.Title, Username: e.Username, PasswordEnc: e.PasswordEnc, + Url: e.Url, Note: e.Note, TotpEnc: e.TotpEnc, + UpdatedAt: now, ID: e.Id, UserID: e.UserID, + }) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil + } + id, err := s.q.InsertVault(storeCtx, db.InsertVaultParams{ + UserID: e.UserID, Title: e.Title, Username: e.Username, + PasswordEnc: e.PasswordEnc, Url: e.Url, Note: e.Note, TotpEnc: e.TotpEnc, + CreatedAt: now, UpdatedAt: now, + }) + if err != nil { + return err + } + e.Id, e.CreatedAt, e.UpdatedAt = id, now, now + return nil +} + +func (s *Store) DeleteVault(userID, id int64) error { + n, err := s.q.DeleteVault(storeCtx, db.DeleteVaultParams{UserID: userID, ID: id}) + if err != nil { + return err + } + if n == 0 { + return ErrNotFound + } + return nil +} diff --git a/backend/internal/vault/vault.go b/backend/internal/vault/vault.go new file mode 100644 index 0000000..2099d10 --- /dev/null +++ b/backend/internal/vault/vault.go @@ -0,0 +1,142 @@ +// Package vault 密码箱原语:AES-256-GCM 字段加密 + RFC 6238 TOTP + otpauth:// 解析。 +// 不引入第三方依赖,全部标准库实现。 +package vault + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "encoding/base32" + "encoding/base64" + "encoding/binary" + "encoding/hex" + "errors" + "fmt" + "net/url" + "strings" + "time" +) + +// DeriveKey 任意密钥材料规整为 32 字节 AES key(hex/base64 解码失败则取 sha256) +func DeriveKey(material string) []byte { + material = strings.TrimSpace(material) + if b, err := hex.DecodeString(material); err == nil && len(b) >= 16 { + sum := sha256.Sum256(b) + return sum[:] + } + if b, err := base64.StdEncoding.DecodeString(material); err == nil && len(b) >= 16 { + sum := sha256.Sum256(b) + return sum[:] + } + sum := sha256.Sum256([]byte(material)) + return sum[:] +} + +// RandomKey 生成 32 字节随机 key 的 hex 表示 +func RandomKey() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return hex.EncodeToString(b), nil +} + +// Encrypt AES-256-GCM → base64(nonce|ct) +func Encrypt(key []byte, plaintext string) (string, error) { + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + nonce := make([]byte, gcm.NonceSize()) + if _, err := rand.Read(nonce); err != nil { + return "", err + } + ct := gcm.Seal(nonce, nonce, []byte(plaintext), nil) + return base64.StdEncoding.EncodeToString(ct), nil +} + +// Decrypt 解密 Encrypt 产物 +func Decrypt(key []byte, encoded string) (string, error) { + raw, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return "", err + } + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + if len(raw) < gcm.NonceSize() { + return "", errors.New("vault: ciphertext too short") + } + pt, err := gcm.Open(nil, raw[:gcm.NonceSize()], raw[gcm.NonceSize():], nil) + if err != nil { + return "", errors.New("vault: decrypt failed") + } + return string(pt), nil +} + +// ParseSecret 接受裸 base32 密钥或 otpauth://totp/...?secret=... 链接,统一返回 base32 密钥。 +// 输入非法返回 error,服务层转成业务错误码。 +func ParseSecret(input string) (string, error) { + s := strings.TrimSpace(input) + if s == "" { + return "", errors.New("empty secret") + } + if strings.HasPrefix(strings.ToLower(s), "otpauth://") { + u, err := url.Parse(s) + if err != nil { + return "", fmt.Errorf("vault: bad otpauth uri: %w", err) + } + secret := u.Query().Get("secret") + if secret == "" { + return "", errors.New("vault: otpauth uri missing secret") + } + s = secret + } + // 容错:去空格、横线,转大写;无 padding 形式也接受 + clean := strings.NewReplacer(" ", "", "-", "").Replace(s) + clean = strings.ToUpper(clean) + if _, err := decodeBase32(clean); err != nil { + return "", fmt.Errorf("vault: invalid base32 secret: %w", err) + } + return clean, nil +} + +func decodeBase32(s string) ([]byte, error) { + if b, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(s); err == nil { + return b, nil + } + return base32.StdEncoding.DecodeString(s) +} + +// TOTP 计算当前验证码(30s 步长,6 位,SHA1),同时返回本码剩余秒数 +func TOTP(secret string, t time.Time) (string, int64, error) { + key, err := decodeBase32(secret) + if err != nil { + return "", 0, fmt.Errorf("vault: invalid totp secret: %w", err) + } + counter := uint64(t.Unix() / 30) + var buf [8]byte + binary.BigEndian.PutUint64(buf[:], counter) + mac := hmac.New(sha1.New, key) + mac.Write(buf[:]) + sum := mac.Sum(nil) + offset := sum[len(sum)-1] & 0x0f + code := (uint32(sum[offset])&0x7f)<<24 | + (uint32(sum[offset+1]))<<16 | + (uint32(sum[offset+2]))<<8 | + uint32(sum[offset+3]) + secondsLeft := 30 - (t.Unix() % 30) + return fmt.Sprintf("%06d", code%1000000), secondsLeft, nil +} diff --git a/backend/main b/backend/main new file mode 100644 index 0000000..e69de29 diff --git a/backend/sqlc.yaml b/backend/sqlc.yaml new file mode 100644 index 0000000..a6298f4 --- /dev/null +++ b/backend/sqlc.yaml @@ -0,0 +1,14 @@ +version: "2" +sql: + - engine: "sqlite" + schema: "internal/db/schema.sql" + queries: "internal/db/queries.sql" + gen: + go: + package: "db" + out: "internal/db" + emit_interface: true + emit_empty_slices: true + overrides: + - column: "tools.enabled" + go_type: "bool" diff --git a/docs/screenshots/dashboard.png b/docs/screenshots/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7418c14fee0f3242062d3355e2d4fa71829d991c GIT binary patch literal 136502 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 z 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� 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

&ikz5lIlKo?y)j%Fe%Dws6X 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 ABLDyZ literal 0 HcmV?d00001 diff --git a/docs/screenshots/dbm.png b/docs/screenshots/dbm.png new file mode 100644 index 0000000000000000000000000000000000000000..a917ddf18d0bf8537333f153d77c8f2077eecaf1 GIT binary patch literal 66060 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�H=btpnwSQN%ZVN3{+GlmG_Ny%hIw= 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

X)+C;mUF+12EiXENk_*h zd=?i+O&Ram$r`05q2{zZYgiTYii?c&Pk!R!2de?pIJINl`ftDZ`uURn{A^{?lbTgj zbSZ=Op;t^wP4y~Dv zKRPO}GPSn0S8uXqFpwBZ<$oaak)#K~d1@fU-T_HE9%1Yl2ze+%L!{e;gu7&uh5XY= z1=VM{-xk^jDm35mm*{{TBfzdfo%6TtA5NA!)w>oC2h#8AKxW0;{=1lJtzY;zQYZ26 zD(9F%+XXW-&;o)#*_xUQ@Zy1mXK!y0^dkT~Xd0X~L6;qzb{BqiV9}RR=5O+ed|;8% z(G21l{d*Lx*|{Cb=!CrkK>*V}KlWIXLGn;L*Hf!=XUi1Pc6OzvDNsEry}F*y+oA7K zF-X2Re$L~@Y&737pS*+Ml@b^hba3W+25iR(#m7BrXUH;o>8({&j%zpsfFcGTzk}a2 zE)OsKYUQEn?s#J`VgcASw^zHvfl+gJc^Sws@E<7$yat_j$KaJRXjFmoy47@P>)>GM zvSGuh&+0_5@uAJ2Y~N3Ooba@p(=o}2Q`H*h%b!u*ef|Bwj+ky5F-8)1bqPje((2^%lu37a6>V zdO}XB%=%0?Pi#Q&dUE{RU!FX-)A9-mz;l7%2VSk3y1H!zyaAo9-SKA-c_BY*Zl|v_ z1x>_&=>qk(GhfZ(01{MG#L${_0fe1_dt39p0#_&aCX4NLDTY1ux%6t67$2I*{)xRT zomliAxTbCb`r4+ZyOxsXM)y=$eQB12Qy+tISJIY}1 z!Jx(!1fdxi7UWhb+cX{dQ1%yTse3eP?AeGnaeVI)hWG^B+?z=RtOorV)}le|Fduv< zK*_N+k@|lJb7}>?%+`e@ms&1GnmoWIPK>j`F}5VJ+phHh-#Un=6{}VFCn=e*&H{4+ zFqfp$)6&+Nb+aEnEh$y~`SLj$E<4ucKbZe;l_>52# zb&Fp}sNHpjt8KL{s~;73tpr_GnFdlemebo;9zWScV|+f^ns?vUDJgJq*&bbgz87Wo zDSUYum64;`W!rHGORo=P4pv~G9Y21OlY71nntq-i0xR}A7OiV!oCdvBCIDp?DYNO0 zjL7Lzz0~V2<|}<$5KsdmwR4S50&2)e2mzhIFtqHFy{#EvZg;+DgF#t+D$k_V1Ygh& z(Mp=Wh6(LcW_G^#*e69SJn7MX3ox#v=Yu3k>AhC1i_3fi?RY-eD1tEk?CGAz^@fA^ z%Kbl;e))6M&OAU-*aSbs_<`8E6vSK|r;#e~3BgPOtJ-W0c&PZu$f5|_=xr0eN1|Ww zg6_MLdlw8|WMV#OjPN^`WW*H>DMm_ia#phqd*}G1q`;4`QR_rcHy$3=C#%0I_*}It z20xRM`TF^#@i&NtkuUFGvoliW-?yYg`*)RS^Y)9&`ULJTItK(m<0r(QZtWg$ajoob zd+lvL`mKGh=@#Z!`<~a~eBez3!BF7C47KM?%XV~hCNe%(U18w~sNUNh?}h%_c;<_!xzB;d@;tzx;Rn z{r+0D7YFj?&OiG=3B%FR@yZDnFF-N%zWW6OxY34=fx)8L|Et*)h3}F4 zh4Y*0rdJ|b!gQQH&H4F*n9z;IjO>*1OD*v)bCV=qzg-;sAcRJRVDW)0i>tzz@6@@; zv%SmBDe24u5JwEEFp{+Sv=&7;goG?ktXQ_-Gx`wRXJu1{$1M1r8#&$*?c-y zf9bJ>(&_h03@tv$i|S(p*cM4Vu0$7GMTk_Hkg2$!^zO3Zi@(?wwgjm?3>_J|i{ zl~ttN=;&>{>s+8Y)QF?oBv8h<D_Z2WOq#cmNEl~e$+6iR$+5vwlkq=_jxF>bW z*r=H3RaIrH8~PtcybzfY(9zMMuda59k3Yg9FljY>q?Nc`olblFSo&PZKWeu^%NTvP zZr4*qP_XIqvX`YhpXUuH2<$rMX6J)|DQtDn*Ockg56oTP zf<+6+Y;idBkyDSUav{*s^@BF9bP`5JMg|5gtw9-U#rBF2R=XNppgrPzyeRV((0Y{d z5($I*OV_Eq9%sAtPLy3;5ev^bSomyqP{b2oekcC+0uuTp;8hn;+|#|Q1XDf@52cTi zU^r{w2o5aAfRQ`Ui-`Qfyni13%N_>l<(IkV>N8PdIMVX?fTaHy=yWi?nvnYuc8$X8 zZas}CAM}O_KHaP5$RgSPVJ~j{l9ZM^yQ(;rUhHkj1NJRI{&!7#oG4mqazjl}!XiqI zrSDxz7v`E!+(bnAAZ?!+aB(vuK8@$Lv%H@3MuI~KhI(>^rX#9mVkXRh^>nRO(k~h+ zx_2FTEJ^4_%VlIhRw1H^v_Ic;>wudyeN0)2rZ*51{t5a+v--isQR?&mKpytSsKog8 zSd_2rk68fI)NHk|oSa^Cf6wRT42 zvHS`b`Nx#gquYneAe66ARN)rx`I2QiG?vs%!MU)T;`bn99C0jt*rd5wPUC%E~Ua#fXW) z0)3qVW}_zG)px#<$D1iBRw^m=gPSHkIv#5GH(_)05M4W9?o` z^MMH^1nX4jSq9vM1QC%=Yh^%P{vi?btA|ATJBL`qc-ni8JusIYmbgpa8P5qX4rAXb zSCCie4J=u@h=4Q@m&@Cs&}+uHcmWF!HV=M&14Ou>bH+QMtZeivU7UK+O{*d;nT zHYQPUZVYHV8U}_BjcP}>Rw-+0DsoDpu+Me=GreB|{6y|OJ(il>1e{vGq)pv-G?ix6 zc6EUcz~p32fbHo`PUx*EQ5ge<>~^CF*erNZ2%k}i_>rwuo`eh(7J`rg=&2P|{-yzP z3G}?08XCbS!r|Th8!NlLufXcZ=8Bx*f2^Bx?tbezNkMaVM zqn_VySzPKpkS#QxpOP_cY@z-IZff524A#~90-+-XA`}0qcFg1_o0&0Sz%PvPLX|I< z_ZXZvi!9=)B+I1J)x(EOudbm%_v{%W95Ul@ zW?w%Glk@PO#ABZJ049y#o&b7uB}47Xw21gj5Dr5My9q!fT65gxc9Y;NGwn40fZ4ewOh{V*OIUE3lb!7hvs`*iBo4w!|5(Wv;gj=^jeH55 z7uM*!;&Otmb7PS3Y+t{cO%}<@4!>uDZJ=ttcYJdwsWaFl69yJ|Ogc@J?FiURQBTe& zXvv{7Qk9k1>}wF*;Oj#k>MpcC7k**)I_z_N3ZAylMw;L6A9$@QxQ8sc5rgl7Y{}=U zNFlW9Vt#Sx zbY@lELKQo!`#5J;9RK)DAqEa6=(Q~j$?jC$l{&(UIuo=227#EUB8BN^aY~IRM?K<_ z3C|6)skhyUYNZ^oLU%gkG!)tg5q{7cb)(`*tE$4H6Rcr!r!f9AJDH@E!Q%Fho6hV) z76a5CsDR?IA+Ud)=h}NAfy_+HNXc)pW#G6~J%dB=28H0;1E>TxV+xv@@@f*tp1nK$ z=XN_Rh5@j z0VVd@4~7V+|EkNO6YiDr$Oy;BVhSA#o#ZTUkD{Wu*jP{@K!R{BkX6iMJ@0tF;vx?M zGkDiYiC%tHR0m=hO)@)E(16?iWsTZd5^?yY+v~@VSMvUZ$ajpA&W|5dj&67zi}lD~ zIJ_H7`&edk-QBOF$ujo?x`809Cz_F-=-b6{DG^f z(6U;zq7=6*h@yVdJ#^%3udl7)^ICfQS7522~as%lqxKI!ByWT@4RC1H-(9 zjImnvQuq~Eej?PnUn}~`gG4f?ccS#&qsmF1i{O<$XanC8@DouP5%nrP$XL!01E#-; zF%D%@t79Rv5nDfeNugTv$2*5T8Q zLxuAHGp4MJ{hSLT%J%`q2T;L4r1SH`P{dDfZQUpeqpHi9IX3+@?3)yDvO_|n`=+a; ztUAMXB~m1OY`%vH5U*%LaNrd%6j)Pcyusj!m))q527Z0ZR0c`&kju^i08eDqZg{BR zinV`)<-rf7rK3{;+7A^KIz~2-!}^GV;G?Cnj9%vL>yxSq*WGo7!3MVSzj0)ugAL0z zfEZn!o*pr0*lL@bV~qFY?p=4CY6BM!i92gEl9G}Dmzb5E&*uq`|NQv}DSOj012Bx- zUBNY~R!9~Y$TtONGDbdndJg6S(xNn4iXr`a*8yOb<5Z{3$e69Mu&QT6mH$FjdHxC^ zb8TqY|12~P2<^Dk7ZX7z8mqLxBStkO%&t?Hom*OCI2*edPEk@;w)ow1J}jjX!1hGC z92n`D`N|Z_%eT&F_oUNp(`ne*#UhuAF$tt{85!r#*`5wte7(uAxqr!2X!Zi3ZS__B z>n~wT6_t2V->4|nZu$7cll`NUFIldyy}l8VM%EG?T<^0nbF}!Y0nEqDIFSjHfddY! zA>mQ@RSVvnHCaNuq@VxGmbR((5G3g^Fr$mI6Kx<-D zwv-PV^!V_zmy*5FyEIR4aLg9&HgnYqnyh33mJ?tnUK3Rj6N`LIi_t^6=u~!oeh%6? zlCLDOL*+2o;u8`8@g3X?HgQYu!+zfKe^{;WYBN6Z3o|&tM)aPvDiLJPo^MQoXR=4T z?*RWsS#lCt>!(}!kpXUyDvJ0j(W_8Gb#`D#T*N+?{^bq``%WB4*%63_FCGBvS*Lyt zI5~GrUt6*RY=e~* zmDURlg!m7qpw^2ZxTaO9v_8RHQqo1qw!ArmlKxq$t59_2sb#~HNE(_CwwD{Mw$;D5 z+M6w00dpC-w1+p;{~g2@4Fl<}D2>f@?*z`t37ED-%k)V}s6={=v$MQ~DIg+8#|Dgi zT1`(Ywr~B82nym{tN&Ms-)jZRIj9EUq~$gaCP${Gav0BG10&?Tpi_hfQ@I$y?_Raq zNf8m2SXKr%me1xYj7bTJ@Tuyq(N=?L&J9)*465ammGuL{JQ`S?&6SR0p~Bmj@Ar07 zB2F4@wlLlIr@SXU_mBLsX}o`a{$C64)7Inm1H;P$K|r`-R!qx7J!kow$l{~b7N^Nq zk2FsJhO=ID#=^kBbEGQh{au1j)!XFE&nZMiwbm-DUhMtsNBn^bP59EziK>LeC%^66 zjxb!Ss)mf%^jpsX_!p)~?JDest!-~khc_CM*szhFL3jJoV{7C=1pGV1=40N|Q<|*E z54547IQ9I~yS!ylDt?V2z7_#}Q-R^T^M}XIaxqbfe8wbk-d^Q|6nD^Z;Wi)4LVvhW zg1j!EQ4%xNqs3(ssi)`+le4i&z}fP;80FJ=N-k$yXHUPXwr)>~M7(rC z?ouYF`&9xx?~ad0dDRQZk|OYD5vvh@b#R*cCuc8ja3o7Q1&M40HLMIFqkS4q?jxi% zI;i0|?#<$z2Ls0t)Ic|Fv?%T-tJt?*)A<$sQDPiP8DM^5gUU*jCVQ&ei=_KrUBHs? zBWprUysh<3-y7>Xj&Kxzo_2iFV$-f8e28$rfC@lge*#Mrd0}y}nWgcBg}~onF%WpM zk*ub#zc@U6e@_m$(F9U@5HEPInI&K$8#Vu{IUA_0S=~`CB(}x1`&41rE zY}x-UZ$@VWJ(c^e(W3w9*1zOO7pe3NUV1Hdz@9TNYpp1@d-&_Vt@oVuD`{!MMP;xA z2;Qlv=}2}kH2J%xEuLQq>g|5mWS}HkVbBo-8VvSzO=V?id2t$OUBYGzPrfN-=>QXk zfDVx5G=nm~FOM4VUb}p)`H#@Lzu97GqmyGPLbfaQ(4oD#YSaDnFW&cXY4BGl|6gg4 zM3HR%JNH3s2>jkpiUzfa`O9SZO`IGCsJ~m+4<>#vo^IRrMTBbCg5TV9bKlO=^nL$i zW8G%`rxq~D_CzlJ`7&sifR$v29ngP(hO^DnK7Rf)CO~EJn-d&WJVHnJuc~?h@*_x} z;@xCdWL!U5%K&*|49x$@ z6POqnDZ*~*a&jR{%>SK)u_ayAw@yC)j&RC|1#vMT!woo$CEdwYpA!F06LR4^VD-qN z6Zt#1?4}n5qG;}EXiQF)=?n7l;V=HBirg4TvQC*lbF{$*_~w6F=mDmH-!|9m#>>yo z4|;5$*B9-TG}P3puf-J=!;KW_;agWck&9Xw8^Ue>(0AR+kW&P za=-tUII8VjjIf|XPEhapb9)PZeT9gQN_Iw^g1{x5qSP8G#tjfx(9Ww4;4wLG{tf@b zUy+|LM@J7;XQpE@HM7`2o}Tm=LP4Vm1i6d53(wY~C>{~CToRx&Dkz-7 z*66KbvGKqL>1xXEqDsaPz;popP5j@=sQjY_Lp5Y`z5>Nf{@+7l zTxOo@=Lm(`8&?qDqi~7d)$z*L_wKz5B=GBMpTKjh-?w*72I5i<2~+9U+f!ls-IDNK{#3K11kDZ0yG}dlf$Ck_Isamsl!G=pU;U=^K3hl9QXdeBhwt-fLPGi zGJV5lF}0<-M0u~DJb4Q8t@~J>{^WqBr>Fboa0^vbaso&V)gqLRQDN)D#Mo#itI&Q6@Lp_0F)ORwTTcPOf zf?{h1vzf<+QUyfZ+n;Fi^FR3Tz|K{{5lDDwi9$l;Xt=pd7sMSIieg#k>RcNAa!LM? zAcIdcV{jYfgyP8WfWS*r<7|MIV`%SkcShuvmx}@Cwx*_}o*0HQl|X}QR;R+F7e=pM zIlq2o76IK86!rjhZfP7=%H7(iq-YPsp!`?vm-jT^swzLm0zxyOD+A(*Rf8-L6bUYg ziS^XMq&$KQ`41UR0*P*zgw>+55PJ9a-E?*qj*uha{y3ZCQ`!01@*VE$)S278@2 zhcQ{eOd4Mni=}N;ib&Ic7oqVk05f0tO3?b);6g72TebO6f~A+lh^FA7b+ zPXCY$0SAzUmea?5_4O`y{6m(Ki;A<4Hzptz$kE)2pN#m0gzPSg>ysgF$Iqc2t50v#5@}lkX^_$ww=wogTn+0QqCGRF9?)9QR;Xd&FOBnk`E8u9BW1(cP1wHq0=c z#)W1!eN^x>-cnqfSi^b|gT^bE01P1HxTp)xYzZEM`zpICOCq!)rDPMfQ zE+CZdsX+9BfNra$WI^ZQ&mgLnrcaMuuk_NK4mJm3R47Fq zmUsv2$MWPC%1gE`>m4tD0Ym{GY%rO_VK!FY=)7k68K<*$! z!9yHV%3V~(7LQlfi=fmZo%~L_aLpth9uKNP{U-Od@YQ4pDEeI<`BfX8?_tGV^IeES zNC(p@ukvz-mg}Kx6oQ^Xkljq*BTucypl50PNW?+3DIbhYv+4cLZ(^hK@hu3GV%lnQ zqs3O{1WQtj;PdVA>p|Ue_nzK$*=|l%tQg3kuO>@Nzi-@K_7Dgg_(k-(XjsY_62&lB zzaO>a8@b-*g>qS5AqIaP`r<{T=~w$;L2j^q+e@n%lv^rLvl*gn`pupEqzh8cTs@qq z^QG`^ey6b^(Sek)Dr+B6VfVlIV6AC*R{=(3B0T%c%gIPdK z7PDjsVh7AEty8&^PzNS=8Sr2Y>=rZm>(hbRWi0U8Evfqq41M3VKd0P*>70FRBTatR z#)jtjEE)2l_Af#73hYL@DQ4S2UtLW2JCHNp``#a1iKFy)mB)SG$$e62VLDSWKXF6B zlZYI%!IOCJ3rc_|=}%#R4^H<9{B;i}yx{{n_@5l1-w7G}WMfNQ5m(^$ayK{Y-{$H>;=uoP?-N%o81Nv3{i=q(_Hq_jYa9mBlH2$;l z)nqJDwS8~N*kvXH&nMuThA2=|k;Z2(zSNrF`Ux>azZsEvCN85Vf~PgLhCkCx6@gfR z0t&c9b3bac3T*k?W)E!!n_YG)V$|>|gj{BdH#gr6)(_Kv_PeE-%4ci>?2j-VP&);A zg+?x|*;}>;X?!qomAjAVja-1uGmU_NA0|$Ro+tpS^!Er}M0X!OcOT<$G!E>TaB-jB&?-b>Mu_{iAA=xE)DG_d?@M5HO|WoBk)XJ!{aW-80i zDw2+gDlN>E1cGS)dur<6w#grYRL}wPm`^78lqWZqg>W z4gl;6BCsnfta^1uw}0!H%6&AAP&(CB{Q(G;3fB%Y%Hnnx-)v2<8k!s@2%ICqo*C>I zIu+uZjK>t$EDe4FcLFZz6x}7TcjdfP&~eZIqD-&lFY1(nG1CVDlJX_Ykn?}pC6WLX zLsPRI@RW9%?bgA^)JyA)o_cn4pTE2!ijh|Ak4itl37py6J3G2MJ0&H%UM5VK$0>%0 z6BCdyw4{O@>`6*RP9_FV*CkICJuxK(y`RHV#UKI`I3eI@U^BUR-h?6|9h(L|?#7>F z7s&IC^&0{N02hF5G3bg^R+8)PPbNfPUf}r%p{@0SZ55y?p8$GxN3oJ*l8lVP#lt00 zSGe;j5x2w1_qQhKmEeASw9BcLCHV%;K@rrJZX}Z6n43-9LDL@cElTo?fPPe-VRW#vc zlz)im%f%rI;`_7rT}0brzhk4SrdFm?X)y}+&$;q+4dqq>axW`ce0uxdNJxlC`TC1D zbhnPWqO!N5#!Ex&m6h}shNs?YDv9Z7iI;iPtFn&EWW0Wul$au53(jHA7a`NiqoZ&& zniHC`^x@*v)~{bH^RCItu%e!2bt%Me2_QC3SOf%I0hGOV_lnD447T>_3p?pEK#?6a ztJ>m%owXbVh6egsyaqULz$8Dx2?5Q!vU*+Mt%!)zpuuNM)A?Ccj7b$x#F+5o^ugS5GvSqB>i=-$#ETn%_0v?;y4xrhh} zfhAy#aRGXg(72Q)h6L?R*igd4)bRVUBJ&r9VzjiA{8)Z`c>dCr0ylt9)YLMwiYb18 z^!S82yMB#x{1HAkx7wdmcZ&+xVKlq$fbgb`^}e`9016kx<$Od*cDg(d7_TC<)R#qt zMg9G(mKHn4M(Y7(n4@Ia_q>}jeFy=c{V}bWJ;r#?H1dvvCW89L!q)=W$p5NlW>CL0 zdv{hmjRlgX2?#P#>#TTbK&t{UkO5URu2gGiJyZ$*!pjzQCKFV*nJ#vz zcI9wL@UL=0gQ%=5)@vKU$Ta+#1$o~1rJ)`)Jk?cIpRllexB;`>d~wHg@w{CQJ(=ef z`fg_dW%-S`oI!69>e_uxuR1FH(f*85(ZBeheHUJ}ypV_(AeH}lsSB}X;Lf~s+F;l7SR3Mk@Bdsi zJ&G%KIXb;_us7!g=WseGWTMieWJ^el6)`(=Mj*q()2_Cc_PnAba{k~{o&Eyv?O5M% zd{VGc&cNS4JjDl{C=AumHB-3NQfbHt5Ni7mkWxK1T3}Q z-ae-t;crQC8cLc4xrJ~6NN-!$kgD1@x7kzFG4qz8&1Ugdbj&y6={|t*Qq`CQuqa8X zuJ%r*cUtkbzwTk+)^0l`T0Z7tNrGe&#k?shoT+ng>LWcu_!jMdZcV-8&&@sV;867k zRlEYyv|r}4C^&Hb{27>*W}Uiy+}=I}a3yc~4q;^S@+apI6B{zJ@B7O@7@U+|N6S6!o-%yTeR+1Q3;`qZspI($XLD@X!N^i0HUlGRR+mF zUo+`SrbM9C4S)5LG&_Gh3%ZvCb*@fGkSR<3Flm{V^!X`%nVtw)_Uq9)e5OD)L z?Fsear^}i;RVN&8)YM{Vzl>U}SR(uZIf*ro?qA=z;*C@Gy5RE<8c8(%$pjU%m?{mC zJ<1)+_4V~`&3mp5$+8=yMECA-9A!~^O8}+AEA2i5B4U!ZBVU%u<)5ifXlRypY)LV} zb+>iNt?PKgHy;FH9s>5^pS#RM|E3e?Ma#f?IYj7{lWY2tUziS5>vQt!vWttGGBUn2 zhs4J}ggqrKoBNF2F{e4krr)@JFz2>%Ho{so(B1C``yhc1ywVXW3oS=LiA~oB)bbHB z<`!m6j$`vbUJkX?hL&fDnW#H{)VlZQrdlMu=_BjoFRkWrZ7Qo4s!+QT(n%cFF#qU7 z;^s z18H2|^Jy!Pj~5%3ZkrCr9?05=gy1U=O5|y|zfR}XVwjZ<^o{B(ADzRg9*Dhw$6S0Q z{3EStWMm8uR`dr2D^GPtg*P7`ee)2#+vL8ZqOI+;+RM+uF@laKjD2hmMs-)$-zemp zx3c&5aPtM%(nXaD_+__mqik(4$`8^ov4Y9T$$>l_wC92~tX@fW4J2}qjpAt6?35H1 zOaoqebqt!@2`l+z$!y&OY~Fu-{P@zXrr-%ns*u~V_jT8;%L{C(soLC{(~T75_jk5R zU{Bi|nITfI{??|uJlad_Rv|GdDfjbXH8u6v$kiQBgBmiGK#`fyh6@ z=D`EoMo(!ay%zuag6pm&^zhWvsv4b&_X^M!T!0aDq84g_AZSCSK#oyKap6pbttGYp z!gXWA{<625RA0Y2ZJ>Wfp` z7u?*jkx_s+z5%<2xmJNj|6MfyoE+0q-Rf6>zWZk$UjUg}5g0dghW{|>CN=P&kYcvD z@I?vx6Y-deLpxgz4q#`zPI#xz$k0I{NXN$Zv%UTM1Tp!eM~^cNN>~RnP|UP4vMS9J zZe4#S?Arb6dt{fu83U%Xc_oCRO+?vqHi19E6d3>#Bo%n(bc1DBko;dX-#7l9XY}{b z%Qw{j6ft6b_hw=12x6NT{qqvTyf?(*1_OqFn{@9s)nz`i9ZEk?A}XA^}`WOA!x# zKT2$`9hsV{^XTg&tK5iBOsK7#0#;d&bVgwPPlM8@B+=aVI^(IL&Bo(j$r}(D8W4=hg*}N&H9g37G2)nCZo#XlnQ<}#}PcfI%m!`Qn!Y*7>6Qk#?flpVNb z+S-6<3ryTJJu$(-!6u4n-G0c^?Qk!7IYD6T6JL6*;JGPaLJ{WU?|X5+Ao2bvoLsWy zYMxjBdFxLaK?H^PUj|(oi|*H3g~qy#4qmGz2xaP=Z!xiUlcr=a5!;a!-iIrqcX!gn z@$ZWL{WDS;VKT!K9u@tp(Gm2zK%!Jg$bHN8exJIBz*UJcfs~Sa-vygi92tM5E&4({po6 zi;B8BpE9$^I#Y&2@*M0Ps;eD%EKw$--?l)!)}XhJ9i$>pAx%w1B|0{7=K2nkpYwV_ ze!kBO68z(rDk`ge+(m#r`eSVhHLJes<5hFzdN$SbX`%LfQQ$kw+EBt*MoKHrByU9sc6lxO-5a8@C{ir=Iy^GyWosk2QH9YG5l_R}$AOPsz^WAoFj}=iPc_!zD)u?dI>3Zstv*hb;Q4eTp%IOmY z7R8xg#nyqbcFWB9MJ1tup``pzSQ(tFY8SPdmXT>CC9XDm)b<7(M`Ned9%9)!rl*@DmIHY#r__86ZhJoml3I*YL)EF4l|J4Z zBT=AOpu|608lT2m^tddWXa*?!Teu!fb>R>AU3TH-)=fP6YfFCAJX}M8o*p|fM8q9C zzh@F>BEH{8&BAMysd9h@9kt$Noj0I~qCgA#<)un+`?j}qNJ^_m6Wmr@{fTyadxu*p zf9w6dIpju){Gtal6O)FX-Yum&zpmmjxX88Y-f#c=3)K9a>)pOa&2Ohe(=~@Uq2@8d zzYP;+0}SlFq?0s+)zb<5^+nBwQJ1B^{+Wd{?~E#-;T{Wezo_OM9oyf#nxCa5d-e`h z6JT8eT+ibN5)yvlyL+2+xxqkE{k<_C)!?EcAt8)}wE2ZLIi8FP?s?GI9mdF}Vg}s~ zR?wD)iP_LxO|1fql+m5IW+9!fx@Am!zJ38G3|vfraNeG)4~~&912eC*kN-eIV+8FL z1T`)7B@1AqI!hWGYp+X7a`+A6`vQ305R};Lbnfi^D*G@Ku${lGXv>W#&r%s19ZEhT z2ycL`;$8E+lF8i>z4-ds*+=xQg=NVdZ|dhm-mz3-Z{A=gKBK#ZC*C$7=meIErLgaY zB>4A(1Hg%;#Z%(B4{V!VR80lgGcY^-?hS+!eAKGpf6iCs#)}n+1Pqn9=s1uZs-pbO z-ZlNz>n3%3TN!h4ae_A~-M0Ro9z2$3nK{;CffBm&)5T+lYTGUEDi2}$*LItxqJ|%{ z)-H(@pSt|2RS2%9;aI`At>lN5b%lZL{@vgl374Kby1)?yC8MQ)reX@GnOzV6HR3fU zo&t*j#`}LygJqmb%VKhZxtDC5g++OIU+!c|0lwjtG8JQ@LwP`Wu+%r>NvdK>hK|?7 zYcO?n)hADxtG8D8r!_G!4$KR}-vkB6d{euJLlu>faI1r!j!swcx}0r8P&Q7i1Rr|a zwa2BtUZ9~tC3omsbTs0SuZt=?FsuqbgY$*Kx&`^O(fYWm!uH`(H-4GW1EunYnVDS1 z&VC33|9NC;7|$z0A1utEtEmP$nkTQMwT~{H7Hl?W1zDJ2I|%juWRCOh@-bmIdtInw zb(HezS@3!AqHdGTVE3{_VzU-Vt6U?)YSBWNLq#0)#8rFb{G&yx7Kq>hg0}@cPXX_+M&JyEVuMtGg=>EZPiB69?3vjwFgWD z*yz|r^v!?uR8f6T2E@*-(4U6Hq-sgB|@QrhW-o^PKFO zTpTGECHHf;+26;=?x$hu3Kw-JEk#m$O=o75k`Rz2q(+`!&6b5f;!r)b_ogp5KccSS z6!UbAc9|*Ypq*1n%rlPHq6rJSs9`UQc7=Gb* z?_K@aMw7Y*au456LRw)(QAikhY&BOR(^^dGrF^;_SL(WY;dVVe>MZKu07bQ&obDjm z*g~^l;IP|ry;a+C>S(AdeY!OtENo_!offfg155QDV^mzSk`Yqu*u%-YY%nHQO%3XO zYErBh)>T*|P=%g~0u-5r`+$T}p<+aW4 z*GuIrlh&8#+jz;!(-vR<+3Qqk&y@Wbnf!S$UM_L!LD|`$sQgy-MJ?R%GDssGikIn% zu(hG&6$9b3gI^~-_U0EZx)kYj=izXq@Im5;Qe;;c#WOZGt<&Yk27!ik;&qVpyRkt6 z_=AH(LvCQB<$1lH#}2&;n?+L(G9QV1r=@i`GFppE!Jku5&_UTABO&qCv_BPn)o8Dj zvbXms2Zs`3cg~JV$cI+X5*XCgCRE$Qndn7~{Kvw{x$GtmbL7bDJ-2ZM$CDovGe=k& zPOP>e7WSK*E!z-Did54B_mdkKB+(?VEiR2u5>LQ^7C&uk7(VeH#fL+I|M2*)+tQ$f z^UitvcetpcUUKtN^})8oeysJ} z@wVqOeF_{-5g&(iysAFv(hpDN^HyzeUw8fZ@nYnTU=&p@5fP?(#b+1PbVFH63WWx% z_Qm9>F4K@syl|Fnl(?!rML%?YmVBpEy1<+zt~tEv4AqB2r5C3;aj#}Qe6a)%BlPUN z7D|DB9Q}NLe|)=T26#;3ZL_3H&p+NN*1#NQoQ8vztvD@4RSm3nJ3Nhkf(qPC1Q`#;m5)(v~xLsSWTp2MURir-n2tl}Cj!=OO-}(8BHDZ^D zsJ=a$zS|O1`5|PgD~F?FxJ(-iKpQcaXj7+S14PFeu7^2@zpC3@)B zNd>X<1lWCn#8P>AIb$knXS~bD&wnUtYfG5n!FCspQcfVN3Y}oJ zXwwIQ>(vh7nZh0Q@@|j&6Bxb^ONBr3M5KnVX+Wr^uJ>)fl;Ap~M;DZ*rYf*3HrsA5 z{J5p)dFvdYyyoj+jiz1HCMk0D-urfnDKA>wcx8PC`sL^z8`@V_)9Cf5aOuj(jC3}@ znvtFXQE?g|5$Xv$``$=13$kM~z7C`$Op^3>YV){c&XzX&^L;VA&d562=wFIeUGsCZ zuYRs5=h{tWPquc~vb8CAI!xD%?s3+@fS_!Y743%y-=Eux)g8D^zpLF(mwW#8JiX7lfeY;vHj}v|}+(be79(!-vYppvn zx-wT@z->9RIK)C(*-lqC1ra(c(-XcikjR#rw}R-L8F~{GRGgVtnmaQZ9`u>IRS~gm z3N>4J=D2p7j)Jbu+&$#|tuS=-@Z=@Mf%jjE@rfu7Pe{|mU%7od+=S`Zx>p@r&g%UF z&1tXeO4B~}MfPhWT?_ZeuLNXL$IHD$4~n6#mm9}Dgz|DBB>gEaE90P*{n?M?BHYEI;O3?9HgghA$5F7Bgp%JHqRTSXNg9tHt5dYTF1aRO(RlO723oKuU_m%(D0^+JI&t$Bu zsIGj)`b6W?^uHT#i=G*ZhEWRNzKu@G0=DwnO!0LbCdc~?7Z)ewo?j;}4$$`m_>Oz} z`q-a7rCR3+6(*EoM3e;N$2%=Jha&G|p?;+xNyIU-3L6ryxABER?J%|;&%*nDAy2RzyqsF=Q)V^5t;D-}wv z%__M#6{zO#_^wQ{&K7?9D1}2UU~sl%!mE97f>%}Z`zLjpvaasfbiYN9U|$F}&OLrX z5TGZ4W|*}xlyGYYiL{*{^nt^1lh!EM(Y3` zl*%CXxh7YjFde`sh8`#|6#2?gQ>?6#2EbT}V}C%XNDsPFhIYTvr_y9w-E0!tS;ExH zT0(57SjO1QCvT%S1V-l$1chNjJv&$!pJuu#ZYz~q<(lYz`xXPX)Zi`o+(joMS!={5 zpD4S2nY;7sa^LX4dzI-*u*T*1Auo!9;JHWcdsTk--k>*cYHFG%oOSzC)qf@NT;rB| z(_Tq5EyiA|ybRyf<*KyftgOJnIZlAJ=M*g%I)BpJoodzxWcG#C!ouIwo)@}nYw%8{;}C11#w4ber`K^_KR{ZLH#H>G0A1{;QMI zw)T`w5tEfO+quZKHR+Ybj*j`RuvUE|)&2^#Pl&y%zL)xnva+jd%fg_*CESvRC4oMD zJhsOa1_#x+#TJNl*f0(3vx+b~HP3Iug7G&E1w}=z!y=^urT>e*{bXI(19q${73jgzEB|DT7d?kmcoq*16IF8QHVaEWcH} zoZaNE9ARX*`|*|bW2W?_6rt7ft6BLhX`D0;hsTZkp@l&o;|5bY8*u%vZWB`SavA>; zcT^#ksyi%d)@AQWJ8rT5{QB1&ArrmS^jBhpJw1KA!xIC8J;YEi48m8*HH@!U+K9TJ zfb5<|$IICAj4xoPr>V)`+hoc9GlFnja@G;S-N`-+T&aQ&9P23xFTznSz!-@(Ek`faK;w!&jnxVhx?LE_w@Bbq8 zyVEN?$C8LI{S&r>?bWB*(sk-4@(08NLR>aKI+^H8^$yC8BN7rASXt4o_Y<(fckJxe zFG?`1O2J`=R8zU?q=@6I=2P6sCTn71lFHRJ?GkE zch3k!Qa4!Xvf4X1D5jkxbM}-}R8-{F#GWkBaeH3vluha&%r z5EFHW+TQ%p1T_gb4J8mic!1`OpX%jY!XU#dsGaotcj1_+FvZ2pBH{qzIy#sLUayYm z>h7 zO_tqCzwfO}N;ci*(#LNbEL#)Z#u(%xYFZiYV~&Vg=)$>g!1A)?c^L=_nFKhU;y$<@ve}?I2YS zAUIT@bAa$}U$3z&bhgT6VWl<{8#}h8(MPzOl@qh&{IF6?;SPP%j4<^zs1}{!u1$00 zU7a9G(PS4=xD7GA8a#WmsuGUC;o4Nxwv`Xe7s(Xps0QOkWV{l*3rqpQi&i=x?dP&J z&jQ#hsoqKMBWFtjeD`LKh}bPhkdx8-@T)L zjZd*tXYH5h;o^xO1fGdrRY-#9IMFe&Lwh&JCF}5DMIqM}IZ4TN(=_+&jM80F{3_Gy z58WT&;U#6G!w%Y3WtP zI(iBuWH{y6xBWvy)kwKio_J+^hzuqj6n0#J=SNDct*_}MZDf_8j{DNlGgc-)OfJ3u zryD~Bb1C_G@b#Ic5STj$_ZDI(YSDLdS!GhRpfKY2kx^D2xx8>kda_t=kjiC+C?*!)%SS_JLmM}G+P zeMi+*FLuymZ1$+KX6wQSL9gb2_k~gge{0?Aue|xV%>SdEbwATv;x_s} zO_{&{RP3B&|4}<(xp560enbJ@{+}PAgh@S8iyj1AaP82|OO9SkjR@BeC^1>oZS$3NZ`vv)QN!W0w2xv7T_zaswgQhE#*g2#q4 z9LhjRh%Hvd1;~q2FAdyXJT-KkcCh z3ZBa?dz(mjcjOuO1b04q{586yuP^XKVbgLDuM>j=Zfr4oxtj}(dt)JIuVaCYIJlYz zNwM`PLS+@@-o8iji++B766kpJFPJ`(kdl&;5Ir9XL>Cjwk!PJwP|2gpp~v!S_JoD) z#1IqDnNMp@uQ0v=$waTv=Zu2(wJCU;%Jh%-*NU@};qXi3Y2d?7Jt|Ug$bAqH60%=s zlj=gicRn7t3R;r`ohyT&Z+s?wx;INmO73>tO>3)p*U9ei?4Uc~b#5pKM?HdXjlE9g z@%V&X^g6N@6cxSx(bC-g>WZIZWF)Ss=`8EFxHuJsh0h zT|(r*Ua?MfG&8{hmiq50pp00m(=gtP;f8=078V;h^9CLhiQ+ggLK&xuv=gcxFKB}kZ%M!bEo|l`EZ5Ga5HJ0Dg z<4JpDS@6in$nuBV!_m)>2Q0#>X9u?B7JN@=KD$IF^EncQ4~c#)>>rv$C%EHu*HSYb zDtbhKfr+_)Tt~Cq^CTfZ*k(2=+5KW1!^=x_mo8DE>hqlR+8{`@K9ekG5MtBevpajM zJ?r#2j+g&@@fWSoGx&JVYX8_6*hSGU7XX-vv!0Z}ev7PKOWdSyqtotlOEG%VAM3KD zmne6#zFN3T+3hLRB#%Hfo`1NfpsK zY%-qYYu2g1oImn3*^C%47&9C%6;UHFvbH;Y*NKTppe;T)SqWt&F8%!xCfPthl#Gre z)ujbI(t^0$=&o-ZJfa@i7W~+(n^b)A@!sUZ10rnr|5)TRbQ! z(K%a7@m$T{G<|c&r|^M;JQC^CWwL2+AYc~}L8QOICKD*m`6T{)rhLbGcQr(_vf4$a zSK{N^=7>KoytWGdaqc&vFj`%DMTN4Qs%o0&K>rV)d1d{fy=__Mq9@=Oo?lpa&{zo4 zy2Gi2D_Gb?ALlMTeE3iyRUqcUBkKt@)(ccu7-V}c2aDV@OfxMh`VO7Z?_)CO6Fx)i zrH@aVb(1h%whXh+!T3U!<7+>Ud6S6EX5&dO#+&XI9V=efnj6i5B(Raq6{ zpJRk5&;_OuTWuXJK9f8AxB309%d3j9J#3j%!%auO^(ZTk=4)$>D%uu-cp-x}8?-!n zQF~jmR!^U&cXbK&4wu`|tE%nO145~|I@`y;T|2O{mH=$ZVFl-SZ2#EpKzuY9>c_2vZQvt-oT zF#&~@V*HB)bF7I8?uE%_-L8-=1FMBm{^xtN_OY?wzPo(US^*9jXL*|l;V=3LP_tBP zm)DB&ICxl;I|5Q`$zM^oRS)N+k8cqW5;Qt)huOR~d##U-zKM0~*7Y0g;J7AQcJ`sF z$VMltwqz3@vdJt{#qM`-+o{;I$<|haoX7_c&rgQUBe6Dh&yeSd+V6ny)6eHFVs{L^ z5U9@m^M>NVsKu-<&8}0b8|NLy!@?mx=Lc1X4_F}I^InZnBdAtLUw^@oAPox%c;7ZL zp=O2vT^(B1iTLr=xp}aO-`|DLHYOnjl4^>bUcGq}B)AK?*6a3`>I0B~RtNaI4Vm;{ z<##mr#h}=om6dg%eVz(=|F>(b=VF zR>I6ansY?mnrMS92wz1~F#4A{;n_P%IVl|KfK|arjfaT%;9XpX1hCoCG=bG8z?0)~ zG8W#$r6~(V?=$G?Hq%>k>2O=q8HSs6yYDX?At(W2iJxOQ?fso%;l0fl`>5$k;gVu` zd3g}{W`p{1)s-x3HZ|OYLn+dK)asl5<1ZE#a!G~`$+G6uG@v3JyCWPP6}2LPj=K+C z+1O=cfGg@8ANnX|WRw!ry1TPyRp1I?J%C*7fTv-67qjf^>H`(t>n1(%s!H(p?HjH%OP# z-QC^Y@IL#T|K9st*ZXaKKo+h!*E8=J<2SO0g7Jcni6*E!{9Z3Lx+s}5D{A#bSrnFs zRK9+(%4&_Ws<^qbz)nn52%RmXyC=cxT!w#Bq#zEn|EhWJ%1?7wYVnJn?~4OE z=j!I>LzcxCi&3E+LKW>sN59mr-zd&@W22+-iLr`ex@w=rJ{G0q`0tXDC5(EAkl;gv z)!lX%jr3s`oCVo1cS4(-3M5jk!A`Ln2#N~~OUb4M== z7Y|>`)yC>aSt~Juu(oD?2#5*pt6kz=$H2nF!XvAQ*L#j4tBM_IxwYZ80Y2`1e{XN+TcAJ|b_<{m zae+;oyiBQ1ZJ}-4LtoQh?1_&zf{Q;#IKSmG8)BRv$>N)7kwBfy8r?=YT>a3?7#qe(a{#*yFkp=%7VdU}3WTJgCE zP$5mLOs8AV9{LhV;j+#`>%bZjdLl{cKQak(2nvKe|9Pc)zRrlg!4 zO8=`($dG49+Yu@ljd=~T-q{=U6Au|j8FBIZY@q-s5YTw`N61orc20yn5IW=AO_r!w4(3D2;bK_PI9I~32FA?+mZdSMX!J0f1 ziKh_iDUM)y*|lwQa`I%QDP)JuvdrBEUWtW8GK9m%RX&p&`9#|fjcRxiUR?aosic%^ z?J-~DhI;lZxit2!vAz_6x(Hbkc3(_TeS$`XAd~tQRn*-;P%OD%Oi{6c>DkV^Cz64- zim%J;+a|=Dm*G;)UbZ^8Fkcdb6J%K*N*0z5Cjg}dBzeICCi=S1#qUr|}vV~gds*?GY=_!ckaT0{A7xi1^w zOrQIDd)rz5cn!+PhagZ;Qbt%~bDx#BwBoifFK12jT?j^)%|W}smaA%L%mY}gZM`Y`J4ONVl1o}WaA!4 z``kW<5qChqnX$7j`cj0|nPTA0&;KaAJWz*YK?q^w=jTVZ65Oo`!}U{LsGwAm$qMG0 z?tb?xRkCS*YYs63gCjCKgI?_yfQ2kF`ca>B(P*~{*5fN#oW@l}Me`W)+Yju&!AKYg zr^lwp`8~E-m9?A0^7AupyFL{a6$47eokZ$8c5eL7i=_n1(=C=cW~4-VWHZV$fBuN) z2~ST=34Jga*`)n~eyT#uWgm)MJ^g!`ugPR2b0CGj0#6QTJoMHq7FFN8S=VO?MZqYv zX{SMTAgYQDZSOz{Fz|CizS0LVKeDj+^zQD)-wVJtNG7;o@$T-nzrXi#e`@gHQ4|Kn z(SHw-=7+>c=Aw|AneCnMv7ap|9UWz^QkSnNQ1fMn&gh=wB^_1pG7A5SbJJQhm6DM; zZS^LKb}YPeh0Z>yAhs#gK*XkZ*I^MoUTP@-+cpT_+}56c%Z@A_2}*IC)N6zl=SKRD;8%BYi+hH6bw}MAS^8G1?A&<2gW+avggCmfR0W{ zMQECj)!BiJ;gHG3T-Ca~?R^7J{``mg>q4BR{uKP9f3G>-&OCZ(T^R z3okSX)Ze}n9u)%$HG+o?Dy_#TB+&`A$Q=G6-9epMlkHiR2BtFvFa3S}!oPY4h7Nib zE`FAjU^c?=vL|3|HNdjw?nVnf3(lB=9t6nz*X2c4i35oodavPU@20@;?GR3i)81jz zzM+nVlL}4cAZ3D5|6Oi17LB8$ZK9E|uyD*V-z=j$b!mwR)94)Di?EO12}BFt?uT8W zOdf|+^mz}-T(HzIqX`R>I^O8n(EE7aMa<{UiR%2z`)a>5ZCO&@v7?xpx~K7%gR_Qo zHjS@OR8(Y2-K#cwODs7m;eOkbkDGYkX>v{Y2QZjZPx{cobDy~$OkRZcb6*D>D9 zoj2POFPS|<5K3;EvzV+Od2^K$z&?&M;sd2QpZ>WK^0My>d!=h1L4kaRtrL;Q!8UTk z7Mb2sn}@uFri7}*PVfyIuGpcu>${xCI_uF+-@d*fzaAbsKCcRe$}Px=LTRPf!FbT> zucxYR#S{#s!+xq|X5uuczqvw=Dc$qcAUd`dKb>avrg{JVUhyM>D3$WBiu_rw_lqU3 zmGRL=&rHZl3cs5f8*6J)sClmC*WY~A7s=_s!z18ACe~8>dg8XW7#f?W2>Icw|Jxk>Lsszgx#rzvRL0{&_J9=LKY-3ST`h26IyB z`X&}8F1(q|mfr~H%~@e%lOvPq0`nwpMw6pSh;qeu)9)Oh_tCI`@8s|a?NIuf`VB%I zgAI~ait59m&@&v0ir`9{jl7#W7fU9u?apW=ZGLgZ3^}5O2(TqU3rB{BQ*RpLj>pELkHyB4YRxkH zF0vjO1^b7eu5&C4DygaH@LM&uYx>d#Gtbt`x6L=E?(P->MU)wAq-`xe2&)HvHryAE zGTq{zW&Vc#US8Qmll&ip6qsE)AjAzwnFe(ms90Di=-8~bOVrI|S7U{vZx`Fj3$yho z&(d*rt%&9~W9s@3tcUglk+zfV zlY0{s6y$7dtR#8Tf6oGH$G$GMx!~}BaYA-tExx<^#W<`UFfof;Rso;Hpe;_BeDKoo zNo+;TQ}3-rwFob6$5#{08vR`>9xhaWov!%{Fy&)sMN8(J`kb&Bj)i8u`gx-@^cd3; zawuOhB2^>RGQ6^sEzo5vN&&z8A(ElVp4w8ryp?3QN{&;T)8KBVlWc=1Aij0A@@k_V&z! zCYvAXGq)l-?b)mWbLGJv|vY#L5==uixjU;fd>Tqz67^TG{ z@uMVsu>y6|S)EdQE8wK|VMD9W?EK9?xH3h_%IMqd>}+>V+FTm}#iDgMg2(rS57b1t zytW%JioLP(ry-{Krl+u$=YG3`QLB0}oZMV<3j!0ESgbPl_b{MBEvzIYycZEM4aAt% zE8aL_IWn5RTrIP!1bLVK38jh&9Ze`vhJ&+qpv)a*XuR589=`%=Brh*dP+VIb9qLhD zlH>BVwt-#yN%P^S97h{2^u3#tvVF46Ipf)^CnN3zR;->!cn8VcKc=qNP)`kQs z8aMzp`G;$25AsS%;%Ai)a(%#vQBB3a*>f+q*S>U}R;?jaTT?_=nKpZuh##5#7> z5RmGFxs+y9Y2z zkpzhYW4|1dDy^;GJkxh8dF#@@=WeVh>{Vo34T^f=IC$3weY;6&D@eVL`p%HcEw|D{ zBvsH{EXbF=?AO%g5Ai5sx+-iQAxSu}2|D|<#-Q2TK{7D_H-%8s5SK5bA1KSF!4nEs z;m_HGvi+JPyT9+N6)XhHbESqv7#Palo<_7AQzxKnF=ebk*5kJY|LTGsx| z<^76AH8qU))AXVy95h__-{@s#WBlLG7~k_6ndTTu53wyOX*gi?;uBzl>|SF zVLdfZPD(7gjI9my4Tr}>gebwmBdVqW=x{U1AveS>5?hnCZguw`(F@g1dHI(aB|mSN zg(sQnR!VT8+}?R7_2s}UPlBz=9QUYgrsQWILvKKN0y?dX=I+kUOn(cX;k1ZnH%S`e zuIh&mG{BS%b^DNukq#mO3Dcf19VDOxn>rY(*sX4e{Q;1d%eMIGo@X^x(rr z?u|bC261EGK=ps?JKC| zFdoc=-_6a<6>33wVhkl=s zVxlLBzqF{#pDlVDX6kU(?ii$egL0_dDN;b~5Lr0F_%gYYGKQ$DiFUlKGBk44mod05 z|HpsM6yQPVB&omZ!kww?iH6&z2z|>I(F+&to-cHqVp=0)?EBF_oE+#-0F9+rl+hM- zpGVorNfz1d?vEYQXmm9*vzi~|rNeGak7xWhh^__+c^c@u>EIE#%Yp4L{NIyZKkGi- zs#qj!VJTc28yO#TJDZES84l2;HVd1ZyPCPHsf&pR=K1+HXaUJh$y-cLd|bS}KkLXh zf?&SU(9i(d0cBFxYCt$)ahx^$f_bUlOpEX_eDCK^GoOdcJ%^+H1h)$ua@lNaPE>n# z^hTMYBI-HCxJX;NPgF_)T3W}8jcj`DudIkNz-n_a^&KvX;miUtA9ns{H7Cd$pTpf) zskrym(S^C|K!;t3t<_UwDzSgW>~)o)YVX9vM3C_hz)k5)gLJzYwyLRO9J_;5nqzESwPhm>cYpmaOt>to!)h}(1AV2_}Xj8RMGImL(4@gnrgL#g!obO zRh3sqD?YDyFfn3K>je(lpusjv;?peakfDSrgcqDS9^P!P>!)2IPLk(O>>T=f*{W~( z%X6-r#F3GZg3=1$8aaE3Xb28g+g~9{OP@uo27k`fpMgPN4Fb`Z*2+e2?cG3=eio#t zhr|eh+P1zH0NdRLCiaK{qCf}2VB5}=oQ!s|wdP09X{mR{kPBCNmwWnM=x~JT07Mys zE#wfli{HCdhZla&FpVKP30>{aznxY!@C;+(*F>cf!_N_n{V3$VhM~c(bzl>g6k?{> zYbolW@Wn2?!Zj8C;31@>tcRpFHw(0mBDP#k=TH>57qtOV23hEBG~>v|nHH%c%WqU0 zaM$$g9i7?OYHz14rQ6MfUea`x_bn`HYipv3xcnzsSPmkeYqD%Mj!|ui@~njte$#3E z%+2NYSrd-65N`FHWwk&OC!#)*%NPW0wWZaOUe46{C3MHG@2J8iIuA212L~#1VIYJA z4FzWdnPOpLRAeOadx1J_J2A0e%~}=%Z@`97`LpudckeIvDAbT}_uo2Fnf05gLj@$@ zzW)g4@?34%9k57paJ<~$s2s2>N;sU?WVpb7EoVzWC#$%Jwj~5Lxsa#TQ$ZsiM#1b4 z$?oH7Nvq*0&PZGK&5in0W2w~%K}DOVaY_M&r{gB);xZi7hqWG}zyf zv$8F()4uqNmRWz|I>R{BLqLCQwB%NLkQ8r)^)I%hO&%E8ob z6dFkx88|nz$l%$<&UDS9KW?LX&K*?|nVI7t;#o$-eV>AAUJ5ntD?ftSkH8fyq%bRM zN*=rQjdi2%KWb~BhoH(%8PCWx${HHfsAdLPPEYmU#DA#CG#iqY<$;EVsYq)kg&qwP z&QqT{_tqg)aJc1yPI^o~IJhbvnvJrf!SOuIDL)8}4d*kurKq`1XKkEG z8__#{D>Ja+6Xq=J6XM*`g#1@^!TNYt1JQ#aVf!64a>Y?>Dr;JRCQf{`zrW$|BBWs3 z)}!hch`H{zCX9pV;QZO4hQv&aQzR6cj>O0AXQH!WU;p6f42SP)6(!99Yr|zcltQ_T z8YibIS0c8{!!axtc5Kdk(Qart4xubxHVyJlun(Q&W~0ldtAc?oX#z)9N^)wa7c3E! zJq&(zWX)y173ufyKYoDfP)&)NgF`}0J>{X)r=ak3zKwj1Q(Rsim6JULiQo^bEEy#}6-#v^M=35j3a_Hv zZ~(}zg=d4!Fn2zyUZaqxTxiO5@OiptJ2uK;BMmwXR_F2TmK0~czji=dPTn<|aa==p zq)#!05gU7$pIToxFhW}7IlX0TawI=~oia?%*f(>Y9ci0ib;}=mYCi*);nCRWF0q0pG`52H6SuIeFP+A`rStY;5D8l)9KINi`nv zj~>&fcRzlVpnyY^l(+XK7r#mN%}e&ClJaVIG*XPhjKT?6&s>$d;pRxb9w3B(Em1oD z<>__2u^JjnaaP3KnX@q{?Y6|JN8O(KS#`#2dKxQ_jP~v3{?N_^rzBS<2el4 zK{{rZzaGITGP7$@F4Lp!>WGUvX(K4K9B{TR9t#(!=#<_X9>joRyAE-Xis7xs* zB_vh_{05qfGprgOs>H$!QKS3}o;sEhn73TL2tVGvJA{CZx|yqif0qOYdwo*9XKXnA zi^Y+=&qdMPW=n|B+E*VEDpMtj6)qJFD68&qIK-f>!rQVK(qB~ATezhwn16(Y!Ou%z za@9n7(!^rK&dH9>GGhLqNun3J^_{mivc$w82tbf6`nfJ_DeS2Qj8e`j*O}TpU?Crr ztOMpIm9MaP_I(;SFx@`(U=$PzwvO}-@6D9^czW{kaD_$~Bo(gm|M~L=t{CoT8DAsi zpB|yr-oe4DdYoOm!p|B-P1zztsVr%w@oM<{`)HDH|KSCcxNGzwjO@IZ&5q{wxlWIa zye#2z_(jlU_r(1cwfX(%ugy*4resZ!1(bV$g^mA-(y}Cl-QPXN3rf5pue33r8{rxx zlz`QLKIz;&^SWSDp!+)tI)~#yyudYH1yv=9ol=YXGBmy=N*U1;D*E^NR}mEfkqt2+ zs6>H*be{|>j7$-)7e7!@u`<6gBW!fPU&O?^N}LJA9)Tu9Ml)DN7plf3#>Xd~=k&_| zUef?R|Vs23=F_lIdZr#sR!D@ zL7Fw?^h1V#3reedS%Uvcat4ZTSy9#KOnmA!98pAY&(3#2-H?=yE8Vmuu^@CA;o*Jo zEEWwJ|296e;>z-Y3Ec|5ar>0ONLuQOgi%4gkbzg`qp+s&+|-+Xz6t7GNSSh0NrfE- zx?ww3Yv7%C^?R4tELtTL5P9D z;$B0;)4ICVY)x2bHF`$G_vbTnNaiH4;{bi&YFtQaWYuK7u7I*`p(g3i!L&9(E!~Ur zQ!{7MWKLIG5ef=5ew&sH6bh=usd45{CK=p$Z<0CPnD^yXJ^>urq=kkN6#Zy=x}~!o zzE78GbYP%s@``1{oRtCI8vF7#Huli+o2u-3FOtVfO*+A=KhSe^hL3?^aRqW>DS)ad z7vfAyW3f_T$6z|_ka^CSVvYp{8sdx}NO5h3^5QY4X-m)s9WZgVu@kXjWEXh5vO6_j zDSGc#=%dDwuupPa?k(Ibrc4ydE!0GO1j}uzeaY+G+|Ju0Ix8~J&IZeg zV(GkcoCt@EEcBy;zIY4)>^P}j?Y>Fpa$*LR)d^xZgd}f96I?5tl(bG6QyS^}^$hCm z()W$u8T#0ivIyN=xcF(4s=2O+l87&RrHU_ z8ecT*?9esDsqBiVse3W4h=`n5DI2ALXVumAa%(h|aU>?N!8QH|PrbEVDmktRbVth^ z%yHPSb~x<0vq4PDC=RsW#P*-ET2b8Z-NAO#iOs1?B6797nTCQc%KdO2Yi!ElcA$^X zNmIPJ>c1z=w1jQ8;r*%p_}84ST=G1KN3StAd}c>DidmV-Znx3wG>+yqNjqm^C?`Q7 z#n;Hog=1v}av6e@!R4cQEHjfA7Lp%7?vt@MyrRtJ(9+TQQ#Kk_2is~)L9qd*APtVA z8Pqg10kTdPe~>S|&o9nxTs^7>eA*M1z`0P@2I71unQK4QoAYQo{$$X;>})&i^*8zD z@PN&|zAN03psb~=rKZ+CR53a=R$ICEqqv}$wA@z?p4-jD#N^a(m3_8W5^NNK6rSv6 zl4CL{DG^{e@$m?nnqoNiCgHscXn^Kg8u>-g)6-Jj9Pctilfy>kv$F8*&Pux&gM9wc z?EIBD4Ue8LMlpUi2?jcGv=-XQJBRFQkG)(6X3qVH$No)}y#auc%EAe4dk=j+B1!xB|mijk~>tNzsS(|_6{cHX6rHE4Y z4J|^zrkGC>x*UNJmr=n{HYmCmbfl$OI4INTDrFGGGEl1+MppzF83onVjXS%g{KxuI zQ+v0-RHrK>X46p+NGzXyp)cKI6#$rpS-5-y3~euKSUrCvm0IQ64#$_49)YD(JnP%{ z#7;oT5&xx`rz8Wl>5|$ZB^F5yy3Fv;E!YltUE~A7vqk<2PH>`6`2bk*YY+3&V1=m-EC7$5U;0>qL$N_<*dn>9Idt3MSAnYEolnGfb|w)$ciYM0ctSv<`( zym7Z-zib12DrfzvcZY|~U0J(dOn1-0s{xE?^}@zAeKNpBIpi!?S;aQ-MLCSvI+}`d)2@xHX41RkBS=5o;?wivUyaE;ppV-p7 z&`4oW@yH8_DP0}0gOoYEhR7r? z7b-J{qy)o&>SSNE&hl*!^TEN*Sm9FcG(!IZw(FOG&x!n#UkyUj7QAPJ>unr zi*ZL#+MZrXNI07<)E29XCHihr-*x6fg#HGLkL|=nFx88R2oqNq{;Z^-k(iVgk{{(j z1|FUjN_4ZeBLqp17G_j{=3$lEMKiE)hZ_c&^_tqYrbxliW~Dh64d;N>jIpL5-^qwz z)i^fsn}2z4M~;t>kY8e=ii|Sryn%IY%3Cn^>xA~NilRpUBcLO5y$9A$UeAl_%EP0< zYN)cpmL+X%S$;W+(l{X~PFMX{&6rd$1#+3bp6~_2vQ|Wo@<6Xq>*VwVi&R)#j`Knu zPJZCo-#5e*Z>fPy5^vf}f$?6p(dT6Oj6wo40Tb_o`6WGYU5`c!)`|xG>DT91|` zpx-6lOVgWU+~#nA{W1EqBHn9-zX8996B*gbc^mx;%vq4VsF{VP_^M*22-Ru9)lr2M9{bAI{-7l3GuCvg#xJfJ(vQxszOPAQMr=G4VQP5n_-o^gT1 zt7rV%n^p|0FAy?krxl|>dcGJOYgroD zJnk<);Chep>xFBw4e5#{L2u)^7(a`<;Nk6Ysn2cvX`-R2=X8FRQPe?Cf2b#Sdx^G< z%@v^6!G$bL61eK7FP0s}%E*iYkMc=XX(Q3W5RcihK~I)JpJS|j)b}VW#{#0oWbyV4 z+{?Qy>T1pj?w)iUuCGLS6%SF~mqJ#P-RX}{(IipIWkSJftGVuGchHxX2S9^VUIw4@ z_z%5o4R*6vHM5_E@jABg)yZUeGRdJw;p=Lvt&HDEziqm`gmRV4#%5|3`#dVqXn$v) zM6X>^lC>%vGJ}#ir2>U7zKgLjEt$pdfSoS=`*f+@ql{NZV(6LbP*yN1CA#F>(Fc{dBymEof?3?;yM!V`KS8txecv zg-nGQd^{%>(T=ZrJi1pn^VPqSu0un94A0O1%U+Ni?B1gif>PQ3pOM;Sq=UV^V(-|C z_`5EY-y|%~gz34A#yl>^kDe2czsXuN2aYpdZ1sm3H&CH`3-C4c?axKb%E|Mx&mu?l zot!Sgw_E8Gv9O=;yY#q>AXQWwSPzuePIrFwugNMZ(@*zZSUyr2+GHrxjM3qd zGV=0H_Vy+-^9qWJDoUT*Y@bV_qccT(U>y&{h()Conb_#2G_uLrmU(sF;g;o_WMmh# zw+BQ)#STcLXjZ;oZv9TM7AdWMKRO(4V{WJUlx-o$|;pz@7OOwvv zy>N@-Cymr^>Ac>ZZ<`Mp6M*yr;Dnq`#z5B0?&|C0B)X_iZaU4`=dUcVJ3HckPV65L ztd@ne!@_4@pFy+byy?8Zy157vA)aA6G4Q?{7uzMO_`zjCsQ%~K@#dJ%^LXXr2E%gU zEqIf6)E_InvBybN3~Uv$>u;Q5*BE<+oQ+;1ZGaXdC@9cze-RI#Fs-yuCx0jz{mmok z&RONF(>_2F6jt2AUh_|TZI9%$A%#FNDt{(bf9JlCk2repIv5A1TuYOj%b1v&-e?U%8Q|Exx8==U5FMH+vE83`dI8 zGB*H|Zp(rf+uV!eqq`C!B~{Y?8l8omUPUOU)}^Sd&iXMBzJY1cQ%$Y+$zzV3a`n@Y zoWa#)gRg&J{qlUH6{3$IprlYT`{lI=0++bxj5Srv$o~0*N zI%c?-;vA+NV3bA+bk%{HS-fYzsB5uhgQ<9_2dg`l^_5-GE)0ARL|Uh=V<5}mw-Qxj zMQO&v``cez5x1n2l*o9?<9pkyIIe0t|gAHpT;Eo4rZDy*GS^V7t58D6Qw^C8|Y=VFg!$GL~%o!c)@8lpr>OGThW6^%Si z-rc{p`T4@vK}!RvR={=y!rM>FfkDZkC?)N|6-Y755qC;0 zElWx*MPy}XJt=AcNRbDiFra08!y&fVSp`BB!~sOaCi05uS8hgWS9 zMuW~tNh`A|CL!x|)Ss}q`F+k|+hJl_Z@k?RQ~miGGzA-ri?bc4-zHJPPWVlkB=tBw z+_Ys`skzrnwCUQ91lY1tOK4Y$eOtv5D+s-m$l>vUp1^pB4Y4eIRUxSxcra(vRs(iKoo7S+UG&M8Uck#{EJ z$h*0zJ1V~wX+F2SvmkiJ{o4^2sv7)^@^so$qsm>>4wT}y$Cpf4SW{ex*z)Pz6a9a{ z4%Xjo+30)0P}bAz{Q~eEu)j1YJ`Q4$wBTUwj(A1Fp=R)Tt{vfM0mqYpwSFHjBuJ8Yv0V9LWNE+OJ?1ADfP@q!D%XB?+mI{* zd)?pv+(a;u-60|#V`umeQ4`MJjE6{SH5333oHmNeGCMMT>ee?V@j##u8-O6n6Y~^i zXEU1h+QOG#9R0~^bUw(8%h=kpvj$knJH@X8$)?+#za~J_*Y;THv=d#L=Cjz~-G%zN z)ZD^5oW=8L&`TfO5Pa9I1?uQg3&mBz&- zm4o=^HkX+x8XN(&I(y$djEAd;Oq??9U$=dxx{k~TgJ?vse0(h+&1rWxxg#J0@1Hy3 zg*zoheGdRy_TDJ}ZBA zIp*p{BYF+2LU4qNOqD>Q^nYf}GiuxAE{h9>wx-6$n#^CZW&et8w7ZFTwV9F!0J zzY+6#qRY#Eq9CIj>Q6RdbpUlqs`Z=jUjJ6Z{QrZ+YQmz6C8QYfw0Nt6x8^W-lF9HGe`lEIXE zG-3EoV61C1zm82=086GKE=s= zX-{@e*Uia%VoGeC-kIjFxjCWN{DzC~Yxh0@mr*5?;=|1`0K=yP=x6|}kib(#M_v6> zyI(-}h_Zr$<pn}jd`g9)P9(s5V8Hh>eq_me42zhNOD5ai4_KX4I^-4-O zWB$D}p+h6&8;-po{q(znnT`^o$!vK)O7Xinwgnlx+(;?$4QGBSw4tJ<;&j<}RaDeJ z;YX1n_`pCDmyqC;#mGfY%gV<$)aQ0u!gT?pWn`wI>F{$9KI=-#mPd!c0?$Setnf)i zBiuJH?xJwaaK@{Znw~w_r|PylF0f(t+d4T^5jBm{H1J&;eLbA=zz$`lE@NI zZ7Vj4#T(U+MNjlK|D|gJ&+r+ovZ|_2vw@SbaRLx&r=*AyBtPr!0y>z+ejbLzb&95= zLl*1N@uWXmz{_uQpAbBW|8_~^hH{D})Mn*xmMg1?)5DBDK$Hoo~eo(8q zr4whIE!d1p82GGghVbTZ+w}_@4EMF}%an!_GLfO7VLlPXP>rF6B1dK{dY;w|G4Jzl zRRZxCJTNWZ@jCjbwhI~SK;1te;tzRr_`o=V^)rzJnVydB&TLN;Y0PeSxn<{kYXF_o z`Em362NusKh<~3iFHq)tds?o~kI*cp!$$KQM=nqjo-&hn)s#Acjg^KASsy=sB!?Zz z4Pu-vUBnpRX!osHqjv*Ye-z zGsmFf{wyvoenXX> z5OZ~Bn_p4<8eS?s8SSOZMHl1;x`3mU*xU;_`V52eWbSDsQ+wez!w;Y%1bOMfyJnF@ zJlFSE2U%X%bF~&T=`bj((Ic9t?h2=d6ags5D^vCI8%0bGI}9k^Xq_>9j(RR656hOW z*Nb9gIr@*Y6tFkf$5sQu0JKL!Oxzd4=WXOuuETEWIzQ+Z8>{#@3*e#iYMN|+hLZ{K zu!dsz+#bKye%`oe0N|(S=&-GxOn$fTrZLBsuH*43m3C*3v(*JLPAAg|$iQaF$4?eZ z;_*CqR;EoiSMkto4n*CLYB3I!bhLwW6$^nsrKC{rDs^z=T=vFq<)ZA;;=i4Tl6dr8 zl%%K|1~IxH22M`0dLM2@?C_+pw{GNVE#>f?8G-~Ny>Zqm>u#$0$UzzJ!>i&<@!()f z*V|sZl$i481Fxxp(NSMyR3fJLJUk&J9vAoTyuA3>51B0;t&a^K4i{VP#8m4QI|x~g zjJ&;H3tS(U7SGSmTh5xFh63-1_u&)I3^)ttD)KHWz*^C}WTb@vERif-+uO$EaL8<7syHk1O!xwlWP6%ke>obiVF+T2uE=>b#%sT+6_JIKhV%X-!B<=y?gfq zGClaza7y{}lc#m%TJHL~-*DG$CEx+H-dI=Y$0Nied((b|?uQ|ur=qfT#_Nj#lILcZ zCMI^!il2@uKB@sS5D!Or)mnP`B3YOAU8TEwV|_D2^;${Ep*}^=xgj#rrHxioZ!ilp zvqYrxM$bBOr$=}2+BM!5cP5F2fu-YB6)b^weCjs0GlanO}ddOj>C4_pP1ScE0^0< zV)n>HxFd7{Y&!IL3U)$VOkSV0H{O;Yst_3Y`huSzA#hr+ffk}oqzx2}75XH)FTbUi zw|h_=58FI;*x5Z#T2H=lkSIN7q!YS;*)Oigh-shZ$(dnB2JG?TGY&zk!_IQr3GG%7 z3h`#M;Q}_h=fQPG{fd|6UNIScK%Lc70P;$M+r!e0zN;a&*Ig`q?=Zr#?+kmc=pf&~ekPl?hFxBI3|+hcDqgHB_W=kG7x zhqyC}ZPv@(!7cPU&#qeqkF$1$+8i&Jd<55*jWq^SrjKg%c81>1b`%7yt82AiuJFAN zU2p6Jo<})hpBLR?K*_ezBbUDV-1Wz1_B5q>M?mx9oq+TCcToS9n05hNw`TP*jrZZ? z2Mi9+3D?c;K9+;l$5av?ul-G%)@OfY5+1w#f%4X^p5bKvtAq0Csx^@HM5GJ)65oF?^`<-f$Q3!3ss--K2J~b-iHe| zbn4e@506S&IB@cExY!l*gnYv>Pxecu)uwFI*ScjZwuiS;-iNBXWhrcV zc6@;ht&iKceG`wfoZb#K(T`{ImCAhKDzJynahe)aQmcQeJgM+wEaxMIvT?-0fJJQ&(r%0Zy;|Kjl7_ z7VoahkD7j+auE0w6_;WO^tgRRd~S&Gy5~N<2@;box6y0&njD+LFpmSRR2PZJV?84i z(_FocuAZJA!vQg9u#vBvKri-ROg*T)Uu@KtF7&~hqUwD8JtHS!ySux4RdrDMBX7dT zH?E|F_n$fu)H!3!;FGX*bk_gp;SXNC&C|BQ_zykj|GEY;_38^4FrHLk}bOo!8dmX?+l7S2`~4}_3-_x^jC7&5IE>ViT-SQwD-JA|4w0Qa<| z&J3MkxyC#$D~sfRK0Q4MNXWLQYt7>g2a?u~zdite+J{{V(Bz48?j1yNEW73)kpEq~ zLcOlG7|Rtqq<)PooQ(4dp0JxH1mLiTiFg285q)@IAdS_0Jc@whpCS})9r{%OqKPVS z=T6kr?MC4K_bpO)4-QCpZQI-1U#)?w=XSA8r(PA9U;NL1fLB7#sO9;?-D%@a!xP*ON3ls1w!V#xjb|9|MSHrNIniVPW*e=4 z4vr)09xB_hI2*&nT2J^Zsjhde3&<82tTX=0mLjVtV#C=jfzc$~Cg(Wrp1jrzTa8km3IaqRs(t!=}vUw;JwGJ-qO z#l=Na(?ebT4s2WQ@7+OUg$XuD8l9MR&{_V_3)E6c{kr9NB3%*>2*)~}?h3R_#8pI?`qEvl-@!Z`X8 zfXd%HG?c;R@E##1Jv}{e=6??cGYl&JIA~-4{A_jn2(aO^D?H&F9P^v`J%)02QJ+2Qy*JrcLOH-@^sGaiN?Sf6s z%~kSQ-e{Z(_MjUfi~5&F1$72t7SN84gA?Mjw6t__fhe^^`u6qKTaJ3$MNW=DkYHbD zy@ZdB-tq61?7qlHhLCV{WPLuGo6t~FigGiiqNX>QAC5KbAtKE1@_c&B#l=sCzC)p) zq=fMX|IcIn==i7Vsy8yy;J5;rkF)hQ|MN{n8zE5dlc!@~zXE*hwqaS{_hN<*!dKBcq}$#8;{4o=kVFCCN(K^_PD{)FvckWRw;a_5T=9 zt*Wl>X^P^pT^+3>dYQecTc~rAHZuASmO1KcWvn704Y+uC;VWti3bqQTkogq8>qKOAB;dW&?72nx zD8%Q!Y=lQE8iHnPn4yTNC@wueBYO-l#XJvhKyeg{I^_&6{jL`QS z*30t^Yia0RA#0m*`S}vj#29Uo?&9w!gaplnQr7svdGYVT4|Udj7bDNk<*b?*q-B zREO<(1406z${}q);B-PTeJnPvR9s;aiO7XcoZwwDfxI7->a#|e4w z+cQ&1;?l>(LpQ|J@{yK>Dk4)E+<#ya}L%B?8W~kAu=tyK#4>Px&PtSR3 zp6C2^{@nBYw&(dh`+mRg`+h&4&->0STh!mVYt~N~XTyWG*^JJ1MmSN(ook^0jrOw~ zW~M?wnUAL@Wa}i~yEjb!l3CO5+)t=O{>>lmh5a#I+SZm6A_!}tY=%(Cms^U9%UkHs zZU?a;;2~NS6f{QXbbh89?OhWssfxkqw{Z|(Ezh^BTDC44rlq9t$zPJ#W|m4yjgM>V zNG>ieU}ck(lJuwe#2xGzc3fvT5ID!pcJtVjTTmbz%%4idU3%SKp*%Z9v9o%IK3eKA zP(pPg)CDY*S;!W+3ChR4vuOD*WUj_}g#-q#RWiE3zkf8|FV$H_nSXXj%4lsvt9v-F zMAU!}NMxbIXKW#qg5!M$R{me#%odwT1Jw@_5@zNKQ#n*aQ$4NEQ8cHDvu?M;D!ZAc zVDwqz+gc^N1rZwy$R;CZ3X)&izvam&i+uarMR$^u=f+zoPxF;J>>b@q$nERV?mX@A zv-*0v5gQuM7J+LXYW;jeQ&T2~gvY$cnXr0#p56*1cL{i5mTop-ANeLyQnPc@vN+;F zn?tF3DT|P! z`uUA0B9S>c8Iz4&f>bU(WyvDO!y=A}tWWD6O2QL~L>uCG3yGfiVN4FKHu1JjI&#XL zR8*u3R}kcYO@_hUZD6(vzR%=QuWJ7F!tU;#%5VPu{tx$Uog87Q&MPS@GId0ZafcUC zmzUKGaK7{JUvX<7jDf}K)zQ>R(+?Sl-^fnUxzIaFR?`e4=%LBimw5qH4D7-Iv6wpSf6gWT6&O!4NQaY;@GisI7c6JKJ_)F$N6= zw{NHiu1bO7KesyI)`uDt3We4YRo2#qvZ90`0<@Ktb##V@Pp^g=P9gmB9zK64G<06g zMwxjD639&XtLO{G7V{+rB<_v48wv!vvAVi|mLC4;Ek|ps?sfw#cIVDd6XSc4-Y3q; zVpduL&-xA&@na(s1`E;2>o8zH&&Rzw*AT(0m=Bujo03*}2ytqV$8x-HM_Ji+?JhBa z#@AXs0^9O!#y!uUONt;jCO3b_4h@Z6o1^db^^NA8#Q8?J1#b`KI>7tX7xmSIyUNJFvT!hfb)}dJf6anD@P1ya(=c zUfd3Y+9S6?uV}8e@`gW?i9Kv@ZxlQdQCz$(#Kp|aBf$1Xe`PV`(g0AGme$&#kMXTs zT>9kc%?h}L4QS>P^i2l5_9T(glaq%&D&}NoXBQMS)h>s&7BdHrYbU@Guk_KQ=PVZZ z->74;)TI<7a9OD*KLRHI7Adi@n%lSg5Y||60|Qpc9?Hy8Me6G7FWCZv&7D4xL+=__ zR9D~QaD=Pz#)zBpH&ejyS>*{}n9N1S(uafCS1k@GYB?8$%}rld#7ap^_w@^|jOP^= zb|B4VGorz*l3V3JHr$a4z{6lLtdp(P_N>yoCYBco5xDqwBi*^|JjZ;bbM*xcy|r%& z7>tcD1&Tu50_(EXco~7U2DZ>V*NkI^K*&rUe_=f_PzPafcgtnPE}oJq2%@vs&E}2{ z?QPp6BQ+edt?%fvdV6gT9Do{Hc71)C3Lyxf7RpO7Humh-utSqe>` z9nCl3g*qf*lb~1o$G1umxwK9VV5$l_uFeDk!QLLCFChyTRE-?Cn5n9&lF3t1QBk1G zrZXrM3WUYS$H#AT+PXwFo|Lz@GeBkm4FnmrI5$B*<+JKvx*PQI0nlBz*8XhLSZ?Y038MBThsN-@mURXG$C_GD*Q*QlCT?~|hovr}i-7Z1J@A3uH3>$M-_OZNlQ z|A`WP=1S$Un4ce^{F(l}IrPV?A=gbAt534UBs`D2tECN_-!j4+WjRY4qDin7yRlG^$&l-MO**? literal 0 HcmV?d00001 diff --git a/docs/screenshots/memories.png b/docs/screenshots/memories.png new file mode 100644 index 0000000000000000000000000000000000000000..2b115363d05967d4d01b0313254c7784493bd8a4 GIT binary patch literal 81676 zcmbTe1yEK|v^M-IBGL*1(h4FC(hW*VH%Lo&cPl9%Al=<1-QC^Y-Q5k}#<($a@7($S zGvmw|<~`@^z4l&fuk}37(nmr}01*xg4g!H73V!01fO5JX{F`J%4T> zkP*vg%zy6_=Xe?({k=cMdyMq=HuLelMy~ZneESUh*MMJtHrmp@UFomr)L`&Yiep5&vuNJNoR;5{QmI=_qQ$oeWYsd z2naO53{2f!TU%BzxYqzq7NjeE8MO{M5*& z&K_Pt$&bF5-%`_8pFYia;`KJ6a^rlAPE1kp%C$Qj5sUu&pIPdCGT>V1=;^7kSnbMF zD(B|mSzcP&m^cb49(WU#o}QkRq#lUJ;c&j!yJ+@GZ^D3Rf!XHfqG;YFfiwq#A|uazjbU&g?L90R;TMKjWl-Rf=i$_T4*^>9XC8!4!LY`=0gS z&`=x>hm(`d;jpkUOufuH_JwE2$orEy5lMA3eTcI^5W^y$E+3uRDxKFkV?^4W94v-Y zD$)L(I8PolPPyrPx(opuTTGV<8`h*^)?i9dX;t)AMvUg$w>plOZH|}kE-r{?>hOga zXlZHLZ1;913JIYQhcKSv5D^jacp*6-pY0~ADT!ZKk`6Z*&7V8;w&^xCHPz=UGH7)? zfBO4ViH|bl_^+#NcCnaDaMG}^s*WN8knePr5+%5lgtOd=2-1`Zlb02RtX6ak&=q) zmxF`6{J2hUv}im#%e!}8d88B+sROzj14&FKGhY%d@Z#e`O_&)6V~;|hh7ShRjJ1=v zN&A~x{Q_~h{uvljY8EqLx!#wjSn3N$_;iv&Z9-K@=*Q(_(cRL0Rh&wV-IQfr=Lkxr z^vukOde?h{zUnWHZqwza*Votghwb<$zfe$7uTM6QJ1fCNl~0{{WMySJ*x4`KLzhDX zCA+ZcbbbU6)ywm*tghZ3byII{Zr0!5InLKPS6gioynAP}(D1E`ihzWSOtDad&nl~= zgubjusodmIT5xbM5-w|t+}ZYcAOZ5zCk6MaP}iT~;O`f1O=P~`ZAtQAQ7W1zbh+j# z45wQgIy*c6G5mt}S=u8QT%%!gHncYX0;SSaC~0hFoWpuKn-^$kEKWm)S0|gStgNCj za@%C?a(Rlgm6q#ZH@$oJZv2Pa>%5#_XF^OlLpcJAhqSUgrQ zQUUq*E~3X#!!pZNDi4T1wO)WgKp8)$Whyn60wDUZFsJC-VON z`$V6t50AVTGJpK|QK;F@X?O5d!?VoQ$vXz0#tbp$CQ zp@^txyFYe&XxZQ^8V!#Htxyx?dY3rT+DOmGNMtu>)25+c2Nu8#^xj}I8YM~}f~}SK z@e0cI_H>-Za_zkY(sjO$MrH@S%sc-3yIm90IXgRtDJK1a3Ul!%@ zSS(g%t8HaIiHp};t|KXtzIh|*a<)9Wm;FIhl#-P-n(Ja~l>Ce!8Xkj+-f%3pNkJ^0 zy|WGx&QL6t`3+l8 zWgk@4FXmI@;o>8nYim)Mbbvy&sA&&b3? zEscqTQ=>OXu-5&Sh$v5?*o$%)zsP!9j!c5vd~1YM==(;V&E8C94+Rd=dc!xUySqE1 zYBa{@nDlx-7fvZC;Nkro&-Z8!TE|RW%B=ghTP+pEapz04BU}km=$^En?dq2~JBv3) zySH+6ez!z`iDFj%B53`gsQj9=$;Y2l!0o|Z12@GCR()9DU9G&J-H1vq?|ms15p`r+ZsxD|{$QylyOAG-jfpd3d;3 zw?7_X1jSIew0_)%&ABl@sy2MVlZ-U0(qD03r~5k4ZeNSkSWII>V z{P$t9E~#VPG8E;+@nS(CUn%VJdS=^6*ZKKcieAI9W&MvIJ3Y5A^P+vX4g3baXjvT| zk3-)&3Vru;6;n$Z#NVg?lcOA@TqS=cWMYblS7IE^(ufuwhf43S9-NVMY->qJj`vty zLmRw2C|5OSJFi|{LC2dPzdwM@--&H9!SyGLon5To?A)C$2dh0gN?u3M*3j_v-Or$) z!t!!1`=hteIBuG1^)9&B*uu~tOK5b^*Viv|N6605CsU@4=uX>lzodv`wI-vW0HG0t zywc)gY~UIG{)ny=VVCFji@M}8%$93Ci_PBo%9Tx_j4GRhDeX(Gep&3*FQH0(xqaD? z?O0^;P12K-3Bp9tu@gGyqFN=?7B}?wv$)vMF)WGu>*SP`iyWn?T~d(Wc#R*o?r$f= zRvj$Rpu3cu4-*`(+e6J8kJe( z_4Sb8V7c7Sji&Q2)Uume;rLM)MnNujcYA%lIh@hN2Xc>mCw!cf^R3OzLr1>b?fm+Y zk&%faPfyQmvi(=!bh`$Nf-v&5-qvS_=q(}4w9T&GjO|4h6JDWqdeTcmTGkXzB`7j! z?!gH;6)!?gjV8~6?ZBT^^Q$jK#6=hKOz-!sRnVdvWz8&MmV6hpiPpz38xe(LQvEQ6`#!-y^ z0IVe)sx~-byv3VgPRbU{TJ|{{?vJVE?`KYKLi}pMX{?#m>cp55u9;N~+N z)>N4>n?}p`*jSu0ujHRF$at?XF=s2xHJ4g^WC#ffc?uuL1kxLfkPr|wwYGY82?+`H ztS5isF5vN}ZTo?laytkNHrchbv=l|BySBPo=&np<3yx;F;Zc zq-$?cD0*eM=Q23W{p~K_t)(f^b7bqik@8B5imc5exh;+eeJ_*v@lOf_Av-J;duBR! zX)&*EYX~pPiYm6q*KXsb!uE<%(f;#HqQ0EDgqHXVV_Omn~ll5!a{QeqseAAlk>)M z{-?gK6x$!%_f!jQ@jQ?)zvSiT{0x!)#DE!fi9p>goC?!iU2nwBk@kqS{bMAc3`M162g=t<{q!PYR&b)aF5~X_S1KHLy)9n|zQg81l|2stKcPpw`jW zfi6cbFZ#XuV??nSMt`o`ADDC#CUW!juKoOY64fM?B+{09MfXvDF7P^~21B3dC~tX+ zqq&$kLPEkODwDi_AneD4|BO67-y3(RI(li=(xPo(&~dV1WTgL#JB0)|_Nj#~akG!+ zuKv$?S%J|dR6lCverhV62~K9J*^ZP(^;^#onVf0Jlix8b@#Cf9qxQ;XDk{xBhs(C% zP6J<-wcDX0;d)I10&Zu==yPm)R<`Pe{IHq;;*BpwN&+bXy9i=xCrZVKlWp_WP8OXz zaS~lAv2Ol&UI>`n{rvp=`Kq;788T6%3=9mbD|RZ5nORwq>`5sppvKfNHLcehOy1bg z%T6^@2mT3CB%O?m+?J*$H%eU{9i(lR?>hgm04(~1DFi26jC`0GSCsUmHd4~XBXUv)$14I08D+VUK>6eqjF-~A8%h4T zlw3F0>8XdTGhq=SA|hR9&Jh#oo_jwN`s$Izm#J6wn$J%X-ICo>&yPD3oQSy*2nl+a zSp*}o4&{cZ#8AYQ;a~j;@tG~-aLgO*XP1QT=y?^c8fvbngsPT4mvl-dbC*R0nUO>O zl03%OXYaSUj!w4A@%L0g+Iv(Fy{mksNR!~F@x9F-+b~Q+h;3g)P%vr4eLe*G1uE+H zP#S#9*XM|c{)XefvT|~yLx0A`ie!0bWo?2CfF!H{)HQZ)=}8OA>j|#m;o&-Ulpr#l z@6Si%3lOKM336*Na_DNKA?z4~6fZbFi0dT7(rCZzV-o2}Zu-%_HBmA_Q*barXxAa( zkMX_1liVCi+mjMobDDBzY^WFNZ_dcV+}xJ(995?$Gi_~Xwd~YJ4DzfDvI$W61%*F3 zZK#g-DfTq+hH^doV+HMJ=9S88mimUrdpIvvnR23sZL!UI+PT!*z2Z4ovCDVOB~(?9 znt~edz6ok*G@+-Hh2dUoem16(M#aTdnG}`~OUljdaS9(9kpbyZBU@R+d6P@&Cq1+G z(w6*b7}qlQF`c1jE3CSmadN%ZB-(7Qt*)J0Fi}zWWsw^rb6@(`R8P=OPkb!#oGebI zh_#6}({k|NW~})7!eMW88fJIf>>IJ`M9#JvKB`8pVX-uO?YIdi*xylXQfRmV$vMrb zrk$6FLR&xP6#PA*uUAXh&^RY#^;DIj?Jll2V}ogdt(lz>AZVwit?u7eM@VD4XRqf; zHaz0;GQ~H$vHJ@QeQ-abH0hF%kzrpQ-$qXGOyFEtn&2(I?-({^*XYN!5ftvfzlmIoY6d9@$y}W|5%b3Eg%91^w?vqKb{ZtpSZ&0OM@Y!`($2NF3&c4M z?Oz&O16II2TC*6ylzLbHUL9NzKQAVaC|Hm`r zloqX(6}~SmEiXm)3vbk?m^0je zOL$=b>Px-pGD?o?+8mRohnP+|vu-@9s+@#`?zNF=1&N8ND_rH;K|g<%6q%%ErKRO2 zca1261-VAX($Ukh*q+k%x-_f(VWh8%lSN9p^F`4h`^S&i1?3lu`-IK&TI5QL!<|i3 z?{1OT6ciM1PcB{aGczL#zCT-icXRi_>I5N`C3aiB&)e%sWvvC`!8t>D!IHT6o9y-$gO5p4g(z^9GsMn*GL*4v~ZRLxcl9rHQcex6?anm3x z#)vNfJB2JZo{*5uYGooL5MPt=3CE z?3Y>{Mt@Jw1926ugXgkPqx}rByB?-N9gl zQ}7=jvQCh&Hj$9>JUB2TCeCnkb4w6lP_66m$>QjCaWz|r)V;j=rBaQ@Z1(jVesozG zr&Ok{frdVb})6r$S6>)u}jLy>25&wpLZjq zXO8^4b{C;ju*@Bx+yRHAymHSrEl9EpE8XQR1>(kft?5U!xmZkPtlHqqGb2m}spne? zZo{Ns%2T#GHtb79y2t`Fjgqw|jc>V`}Cw+Vz zH^oweQkPqYJy%&>ejD1xb+|m0YV?0D4GEQxc?5$ArWng&>G4EHm+IjsM=k(9dw?bW zO?!1@jfl*t++_A@HA1bZs3>ykH6dZK@pKuLO4a*;FAFGWe|`zZWzyKVHI_FG@@`h! zy&fkT#b=ao|M4}M#hj9b1;@jsRxow|vSPn@@d6t=CM4vzQz11ije(y28v`Pdko51< zkP*lK@&KxTK!6Tp>y zN8aXSq7bYO(c5>lkOMWVnbYncm{e3$$;qW5UKnU-aqQM`hBzNQ4V?XfAL!{lH~XM+ zV*D7%)%9MVPRGO3lLjKe!o$;yJ6#qP6GNx=6#UqFWbF6HAa54FR&8KE!C^9)i>0%r zs#mXZOV75>Ku@RKkvS<1Eg2qmL)P%&(a}{i=7$I4;&3rBF|n|&ZZ3}j)ZW_CQgEtP*9QED zUgQ~bBGuK@qjumw??j?o!ils}9sBTxb+k;MLhiN?ll+*dRRdv!j-t)SU3<0NCtEPnBo=&smA8k z!9Upi4l5%w({6W)Ar%`Un~1iuHJbCgi2r2{-i3(y7)kGQ01gKh3QAKr@t#^*W?CEM zLl`i(>(nLJ8}+#}w}1+r`>X9)$Mb9!YFB?$G&By2m1p(l=nxrx0fF^}M)#(K(%)7z z16F+0+0`{wp^vIm22x;S{T|2%hFA23R#;p=DTiN-r*?+qE7x9iRz4Z80Js5@{*cF` zuLfpjW|6eoa7w@K9*hQgcuS@EGQWcZD_Tn$k@pQv4EWJDtu~3kH`^rNpU?A;)1l!p zuCA`+IJ$e4$|o4a6cp?M7=ZwhneK_ALqkK0iq3tQ-Pf;BSkKGLO((fiOpLX{&|X0@ z8DRrLjmyVw8f$85SdQ1wFfin4x(%6_$_6sT@dX71pF*bMvPVgHIs)qm;3H@JEOw$hQ_OJ-xhu^X36Rhd?qPKZLytCm%sU1c(#)=ZEc; z4Xg%%aH=h}$MZf~z;1Z>pak>-YkP2MCJyt}6xI;je?S}P{EOWE_b~RqgrR?j2jqVx zgMDbNKWOpV$?$$wHWCpLd5dVEzipsru)T%3v9a}+zy%!iQJj+5TVkSkR+o*XN>o8X z*sW;AxN4SR9u*ZjQkuJ_sO1cCoOIa8Xf~IVpMmAXN}M(;Ukrr0h;>;COqn=1Sui8I z;}|rD{MN<)aTX*ybhntD$!eRMS=WUx*4Bt*0(|@+ zHXW@Ci3uj_x=?VxeEAYO=I0M=20(g6P@2wNVDKQ9-K`7GRdyC zF!mauq)5Ld-|2WNEZ*^COT7Q3{fQ;!*7B;$@)n=9D5WN9u>sZP;w>9s?Oul7m`=LQ zUL&1v4ZG@Y+>igm0(PUrKsZ2o@xs~eey?M2d7@Lb-eGa`Fq})?Ng0PDp_jceztGS~ zZ4E1`6#H&$boBc8e6?91fZ;uTax_Cu#D9#e);ZxTAvN{+TB-0^KrdyUv8hV^NQu26 zkv3Ykyo?ML6{${!pqP-ABNP;zw`645Sy@DcgxzA3z;F%+x?1_-nCHi-*%8zrvDecoa%e3&jVT9p z4()&hkq0lwoF!*ZZ$ejg8>{9Sg*s#*2Nn))uE=`YFCstF$iTp2X+!;Yaj{TeC*EMb z+{FiL?sJA)4F^l(>B$H3`R?Lb@B-JXIF7(6Zc))Zf>Il3QtgjHR^D z(Xqp27Ylc3Z~yV*BN2To6qJ{dQ9<9b%t9M~>4NXz|L)=@^NWfPI;UhlAw(4PWJ> z$(^4jlY4@FDsKx-X~t_(6%RsE0;0wduccTgby9P> z1c2G^-TRfy+53TAAZFfB0ZBjUU}9r@?X|sqjdpkLkCT#)?*(VLRrCfs4UT@v$u?)A z__OtK5iv^EgaM(UD|8tF$1u~j8zs)$>5r(~iHPhiYh6w?dL|N}Gjs_N*352UCtRdg3@oh zufDRrqoTT7NK!P(J=|(7PK3CY#m2>jQ4L_C8cy|G4&n6q=U_P3%#bTTX8XwPG!x=& zi_KtOVRyFjod#7ofiso)Zt-^76B?RFAV3quKgdGNHPRyi9sZ{rh6eh@?oCYupyDoC zU0OEV473GW6lkNA#ttoO!Jq}NRl8rD%3KM7(QYF~S$aUH*q4yNqvUJoSuerpDm z=(h5s=I1N2m)bxL^Q$5qMM*6-PChn>@a@r}&8JC#()w(Qz%s3?ylvJ#B0#C|Z1oh} z)bDp*jgE%*^BKAd#}6S;o=PX2K6h5-)esbOaghmrCJ>ySBt*(1>JtbLbMYoE{cSt1 zK+$Tr@l-@;D6fZSTN|&E;&fLhxkOrI#Vc10fN;DBa%BqD&mv`EnKX(z+RN;CmNkS| zRLed?vf06+|j}AVi!RsDneOOV*MKxypXFCXb6hxd(T}m9vKuAA0M5Z{)_ZY zC)*4eNe9GPgjqGtaQFcXS&WBh;4J#jxcN_ zZz*RE6^MEOa%tc;wDlwyrhT6u(be&G4!aC;gHt&RUKrr!I|Q-<}q z>CS_uiX~B%47yU@Ea{NoSoX`caDt*Cx6>dc>H^Q3%$6P77cwLwpG0eJl=ED%)y)fo zYnm!h&ntfXL%kmV$9Z2c>2E^89{yAnL5yF-zBD&4E8_!{XQlAD7&OXPp zEhQZz^^c97$GRId`bSoVG5jUA)kcRiEgl!$5q-9sQ#`yE%?gKan)yvGwgW}6??G27Ke_R{iwO*n2Aqd%6T zV;vegIwrH$6G|af)yAGC--nd(tLX;Y6PzxxZ0O6&$w>t$m_QQg`7}prOa%qBQDgx_ zQKgb}7__2dT6nl4S(%Z@XgXR(`|CA^`zkbaK5Sc3wMwVKvY@!w*w`wsr?TLSXV~xU z#?L6>(eKAv<|NIo77`mkJz=PCn6Gx--qJa|u`}Pdt3)#X-Or!H`REnY0W^sQ*nTN) zMw%g^?W&V?>Z=(^uX#QTxe4}$O2`KnGKGyui8-!?U_`yv&m`Uce1_C@Iq1WUa$iF(15glU zW@pdWTIcmlkWon{(X-RP{rpkVhfIH9e7ZREdqk7;K~Pwfy1q6cIeGg-gl%9t!F`Ox zN2JZ#9v1q%pC*))&UYSD023DWFqHIJ8S1ZqrR3xk0VGbxBfZOPuKsw=!lm-oV|1z@ z>e?1oQqsfAVq!}S3L1LaJMFC}UKr|6*qx8`HV!09a<0zPt*P9-t@j6oc@wv#;e&EpW$n(UoY-W3+#T_ZoKV&9j{aK_lHo^9ys7&=wZsa&V( zLKREda0nmV3EuM7AD0L^T;2$BMI@cTtLZqQMPy1iw^PkZ2ZzvxhO*ggzQDPlVxo=+ zKR%e-4GuQ=NA%!w?7JOUlV9RJ-^k2M+~nlOy5I2q?5cBHq+qos5qR zsh+m8M!$~`Ytz;>R=(HO;r9wFrHpgkX8Q88R5Ya-<~)wm+G%y)b#oj0Jwz?U!z=my zI+v}5?Cz}40T!yNXo8%Gqvq`^ogR zAvp(n-x#$>i+L3(;V@0zPn~?KyftR}F#)j+BzG!`OHF(4)5eQm@)EB-5uj`0)~2$4 zba7~2dE#2O>Uq%KjRGxe?&DjZZ~N)MJKkghUN*fq79Z^e`axd=y7qxN)I)&?neVZE z#{9ij(=TS6ij~nY=~q%%T$ugd35^gF$@sv?(rG*YdLua6<;Dj#xb!N1`~a!SkNSji z%Y;XxYA&!5*9UjCsDZRsBN!ny%4$Iy#5_b+fo^wjp6*+8Lk#(VI^y211K4 z|GeN}S|xo09kD|J3#s5D->wwza~qT8-32NY`UqS$<0Ek+^-N)W7xk`_sNFbL8i-or zd(u1+QIJ`tJO1R3;YU&Iy6MU-|41}x50_^=(G~2LY?N?LfFnqQR+-w}PgEjnF~`@z z9O;`wwo(%qW@;RU`#fs(1%Y-lVOAQ2ac&p}p1?K~2y9#gb*U8|Zpg6C{x=gZVMVn; ze)x1jAR;z2GPcJM!TMwY&m2}Y_!VyJb|VutQ~6EHL*_F3rI_38@x<%+NQKX}$cR8- zS*Ab2OhI3fSrM5||DM)Az;|S9Fy|^>?ZLJO3{DAOo5y|$N_?HKogT-HDXp?R&MwNH zTG_N@roC6)3knOfUu}wNzTepz${QCR&v=)PF2`sys(gWO$TVOXQ?}b3XfT?tqUi{; zIaGaBcyfZsABd~AXhAZYsOwAlQ@No$$f{LVIp#c%oFk`4I#})V^9aSzaH3acD||#! zWN)^=T&YJ7Q89Y7v|8K{`qEjpyyVIi5Q@Vk!56Gt7ej>?{GP^fGVp3na!5i&N{x`3 zB7pWqG=E_T2PNh5D4W-~rGV7cHsmsW4WR*f_bLp2Cr=z;i%-vXQ1R~KYd1Sj$uSUr z{FpA;Ct)@pUdwwzE!o-Au>ga^|1RB&KRxNqxA;91g#9%NXTqj=z~b<(c6@(7t{bRS zX7Vq3IX^}k>aE=OX}+e8;~LEty_Pd(3$482<>$BA=m4}ndxhOzYL%A`(>kXJ&IY&qE^mPM*C$ zL0kO1kyd@R>e({jqiv!tsjd;7`YXF8#l9`3!7cTN+r-M!j&hec@7q?tW4K{F=MB*h zt(AOJbO{LZYA^K7F%5a1F0_wk=Y;?kEWFG{gZt)~7@5Zn3d9hScsr|mxzBoLE~|`sHo)o z$Sw4dV!sWs2}e*;fm8w2n3M+*R;Bc2Kp^JA4y%-bp|l zT_eR+PqQ&%prhll9%T>*?yYL+`oURItF)@f0D24_9v-aabS_`a$VlkPiH!I;lW0sS z@PxsUp*ovYH_tFcfPjE7^Yi(+rl#H3DxQBr_|o={77t3JXBB-;Q9$wnGQeVOS_*HUpCGkr`Wh*QCV{KnwKcy0b?r>32Q865jo)NE_8D*>RN7k6Gh9sFb z++N){lQ-%#sw{mu|6rK4Vj|rF|`L;;6VCndq&V56d7c{{NNk|KE7@{|yAaS@=B)kVZXp1<=2il9DodLr5A1 zz_+gcPEB}as&dxh}0VVlGd9beyed3gx{YM#2fx(Dgr2w+9@J{TDp zO>i4QfI&tks@qKs=mCIN?RpQ;TZWT`MyUUM-)yzDCOqf4YHDjzFZ%~XtqdKzIVY() zRXMq`B83aX8K8~@s(SFbGC*IE%l`_^&)4{k$~_DkNO*f%T0G#OS?q~1i~%}0wYSF_ zmC(9<^s{>v4x~lfKweHB<5}OPt)pWyTLo-kFb!mra&qX>Aq)@N9sspN5l~RD8?K3z z;hfA6b|G_^Lu7zn49C6rF25d73{gXls&^bK;~oPfPa$M4uwT|` z5A4L2Aam%MRnE|&o&Y?0izrvAI$UKLXzGDl7Z=4XTpAJv_v{%UqQ!tn2!>1Z_ggz2 z-l}PAe0v^FX~e=orQf|UDITb*4oQslzK@+}T^a95bqy@-N)RbC9ZKEu9Kb5qU-qUl z)zH-3T*xabJBJ|7?9En_K1n6{iF;G?V7J6KsAR4dkcH~ue;miS`SM!tWc{6@ghT@p znO?S{t%3SflNg_xu%G}Bw_-5hO)Th&q%=`Onl!mp9S%(xUn*^WLpL1ni&Lq#%IXX5 z&NT;o8IY$N&Q_HsB_+kj#{+dg)WCt3mKLvoKs^wZT^_FkY}ePvM}52A>G#wkx0D+i zSHqvcK=XRk_IG?59~-2jVUU;NcU45}V*<%eJ$UXL)TMwjMB;{qd5{*|%>S!j4j$wEIIpAf`{*gFa68Ja@3Q_Cg_@={k%Nq&r zY;E_Z$FeMAY;cK1qM{NKY}eal)7OGSg4cRtyY?^dg&&gf%OcO4k5kYJ?`_iq%Q2u% z0VFebF<@%wtVhFt0ZZ^M6wkqPPZ)=BG;vpjyfh zo6U?3i#qI|+Xa|fv?41H|Bu;fOX2>xTSNqZuF9WzG2_I<_m0c`ws$UGUi>J*_homl z=uF&_g3NXJ+dLBZkAQfO5&_ZQbhf4~AF$-iRyznvVxodkT)JMFk&#tZPZ6_o47WD1 zaac+c)=<#zem)nyULDWv>EVGnJBoO3aZ-`yw-#pah!q_jQ@*FOAYG>bk2DrnZL8ZC z9TpMM4r$03h+4@5e8<*^&!zIiYV3E9KdgoqPiPihQ!}^Y8D>K`IC#_Erg{0_Y%bK{ zzO>oc#Pp~~&Jt^Qfr2tpC@l|k3S^EB71M0=0J!VLzU%7iOLohRo#`x&INzK4m6*w_ z*I{Op8(ZUa9$(9X6Ohq94Ry`c^1PLg4|_bHro6;pIQ{m_I7aur>gzi;=c9>&7n%V1 z$LDf*@6x0-S5ss$BsB!;B9(d@z*&{4Tq6-AYZwWkV58Pr9oF2!1j4{Tn_N3WL0OAO z^u_hZvYvM5D(uhK>p~JOyu7@uasmC{>huV(Dx)&wzw!TONqH*C&hFmc&d%_V5M3BF zv@lvaIsgb$5D*Z6LXVJ`c!DYguB|P7jM3l7Xm>+h$HaWMD-Gz_$N&;EOiP)U%YuV> z*W%*B8Tz$gH90vM;E~0Yv_nHuXfH!29I}%}G~1{x6-x}3Y4sw1)wz86T3;4mGJgkk zjf}H1QBnzbT}m}>sbC;dg(f1>W?--;JHWR6Q2WK$*m=9ne_lRMN?uwMl8B?oAeW~s zm?M~nW2o*Am8rPbtTKs7FXCTi}oGx0usF)}icy5SYy+zU9Jv`eynL2-=kQoQ|ptZ z;F*7Ma@d?BPbHO9kofv_0~d(!8dzLv_yIo#h&2wC$OKK zpJKh&tJrB5mbqBcNTH5p`MBxs6UAqZZhf-k`_BPgu2b+v)-hxCQxFk5Wxi*D<+fu^O@R9+W%`1BJ0wqMTP0AX6E z{EYGfugq$222RaBeD`7#2uYGEWRCg9f{D1Ta8TQ!qY=u53Dlue{Gny!DoCe)N(=Y-gcn(TQ3SKc8e6MkPJZ=3tG zhn}9A+P2e_Jd2ZvMnGQu?(DHFVznWNiq^)T;CHBDR6Uhi-`PCL&bbSHBq-5@|Gyg$jClow55&L z@4Ih-_TQ0QVAbh11cgUi+?|*^Lp7I)#m!aKu7roaM=0O_nj|$e)T5iEoZ^VQKAaH% zz`hjS{5lz)7j5FG?{cz_95(CC_DZs%JpGLc$#QZGI}S`U2;Rzo>s`&~Is<<G(y9+gToq6Ov5~Q|3aO+d$3&V{0_P*{S4GdYbUc;1tA8W_ z_<|#C`0gGoJUm9Li%gT*IFQcddpv>htVI?T6LPp(S!|m3>u{c9Boy#NBuWn3!@^&M?M|m%EQV9bOa@Ru(d9<1;d!~n8;$r3 z4ca6`EURh}qDp`44M{|YeNeN;u(__;Nvbb?Z6xfA<4n)d9mp1SeEQ+*S^F9t5RUvE zOhd*qx_O@CaAU%z<^Y@tTx?uz9h1(#Z-O{|+aGpwZ)tDkbTG@%-ae~xxI$=fb-qT& z0pbn{OWh*2LT9JQqp38@ifSWqw=5r z{?+S+3iIQ&*h1e5Rm8t#nFGs^3wiOpRx_| zZ0o^sBxaC_GN})}Eu#R6C$q&S&=dEUS}|uCnt||JV(cM!{mjjZRrz=3D23lf8^w(>#nN0qMu?6gC7R!f5BQ&670J%ie@7Mm;Z@3i~ z7Xy9r-?f>4?%kZ!Otbc2O2khc?T$rG{z#60cEm!+gC4^P7xa+32T3?c6M(h*ZJ`F+ zqf=HM-iIeFENGR0D*SKET8Pm!MZacv4Tklbl<(;7{x|ksAP6ahe)Hx{x?B?SLzVS# z{r3X;f0lh)6mHfk2R`^c|?e5(yR;7p2oh4o^Pp z`}6+=B;7Xz@_qjNSy@@+bEKfCsJXSX%~}s86QGlcGh}n*5V0A5Jc49~bQxtE8Sm25 z(*y3jFw2d0qE{I-(kLtch=^FnFj+GwZ0FHuD)Bqw3_l+qpYH>G-$-`xzkmP!{y>wIiYk5i8wl=8OTnODRa@H= zbiU@P)z<-3qc@r%MIcZIfFZApvJiFH}i#&+?O{McS? z>Zoq$1^z>e|8p}ZO?7p3(E65)sxh9g#za>Px^Y1b4GN+!iRvA_z2YJwlG4%+=Y8{l zpaF#s=(XhM=SP5ii2Ejr=2S)-!vYHnJJo}UjqPgDrf5ndQ>Ov~P<3dijQ>p&*g{f*^FreuQh<0%^Fp$MkAORW2o)A0ybeR9Cp{T{>+sOhiH_e~8A9Z$DL zBO~8(a3DU#%KrZSGbLpy9!J&n<>l^teLYy)4rWLq71(+Po(noApqmGXQ^Snc>OT4< z9rY56$FonC=ns?W0Q*Kac`H|QE+6MM$+Iq@>Od0J9go)#IRden0>mBZx866JIh*U3Dz6tH z7h`zrb`rgQ=lnljeH_%`=6)=Vny*$5{>KHY=`v#w*K_D@L7)C+r64K{4NW1|ts4w9 zbh-X~p>unm`r=}kXz}H~vE>@&ckYlV2e&$ihDI_HQq9}B_IB^?RX}BdKD&AB>MQ^l z->syR5fiU>h7j*alHYzGWTfZhyj$(AvXcSs&D}*^-&0allq?PJc(%jh zbYbQx?J`%rBrGh9&9=9mLM%qBmYA7I!cO2;7a~%7)XTZ@H2ErF-S*o1mf=QZ1p&3cm)uH_Pc@6QRl-YzNjwS{kcI3ZL+wyxJ?*O zQjXy|jaE1pX9tI>hzJa{r${&?q@DvYT5MnDtr@TXGG` z?r-m2Zk&w?mXbnE@5wcazP2^FxOA}I81Vw>PgmjdlbVn)3_AsOXHvyApb9to~k?IYy#3p{u1lanm)=+uLQLy8q& z;=gdjAtJ)Uo}L_S&F7nX*17=X$Yy6~H)3;`KMWzd8LWlkSTrH7sI07DfMNucfLG`$Pa6Af&i&HtJCDlqQ zmR;Qj{lkM3h@*1e`eCH#P%}&mFx=g)w!rj@Wvi(#3ogRA#6 zy6jaxRYIVt(Y%wsKEwZ9x#XIsRH|iobaX7AtIPose`8nEp@9o4SDF(P;N3>mC{yPt z)D5``#eE4})D#pqll$2}tg#u>($k}&qNAgt-!nwS23xG{uz1-z1fE-i=3B;HM5)G! zj!fZoWZea)+ne?u*y7MrOna*{mS;Vf+HFd&YSFZ_XKK z9A3D$FE$(In)5sFxUSD-W?+z8ShzJ+N5MTUOD1{hSWh-LgXxd4*7WDP&Z+K!+r_RfZi%MKq z++G_HC^4#4Da$JAqOJMBOXpx?+!l(Hvsgy8%6yHw$j9B%y{3kSM@O?BaD+K^hSRmR zwRuh_44x28;OcfM7pkk&+8K96)-x^w+kuW?QCf8V)?%ju?U1Q83{d|9W9Ui2V*u48 zt6{I6VNs^1r@+J?Wj^dyf+~yI={ie~_d?gtXC&kJ?Ypx>6>2WOHS@U~A*YY}0^_Qv zrQ`bxObI)w$r06)g@Vft=94gn&Jel~iKYm%Co#u3@u!-6ajDdP9@6TR*F z97Wt8^{UuM${Zt+<1Af)XXu!}<$EVJ+mr0?7%Z%V!DJIrS|QR|6021WhR7?>;@~2u zao#WI6{chqSUD1$L`J6zIztO$! zaD&fzCAqK=r%6su!1K^)#B!=$XPAPw>as`ST9XEbn{UjZt%BV}cXRXn-i#uVa*;-p z)85DcMR{bg;`21u4S$tZF)=UQ=ebFZZCT4$kc?bUJ`uDku4EsxzF>Dw>KUe0#32UEz$KYA-_{2fR#SFc`$W->22 zYUC_SI!Ug%wKZd{u4qW=OKfcHr|ZnzP%y@`8RK~q-q(Kbk(B} zW;<5;`KO0_4sC&>*^K|kBCket{ZiM7yh1C007+;+(q6TxySv}8iDc<+>DHNCs=)7k zQ%0MYdtkK{7aeVNvA?71duUT5VQnqj&csI=%aMg-fi@*n+9H=0D8k}S9GuLlkG-$^RaU^T-msJp~!Dxtw7K_pMW1n0?k2<^B?NG!>egeWV=v{3yr|Hs{(6qLx zM%yaWsNU|Za7*P~=*C&v{f9l2J!w5p+9ZosX%XpUQ z4P32?>O0-1cysd8$KEuF--UmDw5G1oGGj>LY!&Q=0?Laz_ zUtCN|iuwGFKELLrseuXo`&U(dCC^WBX+B@LCAm*^6iL|g_aT4t-Xz_zevXdb7O7OJ zC_zcdaMYJ*UQ@#@CwENei-hvS8h>X`Ey-bdcaFewsfmeBZm!NXtxpSsQAz0G-`PIQ{1DLH9uCU(A z;vt1S`u^O5twq_S@j_Vx#uA_K3pzq@VG7h{`r zPBytPY%=y7+iS--gJ#Y7Sww*Umyqy~7clbz{k+@Fee5qntwMsHS1G0%@bAwRs4lMCnY zEdTLReMPby>(7$B2d^{`L%*k z-;7%?U+oZQ{2~6hu6-lCs0W&)0+xCrnN(Uv`d`&m1v8U1M5%lRXMSEJqfr&>i`2nH zWYeJU*?4_@LsH!?YMS@`%B{GV+B%owCK3!kKMM=Xv5pl(5qWvt@k^zawpMcfW8Bt+ z$%~n8MmjneBC;fJP1i;``5E?UjEDPw`sCviet|Za_@YT3UqH*RkJtL`dEXw%sl^o$ z3#s0`sJ^dwc-0ae9ch>0L=4nSAXL=V)~9^>^s%b5C}MGh9h>Vl4q36qg3^R?<}OK< zGA?p9;jqh0wBu6!j{D_I0$>KZ-0QC$p7!5yeq@U!zJ!&3xqei+$-`#3Gan$JRunTt z-K$dE(%=92BP!TIWem$`^;UdCWhA4_lbw$4!I>HNiE?9TS}*_P)q7kf9vY8*8}95} zgPJG&7jV5|G&3w`8v$cP5Fd@rWyEO#s5_{QjE8fFVWjdUFp%yxzte62s*?#Qbc+*< z0s9zsTR+zaD47wKBSrNWA(fWXkL8?O69Nmc&2QwqGd3307Ks^OB|KRa*Q|O-#>=Bo z;}FPE9!4Ymv`qFsYWpdhORp<^{0sN=v&nB%^hrXeJ^PE#8Aj)xJ$(4#P|AeZO2Owx z=t(^TBQOlBZ%4^}fkFSlKK81DppMQNvn_#76K-H|K!%M*^@n?CYjlTZXK|dCD=)4@ zoFu4(BZpJJz!WSw`5{l_yK6e|t_tzmbP9j`MY%|GfI7O;qTD>=%oCdn9+_1k+Qqy2 z1g*1z*;mb1j9tX!QKzSBs*%%s>0W$1`rqfRXr(c*u+ntNWBEBbM?pd%WzfGaCYoZJ zTeKC%=yUa|mtA>99+$6BR_AI9t~~bQWJGa;hI?Dx@3b({rrwy@RN=i)N`7@kNt!(x z>4e^-?e|i&mW*QtXiw;Oe3s2mJ*Jg9ta@^{CgUQR39UJKQ-a(BYh9LBgUL3;4Bc$l_ zxxo{W^zek3VmE4gXGa$yF+pWu+MC#vT-0YeXn)x(=v*8zlqIz_TGi*}8=U<@Z|o;W zf!ex}X~GLPAF?iR4lF&yZ|sNadqpb*b3`c;%5gf@LoC?7Z7rV!x>bEx=FcQ^Vsc7G&$0kvyNe1$uBTp zH4+mRt|%`4kno|GLGBasaE(fzAcE0p>x?R>C$?-6F7c_OWnGhXSVSn63&k4C`{*}0 zuN?w&+|NFB-2UY;GBN^YQ~meKPkflP8sps)+Jnf%PoI|)3a8~JRv8avhVzDFaKXo4 zsMVxt&l}87vr2mOV+JdW_I2CC zHQQb0zu(B@_}Q_u<MjoWf##45ZF^YI}5%=O76fO^IjL_)BZm00ad&dsHFIk>raHynoz znizi~W6tTmzTm`hwZc1M_jY%8uNNKZdEJivP>Sb_KM^S#Yo;xGNXkWaZ;ozSFiYU%A1Y# zS6ouxPT2l2q#dH^GJIQN!_IeEY4@CWtk9fg{~XfZW;g)!hGsYZLWI#SV9es((tFc56YNj>5yGb z`R9Yv(uYviKK1K)~F19pycNd(z#nHktZqh?xoGHZ&+ z)x}1jngcCEUjNCqn@eS0PEOIOZG|@#GLGiC7z-EeLwfq(-s|!jd^0=qu!h#tcTy%b zGF>+tD@{A=>WO_WYW0ln`TYEOof`@}eHW}FtBQx4Cys@@Rqt<>EVw8BD(E~_7a$W+ zc@6iy;iO}TJ?TC|`6_}ww^Q66W|ngo&NZwUC4KSm6JsPFV##Rw9!!3j`i}{nlKs7t_tUzML-dJ36Pw5zkPB5jMik==e|DUJLleG*uJ}gNKF4O4f z=&U@w%9zv}@J3V=<|%UZ0&2Dnh)001yO3)>LZ9{i*vP*bCL}DpzM|l2wtBghkl}q} zkB^kcX1`)GF036V%2->-u+^Y9aq`zdivP+-VZUoG5iR}AUbNQH+%7n3P45QqF4$N| zdw-ld9((P}%4Dac6*M>vIr9v-9G-b=3;HUI-2bghX!E@*l8Kzf?9V2zBjA`aC1UJ; zx6oa%VeZ=84_8Qdd~$p|nELd+qpbAh<1S(Su7OfTdu!E~9HUCz6nOEPMm&K>UcjQ!90!ujz$U!_D77pJAV{-_BL2`TCQ z*|=L3lu$Rgw7r#}CZE{@+hVm^qr$Gad5+4M+#wW^?t+6g7F}YRDAO1j}E*mN58k{f54H3 z@6atv#KX)6)9YQsu&dXu0ZMfgRu8bZ*<)Cxiey@}*DbM_=5cQ)EuX!2_wFk?H2I0{ z2s*wjHuDL#3-$6@`Sqym;xAur8;gX{O^fes@23icwFQnxLaBzXPi=r?wx65Ru3S$G z_Arpqds5V-Khc@U>#_4^bx6W^G=y@o9IK8+~@J*H8#qYt>=>p3V9oU7;Mn%3L7(bLflXNZ8of38fX zzt2)5J2Ufjokw|r*s#kyn1g!G9!?T>}g32 zx(hodZ%Jk*wd8B8r?ZoH%}ZD9?C>4YGcb%~%|uenu!WzL)1o4l9Dj)nVBBD{>wc{! z>P{6vZ`>DoQ7%0rK;C$T$BtryWxKCw#QfC7_M`-P^(u1O#p?Ii-KDrRp?B+*Wcz&T z_4}Ne`=)zcUslbK5ik=Yz#~w-m@JiM())g#gucHnE?!(^DKN{UO13pqH~VW~ptqNU z?)mEPMFyNhN!42$olA?$L)_1JI^#Hxl>OZ&s!YX~e~g%qm0Mj*VMg^;&Lv;2tglKY z9`99>)vaiVUN|0fYX131hu}FGO@i zs~Q8173u+6x$mF|Dr+^i^*U#*m_F2OYo8QX0FvH&$^L@inO%A3X<%m|>K9 zrZw>;9zKiBpPwv_?k8qLnXc7i{tk~P?dJ~SDs0=824dB7-ftXZa1ad)4dLI#ce>>N zQYiuVje|qrM3s3RGU*x&D?`J=GPF!4TsbVvI89nSoPRLUGyQg~Z&5y;)eQO|oPwSZ zm@Y8vxOvhxrCGyY?IN!tTwyz!c=s-HVL@l*xOuSC=W9?_@D_5m!X7*b6FjxBgo{d9 zN$RDouB#hzx;62A&>*9fLJ*V1h?jwpQSzgJmDMxz&#PaqnwtPI)>u6D>sNuYy)841 z3AwM(W^^pNpwc^N6=jRgtO9WSfDzKQKk@F}yO$)^{^{edID#{kqe4UJY*#TC+4pgb zo!up0zC^q6*o^rd;3uG8$#86K;_&6T!ok5YyBmKP{7HwIM=ZaV*(XPy8kg+3&56IF z?Q-|hY=&pO>w$A~Div(-^77R_v8;IKj$bO+&)}1>Qg9zjAGQ};?hNXina+Yix6%0k zbv*C`5AS!9GnvmW7Bdo8IT%KYYcQX^UDVDgtI9r8Z=H4B>Yx)pH}|rVOtW^w<55bA z`D8i36sWL!lQ@@KyP^#H5~@neSBC18hl|)9x{aK}i;zQEqvLyoK-W1rjjf>c!Ogfn zza5%(MDc?&}+ot6Z zXa*x`jitq0TR*V_M4#n`2o@JTxJuKXSe>9zkui8^iTh?2_9Z3qSf@X1dN+(T6&7l} zK71Os4}8sh*s@BIG(R%WcH{QU%46O zyV{?_s=udPwB2ZO;m*(CermNya)ZJnf4au>2Pjzn)dhU}7VLTW-d}w3v7&Wq<_m{w zg0!SWAXoq_xbbMZ#It70%;wZ-7BSmZ^+3s0eKJpCc9TB~3wx*o)6NgzS_HXH8wJ&S zKTLV&S%OTCGag6&%{L|b?(G8dnZ0*V?+v9@B}WQRs``j8h8a9#IZa<9K38&Z-18t$ z3X5C+)tDe)95nz_Shn|Lw^RfEEdHsyXz2LaAJWU(zj_r#)?{^fA92*t+dDtIMZ&+z z`lX)t`iXrKV{gJD80Oejh{)2N?mPgsnYFz?g;N>=|Mhd!|ZfD>OIpTQDR^VcV% zALU-W5>F0%chdH={N_(|D#iK^metpwjl2vB)(;|}eY>-L+(#7ywK5=Jo1}`95Y4Yp zVGMPMTM9Q_!fw&!M%%pM)#AH;>ca?sH&3+BF+kmt8|0@mcSdr+EqD z^C@jB!o54}q=i(V+=}61$p@-9$g0*#!T~aBByFYw(uxW)J`{q8W0M|M>qk|y?g7W|xk+z42CWE8HPdvJ zF}Xn5PPdv&bc6X#&MFoCaCa^rU&CORhV%+k@o8({ZeZY-&Z%>$gM~uvzQk#ov$K0@ zk%TLv<)HRQCJ3<))LOMiu*=HBCFhBcjARYh6@$=0#RvvMQy;dsJjUorNiZFjoPO5FnebN42lFbs39Sjj&g=zVO(csf^hBvx^ zjW*VufDDCZ_k3foUS@flfyzx^Bh3{Q9IRvj|JUA{|Gh13Q6;x4_y-QXT;IZq@ZYP)E zpj_|fO8Y8VS6p9zktu<PvDxNd7OUt-OIX;enTZXZgZ?MVlyytxIs-BRlce>44lF9vopHK+9no1-@&$dol+#Y z!G3ueK77|(@M&uwoA3lr;WTS(E|r89Ed z-H+)C(Uz4R*bgpsTPL{YcHSx;HGohzS~;EOt=jRJ;y>jm89sT@6hJ{g2PBkx^&xsa zYm3t^`*>#6^Qbcwv)jl{4-b#oC}#E5^6i7Xs9*{{A>8K0m9;hO2VBRfimrV|PnYc zPLkibHFzMJlQB3rXw;DuN~Z9@VW=+8`6ZR5dUZ2T(@?w_`9(*M%Et1< zb1iq%G0stkiR^}&u+4Pi8(WF&EyX3@Af>iJ4CnTb#E^UR+$+y~;beW?}*%2o2ZJ_0`Ng zoD{QrLF{xUbHx{|$9#|};vE+ovuBAZK2zPGr0_>$Ko8j;PTtWWy0#W;utZS9%}ozbMqIXS%U<+X_nRyWhA zqPO}wW@g%&JI`$`EHt&Xcec(Wn}v6K74C)2y{#>We36b6n7xgpKMmmMx%ny>7+$P<>UngmF5vMTp}A^l*{D_!U{v1u=6{&l}`&{sX+2^D>iLhi*U2KjkJyen10o#8j^P35z#N8O3u$J2O`s4sEf zmE^5tl7p+3^P&o8W)d?qm(Eay)6PrF%ZO?ZU!~(MK8U+<3Z$pw2n&l^A6xy=@VUIg z9-Kh%Cs+;u6{~BD#Xy|yTY+6Po$MXK&inI_P#6(qvbNR}Tk!xfFE~R)jQ8|?e{@__ zTxe)7+ApdN>60J6N08}3n`e-GMR)JmC8KAg({36UZJVqlj-IE$AGDho%|pG4W=jA7 zG%zxPv!z1&2Rb`D6;Ho@;}`E56@87q!SBPv5v$&*X=#w4(SVq#{&e0O<)j>5G`z2Y zO|((!w186O&Mase!*zm}{Qjf$l@-{?mmJ``|ExHbz7)*8h&rsxz8AP&t-kfcrOM%~ zVP-SJG)8vIQuDdi#sPz}KVzX*!>@YbJnF+sO*Cq1CVrj$w>NIg41Y}CO-@ch z{cNLyvaYSZtK^fnu#HWr=hazJwaODT;#qB+(td|>FIyArt3)zEoK|1XJ@4~6JcVb zw%$RU!8ku(vb)RD*f>+?u&E=4EdJ3H@#;iZ*jaf)kKI{WYIBl^bIw3y5ut}#UaI2C z-WI$-2Suu~BkUlSWa2hd%X6%0n8?|*L@jE}(E$0iv8gkk)k zjFR6+AK#vZ(E>Gbr}~c{KYIP}<*F`SAG?CqNc2&17~L#)Zts@G(h3{@We_Vq{ELja zQ_huZ=-u#c``sc6I#;`7hR2VeFiFvfw(>KdXO*SjxZ%A|%ftjBR3ihjlQp(9Zh6KE zU6G6$8kc;drt*bdgGEP$?$3@xOh*ab6K4}c=FaJwV!*n@H_0g(%c1sG=ufxb57HnB zk0|YZw~mfpe%syRPoG*oyrTTQpd&@j%jGuWDZVWI`t>L2cn&p9&5t>gEUYYgmy>8u z85rbWFP&=HD;%A&><@-W-zbH zVXYB%F07^#Y#z#z&t~B=pYd7$zP@=@qKkk3e(s7NHrm`I2!r|6PI{&w7X%wIW0vaQ zLn>LR)9PPe{{r(|Lmk{K#PG_-zzP^_){G%}s}NRP{EX}oAt515E}dOm61i<5eojMG zbz*SvZ;sbl2hj5VE__t#kg6WahzG7eEAo{1`!L-yC>z0_qL-evIn(GT5U4iF430I2 zm8pbT3K1xxBCrfWIILP};jysh?G&JFRYL_28E2*AeS+6d#$_JP9UmTUO^d6rC$!8N z4#Vh}>(TZ#hOqX$?4M;$uCHD-rwRu+AD$ga*_fD^uv|@`f^(F4OySjmVo6LKyXgpQ zSWo1YAp7RtFUTaoxOMAYBMt+D*g764C6C$n3mWa$BSG(nZ#z(@T5Gpj=ZJiejc#hX zetdk~cO25RhHyJS_^Ug2|MtNPb2{M^xZHGz<{^*)ffO`^>c`n zI?JD!nUU4d@DebIzjOSk#Hl{y4(Fps7d>&KXD{3#VPb<@j)3ePU2EJk?e8>%a_h*on_jpz4#St-5VR^wG z*?T}#M#$2?y~E6-{J&WMTw+bYavT3(9-FyM90b8bkYG_Q!Kwjjr2XbAX!c^@9Dzuu zlk>xC3i4splC)&$`O}(_V1eX$a z7&*DQ{{HTLr=Q{8!vo2`Q*q3-Y^=Xd1tl}IY&7!&*ew8&VsCfXXf`y_81eNZ1>t#s zcx37{Khy-^5b6B^4iQ|ic%TEg08K$G|9+u7*o%3v;^+Y!0h0XxzSg+^KSwM6|DVtB z-~asoLK*-6%m95f_wTp+aQQjV&%t9aS3+O=)hhs!U6WDv`zHZMhC;pJJ;&qU=fAX; z5)f`Lo^XB8Vy0x^)NdoDKYm{`TkTu|0?t}zbO7Z?F~>)KU1BpGh<%-7Y9uBq%8J%M zN7d3!Hqlg8S^`$`;lU9<|1aAIhueEQUn7^^*y;fQMJFvSHSIABRlz-CZ#MGvN_N;^ zjB8NzN;XK_h~uJhAH0a~T1@12dUEFWIite7sF4W_Zr=#^4qf9y?kI<7%^h|QfA3Tk zmJCFQ$q9T-TrFRNG6C#-S}$BkNsoh4v3b6vI8Trd?0tJg8Tdt#2+e5wK7Ir`Hc3z9 zf{t$Di~7l0^P7xh8kMgBYRbU62k(cChl}Z?L_}m{=N}v!A3sj@X`_x+ow)r{1FToc zhtu^Ap@-CT8&+GaA>f0`#J{BH_}#TmB;+reH&m1+PAMC8+du44UCCRRB*m z4FjteCfJ+JevN6$$#wRP)>T*k9O+G@UtGi&5WLDaTMIVPjKHswY1B&uO3WHb==FOU zzL}9y%ND)hB!~G;&RKmuP$i2Ozo8*2lk@Y%`pOr0jK)^RR%j>uHplClzT}ghSHAfy z{y8APw*lAMXvFhMcAga>{)dV1gb3l~o@}erw&An07xVz+&s^>gc__x2edpdiVcvw) zRGp6(jeZzAFa%Z_3H-#*b_qXr_OFGTjw>qAzp_(N@i7KgKDDvg{rwv+rTOFQHiKPE ze?JTi;5WjyR-GxTw0YAnV4!D3+l7lGSJ)V z>5a*bkB=Klk!IR?b&eLP17;8$Vt$^fUqk(fc%Q?=3&ymU=3gTsNVqM2x0cc9=%!k# zH;h@%$y7E9=T+9Ce4LzWfQ%3JTll=|U(CF4vI~Aa&TjqJ8knmPk|cfNo%$78*NbY5 zA)Kwroa}6&SLlmmV5El5ZDl3MXgCwh=_fs=gg0-K&u^YY4h9Y&B;D5!S*W-nE>5{~ zgb04{T%+b!2<8zO;>@jY4)o^OT8miybZ_Mz;C=C z4ChKtZjL;&gu^C2H%j{@?B-=_6L`P5m$4al<-tO9&8}t>D5b$Xf2lYFYWvdImThomf$MYJ_GSAi)cOvKQpS7P36jxTH-FZSusd2jCrgvqH zMLqSZ3@?B_X%ClhCnnfaZmL-fkNA=6XDZ9cq%vZ95W`!{q}25H%i#06Dg%9kevG%G zbU8^`-RaIe3(g0F+?o3;RNG`l%}#a-Pvo7|RGhn2juG10b|=@-ZfD?2lg~tiMx<%s z@m-^wpbQKS#K3d~f`gPU7+MSRNjiT1>_@_^&ZjMtYU*qDXDe1L21{v2IJEB3+Xg@X zQ2X-#raA$eg>|+s?WUuRt&MB_i+nTUgN^B=e_W-<4%LvI;V;%Xob(8RkQNYtmja*G z>SrXR4pcj@KIP*y*|1t0Yu0qrcrnvR%%um(GuL?!zs+ol`e7(MdtPx1$C-v}pUjR*KxUHOOnpG>C*JgTndo z9V5xd3^a~MBkX-uMa=QM9V5r5&a-uH=Lt=?*JP3ci4gjbI-~26NKZ%A3imR6y)@50 z|FCeeNdoADOalTH6(Y9i_;9^oy2&XbEa6|W6ny_%*^3`2pKv{!OJM$iMOm1H zaEr&3rkKXPy$#tZy?uSUS={y;?_N7kz$bzCd!e&(?)WqH`}M=!iv$vQ*5%F~LAF)| zgy`U95Lw;)U-5HXq`KurN&78$5Yzw3g#F4?hD#CUO$TTuDv@J~R>T030hOMKnF$jh zC}HK@$->CRR$=!#FyZH-4Le);lx7`(1c~lm0AV2~yZDV9UIFS~LowPM!i0Oy=y$(_ zlGMdXm@lTkShyW+PIjA+h^!mDu-KLbktu7$v9QnJ2#hsaU3o3+(`4hu^KSbVF;!3K z60`qcAeNzYv}^q$uhY(%Uv_kMW+gax|NVNH9LX#zJK8TNBl8D$ZDplZNW{RZ@K(C)}|MP-LKhlZ|hO(73^7*r=F zp#3(}Uw0q_CJitG;b_-va_v=0`F6A&5p>*W zffdC3(e8g&FgIrlofOR;8=?Q7M?EvBBIvR<;mg(kE`;kfqvk6z8Y?yb{ujx;0{?~K z{eNfW{$EM@FOBTK@A-dKyZ8PFznHrzr>^dLw55TIi%UTPeybu##iOTIl9eq1|9VM@ z@l>@nSZ%$%*G=Ci;1UtG!uVvygigW(W*Z9oZZZft;$4-Di?Oo^Dp#B1PW!k1c@pC71mw6wMc zSC2F}TEb5-|8_r2vj5#y*|U&GK`osa9~H&vadFl;7zpyvMFwR^8k7fXw#|v9IPep? zq(`c)7oeZBgkNePRd}I2xJYC)7aYO=d>OrbO!*zU?jS$^@QASWttn+VBk60VzLF?2 zqoW_go~Z&k);ic3NncjVLNQfCL_qME{_#Y;qM8e!yuJJPPaQ6%TS{tl%OVx)N8(1A zp$Z@E)6f2x^89ib&RC#OrQ}(TWnDgmrvuZlySKB}?n2L1Js-*vq`OzRqQW=|fa`ft zz1B5TcXi#F5K)s1qsq*Q1cdmL9F?GXsfaDA-a{VjN0sWMSSPvdGn@Jj#_v%uJLZ-c z|4P?iue?p!^2^-9!UEtvm6esO2Hmu*^z7^hgNv~HRrg2yzK5rdzMs`;yUYxRw6_8R zazaAsTC+2ln3$er-LdR|JR)&-cLz-tjG{>SoY~|levQ^Oq5aYV4TGK8j7fXpbcEzK z(?hG##0K(2&2GYIR?B7-c3kwt!uql_(tn2JpC{x%$=!YCgtZ;?+#ZR$x=TBE{x=H< z2rXIlW~HIcO?Ku<%wp3%Ygr}ZajbJcxw?2u0hpl1{&=s0<9>ma?xv&V;PtabIQliz z<$m{-a&t|^c zx^i%6+Wd0Me4j>vOI$&(1!gLzI1gA{_unHZq!AQFotmK+da4RwGg@_>g8QX|6+B(_ zp5z&;=)lx959Ta#KCoXM^02Z>z670LG!GKyQ2Q@H4ZM49ai4%-p!X~wiW%HU`*wB< z3-?x1^3ot>>iBo7IKsoj|5<0Z02UFe!EOBe`1l~Thr6Zi*G=dd8rdJENT)Ylx$+Tw zqT9Q>2K>r4Vx1t(cR8>rG`kwr{3BE1)H??QHh9j;SN`p`*UD1Ar!Fy0lp75z0ecI2 z8ZfPj`uQm!7fvdjx++U;c9#f7poZh0NwoV@l#;VK<@oRh_TMsa3z2_Pfbv|DnFpPx=g+%$rWmE)K1|^pIo80qVn#tH?rgv?H7}!oz zm@XQ5=66eU^6Kg9-ymFEUl<3BXl7(35KYS>&ClE#SAH#2?Hq2ZIdhzhZ6-ftXCKz= zpYaT4nG4AcFL#VXHsTjf=_xfw|6=r!NW_P@Pc(5G_^cQ!II zD$(}`atYSp&|rADVy84UC4-1%#QY{cR$gunO-sA1&A@aG#qbmA7R6)#@Lyx>*2l$G zl8i^3%z5uyRrVrFBZ!DrNr}c;l#{J$)G<0dY-2nQ2Y}BiZg+35lCpA^A&#&7{8O`Uck%Is`@sE_xyZ}K^~|2+FI6(Oe1Bh9JQ7YP zrn%0|B;}sN0Gqob&z#^8vvDlAnV)*_Oz5mKK+48|-?ze~wie zZx*aa(XqVCWu&2rqac;BdBnujj64&NaZsvEf7vK=ajrni>AQroQoAEcx4My^zgGYF z1}PxaL~yspYwUlNwI9(+tzGYu_ zz;}W!?7r1c1(pDROy>7uZ{EDSIZp7{p7if^#zAF7^K3?aOMaMjSTCe5C^+3Y`1^?6 z)XmjZ1AQMC+l`rl!Bodg;rs^bIV=g7tMw&z3EHh zHT?B+p4V}+23)X!^MISaM~h4N(H41P6QjoW$WhCw{FIb|CM~ThPD?mH!1d{aO=k1^ z4;>?8Pax=yQLSPP^vKu2KVEPUIwGsit(n_ zIz$*mGHJHJgo$&1@E~V>$X8LKzcf%DI7-lrule}}x$bUneQTzS_DA*Ihv*!lX4tp@ zHm{5NAq-_QiHIk8J3HyB8=Tz%+k7PhD~CKq`gWAd0X=n|bLC){VRe#g>(}2pI3k(A z_tmt5sF|+CO5@oa)8HAxzQn2hQF;gBs7I7B&Ox5Ioh&V_t;bhxCa~e(yVup(J8zYq zS9Tq08SI~?Rh;1#cB@myJ;=P{2yp&4y4R!M1&?`8oTBs1RZy;$f>J`*pMGq2TU!v& z2(sTmVrr_Uw!Z!O^Of^cMoq3JQ{3geQ45)G&&__$fehY!vXPaKa$$J~u@mKRfI6TK z+_bY3Lq63&ofbS~=F~avrwSmDQPGf;eDx~vDvmkhX2(66;<7IfIKs*A+xBbSz4PFW zHx?oD;xggWc*Lmw99nY~YU-d>%`sT?p8G|yD>TPSwDm9Dub@>>F|sdiDKh-V zE83=c`)gd>4-N+?wid1U_y6KHGf=B!PE-H)FihwCuWE8yrQ&ZLCchqit!vq2dK9z*k2y(*_w#$ zPEU?v?e5(Ul#Jn#R#aq%DepJ%*NZ=VkxZd zj1MwOiiw#BTrFCrX&qGKG&V6hMxDrKKVqgC&6HgKYpfo5XRhCowBDQGk|yHzGupnq z0#xZo%M%BGLHt|4tf7FrL2|m&;6nHk`Dp*#g^o@$tucNhevE4-BCyRqx(KBfO;~?9x3`@vz%k+qQ96i!^!E6conlE+4uN z-GowjKG>Y<6MwD&B@Q;@CdgpR7Nb{;tT8ZzsWNu26vD{OLCL^E3Wo{@>RODQodhs& zJid!9SuhrDR7V|jyNGwtsB%3JEna$)Z7tqx_U;^J6Z&Pd|g+1ZVKpj4epJ>bD| z`+ERkW7|99;9%U%H=Au_{9&4G9hgYQu=>@P$J?9FU2cd_QSG<`^hfd6uoIRv z^X4))H-PfvzZ4gjrgXw(6&CivCFX%pg)fMVoSKNN zb`miPBke+IikMgxDC-pWhUpYRnQ7y(=bvaiA6c)QGq-lnp9C22$k_(bqFopnaXUHL zWA{iiO?74t@OmVzWPb+O=22DA(*kn_7hxO`0}wZv#qL3rk2s+e;a5*jXmicYNcITd zJo;RqaZi!mK9fKBgI6NmT((s{Af1JUVb$Lcuw~RgHQ4rRN={N^LEM7%3Iv7|_m2*x-O&c|F&$lwmpZ!L=HU3)&o|(w z-kNp+ek!;T*j31F*AK}?3F{M+)YvQybj@4Uj;CF27v;Q}3Tb9i;Icb5(saK_@;Ku+ zm^wD+b=aCL3!sM>Z-^gcaA#%JLR=~~UCy&4BPOl9B(=jMBIuekX!W25ujS$C<#TCi zYw7Ihg5<=d#XRMWFAXl-6uj;_d$Ve@H?AL}Q0C+1i3JpVCf9GAm3PB7?GQN{_0I?9 z+&VhRK+W?NbA2@bovCWoW`Ayhe87mgyxdngg#m)lH?`dmk3?{_x* zG$uJ{>FJIfiIft&P1jc{j7N6{Pvl#LTklI0Uumb&8Yoc-h~LPEA)Z{d?ohUMS+Ke2 z;~%r_?d^wTT%U4i#y%{-DiD}h`OnIdK}1COiXB}<7ljHiL&yL% zcemQ>_T;h=Sv!_lbS)zr*Y-#t!$}Rdx3I8I#-$XnAb{6&p9gZ0>onD|Z?Fq9kpIU@ z8)%Oc@W@zFwBFe_rs|EmyVLC==FF4}@Q@3|S_7*PI|u9hg-P z5n_TDGxa4Z^6lgeC??fs!EVNx3a)51vvp$}tU(%eoD&Hl(TK=-YcfWrl_Nej(}`*@^Av*k4qBz@uK zQ^LsA{WPCBfmgVB9(rJj9>&^(6-qiP2&DvDVj*hwYlKJsf)&@oYwQ#AR*F;|9UJIg zGTcaiF-Zqk`QmO&7VC?g$b$VSbJ-dptuxoOX8cVyrEIxZo~hWbBh}r_nN@3i$god2 zA9da~ES{RWqWKS=FIju%;K1I&fr*~J)@D&Gj2eF*?EwLSlH9K4)t81008i+WK7{3S zquVK9vq5{{i9KW44_;@N4)J2J8c;N7+xFn;+WIsO6S!@6fm z#!vg-nUhT?qh0<^O^uz3`n){gjp+49KMoQa?fp3gVC* zS8;v{O>1Y=U@PhtU}IzFv|OaR|4-l>y^GosFqDNwgjFZm@bbU-e@Qm;MnuPd73zZ~ z?u_`x4Pi13K6Ll{G}1}jR&yc#r8Y-}8qq~a5Nm^M=F#-OGm8)K9-;Gk+vs}o;mS4m z?Kpw`#)4glefxH<=2bFkB)9;7_+YCujW#zogJcQO+1BQhv$wYOF(f$Z^fV;o!b{}X zsZQjz56}hqoULdm`2zWk=v!KZXsFuM-cIaOu$`T)F7RuTLsF5lZAbYh7e@>GE!6yP z1`N@Tv$Hb@$l_*U@vzivvhI>X;brIK)ZHd`IG@lbA{uwt26q3LSX4`cs~C8{+@H5ne#t0XU;v0Gmd!gy}S4QiTC@u zURNwz%6OgpInaESB7m{LN`phxeXn6&w%Pme$ME7@J$8HSZ0>^f?|a^%AQ58F-<1$75!T=&gmSQoW>0mPa$Z1L zBzESW2fb(jjkU9$jryfOhHnG483mhrVH>?==Ph)mrcXBa9XG13`T*1NCooX&Iyyf5 zJ=azXHSbS~h(yRM%6jadnYz2XgJ82pq3UUaM(JNN5P2DG0B;8H?D*Qf{r!NtgZJIm zViNmvLPUcEt8VC})QRs*!{&MF4qWMbSc|Ulu{w>g*yaK$YKb$4-^l|g> z)au!on9!O&QI{(Qc>DZ(7FycJgoLVBlI1`{!^y{Ij`0&DwL|9QfE1&kU|A(ld;mcO zMHk#J%l0NG8<4N{#KmQ_>9}Yb1!cXD4(un+mB1=ZlOxrz?t=nIHW%gK5E7aywd67- zQU|;ZY6b>FEu}|~=2N4hoKI#Q`oB;2#vuHg~gSpj0gjk3| zUVu*Ce?IXW(3#Ko2tn@`RB+<|WI+cmLJ-BVaCj!&LsIUqCEvwjQosgJ^29v0c>&msZ+jXn(aHfb|qsrC%T4nAOHL5>t#xYIyg8a2zyk$ zvn3rJFEi~18O)Xz7B3E^C~)}SfDM`HA4X}0$K-v^YmEn-;?&fsSGoWir&|r1%TTwv z&wqk&Kf?khBX%yjzhLR;bVf!--2W%AbY|J-JH=BSCnx+t((e4nvscI2GOFfEMPim; zid7h-spwV@ydIf;w^jSvr^3wAT3l2^w&`LNulmWZEYiMs@SB6t&Pe@dh z3U8H*+JD*`MDM!`PHJj23a3Iz_|oNda{zO@r?*@Gs99aTA>fCv?}L^80pp>iH2|mw z3sf+KCB{ASIUPD>*Z25(am+6A_Z{3Y{82LkD(~@B`ry+Z;pR?#RoA}k@a6Mo6O(0t zsqI#TBjWpxdd1*$^gluG3Sa}=?%wtCx^2tR|C>z*l)B^+UwI!lNf2B&qEooZOqRSCD8!xP zJZ-Q#kGUJ{V`a46@$a?0?ge9=gyP(54TaguUKUWx7IZ)Re$!h!o}^Re-UiHQSVa|~ zu4bG;Bj3wvF|diqZ}80sOS!pUE9kQ3vNDEqpWpY^ZASMrCmNs!pyg-W@Un1F85(B5 z6j^?g){vm460{lhE}sAVUaGeetERw4zMl&+yI-I)DAC!twR*0@EXRIaC>x z?jMDOn#mg@@rH`W42<8dcm3*HBtr?We1nD1RgQw*OY^fcLJp2)o22_iUjjoz z!7#dkF3kCq9$A!fmA4FP{gAk!KB-9s$w?CV-X4Hym+EY-3Y~wYC3RN^RJ6w04uRXlL`!?>2@%mD*^3Tz_TVqSbA?pFroH3l z{~db#I&@^F?UwtQ|L+)$*uK=Eu|ZX3_2Hp0*j)O9DQs*a%IQXJwFpBs6;FUx55lU* z8@wq+gv=5k%;^E1gq)bFz9hFrR9hF?pYq}!R?Wk#j*N?t=w=X!Le%}Fv#XQMuue}| zIZ!mHEKdDeHww{OUt@OhlgZZ+NXtM$ce0~nqN`0qMkc-xS@3xwdureA1TgGu0;lYOZN;m~VC1rvw@O zc>nU`{1m8VzAg+!p)(5$d!LB=`NgA+O^kj#$=ecq`uF>5N-1{wshM*-OhvC+lY?@m znT?fJPfd>&$d+wufPLtV+-Ig=0BHLg^!{#(lK;2QC7So=za5iN{3F0?WoZPZB*CYo z0KsmgKmWO>I~fOsuB~F$SpLx?xV2N+`uV){E}K_ADXxR@k4v}P;^I2)F+8M84zflx zvk2UKRv2wHS0ybi)oL@4TH9(L39@$a_(|)2r;@S;1wUNE3Hl0MjT^NtsbOT#tMhAZ z{j#ep#?460oLvOdDgU0~d$Kw|BxdgngXdv`QXv5Z`)%vb(F!QPR$S^MLKQ*A^lVeT z!*}oB8yLwk;sgcC=0Qop*`9H5R-O6P29|ES`4On(k^1wHvUc>#1U<+Uv%hCagyV<7 zg+TIi$E{z8atH~CBw^9f3Up!4Qb~ItVd3*QkWR+@wNZ-~9DFQvpZI{1DDzDjc3f~Y*3UM$C#Dg>Dj?B!@R=RC@u-cD&Yis*$7ro0im7ec~3Om$952Fh>iG2fFm zDk_jqNuy(wm6XQvCf{l3=r|l=s}4sQyZMy`-G?ObkoNS+$tVV9iiH(KHAG1|gL5ts zS@FgpB9kIX75!d1bU6fJQc2~yq~+&vy7D`ia>0dh2{Unp@-ea$Q=hMW|JhUVW;V?? zub=_ii#1GOVVUIzt*FSu<{Ba>1DE)C)MN?DA4jT~s<@UW=p~|#mz@Km1(5^KE7gh}m6Z|ZhN(JIk+_`B5Ol+B2<=>Z{p5eygLI+)I>in2>hLRF+hR&p|=>GHP zat>CeLpj$6zdS*eAqO5(q~X)E6KZ(Z03|FOLz9Uacd>hC~kuXA7I+XV*v7~M;6qmlO zk)x+SOH5r}g3}(AfYatbbX4aw@U?1ulw#y10b%~+vrUfy%71W%ny@!Nc|u>*r->TuJ`0n8>KPllYf7#1Y^3FbtVNpL+OlJ z+l;`EHNyDI=PzGY*tLdGsw-dz+Xa~H|7n+aW&<|7X?KjUn}a9^gCDV7^yA<_YR><( z{y$P-@x7v6=tATJ@FjEz*=gH+DTys6M?wE4kpH$)yM>ii`ImwDciBfz@zJi=$?82EpbfL0EXx8R zRXwq%;5+%FEZ_pW$o27JDj^rvEHXhsw6SFPeE8oqH<0f}pstPphWD-Mrsau9pVgfJ zT;IM_A$V${I$t6r?XB?Z-Xu{U@8N8DH9L|4+h4x5~_{V8O8 zW@_FBRS_j$Yd$|#Z3j*7+)vX<8}+U>%I`bg#V$9qnf<+BQY?V;S;Nxp>+XE?rzHh{ zhamtXFwZwOri=Cd(Ut(*s*)GR|Nd?s;85~Aul9`4t@_nIanqk2oM2HWr|%2organlsxQ)>tE#Cj*?r*Cs_{DKRgL{~T}92;vo0Ga zs0qO*Dt{#yp2jXNRR^B?zvZ*p#i+<{j8@uSi^j*t1ARaODJjr@g5$M>>G)S-y9TfG z7?doKx2b7*i)<4Rx%`bqy?lPxe#+U#(cIF;FIhN<@|Rp!ll8))9Ez&4<{e*4!A}342bIw~ z3%$&{v(2vN{e6X|%+=O&nMFmB$p{|rjh&a7*>b5URy%d*{^c*NweW|H`goeuJ zZfviRJ)OTE?uVl;+BOlnxmu>hTZ@~WPnL)t+vqhfOjNj)8)^mqw-#Wjt*ogT{)tK) zh(HpYb>%`HNJ?8!MRpbCde z#wodz1yY9biAb1eSR~vaAk!XD&a5n;Jhp@PZ?@w}=;`Sh7}#8=8#Cv1%?P?$%#@ve zksUuj+;aPUf!Ih&Vj^rSgM&8H!K%<_(Q(`0755LPdQI+dLmC=3uhR=4b5EnmFC$vsRN=N%s zb>LfST3$X@%{180Pzrgz>#EQO@+(kjd-4Q79rNvm4_HqVlKPu+(-3<83xLx?<@#7LdL3b=+_rvZU zXuSG7E!EUJCb2a zW~+!7q&)F4Gr*C`omKZ$_z;dN*8?hB#e4SxfeGzwLI{L;b$4|&`cR3(wLv|PfWRni zYto?Q;-{D?{}UV*zVF zDjAUlR?aFa4W5(Y-i@`jUutUjeUuawQX1cwE}n_jJ}kBmh%A+PH!p>wk)g2QP4kp+K$uN!6u zwA#K2b%MGjWmRv#?HcntY#g+-_vhMOE!}kgEiG%SQ=LFTL`W>^uwn8&P~WgA=xfLV zoM!%m6Al z3(=dA)vZ}Pe@H$FGQC1VLMGyTg!F)g>|pmuPDVk*!R~qhKNH-(a9pEU_5>loN0fAt ze|qvOgp^ZDSs8E**EinT+eg+1@aZbZ$g^?na*b*o#3YAP3jeFx z!$f6=;mT2{;@$cmv%eTAq#<%2s0d60kHVtZhaVKkM?DLgfShUxXM3uU@>Ygl^$&u65>d1fXJ-o{)eP|x1PRz+WB z1aR55_4GV+_n;7RPTvE0Y;brK@H<@U`=6YCu+_O{sQ)agajCe2147ThzBcI7S-+B@ zqONSPXk0S9+csKxc3OK_x6#$SBS!8P2_1h$aEgMB!A$xm7PSi&dh5 z*I8_QicetI?@%B6S~@l0H*j>oIBjngcB-0}4Ch)|)I6lD^4Vt3!A+iM;+3|xJ{V(L zCo#AU*P^oRZ^zqK7I}G;b8l!U$uxKc$h1Sw-3V~{&P=;Xeq{pPf`n>cvar|Es(onU zNbSRi(AJ-+lGPT!OHQii6e%Y8-eWdwl7-~rxr=(ZFJ$4MKmyhSc7Y((1{|*sN7p{m zfL1=3md~*KVabAx&K{j?>0duhxqeeZ^$X9cYAEBAdmb*-e#s)z6>&egA)Nb~6Ze=o>H- zP5LehRi8iCl$B98d(ULUyprn4R()zXUtH6RD07y!Nn)uo?Ag5YJo;g3Yb*Ni9~&>)W0Rl5&#VscbMmGs-W=Rc{&K zhLY*mdUrS3(*T;t)r5J!Ax~+!>Fuz?#OhLnr6q}&*mDWJGhWM{b;)B9!TJAuWgqKS zmRb1Rw}yJ-QCy%RE0$Bnks<_`053czR%<3EKm!xF0{%D?4gH_Qfgxou@KN$pQLi80 z(=GGIU@LtoeZM*wnOM$0c$C58;}T>b5SNV+Q-C9VtoYBpd$6gp?<3O-volluDX(-u zid=S#i<;Wcs4tFygW2X!4C)8tMFyn6p*_jP00tGu{kCQx5=-(p&Sd<2jfp2A0qy}H zhQr6lCuK$Q!DAHz>JKnaG{&Z?ZQRcRM{Q?t@BaOV7mO+@!>euw9nVF)w-*)&S9*Wz zPtsB|Z=7|pu`meHaKT%Cfqn`YqT)omlh5(iYU4BgQhmP3R##tLZU3bE`8S~KW1Y!{ zBH@~VpkTs(J`DTqqeJA+llL?}?nft0so}(?r$*n!KCxU^YZ(}bK_HmZeFhev zYgt`gDM^j3p)citb7oFMGtv2k{WSGcI==8#va0k~apHva<4UrL#GHX&_*Gu&)Xcs|M!BO?vLkUdQi^}zJ3 z5n?cyKjVXb$see{Fx%$Dpx^q~Rg-(KAn154wmo!pvdBm;a@id^Y-;o<34M9S?<#74 z_9ImHScluOqOz2L2%m%x`c%lv#wH~vheLfRn!~VJz+r=1-DPbF2%Y{MuMwY5VlT&; zfH{JLnU(y-ho7~>?M{aebt*0UN2-#NHeMzhdHzWix-Z07cmg1&=0ItMSR?#wVr~k= zf^k@i|EIgd)1d35eVZk|zfS23Xib3F7fo}>*%MRKm5$B@V4(kVILBs%3h&ppYXED9 zVjKz%lJCgyDP4fN2iUQ=9A$sA;w8rkl6`#3fb8E+IjId zFS6#Sfi>+v2amecZCucFR9>WaFfqZ#B|&B6wf+4k;2$g6ntl6LU&3*xWbnV+kr7UL z3W_+v>_3Y3zlRbZg-A^-${4ds2h5&-kI{f9U=zi&S-zF}uf~1u#x2~Bn1rx@7aeIM z-I_KB(^-jJs^(xz_V@UhKyp6(#yt$giD4f8aqj*-4k>{+PHh~Z|48(5|DH~X;g4I( z88%ny;bGbK?=L#z{9l`^5STO@qn?9BJxT8)KnlAisf4 z^l~u(5A+7(i|JoHdw1MNVLLtD6~1?T%yJEyyk9oXfi$16pxN!RE++@u$F9F98ANRM zn1lVUfa}p*_ySm~fYxuf93d4|Y;tn1S_{B+q`va*8Tovk$OlT@rZ=AYhgAU3`6Cj1 zR2E`H{Cq@s9alG@di`s#RN7+}h@-hOBn5AES*sHqAA~cz7 z!RzJbzC7R>+rueQMxWGu(5G&0`1rpLjIVaSdu5ZZ2G}3>B_9|Y{F(?kI0#UhV_gA2yYiiC1M&hJ}h!YM@b%({fCZSG><-NUy01x8M@ z8}m}1K#Y9eI+x8OGGa1##G|=F4?Gv8H*eHHdw@4fV~p1{8`)U#vi*Y9L#iFX(o0PR zjcV<-3d+iY0Hp-PUjPv3=Blc2KnsXut1hX?Z~&`iJ!@7WFEg{Tde@uoSXi~!Fs0Yq zVhJrw`{GVb>j378iyKxIO1jHp37CJ7I@9$rxwpVQm?G+h&eE-x!lUZy>Pm=Dyf*&? z+JD7*7}0NZY-}JBHIcx^-ptI*z=pE1!ndKJH1M7(;WD1_9cRq+mM=YyeQ#Zt`|;QfyaTpx z|6>7j5K>anbKrY^>1$hDMK0{gFC-Lv-vPYUt}f8@I>W+}!_lv^OSu|lEiA|vxCB|O zJe3t>e3sN&G9I}=8w}&icj?Jk27AnItcob4@{tqfpV#)h?R0!2!o!-`w2p;uUJjc+4 z%ke=~_77W9pBbc~nb&OzcQzRrrfm9Elf6Vfo54Zl106v@!N+t1#Z<6=8!hh4vmqxOelq6p_1Npw_9wrxUXD9Vpt>?eQ=Qfs)H@#OkdL}1(w>CDC zt_2*;rm7%5tsg)6Ei5i3R9j>v)2y|mcE_&8L~f@|SGeXVq`p^Q*6gn}Xap?quHuxV z`ZQ-N+im$yV?siL`r}pm?pE5*QOBNbjmJ!`AMZQ~z|~{OvvYK$5^*reivKPZ#tk#L zq602JA+NIv@=p_!Q&zE%X!?5{p(!1sl~)~unWh;P1#){rr<-=iI~SMLcM(DH32}W% zJhG@0%;sGEI4CxJAGDuA^U$>}cfPfkDMb=s5wTVJVd3#smqbLcFF4ise7pwKku03W zjd(^vLYmWbCU!Of7gn{PPDPj3-{#sW!7vUY&ovDtPj^QRSxjJMevRf6c6VQgQi!O` z_DuTBD`oY8S!63MrO};3yNnUUZzeou*RMlMGQ+f?IX60)C_PW`Vef&)`$4eJ$is3+ z=3}FXpa^%ZG?3crKA-OMlYK}I-}4|>&}(go4jcP?F}ls~qZh1Ug<`x{8o}O5*+pds28lP=u#vd{5E0@PzkrFUX#bDEWrNbgM%WUnnPM(pilu z6l+&@wX`hmWi2gaEQolWcgZJnU{AZ%3MeRqp`+@G$Y@ud4sBEuy*CMAym6lU|Ei+tn%X>i7wDeK^IPJ)Apuo*>(u|gS zx(b%q(0O%rg}5IVA8T$GfJYG`>Z)LzrrqdZ3fjCB z;U(GR*jQbS?9Q$(=8k^&qc^w0G+=0_jR~fg)O$ZpPkb*fqal6|SNAYCQS&tMfM}^OsD_Ws{{avJsk)+C^ya6agW1mCud0-MkT5Kf1Y^XNcR z#$(7VRG&Ui4U3J{e5YjC`E|KnHg5pxll6p6w+nXN)ClZm*m&emi{S(JPP3F0Eh8J6x(<&cI^b9r;PE-1&h=cVu*U@fJDkL7|4XSQA=)W@G~CJhdL8 z!y38*y+y4a>-*yt3Ocs)O%T%qq;zQ6^7rmK34Q4pXX*6}azs~Hf#UTQ=6h;ITL)+R zSZ`uqaxidF!pSpp>FCfARo@Q|xKSHKPaZ?QDyymGrf6rXRb)wL4;N)L3ZAUuOEH+k zgzYZtoY{0LBK*ZH;mS}644K0YEs-CPctYkK=&8jEshNp-=bJe$>ObXLRoehq;AjCP ztYzu0>>)i!0_?>gjzgZNdwUK!F+~P6lwx6xqgsd`FQtk&LvUN8eZDkcotB_MJ8N=k zbGoTze0**wPqL9g4IwzArkIDr&VI;T=+nQYA4#U|A_1(tl4AHj^TbVW1>YH&Ai z);S{USlHTBv*k6z-Lu}i?L<5W>z}S>*(Q8QYbPY3tSpQ|C=YvDU8cLay0|zeAcxz2 zWyy?zp1vc7W{%Vu#hp$@im){s%^tw1H&G;dHDsF0Sip^t9`zuX{Rq)Juu}yv{ zF~fV;G#}Jp3f1^+FJ?GbhjR>-?MqE`3w5f;wxWT8A?s+i+|?FV3+DTov8&4bacTPe z=;KGI!8LszT0_S}GWQ4M6coWhLFHxT>-Duv%eM7XN3bbGkO6acwprbpe+*y)eUg-s zWubcdHalOtmRo9jrqSvLQU=2(%*U{DQmO=l<|Qqtlr*N z1f0^ho3=b<2$tvi^y`v>g>o*vc`V&4w-F<~V}(rpZp(Lbsp7|RW{0kS&uB2~G~iK8 z0~74&FB-`&pUHu@V`~QV|A49P>d^cgmx69Qe?pv_sJgimn>I14Zoj`LB0T$*`k;%a zs?Tp)G5C!1=$0&`wMdJ(^ENhK^BxOnpq6=tlcwu95d1Yk01$?~B+Y3m2 z6Jx94GH0t95z@NOJpkRfQye4J9R|!)lqm=`_hu+ABEV#%50WNhH89qmGC)lj+a*#_KQ^fLmrd=dsig z3Di>%>P(j=I3BvUML(>4V{@YrR$vIEm0^6U;US5GCH-#ob%|8{d^gm3OLcjW2~ zQRPh~TzMpgBAR=8W@dTkZCDEy4y7Jy38XbdcQe&FmN}Ls=@kv*r&S=AOy1PoAvZP6 zs^f_Oua%qQEgifC79Y#l2Gz@u+Oalu=P%2*-IBt$C-Y+~Y?4JN=ttF-u~gU%qn>zJ zO29|VhW!ugf@w=4Zl3`XbH3u7h}-EYRB`+sAw%bGK3d=KR_sN?D;PE$By~!chqza; znIeOVg!f|nmd!g3v7(8?z}c+v!v4=Dk~O@V%nwbFgU@k7!nk$%o%vPOrHU^;0)A7rl4 zYCL*vSmUy?OnI@*0bVGLJGs2W&B9XNlo()rPrcqTHS`tX$SM_fvdUW-Ik^q5LgQeS zK_(g9NH8ZP6lc+|{aPC=HD)+0ph7iM2s$b8I(Jr)@>ynhy%Y0pBKHSuI4#{NNR|35 z<%iXG*47&}yNjUPXtFpBDETM%2%YQCx+Pf-+c=He*P4)A=G0k%y4CW<-d@!ZA88$( z1T}aR0pTVEIqgc`icU&9o54YVTz+WF9xp_hjb_j$;w@iw;-}P{c$LbX4B-dKm&fCrh)=iCeHNXRZjFv z($Kb!k_*1G@Hy{p6#uf=`naJtbRxG@1wS`SukO_eI{tAPfn}2QU>+jDM*4oO&vps~ zwg`DUs-LV?>ZIMRQJA29z!e@8h8ZiYx8V%7N)J>~z9#22I)SZyPZw5yBYWN$({}M) zVp|z1=Szu70t!{gjFtg{`xH|oPj3>=?Q-!M5)Nr{l6mP1B$M^EwKI03SV2pL#O!+f z_6u+FHI`RQC|-T|);}jIEUd3@0B0*?4s_2-L3xNv(LotlwcT1IaacBQfG&r%8FV(& zTrsPmK2=gxhrWJbroOAa+hV##W>j5<{T*1e!CiHI#WyF(F_yP3u^#4CfCjVfUl5YFGXeA0Lx9ADXXjBR^iWp~wO+$R0nXeXoQ@sKql0 zU0*clHCv@4%Y$V<`h2`#>5K&T>&7p-_^zVWxk&p>uO|SZ=j7rNITsce;>2QQ6*%s! z_*7Y2NBb!$X`x}YMiWmVx-wQ?=LiohdK&GXH-T8?l-XGbtYa4S+qPJ(+mufoULLW8 z^U&f^uG)bWzca=}Ln9{BFEbETy0MBhD((%dY*ow3qEj}{?0X}*MEv+*bx>8vuxW!O zh7C52L~ZP6^~?&_n+orV9Z9=Zn#u38 zj5BcIsv|xfI_;0GqE~{wVVhG+#3eHDwq=H1{f1}h{+{?)IHX{JlROPnUYyYpuwVG_ z_FDe->78Odnac9=;4nemZM1@VJx;e7)AxXlSWsA4-t6k^WM^Z8|B!uh`P`>$;~;^@ zHrwW#fx#?jyO~?9EN#%@&}k5fE;FB6!sjvzLgYk!nv*uHH^;?M7Geb%aBDIG@*T+HMh!Gg6LGTt-T`XTe09U0w zzm9+`xi7C%Z2F)Nf{a4-v@V7Jp3s{KBP(HC=6k-1o%O}{aRmK z-&ksJYZcq+fD|Gxn91i^udekS2mVUf`WR?It)^HOMEkG@MV7ulrW6M1 z^b*g>q4zYBkoA0W`@F+8pU}q4Or3K<&mAQrBk}E7uZItDTy5qwbX1rFY7h1FscERu zY6oWmWJIGSGR8ADOORG&=W0q}+OSB{GQoy#Dyy{{`O!89Y$M94z@QP5!ERYQQ{tUGVoooYz{*y4ezd_^gMLytS@J$PIr%b|b8$#T z)zx~AIe&AY6s~rTw;?*vh`3NBrd{9yG@!RVx9nejWIV)Lu zsnR}C;(b{`GF-VCf#_gpdOxG%MhAej;a|T-xce3t8@AR**4y+Z@NP~WZj1`Od?~Ku zy38@OITe+f%3u`(OLGhUy(e$0!r2w*>7z(4%C5+ak{a#1TV@}Y`eJRQlMsiDPi1as zkF6>LyB&=Drs@H>l+i&85$VUd*y}32a358UoK-WJkn2VSv()++-^Od&-nc~G7`(3B zum?1$Csk~TSDSw`EM8af*C)~AwaoHDDZ=hwqYm*8*4A!A$+@XV^sK6W0-mpl-4rV&aMW1 ztzZOEE#F&9Y?{Kkb$i(bv($O}{1^w+?Dk^!@*~vT)XlQZ_8f;Rv{jwQpKHr$$4d$; zzL`}Flm%IfD4U5r{eiGTLWcv3Ov%vc=@~k~Sbuj^7bff;A~AN|bS=}X=|5s`3d6$3 zHGcJpbg~nfjSL~DQ!e=QW@1?;wK(<7)ZX6G#OouEWxtU-JPn?wQ7ZQ0N-~{u+b#+k zRe})_D)y}DmlkC&%V~ve9bnwKJ3GSy0<~y%R!pZU#-eO!;vE!PWqzzcrmH(i2t*)v zu&^f4lle0WbiL!zhQi-piyH{shtxWCcI8!mGWzgtZnmLe@5%yW>5hReE-cX2sq`x& zR;65i(`Q%mi{if4m;82fnkl*71(E0?2XgKOAy%6jmW(XJa><+$x~u{Mr*$pvYosqM zBFOKn)g8UJw-4mLlHM;~qEPvySl-4!#*@gX-Gsn%tiTIeU7XtQ}?o&}g$M2%W4f zB;;ZDxXaGGwn?diY;8JJJQcuM-ChG&(%xY=lQt$CGUPQJI&rLaL=^p9+ry@p3 zlbaxs#etQQjF4utMTJ7A)KeP>bs9F+&qrldbw_Qk3GOrUaXBn89wtQsrjI{XfQgxz z!0dcd!SMU{TU%RDBJr>g4t=4VHK!9xb7J$rosm=g97V$#I|GV$DyY@AW=y{*Y;!X! z#=_#{*sRKSn-WD7PQs<(`qdD{)*+92s-PtCo$Ego zF3|GCKsGy{KmX3AQ+c$rqgCqZ7J!?)B;0(J$Fpp%E#2GIKEAz^?ldY{Mes`#w zkU)J88#^Gq#eclyRzcK#XhPp%4)^j>BK>wF{bd)zYB43o=aGtLPj@FAf~u>J!A?j_ zv{bauBRa*8)XDNJh_ZLWOt`4OaFom?J|>-)LvXu7Fq(U}ru{z}ND83*|z zl5eGC(pVGWHh8#`j$K!^sw;gjpHJDf1Ud53;S35Zzx{#hY^CW6*<%~_9&{p#ecY}2 zj75m$O3zcq-v0D80qI-j{ixex`I`NcSt-Hg!DrgBh?uUXMiqv;>X&C|Z!I1#kLBgj z9UM)?mqTr7d-^eR5McA-u(h4!dtwl*=@e{gV)Ev*RE70unfCq@gc0ezuXNgRTKE{I zWqt3yFj6kby+1{|&sTfFOX+*T^c-5$1dfjTva#%z=aZw3Y*Z<5lngi$@0R**e_dYV3d8BCy zV6I5Bh9G*Mc%#NMXA zo%}$ddmkFkTTu~9q*SC{JtB6pU}VIuhr{!5DbBz{Ya4@Mc9wN^-x)|(p#s=()GsDx zjK!%>5hGQS+-%EDzkrKcPgH$62$HmwH~$^ato zS%8?sLvsV=_hG?83>+#RjSnl2*hkMP8{K7S?dJ-xt@avtST)oobrQHA0~8m6Dz8Yp zFxkiXXJy(q3G5@;1bV7!wbOz;wRaWj8jcTwqpUQ?hXp<8%P?!Z1UEY8SjZ$sF-k!C zYxBy)eB~K-q-2{UFYg%ILS{;hQ6^x7?)TM2c~t{i`A6TZNbdX0o#7m*5OQ_POSf9L zz`#I+mD2fuEL zzN=NV--pc#Yv;a0%;Dy^VN*w$o95XNTfsl`$Tl9c4XvyOMo%PP%gE$LRz8dgs_srCaq90l0bQo@$wnvFyRuC;Az` zp0HhWo;iRa<*23Z?q+vCldO8q{+`3}_7sCms=H@Yq~P=p5`6 z0!I~6&)!}klFN^N2=cg9A8$=kJbvnQ@pISv65D#R0ktlws4)};E%)mS)B#z9q7F+X zyr+oN7lGR0lmaSBS}!d3qQCj$nEDWS@j`sD6ghyBLMYLy<+bz#Iy2{Ia$D4DvY=a8 z?v9|JHsl~1Hn7Vr$L|ovR}7^r47tbWg{FGz!sbp;wik!Y=3`eJu4HlTvK@bsy%_zDHSDDUPYm zy+5T;l#?5<`KFFdq7K$4DWR{*ZTk(h-HD&h7qNxP)E@*s3J~Hm_2La-o|gt+oRW~l_x1HvKWVz* zT6naHW_Esf6MM&&v7=wq{Xp7DZ3H;LvwqatY$mWJ^5Idw)Dlu!1|~1xAD*zOXqQ#i zWu0T3tc>!afSeqcf9wUcBK=w+ibxH;<$ol@yq$Cn5kd3`>rK1@C-9>ljq$olSubZ$ zo!!$@mwK;+BDRl~Y<0F8>v$gP4#P*K^H`4c6m^ZNXYDHM6q*`Me-1>uIBY(E3+`7! z%jrSGjKa9mh_RBIIM(N3sfLzPnunm5yn-@-{G!2POOLbwxu<$~3#V1R%^EIxtz{v7 zOnIGqBrOzI;o8ZC37c>Syd?G3cNtNFDA*qQjf}{8RK^0CzQM6VgrI?fO%XQ_WD`9w zpoH7RqQ9nL;P$R1jSC+i@SBD%vPS&h*gE_y@W-t~{xEEAO5B>FYaiHM>ATDQPsF0M zn{EYOM~`$d@rY7YuC?X&VT+Nl8v?bO9(moRC(Z$vaJJ_dYh-fc3@GEVsK230kI1~v zqsaqxPtWdboXN?}%0*&nIzxky%}PN8d3V?Qtu!$SliP4+xXXuIs1++X?&c^7s0C%+ zFVuINtt>j!7}JQlF}o@lxi@Z!x7ygyP!YQpd1G+2(%_JII|AC4_FbYF36M`<$AkQ& z>C14sWj$FT16_n_oKuC})&S`vQ?>8Lb56mnJ}<Q$=P3IMe6~M+Lp$;suHmlPw1r)EP z93>A;A&pWoCOV-N ze^FQ+^yFwPj$VY_2HE$I5DzOo$(!{m*s{y+LhIMzoaI>MrW<8`(7`f<=Q`cnsFvIN zdz9(~iXnv~gz`r%e{y%+PB9U=QHQkjv{k^~lOPfYl;KfV&|*1`+R?zp_R+PVEwUzb z)LuC-Epoa{f5@UH-yTT!`&nkpyme*#g_vUushZ=-;^*s&95fnP<;>SC+8E1h2PJ0+ zF+vq2Jv|q>N>bUS$?sEkRmI@en&ywaZEf`r`m~WuKuEys?5^Hfq++AXXRE>7CfWGm z>Y^`MNHT<+?OaD71bE-(E`DKM@m=s038jJ8T}d<%*l<_Ieu1~F9)gi0GTtV6__gY6;rPB8 z!%_G5gQ`MZkXnF5s_mFm*1K+5JbKEAV~_rhUxd_0aihh1Yc-r+^?mIx7`b8%__4dP zz)Td=vRhL#0l6745(Kk`Z8#^eFF#V%2_6k2>y(ihdX-DDs3g1jJzY(Yt)?no9H*0) zhQ}_j4w-(TM?_{^K`0M*9X zM+{~6c%aiJi)V)vg3fu{I--JtXY-$KH=ZAz)}v8O+v3YA#VO>`-I8>HA%atbo!Vg= zS&)eMcp?(Y^uCAx{G(OrU!W$f#(iIUxO=7n00CTM#3OgXXz!SCBMH(i@L@6))Up(0v3 z?HoM?0iRzN^Uozl_y6$^C1=9E#6H?qC6q%s|S!K<+&kkH+?W}^Q~;Ob6nK}C;3N)_zb(w9^voB z6$Z;z@F-qfl703k-<+vV-KykTvZ9Ok74!m4`bvM+N{v(3#Y(+*CT?M7zcv;!C}?Y- zcwr!0)_I=xFxY3;$jwkRzOTC%-mimGUtV6cN9|zSxbk|30MkDLJSd!KNw!7VLWQ_{-iDt#0Lpw_i9p z6BX&$x7Q*<`Bp8)-MmaB@q!0Y;5(?z`wo`R) zUG~sarkx-R$v3kvWia%CsX4o78HJ#W8uZzTMM|?JSUC?t-jxI*|Sp;Y0?uH*U+-)2}+2svc4ah{G@-3y77Kd2i<+;t&`Q zR&np|go`q>+W6yQ(gR?AhOx4AW`0DXjSQkepi!CJ`JOi2JZ$dVm?bc#M~K zZ=}v={I@*WL!6Kiu6RVy5sc}br70pQ2{~n_si(pkU=Q?M!Zki~%d(vYvj!^tk$&C+ z&+^?D6i;iPzWw4;q-EpeEPXdr4S$&iPNOU^5+nM4DR;2WcD#AmPr@UmnSA(EPYuGoc+pTOjrLq+zbyFe&(j}l90cmOJmhO_a5fPA< z?(U8SER^nA)PhBKN;m7fi2c6bob%2%b7tN-^PTW72bT}`dY=2a?kj#Flqki_&xomM zv90~=ztAV3-`EL4SY!frFG#*192S#{i^TWt39b4|S&Uoa>a+c6GVnq(HORk1+BbLl zO^HN*HlKr8#3SQ;XDRtTtLYh+_lwsZ=F!WN`uaxCtxTG48V@X?D-kPuN^()AUGYg3 zJTys3>l~v^@|w zWP*49KHS{SQkxQXSm}mA-X~WvJbVV?K>Y`YcBLQu(0@E+;8W97l+z7>%J%J3d*b2X zwkpvMO^0F!4kTx;S$6mCLyjcx*-7%qNU$eL@a>IPz`N(vk*jn0^0eN?#;OUUJ%jJZ zPx-ERrkf)iZfJ|OK9GG!ZFJL3hdsEQM}g*P&HS&Pgl5tEK~s6|CgS3Ib$Lfe`urf@ zpIev6$h5oVDiozP)LBw9LJmT~wZgynOwEmT}!DcX=>d~!206&%xC%cd+TSxkcDvPh~b#f5Qh z?e#{mnxpL-RQm$Hmi}n-Ow2N7xkTl3@ydFZWnboC7nSHIJn{z-@k1hL5` zE^h~*M{2i>)^I0xb362<#MQ_*lI;zAHl-a8vwX>Cz!;hYC1}aERsu?1>Wx|Ke#Xb7 zvZRkm3S~)KF0G|&b;80x^mcbwX2^4zrXyj`FCTefr6`sWOZI_&I$;_|QI(pbJ8Y*T zoxK3+|Evclb!gn!xd%*4{>2tNOM!m~`HGS+El+6wF!#deiIxbndZh} zVN-;5z{o2kD!TT!N4XtrZS2pM?M(O1SQBlpjxykM0_QOVomqJK@)bq*HBkaeU#MMQ z*4FJU#711gA?PMJo>lK67D?BK_LzV1Q&P1d%}SRH@5JjvBkfhAZ{FT;VmCuQzY#Lo z&pXkq<6N?WdBE@Q$}XQQ9s2nhT0Z9%8@pZ3?J2DXzhFK`z;!!my*q?joZ{pj`9pyj4=Va2{j8r}Y! z-9Wac)i2pJds33yN(*-qU6!erw*shdegS~r#FZv_J6x@uzk-|lFVa(b=N~3AV!WRj zHE7KZW|S!Wv(TH-pXK5=JUskuYz+MRh;RL+;9Rsk`gn4h+my7NMoX$oSuOu98opWh6_nP3 zw3KT0KIz@dptkBFCwpc?L?_x1#pgh)SpjOSh6V=o9=^|ffB@jI?EtuNxrVE(Y+R1k z)&#-KyERS)aX&;295v<-8`7aD3JJygrD(vuDQEes%c^`z(Vg8W^vs`ALob97+sM zb<|GgZQ)!+DddMKEPG9Z39A&+&9 zj8MfI2QcCjOQnwZkohQA=i8F1kkCIN4>j2h7TEU^Oc1u0R#ZLxerK3Q62 zO*!VEQlEq?jpdq{?0}y6Ty(2qu)dntRWC)+zKSAU4m!aGiO`1ty^Y`$&5o|sS)t%q zSd>jLWPZrX!B)0h_C(Vn`$Jdo-=t}cjg$u+m#+G__HmUTm$akT6KT@A(SJ4rVY8U= z_fk@J3#Dk`5mn3rLGSL|kBf(bfmEyEiKOu>{l82jEbiZA)Z?HRDZdy?(of3M{RCq< zycu7dNNVrte8+OS{m}zQzQydD{ACt$v{d{;m9+il^h!cqvW7dWqr|swE7aHtrQOCA zD|54WeQ^fsJH2(~GsmMxsWx|Yta|K(y2?y)n7Z-DJT6?sc_BaQx|1MxK%@w&)tEGM zeCeLwo{C5Q+Y3l_$|Etkc3WZVtHOm+36x1H&sg^MvZJ88n!oiaD`|n`oNN@k!!3OI z^hYS^0|)b9-cRQgCse!ThKJI^ZRv9p31>eFvL{mfd$kE*6##iBdYfkXK4R`B627k!Md%S56M*yJqA;<&)Trzk*vr1C+B~27I@Yy}0?--{0$$ z3guW?w8$kL<}lilQ;~+EDJjDd1#yTN2@m8-{8jmtt?f1W6hN6|?%Ou?&KF!?s13S9 zSnBfn!4nMweKR>Z_0;oU$GZOYa0o0gl9Lxxymrdgk%jdem|)E4P`DOLW{)a>Cfliv=!Kk)p-$nwyv1ex$oqBj!n;U zG%JJ3r+g#hZFrZSNjvmKYYhGKo^W=0W@Qv1?>HYFrDotSd04jO2kn*Ag6pLHSwV;! z?`oe#$-XIX9;me&eOuGTlE+-NDK0DPe|GIVX@Bw`3`>Bm>7^@pzqYrP76SNgSiN@M zGaD@JL)Ee~E;si{ErLkAT_~s7utGxDOI6h(GYk_Izs9kB)++6WTA|6}!c6uB`vEn} zN~mw0IUnrzbRgwUZpIiBk@M0A_I>f9xyqdp`GxOr>EAic)Kyg%M(^gDA4mgo2mc+( zJQF`KiJ~2nY)hhH@^**Q#H`YJtEZ>i)%6JE8w||N6E4`l)Ycxup>(}Vgk)6TtXEqF z`_lU-zr(734wZMrXIXjNB}!R_=r1Gq(@`JKmkMX68EN=;4LLb;sLZp-J`Hr0d_O;; z*m5-gsYUt5&dSJ$WA(MmsYA5Vk)Wzt2O6xdN1uEM1z#g0)Z9*Y;c*HLHLbI{1Us!v zn68CR7K-1IdW_qC;&nfTTR`9q-T3y#TL1S4cA+Hj=k{&T-(33jmLKFn{^WOle$4Om z_}RtH+Kmpu(M*+s$V@x)^6M1W@7%nJ$<`qCD-YE@x&PIS&n3nGBp_f!exv{*7^c|1 zGRr6HV{nvLT9_Oi#{Z38W3H@Jc+qpo(y`^q+3|S|1%=m}o$i<3XQb4qi%X-7*o~D> zNGaXfiRsu|#KdrV%uG+Ks;bgIiQ+QqjLpyQepithFtOexqMW0$GxSrq{IURcGvVo_ zgN&Yz$2;B0ZE7mb-Q@+F^M+c(6$YddD$Q~s`9Vn!4d^9QR5y~q_<}-ECq6FDUrrg; zQ`67<KP@ew|8`*VWa$JUJTsme>E=x7d?CaN$^RIF4kL zyzI*vFT5J*bsjn9`2QoeoSR|38KD}h)~E$#(I~L%ls*0LSl;C%I?DqNnLoI#h^-wQ zl<1&adGq9-s*x+}(=E+WPpjV|KDQLTncST3IsF}DT8^{ndcTvEdb6AMNk+xu;GBH? z5IIn0n_^>l&1#H^)oARAz3v7G3X6%I{}=Eo1t7cXG%!}GH48gc6{_E#(mKEK~6&G3Zcb7ZX7S{N_J_56V~P%kEj3q^V4kyqpYNq zJW{!g;ra`H1CeIRJ@fJR=*u`J%0Qc zeQE52}n zJpSs{tI&-&`S%JZTTq4OMVb^^V@O6Bsm7jYY%>2&_V?qzb(c!5OB|wcPo6~V45&DQ z*kokRj-Qj?*%)5A)hi5-H5`FBAdc~1(qd~9q(AwuZ`9<{ zoDHmeY&`EURAbD?-drpxQ9FG+W22&k1ScQ45WDj~ZKu9xw~Kg+@e> zlG`Vn8ufa z0)eIR@%h&UhMVi#Ja$GN5e~~ah|SuyPKnR}a&j`q<>6a5TsP}NwZPmK{#1L268`4f z>%r94{5)o>Q6Bi7XyEG3FDhZn_H&ZhRy+o(V{aY3P%X0PDHZD}Eq#e=>n%#*AwFY< z&dt6nh;;H+%0cdpz)Cf9qD>2C81;`uSCP;-*4H-LT~>}!dHJ%6{a}k9;Q)Nlsy(y^ zN>N2sRqU~UboBPLA?2cgFgln4eq$&z&0@RjxL^+Kxu#lxj`BLLnCP3iPVDA=@(DjZ zaW0G@mHIFFf}Utyok)lCt{BEnXkd` z&+qNpW*n>DBkEJ?(`%3k@Nz;Pf)tZC0Xf#41;|@Lxq1^*Y119LuV}H&M3*UfHix|n z0B7`5I_j~_ZfwPkFiO{{U+CU zJB0Fb3ztx3F8aG^;W%V*_e2Gc$H3ZraHD<;RMT#xefXWS z>wdWM=zR~CmQbekwJrI;>sKw#x4#64A-_bJI7w~}5=52TFLpHyCXM)fadJLbLgyuw zraqUul5_=KXpYnu-m{7}Atc!PE7;aLZ?$q`zXN&9)C2Ge^@U!<%6-yFI!S2>iNN3R zNjYG`@+Sx9egmSvKsiRygAr**KiFGTHSF{C4;g83nA(IEKXKO_`DCAgkKZDb%9HD1 zx!7mkyQJ4sZ*O6-QqJ?o>F!JoF28LR`IzDcF_GKoB|n%JD{JLPXGG-XH4)p6uyK`u zRh7b%_n*YggF=GTB}?x+J#$bBku=oTcU>PxbUeleaE|ScM=-axwt=sNKDxT|5fjtQ zBJ+&v+Lj-IuWu4fQavqgxv6c|YG0C?pD^yM;D+noUGkQY!>$Y&K~hpu^S8oCW-HsB z);4;ijre48TScYY*^RT}L)Ep^M)O`rr%CKYB$Gnd`EMEIBbWLkzd|F-c8-B}e7nf- z=Di#Gz<;_!e)A@o>+Ag>Yi8uHLNKO^syy1mot<^vT={NI6wYZH$2jaU_APIcZfjBS zdF4>HTZ79dzx@Sop74B|WW{nHN#xFPZf>9_!OCc!o9WLw+(25Heht;pwxe;;7=;wD z*MX>ABOUcEqLu;p;0uMr;1hBFgh}o5a72L@l@Mr)k4t*{cAsq)bID=F@cV=9->-0F zCtCqDmYT%<_~Ml-SKhwGPmzq7pPPf%cW!!mW3XY>aSmnht8=`3+P-1zh1kg2J_ED( z7vM`7DY00jGO4K{45JS^8Yn9m*BTgdkmd&FSqMN0Uq{dPGGJRfI^bp$8%QJgWeLrs+3q_02ZfdY-}wnYt$$!Bt&&p zYI}Kbd2-Rv*w`MkRl$B>zq|=S*c4splw@}PI;v|t7EW?^+d-lJN(%WC_fKla)T*xl ziAf|yBaDreXR!M{8Ca)mkm*@lvsO_FgTXnSuO}Kkpj2>UO9e9hhxXCZ@=C>q{l0XR zE{~?ylVp&)+Xc*3fkpfqwNw$DnG!NG=TSCFf!M{$ZmHX?E?|_D(?o8ANRb-DZDWM= zOxcl=prxZpO4juC^0l$u_yb=cJUn21#WTj)$w@Sde`AAiw7krP{SPyvSMEiopH}WB zJP^}liWXd5ULy^t^7sKQmA3-s{(C_H7Ulxio&HOy88OgY)o@bV)fB*O* zK85qtRZ1l5}#z{ zm}4%Dy}gZDlxflS5~k#-_6y`iWt?kn3xmX?P8-2vV^qu)n_qZEX(1Ix8S71ESSnb{ zc6qKMX^5oT+j~|kwP+=!B;gE<@tD84^?_L9?NfX$_pLTu;&w}PZu{V%@R_280!ryo~<9|)S1$MKyms(Axe)> zL?p6G_HF+wq}8@$qU6#Lf{%?#DC&*}lf{pUn2ZoSe4g~mi0F{*UzvA80|L(@YR1=K zL~lNg$I@sC!hC33q`p2yCMvhMn1bKoox?m}#OgdAb~46<#_a5+P(lzjodq>Xf$b?a z*6XB1_Q$TSPo7)~^Zi56ZCkR|4GNt3Z(~&T^|V~h7hyh_O60J;P0E#N-e09xSNJ|O zwB#%uGf9xfAp_^ASzSF=)^3QRQ`spgC4O3OBqP1O#Y$gm(tGO7OL zcuHBzeWbbBWZTrz)UfKP-V7JFWmYTFTzhxNm=Bk@)yK=Xd__FDtyD1fX{=-){`|x!4jxIoQ4l2O>v5vdRyH=<8_XRBoY`6h+9S*+VX|OS|DD~wYZX@< z1i9KuWsWNy7j`M#HMJr3Kau?}>W$MoUAoxYML`A*D?(FKuc=f2vQ;sosrGIF+O0$LckR=bseZY2;44Pe;()1tlKXQs zFln|W@^rojyL%ZI8^+>lrnk{c#MGES@J#-4Soi?xb=;+cpXfp<$>+Iwc|P9W{<>gF z@dzqHE4`T+NaZenk){JjZOEK8vZ2foC0OR-?UAH!k-;RuBHa_n1C5RG`iNX}+45g?2^`g$wLFdFL@7+An7-+ir2v7jCN z4q%sItH%0)D%p%lBNCPPw7yJ;Tp2>Nv|*@t=;>*uZJMVgJqPJz6#JqrT6>=3g zUiR_P85z7?iN?0S`7Ss)Ict`_mW_IG8m&?bE{fXdz6&k!3%AfWG&EO5mKP?L%H0tX zN~pT@iJJ#;o0QbWX|Gwxq8uGLOeQa1;C13S(Br5WiXI&FSl{a(b>DI_5&uGBRk6N5 zB#v*l+y_X6UO-@24kr$_E{L$67Dfi0o{pTEY6x=nTe4k>6#@ZE7t=|8H7e6Oi@2dX;#l z?MRG*>7u9HTKW}2@@$1fw&RO$-S)Rb=U1!tCd5rl!jjEU16Rj&QUqc);>y!^M)w+; zST}Al73Joh-L5A-p!Q$W+s6NRN#EGm%illG$7i|Jt0$cvcB74eK(-Sj-&1P!in*nw z_)1U`G&k2vP#cRLPnb%sgz|6($RePg0p*TQ1bgsMZ>TErcwj9MaJWJ_BW}HUoM46X zvtO$nCIG@>fAZPuc4RNNsTG#*RN*{i%+icFdRJ7m)v7|+gh`2U)$g7%jw-hc4CoNt z*hS5=sTEmdoZhxKs_6+AMGt&6q$vnhigd+NviX5kjWMDou>aO|>A)e_xEbxZl#Z&O z&kZamekX;nse?YKi{p<9El#Dcq|O?qN=IqZuGkG%9Gr$%ZED~YAYR^8Dpru6&nldd zmscdOT%~hF;^^1`N~lm}Jui<#1i)j^5SJF0lA?cjpWAge)<5pv{FT$?l$;n$Kz)+X zAf;IV@9(I4S9|6RYhFUVba8IQ9*sg_U-mHr7juc;ERxMXG4UClq2V&-4x3?<)XqIE zw_~r+k`CZ-Sr)eI#oa?VE?e1dJmTO0aKUPA({!aY0r~Li$~gVi=e_G1_G)yY>je`38OnQi zZQ_z>Xpn6&aMa8gM!SdweBQOS&fZ$2qosZpNkGb_W7E41N-FS*S*%b%3+5vJZZkaR8-!#75iy}DA!UZUsqh3V4*E<3>Rk8Q{P>E^RjSPLGBOscp9+E7g_*7{c+Rgi ztZvYlIPJWb2|`4JQwr~OQu3^`_mj<0quP2~@`InBR-20}N*q$*599$#!-a{pcc8p1 zr>jc^8N1BPtYE}>E)=V@QvT+WGpIbI*sQnD_$f`1 zp*rJbc&ulk6DJKsotde%Z{n9%Jm#)~+QoTsz+quqJ+3;Mm78nTQePzW$wwg9vOj6+ znLLqv6IEf}#}Lnyl9Un|xu10(you5FOAl>*DbP7;u}`&y;2aA3?B^{8HU2=-L9epe zx?ewRunD=GDqfA3FRf@e{!RG9hRCcE)vHkHQZ8JM9f}@IT|q!V4L2|}B#rofm^U^{ z{0ga>*d!fTjZVMNvP^qinU-S`@0KK{=|ux$*AVZW?2RD)3>ez3fEzDy;4+hQGuhC z9V;hGw&U_&HyC|HCZ{KdibO57kQeCh64|h^mIo8-85{KGNjPrxm5SM(-zPT3!N{A93ESq!6b=ZRfz-SzU6snnRJL(&4uY(lP+(u-m%k8Si8;W_2=Gj%CTDX_U+g!CdUFD9*Q4K5 zOBP>#12#u(@>T{I#T0sf@}n5d{d=sjt;yPU=bN2SGGJ3N{1spl&6%x6FXri)5~|A0 z$B-e1#9PKx$JU!vUnjQZ9G|%CRPI2|OaJAmcZm5zMn*lLUGh3*x}`9kg_Kg$+Y01& zC$+$(MEljN>UDOvc6a_CRLQ};{qLyW z{w-$fn=vK}wOt)mzic9kmpMq<)|0#mmPT!-n}heaF#Fr2+(wQ3Xk9Sh%}_qNbBDr0 zPp>E4eu!m~4n4$|>ir4{trWjiAwPjQMs;-scJuXt4&?qw8%AiS3>pzf``$dg72oTn z!lk~mD@G9r*t~9USk)*Qn3NG1$`WNhyZ?uQ6u(3Ayu#;kJUnn!o}mq3BXNE?9-h(~ z!Pxd@!O*5G(GCGdwGuC0(lTh#4i2hJ(n^L1gC^OL`7sE#?>PS&Zvg*wSopz2flSqQ zp9>KC7xWxg2GC`uJHQ4~#Qw#}SqvTA?h$_Xhjty=}_{$^Ed?Pll2mZ?CPy=0*vM1vH1$2{Hs#+`Mn>O5Lk*1$G>R&Ge9SGReIf2|X9F({Q9yhi6HxI^#4^5A++5dsJ^|qwCL-2_OT#cCAejA#^pjVGP8WHX5Yn8wYYaA( zWrE`SV@}Jgd8(i_walg63F9thchLk{3$JI?L=6Nn8*46r^cF7}_?~K6wJ&+H-O|sp zIdc0Q);w@RBuW`U7#}T+&Kz;OcYjSMmMe3mcN(0y4D}ZWQgR8R3^mStZ^~#_jpy(% z{{o^6deL~~oa@1%(Gl$S<=)<=sKG{aHO?ZVL3HxZ%{x2TVr>fx3+Qx6QD0LjSjefZ z{m?(^j4fw~v0ly8gVm_@LO>~NdIl+PxFSQj{VI*lmtlP9phiXxq)Bi!pzY3pD`P7< zv1Pq0SFqtVINQR9V;s<1>!dM`7#iN+aZ1Q z9C6(ExyQ0=o~ACd138qJNTw!mx0!NS~jQ!|!;VZ=TNJFT4r={}XmVEu8K0|7y(q^`@KEJprb} z%`}EIdgbiIMh!YTVmiZ60CzY$@7;Iwfo>_#I?qC@f^5Hw)nCLn%`t@*O_UowJ#rwZ zv5!yofXh*a`6}Nn=0p2~5Pax@HJh`}PVOi@RLiV%nRq=11!iWKQIJ|dL?D~}#9Uz^ zLnsr>N(lwYp`Te`Kl@DN1NIk2S+U;=TN%i#$(ZEbBJL13e- zY!8eshl&?}ZTO9nM0_`54~Lhyp`kAO&~`8rhtlUO_tA_gbA}(f#4t1y3!}C}*LF|k z*(D4kw%J+WaLQY}!+x&1il{;8Jqf33o|(0nn6VuytRcoZTWu%$-!B7(0;L0UL=FGY zN|z}|SST2P=%LYEqhRRqNUkiArItt1t1I|b4LvO`{q5#3gbR?Lnwc^phSBoFdp~g~ zJyvEZ9~~sk0{VP=noCe=<2QvK!**4~KC{ygXdD4zq^XMv#~>Mq8=YXzo8ZcL=N(MQTj6hH%%#=#zu&bH@XOtAm)rLf6botd6gc4KL}tiDiog_2)k zpv=CU^)NDBmg9|id{Ak78T^qWcX z=IQ!2Ra9bPj#l*`+x2F~tAgYXdvZ280!Qi0+g&MIz$0v~#qAJ~HTDlnQvoCci2JH2 zDCk?RcBWbf`X|({SYzhL#$NAszU3O->qIy6($hcf#;33F7#)eOJos>hQjzkY@zM@} zL7HlVt|k;RxbD2?!T;?&YQ8%^Uxko=|Afq}vPUT>=ByBLr637&*OAtAT#^v-oS!P6<~Nls4Qa6d|ektAnvaWf+eE9dss zhR1kV1E9t9b#?P~Fbmz?a+fZ>myRmu zTGw~gBbs^qV?tmLyaNnbO2>!Y0o^b+IEHN=gcuR1Y6~w3QYO82n_AX8tgP&;aL!IE z;OEAhdIi8s&^(=5qsNeNulGUbiMvi7}0QW@q|QHoOKWYXWS z6j@*#_VR@R-d-B+S+O}B$fc^Fu721S8Uw=Q0A`R)yDg9)*7FMT+8ZL=JUrwBC61#b z8d|O!3H$G~+$PDxc5V0MD78j?qskjM^E$Qp_z<6eJ+e2i5%o3hDLz^V_Fz)kj5rjQ zPsR*Y38gHnah;WbWA$Kmp#v?5xhcCnxd4P`%F1Co#+k#sPtK)8lV3S$0-Xn=%6i4_ zZzSRD_Vp4S{2V&ng(l4kwqKfy8A-vh2(-c>7(aA`?S)mZszsxd0dKKFL2*N|)%lm~ zRzUU*t47JNfpVvW2!!Y)ZJYl7An~!hoZR0;zzaLxYp`5iCfDbUefmCB%WbixL(l?~ zjQN^2$#GLd5S7v+!pMx=l}ze-a(c{^n)bqBtiFg4tClrRj*6-re$ZAC6gj6ls!=vg zXOo59auifkQFKfT`kq=as1n91yVab(%;x~tJX*}D*qz~2wj<#bN&z zoXYcA+~p&>M~4nZy^1Ok)U;>utWy@^S@AL-qefw@h9{G+bbIe#h=A}LcdVU%PPqh= zfEHT>^j-l}5M|qQk=uQL{L_19UOohv>%s)30w6$B7Gh#eLzb<@#l^h($d?tOA>f%vn7_Ecsp3EL_>7c%+nu7xlk5XJl**MmS8s}JS+#(vmy|l za(aTUJHaa$&;tty(3OzLX#I*uMU{{~JN56uBR86T41;iqD=SDd(hercD7P4 z3vllEB@39>^(LYQCQWX^&FJgve@AdIBbi!fZ3XPolx6ryr{PQte)FnkzbK65j?2!{ zQZFI~lyV&$isRnC)u+j)@Y35}9jY|gsCS|6z0If7`Ax?Xd7NKMMkV57+!Y`EHZJZg z)zipn6FL_CbkR@T~s;Ia*3#c-mV_A>=C z0^hERilz|UyyT%YDXCve9OJHUYT939>S(V(pqNBKc0(?;=3{#WHZBJlIXOA0EcJU& zsx!^6PD_ILy5h z@y>4R+P%y^H(Zo*b^=&QMGP%iwpd%yX zF9)3xeH?^?nYXu3S5#nNV5QkS<$2(e*@w!8W0Kqc@6B7R1PlxrpsgLtXSha*>x1WW zjuMGES(G9V4flbBKG zrbL>~7F$o!feVrtV_=Ab!QjLwNCCbw0b>mrq<=y~Q|s=oEoIfg42aG_ilW}pyLIFz z(34#Nl)A0cSIvFt*G7M~_ZQg`2C(_6f1Hdf@w< zvZlNH%Aw#x#+HuSy){IRx5O7pk9CzWCVKiX7W+f4QLGIlrKza}faW~dSpf~q`5wz^ zFep6#jzS&3`9jwyMMJC-+T*h93uGW_lgf6TgOEFDor0_nvOo3Rp`2wtJ0L(|r&Ct1 z5Z;-AVP{!!dk%6JRo~~Z->MSemie-H08>*S)X>fl-Ng>U?_pt%iz7W< zKh|J^TIswK%-Z1h5rRZZIy9>NJU;}`(J&~9{oLDNZgb~n-%WLxFJP`P zGdDNa*6u8mD1mu~G?`%X6*ADxh7ouf%2v_)&V9LB?T(cwQ9rW%-JTptNl7T53CVeX ztut7jWOIMA|A7~N)LmdQZf~j#)HqidN23roTwz8n_nU7v#zoxy-bK;FGH#WKXQY5b zX|53x@&oHE6-G0YSDe<}?2s{u>8qAlxX`kK?dxeQ(tuz!H9ey#R;g~BoDk?8TvQ|{ z5x%ZTLPDiCN#A4WzO_f^;IJK_l4|(b$Ur|4yNgiFKu zLD7X#ZC^B|>XJUcc%9gNkx!pY%I_UsA}Q^2luw}vHX4mf*v9);x8Hly!7Eb^NbZaTU=ZW zLgE%P_{ulH<2l%(oSR-wpzpa&a;!J3L`Ai%(Iyx(`s64EX!=<`%aHb!`NVJ+6&6k* zEUV`FJ-+#5Estslu6SzlGvac`a9zPW}5Fyn9U|1x^aUEgm2Unx}dgahMRpr5OwyRybE3s zWp81i`q+Z1l$Q3nruFzD^Q=)%;5bzS43FB6<_`c|QEV}i>)ab7pWLQE47NJUFO94Y zw#j>0xPgg!gTz(g_!Q-K;Yr(^D53AN8Yi>~6vGGa=5|3b6Eq|x=Lh5JeGHctq-B6K zbX@FWqoS(Z_vGf`^CJ~B25=9r!^(c^1~a3^VFR#yz&w+~tk!7gOCArZw`zL^5#zpo z(jbHWDTUJ#{qhTP0FVZJwVQj*(FKQv+OuchOZ?8KZ6@_k61wi&_xg>x_7#b)0vbnI zUZnC%vyh)49NwS*594P0avV>uG-FK*i!Fo1H`{+woxT!dD+(!>2!ORUmVmRdZgE=& z?)JwR0r)~^gsKZGy7s(dbA1JVK3N(+>o#cWg!>t_&2-V_<_$pfdT@3TCZ&`Hpn!O@ zoE`o}@s$4#gi^2k?~3DtmH&56fBqLxMomECv23j|iH2GY`g|Sk^D0~SAch7gOdnWQxo-J5+9?C=5x|BA`)aGRGc)Il z3xUPShGj*$#UUX}swWDO6-LQPjuuh;s%pBj4FdaqpL&Omoe&L8Et9l;GkB{X(q_l1cx`wbNJ6B#G$? znZq_gSs9OnJGQsG8J(o*rrf{ZzNMbjZfRx)95~Z58d`Nce1XE59D`dSO29}o9xRW7 zagJ2q%{9uug(OVo#m@W*9NNeh?yyo;erx2aucu@g1=m3MR_@AXD>VpOl~u!uu4oy68_)` ztRg}R-qzM(^_hVozSpmv#q4g}Ms>DfCcBgLi>8wC7db6enTfRA4+0Z8%Ng?E(3?6t zS1eS>`GxB+HS!hf%ym4|RSFHvU>x>l?*(0@jrv3b8$hN>YQF3KUKu zhb$iZZiHohjybQ21_uY>wMo<{%eOin_L5IV?zMZ8_{{W%#>6}_Ht`P03RVM=a?vuW zDDHi9M(-gY0dWYI0_P2*U7w}RamF)+^DN#g!+5OfhaKDQ1pp2!ZR61ex z_!!-D5T1n?w2i6!1dmV!%qFwX@1HaV#Os_lmoUJ8?-1Ct+OOPkaXJ8_B$%7Y{|ER> zQ+WEH!!n?3fo_+aJX4Z-Rc3Vj#KOXi>L_Eb4v`@Cf*RjJMa3}mVSPO*+M3!;t_>JX zpu)B=kJGhQD|h&{jg7Ye44RphW!Ucgd_~}BiV9d3i>RcChiki*-UTKq)!XB|JHILeD#W9X>}4W_fw(mv#PydO`f=lUZZW1!10 z-<{?8K4B74^|TtD$J@1RiY*G|WJ~tb)6>nS1-ZHGEFAVW4k0`cQ|WveDV-B!MvsFf zOxbZg4z{XP2pbw=0!10s)*FB~K;gdGw2s>s67)e3Bl3^Qpc^-yg)NMUOSY!gOXBM@7;F=djq=x!~EIJOFY0uTF{exbT+u$_s++65FjLoa=V6Oi0!@B7U2RaF+ z`ry&BVE0Ny1m=ZM4FN^uraP}>_cb38Hlts;5Fw%Y;jYD`}bUQ9t7A96LKvM)6ac{sOul46c%Y(k>wE9F&<<3D=g&*O z2L&0$-Ukrvix+wn6ch{3{^vd$1sQZYRtC!BWCLf|UhxC%PNSo!DkW3~D8SOv4h`+@ zM^^|iB`yu%!1XJN9Y9KP2$DFaH;rmN_If&JZmlB70N#N3y876e2cEAh+JBNR{-YV5U~J*e4eEHruAF|RmV~&S|lTRd*DQT zJtrGhTvX&Tw`K^&;U(spmL?|KyKKz(l>F|P^qXp2SrP2ZRT0^p&FK9E8Km=SR=@kc zecW&`b-WaU000uJRa>gsFNC1KTgae1%+HTyJHRhGtlu(KJ}Nqd60tBZPj;3Js7L+2 zSv!KiK08Fg@HCI53mW(VFQ}GKS8Z**L8Gbz$v%Wap=x@Dnw!PXA$>1lCbn*4tBftP z>+Bp4uxQjUBv!`7m4K;kn1hWCoZH}|qxJXIvY|}jj9`Wkc%YXqT}mou4+jVac5SFa zl)?p#snHu@79>&1EXd3>gGpA2Ic5`@CysLiDvSu@nsOLtRPL@XYK{n?Z>oz)X~3k> zMh^S|;+y$vMDE-n?J_uR9K7+6Kbg?IBUE{*N^+aTeQO@4O|5tZW7#>`QIW_LU^JPR zqJ;`6%q@ICPtU}}#LB^u(G*^g&w9c&l`b(J(mdY!wJ8on%8R0|hh-Np;j~0Tsp$tz z0HPST^`}XmNNoF}xUk_jXgN21_&|OFfcDUQUH(+N6UL>Lug6^hdgE}E4s*GV?>rLc zx9EiU%2e9`%@JPviI+M$Pj}}7Z&(ed>Pw>Tz_gqsne-==6a2!{1w51{LF+6^N0&$< z`AG*sZwBV3U}9w;4N1w2>vr3uU%N95)qo4jI{VQu<~zGVImzee1nV#eFJk!W?afb2 z46LHQl+_D*dU{O)Q}oc$zKP29e+Wga<@WaW`D>+z7tEWMn8?jCuKPs0=HAZNAS1ou z+Ig-AcZl3xVMn4cF58MG9ZzK04W=)OLhWB86Z6H#2fQ2Y9UNpsVYb|7#lP5}X=me* z4KutooK>KBf?7sTlIadAs0HR`W&^2WKkK}qgLW2EtI6d>U913-)NhzIhp*GKQa@h} zJ6w?=#BOe)Y*3ec!9R{Rei>vT}jL4pok3l}IbY>BF$uIz9 zb=&KvF@O8vgZI6AqR=q`uXb#;kRKq`LBt5Sn$uf@%#x9;M^j#t7T^%j&*XgerB^<{ zfbskH?||i}rlD;_;?5pX`~N5mBTN(eViFu2d{pEr>o;-)9@}ym!KN~@dlLnI9O0~6 z^Uhlf2)CgbPfE9Vx$&$BF5suMe1NIpn%+Gd`B|LH9CCD^tRkpAc1`8|?_@AHglSA< zl!mLTD=C+GvF_jmv@gMVB>VIfc*B>r0>sWGIho%Gu5v)JIqh()o>`LPVW*Jz@K2-8 z&VkayVAA_n=8e+;sW5ZvR*|X`&|O$qx`3n&rsJ@&?U(zgczDEa1Okg7)H%DHhSL4e z*dEUps!H+f#AIG*V36>Q)YjTTrL%Cz=06U=eS?W(915oie-!{XK}DHxmWQk)^73Bs zH0sHogvjc}a|NHAMYr{L?UvcK1Y1puX8Rq_FXg<)W zw5+qJCcTCD((m_y>SKDOlTcr@isAr{id3I5I83BiVo&&|J9QrJd&@G6YQ+|P@zWp` z3|Y?Bp?3iw{ZRm^R6gENA4nrIaS~(tTiR1hRo0lFm5zG%b3%fA(jM^DGFNAq;+irF zw73Cw@Lop1W6qzhYHy(cn0Fuw{x6$V)=@_Gx{#-z3c?0PaK{7X<_8p&`%UHtA?sD7 z0Y<5p*~9Z;cum1yh?Ebd;82Oni9rq@S6OP`&49t)>(vCAUhkA)m(lmEcdD~ z${)engRbxE=|MPRpX%daUCvp0MRF-JvR#0mng1WnU3oZ^d*5$&PKi@02RTl4q!h|p zG^Ub$t8AUokjTF8+nCCcL`_A6kR?pARfxejNunCtAdKCJVT?61%$Vn!ah~UWpXa@v z>wT~5`Q!c1AIyEv?f1KX-|y%9S;)@_s%UfbgCdccv7kca`T-{T2EW*pO>Qv207{b&X62XDR>G*cjaUI0?qsas^Z3vyyfy$!&Wuz6wUdBCwI8v#98fYlWQ}1(K+50Rc_BVO zx$Dtzy0(yPDx(?i;_6F&rmwq53MMx<=RW-vsYHDknG;~xF+DFN2<4oa$v`*`xi}|y zeq|f5E{N>;s$@bvv{U{x3YZ{RKHDIVguv1au=^4db8_&fcVu#F_VF&2i@vH!qZ zFmxN898%>bFsM^wULY}}L$$~)pA=M3cvXN1?({@3!j9#D9vfbcVjYEv$LH4fYVO1y z;+FWim}loa!>lYU`e0X9U?LniQHA^TS?0CAN`Wdzk@coV4JS5m$FM}va$3(gT$T1-{AtpPc)BrkrMxrL(O4GMXu&lYA7&fKD* zWv_MFHAc1m%NHh$yv>|F{tLEruP}`ZdbI>OOZHp+Le&!|)M0wkgyb-v;QFxR)K0)r zfLgIfmg(2iVQ74c1IpNx_DB$gyDxVb%2l%~K&{Bz817Lwv@$QupM$wSMS|$>>zkxJ zmk#8xlvBT;%+0&2Z4=xP%N?PT#>kmwQ2joL+jjtwZ`{AWYH24%Po&15u+>F;`pT#&^bj@P%>>c&0qb*7feTT^OGr7r8}3^n~5Hd~>tQmqS4-4Uh~huzKSs-t53J`2V={ zy)?@)Lg&RRDkv0&xdB`Rti7hj#>-{)k78rzY>m}dS>xBbODCQhUAlylUVXa___yJ% z4?{{cGC_7;@CQ-VtMg0kSf@rsh_DBda%y5?%S%7CXYo)h!}5z15DQu&Hvpv<+)O|z zpmg%^g`pTdNy*atJ@1NZk9_v%%V-BbiF_md^T2#OAOuEiX4bdv|Y!@;H zCop@5YmMh@&iuJr8&NYOFGHN;8X|NKjBQ|`XMU9Rxe4hxcKf)Cg)A=mZqhNoY;fP0 z61PS<|#M^*FGx;_drKp?i;jHu&R!9P7n55{o0)Xo{ z=~NMxL84oh1V~`mPS39L3kW9!YiUsvFc?<5ie;&DCb|}m&)oS9Frc|&Z`F7FT}#6_ zv-qcS{9O1=Q6BvO@a^E5edF(6YR*aa{;cf$B*{G{Ewm6sFh@mILu0)2&W7Vi$;+AP z4;~~W7!MybM7?&E35qQCq6Q34b}4|+p^^S=de}J6*3)!<;h>)L^VnCfc)P{9_B_7! zxvt8JK4xkArRnFsZp|6c!Gjwt!f!V4nLHePQuiWWJGP_B^Z7@JVjmhB!e}!} zgL_cx)@|?AJ$J^Gj|W?w*Y_*q)1FVfU6N-(iSix(DnSI5Y_6iuLgQ|40*nCtNfjk| zF@^>xv|87Q7Qk`f_ONa1Hu9XwJ3dA=-m(T7GKqlv7f@>i1qX_9Ue)S2>FVlC?*MF! z#}go5grURMt+OziCAYOLm*$9Nco1&@yQ&jC-`i>Jn4=Mn;}=n-d|)2*xZr9qugxI3 zIXlw;swLsW`0s&B$`d3bfd%tsKQj_ZHuHT9uShv9v)VdOoqUa5?CVtn=4Ak5iVov- zh)PY0{7RSDmKhA{MUa5f9G@yl1!*HJEL>c~7TjU@32YVMobM*zsxR3jt*nY#T~Gt| zKqM=+pel-s&rX#BL4{`a6%h#l#eP^Q02T@qrO?wT6SOtqJd2uCmZt zKtHxdj1J-c-HBt_;oSyJ9LG&WOL* z@OX61%uKxmmPJi~cDO%u&M?p1t*upllI~_;5cAB)#m+8kpuhqJ5k3}PUb?}-rLSIL zbUw65r3UU-Qu1RnXI{=gPeu0p$0usmc+HM9?9;fV!!pY=S8^u*h9nkQA+$HYxuKuX z3M~WV$)y~#xe1K0#i?Fv%tCv%VRFYv@!xN$&dhYQDNSztecWLaQ5AQsiW|w^etw@m z9&zlg$ltX0C3V-XT{?=Z9-Het`N;bRZr{1{5DWQyN%8S#DsCj*r~js;WIH{uPyTx_ zCTFofO&zngg#Gh0Yj%i;9Of#+_^hu_tK7xT{@US^GP|x>e{sTI)zGYi5t+7EpH&(F z6E*EbkRGdlXy8iQ8!wjA;RIQ9tD_>3^DTLG8P(A3FVAHFn{YQ*MgBOxE_1#cxDHlf z)DmE)jHjk+jDF_Qdl=YU(<_sYTev|pL4LstNq$aFP6~_^E4+b``xFCv)VVuLchMb9 zoq}H1irwVeyFm~+Un7WId6M#|%BOQ`o3I?XPBeCWbN=JI_vDLy&)NyxQI@Su_?Gp0 zy$3C$qoNWGJ|rLDYAIx|3eg7ya{WxfoXFJ~)#hSD-V6aCl67_6g!uS)C9Er$0&dvA z1}+=g$H&KyL_Qn~{`=(d%|sBE26Ap~cWBZ=OAUNzU|pL!6?K+;y|$^dOn3Ob3`()A zw6weO+647u0z*Wex!5nvVb00rqe_`G%O2oa*i+)+aM{Z&DHTycC~q%ZdV5v_7x9Z) z!Pv$wlwER5pX9*mKBoLwB5=*4h}6ds?o$S4!3gt zZC)X<>*<+|KN|Ye`{w=(Va$*B>TeAR3YPu7;ss4_XY9k>h<%pPt_s}*JRZ-hHp_ar z%!Y$t6`aaxjV2ixb)WQ6=Tur@I&a;$p%fJ8sgs-0a%$y6tzokAkAk@SC)=`(N{dU) z6Vy4BZF=-_0)aEnDQk1+<8lO%iH{$D3S00i$48s9Sqa6(GoX*^OUJ1B8F7{h1W6S{ znqEgi|M|~&UZGBLTdCi8Pd~v#QMH%vkK!n=GiUzP_EE=-v%b17O>0YFFVZnZBe)3K z6Je|1@}$|5<>ho$|CT-syCLVu?n8$Uv#6axaOcHE$FM714s14i>+D>+=AQU?ZQL)M zYg3hA4B$L+d+-%^Zb&;VRQiFolCg?cqatgu`9{_IrM8^a<#JtZ&|O587Gq?#JK@^O(@TXwp}7A21k zgFV~D<+ET5I~Pw+--bB&?Y}5C*Vdv_+Ft7lu{xnPo*v9pE>*@`6jUydLOL<6vMFl1 zn{0t`RHE+?g&b*aRa-ms)~O*|Js|hWhP1@#<;2V$LzJ1$l zThHNe3YQP*>b@36fDx!Her7BShnOt_)(<6U4vn7RloqiwWQ2uwYQ_AL{OD2d3#V8W zs}Qp_gb~vD(y&0A-%*QYUzvkuckdc>X0J<`@R z0s*f`*XCbO8&U?)j1@u{A}bkKCTIY%ZESLSN?gSD?!D@6|Lxr{ z4!yYbC2uE3!15L743^!(jo}C2>5jghdf;M8(kV8joLN3M`thBm2k|R|;Q*8EXFfSA zoF#w9{sNZ-l9g3zVbx0Z-Tb?yp%6-wB`EJT_;xc(MOqpqzG4!Rf_n6cUp;i7N>IJK z49jH@KL>dGe)`mybj+*qjhF3IT2Nu>7kJTDuZ$9yb6+>IIatAs`(zb?`L>d{s-0)OfOz%NXpdV@z+R-6H~l#hl<9F5&$G^z?%@gCud6ix zi-=s53r!bWOfim+=z&}R!t&-K)~1C4A+p&$>J9|rGu}Xo!#o~Jqy&mf|S_%_dVOR z@nXrh|1m~GZQ7fUD$&!`J?Z`1yZ7(kcXa5LZv#G~e)iJ=BIFarrlj;1+p0-PDt3BP5lM5EFzMTbV2hzDsn-nYbHwd)eWROWNTiSvlAvyjI24Wys7vW9LJeZBVM$B77GtNT$ zUbyaq^V_o2b!KjkFdqCmJf_3$+^W&_I%jcDs2mZC6rYP;`Sgum!t#oWlV6H$b#!jK zDtj+}R_PBNxWVTJ&tyErm;#td9H4I=emIo)vHi|g)WWfK&`sbA$uSj0Ah(S=HS=V z5E6w_c}s8~@31G|`4j7tt*fK+0UkzHl)ALEUN>#`>B#VKjVlp93LSjy)>CSZTdNGn zch+k-Jr&r$_NRI>vO?QduXtYNKW9|Hk${ina+?Yo%HfAC-y6M+>yC*W|B;4#eenC$ NbTy666`Z|t^KWS!);$0K literal 0 HcmV?d00001 diff --git a/docs/screenshots/pay.png b/docs/screenshots/pay.png new file mode 100644 index 0000000000000000000000000000000000000000..4dc699ee994ad458a4d7e138e199edeecd833ade GIT binary patch literal 153685 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

Y-g}JP&?GusmXx@L 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�jI;#;@8f@n-$dU`MliW4bnF89}85iJ=1HG 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 a0R#gg>swLH5%3Q~AmpW0q;l>&eEC0y-??A_ literal 0 HcmV?d00001 diff --git a/docs/screenshots/servers.png b/docs/screenshots/servers.png new file mode 100644 index 0000000000000000000000000000000000000000..24000552a6bbf5bc8d95f853375d1f1214e72532 GIT binary patch literal 63762 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 I>0G}1Z>6}>&;S4c literal 0 HcmV?d00001 diff --git a/docs/screenshots/tools-json.png b/docs/screenshots/tools-json.png new file mode 100644 index 0000000000000000000000000000000000000000..3453a4806ef0d0ec2b1aba34464797215916b5b7 GIT binary patch literal 63510 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 BM^*p; literal 0 HcmV?d00001 diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..a1be704 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + 池易工作箱 + + +

+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..ea086a9 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2439 @@ +{ + "name": "cyi-box-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cyi-box-frontend", + "version": "0.0.0", + "dependencies": { + "@codemirror/lang-sql": "^6.10.0", + "@vicons/ionicons5": "^0.13.0", + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", + "codemirror": "^6.0.2", + "echarts": "^5.6.0", + "markdown-it": "^14.1.0", + "naive-ui": "^2.45.0", + "pinia": "^4.0.3", + "qrcode": "^1.5.4", + "vue": "^3.5.40", + "vue-router": "^5.2.0" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.2", + "@types/qrcode": "^1.5.6", + "@vitejs/plugin-vue": "^6.0.8", + "typescript": "^5.9.3", + "vite": "^8.2.0", + "vue-tsc": "^3.3.10" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.1.tgz", + "integrity": "sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.7.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", + "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", + "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.2.tgz", + "integrity": "sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.6.tgz", + "integrity": "sha512-kAz+AncRtKuIknedxT1bq4XwXv4UowhbkHU1myPrtVb/jZtImWuV5BXzv5vK6i3kYACsdiZiQKFQQ5Mq7elW8w==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.13", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.13.tgz", + "integrity": "sha512-sihaFrUzAsYBQsL9J2t69y8nfMQGwcYmggAZsk+kjPbjYZMyuf2hU8tUNTZ+P+isb6XRr8JE22TZlJxBoVdH1A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.7.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@css-render/plugin-bem": { + "version": "0.15.14", + "resolved": "https://registry.npmjs.org/@css-render/plugin-bem/-/plugin-bem-0.15.14.tgz", + "integrity": "sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==", + "license": "MIT", + "peerDependencies": { + "css-render": "~0.15.14" + } + }, + "node_modules/@css-render/vue3-ssr": { + "version": "0.15.14", + "resolved": "https://registry.npmjs.org/@css-render/vue3-ssr/-/vue3-ssr-0.15.14.tgz", + "integrity": "sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.11" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@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.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "license": "Apache-2.0" + }, + "node_modules/@lezer/common": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.3.tgz", + "integrity": "sha512-H0iErY4e43LpXbYDyBci5W4v/RwTgGV3YzYOlJPiGZ8RY8w51kVE+xLn1tg3Z6UmSfyETvtsUK3r7YsgDQEI7Q==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.5.tgz", + "integrity": "sha512-O1GMVKgtf5YspFaRzpmqkVgMtIk2HG9uMAbH6wdAIdNh3TVzCsch/+cRi+6l2//UGDKe6NB9BW3cvuR5ezMQ0w==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.4.tgz", + "integrity": "sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.151.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.151.0.tgz", + "integrity": "sha512-J1yXrIlNDZVzE3ada310xeAw7nH8yCAyLPuUIsjKatFPmfn5bS1oW+cM+QsGOtVWd5nhSpbwZWx/rue+r5Z+PA==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.11.tgz", + "integrity": "sha512-A5kXfGKvKWWZE0TtPrfsvT+q4Y5d1QG8gGUzpYjGydM+fARM9MuX90PrXYXe0XbsDVgyxxNzHo6giCj90bsFNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.11.tgz", + "integrity": "sha512-z6cTycz+iJ4PVkuL4HHW4DfTfoeU/2nqYYuSOrTmH7yHK5Y0LCOnA03V4ZNxavyVaU1oOqUgIg2klN/s+USGOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.11.tgz", + "integrity": "sha512-jShvqNtP6vDC6/A5JOAzbVV+DkgHqhl/ScVCJEbt+TUY6QYz7YnXcrg3sLtFBniro0f/Ld50ZwCWA6f7KYD1nQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.11.tgz", + "integrity": "sha512-f2i2xiNWq1Z1l2++q2fuhZRdLAT3aqxD6vRNm1RAxpUoBcdqNB3C0s1Bt+K+PbEx2F5F4gQp6hqKkphCY/xF9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.11.tgz", + "integrity": "sha512-4Ir5FSOKIAMr4r0kExpt1s3bMgzJU3rA45AYOHtQpls0oNeqcYBKrWMlckrYH4KCfGLfkfn1tN1dmZPMVsdXow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.11.tgz", + "integrity": "sha512-/gnRDM+39BROzAN/k1OZjDPnDMcZxB/0EUxKjONO5yVkNEvlsoMDrxGNKgZi/ttFriS2gwlDNzB65pvNbFOXIQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.11.tgz", + "integrity": "sha512-PFaK8HwvAHbaKbBcDNQihjMKYvFnA5hiENx/l5tphTDz1E0WFp32l0A7aq7lyUwGsRw/xSrNIy/gIK4thrSCrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.11.tgz", + "integrity": "sha512-AskzJUIKRLPxkruR1wLKewGbOw+EYfU/9lOrBFj4AFrEA8hPpKFnODWNu2WLaNs0QNkEb9QIJufmVZZIL/bJlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.11.tgz", + "integrity": "sha512-qlUGAheh2yh8afH7QBgx0PrRHN85hKnNd78x8MeMhXivuevgd8vgf6/CstOzmNKY/lLTHvNTrPy98cLnAugzJw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.11.tgz", + "integrity": "sha512-secpEad+0vCbSfn8upFySkDskv+bGPk3THSDS9Y89yc4rb4kzqHp8Dmyd9BkQW4SnhNXBZCl/6CrO//hZahNJQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.11.tgz", + "integrity": "sha512-mOVBT3dPpkWm8XBWPmU4bf+U6dYDLeMo/9ojUmis4N0L5uu10qra5vOyngZ7/PSdoE4G9KvRt4bloRxNjLas7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.11.tgz", + "integrity": "sha512-Is78i9A8Ui4SqcxUwFJ9uMmjDn58IbVTjFWYdQestFEgeuEmHMLGNriXnVJKkwG2YiZjw8cP0zCTyDMdDGtOOg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.11.tgz", + "integrity": "sha512-dUCXneZ87INUMyQ0D+C0HrEBNUPNXHaPmU5GTjyKTJEiussw9Kaj5Ln8UztPe4epV/ffvgNBEadksdYhmW6xJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.11.tgz", + "integrity": "sha512-jByxb6qfd+bH1xUd0qnfFnb17i9sWBPY2tOavJ0l3tdr3OTu+Kvtm8cd/JV5nFt657b1VqGltxg9olOEfofXWw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.11.tgz", + "integrity": "sha512-/PzKqzAJ03i19oy2ItPvyvaVjOjBCNnfaJs8yvUdGBKmiESgnrJSQ2awd81QzFbbnAmu7YO9ZnJrDCb9VSJPRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.3.tgz", + "integrity": "sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vicons/ionicons5": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@vicons/ionicons5/-/ionicons5-0.13.0.tgz", + "integrity": "sha512-zvZKBPjEXKN7AXNo2Na2uy+nvuv6SP4KAMQxpKL2vfHMj0fSvuw7JZcOPCjQC3e7ayssKnaoFVAhbYcW6v41qQ==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.9.tgz", + "integrity": "sha512-rD/MORlhaZMlXWW0rEn4FB4wMinWC0z/D7Ye160S5+Rs1mC8ZDAdcDp4SjKfUhG5t8q1ktcPVw4xuTkPRlzrSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue-macros/common": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.4.tgz", + "integrity": "sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.22", + "ast-kit": "^2.1.2", + "local-pkg": "^1.1.2", + "magic-string-ast": "^1.0.2", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", + "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.43", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", + "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", + "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.43", + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-ssr": "3.5.43", + "@vue/shared": "3.5.43", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.28", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", + "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", + "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^8.2.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", + "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", + "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", + "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.43", + "@vue/runtime-core": "3.5.43", + "@vue/shared": "3.5.43", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", + "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/shared": "3.5.43" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", + "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", + "license": "MIT" + }, + "node_modules/@xterm/addon-fit": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz", + "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==", + "license": "MIT" + }, + "node_modules/@xterm/xterm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/ast-kit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz", + "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.9.0.tgz", + "integrity": "sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.2", + "@babel/types": "^7.29.0", + "ast-kit": "^2.2.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", + "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", + "license": "MIT" + }, + "node_modules/css-render": { + "version": "0.15.14", + "resolved": "https://registry.npmjs.org/css-render/-/css-render-0.15.14.tgz", + "integrity": "sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "~0.8.0", + "csstype": "~3.0.5" + } + }, + "node_modules/css-render/node_modules/csstype": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", + "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-tz": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz", + "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==", + "license": "MIT", + "peerDependencies": { + "date-fns": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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/evtd": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/evtd/-/evtd-0.2.4.tgz", + "integrity": "sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/highlight.js": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.12.0.tgz", + "integrity": "sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": true, + "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-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz", + "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==", + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.19" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it/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/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/naive-ui": { + "version": "2.45.3", + "resolved": "https://registry.npmjs.org/naive-ui/-/naive-ui-2.45.3.tgz", + "integrity": "sha512-D83gJ+6zUdkQ5DuUbmlfyaM1t8pV5WMPa6KmL9mByqQuiL12qBdA0n/iEIth4dQw1x/duNGpOS2anpt5FKO1kw==", + "license": "MIT", + "dependencies": { + "@css-render/plugin-bem": "^0.15.14", + "@css-render/vue3-ssr": "^0.15.14", + "@types/lodash": "^4.17.20", + "@types/lodash-es": "^4.17.12", + "async-validator": "^4.2.5", + "css-render": "^0.15.14", + "csstype": "^3.1.3", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "evtd": "^0.2.4", + "highlight.js": "^11.8.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "seemly": "^0.3.10", + "treemate": "^0.3.11", + "vdirs": "^0.1.8", + "vooks": "^0.2.12", + "vueuc": "^0.4.66" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "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/nostics": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.3.0.tgz", + "integrity": "sha512-wzRsrJwNlFbqIRQt8aCeL2WE8USip/uuvGSOZV1D02CxX8ABTMbw8IywKjH7yWHETMXbWJI4OBpihg9rmz2Vyg==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "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.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-4.0.3.tgz", + "integrity": "sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA==", + "license": "MIT", + "dependencies": { + "nostics": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/devtools-api": "^8.1.5", + "typescript": ">=5.6.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "@vue/devtools-api": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pkg-types": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.3.tgz", + "integrity": "sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==", + "license": "MIT", + "dependencies": { + "confbox": "^0.3.1", + "exsolve": "^1.1.1", + "pathe": "^2.0.3" + } + }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "license": "MIT" + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "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.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/rolldown": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.11.tgz", + "integrity": "sha512-qpSwIyz0jHQq5qXBTNxFmE6664rJ7O+4TvPFOiOaBSrz8IOHc1koKKSqTM2H6u1UG1+TveuC6vaDHKXFOvb1Kw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.151.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.11", + "@rolldown/binding-android-arm64": "1.2.11", + "@rolldown/binding-darwin-arm64": "1.2.11", + "@rolldown/binding-darwin-x64": "1.2.11", + "@rolldown/binding-freebsd-x64": "1.2.11", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.11", + "@rolldown/binding-linux-arm64-gnu": "1.2.11", + "@rolldown/binding-linux-arm64-musl": "1.2.11", + "@rolldown/binding-linux-ppc64-gnu": "1.2.11", + "@rolldown/binding-linux-s390x-gnu": "1.2.11", + "@rolldown/binding-linux-x64-gnu": "1.2.11", + "@rolldown/binding-linux-x64-musl": "1.2.11", + "@rolldown/binding-openharmony-arm64": "1.2.11", + "@rolldown/binding-win32-arm64-msvc": "1.2.11", + "@rolldown/binding-win32-x64-msvc": "1.2.11" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "license": "MIT" + }, + "node_modules/seemly": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/seemly/-/seemly-0.3.10.tgz", + "integrity": "sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==", + "license": "MIT" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "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/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/style-mod": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.4.tgz", + "integrity": "sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/treemate": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/treemate/-/treemate-0.3.11.tgz", + "integrity": "sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unplugin": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.4.0.tgz", + "integrity": "sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.7", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rsbuild/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.2.tgz", + "integrity": "sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vdirs": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/vdirs/-/vdirs-0.1.8.tgz", + "integrity": "sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==", + "license": "MIT", + "dependencies": { + "evtd": "^0.2.2" + }, + "peerDependencies": { + "vue": "^3.0.11" + } + }, + "node_modules/vite": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.1.tgz", + "integrity": "sha512-/bvH9E9tmCXRGp2uXY3WbOldqpTwFkbha/8ANaEQ6VkxhH60KyqLwgZq6lG2y+4uT55x9+9eUHMpQ7uGnOCKjA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.28", + "rolldown": "~1.2.9", + "tinyglobby": "^0.2.17" + }, + "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", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.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 + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "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/vooks": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/vooks/-/vooks-0.2.12.tgz", + "integrity": "sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==", + "license": "MIT", + "dependencies": { + "evtd": "^0.2.2" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.43", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", + "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.43", + "@vue/compiler-sfc": "3.5.43", + "@vue/runtime-dom": "3.5.43", + "@vue/server-renderer": "3.5.43", + "@vue/shared": "3.5.43" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.3.1.tgz", + "integrity": "sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==", + "license": "MIT", + "dependencies": { + "@vue-macros/common": "^3.1.3", + "@vue/devtools-api": "^8.1.5", + "ast-walker-scope": "^0.9.0", + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "local-pkg": "^1.2.1", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "muggle-string": "^0.4.1", + "nostics": "^1.1.4", + "pathe": "^2.0.3", + "picomatch": "^4.0.5", + "scule": "^1.3.0", + "tinyglobby": "^0.2.17", + "unplugin": "^3.3.0", + "unplugin-utils": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@pinia/colada": ">=0.21.2", + "@vue/compiler-sfc": "^3.5.34 || ^4.0.0", + "pinia": "^3.0.4 || ^4.0.2", + "vite": "^7.3.0 || ^8.0.0", + "vue": "^3.5.34 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@pinia/colada": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "pinia": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vueuc": { + "version": "0.4.66", + "resolved": "https://registry.npmjs.org/vueuc/-/vueuc-0.4.66.tgz", + "integrity": "sha512-hHrZl+0AsrUvqYPBXSXQ2QaSrO3razjeUseLAhJWlOS/Hdfw/Y2MU05fOTP1qn1A3121gMxsHLaEz2V6CkWdTg==", + "license": "MIT", + "dependencies": { + "@css-render/vue3-ssr": "^0.15.10", + "@juggle/resize-observer": "^3.3.1", + "css-render": "^0.15.10", + "evtd": "^0.2.4", + "seemly": "^0.3.6", + "vdirs": "^0.1.4", + "vooks": "^0.2.4" + }, + "peerDependencies": { + "vue": "^3.0.11" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "license": "MIT" + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..0535a43 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,34 @@ +{ + "name": "cyi-box-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "typecheck": "vue-tsc --noEmit", + "preview": "vite preview" + }, + "dependencies": { + "@codemirror/lang-sql": "^6.10.0", + "@vicons/ionicons5": "^0.13.0", + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", + "codemirror": "^6.0.2", + "echarts": "^5.6.0", + "markdown-it": "^14.1.0", + "naive-ui": "^2.45.0", + "pinia": "^4.0.3", + "qrcode": "^1.5.4", + "vue": "^3.5.40", + "vue-router": "^5.2.0" + }, + "devDependencies": { + "@types/markdown-it": "^14.1.2", + "@types/qrcode": "^1.5.6", + "@vitejs/plugin-vue": "^6.0.8", + "typescript": "^5.9.3", + "vite": "^8.2.0", + "vue-tsc": "^3.3.10" + } +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..fdc0cb8 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,46 @@ + + + diff --git a/frontend/src/api/authSvc.ts b/frontend/src/api/authSvc.ts new file mode 100644 index 0000000..eeca801 --- /dev/null +++ b/frontend/src/api/authSvc.ts @@ -0,0 +1,24 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type changePasswordDto from "./changePasswordDto"; +import type loginDto from "./loginDto"; +import type okView from "./okView"; +import type sessionView from "./sessionView"; + +export default class authSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async changePassword(dto:changePasswordDto, options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "changePassword", dto, options) + } + async login(dto:loginDto, options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "login", dto, options) + } + async logout(options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "logout", undefined, options) + } + async me(options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "me", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/bookmarkIdDto.ts b/frontend/src/api/bookmarkIdDto.ts new file mode 100644 index 0000000..fc4a4e1 --- /dev/null +++ b/frontend/src/api/bookmarkIdDto.ts @@ -0,0 +1,3 @@ +export default interface bookmarkIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/bookmarkSvc.ts b/frontend/src/api/bookmarkSvc.ts new file mode 100644 index 0000000..9505ba8 --- /dev/null +++ b/frontend/src/api/bookmarkSvc.ts @@ -0,0 +1,21 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type bookmarkIdDto from "./bookmarkIdDto"; +import type bookmarkView from "./bookmarkView"; +import type okView from "./okView"; +import type saveBookmarkDto from "./saveBookmarkDto"; + +export default class bookmarkSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:bookmarkIdDto, options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "list", undefined, options) + } + async save(dto:saveBookmarkDto, options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "save", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/bookmarkView.ts b/frontend/src/api/bookmarkView.ts new file mode 100644 index 0000000..41913d1 --- /dev/null +++ b/frontend/src/api/bookmarkView.ts @@ -0,0 +1,7 @@ +export default interface bookmarkView { + id:number + title:string + url:string + icon:string + sort:number +} \ No newline at end of file diff --git a/frontend/src/api/changePasswordDto.ts b/frontend/src/api/changePasswordDto.ts new file mode 100644 index 0000000..ece2f50 --- /dev/null +++ b/frontend/src/api/changePasswordDto.ts @@ -0,0 +1,4 @@ +export default interface changePasswordDto { + oldPassword:string + newPassword:string +} \ No newline at end of file diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts new file mode 100644 index 0000000..09077af --- /dev/null +++ b/frontend/src/api/client.ts @@ -0,0 +1,665 @@ +// 大整数安全 JSON:超过 Number.MAX_SAFE_INTEGER(2^53-1)的整数字面量 +// 解析为 BigInt,序列化时 BigInt 还原为数字字面量——雪花 ID 等不再丢精度。 +// 自包含实现,不引入运行时依赖;如需换 json-bigint,替换下面两个函数即可 +const BIGINT_PREFIX = "\u0000fun-bigint:"; + +function reviveBigint(value: any): any { + if (typeof value === "string" && value.startsWith(BIGINT_PREFIX) && + /^-?\d+$/.test(value.slice(BIGINT_PREFIX.length))) { + return BigInt(value.slice(BIGINT_PREFIX.length)); + } + if (Array.isArray(value)) return value.map(reviveBigint); + if (value !== null && typeof value === "object") { + for (const key of Object.keys(value)) value[key] = reviveBigint(value[key]); + return value; + } + return value; +} + +function parseLossless(text: string): any { + // 先把超出安全范围的大整数包成带哨兵的字符串(正则的字符串分支优先, + // 字符串内容里的数字不受影响),JSON.parse 后再还原为 BigInt + const guarded = text.replace( + /"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g, + token => { + if (token.startsWith('"')) return token; + if (!/[.eE]/.test(token) && !Number.isSafeInteger(Number(token))) { + // 哨兵必须以转义形式 \\u0000 写入文本(JSON 字符串禁止裸控制字符), + // JSON.parse 解码后即为 BIGINT_PREFIX(真实 NUL 开头) + return '"\\u0000fun-bigint:' + token + '"'; + } + return token; + } + ); + return reviveBigint(JSON.parse(guarded)); +} + +function stringifyLossless(value: any): string | undefined { + const raw = JSON.stringify(value, (_key, v) => + typeof v === "bigint" ? BIGINT_PREFIX + v.toString() : v + ); + if (raw === undefined) return undefined; + // 哨兵字符串在序列化文本中形如 "\u0000fun-bigint:123"(NUL 被转义),去引号还原为数字字面量 + return raw.replace(/"\\u0000fun-bigint:(-?\d+)"/g, "$1"); +} + +export type result = { + code?: number; + data?: T; + msg?: string; + status: number; +}; + +export type resultStatus = 0 | 1 | 2 | 4 | 5; +// 0 success; 1 framework/client protocol error; 2 business error; 4 external request error; 5 external timeout + +export type RequestOptions = { + signal?: AbortSignal; + state?: Record; +}; + +export type StreamOptions = { + signal?: AbortSignal; + state?: Record; + /** + * 原生断线重连:流因传输失败(status 4/5)结束时按指数退避自动重连。 + * dto 传工厂函数时每次重连重新求值,用于刷新续传游标。 + * 未传时关闭(默认),与 request 语义一致。 + */ + retry?: StreamRetryOptions | false; + /** 空闲看门狗:超过该毫秒数未收到任何字节(含心跳空行)即判定流死亡并走重连。0 关闭。 */ + idleTimeoutMs?: number; +}; + +export type StreamRetryOptions = { + /** 最大重连次数,默认 Infinity(直到 signal 取消或成功)。 */ + maxAttempts?: number; + /** 首次重连延迟(毫秒),默认 500。 */ + baseDelayMs?: number; + /** 退避上限(毫秒),默认 10000。实际延迟为指数退避 × 0.75~1.25 抖动。 */ + maxDelayMs?: number; +}; + +export type RequestInterceptor = ( + serviceName: string, + methodName: string, + state: Record, + dto?: any +) => Promise | void; + +export type ResponseContext = { + readonly requestState: Readonly>; + readonly response?: Response; +}; + +export type ResponseInterceptor = ( + serviceName: string, + methodName: string, + result: result +) => Promise | void> | result | void; + +export type ContextResponseInterceptor = ( + serviceName: string, + methodName: string, + result: result, + context: ResponseContext +) => Promise | void> | result | void; + +function messageOf(error: unknown): string { + if (error instanceof Error && error.message) return error.message; + if (typeof error === "string" && error) return error; + return "unknown error"; +} + +function failure(status: resultStatus, msg: string): result { + return { status, msg }; +} + +function isTimeout(error: unknown, signal?: AbortSignal): boolean { + const errorName = error !== null && typeof error === "object" + ? (error as { name?: unknown }).name + : undefined; + const reason = signal?.reason; + const reasonName = reason !== null && typeof reason === "object" + ? (reason as { name?: unknown }).name + : undefined; + return errorName === "TimeoutError" || reasonName === "TimeoutError"; +} + +function requestFailure(error: unknown, signal: AbortSignal | undefined, stream: boolean): result { + if (isTimeout(error, signal)) { + return failure(5, stream ? "Stream timed out" : "Request timed out"); + } + if (signal?.aborted === true) { + return failure(4, stream ? "Stream aborted" : "Request aborted"); + } + const kind = stream ? "External stream request" : "External request"; + return failure(4, `${kind} failed: ${messageOf(error)}`); +} + +function isResult(value: unknown): value is result { + return value !== null && typeof value === "object" && + typeof (value as { status?: unknown }).status === "number"; +} + +function mediaType(response: Response): string { + return (response.headers.get("content-type") || "").split(";", 1)[0].trim().toLowerCase(); +} + +function excerpt(text: string, limit = 180): string { + const value = text.replace(/\s+/g, " ").trim(); + return value.length <= limit ? value : `${value.slice(0, limit)}...`; +} + +function externalFailure(response: Response, detail?: string): result { + const timeout = response.status === 408 || response.status === 504; + const statusText = response.statusText || (timeout ? "timeout" : "request failed"); + const suffix = detail ? `: ${detail}` : ""; + return failure(timeout ? 5 : 4, `HTTP ${response.status} ${statusText}${suffix}`); +} + +function responseReadFailure( + error: unknown, + response: Response, + signal: AbortSignal | undefined, + stream: boolean +): result { + if (isTimeout(error, signal)) { + return failure(5, stream ? "Stream timed out" : "Request timed out"); + } + if (signal?.aborted === true) { + return failure(4, stream ? "Stream aborted" : "Request aborted"); + } + const kind = stream ? "Stream" : "Response body"; + return response.ok + ? failure(4, `${kind} failed: ${messageOf(error)}`) + : externalFailure(response, `response body failed: ${messageOf(error)}`); +} + +function parseResult(response: Response, text: string): result { + const body = text.trim(); + if (!body) { + return response.ok + ? failure(1, "Empty response body") + : externalFailure(response); + } + + let value: unknown; + try { + value = parseLossless(body); + } catch { + if (!response.ok) return externalFailure(response, excerpt(body)); + const type = mediaType(response); + if (type === "text/html" || /^\s*(?: = {}; + private requestInterceptors: RequestInterceptor[] = []; + private responseInterceptors: ContextResponseInterceptor[] = []; + + constructor(url: string) { + this.url = url.replace(/\/+$/, ""); + } + + setState(state: Record) { + this.state = state; + } + + addRequestInterceptor(interceptor: RequestInterceptor) { + this.requestInterceptors.push(interceptor); + } + + addResponseInterceptor(interceptor: ResponseInterceptor): void; + addResponseInterceptor(interceptor: ContextResponseInterceptor): void; + addResponseInterceptor(interceptor: ResponseInterceptor | ContextResponseInterceptor) { + this.responseInterceptors.push(interceptor as ContextResponseInterceptor); + } + + private async interceptResponse( + serviceName: string, + methodName: string, + initial: result, + requestState: Readonly>, + response?: Response + ): Promise> { + let current = initial; + const context: ResponseContext = Object.freeze( + response === undefined ? { requestState } : { requestState, response } + ); + for (const interceptor of this.responseInterceptors) { + try { + const replaced = await interceptor(serviceName, methodName, current, context); + if (replaced) current = replaced; + } catch (error) { + current = failure(1, `Response interceptor failed: ${messageOf(error)}`); + } + } + return current; + } + + private async interceptRequest( + serviceName: string, + methodName: string, + state: Record, + dto: any + ): Promise { + for (const interceptor of this.requestInterceptors) { + await interceptor(serviceName, methodName, state, dto); + } + } + + private snapshotState(state: Record): Readonly> { + return Object.freeze({ ...state }); + } + + async request( + serviceName: string, + methodName: string, + dto?: any, + options?: RequestOptions + ): Promise> { + let state: Record; + try { + state = { ...this.state, ...options?.state }; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not prepare request state: ${messageOf(error)}`), + Object.freeze({}) + ) as result; + } + try { + await this.interceptRequest(serviceName, methodName, state, dto); + } catch (error) { + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (snapshotError) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(snapshotError)}`), + Object.freeze({}) + ) as result; + } + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Request interceptor failed: ${messageOf(error)}`), + requestState + ) as result; + } + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(error)}`), + Object.freeze({}) + ) as result; + } + + let body: string; + try { + const serialized = stringifyLossless({ + serviceName, + methodName, + data: dto, + ...(Object.keys(requestState).length ? { state: requestState } : {}), + }); + if (serialized === undefined) throw new Error("serialization produced no output"); + body = serialized; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not serialize request: ${messageOf(error)}`), + requestState + ) as result; + } + + let output: result; + let response: Response | undefined; + try { + response = await fetch(`${this.url}/cell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + signal: options?.signal, + }); + try { + output = parseResult(response, await response.text()); + } catch (error) { + output = responseReadFailure(error, response, options?.signal, false); + } + } catch (error) { + output = requestFailure(error, options?.signal, false); + } + return await this.interceptResponse(serviceName, methodName, output, requestState, response) as result; + } + + async stream( + serviceName: string, + methodName: string, + dto: any | undefined | (() => any | undefined), + onMessage: (data: T) => unknown, + options?: StreamOptions + ): Promise> { + // dto 工厂:重连时重新求值,用于刷新续传游标(如 afterId) + const getDto = typeof dto === "function" ? (dto as () => any | undefined) : () => dto; + // 重连默认关闭(与 request 语义一致);传 retry: {...} 显式开启 + const retry = options?.retry + ? { + maxAttempts: Infinity, + baseDelayMs: 500, + maxDelayMs: 10000, + ...options.retry, + } + : null; + for (let attempt = 0; ; attempt++) { + const r = await this.streamOnce(serviceName, methodName, getDto(), onMessage, options); + // 0 成功、1 协议错误、2 业务错误:终结态,不重连 + if (r.status !== 4 && r.status !== 5) return r; + if (!retry) return r; + if (options?.signal?.aborted) return r; + if (attempt + 1 > retry.maxAttempts) return r; + const delay = + Math.min(retry.maxDelayMs, retry.baseDelayMs * 2 ** attempt) * (0.75 + Math.random() * 0.5); + // 退避等待:AbortSignal.timeout 免定时器 API,且后台标签页照常走时 + await new Promise((resolve) => { + if (options?.signal?.aborted) { + resolve(); + return; + } + AbortSignal.timeout(delay).addEventListener("abort", () => resolve(), { once: true }); + options?.signal?.addEventListener("abort", () => resolve(), { once: true }); + }); + if (options?.signal?.aborted) return r; + } + } + + private async streamOnce( + serviceName: string, + methodName: string, + dto: any | undefined, + onMessage: (data: T) => unknown, + options?: StreamOptions + ): Promise> { + let state: Record; + try { + state = { ...this.state, ...options?.state }; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not prepare request state: ${messageOf(error)}`), + Object.freeze({}) + ); + } + try { + await this.interceptRequest(serviceName, methodName, state, dto); + } catch (error) { + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (snapshotError) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(snapshotError)}`), + Object.freeze({}) + ); + } + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Request interceptor failed: ${messageOf(error)}`), + requestState + ); + } + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(error)}`), + Object.freeze({}) + ); + } + + let body: string; + try { + const serialized = stringifyLossless({ + serviceName, + methodName, + data: dto, + ...(Object.keys(requestState).length ? { state: requestState } : {}), + }); + if (serialized === undefined) throw new Error("serialization produced no output"); + body = serialized; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not serialize request: ${messageOf(error)}`), + requestState + ); + } + + let response: Response; + try { + response = await fetch(`${this.url}/cell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + signal: options?.signal, + }); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + requestFailure(error, options?.signal, true), + requestState + ); + } + + if (!response.ok) { + let text: string; + try { + text = await response.text(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + return await this.interceptResponse( + serviceName, + methodName, + parseResult(response, text), + requestState, + response + ); + } + + if (mediaType(response) !== "application/x-ndjson") { + let text: string; + try { + text = await response.text(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + const rpcResult = parseResult(response, text); + return await this.interceptResponse( + serviceName, + methodName, + rpcResult.status === 0 + ? failure(1, "Expected application/x-ndjson response") + : rpcResult, + requestState, + response + ); + } + + if (!response.body) { + return await this.interceptResponse( + serviceName, + methodName, + { status: 0 }, + requestState, + response + ); + } + + let reader: ReadableStreamDefaultReader; + try { + reader = response.body.getReader(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + const decoder = new TextDecoder("utf-8", { fatal: true }); + let buffer = ""; + let lineNumber = 0; + let failed: result | undefined; + let cause: unknown; + + const emitLine = async (line: string) => { + const payload = line.replace(/\r$/, "").trim(); + if (!payload) return; + let data: T; + try { + data = parseLossless(payload) as T; + } catch (error) { + failed = failure(1, `Invalid NDJSON at line ${lineNumber}: ${excerpt(payload)}`); + cause = error; + return; + } + try { + await onMessage(data); + } catch (error) { + failed = failure(1, `Stream callback failed: ${messageOf(error)}`); + cause = error; + } + }; + + try { + // 空闲看门狗:idleTimeoutMs 内未收到任何字节(含心跳空行)判定流死亡, + // 抛出哨兵错误 → status 5 → 交给上层重连。服务端 25s 心跳时建议 35000。 + const STREAM_IDLE = Symbol("stream-idle"); + for (;;) { + let part: ReadableStreamReadResult; + try { + if (options?.idleTimeoutMs) { + const idlePromise = new Promise((_, reject) => { + AbortSignal.timeout(options.idleTimeoutMs!).addEventListener( + "abort", + () => reject(STREAM_IDLE), + { once: true } + ); + }); + idlePromise.catch(() => {}); // read 先返回时防 unhandled rejection + part = await Promise.race([reader.read(), idlePromise]); + } else { + part = await reader.read(); + } + } catch (error) { + cause = error; + if (error === STREAM_IDLE) { + failed = failure(5, "Stream idle timeout"); + } else if (isTimeout(error, options?.signal)) { + failed = failure(5, "Stream timed out"); + } else if (options?.signal?.aborted === true) { + failed = failure(4, "Stream aborted"); + } else { + failed = failure(4, `Stream read failed: ${messageOf(error)}`); + } + break; + } + if (part.done) break; + try { + buffer += decoder.decode(part.value, { stream: true }); + } catch (error) { + failed = failure(1, `Invalid UTF-8 stream data: ${messageOf(error)}`); + cause = error; + break; + } + for (;;) { + const newline = buffer.indexOf("\n"); + if (newline < 0) break; + const line = buffer.slice(0, newline); + buffer = buffer.slice(newline + 1); + lineNumber++; + await emitLine(line); + if (failed) break; + } + if (failed) break; + } + + if (!failed) { + try { + buffer += decoder.decode(); + } catch (error) { + failed = failure(1, `Invalid UTF-8 stream data: ${messageOf(error)}`); + cause = error; + } + } + if (!failed && buffer.length > 0) { + lineNumber++; + await emitLine(buffer); + } + + } finally { + if (failed) { + try { + await reader.cancel(cause); + } catch { + // The reader may already be closed by the runtime. + } + } + try { + reader.releaseLock(); + } catch { + // The reader may already be errored or released. + } + } + return await this.interceptResponse( + serviceName, + methodName, + failed || { status: 0 }, + requestState, + response + ); + } +} \ No newline at end of file diff --git a/frontend/src/api/createShareDto.ts b/frontend/src/api/createShareDto.ts new file mode 100644 index 0000000..5d110a8 --- /dev/null +++ b/frontend/src/api/createShareDto.ts @@ -0,0 +1,4 @@ +export default interface createShareDto { + fileId:number + expireMinutes:number +} \ No newline at end of file diff --git a/frontend/src/api/dashboardSvc.ts b/frontend/src/api/dashboardSvc.ts new file mode 100644 index 0000000..df6e279 --- /dev/null +++ b/frontend/src/api/dashboardSvc.ts @@ -0,0 +1,12 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type statsView from "./statsView"; + +export default class dashboardSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("dashboardSvc", "stats", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/dbColumnView.ts b/frontend/src/api/dbColumnView.ts new file mode 100644 index 0000000..2313a4d --- /dev/null +++ b/frontend/src/api/dbColumnView.ts @@ -0,0 +1,6 @@ +export default interface dbColumnView { + table:string + name:string + type:string + pk:boolean +} \ No newline at end of file diff --git a/frontend/src/api/dbDatabasesView.ts b/frontend/src/api/dbDatabasesView.ts new file mode 100644 index 0000000..ba7630c --- /dev/null +++ b/frontend/src/api/dbDatabasesView.ts @@ -0,0 +1,3 @@ +export default interface dbDatabasesView { + names:string[] +} \ No newline at end of file diff --git a/frontend/src/api/dbMgrSvc.ts b/frontend/src/api/dbMgrSvc.ts new file mode 100644 index 0000000..33c1063 --- /dev/null +++ b/frontend/src/api/dbMgrSvc.ts @@ -0,0 +1,38 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type dbDatabasesView from "./dbDatabasesView"; +import type dbQueryDto from "./dbQueryDto"; +import type dbQueryView from "./dbQueryView"; +import type dbScopeDto from "./dbScopeDto"; +import type dbTablesView from "./dbTablesView"; +import type dbconnIdDto from "./dbconnIdDto"; +import type dbconnView from "./dbconnView"; +import type okView from "./okView"; +import type saveDbconnDto from "./saveDbconnDto"; + +export default class dbMgrSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async databases(dto:dbScopeDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "databases", dto, options) + } + async delete(dto:dbconnIdDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "list", undefined, options) + } + async query(dto:dbQueryDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "query", dto, options) + } + async save(dto:saveDbconnDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "save", dto, options) + } + async tables(dto:dbScopeDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "tables", dto, options) + } + async test(dto:dbconnIdDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "test", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/dbQueryDto.ts b/frontend/src/api/dbQueryDto.ts new file mode 100644 index 0000000..0298521 --- /dev/null +++ b/frontend/src/api/dbQueryDto.ts @@ -0,0 +1,6 @@ +export default interface dbQueryDto { + id:number + database?:string | null + sql:string + maxRows?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/dbQueryView.ts b/frontend/src/api/dbQueryView.ts new file mode 100644 index 0000000..df39f49 --- /dev/null +++ b/frontend/src/api/dbQueryView.ts @@ -0,0 +1,9 @@ +export default interface dbQueryView { + isSelect:boolean + columns:string[] + rowsJson:string + rowCount:number + affectedRows:number + truncated:boolean + elapsedMs:number +} \ No newline at end of file diff --git a/frontend/src/api/dbScopeDto.ts b/frontend/src/api/dbScopeDto.ts new file mode 100644 index 0000000..6dfe44a --- /dev/null +++ b/frontend/src/api/dbScopeDto.ts @@ -0,0 +1,4 @@ +export default interface dbScopeDto { + id:number + database?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/dbTableView.ts b/frontend/src/api/dbTableView.ts new file mode 100644 index 0000000..b56b145 --- /dev/null +++ b/frontend/src/api/dbTableView.ts @@ -0,0 +1,4 @@ +export default interface dbTableView { + name:string + type:string +} \ No newline at end of file diff --git a/frontend/src/api/dbTablesView.ts b/frontend/src/api/dbTablesView.ts new file mode 100644 index 0000000..e3aad2a --- /dev/null +++ b/frontend/src/api/dbTablesView.ts @@ -0,0 +1,6 @@ +import type dbColumnView from "./dbColumnView"; +import type dbTableView from "./dbTableView"; +export default interface dbTablesView { + tables:dbTableView[] + columns:dbColumnView[] +} \ No newline at end of file diff --git a/frontend/src/api/dbconnIdDto.ts b/frontend/src/api/dbconnIdDto.ts new file mode 100644 index 0000000..9058dc8 --- /dev/null +++ b/frontend/src/api/dbconnIdDto.ts @@ -0,0 +1,3 @@ +export default interface dbconnIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/dbconnView.ts b/frontend/src/api/dbconnView.ts new file mode 100644 index 0000000..460bf9f --- /dev/null +++ b/frontend/src/api/dbconnView.ts @@ -0,0 +1,11 @@ +export default interface dbconnView { + id:number + name:string + engine:string + host:string + port:number + username:string + database:string + hasPassword:boolean + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/deleteToolDto.ts b/frontend/src/api/deleteToolDto.ts new file mode 100644 index 0000000..7f165f7 --- /dev/null +++ b/frontend/src/api/deleteToolDto.ts @@ -0,0 +1,3 @@ +export default interface deleteToolDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/deleteUserDto.ts b/frontend/src/api/deleteUserDto.ts new file mode 100644 index 0000000..46ae2aa --- /dev/null +++ b/frontend/src/api/deleteUserDto.ts @@ -0,0 +1,3 @@ +export default interface deleteUserDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/diskMountView.ts b/frontend/src/api/diskMountView.ts new file mode 100644 index 0000000..5471301 --- /dev/null +++ b/frontend/src/api/diskMountView.ts @@ -0,0 +1,8 @@ +export default interface diskMountView { + mount:string + fs:string + total:number + used:number + avail:number + percent:number +} \ No newline at end of file diff --git a/frontend/src/api/diskSvc.ts b/frontend/src/api/diskSvc.ts new file mode 100644 index 0000000..3f81cd5 --- /dev/null +++ b/frontend/src/api/diskSvc.ts @@ -0,0 +1,28 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type createShareDto from "./createShareDto"; +import type fileView from "./fileView"; +import type idDto from "./idDto"; +import type okView from "./okView"; +import type shareView from "./shareView"; + +export default class diskSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async createShare(dto:createShareDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "createShare", dto, options) + } + async deleteFile(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "deleteFile", dto, options) + } + async deleteShare(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "deleteShare", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "list", undefined, options) + } + async listShares(options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "listShares", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/fileView.ts b/frontend/src/api/fileView.ts new file mode 100644 index 0000000..020f18d --- /dev/null +++ b/frontend/src/api/fileView.ts @@ -0,0 +1,7 @@ +export default interface fileView { + id:number + name:string + size:number + mime:string + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/fun.ts b/frontend/src/api/fun.ts new file mode 100644 index 0000000..f016894 --- /dev/null +++ b/frontend/src/api/fun.ts @@ -0,0 +1,43 @@ +import { Client } from "./client"; +import authSvc from "./authSvc"; +import bookmarkSvc from "./bookmarkSvc"; +import dashboardSvc from "./dashboardSvc"; +import dbMgrSvc from "./dbMgrSvc"; +import diskSvc from "./diskSvc"; +import licenseSvc from "./licenseSvc"; +import memorySvc from "./memorySvc"; +import paySvc from "./paySvc"; +import proxySvc from "./proxySvc"; +import serverSvc from "./serverSvc"; +import settingSvc from "./settingSvc"; +import toolSvc from "./toolSvc"; +import userSvc from "./userSvc"; +import vaultSvc from "./vaultSvc"; +import webSvc from "./webSvc"; + +export class defaultApi extends Client { + constructor(url: string) { + super(url); + } + public authSvc: authSvc = new authSvc(this); + public bookmarkSvc: bookmarkSvc = new bookmarkSvc(this); + public dashboardSvc: dashboardSvc = new dashboardSvc(this); + public dbMgrSvc: dbMgrSvc = new dbMgrSvc(this); + public diskSvc: diskSvc = new diskSvc(this); + public licenseSvc: licenseSvc = new licenseSvc(this); + public memorySvc: memorySvc = new memorySvc(this); + public paySvc: paySvc = new paySvc(this); + public proxySvc: proxySvc = new proxySvc(this); + public serverSvc: serverSvc = new serverSvc(this); + public settingSvc: settingSvc = new settingSvc(this); + public toolSvc: toolSvc = new toolSvc(this); + public userSvc: userSvc = new userSvc(this); + public vaultSvc: vaultSvc = new vaultSvc(this); + public webSvc: webSvc = new webSvc(this); +} + +export default class api { + static create(url: string): defaultApi { + return new defaultApi(url); + } +} \ No newline at end of file diff --git a/frontend/src/api/idDto.ts b/frontend/src/api/idDto.ts new file mode 100644 index 0000000..f69ce11 --- /dev/null +++ b/frontend/src/api/idDto.ts @@ -0,0 +1,3 @@ +export default interface idDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseAppSaveDto.ts b/frontend/src/api/licenseAppSaveDto.ts new file mode 100644 index 0000000..52277b2 --- /dev/null +++ b/frontend/src/api/licenseAppSaveDto.ts @@ -0,0 +1,5 @@ +export default interface licenseAppSaveDto { + id:number + name:string + enabled?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/licenseAppView.ts b/frontend/src/api/licenseAppView.ts new file mode 100644 index 0000000..b7d4748 --- /dev/null +++ b/frontend/src/api/licenseAppView.ts @@ -0,0 +1,8 @@ +export default interface licenseAppView { + id:number + appid:string + name:string + enabled:number + cardCount:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseAppsView.ts b/frontend/src/api/licenseAppsView.ts new file mode 100644 index 0000000..d176362 --- /dev/null +++ b/frontend/src/api/licenseAppsView.ts @@ -0,0 +1,4 @@ +import type licenseAppView from "./licenseAppView"; +export default interface licenseAppsView { + items:licenseAppView[] +} \ No newline at end of file diff --git a/frontend/src/api/licenseCardView.ts b/frontend/src/api/licenseCardView.ts new file mode 100644 index 0000000..d440cea --- /dev/null +++ b/frontend/src/api/licenseCardView.ts @@ -0,0 +1,10 @@ +export default interface licenseCardView { + id:number + card:string + hours:number + domain:string + status:number + activatedAt:number + expiresAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseGenDto.ts b/frontend/src/api/licenseGenDto.ts new file mode 100644 index 0000000..0bb7e32 --- /dev/null +++ b/frontend/src/api/licenseGenDto.ts @@ -0,0 +1,5 @@ +export default interface licenseGenDto { + appId:number + count:number + hours:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseGenView.ts b/frontend/src/api/licenseGenView.ts new file mode 100644 index 0000000..c07d4b1 --- /dev/null +++ b/frontend/src/api/licenseGenView.ts @@ -0,0 +1,3 @@ +export default interface licenseGenView { + cards:string[] +} \ No newline at end of file diff --git a/frontend/src/api/licenseIdDto.ts b/frontend/src/api/licenseIdDto.ts new file mode 100644 index 0000000..906a6a1 --- /dev/null +++ b/frontend/src/api/licenseIdDto.ts @@ -0,0 +1,3 @@ +export default interface licenseIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseListDto.ts b/frontend/src/api/licenseListDto.ts new file mode 100644 index 0000000..d24da9d --- /dev/null +++ b/frontend/src/api/licenseListDto.ts @@ -0,0 +1,6 @@ +export default interface licenseListDto { + appId:number + page:number + pageSize:number + kw?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/licensePageView.ts b/frontend/src/api/licensePageView.ts new file mode 100644 index 0000000..5d28f99 --- /dev/null +++ b/frontend/src/api/licensePageView.ts @@ -0,0 +1,5 @@ +import type licenseCardView from "./licenseCardView"; +export default interface licensePageView { + total:number + items:licenseCardView[] +} \ No newline at end of file diff --git a/frontend/src/api/licenseStatsDto.ts b/frontend/src/api/licenseStatsDto.ts new file mode 100644 index 0000000..65ce456 --- /dev/null +++ b/frontend/src/api/licenseStatsDto.ts @@ -0,0 +1,3 @@ +export default interface licenseStatsDto { + appId:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseStatsView.ts b/frontend/src/api/licenseStatsView.ts new file mode 100644 index 0000000..35f3496 --- /dev/null +++ b/frontend/src/api/licenseStatsView.ts @@ -0,0 +1,6 @@ +export default interface licenseStatsView { + total:number + unused:number + active:number + expired:number +} \ No newline at end of file diff --git a/frontend/src/api/licenseSvc.ts b/frontend/src/api/licenseSvc.ts new file mode 100644 index 0000000..cf43aca --- /dev/null +++ b/frontend/src/api/licenseSvc.ts @@ -0,0 +1,40 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type licenseAppSaveDto from "./licenseAppSaveDto"; +import type licenseAppView from "./licenseAppView"; +import type licenseAppsView from "./licenseAppsView"; +import type licenseGenDto from "./licenseGenDto"; +import type licenseGenView from "./licenseGenView"; +import type licenseIdDto from "./licenseIdDto"; +import type licenseListDto from "./licenseListDto"; +import type licensePageView from "./licensePageView"; +import type licenseStatsDto from "./licenseStatsDto"; +import type licenseStatsView from "./licenseStatsView"; +import type okView from "./okView"; + +export default class licenseSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async apps(options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "apps", undefined, options) + } + async delete(dto:licenseIdDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "delete", dto, options) + } + async deleteApp(dto:licenseIdDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "deleteApp", dto, options) + } + async generate(dto:licenseGenDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "generate", dto, options) + } + async list(dto:licenseListDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "list", dto, options) + } + async saveApp(dto:licenseAppSaveDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "saveApp", dto, options) + } + async stats(dto:licenseStatsDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "stats", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/listUsersDto.ts b/frontend/src/api/listUsersDto.ts new file mode 100644 index 0000000..201d57d --- /dev/null +++ b/frontend/src/api/listUsersDto.ts @@ -0,0 +1,5 @@ +export default interface listUsersDto { + page:number + pageSize:number + keyword?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/loginDto.ts b/frontend/src/api/loginDto.ts new file mode 100644 index 0000000..9fc8857 --- /dev/null +++ b/frontend/src/api/loginDto.ts @@ -0,0 +1,4 @@ +export default interface loginDto { + name:string + password:string +} \ No newline at end of file diff --git a/frontend/src/api/memoryKeyDto.ts b/frontend/src/api/memoryKeyDto.ts new file mode 100644 index 0000000..3f12499 --- /dev/null +++ b/frontend/src/api/memoryKeyDto.ts @@ -0,0 +1,3 @@ +export default interface memoryKeyDto { + key:string +} \ No newline at end of file diff --git a/frontend/src/api/memoryListDto.ts b/frontend/src/api/memoryListDto.ts new file mode 100644 index 0000000..f331c2a --- /dev/null +++ b/frontend/src/api/memoryListDto.ts @@ -0,0 +1,6 @@ +export default interface memoryListDto { + project:string + tag:string + query:string + limit:number +} \ No newline at end of file diff --git a/frontend/src/api/memoryListView.ts b/frontend/src/api/memoryListView.ts new file mode 100644 index 0000000..df96fe0 --- /dev/null +++ b/frontend/src/api/memoryListView.ts @@ -0,0 +1,4 @@ +import type memoryMetaView from "./memoryMetaView"; +export default interface memoryListView { + items:memoryMetaView[] +} \ No newline at end of file diff --git a/frontend/src/api/memoryMcpView.ts b/frontend/src/api/memoryMcpView.ts new file mode 100644 index 0000000..07bf2df --- /dev/null +++ b/frontend/src/api/memoryMcpView.ts @@ -0,0 +1,4 @@ +export default interface memoryMcpView { + url:string + token:string +} \ No newline at end of file diff --git a/frontend/src/api/memoryMetaView.ts b/frontend/src/api/memoryMetaView.ts new file mode 100644 index 0000000..502e85a --- /dev/null +++ b/frontend/src/api/memoryMetaView.ts @@ -0,0 +1,8 @@ +export default interface memoryMetaView { + key:string + project:string + tags:string[] + writtenBy:string + revision:number + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/memoryProjectView.ts b/frontend/src/api/memoryProjectView.ts new file mode 100644 index 0000000..15d8e59 --- /dev/null +++ b/frontend/src/api/memoryProjectView.ts @@ -0,0 +1,4 @@ +export default interface memoryProjectView { + name:string + count:number +} \ No newline at end of file diff --git a/frontend/src/api/memoryProjectsView.ts b/frontend/src/api/memoryProjectsView.ts new file mode 100644 index 0000000..f6ede5d --- /dev/null +++ b/frontend/src/api/memoryProjectsView.ts @@ -0,0 +1,4 @@ +import type memoryProjectView from "./memoryProjectView"; +export default interface memoryProjectsView { + items:memoryProjectView[] +} \ No newline at end of file diff --git a/frontend/src/api/memorySaveDto.ts b/frontend/src/api/memorySaveDto.ts new file mode 100644 index 0000000..141a4d3 --- /dev/null +++ b/frontend/src/api/memorySaveDto.ts @@ -0,0 +1,6 @@ +export default interface memorySaveDto { + key:string + content:string + project:string + tags:string[] +} \ No newline at end of file diff --git a/frontend/src/api/memorySaveView.ts b/frontend/src/api/memorySaveView.ts new file mode 100644 index 0000000..7e9a666 --- /dev/null +++ b/frontend/src/api/memorySaveView.ts @@ -0,0 +1,5 @@ +export default interface memorySaveView { + key:string + url:string + revision:number +} \ No newline at end of file diff --git a/frontend/src/api/memorySvc.ts b/frontend/src/api/memorySvc.ts new file mode 100644 index 0000000..b546d0e --- /dev/null +++ b/frontend/src/api/memorySvc.ts @@ -0,0 +1,38 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type memoryKeyDto from "./memoryKeyDto"; +import type memoryListDto from "./memoryListDto"; +import type memoryListView from "./memoryListView"; +import type memoryMcpView from "./memoryMcpView"; +import type memoryProjectsView from "./memoryProjectsView"; +import type memorySaveDto from "./memorySaveDto"; +import type memorySaveView from "./memorySaveView"; +import type memoryView from "./memoryView"; +import type okView from "./okView"; + +export default class memorySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:memoryKeyDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "delete", dto, options) + } + async get(dto:memoryKeyDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "get", dto, options) + } + async list(dto:memoryListDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "list", dto, options) + } + async mcpInfo(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "mcpInfo", undefined, options) + } + async projects(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "projects", undefined, options) + } + async resetToken(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "resetToken", undefined, options) + } + async save(dto:memorySaveDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "save", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/memoryView.ts b/frontend/src/api/memoryView.ts new file mode 100644 index 0000000..55784d5 --- /dev/null +++ b/frontend/src/api/memoryView.ts @@ -0,0 +1,11 @@ +export default interface memoryView { + key:string + url:string + content:string + project:string + tags:string[] + writtenBy:string + revision:number + createdAt:number + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/okView.ts b/frontend/src/api/okView.ts new file mode 100644 index 0000000..3f1f6ee --- /dev/null +++ b/frontend/src/api/okView.ts @@ -0,0 +1,3 @@ +export default interface okView { + message:string +} \ No newline at end of file diff --git a/frontend/src/api/payCashierDto.ts b/frontend/src/api/payCashierDto.ts new file mode 100644 index 0000000..453c260 --- /dev/null +++ b/frontend/src/api/payCashierDto.ts @@ -0,0 +1,3 @@ +export default interface payCashierDto { + tradeNo:string +} \ No newline at end of file diff --git a/frontend/src/api/payCashierStatusView.ts b/frontend/src/api/payCashierStatusView.ts new file mode 100644 index 0000000..82fb580 --- /dev/null +++ b/frontend/src/api/payCashierStatusView.ts @@ -0,0 +1,4 @@ +export default interface payCashierStatusView { + status:number + returnUrl:string +} \ No newline at end of file diff --git a/frontend/src/api/payCashierView.ts b/frontend/src/api/payCashierView.ts new file mode 100644 index 0000000..29f571a --- /dev/null +++ b/frontend/src/api/payCashierView.ts @@ -0,0 +1,13 @@ +export default interface payCashierView { + tradeNo:string + outTradeNo:string + subject:string + money:number + channel:string + status:number + payurl:string + qrcode:string + cards:string + createdAt:number + expiredAt:number +} \ No newline at end of file diff --git a/frontend/src/api/payOrderListDto.ts b/frontend/src/api/payOrderListDto.ts new file mode 100644 index 0000000..dc14713 --- /dev/null +++ b/frontend/src/api/payOrderListDto.ts @@ -0,0 +1,6 @@ +export default interface payOrderListDto { + page:number + pageSize:number + status?:number | null + kw?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/payOrderPageView.ts b/frontend/src/api/payOrderPageView.ts new file mode 100644 index 0000000..7995104 --- /dev/null +++ b/frontend/src/api/payOrderPageView.ts @@ -0,0 +1,5 @@ +import type payOrderView from "./payOrderView"; +export default interface payOrderPageView { + total:number + items:payOrderView[] +} \ No newline at end of file diff --git a/frontend/src/api/payOrderView.ts b/frontend/src/api/payOrderView.ts new file mode 100644 index 0000000..9f59c23 --- /dev/null +++ b/frontend/src/api/payOrderView.ts @@ -0,0 +1,14 @@ +export default interface payOrderView { + id:number + tradeNo:string + outTradeNo:string + channel:string + subject:string + money:number + status:number + notified:number + payurl:string + paidAt:number + expiredAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/payStatsView.ts b/frontend/src/api/payStatsView.ts new file mode 100644 index 0000000..aca80cb --- /dev/null +++ b/frontend/src/api/payStatsView.ts @@ -0,0 +1,9 @@ +export default interface payStatsView { + todayCount:number + todayMoney:number + totalCount:number + totalMoney:number + pendingCount:number + todayPaidCount:number + totalPaidCount:number +} \ No newline at end of file diff --git a/frontend/src/api/paySvc.ts b/frontend/src/api/paySvc.ts new file mode 100644 index 0000000..75cec01 --- /dev/null +++ b/frontend/src/api/paySvc.ts @@ -0,0 +1,42 @@ +import { Client, type result, type RequestOptions, type StreamOptions } from "./client"; +import type payCashierDto from "./payCashierDto"; +import type payCashierStatusView from "./payCashierStatusView"; +import type payCashierView from "./payCashierView"; +import type payOrderListDto from "./payOrderListDto"; +import type payOrderPageView from "./payOrderPageView"; +import type payStatsView from "./payStatsView"; +import type payTestPayDto from "./payTestPayDto"; +import type payTestPayView from "./payTestPayView"; +import type payUpstreamDto from "./payUpstreamDto"; +import type payUpstreamStatusView from "./payUpstreamStatusView"; + +export default class paySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async cashierInfo(dto:payCashierDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "cashierInfo", dto, options) + } + async cashierStatus(dto:payCashierDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "cashierStatus", dto, options) + } + async list(dto:payOrderListDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "list", dto, options) + } + async saveUpstream(dto:payUpstreamDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "saveUpstream", dto, options) + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "stats", undefined, options) + } + async testPay(dto:payTestPayDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "testPay", dto, options) + } + async upstreamStatus(options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "upstreamStatus", undefined, options) + } + async watch(dto:payCashierDto | (() => payCashierDto), onMessage: (data: payCashierStatusView) => unknown, options?: StreamOptions): Promise> { + return await this.client.stream("paySvc", "watch", dto, onMessage, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/payTestPayDto.ts b/frontend/src/api/payTestPayDto.ts new file mode 100644 index 0000000..ccc77a3 --- /dev/null +++ b/frontend/src/api/payTestPayDto.ts @@ -0,0 +1,3 @@ +export default interface payTestPayDto { + money:string +} \ No newline at end of file diff --git a/frontend/src/api/payTestPayView.ts b/frontend/src/api/payTestPayView.ts new file mode 100644 index 0000000..8dd173e --- /dev/null +++ b/frontend/src/api/payTestPayView.ts @@ -0,0 +1,7 @@ +export default interface payTestPayView { + tradeNo:string + payurl:string + qrcode:string + money:number + cashier:string +} \ No newline at end of file diff --git a/frontend/src/api/payTrendPoint.ts b/frontend/src/api/payTrendPoint.ts new file mode 100644 index 0000000..68571b5 --- /dev/null +++ b/frontend/src/api/payTrendPoint.ts @@ -0,0 +1,5 @@ +export default interface payTrendPoint { + day:string + money:number + count:number +} \ No newline at end of file diff --git a/frontend/src/api/payUpstreamDto.ts b/frontend/src/api/payUpstreamDto.ts new file mode 100644 index 0000000..312b862 --- /dev/null +++ b/frontend/src/api/payUpstreamDto.ts @@ -0,0 +1,4 @@ +export default interface payUpstreamDto { + username:string + password:string +} \ No newline at end of file diff --git a/frontend/src/api/payUpstreamStatusView.ts b/frontend/src/api/payUpstreamStatusView.ts new file mode 100644 index 0000000..eaf3fda --- /dev/null +++ b/frontend/src/api/payUpstreamStatusView.ts @@ -0,0 +1,18 @@ +export default interface payUpstreamStatusView { + configured:number + username:string + nickname:string + shop:string + tokenAge:number + goodsId:number + goodsKey:string + goodsName:string + unitPrice:number + stockCount:number + targetMoney:number + walletOk:number + walletAvail:number + walletFrozen:number + ensuring:number + lastError:string +} \ No newline at end of file diff --git a/frontend/src/api/proxyKeySaveDto.ts b/frontend/src/api/proxyKeySaveDto.ts new file mode 100644 index 0000000..0a9aa66 --- /dev/null +++ b/frontend/src/api/proxyKeySaveDto.ts @@ -0,0 +1,4 @@ +export default interface proxyKeySaveDto { + name:string + regionId:number +} \ No newline at end of file diff --git a/frontend/src/api/proxyKeyView.ts b/frontend/src/api/proxyKeyView.ts new file mode 100644 index 0000000..76a7d61 --- /dev/null +++ b/frontend/src/api/proxyKeyView.ts @@ -0,0 +1,12 @@ +export default interface proxyKeyView { + id:number + key:string + name:string + regionId:number + regionCode:string + regionZhName:string + enabled:boolean + usedCount:number + lastUsedAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/proxyListDto.ts b/frontend/src/api/proxyListDto.ts new file mode 100644 index 0000000..68cb224 --- /dev/null +++ b/frontend/src/api/proxyListDto.ts @@ -0,0 +1,8 @@ +export default interface proxyListDto { + regionId:number + protocol:string + keyword:string + alive:number + page:number + pageSize:number +} \ No newline at end of file diff --git a/frontend/src/api/proxyListView.ts b/frontend/src/api/proxyListView.ts new file mode 100644 index 0000000..ad8612a --- /dev/null +++ b/frontend/src/api/proxyListView.ts @@ -0,0 +1,5 @@ +import type proxyView from "./proxyView"; +export default interface proxyListView { + total:number + items:proxyView[] +} \ No newline at end of file diff --git a/frontend/src/api/proxyRegionView.ts b/frontend/src/api/proxyRegionView.ts new file mode 100644 index 0000000..613c5c9 --- /dev/null +++ b/frontend/src/api/proxyRegionView.ts @@ -0,0 +1,8 @@ +export default interface proxyRegionView { + id:number + code:string + name:string + zhName:string + region:string + aliveCount:number +} \ No newline at end of file diff --git a/frontend/src/api/proxyStatsView.ts b/frontend/src/api/proxyStatsView.ts new file mode 100644 index 0000000..3d51a14 --- /dev/null +++ b/frontend/src/api/proxyStatsView.ts @@ -0,0 +1,9 @@ +export default interface proxyStatsView { + total:number + regions:number + lastSyncAt:number + lastCheckAt:number + lastStatus:string + checking:boolean + syncing:boolean +} \ No newline at end of file diff --git a/frontend/src/api/proxySvc.ts b/frontend/src/api/proxySvc.ts new file mode 100644 index 0000000..1d7d8f9 --- /dev/null +++ b/frontend/src/api/proxySvc.ts @@ -0,0 +1,44 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type idDto from "./idDto"; +import type okView from "./okView"; +import type proxyKeySaveDto from "./proxyKeySaveDto"; +import type proxyKeyView from "./proxyKeyView"; +import type proxyListDto from "./proxyListDto"; +import type proxyListView from "./proxyListView"; +import type proxyRegionView from "./proxyRegionView"; +import type proxyStatsView from "./proxyStatsView"; +import type proxySyncView from "./proxySyncView"; + +export default class proxySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async checkNow(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "checkNow", undefined, options) + } + async createKey(dto:proxyKeySaveDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "createKey", dto, options) + } + async deleteKey(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "deleteKey", dto, options) + } + async list(dto:proxyListDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "list", dto, options) + } + async listKeys(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "listKeys", undefined, options) + } + async regions(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "regions", undefined, options) + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "stats", undefined, options) + } + async syncNow(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "syncNow", undefined, options) + } + async syncs(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "syncs", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/proxySyncView.ts b/frontend/src/api/proxySyncView.ts new file mode 100644 index 0000000..1f09eeb --- /dev/null +++ b/frontend/src/api/proxySyncView.ts @@ -0,0 +1,13 @@ +export default interface proxySyncView { + id:number + startedAt:number + finishedAt:number + status:string + source:string + total:number + added:number + removed:number + updated:number + detailJson:string + error:string +} \ No newline at end of file diff --git a/frontend/src/api/proxyView.ts b/frontend/src/api/proxyView.ts new file mode 100644 index 0000000..ed241d3 --- /dev/null +++ b/frontend/src/api/proxyView.ts @@ -0,0 +1,20 @@ +export default interface proxyView { + id:number + ip:string + port:number + address:string + protocols:string + anonymity:string + regionId:number + regionCode:string + regionName:string + regionZhName:string + latency:number + speed:number + uptime:number + alive:boolean + failCount:number + lastCheckedAt:number + lastAliveAt:number + lastSeenAt:number +} \ No newline at end of file diff --git a/frontend/src/api/recentMemoryView.ts b/frontend/src/api/recentMemoryView.ts new file mode 100644 index 0000000..9f05cc2 --- /dev/null +++ b/frontend/src/api/recentMemoryView.ts @@ -0,0 +1,5 @@ +export default interface recentMemoryView { + key:string + project:string + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/recentOrderView.ts b/frontend/src/api/recentOrderView.ts new file mode 100644 index 0000000..00dc655 --- /dev/null +++ b/frontend/src/api/recentOrderView.ts @@ -0,0 +1,6 @@ +export default interface recentOrderView { + tradeNo:string + subject:string + money:number + paidAt:number +} \ No newline at end of file diff --git a/frontend/src/api/saveBookmarkDto.ts b/frontend/src/api/saveBookmarkDto.ts new file mode 100644 index 0000000..83592d9 --- /dev/null +++ b/frontend/src/api/saveBookmarkDto.ts @@ -0,0 +1,7 @@ +export default interface saveBookmarkDto { + id?:number | null + title:string + url:string + icon?:string | null + sort?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/saveDbconnDto.ts b/frontend/src/api/saveDbconnDto.ts new file mode 100644 index 0000000..371446b --- /dev/null +++ b/frontend/src/api/saveDbconnDto.ts @@ -0,0 +1,11 @@ +export default interface saveDbconnDto { + id?:number | null + name:string + engine:string + host?:string | null + port?:number | null + username?:string | null + password?:string | null + database?:string | null + params?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/saveServerDto.ts b/frontend/src/api/saveServerDto.ts new file mode 100644 index 0000000..af22b4b --- /dev/null +++ b/frontend/src/api/saveServerDto.ts @@ -0,0 +1,10 @@ +export default interface saveServerDto { + id?:number | null + name:string + host:string + port?:number | null + username:string + authType:string + secret?:string | null + note?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/saveToolDto.ts b/frontend/src/api/saveToolDto.ts new file mode 100644 index 0000000..6a32aef --- /dev/null +++ b/frontend/src/api/saveToolDto.ts @@ -0,0 +1,9 @@ +export default interface saveToolDto { + id?:number | null + name:string + icon?:string | null + url:string + description?:string | null + sort?:number | null + enabled?:boolean | null +} \ No newline at end of file diff --git a/frontend/src/api/saveUserDto.ts b/frontend/src/api/saveUserDto.ts new file mode 100644 index 0000000..85d9442 --- /dev/null +++ b/frontend/src/api/saveUserDto.ts @@ -0,0 +1,7 @@ +export default interface saveUserDto { + id?:number | null + name:string + password?:string | null + role:string + status?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/saveVaultDto.ts b/frontend/src/api/saveVaultDto.ts new file mode 100644 index 0000000..23e6b0c --- /dev/null +++ b/frontend/src/api/saveVaultDto.ts @@ -0,0 +1,9 @@ +export default interface saveVaultDto { + id?:number | null + title:string + username?:string | null + password?:string | null + url?:string | null + note?:string | null + totpSecret?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/secretView.ts b/frontend/src/api/secretView.ts new file mode 100644 index 0000000..9908339 --- /dev/null +++ b/frontend/src/api/secretView.ts @@ -0,0 +1,3 @@ +export default interface secretView { + value:string +} \ No newline at end of file diff --git a/frontend/src/api/serverIdDto.ts b/frontend/src/api/serverIdDto.ts new file mode 100644 index 0000000..6de47e4 --- /dev/null +++ b/frontend/src/api/serverIdDto.ts @@ -0,0 +1,3 @@ +export default interface serverIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/serverMetricsView.ts b/frontend/src/api/serverMetricsView.ts new file mode 100644 index 0000000..d842fbd --- /dev/null +++ b/frontend/src/api/serverMetricsView.ts @@ -0,0 +1,25 @@ +import type diskMountView from "./diskMountView"; +export default interface serverMetricsView { + hostname:string + os:string + kernel:string + uptimeSeconds:number + cpuPercent:number + load1:string + load5:string + load15:string + memTotal:number + memUsed:number + memAvail:number + swapTotal:number + swapUsed:number + diskTotal:number + diskUsed:number + diskAvail:number + netRxRate:number + netTxRate:number + netRxTotal:number + netTxTotal:number + disks:diskMountView[] + collectedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/serverSvc.ts b/frontend/src/api/serverSvc.ts new file mode 100644 index 0000000..5f771b8 --- /dev/null +++ b/frontend/src/api/serverSvc.ts @@ -0,0 +1,40 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type saveServerDto from "./saveServerDto"; +import type serverIdDto from "./serverIdDto"; +import type serverMetricsView from "./serverMetricsView"; +import type serverView from "./serverView"; +import type sftpListView from "./sftpListView"; +import type sftpPathDto from "./sftpPathDto"; +import type sftpRenameDto from "./sftpRenameDto"; + +export default class serverSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:serverIdDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "list", undefined, options) + } + async metrics(dto:serverIdDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "metrics", dto, options) + } + async save(dto:saveServerDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "save", dto, options) + } + async sftpList(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpList", dto, options) + } + async sftpMkdir(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpMkdir", dto, options) + } + async sftpRemove(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpRemove", dto, options) + } + async sftpRename(dto:sftpRenameDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpRename", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/serverView.ts b/frontend/src/api/serverView.ts new file mode 100644 index 0000000..bf3e189 --- /dev/null +++ b/frontend/src/api/serverView.ts @@ -0,0 +1,11 @@ +export default interface serverView { + id:number + name:string + host:string + port:number + username:string + authType:string + hasSecret:boolean + note?:string | null + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/sessionView.ts b/frontend/src/api/sessionView.ts new file mode 100644 index 0000000..7a1b0ed --- /dev/null +++ b/frontend/src/api/sessionView.ts @@ -0,0 +1,5 @@ +import type userView from "./userView"; +export default interface sessionView { + user?:userView | null + token?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/settingDto.ts b/frontend/src/api/settingDto.ts new file mode 100644 index 0000000..46463d5 --- /dev/null +++ b/frontend/src/api/settingDto.ts @@ -0,0 +1,4 @@ +export default interface settingDto { + key:string + value:string +} \ No newline at end of file diff --git a/frontend/src/api/settingSvc.ts b/frontend/src/api/settingSvc.ts new file mode 100644 index 0000000..7f0fe3f --- /dev/null +++ b/frontend/src/api/settingSvc.ts @@ -0,0 +1,17 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type settingDto from "./settingDto"; +import type settingView from "./settingView"; + +export default class settingSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async get(dto:settingDto, options?: RequestOptions): Promise> { + return await this.client.request("settingSvc", "get", dto, options) + } + async set(dto:settingDto, options?: RequestOptions): Promise> { + return await this.client.request("settingSvc", "set", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/settingView.ts b/frontend/src/api/settingView.ts new file mode 100644 index 0000000..9bab4a1 --- /dev/null +++ b/frontend/src/api/settingView.ts @@ -0,0 +1,3 @@ +export default interface settingView { + value:string +} \ No newline at end of file diff --git a/frontend/src/api/sftpEntryView.ts b/frontend/src/api/sftpEntryView.ts new file mode 100644 index 0000000..1a467cd --- /dev/null +++ b/frontend/src/api/sftpEntryView.ts @@ -0,0 +1,7 @@ +export default interface sftpEntryView { + name:string + size:number + mode:string + isDir:boolean + modTime:number +} \ No newline at end of file diff --git a/frontend/src/api/sftpListView.ts b/frontend/src/api/sftpListView.ts new file mode 100644 index 0000000..ece61b2 --- /dev/null +++ b/frontend/src/api/sftpListView.ts @@ -0,0 +1,5 @@ +import type sftpEntryView from "./sftpEntryView"; +export default interface sftpListView { + path:string + entries:sftpEntryView[] +} \ No newline at end of file diff --git a/frontend/src/api/sftpPathDto.ts b/frontend/src/api/sftpPathDto.ts new file mode 100644 index 0000000..9ff76fd --- /dev/null +++ b/frontend/src/api/sftpPathDto.ts @@ -0,0 +1,4 @@ +export default interface sftpPathDto { + id:number + path:string +} \ No newline at end of file diff --git a/frontend/src/api/sftpRenameDto.ts b/frontend/src/api/sftpRenameDto.ts new file mode 100644 index 0000000..aed374d --- /dev/null +++ b/frontend/src/api/sftpRenameDto.ts @@ -0,0 +1,5 @@ +export default interface sftpRenameDto { + id:number + path:string + newName:string +} \ No newline at end of file diff --git a/frontend/src/api/shareView.ts b/frontend/src/api/shareView.ts new file mode 100644 index 0000000..eaee1d5 --- /dev/null +++ b/frontend/src/api/shareView.ts @@ -0,0 +1,9 @@ +export default interface shareView { + id:number + code:string + fileName:string + expiresAt:number + expired:boolean + downloads:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/statsView.ts b/frontend/src/api/statsView.ts new file mode 100644 index 0000000..c778dc7 --- /dev/null +++ b/frontend/src/api/statsView.ts @@ -0,0 +1,30 @@ +import type payTrendPoint from "./payTrendPoint"; +import type recentMemoryView from "./recentMemoryView"; +import type recentOrderView from "./recentOrderView"; +import type trendPoint from "./trendPoint"; +export default interface statsView { + totalUsers:number + activeSessions:number + todayLogins:number + uptimeSeconds:number + loginTrend:trendPoint[] + servers:number + proxies:number + aliveProxies:number + todayOrders:number + todayMoney:number + totalMoney:number + pendingOrders:number + licenseApps:number + licenseCards:number + licenseActive:number + memories:number + memoryProjects:number + bookmarks:number + vaultItems:number + dbconns:number + files:number + payTrend:payTrendPoint[] + recentOrders:recentOrderView[] + recentMemories:recentMemoryView[] +} \ No newline at end of file diff --git a/frontend/src/api/toolSvc.ts b/frontend/src/api/toolSvc.ts new file mode 100644 index 0000000..2c59efa --- /dev/null +++ b/frontend/src/api/toolSvc.ts @@ -0,0 +1,24 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type deleteToolDto from "./deleteToolDto"; +import type okView from "./okView"; +import type saveToolDto from "./saveToolDto"; +import type toolView from "./toolView"; + +export default class toolSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async adminList(options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "adminList", undefined, options) + } + async deleteTool(dto:deleteToolDto, options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "deleteTool", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "list", undefined, options) + } + async saveTool(dto:saveToolDto, options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "saveTool", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/toolView.ts b/frontend/src/api/toolView.ts new file mode 100644 index 0000000..6ad8559 --- /dev/null +++ b/frontend/src/api/toolView.ts @@ -0,0 +1,9 @@ +export default interface toolView { + id:number + name:string + icon:string + url:string + description:string + sort:number + enabled:boolean +} \ No newline at end of file diff --git a/frontend/src/api/totpView.ts b/frontend/src/api/totpView.ts new file mode 100644 index 0000000..530be9b --- /dev/null +++ b/frontend/src/api/totpView.ts @@ -0,0 +1,4 @@ +export default interface totpView { + code:string + secondsLeft:number +} \ No newline at end of file diff --git a/frontend/src/api/trendPoint.ts b/frontend/src/api/trendPoint.ts new file mode 100644 index 0000000..f4338eb --- /dev/null +++ b/frontend/src/api/trendPoint.ts @@ -0,0 +1,4 @@ +export default interface trendPoint { + day:string + count:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/authSvc.ts b/frontend/src/api/ts/authSvc.ts new file mode 100644 index 0000000..eeca801 --- /dev/null +++ b/frontend/src/api/ts/authSvc.ts @@ -0,0 +1,24 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type changePasswordDto from "./changePasswordDto"; +import type loginDto from "./loginDto"; +import type okView from "./okView"; +import type sessionView from "./sessionView"; + +export default class authSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async changePassword(dto:changePasswordDto, options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "changePassword", dto, options) + } + async login(dto:loginDto, options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "login", dto, options) + } + async logout(options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "logout", undefined, options) + } + async me(options?: RequestOptions): Promise> { + return await this.client.request("authSvc", "me", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/bookmarkIdDto.ts b/frontend/src/api/ts/bookmarkIdDto.ts new file mode 100644 index 0000000..fc4a4e1 --- /dev/null +++ b/frontend/src/api/ts/bookmarkIdDto.ts @@ -0,0 +1,3 @@ +export default interface bookmarkIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/bookmarkSvc.ts b/frontend/src/api/ts/bookmarkSvc.ts new file mode 100644 index 0000000..9505ba8 --- /dev/null +++ b/frontend/src/api/ts/bookmarkSvc.ts @@ -0,0 +1,21 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type bookmarkIdDto from "./bookmarkIdDto"; +import type bookmarkView from "./bookmarkView"; +import type okView from "./okView"; +import type saveBookmarkDto from "./saveBookmarkDto"; + +export default class bookmarkSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:bookmarkIdDto, options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "list", undefined, options) + } + async save(dto:saveBookmarkDto, options?: RequestOptions): Promise> { + return await this.client.request("bookmarkSvc", "save", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/bookmarkView.ts b/frontend/src/api/ts/bookmarkView.ts new file mode 100644 index 0000000..41913d1 --- /dev/null +++ b/frontend/src/api/ts/bookmarkView.ts @@ -0,0 +1,7 @@ +export default interface bookmarkView { + id:number + title:string + url:string + icon:string + sort:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/changePasswordDto.ts b/frontend/src/api/ts/changePasswordDto.ts new file mode 100644 index 0000000..ece2f50 --- /dev/null +++ b/frontend/src/api/ts/changePasswordDto.ts @@ -0,0 +1,4 @@ +export default interface changePasswordDto { + oldPassword:string + newPassword:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/client.ts b/frontend/src/api/ts/client.ts new file mode 100644 index 0000000..09077af --- /dev/null +++ b/frontend/src/api/ts/client.ts @@ -0,0 +1,665 @@ +// 大整数安全 JSON:超过 Number.MAX_SAFE_INTEGER(2^53-1)的整数字面量 +// 解析为 BigInt,序列化时 BigInt 还原为数字字面量——雪花 ID 等不再丢精度。 +// 自包含实现,不引入运行时依赖;如需换 json-bigint,替换下面两个函数即可 +const BIGINT_PREFIX = "\u0000fun-bigint:"; + +function reviveBigint(value: any): any { + if (typeof value === "string" && value.startsWith(BIGINT_PREFIX) && + /^-?\d+$/.test(value.slice(BIGINT_PREFIX.length))) { + return BigInt(value.slice(BIGINT_PREFIX.length)); + } + if (Array.isArray(value)) return value.map(reviveBigint); + if (value !== null && typeof value === "object") { + for (const key of Object.keys(value)) value[key] = reviveBigint(value[key]); + return value; + } + return value; +} + +function parseLossless(text: string): any { + // 先把超出安全范围的大整数包成带哨兵的字符串(正则的字符串分支优先, + // 字符串内容里的数字不受影响),JSON.parse 后再还原为 BigInt + const guarded = text.replace( + /"(?:[^"\\]|\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4}))*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g, + token => { + if (token.startsWith('"')) return token; + if (!/[.eE]/.test(token) && !Number.isSafeInteger(Number(token))) { + // 哨兵必须以转义形式 \\u0000 写入文本(JSON 字符串禁止裸控制字符), + // JSON.parse 解码后即为 BIGINT_PREFIX(真实 NUL 开头) + return '"\\u0000fun-bigint:' + token + '"'; + } + return token; + } + ); + return reviveBigint(JSON.parse(guarded)); +} + +function stringifyLossless(value: any): string | undefined { + const raw = JSON.stringify(value, (_key, v) => + typeof v === "bigint" ? BIGINT_PREFIX + v.toString() : v + ); + if (raw === undefined) return undefined; + // 哨兵字符串在序列化文本中形如 "\u0000fun-bigint:123"(NUL 被转义),去引号还原为数字字面量 + return raw.replace(/"\\u0000fun-bigint:(-?\d+)"/g, "$1"); +} + +export type result = { + code?: number; + data?: T; + msg?: string; + status: number; +}; + +export type resultStatus = 0 | 1 | 2 | 4 | 5; +// 0 success; 1 framework/client protocol error; 2 business error; 4 external request error; 5 external timeout + +export type RequestOptions = { + signal?: AbortSignal; + state?: Record; +}; + +export type StreamOptions = { + signal?: AbortSignal; + state?: Record; + /** + * 原生断线重连:流因传输失败(status 4/5)结束时按指数退避自动重连。 + * dto 传工厂函数时每次重连重新求值,用于刷新续传游标。 + * 未传时关闭(默认),与 request 语义一致。 + */ + retry?: StreamRetryOptions | false; + /** 空闲看门狗:超过该毫秒数未收到任何字节(含心跳空行)即判定流死亡并走重连。0 关闭。 */ + idleTimeoutMs?: number; +}; + +export type StreamRetryOptions = { + /** 最大重连次数,默认 Infinity(直到 signal 取消或成功)。 */ + maxAttempts?: number; + /** 首次重连延迟(毫秒),默认 500。 */ + baseDelayMs?: number; + /** 退避上限(毫秒),默认 10000。实际延迟为指数退避 × 0.75~1.25 抖动。 */ + maxDelayMs?: number; +}; + +export type RequestInterceptor = ( + serviceName: string, + methodName: string, + state: Record, + dto?: any +) => Promise | void; + +export type ResponseContext = { + readonly requestState: Readonly>; + readonly response?: Response; +}; + +export type ResponseInterceptor = ( + serviceName: string, + methodName: string, + result: result +) => Promise | void> | result | void; + +export type ContextResponseInterceptor = ( + serviceName: string, + methodName: string, + result: result, + context: ResponseContext +) => Promise | void> | result | void; + +function messageOf(error: unknown): string { + if (error instanceof Error && error.message) return error.message; + if (typeof error === "string" && error) return error; + return "unknown error"; +} + +function failure(status: resultStatus, msg: string): result { + return { status, msg }; +} + +function isTimeout(error: unknown, signal?: AbortSignal): boolean { + const errorName = error !== null && typeof error === "object" + ? (error as { name?: unknown }).name + : undefined; + const reason = signal?.reason; + const reasonName = reason !== null && typeof reason === "object" + ? (reason as { name?: unknown }).name + : undefined; + return errorName === "TimeoutError" || reasonName === "TimeoutError"; +} + +function requestFailure(error: unknown, signal: AbortSignal | undefined, stream: boolean): result { + if (isTimeout(error, signal)) { + return failure(5, stream ? "Stream timed out" : "Request timed out"); + } + if (signal?.aborted === true) { + return failure(4, stream ? "Stream aborted" : "Request aborted"); + } + const kind = stream ? "External stream request" : "External request"; + return failure(4, `${kind} failed: ${messageOf(error)}`); +} + +function isResult(value: unknown): value is result { + return value !== null && typeof value === "object" && + typeof (value as { status?: unknown }).status === "number"; +} + +function mediaType(response: Response): string { + return (response.headers.get("content-type") || "").split(";", 1)[0].trim().toLowerCase(); +} + +function excerpt(text: string, limit = 180): string { + const value = text.replace(/\s+/g, " ").trim(); + return value.length <= limit ? value : `${value.slice(0, limit)}...`; +} + +function externalFailure(response: Response, detail?: string): result { + const timeout = response.status === 408 || response.status === 504; + const statusText = response.statusText || (timeout ? "timeout" : "request failed"); + const suffix = detail ? `: ${detail}` : ""; + return failure(timeout ? 5 : 4, `HTTP ${response.status} ${statusText}${suffix}`); +} + +function responseReadFailure( + error: unknown, + response: Response, + signal: AbortSignal | undefined, + stream: boolean +): result { + if (isTimeout(error, signal)) { + return failure(5, stream ? "Stream timed out" : "Request timed out"); + } + if (signal?.aborted === true) { + return failure(4, stream ? "Stream aborted" : "Request aborted"); + } + const kind = stream ? "Stream" : "Response body"; + return response.ok + ? failure(4, `${kind} failed: ${messageOf(error)}`) + : externalFailure(response, `response body failed: ${messageOf(error)}`); +} + +function parseResult(response: Response, text: string): result { + const body = text.trim(); + if (!body) { + return response.ok + ? failure(1, "Empty response body") + : externalFailure(response); + } + + let value: unknown; + try { + value = parseLossless(body); + } catch { + if (!response.ok) return externalFailure(response, excerpt(body)); + const type = mediaType(response); + if (type === "text/html" || /^\s*(?: = {}; + private requestInterceptors: RequestInterceptor[] = []; + private responseInterceptors: ContextResponseInterceptor[] = []; + + constructor(url: string) { + this.url = url.replace(/\/+$/, ""); + } + + setState(state: Record) { + this.state = state; + } + + addRequestInterceptor(interceptor: RequestInterceptor) { + this.requestInterceptors.push(interceptor); + } + + addResponseInterceptor(interceptor: ResponseInterceptor): void; + addResponseInterceptor(interceptor: ContextResponseInterceptor): void; + addResponseInterceptor(interceptor: ResponseInterceptor | ContextResponseInterceptor) { + this.responseInterceptors.push(interceptor as ContextResponseInterceptor); + } + + private async interceptResponse( + serviceName: string, + methodName: string, + initial: result, + requestState: Readonly>, + response?: Response + ): Promise> { + let current = initial; + const context: ResponseContext = Object.freeze( + response === undefined ? { requestState } : { requestState, response } + ); + for (const interceptor of this.responseInterceptors) { + try { + const replaced = await interceptor(serviceName, methodName, current, context); + if (replaced) current = replaced; + } catch (error) { + current = failure(1, `Response interceptor failed: ${messageOf(error)}`); + } + } + return current; + } + + private async interceptRequest( + serviceName: string, + methodName: string, + state: Record, + dto: any + ): Promise { + for (const interceptor of this.requestInterceptors) { + await interceptor(serviceName, methodName, state, dto); + } + } + + private snapshotState(state: Record): Readonly> { + return Object.freeze({ ...state }); + } + + async request( + serviceName: string, + methodName: string, + dto?: any, + options?: RequestOptions + ): Promise> { + let state: Record; + try { + state = { ...this.state, ...options?.state }; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not prepare request state: ${messageOf(error)}`), + Object.freeze({}) + ) as result; + } + try { + await this.interceptRequest(serviceName, methodName, state, dto); + } catch (error) { + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (snapshotError) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(snapshotError)}`), + Object.freeze({}) + ) as result; + } + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Request interceptor failed: ${messageOf(error)}`), + requestState + ) as result; + } + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(error)}`), + Object.freeze({}) + ) as result; + } + + let body: string; + try { + const serialized = stringifyLossless({ + serviceName, + methodName, + data: dto, + ...(Object.keys(requestState).length ? { state: requestState } : {}), + }); + if (serialized === undefined) throw new Error("serialization produced no output"); + body = serialized; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not serialize request: ${messageOf(error)}`), + requestState + ) as result; + } + + let output: result; + let response: Response | undefined; + try { + response = await fetch(`${this.url}/cell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + signal: options?.signal, + }); + try { + output = parseResult(response, await response.text()); + } catch (error) { + output = responseReadFailure(error, response, options?.signal, false); + } + } catch (error) { + output = requestFailure(error, options?.signal, false); + } + return await this.interceptResponse(serviceName, methodName, output, requestState, response) as result; + } + + async stream( + serviceName: string, + methodName: string, + dto: any | undefined | (() => any | undefined), + onMessage: (data: T) => unknown, + options?: StreamOptions + ): Promise> { + // dto 工厂:重连时重新求值,用于刷新续传游标(如 afterId) + const getDto = typeof dto === "function" ? (dto as () => any | undefined) : () => dto; + // 重连默认关闭(与 request 语义一致);传 retry: {...} 显式开启 + const retry = options?.retry + ? { + maxAttempts: Infinity, + baseDelayMs: 500, + maxDelayMs: 10000, + ...options.retry, + } + : null; + for (let attempt = 0; ; attempt++) { + const r = await this.streamOnce(serviceName, methodName, getDto(), onMessage, options); + // 0 成功、1 协议错误、2 业务错误:终结态,不重连 + if (r.status !== 4 && r.status !== 5) return r; + if (!retry) return r; + if (options?.signal?.aborted) return r; + if (attempt + 1 > retry.maxAttempts) return r; + const delay = + Math.min(retry.maxDelayMs, retry.baseDelayMs * 2 ** attempt) * (0.75 + Math.random() * 0.5); + // 退避等待:AbortSignal.timeout 免定时器 API,且后台标签页照常走时 + await new Promise((resolve) => { + if (options?.signal?.aborted) { + resolve(); + return; + } + AbortSignal.timeout(delay).addEventListener("abort", () => resolve(), { once: true }); + options?.signal?.addEventListener("abort", () => resolve(), { once: true }); + }); + if (options?.signal?.aborted) return r; + } + } + + private async streamOnce( + serviceName: string, + methodName: string, + dto: any | undefined, + onMessage: (data: T) => unknown, + options?: StreamOptions + ): Promise> { + let state: Record; + try { + state = { ...this.state, ...options?.state }; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not prepare request state: ${messageOf(error)}`), + Object.freeze({}) + ); + } + try { + await this.interceptRequest(serviceName, methodName, state, dto); + } catch (error) { + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (snapshotError) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(snapshotError)}`), + Object.freeze({}) + ); + } + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Request interceptor failed: ${messageOf(error)}`), + requestState + ); + } + let requestState: Readonly>; + try { + requestState = this.snapshotState(state); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not snapshot request state: ${messageOf(error)}`), + Object.freeze({}) + ); + } + + let body: string; + try { + const serialized = stringifyLossless({ + serviceName, + methodName, + data: dto, + ...(Object.keys(requestState).length ? { state: requestState } : {}), + }); + if (serialized === undefined) throw new Error("serialization produced no output"); + body = serialized; + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + failure(1, `Could not serialize request: ${messageOf(error)}`), + requestState + ); + } + + let response: Response; + try { + response = await fetch(`${this.url}/cell`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body, + signal: options?.signal, + }); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + requestFailure(error, options?.signal, true), + requestState + ); + } + + if (!response.ok) { + let text: string; + try { + text = await response.text(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + return await this.interceptResponse( + serviceName, + methodName, + parseResult(response, text), + requestState, + response + ); + } + + if (mediaType(response) !== "application/x-ndjson") { + let text: string; + try { + text = await response.text(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + const rpcResult = parseResult(response, text); + return await this.interceptResponse( + serviceName, + methodName, + rpcResult.status === 0 + ? failure(1, "Expected application/x-ndjson response") + : rpcResult, + requestState, + response + ); + } + + if (!response.body) { + return await this.interceptResponse( + serviceName, + methodName, + { status: 0 }, + requestState, + response + ); + } + + let reader: ReadableStreamDefaultReader; + try { + reader = response.body.getReader(); + } catch (error) { + return await this.interceptResponse( + serviceName, + methodName, + responseReadFailure(error, response, options?.signal, true), + requestState, + response + ); + } + const decoder = new TextDecoder("utf-8", { fatal: true }); + let buffer = ""; + let lineNumber = 0; + let failed: result | undefined; + let cause: unknown; + + const emitLine = async (line: string) => { + const payload = line.replace(/\r$/, "").trim(); + if (!payload) return; + let data: T; + try { + data = parseLossless(payload) as T; + } catch (error) { + failed = failure(1, `Invalid NDJSON at line ${lineNumber}: ${excerpt(payload)}`); + cause = error; + return; + } + try { + await onMessage(data); + } catch (error) { + failed = failure(1, `Stream callback failed: ${messageOf(error)}`); + cause = error; + } + }; + + try { + // 空闲看门狗:idleTimeoutMs 内未收到任何字节(含心跳空行)判定流死亡, + // 抛出哨兵错误 → status 5 → 交给上层重连。服务端 25s 心跳时建议 35000。 + const STREAM_IDLE = Symbol("stream-idle"); + for (;;) { + let part: ReadableStreamReadResult; + try { + if (options?.idleTimeoutMs) { + const idlePromise = new Promise((_, reject) => { + AbortSignal.timeout(options.idleTimeoutMs!).addEventListener( + "abort", + () => reject(STREAM_IDLE), + { once: true } + ); + }); + idlePromise.catch(() => {}); // read 先返回时防 unhandled rejection + part = await Promise.race([reader.read(), idlePromise]); + } else { + part = await reader.read(); + } + } catch (error) { + cause = error; + if (error === STREAM_IDLE) { + failed = failure(5, "Stream idle timeout"); + } else if (isTimeout(error, options?.signal)) { + failed = failure(5, "Stream timed out"); + } else if (options?.signal?.aborted === true) { + failed = failure(4, "Stream aborted"); + } else { + failed = failure(4, `Stream read failed: ${messageOf(error)}`); + } + break; + } + if (part.done) break; + try { + buffer += decoder.decode(part.value, { stream: true }); + } catch (error) { + failed = failure(1, `Invalid UTF-8 stream data: ${messageOf(error)}`); + cause = error; + break; + } + for (;;) { + const newline = buffer.indexOf("\n"); + if (newline < 0) break; + const line = buffer.slice(0, newline); + buffer = buffer.slice(newline + 1); + lineNumber++; + await emitLine(line); + if (failed) break; + } + if (failed) break; + } + + if (!failed) { + try { + buffer += decoder.decode(); + } catch (error) { + failed = failure(1, `Invalid UTF-8 stream data: ${messageOf(error)}`); + cause = error; + } + } + if (!failed && buffer.length > 0) { + lineNumber++; + await emitLine(buffer); + } + + } finally { + if (failed) { + try { + await reader.cancel(cause); + } catch { + // The reader may already be closed by the runtime. + } + } + try { + reader.releaseLock(); + } catch { + // The reader may already be errored or released. + } + } + return await this.interceptResponse( + serviceName, + methodName, + failed || { status: 0 }, + requestState, + response + ); + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/createShareDto.ts b/frontend/src/api/ts/createShareDto.ts new file mode 100644 index 0000000..5d110a8 --- /dev/null +++ b/frontend/src/api/ts/createShareDto.ts @@ -0,0 +1,4 @@ +export default interface createShareDto { + fileId:number + expireMinutes:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/dashboardSvc.ts b/frontend/src/api/ts/dashboardSvc.ts new file mode 100644 index 0000000..df6e279 --- /dev/null +++ b/frontend/src/api/ts/dashboardSvc.ts @@ -0,0 +1,12 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type statsView from "./statsView"; + +export default class dashboardSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("dashboardSvc", "stats", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbColumnView.ts b/frontend/src/api/ts/dbColumnView.ts new file mode 100644 index 0000000..2313a4d --- /dev/null +++ b/frontend/src/api/ts/dbColumnView.ts @@ -0,0 +1,6 @@ +export default interface dbColumnView { + table:string + name:string + type:string + pk:boolean +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbDatabasesView.ts b/frontend/src/api/ts/dbDatabasesView.ts new file mode 100644 index 0000000..ba7630c --- /dev/null +++ b/frontend/src/api/ts/dbDatabasesView.ts @@ -0,0 +1,3 @@ +export default interface dbDatabasesView { + names:string[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbMgrSvc.ts b/frontend/src/api/ts/dbMgrSvc.ts new file mode 100644 index 0000000..33c1063 --- /dev/null +++ b/frontend/src/api/ts/dbMgrSvc.ts @@ -0,0 +1,38 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type dbDatabasesView from "./dbDatabasesView"; +import type dbQueryDto from "./dbQueryDto"; +import type dbQueryView from "./dbQueryView"; +import type dbScopeDto from "./dbScopeDto"; +import type dbTablesView from "./dbTablesView"; +import type dbconnIdDto from "./dbconnIdDto"; +import type dbconnView from "./dbconnView"; +import type okView from "./okView"; +import type saveDbconnDto from "./saveDbconnDto"; + +export default class dbMgrSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async databases(dto:dbScopeDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "databases", dto, options) + } + async delete(dto:dbconnIdDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "list", undefined, options) + } + async query(dto:dbQueryDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "query", dto, options) + } + async save(dto:saveDbconnDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "save", dto, options) + } + async tables(dto:dbScopeDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "tables", dto, options) + } + async test(dto:dbconnIdDto, options?: RequestOptions): Promise> { + return await this.client.request("dbMgrSvc", "test", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbQueryDto.ts b/frontend/src/api/ts/dbQueryDto.ts new file mode 100644 index 0000000..0298521 --- /dev/null +++ b/frontend/src/api/ts/dbQueryDto.ts @@ -0,0 +1,6 @@ +export default interface dbQueryDto { + id:number + database?:string | null + sql:string + maxRows?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbQueryView.ts b/frontend/src/api/ts/dbQueryView.ts new file mode 100644 index 0000000..df39f49 --- /dev/null +++ b/frontend/src/api/ts/dbQueryView.ts @@ -0,0 +1,9 @@ +export default interface dbQueryView { + isSelect:boolean + columns:string[] + rowsJson:string + rowCount:number + affectedRows:number + truncated:boolean + elapsedMs:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbScopeDto.ts b/frontend/src/api/ts/dbScopeDto.ts new file mode 100644 index 0000000..6dfe44a --- /dev/null +++ b/frontend/src/api/ts/dbScopeDto.ts @@ -0,0 +1,4 @@ +export default interface dbScopeDto { + id:number + database?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbTableView.ts b/frontend/src/api/ts/dbTableView.ts new file mode 100644 index 0000000..b56b145 --- /dev/null +++ b/frontend/src/api/ts/dbTableView.ts @@ -0,0 +1,4 @@ +export default interface dbTableView { + name:string + type:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbTablesView.ts b/frontend/src/api/ts/dbTablesView.ts new file mode 100644 index 0000000..e3aad2a --- /dev/null +++ b/frontend/src/api/ts/dbTablesView.ts @@ -0,0 +1,6 @@ +import type dbColumnView from "./dbColumnView"; +import type dbTableView from "./dbTableView"; +export default interface dbTablesView { + tables:dbTableView[] + columns:dbColumnView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbconnIdDto.ts b/frontend/src/api/ts/dbconnIdDto.ts new file mode 100644 index 0000000..9058dc8 --- /dev/null +++ b/frontend/src/api/ts/dbconnIdDto.ts @@ -0,0 +1,3 @@ +export default interface dbconnIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/dbconnView.ts b/frontend/src/api/ts/dbconnView.ts new file mode 100644 index 0000000..460bf9f --- /dev/null +++ b/frontend/src/api/ts/dbconnView.ts @@ -0,0 +1,11 @@ +export default interface dbconnView { + id:number + name:string + engine:string + host:string + port:number + username:string + database:string + hasPassword:boolean + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/deleteToolDto.ts b/frontend/src/api/ts/deleteToolDto.ts new file mode 100644 index 0000000..7f165f7 --- /dev/null +++ b/frontend/src/api/ts/deleteToolDto.ts @@ -0,0 +1,3 @@ +export default interface deleteToolDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/deleteUserDto.ts b/frontend/src/api/ts/deleteUserDto.ts new file mode 100644 index 0000000..46ae2aa --- /dev/null +++ b/frontend/src/api/ts/deleteUserDto.ts @@ -0,0 +1,3 @@ +export default interface deleteUserDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/diskMountView.ts b/frontend/src/api/ts/diskMountView.ts new file mode 100644 index 0000000..5471301 --- /dev/null +++ b/frontend/src/api/ts/diskMountView.ts @@ -0,0 +1,8 @@ +export default interface diskMountView { + mount:string + fs:string + total:number + used:number + avail:number + percent:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/diskSvc.ts b/frontend/src/api/ts/diskSvc.ts new file mode 100644 index 0000000..3f81cd5 --- /dev/null +++ b/frontend/src/api/ts/diskSvc.ts @@ -0,0 +1,28 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type createShareDto from "./createShareDto"; +import type fileView from "./fileView"; +import type idDto from "./idDto"; +import type okView from "./okView"; +import type shareView from "./shareView"; + +export default class diskSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async createShare(dto:createShareDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "createShare", dto, options) + } + async deleteFile(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "deleteFile", dto, options) + } + async deleteShare(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "deleteShare", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "list", undefined, options) + } + async listShares(options?: RequestOptions): Promise> { + return await this.client.request("diskSvc", "listShares", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/fileView.ts b/frontend/src/api/ts/fileView.ts new file mode 100644 index 0000000..020f18d --- /dev/null +++ b/frontend/src/api/ts/fileView.ts @@ -0,0 +1,7 @@ +export default interface fileView { + id:number + name:string + size:number + mime:string + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/fun.ts b/frontend/src/api/ts/fun.ts new file mode 100644 index 0000000..f016894 --- /dev/null +++ b/frontend/src/api/ts/fun.ts @@ -0,0 +1,43 @@ +import { Client } from "./client"; +import authSvc from "./authSvc"; +import bookmarkSvc from "./bookmarkSvc"; +import dashboardSvc from "./dashboardSvc"; +import dbMgrSvc from "./dbMgrSvc"; +import diskSvc from "./diskSvc"; +import licenseSvc from "./licenseSvc"; +import memorySvc from "./memorySvc"; +import paySvc from "./paySvc"; +import proxySvc from "./proxySvc"; +import serverSvc from "./serverSvc"; +import settingSvc from "./settingSvc"; +import toolSvc from "./toolSvc"; +import userSvc from "./userSvc"; +import vaultSvc from "./vaultSvc"; +import webSvc from "./webSvc"; + +export class defaultApi extends Client { + constructor(url: string) { + super(url); + } + public authSvc: authSvc = new authSvc(this); + public bookmarkSvc: bookmarkSvc = new bookmarkSvc(this); + public dashboardSvc: dashboardSvc = new dashboardSvc(this); + public dbMgrSvc: dbMgrSvc = new dbMgrSvc(this); + public diskSvc: diskSvc = new diskSvc(this); + public licenseSvc: licenseSvc = new licenseSvc(this); + public memorySvc: memorySvc = new memorySvc(this); + public paySvc: paySvc = new paySvc(this); + public proxySvc: proxySvc = new proxySvc(this); + public serverSvc: serverSvc = new serverSvc(this); + public settingSvc: settingSvc = new settingSvc(this); + public toolSvc: toolSvc = new toolSvc(this); + public userSvc: userSvc = new userSvc(this); + public vaultSvc: vaultSvc = new vaultSvc(this); + public webSvc: webSvc = new webSvc(this); +} + +export default class api { + static create(url: string): defaultApi { + return new defaultApi(url); + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/idDto.ts b/frontend/src/api/ts/idDto.ts new file mode 100644 index 0000000..f69ce11 --- /dev/null +++ b/frontend/src/api/ts/idDto.ts @@ -0,0 +1,3 @@ +export default interface idDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseAppSaveDto.ts b/frontend/src/api/ts/licenseAppSaveDto.ts new file mode 100644 index 0000000..52277b2 --- /dev/null +++ b/frontend/src/api/ts/licenseAppSaveDto.ts @@ -0,0 +1,5 @@ +export default interface licenseAppSaveDto { + id:number + name:string + enabled?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseAppView.ts b/frontend/src/api/ts/licenseAppView.ts new file mode 100644 index 0000000..b7d4748 --- /dev/null +++ b/frontend/src/api/ts/licenseAppView.ts @@ -0,0 +1,8 @@ +export default interface licenseAppView { + id:number + appid:string + name:string + enabled:number + cardCount:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseAppsView.ts b/frontend/src/api/ts/licenseAppsView.ts new file mode 100644 index 0000000..d176362 --- /dev/null +++ b/frontend/src/api/ts/licenseAppsView.ts @@ -0,0 +1,4 @@ +import type licenseAppView from "./licenseAppView"; +export default interface licenseAppsView { + items:licenseAppView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseCardView.ts b/frontend/src/api/ts/licenseCardView.ts new file mode 100644 index 0000000..d440cea --- /dev/null +++ b/frontend/src/api/ts/licenseCardView.ts @@ -0,0 +1,10 @@ +export default interface licenseCardView { + id:number + card:string + hours:number + domain:string + status:number + activatedAt:number + expiresAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseGenDto.ts b/frontend/src/api/ts/licenseGenDto.ts new file mode 100644 index 0000000..0bb7e32 --- /dev/null +++ b/frontend/src/api/ts/licenseGenDto.ts @@ -0,0 +1,5 @@ +export default interface licenseGenDto { + appId:number + count:number + hours:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseGenView.ts b/frontend/src/api/ts/licenseGenView.ts new file mode 100644 index 0000000..c07d4b1 --- /dev/null +++ b/frontend/src/api/ts/licenseGenView.ts @@ -0,0 +1,3 @@ +export default interface licenseGenView { + cards:string[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseIdDto.ts b/frontend/src/api/ts/licenseIdDto.ts new file mode 100644 index 0000000..906a6a1 --- /dev/null +++ b/frontend/src/api/ts/licenseIdDto.ts @@ -0,0 +1,3 @@ +export default interface licenseIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseListDto.ts b/frontend/src/api/ts/licenseListDto.ts new file mode 100644 index 0000000..d24da9d --- /dev/null +++ b/frontend/src/api/ts/licenseListDto.ts @@ -0,0 +1,6 @@ +export default interface licenseListDto { + appId:number + page:number + pageSize:number + kw?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/licensePageView.ts b/frontend/src/api/ts/licensePageView.ts new file mode 100644 index 0000000..5d28f99 --- /dev/null +++ b/frontend/src/api/ts/licensePageView.ts @@ -0,0 +1,5 @@ +import type licenseCardView from "./licenseCardView"; +export default interface licensePageView { + total:number + items:licenseCardView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseStatsDto.ts b/frontend/src/api/ts/licenseStatsDto.ts new file mode 100644 index 0000000..65ce456 --- /dev/null +++ b/frontend/src/api/ts/licenseStatsDto.ts @@ -0,0 +1,3 @@ +export default interface licenseStatsDto { + appId:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseStatsView.ts b/frontend/src/api/ts/licenseStatsView.ts new file mode 100644 index 0000000..35f3496 --- /dev/null +++ b/frontend/src/api/ts/licenseStatsView.ts @@ -0,0 +1,6 @@ +export default interface licenseStatsView { + total:number + unused:number + active:number + expired:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/licenseSvc.ts b/frontend/src/api/ts/licenseSvc.ts new file mode 100644 index 0000000..cf43aca --- /dev/null +++ b/frontend/src/api/ts/licenseSvc.ts @@ -0,0 +1,40 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type licenseAppSaveDto from "./licenseAppSaveDto"; +import type licenseAppView from "./licenseAppView"; +import type licenseAppsView from "./licenseAppsView"; +import type licenseGenDto from "./licenseGenDto"; +import type licenseGenView from "./licenseGenView"; +import type licenseIdDto from "./licenseIdDto"; +import type licenseListDto from "./licenseListDto"; +import type licensePageView from "./licensePageView"; +import type licenseStatsDto from "./licenseStatsDto"; +import type licenseStatsView from "./licenseStatsView"; +import type okView from "./okView"; + +export default class licenseSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async apps(options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "apps", undefined, options) + } + async delete(dto:licenseIdDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "delete", dto, options) + } + async deleteApp(dto:licenseIdDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "deleteApp", dto, options) + } + async generate(dto:licenseGenDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "generate", dto, options) + } + async list(dto:licenseListDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "list", dto, options) + } + async saveApp(dto:licenseAppSaveDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "saveApp", dto, options) + } + async stats(dto:licenseStatsDto, options?: RequestOptions): Promise> { + return await this.client.request("licenseSvc", "stats", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/listUsersDto.ts b/frontend/src/api/ts/listUsersDto.ts new file mode 100644 index 0000000..201d57d --- /dev/null +++ b/frontend/src/api/ts/listUsersDto.ts @@ -0,0 +1,5 @@ +export default interface listUsersDto { + page:number + pageSize:number + keyword?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/loginDto.ts b/frontend/src/api/ts/loginDto.ts new file mode 100644 index 0000000..9fc8857 --- /dev/null +++ b/frontend/src/api/ts/loginDto.ts @@ -0,0 +1,4 @@ +export default interface loginDto { + name:string + password:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryKeyDto.ts b/frontend/src/api/ts/memoryKeyDto.ts new file mode 100644 index 0000000..3f12499 --- /dev/null +++ b/frontend/src/api/ts/memoryKeyDto.ts @@ -0,0 +1,3 @@ +export default interface memoryKeyDto { + key:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryListDto.ts b/frontend/src/api/ts/memoryListDto.ts new file mode 100644 index 0000000..f331c2a --- /dev/null +++ b/frontend/src/api/ts/memoryListDto.ts @@ -0,0 +1,6 @@ +export default interface memoryListDto { + project:string + tag:string + query:string + limit:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryListView.ts b/frontend/src/api/ts/memoryListView.ts new file mode 100644 index 0000000..df96fe0 --- /dev/null +++ b/frontend/src/api/ts/memoryListView.ts @@ -0,0 +1,4 @@ +import type memoryMetaView from "./memoryMetaView"; +export default interface memoryListView { + items:memoryMetaView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryMcpView.ts b/frontend/src/api/ts/memoryMcpView.ts new file mode 100644 index 0000000..07bf2df --- /dev/null +++ b/frontend/src/api/ts/memoryMcpView.ts @@ -0,0 +1,4 @@ +export default interface memoryMcpView { + url:string + token:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryMetaView.ts b/frontend/src/api/ts/memoryMetaView.ts new file mode 100644 index 0000000..502e85a --- /dev/null +++ b/frontend/src/api/ts/memoryMetaView.ts @@ -0,0 +1,8 @@ +export default interface memoryMetaView { + key:string + project:string + tags:string[] + writtenBy:string + revision:number + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryProjectView.ts b/frontend/src/api/ts/memoryProjectView.ts new file mode 100644 index 0000000..15d8e59 --- /dev/null +++ b/frontend/src/api/ts/memoryProjectView.ts @@ -0,0 +1,4 @@ +export default interface memoryProjectView { + name:string + count:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryProjectsView.ts b/frontend/src/api/ts/memoryProjectsView.ts new file mode 100644 index 0000000..f6ede5d --- /dev/null +++ b/frontend/src/api/ts/memoryProjectsView.ts @@ -0,0 +1,4 @@ +import type memoryProjectView from "./memoryProjectView"; +export default interface memoryProjectsView { + items:memoryProjectView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/memorySaveDto.ts b/frontend/src/api/ts/memorySaveDto.ts new file mode 100644 index 0000000..141a4d3 --- /dev/null +++ b/frontend/src/api/ts/memorySaveDto.ts @@ -0,0 +1,6 @@ +export default interface memorySaveDto { + key:string + content:string + project:string + tags:string[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/memorySaveView.ts b/frontend/src/api/ts/memorySaveView.ts new file mode 100644 index 0000000..7e9a666 --- /dev/null +++ b/frontend/src/api/ts/memorySaveView.ts @@ -0,0 +1,5 @@ +export default interface memorySaveView { + key:string + url:string + revision:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/memorySvc.ts b/frontend/src/api/ts/memorySvc.ts new file mode 100644 index 0000000..b546d0e --- /dev/null +++ b/frontend/src/api/ts/memorySvc.ts @@ -0,0 +1,38 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type memoryKeyDto from "./memoryKeyDto"; +import type memoryListDto from "./memoryListDto"; +import type memoryListView from "./memoryListView"; +import type memoryMcpView from "./memoryMcpView"; +import type memoryProjectsView from "./memoryProjectsView"; +import type memorySaveDto from "./memorySaveDto"; +import type memorySaveView from "./memorySaveView"; +import type memoryView from "./memoryView"; +import type okView from "./okView"; + +export default class memorySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:memoryKeyDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "delete", dto, options) + } + async get(dto:memoryKeyDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "get", dto, options) + } + async list(dto:memoryListDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "list", dto, options) + } + async mcpInfo(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "mcpInfo", undefined, options) + } + async projects(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "projects", undefined, options) + } + async resetToken(options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "resetToken", undefined, options) + } + async save(dto:memorySaveDto, options?: RequestOptions): Promise> { + return await this.client.request("memorySvc", "save", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/memoryView.ts b/frontend/src/api/ts/memoryView.ts new file mode 100644 index 0000000..55784d5 --- /dev/null +++ b/frontend/src/api/ts/memoryView.ts @@ -0,0 +1,11 @@ +export default interface memoryView { + key:string + url:string + content:string + project:string + tags:string[] + writtenBy:string + revision:number + createdAt:number + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/okView.ts b/frontend/src/api/ts/okView.ts new file mode 100644 index 0000000..3f1f6ee --- /dev/null +++ b/frontend/src/api/ts/okView.ts @@ -0,0 +1,3 @@ +export default interface okView { + message:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payCashierDto.ts b/frontend/src/api/ts/payCashierDto.ts new file mode 100644 index 0000000..453c260 --- /dev/null +++ b/frontend/src/api/ts/payCashierDto.ts @@ -0,0 +1,3 @@ +export default interface payCashierDto { + tradeNo:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payCashierStatusView.ts b/frontend/src/api/ts/payCashierStatusView.ts new file mode 100644 index 0000000..82fb580 --- /dev/null +++ b/frontend/src/api/ts/payCashierStatusView.ts @@ -0,0 +1,4 @@ +export default interface payCashierStatusView { + status:number + returnUrl:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payCashierView.ts b/frontend/src/api/ts/payCashierView.ts new file mode 100644 index 0000000..29f571a --- /dev/null +++ b/frontend/src/api/ts/payCashierView.ts @@ -0,0 +1,13 @@ +export default interface payCashierView { + tradeNo:string + outTradeNo:string + subject:string + money:number + channel:string + status:number + payurl:string + qrcode:string + cards:string + createdAt:number + expiredAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/payOrderListDto.ts b/frontend/src/api/ts/payOrderListDto.ts new file mode 100644 index 0000000..dc14713 --- /dev/null +++ b/frontend/src/api/ts/payOrderListDto.ts @@ -0,0 +1,6 @@ +export default interface payOrderListDto { + page:number + pageSize:number + status?:number | null + kw?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/payOrderPageView.ts b/frontend/src/api/ts/payOrderPageView.ts new file mode 100644 index 0000000..7995104 --- /dev/null +++ b/frontend/src/api/ts/payOrderPageView.ts @@ -0,0 +1,5 @@ +import type payOrderView from "./payOrderView"; +export default interface payOrderPageView { + total:number + items:payOrderView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/payOrderView.ts b/frontend/src/api/ts/payOrderView.ts new file mode 100644 index 0000000..9f59c23 --- /dev/null +++ b/frontend/src/api/ts/payOrderView.ts @@ -0,0 +1,14 @@ +export default interface payOrderView { + id:number + tradeNo:string + outTradeNo:string + channel:string + subject:string + money:number + status:number + notified:number + payurl:string + paidAt:number + expiredAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/payStatsView.ts b/frontend/src/api/ts/payStatsView.ts new file mode 100644 index 0000000..aca80cb --- /dev/null +++ b/frontend/src/api/ts/payStatsView.ts @@ -0,0 +1,9 @@ +export default interface payStatsView { + todayCount:number + todayMoney:number + totalCount:number + totalMoney:number + pendingCount:number + todayPaidCount:number + totalPaidCount:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/paySvc.ts b/frontend/src/api/ts/paySvc.ts new file mode 100644 index 0000000..75cec01 --- /dev/null +++ b/frontend/src/api/ts/paySvc.ts @@ -0,0 +1,42 @@ +import { Client, type result, type RequestOptions, type StreamOptions } from "./client"; +import type payCashierDto from "./payCashierDto"; +import type payCashierStatusView from "./payCashierStatusView"; +import type payCashierView from "./payCashierView"; +import type payOrderListDto from "./payOrderListDto"; +import type payOrderPageView from "./payOrderPageView"; +import type payStatsView from "./payStatsView"; +import type payTestPayDto from "./payTestPayDto"; +import type payTestPayView from "./payTestPayView"; +import type payUpstreamDto from "./payUpstreamDto"; +import type payUpstreamStatusView from "./payUpstreamStatusView"; + +export default class paySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async cashierInfo(dto:payCashierDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "cashierInfo", dto, options) + } + async cashierStatus(dto:payCashierDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "cashierStatus", dto, options) + } + async list(dto:payOrderListDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "list", dto, options) + } + async saveUpstream(dto:payUpstreamDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "saveUpstream", dto, options) + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "stats", undefined, options) + } + async testPay(dto:payTestPayDto, options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "testPay", dto, options) + } + async upstreamStatus(options?: RequestOptions): Promise> { + return await this.client.request("paySvc", "upstreamStatus", undefined, options) + } + async watch(dto:payCashierDto | (() => payCashierDto), onMessage: (data: payCashierStatusView) => unknown, options?: StreamOptions): Promise> { + return await this.client.stream("paySvc", "watch", dto, onMessage, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/payTestPayDto.ts b/frontend/src/api/ts/payTestPayDto.ts new file mode 100644 index 0000000..ccc77a3 --- /dev/null +++ b/frontend/src/api/ts/payTestPayDto.ts @@ -0,0 +1,3 @@ +export default interface payTestPayDto { + money:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payTestPayView.ts b/frontend/src/api/ts/payTestPayView.ts new file mode 100644 index 0000000..8dd173e --- /dev/null +++ b/frontend/src/api/ts/payTestPayView.ts @@ -0,0 +1,7 @@ +export default interface payTestPayView { + tradeNo:string + payurl:string + qrcode:string + money:number + cashier:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payTrendPoint.ts b/frontend/src/api/ts/payTrendPoint.ts new file mode 100644 index 0000000..68571b5 --- /dev/null +++ b/frontend/src/api/ts/payTrendPoint.ts @@ -0,0 +1,5 @@ +export default interface payTrendPoint { + day:string + money:number + count:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/payUpstreamDto.ts b/frontend/src/api/ts/payUpstreamDto.ts new file mode 100644 index 0000000..312b862 --- /dev/null +++ b/frontend/src/api/ts/payUpstreamDto.ts @@ -0,0 +1,4 @@ +export default interface payUpstreamDto { + username:string + password:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/payUpstreamStatusView.ts b/frontend/src/api/ts/payUpstreamStatusView.ts new file mode 100644 index 0000000..eaf3fda --- /dev/null +++ b/frontend/src/api/ts/payUpstreamStatusView.ts @@ -0,0 +1,18 @@ +export default interface payUpstreamStatusView { + configured:number + username:string + nickname:string + shop:string + tokenAge:number + goodsId:number + goodsKey:string + goodsName:string + unitPrice:number + stockCount:number + targetMoney:number + walletOk:number + walletAvail:number + walletFrozen:number + ensuring:number + lastError:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyKeySaveDto.ts b/frontend/src/api/ts/proxyKeySaveDto.ts new file mode 100644 index 0000000..0a9aa66 --- /dev/null +++ b/frontend/src/api/ts/proxyKeySaveDto.ts @@ -0,0 +1,4 @@ +export default interface proxyKeySaveDto { + name:string + regionId:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyKeyView.ts b/frontend/src/api/ts/proxyKeyView.ts new file mode 100644 index 0000000..76a7d61 --- /dev/null +++ b/frontend/src/api/ts/proxyKeyView.ts @@ -0,0 +1,12 @@ +export default interface proxyKeyView { + id:number + key:string + name:string + regionId:number + regionCode:string + regionZhName:string + enabled:boolean + usedCount:number + lastUsedAt:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyListDto.ts b/frontend/src/api/ts/proxyListDto.ts new file mode 100644 index 0000000..68cb224 --- /dev/null +++ b/frontend/src/api/ts/proxyListDto.ts @@ -0,0 +1,8 @@ +export default interface proxyListDto { + regionId:number + protocol:string + keyword:string + alive:number + page:number + pageSize:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyListView.ts b/frontend/src/api/ts/proxyListView.ts new file mode 100644 index 0000000..ad8612a --- /dev/null +++ b/frontend/src/api/ts/proxyListView.ts @@ -0,0 +1,5 @@ +import type proxyView from "./proxyView"; +export default interface proxyListView { + total:number + items:proxyView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyRegionView.ts b/frontend/src/api/ts/proxyRegionView.ts new file mode 100644 index 0000000..613c5c9 --- /dev/null +++ b/frontend/src/api/ts/proxyRegionView.ts @@ -0,0 +1,8 @@ +export default interface proxyRegionView { + id:number + code:string + name:string + zhName:string + region:string + aliveCount:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyStatsView.ts b/frontend/src/api/ts/proxyStatsView.ts new file mode 100644 index 0000000..3d51a14 --- /dev/null +++ b/frontend/src/api/ts/proxyStatsView.ts @@ -0,0 +1,9 @@ +export default interface proxyStatsView { + total:number + regions:number + lastSyncAt:number + lastCheckAt:number + lastStatus:string + checking:boolean + syncing:boolean +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxySvc.ts b/frontend/src/api/ts/proxySvc.ts new file mode 100644 index 0000000..1d7d8f9 --- /dev/null +++ b/frontend/src/api/ts/proxySvc.ts @@ -0,0 +1,44 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type idDto from "./idDto"; +import type okView from "./okView"; +import type proxyKeySaveDto from "./proxyKeySaveDto"; +import type proxyKeyView from "./proxyKeyView"; +import type proxyListDto from "./proxyListDto"; +import type proxyListView from "./proxyListView"; +import type proxyRegionView from "./proxyRegionView"; +import type proxyStatsView from "./proxyStatsView"; +import type proxySyncView from "./proxySyncView"; + +export default class proxySvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async checkNow(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "checkNow", undefined, options) + } + async createKey(dto:proxyKeySaveDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "createKey", dto, options) + } + async deleteKey(dto:idDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "deleteKey", dto, options) + } + async list(dto:proxyListDto, options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "list", dto, options) + } + async listKeys(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "listKeys", undefined, options) + } + async regions(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "regions", undefined, options) + } + async stats(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "stats", undefined, options) + } + async syncNow(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "syncNow", undefined, options) + } + async syncs(options?: RequestOptions): Promise> { + return await this.client.request("proxySvc", "syncs", undefined, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxySyncView.ts b/frontend/src/api/ts/proxySyncView.ts new file mode 100644 index 0000000..1f09eeb --- /dev/null +++ b/frontend/src/api/ts/proxySyncView.ts @@ -0,0 +1,13 @@ +export default interface proxySyncView { + id:number + startedAt:number + finishedAt:number + status:string + source:string + total:number + added:number + removed:number + updated:number + detailJson:string + error:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/proxyView.ts b/frontend/src/api/ts/proxyView.ts new file mode 100644 index 0000000..ed241d3 --- /dev/null +++ b/frontend/src/api/ts/proxyView.ts @@ -0,0 +1,20 @@ +export default interface proxyView { + id:number + ip:string + port:number + address:string + protocols:string + anonymity:string + regionId:number + regionCode:string + regionName:string + regionZhName:string + latency:number + speed:number + uptime:number + alive:boolean + failCount:number + lastCheckedAt:number + lastAliveAt:number + lastSeenAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/recentMemoryView.ts b/frontend/src/api/ts/recentMemoryView.ts new file mode 100644 index 0000000..9f05cc2 --- /dev/null +++ b/frontend/src/api/ts/recentMemoryView.ts @@ -0,0 +1,5 @@ +export default interface recentMemoryView { + key:string + project:string + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/recentOrderView.ts b/frontend/src/api/ts/recentOrderView.ts new file mode 100644 index 0000000..00dc655 --- /dev/null +++ b/frontend/src/api/ts/recentOrderView.ts @@ -0,0 +1,6 @@ +export default interface recentOrderView { + tradeNo:string + subject:string + money:number + paidAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveBookmarkDto.ts b/frontend/src/api/ts/saveBookmarkDto.ts new file mode 100644 index 0000000..83592d9 --- /dev/null +++ b/frontend/src/api/ts/saveBookmarkDto.ts @@ -0,0 +1,7 @@ +export default interface saveBookmarkDto { + id?:number | null + title:string + url:string + icon?:string | null + sort?:number | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveDbconnDto.ts b/frontend/src/api/ts/saveDbconnDto.ts new file mode 100644 index 0000000..371446b --- /dev/null +++ b/frontend/src/api/ts/saveDbconnDto.ts @@ -0,0 +1,11 @@ +export default interface saveDbconnDto { + id?:number | null + name:string + engine:string + host?:string | null + port?:number | null + username?:string | null + password?:string | null + database?:string | null + params?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveServerDto.ts b/frontend/src/api/ts/saveServerDto.ts new file mode 100644 index 0000000..af22b4b --- /dev/null +++ b/frontend/src/api/ts/saveServerDto.ts @@ -0,0 +1,10 @@ +export default interface saveServerDto { + id?:number | null + name:string + host:string + port?:number | null + username:string + authType:string + secret?:string | null + note?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveToolDto.ts b/frontend/src/api/ts/saveToolDto.ts new file mode 100644 index 0000000..6a32aef --- /dev/null +++ b/frontend/src/api/ts/saveToolDto.ts @@ -0,0 +1,9 @@ +export default interface saveToolDto { + id?:number | null + name:string + icon?:string | null + url:string + description?:string | null + sort?:number | null + enabled?:boolean | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveUserDto.ts b/frontend/src/api/ts/saveUserDto.ts new file mode 100644 index 0000000..85d9442 --- /dev/null +++ b/frontend/src/api/ts/saveUserDto.ts @@ -0,0 +1,7 @@ +export default interface saveUserDto { + id?:number | null + name:string + password?:string | null + role:string + status?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/saveVaultDto.ts b/frontend/src/api/ts/saveVaultDto.ts new file mode 100644 index 0000000..23e6b0c --- /dev/null +++ b/frontend/src/api/ts/saveVaultDto.ts @@ -0,0 +1,9 @@ +export default interface saveVaultDto { + id?:number | null + title:string + username?:string | null + password?:string | null + url?:string | null + note?:string | null + totpSecret?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/secretView.ts b/frontend/src/api/ts/secretView.ts new file mode 100644 index 0000000..9908339 --- /dev/null +++ b/frontend/src/api/ts/secretView.ts @@ -0,0 +1,3 @@ +export default interface secretView { + value:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/serverIdDto.ts b/frontend/src/api/ts/serverIdDto.ts new file mode 100644 index 0000000..6de47e4 --- /dev/null +++ b/frontend/src/api/ts/serverIdDto.ts @@ -0,0 +1,3 @@ +export default interface serverIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/serverMetricsView.ts b/frontend/src/api/ts/serverMetricsView.ts new file mode 100644 index 0000000..d842fbd --- /dev/null +++ b/frontend/src/api/ts/serverMetricsView.ts @@ -0,0 +1,25 @@ +import type diskMountView from "./diskMountView"; +export default interface serverMetricsView { + hostname:string + os:string + kernel:string + uptimeSeconds:number + cpuPercent:number + load1:string + load5:string + load15:string + memTotal:number + memUsed:number + memAvail:number + swapTotal:number + swapUsed:number + diskTotal:number + diskUsed:number + diskAvail:number + netRxRate:number + netTxRate:number + netRxTotal:number + netTxTotal:number + disks:diskMountView[] + collectedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/serverSvc.ts b/frontend/src/api/ts/serverSvc.ts new file mode 100644 index 0000000..5f771b8 --- /dev/null +++ b/frontend/src/api/ts/serverSvc.ts @@ -0,0 +1,40 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type saveServerDto from "./saveServerDto"; +import type serverIdDto from "./serverIdDto"; +import type serverMetricsView from "./serverMetricsView"; +import type serverView from "./serverView"; +import type sftpListView from "./sftpListView"; +import type sftpPathDto from "./sftpPathDto"; +import type sftpRenameDto from "./sftpRenameDto"; + +export default class serverSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:serverIdDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "list", undefined, options) + } + async metrics(dto:serverIdDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "metrics", dto, options) + } + async save(dto:saveServerDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "save", dto, options) + } + async sftpList(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpList", dto, options) + } + async sftpMkdir(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpMkdir", dto, options) + } + async sftpRemove(dto:sftpPathDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpRemove", dto, options) + } + async sftpRename(dto:sftpRenameDto, options?: RequestOptions): Promise> { + return await this.client.request("serverSvc", "sftpRename", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/serverView.ts b/frontend/src/api/ts/serverView.ts new file mode 100644 index 0000000..bf3e189 --- /dev/null +++ b/frontend/src/api/ts/serverView.ts @@ -0,0 +1,11 @@ +export default interface serverView { + id:number + name:string + host:string + port:number + username:string + authType:string + hasSecret:boolean + note?:string | null + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/sessionView.ts b/frontend/src/api/ts/sessionView.ts new file mode 100644 index 0000000..7a1b0ed --- /dev/null +++ b/frontend/src/api/ts/sessionView.ts @@ -0,0 +1,5 @@ +import type userView from "./userView"; +export default interface sessionView { + user?:userView | null + token?:string | null +} \ No newline at end of file diff --git a/frontend/src/api/ts/settingDto.ts b/frontend/src/api/ts/settingDto.ts new file mode 100644 index 0000000..46463d5 --- /dev/null +++ b/frontend/src/api/ts/settingDto.ts @@ -0,0 +1,4 @@ +export default interface settingDto { + key:string + value:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/settingSvc.ts b/frontend/src/api/ts/settingSvc.ts new file mode 100644 index 0000000..7f0fe3f --- /dev/null +++ b/frontend/src/api/ts/settingSvc.ts @@ -0,0 +1,17 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type settingDto from "./settingDto"; +import type settingView from "./settingView"; + +export default class settingSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async get(dto:settingDto, options?: RequestOptions): Promise> { + return await this.client.request("settingSvc", "get", dto, options) + } + async set(dto:settingDto, options?: RequestOptions): Promise> { + return await this.client.request("settingSvc", "set", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/settingView.ts b/frontend/src/api/ts/settingView.ts new file mode 100644 index 0000000..9bab4a1 --- /dev/null +++ b/frontend/src/api/ts/settingView.ts @@ -0,0 +1,3 @@ +export default interface settingView { + value:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/sftpEntryView.ts b/frontend/src/api/ts/sftpEntryView.ts new file mode 100644 index 0000000..1a467cd --- /dev/null +++ b/frontend/src/api/ts/sftpEntryView.ts @@ -0,0 +1,7 @@ +export default interface sftpEntryView { + name:string + size:number + mode:string + isDir:boolean + modTime:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/sftpListView.ts b/frontend/src/api/ts/sftpListView.ts new file mode 100644 index 0000000..ece61b2 --- /dev/null +++ b/frontend/src/api/ts/sftpListView.ts @@ -0,0 +1,5 @@ +import type sftpEntryView from "./sftpEntryView"; +export default interface sftpListView { + path:string + entries:sftpEntryView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/sftpPathDto.ts b/frontend/src/api/ts/sftpPathDto.ts new file mode 100644 index 0000000..9ff76fd --- /dev/null +++ b/frontend/src/api/ts/sftpPathDto.ts @@ -0,0 +1,4 @@ +export default interface sftpPathDto { + id:number + path:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/sftpRenameDto.ts b/frontend/src/api/ts/sftpRenameDto.ts new file mode 100644 index 0000000..aed374d --- /dev/null +++ b/frontend/src/api/ts/sftpRenameDto.ts @@ -0,0 +1,5 @@ +export default interface sftpRenameDto { + id:number + path:string + newName:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/shareView.ts b/frontend/src/api/ts/shareView.ts new file mode 100644 index 0000000..eaee1d5 --- /dev/null +++ b/frontend/src/api/ts/shareView.ts @@ -0,0 +1,9 @@ +export default interface shareView { + id:number + code:string + fileName:string + expiresAt:number + expired:boolean + downloads:number + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/statsView.ts b/frontend/src/api/ts/statsView.ts new file mode 100644 index 0000000..c778dc7 --- /dev/null +++ b/frontend/src/api/ts/statsView.ts @@ -0,0 +1,30 @@ +import type payTrendPoint from "./payTrendPoint"; +import type recentMemoryView from "./recentMemoryView"; +import type recentOrderView from "./recentOrderView"; +import type trendPoint from "./trendPoint"; +export default interface statsView { + totalUsers:number + activeSessions:number + todayLogins:number + uptimeSeconds:number + loginTrend:trendPoint[] + servers:number + proxies:number + aliveProxies:number + todayOrders:number + todayMoney:number + totalMoney:number + pendingOrders:number + licenseApps:number + licenseCards:number + licenseActive:number + memories:number + memoryProjects:number + bookmarks:number + vaultItems:number + dbconns:number + files:number + payTrend:payTrendPoint[] + recentOrders:recentOrderView[] + recentMemories:recentMemoryView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/toolSvc.ts b/frontend/src/api/ts/toolSvc.ts new file mode 100644 index 0000000..2c59efa --- /dev/null +++ b/frontend/src/api/ts/toolSvc.ts @@ -0,0 +1,24 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type deleteToolDto from "./deleteToolDto"; +import type okView from "./okView"; +import type saveToolDto from "./saveToolDto"; +import type toolView from "./toolView"; + +export default class toolSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async adminList(options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "adminList", undefined, options) + } + async deleteTool(dto:deleteToolDto, options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "deleteTool", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "list", undefined, options) + } + async saveTool(dto:saveToolDto, options?: RequestOptions): Promise> { + return await this.client.request("toolSvc", "saveTool", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/toolView.ts b/frontend/src/api/ts/toolView.ts new file mode 100644 index 0000000..6ad8559 --- /dev/null +++ b/frontend/src/api/ts/toolView.ts @@ -0,0 +1,9 @@ +export default interface toolView { + id:number + name:string + icon:string + url:string + description:string + sort:number + enabled:boolean +} \ No newline at end of file diff --git a/frontend/src/api/ts/totpView.ts b/frontend/src/api/ts/totpView.ts new file mode 100644 index 0000000..530be9b --- /dev/null +++ b/frontend/src/api/ts/totpView.ts @@ -0,0 +1,4 @@ +export default interface totpView { + code:string + secondsLeft:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/trendPoint.ts b/frontend/src/api/ts/trendPoint.ts new file mode 100644 index 0000000..f4338eb --- /dev/null +++ b/frontend/src/api/ts/trendPoint.ts @@ -0,0 +1,4 @@ +export default interface trendPoint { + day:string + count:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/userPageView.ts b/frontend/src/api/ts/userPageView.ts new file mode 100644 index 0000000..dbf48b4 --- /dev/null +++ b/frontend/src/api/ts/userPageView.ts @@ -0,0 +1,5 @@ +import type userView from "./userView"; +export default interface userPageView { + total:number + items:userView[] +} \ No newline at end of file diff --git a/frontend/src/api/ts/userSvc.ts b/frontend/src/api/ts/userSvc.ts new file mode 100644 index 0000000..64b54fd --- /dev/null +++ b/frontend/src/api/ts/userSvc.ts @@ -0,0 +1,23 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type deleteUserDto from "./deleteUserDto"; +import type listUsersDto from "./listUsersDto"; +import type okView from "./okView"; +import type saveUserDto from "./saveUserDto"; +import type userPageView from "./userPageView"; +import type userView from "./userView"; + +export default class userSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async deleteUser(dto:deleteUserDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "deleteUser", dto, options) + } + async listUsers(dto:listUsersDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "listUsers", dto, options) + } + async saveUser(dto:saveUserDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "saveUser", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/userView.ts b/frontend/src/api/ts/userView.ts new file mode 100644 index 0000000..416bec2 --- /dev/null +++ b/frontend/src/api/ts/userView.ts @@ -0,0 +1,7 @@ +export default interface userView { + id:number + name:string + role:string + status:string + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/vaultIdDto.ts b/frontend/src/api/ts/vaultIdDto.ts new file mode 100644 index 0000000..99f286a --- /dev/null +++ b/frontend/src/api/ts/vaultIdDto.ts @@ -0,0 +1,3 @@ +export default interface vaultIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/vaultItemView.ts b/frontend/src/api/ts/vaultItemView.ts new file mode 100644 index 0000000..b1e60cd --- /dev/null +++ b/frontend/src/api/ts/vaultItemView.ts @@ -0,0 +1,10 @@ +export default interface vaultItemView { + id:number + title:string + username?:string | null + url?:string | null + note?:string | null + hasPassword:boolean + hasTotp:boolean + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/ts/vaultSvc.ts b/frontend/src/api/ts/vaultSvc.ts new file mode 100644 index 0000000..f6126e4 --- /dev/null +++ b/frontend/src/api/ts/vaultSvc.ts @@ -0,0 +1,29 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type saveVaultDto from "./saveVaultDto"; +import type secretView from "./secretView"; +import type totpView from "./totpView"; +import type vaultIdDto from "./vaultIdDto"; +import type vaultItemView from "./vaultItemView"; + +export default class vaultSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "list", undefined, options) + } + async reveal(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "reveal", dto, options) + } + async save(dto:saveVaultDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "save", dto, options) + } + async totp(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "totp", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/ts/webFetchDto.ts b/frontend/src/api/ts/webFetchDto.ts new file mode 100644 index 0000000..a1e0931 --- /dev/null +++ b/frontend/src/api/ts/webFetchDto.ts @@ -0,0 +1,6 @@ +export default interface webFetchDto { + method:string + url:string + headersJson:string + body:string +} \ No newline at end of file diff --git a/frontend/src/api/ts/webFetchView.ts b/frontend/src/api/ts/webFetchView.ts new file mode 100644 index 0000000..39e7fe9 --- /dev/null +++ b/frontend/src/api/ts/webFetchView.ts @@ -0,0 +1,7 @@ +export default interface webFetchView { + status:number + headersJson:string + body:string + elapsedMs:number + truncated:boolean +} \ No newline at end of file diff --git a/frontend/src/api/ts/webSvc.ts b/frontend/src/api/ts/webSvc.ts new file mode 100644 index 0000000..ab61aba --- /dev/null +++ b/frontend/src/api/ts/webSvc.ts @@ -0,0 +1,13 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type webFetchDto from "./webFetchDto"; +import type webFetchView from "./webFetchView"; + +export default class webSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async fetch(dto:webFetchDto, options?: RequestOptions): Promise> { + return await this.client.request("webSvc", "fetch", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/userPageView.ts b/frontend/src/api/userPageView.ts new file mode 100644 index 0000000..dbf48b4 --- /dev/null +++ b/frontend/src/api/userPageView.ts @@ -0,0 +1,5 @@ +import type userView from "./userView"; +export default interface userPageView { + total:number + items:userView[] +} \ No newline at end of file diff --git a/frontend/src/api/userSvc.ts b/frontend/src/api/userSvc.ts new file mode 100644 index 0000000..64b54fd --- /dev/null +++ b/frontend/src/api/userSvc.ts @@ -0,0 +1,23 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type deleteUserDto from "./deleteUserDto"; +import type listUsersDto from "./listUsersDto"; +import type okView from "./okView"; +import type saveUserDto from "./saveUserDto"; +import type userPageView from "./userPageView"; +import type userView from "./userView"; + +export default class userSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async deleteUser(dto:deleteUserDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "deleteUser", dto, options) + } + async listUsers(dto:listUsersDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "listUsers", dto, options) + } + async saveUser(dto:saveUserDto, options?: RequestOptions): Promise> { + return await this.client.request("userSvc", "saveUser", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/userView.ts b/frontend/src/api/userView.ts new file mode 100644 index 0000000..416bec2 --- /dev/null +++ b/frontend/src/api/userView.ts @@ -0,0 +1,7 @@ +export default interface userView { + id:number + name:string + role:string + status:string + createdAt:number +} \ No newline at end of file diff --git a/frontend/src/api/vaultIdDto.ts b/frontend/src/api/vaultIdDto.ts new file mode 100644 index 0000000..99f286a --- /dev/null +++ b/frontend/src/api/vaultIdDto.ts @@ -0,0 +1,3 @@ +export default interface vaultIdDto { + id:number +} \ No newline at end of file diff --git a/frontend/src/api/vaultItemView.ts b/frontend/src/api/vaultItemView.ts new file mode 100644 index 0000000..b1e60cd --- /dev/null +++ b/frontend/src/api/vaultItemView.ts @@ -0,0 +1,10 @@ +export default interface vaultItemView { + id:number + title:string + username?:string | null + url?:string | null + note?:string | null + hasPassword:boolean + hasTotp:boolean + updatedAt:number +} \ No newline at end of file diff --git a/frontend/src/api/vaultSvc.ts b/frontend/src/api/vaultSvc.ts new file mode 100644 index 0000000..f6126e4 --- /dev/null +++ b/frontend/src/api/vaultSvc.ts @@ -0,0 +1,29 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type okView from "./okView"; +import type saveVaultDto from "./saveVaultDto"; +import type secretView from "./secretView"; +import type totpView from "./totpView"; +import type vaultIdDto from "./vaultIdDto"; +import type vaultItemView from "./vaultItemView"; + +export default class vaultSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async delete(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "delete", dto, options) + } + async list(options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "list", undefined, options) + } + async reveal(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "reveal", dto, options) + } + async save(dto:saveVaultDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "save", dto, options) + } + async totp(dto:vaultIdDto, options?: RequestOptions): Promise> { + return await this.client.request("vaultSvc", "totp", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/api/webFetchDto.ts b/frontend/src/api/webFetchDto.ts new file mode 100644 index 0000000..a1e0931 --- /dev/null +++ b/frontend/src/api/webFetchDto.ts @@ -0,0 +1,6 @@ +export default interface webFetchDto { + method:string + url:string + headersJson:string + body:string +} \ No newline at end of file diff --git a/frontend/src/api/webFetchView.ts b/frontend/src/api/webFetchView.ts new file mode 100644 index 0000000..39e7fe9 --- /dev/null +++ b/frontend/src/api/webFetchView.ts @@ -0,0 +1,7 @@ +export default interface webFetchView { + status:number + headersJson:string + body:string + elapsedMs:number + truncated:boolean +} \ No newline at end of file diff --git a/frontend/src/api/webSvc.ts b/frontend/src/api/webSvc.ts new file mode 100644 index 0000000..ab61aba --- /dev/null +++ b/frontend/src/api/webSvc.ts @@ -0,0 +1,13 @@ +import { Client, type result, type RequestOptions } from "./client"; +import type webFetchDto from "./webFetchDto"; +import type webFetchView from "./webFetchView"; + +export default class webSvc { + private client: Client; + constructor(client: Client) { + this.client = client; + } + async fetch(dto:webFetchDto, options?: RequestOptions): Promise> { + return await this.client.request("webSvc", "fetch", dto, options) + } +} \ No newline at end of file diff --git a/frontend/src/layouts/AdminLayout.vue b/frontend/src/layouts/AdminLayout.vue new file mode 100644 index 0000000..233c7f5 --- /dev/null +++ b/frontend/src/layouts/AdminLayout.vue @@ -0,0 +1,207 @@ + + + diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts new file mode 100644 index 0000000..e5cebfd --- /dev/null +++ b/frontend/src/lib/api.ts @@ -0,0 +1,38 @@ +import api from '../api/fun' +import { createDiscreteApi } from 'naive-ui' + +const TOKEN_KEY = 'cyibox_token' + +export const readToken = () => localStorage.getItem(TOKEN_KEY) ?? '' +export const writeToken = (t: string) => localStorage.setItem(TOKEN_KEY, t) +export const clearToken = () => localStorage.removeItem(TOKEN_KEY) + +// 后端统一走 /api(vite 代理剥前缀);生产部署时前端同源反代即可 +export const client = api.create('/api') + +// 组件外使用的离散 message/dialog(拦截器、store 里提示用) +export const { message, dialog } = createDiscreteApi(['message', 'dialog']) + +// token 走请求 state 透传,后端 Guard 从 ctx.State["token"] 取 +client.addRequestInterceptor((_svc, _method, state) => { + const token = readToken() + if (token) state.token = token +}) + +let redirecting = false +client.addResponseInterceptor(async (_svc, _method, result) => { + // 4011 = authErrCode:登录态失效,清 token + 清 store 用户态,跳登录页 + if (result?.code === 4011) { + clearToken() + // user 不清的话守卫会把 /login 又弹回 /,死循环 + const { useAuthStore } = await import('../stores/auth') + useAuthStore().user = null + if (!redirecting && !location.pathname.startsWith('/login')) { + redirecting = true + message.warning(result?.msg || '登录已失效,请重新登录') + const { default: router } = await import('../router') + await router.replace({ path: '/login', query: { redirect: router.currentRoute.value.fullPath } }) + redirecting = false + } + } +}) diff --git a/frontend/src/lib/icons.ts b/frontend/src/lib/icons.ts new file mode 100644 index 0000000..b37efd0 --- /dev/null +++ b/frontend/src/lib/icons.ts @@ -0,0 +1,56 @@ +// 工具图标名 → ionicons5 组件映射。工具表 icon 字段存这里的 key; +// 新增图标时往 map 里加一行即可,缺省回退 ConstructOutline。 +import type { Component } from 'vue' +import { + ConstructOutline, CodeOutline, TimeOutline, ImageOutline, LinkOutline, + DocumentTextOutline, ColorPaletteOutline, KeyOutline, CalculatorOutline, + GlobeOutline, TerminalOutline, WifiOutline, QrCodeOutline, TextOutline, + FileTrayOutline, BugOutline, ChatboxOutline, SearchOutline, + BookmarkOutline, StarOutline, HeartOutline, HomeOutline, CartOutline, + GameControllerOutline, MusicalNotesOutline, VideocamOutline, NewspaperOutline, + LogoGithub, FolderOutline, CloudOutline +} from '@vicons/ionicons5' + +const map: Record = { + 'construct-outline': ConstructOutline, + 'code-outline': CodeOutline, + 'time-outline': TimeOutline, + 'image-outline': ImageOutline, + 'link-outline': LinkOutline, + 'document-text-outline': DocumentTextOutline, + 'color-palette-outline': ColorPaletteOutline, + 'key-outline': KeyOutline, + 'calculator-outline': CalculatorOutline, + 'globe-outline': GlobeOutline, + 'terminal-outline': TerminalOutline, + 'wifi-outline': WifiOutline, + 'qr-code-outline': QrCodeOutline, + 'text-outline': TextOutline, + 'file-tray-outline': FileTrayOutline, + 'bug-outline': BugOutline, + 'chatbox-outline': ChatboxOutline, + 'search-outline': SearchOutline, + 'bookmark-outline': BookmarkOutline, + 'star-outline': StarOutline, + 'heart-outline': HeartOutline, + 'home-outline': HomeOutline, + 'cart-outline': CartOutline, + 'game-controller-outline': GameControllerOutline, + 'musical-notes-outline': MusicalNotesOutline, + 'videocam-outline': VideocamOutline, + 'newspaper-outline': NewspaperOutline, + 'logo-github': LogoGithub, + 'folder-outline': FolderOutline, + 'cloud-outline': CloudOutline +} + +// Memphis 色板:图标块底色按 id 轮转 +const colors = ['#fef08a', '#bae6fd', '#a7f3d0', '#ffc1cc', '#d9b3ff', '#ffd8b0'] + +export function toolIcon(name: string): Component { + return map[name] ?? ConstructOutline +} + +export function toolGradient(id: number): string { + return colors[Math.abs(id) % colors.length] +} diff --git a/frontend/src/lib/kit.ts b/frontend/src/lib/kit.ts new file mode 100644 index 0000000..09a0023 --- /dev/null +++ b/frontend/src/lib/kit.ts @@ -0,0 +1,44 @@ +// 通用小工具:剪贴板复制 + 文件下载,供工具页复用 +export async function copyText(text: string): Promise { + try { + await navigator.clipboard.writeText(text) + return true + } catch { + const ta = document.createElement('textarea') + ta.value = text + ta.style.position = 'fixed' + ta.style.opacity = '0' + document.body.appendChild(ta) + ta.select() + const ok = document.execCommand('copy') + ta.remove() + return ok + } +} + +export function downloadText(filename: string, text: string, mime = 'text/plain') { + const blob = new Blob([text], { type: `${mime};charset=utf-8` }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + a.click() + URL.revokeObjectURL(url) +} + +// JSON 语法高亮:输入必须已是格式化后的 JSON 文本,返回安全 HTML +export function highlightJson(code: string): string { + const esc = (s: string) => + s.replace(/&/g, '&').replace(//g, '>') + return esc(code).replace( + /("(\\u[a-z0-9]{4}|\\[^u]|[^\\"])*")(\s*:)?|\b(true|false|null)\b|-?\d+(\.\d+)?([eE][+-]?\d+)?/g, + (m, str, _u, colon) => { + if (str !== undefined) { + return `${str}${colon ?? ''}` + } + if (/^(true|false)$/.test(m)) return `${m}` + if (m === 'null') return `${m}` + return `${m}` + } + ) +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..501dee0 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,10 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' +import './style.css' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..3eb73c1 --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,187 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useAuthStore } from '../stores/auth' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { + path: '/login', + name: 'login', + component: () => import('../views/Login.vue'), + meta: { title: '登录' } + }, + { + path: '/cashier/:tradeNo', + name: 'cashier', + component: () => import('../views/Cashier.vue'), + meta: { title: '收银台' } + }, + { + path: '/', + component: () => import('../layouts/AdminLayout.vue'), + meta: { requiresAuth: true }, + children: [ + { path: '', redirect: '/dashboard' }, + { + path: 'dashboard', + name: 'dashboard', + component: () => import('../views/Dashboard.vue'), + meta: { title: '仪表盘' } + }, + { + path: 'tools/json', + name: 'tool-json', + component: () => import('../views/tools/JsonTool.vue'), + meta: { title: 'JSON 工具' } + }, + { + path: 'tools/timestamp', + name: 'tool-timestamp', + component: () => import('../views/tools/TimestampTool.vue'), + meta: { title: '时间戳转换' } + }, + { + path: 'tools/markdown', + name: 'tool-markdown', + component: () => import('../views/tools/MarkdownTool.vue'), + meta: { title: 'Markdown 编辑器' } + }, + { + path: 'tools/postman', + name: 'tool-postman', + component: () => import('../views/tools/PostmanTool.vue'), + meta: { title: '接口调试' } + }, + { + path: 'tools/color', + name: 'tool-color', + component: () => import('../views/tools/ColorTool.vue'), + meta: { title: '取色板' } + }, + { + path: 'tools/color-image', + name: 'tool-color-image', + component: () => import('../views/tools/ColorImage.vue'), + meta: { title: '图片拾色' } + }, + { + path: 'tools/gradient', + name: 'tool-gradient', + component: () => import('../views/tools/GradientTool.vue'), + meta: { title: '渐变图鉴' } + }, + { + path: 'vault', + name: 'vault', + component: () => import('../views/Vault.vue'), + meta: { title: '密码管理' } + }, + { + path: 'bookmarks', + name: 'bookmarks', + component: () => import('../views/Bookmarks.vue'), + meta: { title: '书签导航' } + }, + { + path: 'disk', + name: 'disk', + component: () => import('../views/Disk.vue'), + meta: { title: '我的文件' } + }, + { + path: 'disk/shares', + name: 'disk-shares', + component: () => import('../views/DiskShares.vue'), + meta: { title: '分享链接' } + }, + { + path: 'proxies', + name: 'proxies', + component: () => import('../views/Proxies.vue'), + meta: { title: '代理列表' } + }, + { + path: 'proxies/extract', + name: 'proxy-extract', + component: () => import('../views/ProxyExtract.vue'), + meta: { title: '提取链接', requiresAdmin: true } + }, + { + path: 'servers', + name: 'servers', + component: () => import('../views/Servers.vue'), + meta: { title: '服务器管理', requiresAdmin: true } + }, + { + path: 'servers/:id(\\d+)', + name: 'server-detail', + component: () => import('../views/ServerDetail.vue'), + meta: { title: '服务器详情', requiresAdmin: true } + }, + { + path: 'dbm', + name: 'dbm', + component: () => import('../views/DbManager.vue'), + meta: { title: '数据库', requiresAdmin: true } + }, + { + path: 'settings', + name: 'settings', + component: () => import('../views/Settings.vue'), + meta: { title: '系统设置', requiresAdmin: true } + }, + { + path: 'pay', + name: 'pay-orders', + component: () => import('../views/PayOrders.vue'), + meta: { title: '订单管理', requiresAdmin: true } + }, + { + path: 'pay/config', + name: 'pay-config', + component: () => import('../views/PayConfig.vue'), + meta: { title: '支付配置', requiresAdmin: true } + }, + { + path: 'licenses', + name: 'licenses', + component: () => import('../views/Licenses.vue'), + meta: { title: '卡密管理', requiresAdmin: true } + }, + { + path: 'memories', + name: 'memories', + component: () => import('../views/Memories.vue'), + meta: { title: '记忆库', requiresAdmin: true } + }, + { + path: 'licenses/apps', + name: 'license-apps', + component: () => import('../views/LicenseApps.vue'), + meta: { title: '项目管理', requiresAdmin: true } + }, + ] + }, + { path: '/:pathMatch(.*)*', redirect: '/' } + ] +}) + +router.beforeEach(async to => { + const auth = useAuthStore() + if (!auth.ready) await auth.fetchMe() + if (to.meta.requiresAuth && !auth.user) { + return { path: '/login', query: { redirect: to.fullPath } } + } + if (to.meta.requiresAdmin && !auth.isAdmin) { + return { path: '/' } + } + if (to.name === 'login' && auth.user) { + return { path: '/' } + } +}) + +router.afterEach(to => { + document.title = to.meta.title ? `${to.meta.title} · 池易工作箱` : '池易工作箱' +}) + +export default router diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts new file mode 100644 index 0000000..0b09259 --- /dev/null +++ b/frontend/src/stores/auth.ts @@ -0,0 +1,44 @@ +import { computed, ref } from 'vue' +import { defineStore } from 'pinia' +import { client, readToken, writeToken, clearToken } from '../lib/api' +import type userView from '../api/userView' + +export const useAuthStore = defineStore('auth', () => { + const user = ref(null) + const ready = ref(false) // 首次 Me 探测是否完成(路由守卫用) + + const isAdmin = computed(() => user.value?.role === 'admin') + + async function fetchMe() { + if (!readToken()) { + user.value = null + ready.value = true + return + } + try { + const r = await client.authSvc.me() + user.value = r.status === 0 && r.data?.user ? r.data.user : null + } catch { + user.value = null + } + ready.value = true + } + + async function login(name: string, password: string): Promise { + const r = await client.authSvc.login({ name, password }) + if (r.status !== 0 || !r.data?.token || !r.data.user) { + return r.msg || '登录失败' + } + writeToken(r.data.token) + user.value = r.data.user + return null + } + + async function logout() { + try { await client.authSvc.logout() } catch { /* 忽略 */ } + clearToken() + user.value = null + } + + return { user, ready, isAdmin, fetchMe, login, logout } +}) diff --git a/frontend/src/stores/bookmarks.ts b/frontend/src/stores/bookmarks.ts new file mode 100644 index 0000000..d0bc322 --- /dev/null +++ b/frontend/src/stores/bookmarks.ts @@ -0,0 +1,20 @@ +import { ref } from 'vue' +import { defineStore } from 'pinia' +import { client } from '../lib/api' +import type bookmarkView from '../api/bookmarkView' + +// 个人书签:驱动侧边栏「我的书签」组与书签页 +export const useBookmarksStore = defineStore('bookmarks', () => { + const items = ref([]) + const loaded = ref(false) + + async function refresh() { + const r = await client.bookmarkSvc.list() + if (r.status === 0 && r.data) { + items.value = r.data + loaded.value = true + } + } + + return { items, loaded, refresh } +}) diff --git a/frontend/src/stores/servers.ts b/frontend/src/stores/servers.ts new file mode 100644 index 0000000..059daf0 --- /dev/null +++ b/frontend/src/stores/servers.ts @@ -0,0 +1,23 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { client } from '../lib/api' +import type serverView from '../api/serverView' + +export const useServersStore = defineStore('servers', () => { + const items = ref([]) + const loaded = ref(false) + + async function refresh() { + const r = await client.serverSvc.list() + if (r.status === 0 && r.data) { + items.value = r.data + loaded.value = true + } + } + + function find(id: number) { + return items.value.find(s => s.id === id) + } + + return { items, loaded, refresh, find } +}) diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..4b35dcb --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,639 @@ +:root { + --cb-primary: #ec5b13; + --cb-ink: #1a1a1a; + --cb-ink-2: #57534d; + --cb-ink-3: #8a857e; + --cb-bg: #f8f6f6; + --cb-panel: #ffffff; + --cb-line: #1a1a1a; + --cb-yellow: #fef08a; + --cb-blue: #bae6fd; + --cb-mint: #a7f3d0; + --cb-pink: #ffc1cc; + --cb-purple: #d9b3ff; + --cb-radius: 14px; + --cb-border: 3px solid var(--cb-ink); + --cb-shadow: 6px 6px 0 var(--cb-ink); + --cb-shadow-sm: 4px 4px 0 var(--cb-ink); + --cb-shadow-lg: 10px 10px 0 var(--cb-ink); +} + +* { box-sizing: border-box; } + +html, body, #app { + height: 100%; + margin: 0; + padding: 0; +} + +/* naive-ui provider 会渲染真实包装元素,必须把高度接力传下去 */ +.n-config-provider, +.n-message-provider, +.n-dialog-provider { + height: 100%; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', + 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; + background: var(--cb-bg); + color: var(--cb-ink); + -webkit-font-smoothing: antialiased; + font-feature-settings: 'tnum'; +} + +::-webkit-scrollbar { width: 12px; height: 12px; } +::-webkit-scrollbar-thumb { + background: var(--cb-ink); + border-radius: 8px; + border: 3px solid var(--cb-bg); +} +::-webkit-scrollbar-track { background: transparent; } + +/* ================= naive-ui 粗野化 ================= */ +/* 卡片:3px 黑边 + 硬投影(弹窗同样生效,风格统一) */ +.n-card { + border: var(--cb-border) !important; + border-radius: 16px !important; + box-shadow: var(--cb-shadow-sm) !important; +} +.n-card--hoverable:hover { transform: translate(-2px, -2px); box-shadow: var(--cb-shadow) !important; } + +/* 按钮:黑边 + 小硬影 + 按压回弹 */ +.n-button { + border: 2.5px solid var(--cb-ink) !important; + font-weight: 700 !important; + box-shadow: 3px 3px 0 var(--cb-ink); + transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.15s ease !important; +} +.n-button:not(.n-button--disabled):hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--cb-ink); } +.n-button:not(.n-button--disabled):active { transform: translate(2px, 2px) !important; box-shadow: 0 0 0 var(--cb-ink) !important; } +.n-button--quaternary-type, .n-button--tertiary-type { box-shadow: none !important; } + +/* 输入/选择:黑边 */ +.n-input, +.n-base-selection, +.n-input-number, +.n-select .n-base-selection { + border: 2.5px solid var(--cb-ink) !important; + border-radius: 10px !important; + box-shadow: 3px 3px 0 rgba(26, 26, 26, 0.15); +} +.n-input:focus-within, +.n-base-selection:focus-within, +.n-input-number:focus-within { + box-shadow: 4px 4px 0 var(--cb-primary) !important; +} + +/* 表格:黑边框架 + 表头色块 */ +.n-data-table { + border: 2.5px solid var(--cb-ink); + border-radius: 12px !important; + overflow: hidden; +} +.n-data-table .n-data-table-th { + background: var(--cb-yellow) !important; + color: var(--cb-ink) !important; + font-weight: 800 !important; + border-bottom: 2.5px solid var(--cb-ink) !important; +} +.n-data-table .n-data-table-td { border-bottom: 1.5px solid rgba(26, 26, 26, 0.15) !important; } + +/* 标签/弹层 */ +.n-tag { border: 2px solid var(--cb-ink) !important; font-weight: 700; } +.n-dropdown-menu, .n-base-select-menu, .n-modal .n-card__content { border-radius: 12px; } +.n-dropdown-menu { + border: 2.5px solid var(--cb-ink) !important; + box-shadow: var(--cb-shadow-sm) !important; +} +.n-pagination .n-pagination-item { + border: 2px solid var(--cb-ink) !important; + font-weight: 700; +} + +/* ================= 登录页 ================= */ +.login-page { + min-height: 100vh; + min-height: 100dvh; + display: flex; + align-items: center; + justify-content: center; + background: + radial-gradient(circle at 12% 20%, rgba(236, 91, 19, 0.08), transparent 35%), + radial-gradient(circle at 88% 80%, rgba(186, 230, 253, 0.5), transparent 40%), + var(--cb-bg); + position: relative; + overflow: hidden; + padding: 24px; +} + +/* 漂浮的 Memphis 几何装饰 */ +.deco { position: absolute; pointer-events: none; z-index: 0; } +.deco-circle { + width: 90px; height: 90px; border-radius: 50%; + background: var(--cb-blue); + border: var(--cb-border); + top: 8%; left: 7%; + animation: memphis-float 7s ease-in-out infinite; +} +.deco-triangle { + width: 0; height: 0; + border-left: 46px solid transparent; + border-right: 46px solid transparent; + border-bottom: 78px solid var(--cb-yellow); + filter: drop-shadow(4px 4px 0 var(--cb-ink)); + bottom: 12%; right: 9%; + transform: rotate(12deg); + animation: memphis-float 9s ease-in-out -3s infinite; +} +.deco-cross { + width: 64px; height: 64px; + top: 12%; right: 14%; + animation: memphis-spin 14s linear infinite; +} +.deco-cross::before, .deco-cross::after { + content: ''; + position: absolute; + background: var(--cb-ink); +} +.deco-cross::before { left: 27px; top: 0; width: 10px; height: 64px; } +.deco-cross::after { top: 27px; left: 0; height: 10px; width: 64px; } +.deco-zigzag { + width: 130px; height: 30px; + bottom: 16%; left: 12%; + background: + linear-gradient(135deg, var(--cb-ink) 25%, transparent 25%) -15px 0/30px 30px, + linear-gradient(225deg, var(--cb-ink) 25%, transparent 25%) -15px 0/30px 30px; + animation: memphis-float 8s ease-in-out -5s infinite; +} +.deco-semicircle { + width: 80px; height: 40px; + border-radius: 80px 80px 0 0; + background: var(--cb-pink); + border: var(--cb-border); + border-bottom: none; + top: 55%; left: 4%; + transform: rotate(-18deg); + animation: memphis-float 10s ease-in-out -7s infinite; +} +@keyframes memphis-float { + 0%, 100% { transform: translateY(0) rotate(var(--r, 0deg)); } + 50% { transform: translateY(-16px) rotate(var(--r, 0deg)); } +} +@keyframes memphis-spin { to { transform: rotate(360deg); } } + +.login-card { + position: relative; + z-index: 1; + display: flex; + width: 860px; + max-width: 100%; + min-height: 520px; + background: #fff; + border: var(--cb-border); + border-radius: 24px; + box-shadow: 12px 12px 0 var(--cb-ink); + overflow: hidden; +} +@media (max-width: 820px) { + .login-brand { display: none; } +} + +.login-brand { + flex: 1; + position: relative; + padding: 48px 44px; + background: var(--cb-primary); + border-right: var(--cb-border); + color: #fff; + display: flex; + flex-direction: column; + justify-content: space-between; + overflow: hidden; +} +/* 橙色底上的白色圆点纹理 */ +.login-brand::before { + content: ''; + position: absolute; + inset: 0; + background-image: radial-gradient(rgba(255, 255, 255, 0.22) 2.5px, transparent 2.5px); + background-size: 26px 26px; +} + +.login-brand-top { position: relative; display: flex; align-items: center; gap: 12px; } +.login-brand-top .brand-name { font-size: 17px; font-weight: 800; letter-spacing: 1px; } +.login-brand-top .brand-tag { + font-size: 10px; + font-weight: 800; + letter-spacing: 2px; + color: var(--cb-ink); + background: var(--cb-yellow); + border: 2px solid var(--cb-ink); + padding: 3px 9px; + border-radius: 8px; + transform: rotate(-3deg); +} + +.brand-badge { + width: 44px; + height: 44px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 22px; + color: var(--cb-ink); + background: var(--cb-yellow); + border: 2.5px solid var(--cb-ink); + box-shadow: 4px 4px 0 var(--cb-ink); +} + +.login-brand-mid { position: relative; } +.login-brand-mid h2 { + font-size: 40px; + line-height: 1.25; + margin: 0 0 18px; + font-weight: 900; + letter-spacing: 1px; + text-shadow: 3px 3px 0 rgba(26, 26, 26, 0.35); +} +.login-brand-mid h2 em { + font-style: normal; + color: var(--cb-yellow); + -webkit-text-stroke: 1.5px var(--cb-ink); + text-shadow: 3px 3px 0 var(--cb-ink); +} +.login-brand-mid p { + color: rgba(255, 255, 255, 0.9); + font-size: 14px; + font-weight: 500; + line-height: 1.9; + margin: 0; +} + +.login-brand-bottom { + position: relative; + display: flex; + flex-wrap: wrap; + gap: 10px; +} +.brand-chip { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + font-weight: 700; + color: var(--cb-ink); + background: #fff; + border: 2px solid var(--cb-ink); + border-radius: 999px; + padding: 5px 12px; + box-shadow: 3px 3px 0 var(--cb-ink); +} + +.login-form-side { + width: 400px; + flex: none; + display: flex; + align-items: center; + justify-content: center; + padding: 48px 40px; +} +@media (max-width: 820px) { + .login-form-side { width: 100%; } +} + +.login-form { width: 100%; } +.login-form h1 { + font-size: 30px; + font-weight: 900; + margin: 0 0 8px; + color: var(--cb-ink); +} +.login-form .sub { color: var(--cb-ink-2); font-size: 13px; margin: 0 0 32px; font-weight: 500; } +.login-form .n-input { margin-bottom: 18px; } +.login-err { + min-height: 26px; + font-size: 13px; + font-weight: 700; + color: #e11d48; + display: flex; + align-items: center; +} +.login-btn { + height: 48px !important; + font-size: 16px !important; + letter-spacing: 6px; + border-radius: 12px !important; +} +.login-foot { margin-top: 24px; text-align: center; font-size: 12px; color: var(--cb-ink-3); } + +/* ================= 后台布局 ================= */ +.cb-sider { + background: #fff !important; + border-right: var(--cb-border); +} +.cb-sider .n-layout-sider-scroll-container { + display: flex; + flex-direction: column; +} + +.cb-logo { + height: 64px; + display: flex; + align-items: center; + gap: 11px; + padding: 0 20px; + overflow: hidden; + white-space: nowrap; + border-bottom: var(--cb-border); +} +.cb-logo-text { + color: var(--cb-ink); + font-weight: 900; + font-size: 16px; + letter-spacing: 1px; + line-height: 1.25; +} +.cb-logo-text small { + display: block; + font-size: 9px; + font-weight: 700; + color: var(--cb-ink-3); + letter-spacing: 3px; +} + +.cb-menu-wrap { flex: 1; padding: 14px 12px; overflow-y: auto; } +.cb-menu-wrap .n-menu .n-menu-item-content { border-radius: 10px !important; } +.cb-menu-wrap .n-menu .n-menu-item-group-title { + font-size: 10px; + font-weight: 800; + letter-spacing: 2px; + padding: 16px 14px 6px !important; +} + +/* 菜单激活项:黄底 + 黑边 + 小硬影(覆盖主题色) */ +.cb-menu-wrap .n-menu .n-menu-item-content.n-menu-item-content--selected { + background: var(--cb-yellow) !important; + border: 2.5px solid var(--cb-ink); + box-shadow: 3px 3px 0 var(--cb-ink); +} +.cb-menu-wrap .n-menu .n-menu-item-content { + border: 2.5px solid transparent; + margin-bottom: 4px; +} +.cb-menu-wrap .n-menu .n-menu-item-content:hover { + background: #fdf6d8 !important; +} +.cb-menu-wrap .n-menu .n-menu-item-content::before { display: none !important; } + +.cb-sider-foot { + padding: 12px 14px; + border-top: var(--cb-border); + display: flex; + align-items: center; + gap: 6px; +} +.sider-user { + display: flex; + align-items: center; + gap: 8px; + flex: 1; + min-width: 0; +} +.sider-user-name { + font-size: 13px; + font-weight: 800; + color: var(--cb-ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.logout-btn { + border: 2px solid var(--cb-ink) !important; + border-radius: 8px !important; + font-weight: 800; + box-shadow: 2px 2px 0 var(--cb-ink); +} +.logout-btn:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--cb-ink); } + +.cb-header { + height: 64px; + display: flex; + align-items: center; + padding: 0 24px; + background: rgba(248, 246, 246, 0.9); + backdrop-filter: blur(10px); + border-bottom: var(--cb-border); + position: sticky; + top: 0; + z-index: 10; +} +.page-title { font-size: 17px; font-weight: 900; color: var(--cb-ink); } +.page-sub { font-size: 12px; color: var(--cb-ink-3); margin-top: 1px; font-weight: 500; } + +.cb-user-chip { + display: flex; + align-items: center; + gap: 9px; + cursor: pointer; + padding: 5px 12px 5px 5px; + border-radius: 999px; + border: 2.5px solid var(--cb-ink); + background: #fff; + box-shadow: 3px 3px 0 var(--cb-ink); + transition: transform 0.12s ease, box-shadow 0.12s ease; +} +.cb-user-chip:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0 var(--cb-ink); } +.cb-user-chip .name { font-size: 14px; color: var(--cb-ink); font-weight: 700; } + +/* ================= 页签栏 ================= */ +.cb-tabs { + display: flex; gap: 8px; padding: 10px 18px 0; + background: var(--cb-bg); border-bottom: var(--cb-border); + overflow-x: auto; flex: none; +} +.cb-tabs::-webkit-scrollbar { height: 6px; } +.cb-tab { + display: flex; align-items: center; gap: 8px; flex: none; + padding: 5px 10px 5px 14px; + border: 2.5px solid var(--cb-ink); border-bottom: none; + border-radius: 10px 10px 0 0; background: #fff; + font-size: 12.5px; font-weight: 800; color: var(--cb-ink-2); + cursor: pointer; user-select: none; white-space: nowrap; +} +.cb-tab:hover { background: var(--cb-blue); } +.cb-tab.on { background: var(--cb-yellow); color: var(--cb-ink); box-shadow: 3px 3px 0 var(--cb-ink); } +.cb-tab .x { + font-size: 15px; line-height: 1; color: var(--cb-ink-3); + border-radius: 4px; padding: 0 3px; font-weight: 700; +} +.cb-tab .x:hover { background: var(--cb-pink); color: var(--cb-ink); } + +.cb-content { + padding: 26px; + min-height: calc(100vh - 64px); + background: var(--cb-bg); +} + +/* ================= 通用面板 ================= */ +.panel { + background: var(--cb-panel); + border-radius: 16px; + padding: 20px 22px; + border: var(--cb-border); + box-shadow: var(--cb-shadow-sm); +} +.panel-title { + font-size: 15px; + font-weight: 800; + color: var(--cb-ink); + margin-bottom: 14px; + display: flex; + align-items: center; + gap: 12px; + flex-wrap: wrap; +} +.panel-title .hint { + font-size: 12px; + color: var(--cb-ink-3); + font-weight: 500; + letter-spacing: 0.3px; +} +.panel-title .hint::before { content: '· '; color: var(--cb-ink-3); } + +/* ================= 仪表盘 ================= */ +.dash-hello h2 { + margin: 0 0 4px; + font-size: 24px; + font-weight: 900; + color: var(--cb-ink); +} +.dash-hello p { margin: 0; color: var(--cb-ink-2); font-size: 13px; font-weight: 500; } + +.stat-card { + border-radius: 16px; + padding: 20px 22px; + background: var(--cb-panel); + border: var(--cb-border); + box-shadow: var(--cb-shadow-sm); + display: flex; + align-items: center; + gap: 16px; + transition: transform 0.15s ease, box-shadow 0.15s ease; +} +.stat-card:hover { + transform: translate(-2px, -2px); + box-shadow: var(--cb-shadow); +} +.stat-icon { + width: 52px; + height: 52px; + flex: none; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 26px; + color: var(--cb-ink); + border: 2.5px solid var(--cb-ink); +} +.stat-icon.i1 { background: var(--cb-yellow); } +.stat-icon.i2 { background: var(--cb-mint); } +.stat-icon.i3 { background: var(--cb-pink); } +.stat-icon.i4 { background: var(--cb-blue); } +.stat-label { font-size: 13px; color: var(--cb-ink-2); font-weight: 600; } +.stat-value { + font-size: 30px; + font-weight: 900; + color: var(--cb-ink); + line-height: 1.15; + margin-top: 3px; +} + +/* ================= 工具卡片 ================= */ +.tool-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 18px; +} +.tool-card { + background: var(--cb-panel); + border-radius: 16px; + padding: 22px; + cursor: pointer; + border: var(--cb-border); + box-shadow: var(--cb-shadow-sm); + transition: transform 0.15s ease, box-shadow 0.15s ease; + position: relative; +} +.tool-card:hover { + transform: translate(-3px, -3px) rotate(-0.5deg); + box-shadow: var(--cb-shadow-lg); +} +.tool-card .arrow { + position: absolute; + top: 18px; + right: 18px; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 2px solid var(--cb-ink); + background: var(--cb-yellow); + color: var(--cb-ink); + opacity: 0; + transform: translateX(-4px); + transition: all 0.15s ease; +} +.tool-card:hover .arrow { opacity: 1; transform: translateX(0); } +.tool-icon { + width: 50px; + height: 50px; + border-radius: 14px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; + color: var(--cb-ink); + border: 2.5px solid var(--cb-ink); + margin-bottom: 16px; +} +.tool-name { font-weight: 800; color: var(--cb-ink); margin-bottom: 6px; font-size: 15px; } +.tool-desc { font-size: 12px; color: var(--cb-ink-2); line-height: 1.6; min-height: 38px; font-weight: 500; } + +/* ---- 2FA 验证码:填充层整段线性 scaleX 收缩,右缘从右往左退去 ---- + duration 由内联 style 注入(=剩余秒数),transform 走 GPU 合成不触发布局 */ +.totp-chip { + position: relative; + display: inline-flex; + align-items: center; + overflow: hidden; + border: 2px solid var(--cb-ink); + border-radius: 9px; + background: #fff; + box-shadow: 3px 3px 0 var(--cb-ink); + user-select: none; +} +.totp-chip .totp-fill { + position: absolute; + inset: 0; + background: var(--cb-mint); + transform-origin: left center; + animation: totp-drain linear forwards; +} +.totp-chip.totp-low .totp-fill { background: var(--cb-pink); } +@keyframes totp-drain { + from { transform: scaleX(1); } + to { transform: scaleX(0); } +} +.totp-chip .totp-code { + position: relative; + padding: 3px 10px; + font-size: 13px; + font-weight: 800; + letter-spacing: 1.5px; + color: var(--cb-ink); +} diff --git a/frontend/src/views/Bookmarks.vue b/frontend/src/views/Bookmarks.vue new file mode 100644 index 0000000..8c88edf --- /dev/null +++ b/frontend/src/views/Bookmarks.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/frontend/src/views/Cashier.vue b/frontend/src/views/Cashier.vue new file mode 100644 index 0000000..ca9d61c --- /dev/null +++ b/frontend/src/views/Cashier.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue new file mode 100644 index 0000000..5cc750d --- /dev/null +++ b/frontend/src/views/Dashboard.vue @@ -0,0 +1,281 @@ + + + + + diff --git a/frontend/src/views/DbManager.vue b/frontend/src/views/DbManager.vue new file mode 100644 index 0000000..dfd3391 --- /dev/null +++ b/frontend/src/views/DbManager.vue @@ -0,0 +1,608 @@ + + + + + diff --git a/frontend/src/views/Disk.vue b/frontend/src/views/Disk.vue new file mode 100644 index 0000000..72193fb --- /dev/null +++ b/frontend/src/views/Disk.vue @@ -0,0 +1,247 @@ + + + + + diff --git a/frontend/src/views/DiskShares.vue b/frontend/src/views/DiskShares.vue new file mode 100644 index 0000000..471f6f3 --- /dev/null +++ b/frontend/src/views/DiskShares.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/frontend/src/views/LicenseApps.vue b/frontend/src/views/LicenseApps.vue new file mode 100644 index 0000000..1a3fdbb --- /dev/null +++ b/frontend/src/views/LicenseApps.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/frontend/src/views/Licenses.vue b/frontend/src/views/Licenses.vue new file mode 100644 index 0000000..4c6ce28 --- /dev/null +++ b/frontend/src/views/Licenses.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/frontend/src/views/Login.vue b/frontend/src/views/Login.vue new file mode 100644 index 0000000..d5d1c2f --- /dev/null +++ b/frontend/src/views/Login.vue @@ -0,0 +1,107 @@ + + + diff --git a/frontend/src/views/Memories.vue b/frontend/src/views/Memories.vue new file mode 100644 index 0000000..94eaeb8 --- /dev/null +++ b/frontend/src/views/Memories.vue @@ -0,0 +1,504 @@ + + + + + diff --git a/frontend/src/views/PayConfig.vue b/frontend/src/views/PayConfig.vue new file mode 100644 index 0000000..2ea9fe8 --- /dev/null +++ b/frontend/src/views/PayConfig.vue @@ -0,0 +1,356 @@ + + + + + diff --git a/frontend/src/views/PayOrders.vue b/frontend/src/views/PayOrders.vue new file mode 100644 index 0000000..aaad2ac --- /dev/null +++ b/frontend/src/views/PayOrders.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/frontend/src/views/Proxies.vue b/frontend/src/views/Proxies.vue new file mode 100644 index 0000000..6c95aae --- /dev/null +++ b/frontend/src/views/Proxies.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/frontend/src/views/ProxyExtract.vue b/frontend/src/views/ProxyExtract.vue new file mode 100644 index 0000000..4aa6e65 --- /dev/null +++ b/frontend/src/views/ProxyExtract.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/frontend/src/views/ServerDetail.vue b/frontend/src/views/ServerDetail.vue new file mode 100644 index 0000000..844c549 --- /dev/null +++ b/frontend/src/views/ServerDetail.vue @@ -0,0 +1,537 @@ + + + + + diff --git a/frontend/src/views/Servers.vue b/frontend/src/views/Servers.vue new file mode 100644 index 0000000..cf3d8be --- /dev/null +++ b/frontend/src/views/Servers.vue @@ -0,0 +1,213 @@ + + + + + diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue new file mode 100644 index 0000000..faa6935 --- /dev/null +++ b/frontend/src/views/Settings.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/frontend/src/views/Vault.vue b/frontend/src/views/Vault.vue new file mode 100644 index 0000000..b61fd79 --- /dev/null +++ b/frontend/src/views/Vault.vue @@ -0,0 +1,311 @@ + + + diff --git a/frontend/src/views/tools/ColorImage.vue b/frontend/src/views/tools/ColorImage.vue new file mode 100644 index 0000000..23d4eda --- /dev/null +++ b/frontend/src/views/tools/ColorImage.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/frontend/src/views/tools/ColorTool.vue b/frontend/src/views/tools/ColorTool.vue new file mode 100644 index 0000000..c79758a --- /dev/null +++ b/frontend/src/views/tools/ColorTool.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/frontend/src/views/tools/GradientTool.vue b/frontend/src/views/tools/GradientTool.vue new file mode 100644 index 0000000..a16cef0 --- /dev/null +++ b/frontend/src/views/tools/GradientTool.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/frontend/src/views/tools/JsonTool.vue b/frontend/src/views/tools/JsonTool.vue new file mode 100644 index 0000000..57206f6 --- /dev/null +++ b/frontend/src/views/tools/JsonTool.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/frontend/src/views/tools/MarkdownTool.vue b/frontend/src/views/tools/MarkdownTool.vue new file mode 100644 index 0000000..4d6df01 --- /dev/null +++ b/frontend/src/views/tools/MarkdownTool.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/frontend/src/views/tools/PostmanTool.vue b/frontend/src/views/tools/PostmanTool.vue new file mode 100644 index 0000000..c92acb7 --- /dev/null +++ b/frontend/src/views/tools/PostmanTool.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/frontend/src/views/tools/TimestampTool.vue b/frontend/src/views/tools/TimestampTool.vue new file mode 100644 index 0000000..9f929c3 --- /dev/null +++ b/frontend/src/views/tools/TimestampTool.vue @@ -0,0 +1,232 @@ + + + + + diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..323c78a --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..a02f4fe --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "noEmit": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.vue"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..8c97df6 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2023", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..34a677d --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// 开发期 /api 代理到 fun 后端并剥掉前缀:/api/cell → /cell +export default defineConfig({ + plugins: [vue()], + server: { + host: '0.0.0.0', + port: 5173, + proxy: { + '/api': { + target: 'http://localhost:8890', + changeOrigin: true, + ws: true, + rewrite: p => p.replace(/^\/api/, '') + } + } + } +})