@hot-updater/console 1.0.0-rc.2 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/ConsoleFeatureUnavailable-D0LPL1Gy.js +1 -0
- package/.output/public/assets/HashValueDisplay-BmuB4FW-.js +1 -0
- package/.output/public/assets/InsightsInfo--v4SC07c.js +1 -0
- package/.output/public/assets/InsightsPageHeader-dU83CAQY.js +1 -0
- package/.output/public/assets/NotFoundPage-B5mhGG2a.js +1 -0
- package/.output/public/assets/api-D-vOVlQ4.js +1 -0
- package/.output/public/assets/api-keys-Csh8ARKU.js +1 -0
- package/.output/public/assets/api-keys-OMPUoA_A.js +1 -0
- package/.output/public/assets/arrow-left-Bwq9X1eS.js +1 -0
- package/.output/public/assets/arrow-up-right-BkOVn4if.js +1 -0
- package/.output/public/assets/card-C_7d6O9a.js +1 -0
- package/.output/public/assets/console-features-api-C1jRZTGw.js +1 -0
- package/.output/public/assets/dialog-DE8wFN_P.js +1 -0
- package/.output/public/assets/dist-C7ENGzpS.js +1 -0
- package/.output/public/assets/empty-CVd5NlQD.js +1 -0
- package/.output/public/assets/field-C06q1iC9.js +1 -0
- package/.output/public/assets/index-BeWlQtO6.js +11 -0
- package/.output/public/assets/input-group-Bi61biIA.js +1 -0
- package/.output/public/assets/insights-BjnAtC0f.js +2 -0
- package/.output/public/assets/insights-D86m11yH.js +42 -0
- package/.output/public/assets/insights-DpYstMoY.js +1 -0
- package/.output/public/assets/insights-recovery-rpc-BIm5_ALc.js +1 -0
- package/.output/public/assets/insights-search-KIF7mRtB.js +1 -0
- package/.output/public/assets/insights-usage-rpc-VHj8vyBt.js +1 -0
- package/.output/public/assets/insights_.distribution-ADcrX48c.js +2 -0
- package/.output/public/assets/insights_.distribution-CuuJUN6Y.js +1 -0
- package/.output/public/assets/insights_.distribution-DJRjuXsO.js +1 -0
- package/.output/public/assets/installations-BSBBzqWs.js +2 -0
- package/.output/public/assets/installations-DqIE6WcO.js +1 -0
- package/.output/public/assets/installations-UsELbDvw.js +1 -0
- package/.output/public/assets/loader-circle-CnCBxzHp.js +1 -0
- package/.output/public/assets/mutation-Csp7xUNh.js +1 -0
- package/.output/public/assets/preload-helper-B-cOmpLH.js +1 -0
- package/.output/public/assets/refresh-cw-FF1NtzlQ.js +1 -0
- package/.output/public/assets/routes-CoN9XHP_.js +2 -0
- package/.output/public/assets/routes-DT1jB0Zi.js +1 -0
- package/.output/public/assets/sidebar-D8mJRF15.js +3 -0
- package/.output/public/assets/styles-Bwt7v6U5.css +2 -0
- package/.output/public/assets/table-WBgfvgXg.js +1 -0
- package/.output/public/assets/useCompositeListItem-CloBmDwn.js +1 -0
- package/.output/public/assets/useMutation-DO8cvsTx.js +1 -0
- package/.output/public/assets/useOpenInteractionType-ReGr9GzJ.js +1 -0
- package/.output/public/assets/valueToPercent-BvPAi4bP.js +1 -0
- package/.output/server/{__23tanstack-start-server-fn-resolver-B7ioJhtE.mjs → __23tanstack-start-server-fn-resolver-DMRD4e4f.mjs} +51 -47
- package/.output/server/_libs/@base-ui/react+[...].mjs +7044 -3622
- package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +3803 -4023
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +141 -98
- package/.output/server/_libs/d3-array.mjs +231 -0
- package/.output/server/_libs/d3-color.mjs +330 -0
- package/.output/server/_libs/d3-format.mjs +252 -0
- package/.output/server/_libs/d3-interpolate.mjs +207 -0
- package/.output/server/_libs/d3-path.mjs +77 -0
- package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
- package/.output/server/_libs/d3-shape.mjs +818 -0
- package/.output/server/_libs/decimal.js-light.mjs +884 -0
- package/.output/server/_libs/es-toolkit.mjs +1159 -0
- package/.output/server/_libs/eventemitter3.mjs +236 -0
- package/.output/server/_libs/immer.mjs +532 -0
- package/.output/server/_libs/lucide-react.mjs +46 -32
- package/.output/server/_libs/next-themes.mjs +2 -2
- package/.output/server/_libs/react-redux+[...].mjs +205 -0
- package/.output/server/_libs/recharts+[...].mjs +14350 -0
- package/.output/server/_libs/sonner.mjs +2 -2
- package/.output/server/_libs/tanstack__history.mjs +68 -41
- package/.output/server/_libs/tanstack__query-core.mjs +1618 -309
- package/.output/server/_libs/tanstack__react-query.mjs +262 -22
- package/.output/server/_libs/verkit.mjs +99 -1
- package/.output/server/_runtime.mjs +10 -1
- package/.output/server/_ssr/ConsoleFeatureUnavailable-Du7jNPNS.mjs +58 -0
- package/.output/server/_ssr/{HashValueDisplay-DwpGLfrl.mjs → HashValueDisplay-GQvFkXoi.mjs} +3 -3
- package/.output/server/_ssr/InsightsInfo-Do7QSaFR.mjs +37 -0
- package/.output/server/_ssr/InsightsPageHeader-DSUWUWRc.mjs +154 -0
- package/.output/server/_ssr/NotFoundPage-DsF4Vrhg.mjs +37 -0
- package/.output/server/_ssr/api-BEiVs0H7.mjs +203 -0
- package/.output/server/_ssr/api-keys-BrvBWGRf.mjs +5 -0
- package/.output/server/_ssr/{api-keys-CZeTPQys.mjs → api-keys-K5v32nf7.mjs} +49 -9
- package/.output/server/_ssr/api-keys-rpc-B96bBllm.mjs +52 -0
- package/.output/server/_ssr/api-rpc-DcUe280B.mjs +296 -0
- package/.output/server/_ssr/{auth-rpc-DUaeOWKK.mjs → auth-rpc-C68W56DP.mjs} +6 -6
- package/.output/server/_ssr/auth.server-DM1J6NwR.mjs +5712 -0
- package/.output/server/_ssr/bundleActivity-CpuY9CTP.mjs +19 -0
- package/.output/server/_ssr/card-CPAEKx0w.mjs +49 -0
- package/.output/server/_ssr/config-nsIcsHh8.mjs +15 -0
- package/.output/server/_ssr/config.server-DDCZk3Sq.mjs +36 -0
- package/.output/server/_ssr/console-access-Da-2Qd7q.mjs +17 -0
- package/.output/server/_ssr/console-features-Dzu2iUGD.mjs +55 -0
- package/.output/server/_ssr/console-features-api-xAsSgFa_.mjs +43 -0
- package/.output/server/_ssr/console-features-rpc-qARGpB24.mjs +19 -0
- package/.output/server/_ssr/{createServerFn-B2hcXoPM.mjs → createMiddleware-BdBf_ezN.mjs} +67 -56
- package/.output/server/_ssr/{createServerRpc-DB29C_5p.mjs → createServerRpc-B_y2lrwA.mjs} +2 -2
- package/.output/server/_ssr/{databaseJsonValue-d6I3nAS4.mjs → databaseJsonValue-Bhxciy7Z.mjs} +28 -5
- package/.output/server/_ssr/databaseRows-DwMAjaF8.mjs +74 -0
- package/.output/server/_ssr/{dialog-DS-nw-pA.mjs → dialog-q__BJ7mQ.mjs} +2 -2
- package/.output/server/_ssr/{dist-CyzPaI5v.mjs → dist-Cxo9ghYv.mjs} +140 -6
- package/.output/server/_ssr/{downloadBundle-CNnYxYO2.mjs → downloadBundle-BW9I-pMQ.mjs} +10 -10
- package/.output/server/_ssr/empty-DcBTAooP.mjs +49 -0
- package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CYdinE-i.mjs} +1 -1
- package/.output/server/_ssr/{table-CnZmon_Z.mjs → field-QCDBVpXA.mjs} +63 -51
- package/.output/server/_ssr/getBundleChildren-DIAGCF4a.mjs +22 -0
- package/.output/server/_ssr/{input-group-BpsnmvZw.mjs → input-group-hCk2nUoU.mjs} +2 -2
- package/.output/server/_ssr/insights-BfBO0RFr.mjs +1238 -0
- package/.output/server/_ssr/insights-DsAU3-TB.mjs +14 -0
- package/.output/server/_ssr/insights-RIfg447W.mjs +5 -0
- package/.output/server/_ssr/insights-api-CDn-QQHU.mjs +105 -0
- package/.output/server/_ssr/insights-failures-BBGMhuUr.mjs +44 -0
- package/.output/server/_ssr/insights-failures-rpc-rA2dctK2.mjs +21 -0
- package/.output/server/_ssr/insights-recovery-Dpk6Mfb9.mjs +28 -0
- package/.output/server/_ssr/insights-recovery-rpc-BCaF-xSd.mjs +46 -0
- package/.output/server/_ssr/insights-recovery-rpc-CcgYyUHk.mjs +20 -0
- package/.output/server/_ssr/{insights-rpc-38DujK0G.mjs → insights-rpc-keycmwzw.mjs} +22 -14
- package/.output/server/_ssr/insights-search-BRg5VsNe.mjs +25 -0
- package/.output/server/_ssr/insights-usage-cbw9n2Fq.mjs +29 -0
- package/.output/server/_ssr/insights-usage-rpc-BKgV1QDf.mjs +29 -0
- package/.output/server/_ssr/insights-usage-rpc-D-JYachy.mjs +41 -0
- package/.output/server/_ssr/insightsRecovery-B7oMc8d8.mjs +45 -0
- package/.output/server/_ssr/insightsUsage-Oa7kk5ek.mjs +56 -0
- package/.output/server/_ssr/insights_.distribution-BIqOqRW0.mjs +335 -0
- package/.output/server/_ssr/insights_.distribution-CuUrDa7P.mjs +5 -0
- package/.output/server/_ssr/insights_.distribution-RT_6x_ud.mjs +14 -0
- package/.output/server/_ssr/{installations-DiVKEF5T.mjs → installations-BO5CjX2z.mjs} +517 -54
- package/.output/server/_ssr/installations-BjaHiIfh.mjs +5 -0
- package/.output/server/_ssr/installations-Dt5luA-N.mjs +69 -0
- package/.output/server/_ssr/listReleases-BitHzOYB.mjs +79 -0
- package/.output/server/_ssr/releaseManagement-B3e7I8J9.mjs +11 -0
- package/.output/server/_ssr/{router-B9wG_9f8.mjs → router-DfVAixxe.mjs} +135 -139
- package/.output/server/_ssr/{routes-D7Yh_jfj.mjs → routes-C6LstNp8.mjs} +176 -455
- package/.output/server/_ssr/routes-Q3Uhmwwz.mjs +292 -0
- package/.output/server/_ssr/runtime.server-fL0o6I2b.mjs +1459 -0
- package/.output/server/_ssr/schema-BKY9SqaY.mjs +25 -0
- package/.output/server/_ssr/select-CUlInmjk.mjs +107 -0
- package/.output/server/_ssr/server-C43wq9ft.mjs +8330 -0
- package/.output/server/_ssr/{createSsrRpc-DjWNJ0uc.mjs → sidebar-DKbx2ach.mjs} +117 -63
- package/.output/server/_ssr/ssr.mjs +1 -1
- package/.output/server/_ssr/start-BXUOv_n8.mjs +4 -0
- package/.output/server/_ssr/table-NGWV8B-S.mjs +53 -0
- package/.output/server/_ssr/updateFailures-Jwe2Baab.mjs +24 -0
- package/.output/server/_ssr/{uuidv7-D0QFkPL_.mjs → uuidv7-C8bwiZr8.mjs} +2 -2
- package/.output/server/_tanstack-start-manifest_v-WQ5qGn8n.mjs +124 -0
- package/.output/server/index.mjs +267 -162
- package/LICENSE +13 -14
- package/README.md +82 -72
- package/lib/index.d.mts +23 -11
- package/lib/vite-internal.mjs +13 -3
- package/lib/vite.d.mts +2 -0
- package/package.json +14 -14
- package/src/components/AppSidebar.tsx +100 -71
- package/src/components/ConsoleAccessPage.tsx +5 -1
- package/src/components/ConsoleFeatureUnavailable.tsx +80 -0
- package/src/components/NotFoundPage.tsx +7 -7
- package/src/components/features/FeatureSlots.tsx +99 -0
- package/src/components/features/api-keys/apiKeysFeatureUi.tsx +15 -0
- package/src/components/features/bundles/BundleMetadata.tsx +8 -5
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
- package/src/components/features/channels/ChannelManagementDialog.tsx +2 -5
- package/src/components/features/insights/AppUsage.tsx +370 -0
- package/src/components/features/insights/EstimatedCount.tsx +18 -0
- package/src/components/features/insights/EventDetails.tsx +117 -37
- package/src/components/features/insights/EventHistoryCard.tsx +112 -26
- package/src/components/features/insights/EventHistoryList.tsx +2 -2
- package/src/components/features/insights/InsightsControls.tsx +265 -102
- package/src/components/features/insights/InsightsErrorAlert.tsx +14 -1
- package/src/components/features/insights/InsightsInfo.tsx +48 -0
- package/src/components/features/insights/InsightsOverview.tsx +176 -173
- package/src/components/features/insights/InsightsPageHeader.tsx +35 -20
- package/src/components/features/insights/InsightsPeriodSelector.tsx +89 -0
- package/src/components/features/insights/InstallationHistoryCard.tsx +52 -10
- package/src/components/features/insights/InstallationMatchesCard.tsx +1 -1
- package/src/components/features/insights/ReleaseActivity.tsx +188 -0
- package/src/components/features/insights/ReportingDevicesSummary.tsx +65 -0
- package/src/components/features/insights/UpdateFailures.tsx +319 -0
- package/src/components/features/insights/insightsFeatureUi.tsx +35 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +30 -7
- package/src/components/ui/card.tsx +1 -1
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/scroll-area.tsx +52 -0
- package/src/components/ui/sidebar.tsx +22 -29
- package/src/components/ui/tabs.tsx +81 -0
- package/src/index.ts +26 -19
- package/src/lib/api-keys-api.ts +0 -28
- package/src/lib/api-keys-rpc.ts +27 -38
- package/src/lib/api-rpc.ts +189 -266
- package/src/lib/api.ts +19 -102
- package/src/lib/bundle-activity.ts +31 -0
- package/src/lib/console-access.ts +20 -0
- package/src/lib/console-features-api.ts +68 -0
- package/src/lib/console-features-rpc.ts +13 -0
- package/src/lib/console-features.ts +91 -0
- package/src/lib/insights-api.ts +37 -1
- package/src/lib/insights-failures-rpc.ts +21 -0
- package/src/lib/insights-failures.ts +129 -0
- package/src/lib/insights-recovery-rpc.ts +51 -0
- package/src/lib/insights-recovery.ts +68 -0
- package/src/lib/insights-retention.ts +21 -0
- package/src/lib/insights-rpc.ts +30 -12
- package/src/lib/insights-search.ts +58 -0
- package/src/lib/insights-usage-rpc.ts +29 -0
- package/src/lib/insights-usage.ts +74 -0
- package/src/lib/insights-view.ts +38 -2
- package/src/lib/scroll-restoration.ts +21 -0
- package/src/lib/server/adminInsights.ts +124 -0
- package/src/lib/server/auth.server.ts +13 -5
- package/src/lib/server/bundleActivity.ts +37 -0
- package/src/lib/server/config.server.ts +21 -74
- package/src/lib/server/console-feature-apis.ts +59 -0
- package/src/lib/server/console-runtime.server.ts +82 -38
- package/src/lib/server/downloadBundle.ts +16 -15
- package/src/lib/server/getBundleChildren.ts +31 -122
- package/src/lib/server/insightsRecovery.ts +55 -0
- package/src/lib/server/insightsUsage.ts +63 -0
- package/src/lib/server/listReleases.ts +109 -44
- package/src/lib/server/releaseReachability.ts +8 -7
- package/src/lib/server/runtime.server.ts +120 -31
- package/src/lib/server/updateFailures.ts +31 -0
- package/src/routeTree.gen.ts +44 -44
- package/src/router.tsx +3 -2
- package/src/routes/-installations-search.ts +32 -8
- package/src/routes/-releases-search.ts +76 -29
- package/src/routes/__root.tsx +10 -4
- package/src/routes/api/bundles/$bundleId/download.ts +2 -5
- package/src/routes/api-keys.tsx +5 -2
- package/src/routes/index.tsx +167 -104
- package/src/routes/insights.tsx +100 -59
- package/src/routes/insights_.distribution.tsx +398 -0
- package/src/routes/installations.tsx +98 -41
- package/src/vite-internal.ts +15 -3
- package/src/vite.ts +2 -0
- package/.output/public/assets/HashValueDisplay-CB0Eskit.js +0 -1
- package/.output/public/assets/InsightsPageHeader-Car5LNiZ.js +0 -1
- package/.output/public/assets/api-keys-BzKocqRy.js +0 -1
- package/.output/public/assets/api-keys-api-BHcODB7q.js +0 -1
- package/.output/public/assets/card-Dhr_eFYj.js +0 -1
- package/.output/public/assets/chart-no-axes-combined-Dy4WJv2U.js +0 -1
- package/.output/public/assets/composite-Bp98A0Ld.js +0 -1
- package/.output/public/assets/createServerFn-CA8fs7QD.js +0 -3
- package/.output/public/assets/dialog-CUTrcPlB.js +0 -1
- package/.output/public/assets/dist-C_NydL0e.js +0 -1
- package/.output/public/assets/index-DdAz-CXv.js +0 -10
- package/.output/public/assets/input-group-cSOG4wh_.js +0 -1
- package/.output/public/assets/insights-C0PBSZ-n.js +0 -1
- package/.output/public/assets/installations-CE33wgbY.js +0 -2
- package/.output/public/assets/installations-CStZja8x.js +0 -1
- package/.output/public/assets/lazyRouteComponent-Ccp3bwBY.js +0 -1
- package/.output/public/assets/link-CI8idAUi.js +0 -1
- package/.output/public/assets/loader-circle-BoxLrsOu.js +0 -1
- package/.output/public/assets/preload-helper-CQ36nxok.js +0 -1
- package/.output/public/assets/refresh-cw-Bqc72UZG.js +0 -1
- package/.output/public/assets/routes-DGVXdzpw.js +0 -2
- package/.output/public/assets/routes-DTlwOr8I.js +0 -1
- package/.output/public/assets/shield-check-uK3MHWJZ.js +0 -1
- package/.output/public/assets/signing-CMf4aqDO.js +0 -1
- package/.output/public/assets/styles-BCvbtKsm.css +0 -2
- package/.output/public/assets/table-2vcV89QB.js +0 -1
- package/.output/public/assets/useCompositeListItem-CHLITE_4.js +0 -1
- package/.output/public/assets/x-DCd0j8c6.js +0 -1
- package/.output/server/_ssr/InsightsPageHeader-Df4Brj8u.mjs +0 -484
- package/.output/server/_ssr/api-keys-api-BUPQVSpk.mjs +0 -60
- package/.output/server/_ssr/api-keys-rpc-Cs7tS5_j.mjs +0 -136
- package/.output/server/_ssr/api-rpc-tAupfGQm.mjs +0 -1399
- package/.output/server/_ssr/auth.server-DW71WzsD.mjs +0 -47
- package/.output/server/_ssr/bundle-signing-rpc-BhnBw0-d.mjs +0 -16
- package/.output/server/_ssr/bundle-signing.server-C4NW5mWx.mjs +0 -12
- package/.output/server/_ssr/card-BRiPmi0C.mjs +0 -108
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +0 -13
- package/.output/server/_ssr/config.server-afDuAwaG.mjs +0 -368
- package/.output/server/_ssr/databaseRows-5bCRTxm-.mjs +0 -126
- package/.output/server/_ssr/deleteBundle-BTGwBgzx.mjs +0 -66
- package/.output/server/_ssr/getBundleChildren-C0vh4ZvE.mjs +0 -50
- package/.output/server/_ssr/insights-BdS364Xu.mjs +0 -460
- package/.output/server/_ssr/insightsContract-fjJDBeEE.mjs +0 -303
- package/.output/server/_ssr/installations-CtA59MQG.mjs +0 -29
- package/.output/server/_ssr/routes-CKrrS2q2.mjs +0 -37
- package/.output/server/_ssr/runtime.server-DacU1TBV.mjs +0 -438
- package/.output/server/_ssr/server-D_C2rMzf.mjs +0 -7468
- package/.output/server/_ssr/signing-Dz1WkZOf.mjs +0 -133
- package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-2MjTyxil.mjs +0 -97
- package/src/components/features/insights/InsightsBundleReports.tsx +0 -51
- package/src/components/features/signing/BundleSigningPage.tsx +0 -139
- package/src/lib/bundle-signing-api.ts +0 -14
- package/src/lib/bundle-signing-rpc.ts +0 -16
- package/src/lib/server/bundle-signing.server.ts +0 -23
- package/src/lib/server/deleteBundle.ts +0 -145
- package/src/routes/signing.tsx +0 -37
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Hot Updater Console
|
|
1
|
+
# Hot Updater Console
|
|
2
2
|
|
|
3
3
|
Modern web-based management console for Hot Updater built with **TanStack Start** and **shadcn/ui**.
|
|
4
4
|
|
|
@@ -6,11 +6,11 @@ Modern web-based management console for Hot Updater built with **TanStack Start*
|
|
|
6
6
|
|
|
7
7
|
- **Bundle Management** - View, filter, and manage OTA update bundles
|
|
8
8
|
- **Real-time Filtering** - Filter by platform (iOS/Android) and channel
|
|
9
|
-
- **
|
|
9
|
+
- **Release Editor** - Edit delivery policy, rollout, and target cohorts
|
|
10
10
|
- **Rollout Control** - Adjust rollout percentage with visual slider
|
|
11
11
|
- **Rollout Statistics** - View deployment metrics and success rates
|
|
12
|
-
- **Channel Promotion** - Copy or move
|
|
13
|
-
- **
|
|
12
|
+
- **Channel Promotion** - Copy or move releases between channels
|
|
13
|
+
- **Rollback** - Disable a release and save; devices reconsider it on their next update check
|
|
14
14
|
- **Dark Mode** - Full dark mode support with system preference detection
|
|
15
15
|
- **Responsive Design** - Works seamlessly on desktop, tablet, and mobile
|
|
16
16
|
|
|
@@ -21,13 +21,11 @@ Modern web-based management console for Hot Updater built with **TanStack Start*
|
|
|
21
21
|
- **TanStack Start** - Full-stack React framework with SSR
|
|
22
22
|
- **TanStack Router** - File-based routing with type safety
|
|
23
23
|
- **TanStack Query** - Data fetching and caching
|
|
24
|
-
- **TanStack Form** - Form state management and validation
|
|
25
|
-
- **TanStack Table** - Powerful table with sorting and pagination
|
|
26
24
|
- **React 19** - Latest React features
|
|
27
25
|
|
|
28
26
|
### UI Components
|
|
29
27
|
|
|
30
|
-
- **shadcn/ui** - High-quality accessible components
|
|
28
|
+
- **shadcn/ui** - High-quality accessible components built on Base UI
|
|
31
29
|
- **Tailwind CSS v4** - Utility-first CSS with oklch color system
|
|
32
30
|
- **Lucide React** - Beautiful icon library
|
|
33
31
|
- **Sonner** - Toast notifications
|
|
@@ -35,32 +33,38 @@ Modern web-based management console for Hot Updater built with **TanStack Start*
|
|
|
35
33
|
### Backend
|
|
36
34
|
|
|
37
35
|
- **TanStack Start Server Functions** - Type-safe server endpoints
|
|
38
|
-
- **Hot Updater Integration** - Storage and database
|
|
36
|
+
- **Hot Updater Integration** - Storage and database adapter integration
|
|
39
37
|
|
|
40
38
|
## 📦 Installation
|
|
41
39
|
|
|
42
40
|
```bash
|
|
43
|
-
# Install dependencies (from monorepo root)
|
|
41
|
+
# Install and build workspace dependencies (from monorepo root)
|
|
44
42
|
pnpm install
|
|
43
|
+
pnpm -w build
|
|
45
44
|
```
|
|
46
45
|
|
|
47
46
|
## 🏃♂️ Development
|
|
48
47
|
|
|
49
48
|
```bash
|
|
50
|
-
#
|
|
51
|
-
|
|
49
|
+
# Run these commands from the monorepo root
|
|
50
|
+
# Start the local development server
|
|
51
|
+
pnpm --dir packages/console dev
|
|
52
52
|
|
|
53
|
-
# Build
|
|
54
|
-
pnpm build
|
|
55
|
-
|
|
56
|
-
# Preview production build
|
|
57
|
-
pnpm preview
|
|
53
|
+
# Build and preview the local console package
|
|
54
|
+
pnpm --dir packages/console build
|
|
55
|
+
pnpm --dir packages/console preview
|
|
58
56
|
|
|
59
57
|
# Type checking
|
|
60
|
-
pnpm test:type
|
|
58
|
+
pnpm --dir packages/console test:type
|
|
61
59
|
```
|
|
62
60
|
|
|
63
|
-
The
|
|
61
|
+
The development server is available at `http://localhost:3000`.
|
|
62
|
+
|
|
63
|
+
This package's Vite configuration uses the local console adapter, which grants
|
|
64
|
+
management access without sign-in in both development and built output. Keep
|
|
65
|
+
this build and preview local. For production hosting, follow the
|
|
66
|
+
[Console deployment guide](https://hot-updater.dev/docs/guides/console-deployment)
|
|
67
|
+
and configure its hosted template with a `ConsoleAuthAdapter`.
|
|
64
68
|
|
|
65
69
|
## 📁 Project Structure
|
|
66
70
|
|
|
@@ -68,26 +72,27 @@ The console will be available at `http://localhost:3000`.
|
|
|
68
72
|
src/
|
|
69
73
|
├── routes/
|
|
70
74
|
│ ├── __root.tsx # Root layout with providers
|
|
71
|
-
│ ├── index.tsx # Bundle list page
|
|
72
|
-
│
|
|
75
|
+
│ ├── index.tsx # Bundle/release list page
|
|
76
|
+
│ ├── -releases-search.ts # URL filter validation
|
|
77
|
+
│ └── api/ # Authentication and bundle downloads
|
|
73
78
|
├── components/
|
|
74
79
|
│ ├── ui/ # shadcn components
|
|
75
80
|
│ ├── features/
|
|
76
|
-
│ │
|
|
81
|
+
│ │ ├── bundles/ # Bundle metadata and rollout details
|
|
82
|
+
│ │ └── releases/ # Release policy editor
|
|
77
83
|
│ ├── PlatformIcon.tsx # iOS/Android icons
|
|
78
84
|
│ ├── BundleIdDisplay.tsx # Truncated bundle ID with tooltip
|
|
79
85
|
│ ├── RolloutPercentageBadge.tsx
|
|
80
86
|
│ ├── TimestampDisplay.tsx # UUIDv7 timestamp formatting
|
|
81
87
|
│ ├── ChannelBadge.tsx
|
|
82
88
|
│ └── EnabledStatusIcon.tsx
|
|
83
|
-
├── hooks/
|
|
84
|
-
│ └── useFilterParams.ts # URL-based filter state
|
|
85
89
|
├── lib/
|
|
86
90
|
│ ├── api.ts # React Query hooks
|
|
91
|
+
│ ├── api-rpc.ts # TanStack Start server functions
|
|
87
92
|
│ ├── constants.ts # Shared constants
|
|
88
93
|
│ ├── utils.ts # Utility functions
|
|
89
94
|
│ └── server/
|
|
90
|
-
│ ├──
|
|
95
|
+
│ ├── auth.server.ts # Management access checks
|
|
91
96
|
│ └── config.server.ts # Hot Updater config loader
|
|
92
97
|
└── styles.css # Global styles & theme variables
|
|
93
98
|
```
|
|
@@ -96,64 +101,68 @@ src/
|
|
|
96
101
|
|
|
97
102
|
### Bundle List Page
|
|
98
103
|
|
|
99
|
-
- **
|
|
100
|
-
- **
|
|
101
|
-
-
|
|
104
|
+
- **BundlesPage** - Release-backed list with server-side pagination (20 per page)
|
|
105
|
+
- **BundleFilterToolbar** - Platform, channel, and enabled-state filters
|
|
106
|
+
- Both live in `src/routes/index.tsx`.
|
|
102
107
|
|
|
103
|
-
###
|
|
108
|
+
### Release Editor Sheet
|
|
104
109
|
|
|
105
|
-
- **
|
|
106
|
-
- **BundleEditorForm** - TanStack Form with validation
|
|
110
|
+
- **ReleaseEditorSheet** - Slide-out editor for release policy, rollout, and promotion
|
|
107
111
|
- **BundleMetadata** - Read-only bundle information display
|
|
112
|
+
- Saving preflights the policy, then updates it with the expected release revision.
|
|
113
|
+
- To roll back a release, turn off **Enabled** and select **Save changes**.
|
|
114
|
+
On the next update check, devices select a previous eligible release or the
|
|
115
|
+
built-in app when none remains; the app still controls applying the result.
|
|
108
116
|
|
|
109
117
|
### Dialogs
|
|
110
118
|
|
|
111
|
-
- **
|
|
112
|
-
- **
|
|
113
|
-
- **
|
|
119
|
+
- **ReleaseEditorSheet** contains promotion and release-deletion dialogs.
|
|
120
|
+
- **RolloutCohortsDialog** displays rollout and targeted cohorts.
|
|
121
|
+
- **ChannelManagementDialog** creates and deletes channels.
|
|
114
122
|
|
|
115
123
|
## 🔌 API Integration
|
|
116
124
|
|
|
117
|
-
The console integrates with the configured Hot Updater storage and database
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
|
|
124
|
-
- `
|
|
125
|
-
- `
|
|
125
|
+
The console integrates with the configured Hot Updater storage and database
|
|
126
|
+
adapters through TanStack Start server functions in `src/lib/api-rpc.ts`.
|
|
127
|
+
Functions with input take a `{ data: input }` argument:
|
|
128
|
+
|
|
129
|
+
- `getConfig()` and `getChannels()` load configuration and channels.
|
|
130
|
+
- `getReleases({ data: { filter, limit, beforeReleaseId, afterReleaseId } })`
|
|
131
|
+
lists releases using the supported `ReleaseFilter` shapes.
|
|
132
|
+
- `getRelease({ data: { releaseId } })` reads a release.
|
|
133
|
+
- `preflightRelease({ data: { releaseId, expectedRevision, patch } })` validates
|
|
134
|
+
a `ReleasePolicyPatch`; `updateRelease` takes the same input to save it.
|
|
135
|
+
- `deleteRelease({ data: { releaseId, expectedRevision } })` deletes a release.
|
|
136
|
+
- `promoteRelease({ data: { releaseId, expectedRevision, targetChannel, action } })`
|
|
137
|
+
copies or moves a release; `action` is `"copy"` or `"move"`.
|
|
138
|
+
- `getBundles({ data: { platform, limit, after, before } })` lists bundle artifacts;
|
|
139
|
+
`getBundle({ data: { bundleId } })` reads one and
|
|
140
|
+
`deleteBundle({ data: { bundleId } })` deletes one.
|
|
141
|
+
|
|
142
|
+
Use one pagination cursor at a time.
|
|
126
143
|
|
|
127
144
|
## 🎯 Configuration
|
|
128
145
|
|
|
129
|
-
|
|
146
|
+
The console reads `database`, `storage`, and `plugins` from
|
|
147
|
+
[hot-updater.config.ts](hot-updater.config.ts), as the CLI does, and runs the
|
|
148
|
+
plugins over the database as the server does. The checked-in
|
|
149
|
+
[demo database](demoDatabase.ts) seeds sample bundles, releases, and Insights
|
|
150
|
+
events through `assembleServer` from `@hot-updater/cli-tools`, over
|
|
151
|
+
`database.withoutLatency()`. For an empty local mock console, use this
|
|
152
|
+
`hot-updater.config.ts`:
|
|
130
153
|
|
|
131
154
|
```typescript
|
|
132
|
-
import {
|
|
133
|
-
createMockDatabaseData,
|
|
134
|
-
mockDatabase,
|
|
135
|
-
mockStorage,
|
|
136
|
-
} from "@hot-updater/mock";
|
|
137
|
-
import { bundleToRow, type Bundle } from "@hot-updater/plugin-core";
|
|
138
|
-
|
|
139
|
-
const bundles: Bundle[] = [
|
|
140
|
-
// ... your bundles
|
|
141
|
-
];
|
|
142
|
-
|
|
143
|
-
const databaseData = createMockDatabaseData();
|
|
144
|
-
for (const bundle of bundles) {
|
|
145
|
-
databaseData.bundles.set(bundle.id, bundleToRow(bundle));
|
|
146
|
-
}
|
|
155
|
+
import { mockDatabase, mockStorage } from "@hot-updater/mock";
|
|
147
156
|
|
|
148
157
|
export default {
|
|
158
|
+
database: mockDatabase({ latency: { min: 500, max: 700 } }),
|
|
149
159
|
storage: mockStorage({}),
|
|
150
|
-
database: mockDatabase({
|
|
151
|
-
latency: { min: 500, max: 700 },
|
|
152
|
-
data: databaseData,
|
|
153
|
-
}),
|
|
154
160
|
};
|
|
155
161
|
```
|
|
156
162
|
|
|
163
|
+
Add `plugins`, such as `[insights(), apiKeys()]`, to show their pages. Mock
|
|
164
|
+
data is held in memory and resets when the configuration is reloaded.
|
|
165
|
+
|
|
157
166
|
## 🌈 Theming
|
|
158
167
|
|
|
159
168
|
The console uses Tailwind CSS v4 with oklch color space for accessible colors. Theme variables are defined in `src/styles.css`:
|
|
@@ -167,23 +176,23 @@ The console uses Tailwind CSS v4 with oklch color space for accessible colors. T
|
|
|
167
176
|
- Full TypeScript strict mode
|
|
168
177
|
- Type-safe server functions with TanStack Start
|
|
169
178
|
- Type-safe routing with TanStack Router
|
|
170
|
-
-
|
|
179
|
+
- Typed release-policy drafts and revision-aware mutations
|
|
171
180
|
|
|
172
181
|
## 📊 Data Flow
|
|
173
182
|
|
|
174
|
-
1. **URL State** → `
|
|
175
|
-
2. **Server Functions** → TanStack Start server functions call the configured storage and database
|
|
176
|
-
3. **React Query** → `
|
|
183
|
+
1. **URL State** → `Route.useSearch()` and `-releases-search.ts` manage release filters
|
|
184
|
+
2. **Server Functions** → TanStack Start server functions call the configured storage and database adapters
|
|
185
|
+
3. **React Query** → `useReleasesQuery()` and `useBundleQuery()` fetch and cache data
|
|
177
186
|
4. **UI Components** → Display data with shadcn components
|
|
178
|
-
5. **Mutations** → `
|
|
179
|
-
6. **Cache Invalidation** →
|
|
187
|
+
5. **Mutations** → `usePreflightReleaseMutation()` validates changes before `useUpdateReleaseMutation()` saves them
|
|
188
|
+
6. **Cache Invalidation** → Successful updates invalidate release, catalog, and channel queries
|
|
180
189
|
|
|
181
190
|
## 🚦 Development Guidelines
|
|
182
191
|
|
|
183
192
|
- **Server-only code** must use `.server.ts` extension
|
|
184
193
|
- **Client-side constants** live in `src/lib/constants.ts`
|
|
185
194
|
- **shadcn components** are customizable in `src/components/ui/`
|
|
186
|
-
- **
|
|
195
|
+
- **Release validation** checks the draft locally and preflights changes on the server
|
|
187
196
|
- **Toast notifications** use Sonner for success/error feedback
|
|
188
197
|
|
|
189
198
|
## 🐛 Troubleshooting
|
|
@@ -196,12 +205,13 @@ The console uses Tailwind CSS v4 with oklch color space for accessible colors. T
|
|
|
196
205
|
### Development Server
|
|
197
206
|
|
|
198
207
|
- Default port is 3000
|
|
199
|
-
- Change port: `pnpm dev --port 3001`
|
|
208
|
+
- Change port: `pnpm --dir packages/console dev --port 3001`
|
|
200
209
|
|
|
201
210
|
### Hot Updater Config
|
|
202
211
|
|
|
203
|
-
- Ensure `hot-updater.config.ts` is at package root
|
|
204
|
-
|
|
212
|
+
- Ensure `hot-updater.config.ts` is at the package root and sets `database`
|
|
213
|
+
and `storage`
|
|
214
|
+
- Verify its storage and database adapters are correctly initialized
|
|
205
215
|
|
|
206
216
|
## 📝 License
|
|
207
217
|
|
package/lib/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyHotUpdaterPlugin, ConfiguredDatabase, StorageAdapter } from "@hot-updater/plugin-core";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
type ConsoleAuthProvider = "google" | "github";
|
|
4
4
|
type ConsolePrincipal = Readonly<{
|
|
@@ -20,17 +20,29 @@ type ConsoleAuthAdapter = Readonly<{
|
|
|
20
20
|
getAccess(request: Request): Promise<ConsoleAccess>;
|
|
21
21
|
getProviders(request: Request): Promise<readonly ConsoleAuthProvider[]>;
|
|
22
22
|
}>;
|
|
23
|
-
type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
type HotUpdaterConsoleConfig = Readonly<{
|
|
24
|
+
/**
|
|
25
|
+
* The database your server runs on, such as `dynamoDB(...)`, or
|
|
26
|
+
* `standaloneRepository(...)`, which reaches a self-hosted server through
|
|
27
|
+
* its admin API.
|
|
28
|
+
*/
|
|
29
|
+
database: ConfiguredDatabase;
|
|
30
|
+
/** The storage your server lists; the console reads bundle files with it. */
|
|
31
|
+
storage: StorageAdapter;
|
|
32
|
+
/**
|
|
33
|
+
* The plugins your server runs, such as the `plugins` a managed provider
|
|
34
|
+
* package exports or `[insights(), apiKeys()]`. The console runs them over
|
|
35
|
+
* `database` as the server does, and shows only the built-in features of
|
|
36
|
+
* the plugins listed. With `standaloneRepository`, they run on the server,
|
|
37
|
+
* and the console shows the features its admin API serves.
|
|
38
|
+
*/
|
|
39
|
+
plugins?: readonly AnyHotUpdaterPlugin[];
|
|
40
|
+
console?: {
|
|
41
|
+
/** The Git repository whose commits the console links bundles to. */
|
|
42
|
+
gitUrl?: string;
|
|
43
|
+
};
|
|
32
44
|
}>;
|
|
33
45
|
type HotUpdaterConsoleConfigSource = HotUpdaterConsoleConfig | ((request: Request) => HotUpdaterConsoleConfig | Promise<HotUpdaterConsoleConfig>);
|
|
34
46
|
declare const defineConsoleConfig: <const TConfig extends HotUpdaterConsoleConfigSource>(config: TConfig) => TConfig;
|
|
35
47
|
//#endregion
|
|
36
|
-
export { ConsoleAccess, ConsoleAuthAdapter, ConsoleAuthProvider, ConsolePrincipal,
|
|
48
|
+
export { ConsoleAccess, ConsoleAuthAdapter, ConsoleAuthProvider, ConsolePrincipal, HotUpdaterConsoleConfig, HotUpdaterConsoleConfigSource, defineConsoleConfig };
|
package/lib/vite-internal.mjs
CHANGED
|
@@ -38,7 +38,7 @@ const createConsoleModulesPlugin = (mode) => {
|
|
|
38
38
|
const cacheDirectory = path.join(root, ".hot-updater/console");
|
|
39
39
|
generatedRouteTreeFile = path.join(cacheDirectory, "routeTree.gen.ts");
|
|
40
40
|
authFile = resolveFromRoot(root, mode.options.auth ?? "console.auth.ts");
|
|
41
|
-
configFile = resolveFromRoot(root, mode.options.config ?? "
|
|
41
|
+
configFile = resolveFromRoot(root, mode.options.config ?? "console.config.ts");
|
|
42
42
|
writeFileIfChanged(path.join(cacheDirectory, "router.ts"), [`export { getRouter } from ${JSON.stringify(virtualPackageRouterModuleId)};`, ""].join("\n"));
|
|
43
43
|
}
|
|
44
44
|
return {
|
|
@@ -72,11 +72,21 @@ const createConsoleModulesPlugin = (mode) => {
|
|
|
72
72
|
"import { loadConfig } from \"@hot-updater/cli-tools\";",
|
|
73
73
|
"export default async () => {",
|
|
74
74
|
" const config = await loadConfig(null);",
|
|
75
|
+
" if (config.database === undefined) {",
|
|
76
|
+
" throw new Error(",
|
|
77
|
+
" \"Set database in hot-updater.config.ts: the database your server runs on, such as d1Database(...), or standaloneRepository({ baseUrl }) to reach a self-hosted server through its admin API.\",",
|
|
78
|
+
" );",
|
|
79
|
+
" }",
|
|
80
|
+
" if (config.storage === undefined) {",
|
|
81
|
+
" throw new Error(",
|
|
82
|
+
" \"Set storage in hot-updater.config.ts: where your server stores bundles, which the console reads, such as r2Storage(...).\",",
|
|
83
|
+
" );",
|
|
84
|
+
" }",
|
|
75
85
|
" return {",
|
|
76
|
-
" console: { gitUrl: config.console.gitUrl },",
|
|
77
86
|
" database: config.database,",
|
|
78
|
-
" signing: config.signing,",
|
|
79
87
|
" storage: config.storage,",
|
|
88
|
+
" plugins: config.plugins,",
|
|
89
|
+
" console: { gitUrl: config.console.gitUrl },",
|
|
80
90
|
" };",
|
|
81
91
|
"};"
|
|
82
92
|
].join("\n");
|
package/lib/vite.d.mts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { PluginOption } from "vite";
|
|
2
2
|
//#region src/vite.d.ts
|
|
3
3
|
interface HotUpdaterConsolePluginOptions {
|
|
4
|
+
/** The console's auth module, `console.auth.ts` by default. */
|
|
4
5
|
readonly auth?: string;
|
|
6
|
+
/** The module whose default export is `defineConsoleConfig(...)`, `console.config.ts` by default. */
|
|
5
7
|
readonly config?: string;
|
|
6
8
|
}
|
|
7
9
|
declare const hotUpdaterConsole: (options?: HotUpdaterConsolePluginOptions) => PluginOption[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/console",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.21",
|
|
5
5
|
"files": [
|
|
6
6
|
".output",
|
|
7
7
|
"lib",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"@fontsource-variable/inter": "^5.2.8",
|
|
39
39
|
"@tailwindcss/vite": "^4.2.1",
|
|
40
40
|
"@tanstack/react-devtools": "^0.10.0",
|
|
41
|
-
"@tanstack/react-query": "^5.
|
|
42
|
-
"@tanstack/react-router": "^1.
|
|
43
|
-
"@tanstack/react-router-devtools": "^1.
|
|
44
|
-
"@tanstack/react-router-ssr-query": "^1.
|
|
45
|
-
"@tanstack/react-start": "^1.
|
|
41
|
+
"@tanstack/react-query": "^5.104.0",
|
|
42
|
+
"@tanstack/react-router": "^1.170.41",
|
|
43
|
+
"@tanstack/react-router-devtools": "^1.167.2",
|
|
44
|
+
"@tanstack/react-router-ssr-query": "^1.167.3",
|
|
45
|
+
"@tanstack/react-start": "^1.168.60",
|
|
46
46
|
"@vitejs/plugin-react": "^6.0.1",
|
|
47
47
|
"class-variance-authority": "^0.7.1",
|
|
48
48
|
"clsx": "^2.1.1",
|
|
@@ -58,16 +58,16 @@
|
|
|
58
58
|
"tailwindcss": "^4.2.1",
|
|
59
59
|
"tw-animate-css": "^1.4.0",
|
|
60
60
|
"verkit": "0.4.0",
|
|
61
|
-
"@hot-updater/bsdiff": "1.0.0-rc.
|
|
62
|
-
"@hot-updater/
|
|
63
|
-
"@hot-updater/
|
|
64
|
-
"@hot-updater/core": "1.0.0-rc.
|
|
61
|
+
"@hot-updater/bsdiff": "1.0.0-rc.21",
|
|
62
|
+
"@hot-updater/protocol": "1.0.0-rc.21",
|
|
63
|
+
"@hot-updater/server": "1.0.0-rc.21",
|
|
64
|
+
"@hot-updater/plugin-core": "1.0.0-rc.21"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@tanstack/devtools-vite": "^0.6.0",
|
|
68
68
|
"@tanstack/react-form": "^1.28.5",
|
|
69
69
|
"@tanstack/react-table": "^8.21.3",
|
|
70
|
-
"@tanstack/router-plugin": "^1.
|
|
70
|
+
"@tanstack/router-plugin": "^1.168.42",
|
|
71
71
|
"@testing-library/dom": "^10.4.0",
|
|
72
72
|
"@testing-library/react": "^16.3.2",
|
|
73
73
|
"@types/node": "^25.5.0",
|
|
@@ -86,11 +86,11 @@
|
|
|
86
86
|
"vite-tsconfig-paths": "^6.1.1",
|
|
87
87
|
"vitest": "4.1.4",
|
|
88
88
|
"web-vitals": "^5.1.0",
|
|
89
|
-
"@hot-updater/cli-tools": "1.0.0-rc.
|
|
90
|
-
"@hot-updater/mock": "1.0.0-rc.
|
|
89
|
+
"@hot-updater/cli-tools": "1.0.0-rc.21",
|
|
90
|
+
"@hot-updater/mock": "1.0.0-rc.21"
|
|
91
91
|
},
|
|
92
92
|
"description": "React Native OTA solution for self-hosted",
|
|
93
|
-
"license": "
|
|
93
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
94
94
|
"repository": {
|
|
95
95
|
"type": "git",
|
|
96
96
|
"url": "https://github.com/gronxb/hot-updater"
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { Link, useRouterState } from "@tanstack/react-router";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Moon,
|
|
6
|
-
Package,
|
|
7
|
-
ShieldCheck,
|
|
8
|
-
Sun,
|
|
9
|
-
} from "lucide-react";
|
|
2
|
+
import { LogOut, Moon, Package, Sun } from "lucide-react";
|
|
3
|
+
import { type ReactElement, type ReactNode, useState } from "react";
|
|
4
|
+
import { toast } from "sonner";
|
|
10
5
|
|
|
6
|
+
import { useFeatureUi } from "@/components/features/FeatureSlots";
|
|
11
7
|
import { HotUpdaterLogo } from "@/components/HotUpdaterLogo";
|
|
12
8
|
import { useTheme } from "@/components/ThemeProvider";
|
|
13
9
|
import {
|
|
@@ -21,26 +17,84 @@ import {
|
|
|
21
17
|
SidebarMenu,
|
|
22
18
|
SidebarMenuButton,
|
|
23
19
|
SidebarMenuItem,
|
|
20
|
+
useSidebar,
|
|
24
21
|
} from "@/components/ui/sidebar";
|
|
25
|
-
import { useApiKeyCapabilityQuery } from "@/lib/api-keys-api";
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
type NavigationItem = {
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly icon: ReactNode;
|
|
26
|
+
readonly active: boolean;
|
|
27
|
+
readonly link: ReactElement;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
|
|
31
|
+
const { setOpenMobile } = useSidebar();
|
|
32
|
+
const closeMobileSidebar = () => setOpenMobile(false);
|
|
33
|
+
const [signingOut, setSigningOut] = useState(false);
|
|
34
|
+
const featureUi = useFeatureUi();
|
|
29
35
|
const { theme, setTheme } = useTheme();
|
|
30
36
|
const routerState = useRouterState();
|
|
31
37
|
const currentPath = routerState.location.pathname;
|
|
32
38
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
const navigation: readonly NavigationItem[] = [
|
|
40
|
+
{
|
|
41
|
+
label: "Bundles",
|
|
42
|
+
icon: <Package />,
|
|
43
|
+
active: currentPath === "/",
|
|
44
|
+
link: (
|
|
45
|
+
<Link
|
|
46
|
+
to="/"
|
|
47
|
+
onClick={closeMobileSidebar}
|
|
48
|
+
search={{
|
|
49
|
+
afterReleaseId: undefined,
|
|
50
|
+
beforeReleaseId: undefined,
|
|
51
|
+
channelId: undefined,
|
|
52
|
+
enabled: undefined,
|
|
53
|
+
releaseId: undefined,
|
|
54
|
+
platform: undefined,
|
|
55
|
+
bundleId: undefined,
|
|
56
|
+
scopeKey: undefined,
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
),
|
|
60
|
+
},
|
|
61
|
+
// Each feature that is on adds its item, as the feature registry lists it.
|
|
62
|
+
...featureUi.flatMap(({ features, label, navigation }) =>
|
|
63
|
+
navigation === undefined
|
|
64
|
+
? []
|
|
65
|
+
: [
|
|
66
|
+
{
|
|
67
|
+
label,
|
|
68
|
+
icon: navigation.icon,
|
|
69
|
+
active: navigation.paths.includes(currentPath),
|
|
70
|
+
link: navigation.link({ features, onClick: closeMobileSidebar }),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
),
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const signOut = async () => {
|
|
77
|
+
setSigningOut(true);
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch("/api/auth/sign-out", {
|
|
80
|
+
body: "{}",
|
|
81
|
+
headers: { "content-type": "application/json" },
|
|
82
|
+
method: "POST",
|
|
83
|
+
});
|
|
84
|
+
if (!response.ok) throw new Error("Sign-out failed. Please try again.");
|
|
85
|
+
window.location.reload();
|
|
86
|
+
} catch {
|
|
87
|
+
setSigningOut(false);
|
|
88
|
+
toast.error("Sign-out failed. Please try again.");
|
|
89
|
+
}
|
|
90
|
+
};
|
|
38
91
|
|
|
39
92
|
return (
|
|
40
93
|
<Sidebar collapsible="icon">
|
|
41
94
|
<SidebarHeader className="h-12 justify-center">
|
|
42
95
|
<Link
|
|
43
96
|
to="/"
|
|
97
|
+
onClick={closeMobileSidebar}
|
|
44
98
|
search={{
|
|
45
99
|
afterReleaseId: undefined,
|
|
46
100
|
beforeReleaseId: undefined,
|
|
@@ -48,9 +102,8 @@ export function AppSidebar() {
|
|
|
48
102
|
enabled: undefined,
|
|
49
103
|
releaseId: undefined,
|
|
50
104
|
platform: undefined,
|
|
51
|
-
targetAppVersion: undefined,
|
|
52
105
|
bundleId: undefined,
|
|
53
|
-
|
|
106
|
+
scopeKey: undefined,
|
|
54
107
|
}}
|
|
55
108
|
className="flex items-center gap-3 p-1 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:p-2"
|
|
56
109
|
>
|
|
@@ -71,63 +124,18 @@ export function AppSidebar() {
|
|
|
71
124
|
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
|
|
72
125
|
<SidebarGroupContent>
|
|
73
126
|
<SidebarMenu>
|
|
74
|
-
|
|
75
|
-
<
|
|
76
|
-
isActive={isBundlesActive}
|
|
77
|
-
render={
|
|
78
|
-
<Link
|
|
79
|
-
to="/"
|
|
80
|
-
search={{
|
|
81
|
-
afterReleaseId: undefined,
|
|
82
|
-
beforeReleaseId: undefined,
|
|
83
|
-
channelId: undefined,
|
|
84
|
-
enabled: undefined,
|
|
85
|
-
releaseId: undefined,
|
|
86
|
-
platform: undefined,
|
|
87
|
-
targetAppVersion: undefined,
|
|
88
|
-
bundleId: undefined,
|
|
89
|
-
page: undefined,
|
|
90
|
-
}}
|
|
91
|
-
/>
|
|
92
|
-
}
|
|
93
|
-
tooltip="Bundles"
|
|
94
|
-
>
|
|
95
|
-
<Package />
|
|
96
|
-
<span>Bundles</span>
|
|
97
|
-
</SidebarMenuButton>
|
|
98
|
-
</SidebarMenuItem>
|
|
99
|
-
<SidebarMenuItem>
|
|
100
|
-
<SidebarMenuButton
|
|
101
|
-
isActive={isInsightsActive}
|
|
102
|
-
render={<Link to="/insights" />}
|
|
103
|
-
tooltip="Insights"
|
|
104
|
-
>
|
|
105
|
-
<ChartNoAxesCombined />
|
|
106
|
-
<span>Insights</span>
|
|
107
|
-
</SidebarMenuButton>
|
|
108
|
-
</SidebarMenuItem>
|
|
109
|
-
{apiKeyCapability.data?.apiKeys ? (
|
|
110
|
-
<SidebarMenuItem>
|
|
127
|
+
{navigation.map((item) => (
|
|
128
|
+
<SidebarMenuItem key={item.label}>
|
|
111
129
|
<SidebarMenuButton
|
|
112
|
-
isActive={
|
|
113
|
-
render={
|
|
114
|
-
tooltip=
|
|
130
|
+
isActive={item.active}
|
|
131
|
+
render={item.link}
|
|
132
|
+
tooltip={item.label}
|
|
115
133
|
>
|
|
116
|
-
|
|
117
|
-
<span>
|
|
134
|
+
{item.icon}
|
|
135
|
+
<span>{item.label}</span>
|
|
118
136
|
</SidebarMenuButton>
|
|
119
137
|
</SidebarMenuItem>
|
|
120
|
-
)
|
|
121
|
-
<SidebarMenuItem>
|
|
122
|
-
<SidebarMenuButton
|
|
123
|
-
isActive={isSigningActive}
|
|
124
|
-
render={<Link to="/signing" />}
|
|
125
|
-
tooltip="Bundle signing"
|
|
126
|
-
>
|
|
127
|
-
<ShieldCheck />
|
|
128
|
-
<span>Bundle signing</span>
|
|
129
|
-
</SidebarMenuButton>
|
|
130
|
-
</SidebarMenuItem>
|
|
138
|
+
))}
|
|
131
139
|
</SidebarMenu>
|
|
132
140
|
</SidebarGroupContent>
|
|
133
141
|
</SidebarGroup>
|
|
@@ -135,6 +143,18 @@ export function AppSidebar() {
|
|
|
135
143
|
|
|
136
144
|
<SidebarFooter>
|
|
137
145
|
<SidebarMenu>
|
|
146
|
+
{canSignOut ? (
|
|
147
|
+
<SidebarMenuItem>
|
|
148
|
+
<SidebarMenuButton
|
|
149
|
+
disabled={signingOut}
|
|
150
|
+
onClick={() => void signOut()}
|
|
151
|
+
tooltip="Sign out"
|
|
152
|
+
>
|
|
153
|
+
<LogOut />
|
|
154
|
+
<span>{signingOut ? "Signing out…" : "Sign out"}</span>
|
|
155
|
+
</SidebarMenuButton>
|
|
156
|
+
</SidebarMenuItem>
|
|
157
|
+
) : null}
|
|
138
158
|
<SidebarMenuItem>
|
|
139
159
|
<SidebarMenuButton
|
|
140
160
|
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
|
@@ -149,6 +169,15 @@ export function AppSidebar() {
|
|
|
149
169
|
</SidebarMenuButton>
|
|
150
170
|
</SidebarMenuItem>
|
|
151
171
|
</SidebarMenu>
|
|
172
|
+
{/* The Console's license asks a hosted service to keep this notice. */}
|
|
173
|
+
<a
|
|
174
|
+
href="https://hot-updater.dev"
|
|
175
|
+
target="_blank"
|
|
176
|
+
rel="noreferrer"
|
|
177
|
+
className="px-2 pb-1 text-[11px] text-sidebar-foreground/60 hover:text-sidebar-foreground group-data-[collapsible=icon]:hidden"
|
|
178
|
+
>
|
|
179
|
+
Powered by hot-updater
|
|
180
|
+
</a>
|
|
152
181
|
</SidebarFooter>
|
|
153
182
|
</Sidebar>
|
|
154
183
|
);
|