@hot-updater/console 0.36.7 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/AnalyticsCapabilityContext-BCke57-7.js +1 -0
- package/.output/public/assets/BundleActivityChart-CnsBe_1J.js +42 -0
- package/.output/public/assets/BundleIdDisplay-Dky75zLU.js +1 -0
- package/.output/public/assets/alert-C-1KvDW8.js +1 -0
- package/.output/public/assets/analytics-Bqz0c-BT.js +1 -0
- package/.output/public/assets/analytics-api-CJdrTLLO.js +1 -0
- package/.output/public/assets/api-B_Rh-uzC.js +1 -0
- package/.output/public/assets/api-keys-BurCFhzH.js +1 -0
- package/.output/public/assets/api-keys-api-BEDJdQg3.js +1 -0
- package/.output/public/assets/badge-Bf0fZ-Gx.js +1 -0
- package/.output/public/assets/chart-no-axes-combined-FaYMGmQS.js +1 -0
- package/.output/public/assets/createServerFn-gM2-LFBy.js +3 -0
- package/.output/public/assets/dialog-BS60BRr2.js +1 -0
- package/.output/public/assets/dist-DspNKfMj.js +1 -0
- package/.output/public/assets/download-O-yNBc9H.js +1 -0
- package/.output/public/assets/field-Buq148-A.js +1 -0
- package/.output/public/assets/index-kFhd2S78.js +10 -0
- package/.output/public/assets/installations-BrQ-od41.js +2 -0
- package/.output/public/assets/installations-CGlZ3ROz.js +1 -0
- package/.output/public/assets/lazyRouteComponent-rPzgA7S-.js +1 -0
- package/.output/public/assets/loader-circle-DCYF-wJl.js +1 -0
- package/.output/public/assets/preload-helper-TKOBYGYD.js +1 -0
- package/.output/public/assets/refresh-cw-n8XYbPPj.js +1 -0
- package/.output/public/assets/routes-CrdokJ80.js +1 -0
- package/.output/public/assets/routes-DcQnVajO.js +2 -0
- package/.output/public/assets/search-DLhbWw3W.js +1 -0
- package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
- package/.output/public/assets/signing-HRexfjsH.js +1 -0
- package/.output/public/assets/styles-eB4EtvFS.css +2 -0
- package/.output/public/assets/table-D8CLieHX.js +1 -0
- package/.output/public/assets/textarea-YJLTVAoq.js +1 -0
- package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
- package/.output/public/assets/useOpenInteractionType-4cRD5xO0.js +1 -0
- package/.output/server/__23tanstack-start-server-fn-resolver-DBP9TQ7K.mjs +146 -0
- package/.output/server/_libs/@base-ui/react+[...].mjs +16124 -0
- package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +41 -10
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +132 -0
- 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 +1134 -0
- package/.output/server/_libs/eventemitter3.mjs +236 -0
- package/.output/server/_libs/immer.mjs +532 -0
- package/.output/server/_libs/lucide-react.mjs +141 -13
- package/.output/server/_libs/next-themes.mjs +2 -2
- package/.output/server/_libs/react-redux+[...].mjs +205 -0
- package/.output/server/_libs/recharts+[...].mjs +14117 -0
- package/.output/server/_libs/sonner.mjs +2 -2
- package/.output/server/_libs/tanstack__query-core.mjs +114 -1
- package/.output/server/_libs/tanstack__react-query.mjs +3 -4
- package/.output/server/_runtime.mjs +10 -1
- package/.output/server/_ssr/AnalyticsCapabilityContext-ui4DX8GO.mjs +15 -0
- package/.output/server/_ssr/BundleActivityChart-BiB9ZORH.mjs +288 -0
- package/.output/server/_ssr/BundleIdDisplay-Czk6iR6p.mjs +13 -0
- package/.output/server/_ssr/alert-Dim3MGY5.mjs +86 -0
- package/.output/server/_ssr/analytics-DtMTn8fn.mjs +858 -0
- package/.output/server/_ssr/analytics-api-Cg1M5v_H.mjs +71 -0
- package/.output/server/_ssr/analytics-input-fRRRn0uA.mjs +96 -0
- package/.output/server/_ssr/analytics-rpc-DY5uXvHT.mjs +155 -0
- package/.output/server/_ssr/api-BTSv1tZb.mjs +282 -0
- package/.output/server/_ssr/api-keys-BnM_o2nS.mjs +300 -0
- package/.output/server/_ssr/api-keys-api-CIywsSF5.mjs +60 -0
- package/.output/server/_ssr/api-keys-rpc-BSp1wkIV.mjs +136 -0
- package/.output/server/_ssr/api-rpc-cG5wd_tO.mjs +1468 -0
- package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
- package/.output/server/_ssr/auth-rpc-BrJ6obHJ.mjs +24 -0
- package/.output/server/_ssr/auth.server-DwKoZoQM.mjs +50 -0
- package/.output/server/_ssr/badge-DCxbEeof.mjs +26 -0
- package/.output/server/_ssr/bundle-signing-rpc-COoQhW8E.mjs +16 -0
- package/.output/server/_ssr/bundle-signing.server-D5u8KkMp.mjs +51 -0
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
- package/.output/server/_ssr/config.server-DEiLhKmy.mjs +361 -0
- package/.output/server/_ssr/{createServerFn-DzU0k62V.mjs → createServerFn-B2hcXoPM.mjs} +1 -1
- package/.output/server/_ssr/createServerRpc-DB29C_5p.mjs +12 -0
- package/.output/server/_ssr/{sidebar-Cv_H0_Jv.mjs → createSsrRpc-9P0S0lJ3.mjs} +68 -50
- package/.output/server/_ssr/databaseRows-CwbNwYSl.mjs +166 -0
- package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
- package/.output/server/_ssr/dialog-CQuRMNZ4.mjs +170 -0
- package/.output/server/_ssr/dist-BVSLTpoP.mjs +250 -0
- package/.output/server/_ssr/downloadBundle-CNnYxYO2.mjs +24 -0
- package/.output/server/_ssr/{empty-plugin-adapters-B4HCzgmM.mjs → empty-plugin-adapters-B5QvGpEl.mjs} +1 -1
- package/.output/server/_ssr/field-DRIXKjZG.mjs +74 -0
- package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
- package/.output/server/_ssr/installations-B-azgAvX.mjs +19 -0
- package/.output/server/_ssr/installations-Ctvzp2JH.mjs +535 -0
- package/.output/server/_ssr/router-C-KUBfOi.mjs +513 -0
- package/.output/server/_ssr/routes-BM4ht9jb.mjs +2286 -0
- package/.output/server/_ssr/routes-Z7TwC6nC.mjs +37 -0
- package/.output/server/_ssr/runtime.server-awTLiiXW.mjs +527 -0
- package/.output/server/_ssr/server-BjzmvXnO.mjs +7468 -0
- package/.output/server/_ssr/signing-k8GxyBDF.mjs +202 -0
- package/.output/server/_ssr/ssr.mjs +4 -5740
- package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
- package/.output/server/_ssr/table-v1yCvYo7.mjs +53 -0
- package/.output/server/_ssr/textarea-DYHnskhb.mjs +14 -0
- package/.output/server/_ssr/uuidv7-BbJ-9D_I.mjs +31 -0
- package/.output/server/_tanstack-start-manifest_v-CH4StzJu.mjs +108 -0
- package/.output/server/index.mjs +256 -53
- package/README.md +29 -8
- package/lib/index.d.mts +36 -0
- package/lib/index.mjs +4 -0
- package/lib/vite-internal.mjs +127 -0
- package/lib/vite.d.mts +10 -0
- package/lib/vite.mjs +5 -0
- package/package.json +51 -29
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/logo.svg +1 -0
- package/public/manifest.json +30 -0
- package/public/robots.txt +3 -0
- package/src/components/AppSidebar.tsx +159 -0
- package/src/components/BundleIdDisplay.tsx +18 -0
- package/src/components/ChannelBadge.tsx +31 -0
- package/src/components/ConsoleAccessPage.tsx +122 -0
- package/src/components/EnabledStatusIcon.tsx +54 -0
- package/src/components/HashValueDisplay.tsx +85 -0
- package/src/components/HotUpdaterLogo.tsx +30 -0
- package/src/components/NotFoundPage.tsx +33 -0
- package/src/components/PlatformIcon.tsx +39 -0
- package/src/components/RolloutPercentageBadge.tsx +28 -0
- package/src/components/ThemeProvider.tsx +116 -0
- package/src/components/TimestampDisplay.tsx +41 -0
- package/src/components/features/analytics/ActivityChart.tsx +150 -0
- package/src/components/features/analytics/AnalyticsCapabilityContext.tsx +24 -0
- package/src/components/features/analytics/AnalyticsControls.tsx +115 -0
- package/src/components/features/analytics/AnalyticsErrorAlert.tsx +21 -0
- package/src/components/features/analytics/AnalyticsOverview.tsx +208 -0
- package/src/components/features/analytics/BundleDistribution.tsx +133 -0
- package/src/components/features/analytics/BundleSelector.tsx +114 -0
- package/src/components/features/analytics/InstallationHistoryCard.tsx +248 -0
- package/src/components/features/analytics/InstallationMatchesCard.tsx +132 -0
- package/src/components/features/analytics/InstallationPageHeader.tsx +138 -0
- package/src/components/features/analytics/InstallationPagination.tsx +48 -0
- package/src/components/features/analytics/InstallationSearch.tsx +230 -0
- package/src/components/features/analytics/UpdateOutcomes.tsx +147 -0
- package/src/components/features/api-keys/ApiKeysPage.tsx +377 -0
- package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
- package/src/components/features/bundles/BundleAnalyticsSummary.tsx +123 -0
- package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
- package/src/components/features/bundles/BundleMetadata.tsx +136 -0
- package/src/components/features/bundles/RolloutCohortsDialog.tsx +201 -0
- package/src/components/features/channels/ChannelManagementDialog.tsx +228 -0
- package/src/components/features/releases/ReleaseEditorSheet.tsx +879 -0
- package/src/components/features/releases/ReleaseStateBadge.tsx +20 -0
- package/src/components/features/signing/BundleSigningPage.tsx +247 -0
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +89 -0
- package/src/components/ui/chart.tsx +371 -0
- package/src/components/ui/combobox.tsx +303 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/dropdown-menu.tsx +270 -0
- package/src/components/ui/field.tsx +235 -0
- package/src/components/ui/input-group.tsx +147 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +25 -0
- package/src/components/ui/progress.tsx +84 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +132 -0
- package/src/components/ui/sidebar.tsx +779 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +56 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/switch.tsx +25 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +30 -0
- package/src/index.ts +48 -0
- package/src/lib/analytics-api.ts +145 -0
- package/src/lib/analytics-error.ts +19 -0
- package/src/lib/analytics-input.ts +187 -0
- package/src/lib/analytics-overview.ts +170 -0
- package/src/lib/analytics-rpc.ts +218 -0
- package/src/lib/api-keys-api.ts +69 -0
- package/src/lib/api-keys-rpc.ts +87 -0
- package/src/lib/api-rpc.ts +478 -0
- package/src/lib/api.ts +444 -0
- package/src/lib/auth-rpc.ts +16 -0
- package/src/lib/bundle-signing-api.ts +14 -0
- package/src/lib/bundle-signing-rpc.ts +16 -0
- package/src/lib/constants.ts +1 -0
- package/src/lib/git.ts +51 -0
- package/src/lib/server/auth.server.ts +43 -0
- package/src/lib/server/bundle-signing.server.ts +100 -0
- package/src/lib/server/config.server.ts +109 -0
- package/src/lib/server/console-runtime.server.ts +70 -0
- package/src/lib/server/deleteBundle.ts +145 -0
- package/src/lib/server/downloadBundle.ts +46 -0
- package/src/lib/server/getBundleChildren.ts +130 -0
- package/src/lib/server/listReleases.ts +61 -0
- package/src/lib/server/releaseActivity.ts +27 -0
- package/src/lib/server/releaseReachability.ts +95 -0
- package/src/lib/server/runtime.server.ts +172 -0
- package/src/lib/utils.ts +9 -0
- package/src/logo.svg +1 -0
- package/src/routeTree.gen.ts +199 -0
- package/src/router.tsx +21 -0
- package/src/routes/-installations-search.ts +19 -0
- package/src/routes/-releases-search.ts +74 -0
- package/src/routes/__root.tsx +174 -0
- package/src/routes/analytics.tsx +139 -0
- package/src/routes/api/auth/$.ts +19 -0
- package/src/routes/api/bundles/$bundleId/download.ts +17 -0
- package/src/routes/api-keys.tsx +37 -0
- package/src/routes/index.tsx +982 -0
- package/src/routes/installations.tsx +194 -0
- package/src/routes/signing.tsx +37 -0
- package/src/styles.css +152 -0
- package/src/virtual-modules.d.ts +21 -0
- package/src/vite-env.d.ts +1 -0
- package/src/vite-internal.ts +195 -0
- package/src/vite.ts +12 -0
- package/.output/public/assets/dist-DJX53faP.js +0 -3
- package/.output/public/assets/index-Iylis5kY.js +0 -10
- package/.output/public/assets/routes-HT21hs0C.js +0 -54
- package/.output/public/assets/styles-C0Re4quB.css +0 -2
- package/.output/server/__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs +0 -66
- package/.output/server/_libs/@floating-ui/core+[...].mjs +0 -698
- package/.output/server/_libs/@floating-ui/dom+[...].mjs +0 -644
- package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +0 -839
- package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +0 -2138
- package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +0 -308
- package/.output/server/_libs/@radix-ui/react-select+[...].mjs +0 -1038
- package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +0 -196
- package/.output/server/_libs/@tanstack/form-core+[...].mjs +0 -3228
- package/.output/server/_libs/@tanstack/react-form+[...].mjs +0 -430
- package/.output/server/_libs/@tanstack/react-table+[...].mjs +0 -2382
- package/.output/server/_libs/radix-ui__number.mjs +0 -6
- package/.output/server/_libs/radix-ui__primitive.mjs +0 -9
- package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
- package/.output/server/_libs/radix-ui__react-collection.mjs +0 -78
- package/.output/server/_libs/radix-ui__react-direction.mjs +0 -13
- package/.output/server/_libs/radix-ui__react-label.mjs +0 -22
- package/.output/server/_libs/radix-ui__react-separator.mjs +0 -30
- package/.output/server/_libs/radix-ui__react-slider.mjs +0 -510
- package/.output/server/_libs/radix-ui__react-switch.mjs +0 -157
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -493
- package/.output/server/_ssr/api-rpc-BIbDUKSz.mjs +0 -292
- package/.output/server/_ssr/config.server-BSS366KT.mjs +0 -41
- package/.output/server/_ssr/deleteBundle-BgbZTtpW.mjs +0 -141
- package/.output/server/_ssr/dist-KboydRVk.mjs +0 -120
- package/.output/server/_ssr/router-C3hqEy3k.mjs +0 -307
- package/.output/server/_ssr/routes-B0lQvvlC.mjs +0 -3048
- package/.output/server/_ssr/start-02sLteYG.mjs +0 -4
- package/.output/server/_ssr/storageProfile-FVkjPoGb.mjs +0 -9
- package/.output/server/_tanstack-start-manifest_v-Bpx52WOU.mjs +0 -20
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/.output/server/package.json +0 -9
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnalyticsModel,
|
|
3
|
+
ApiKeyModel,
|
|
4
|
+
BundleRepository,
|
|
5
|
+
} from "@hot-updater/plugin-core";
|
|
6
|
+
import {
|
|
7
|
+
type ActiveInstallationInput,
|
|
8
|
+
type AnalyticsProvider,
|
|
9
|
+
createAnalyticsProvider,
|
|
10
|
+
type InstallationHistoryRow,
|
|
11
|
+
type InstallationSearchRow,
|
|
12
|
+
type OffsetPaginationResult,
|
|
13
|
+
} from "@hot-updater/server";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
parseActiveInstallationInput,
|
|
17
|
+
parseBundleEventAnalyticsInput,
|
|
18
|
+
parseBundleEventSummaryInput,
|
|
19
|
+
parseInstallationHistoryInput,
|
|
20
|
+
parseSearchInstallationsInput,
|
|
21
|
+
} from "../analytics-input";
|
|
22
|
+
|
|
23
|
+
export type InstallationSearchResult =
|
|
24
|
+
OffsetPaginationResult<InstallationSearchRow>;
|
|
25
|
+
export type InstallationHistoryResult =
|
|
26
|
+
OffsetPaginationResult<InstallationHistoryRow>;
|
|
27
|
+
|
|
28
|
+
export function createRuntimeHotUpdater(config: {
|
|
29
|
+
readonly database: BundleRepository;
|
|
30
|
+
}): AnalyticsProvider | null {
|
|
31
|
+
const models: unknown = Reflect.get(config.database, "models");
|
|
32
|
+
const analytics: unknown =
|
|
33
|
+
typeof models === "object" && models !== null
|
|
34
|
+
? Reflect.get(models, "analytics")
|
|
35
|
+
: undefined;
|
|
36
|
+
if (
|
|
37
|
+
typeof analytics !== "object" ||
|
|
38
|
+
analytics === null ||
|
|
39
|
+
typeof Reflect.get(analytics, "append") !== "function" ||
|
|
40
|
+
typeof Reflect.get(analytics, "scan") !== "function"
|
|
41
|
+
) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return createAnalyticsProvider(analytics as AnalyticsModel);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createApiKeyStore(config: {
|
|
48
|
+
readonly database: BundleRepository;
|
|
49
|
+
}): ApiKeyModel | null {
|
|
50
|
+
const models: unknown = Reflect.get(config.database, "models");
|
|
51
|
+
const apiKeys: unknown =
|
|
52
|
+
typeof models === "object" && models !== null
|
|
53
|
+
? Reflect.get(models, "apiKeys")
|
|
54
|
+
: undefined;
|
|
55
|
+
if (
|
|
56
|
+
typeof apiKeys !== "object" ||
|
|
57
|
+
apiKeys === null ||
|
|
58
|
+
typeof Reflect.get(apiKeys, "create") !== "function" ||
|
|
59
|
+
typeof Reflect.get(apiKeys, "findByHash") !== "function" ||
|
|
60
|
+
typeof Reflect.get(apiKeys, "list") !== "function" ||
|
|
61
|
+
typeof Reflect.get(apiKeys, "revoke") !== "function"
|
|
62
|
+
) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return apiKeys as ApiKeyModel;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const providerMethods = [
|
|
69
|
+
"appendBundleEvent",
|
|
70
|
+
"getBundleEventSummary",
|
|
71
|
+
"getBundleEventSummaries",
|
|
72
|
+
"getBundleEventAnalytics",
|
|
73
|
+
"getBundleEventOverview",
|
|
74
|
+
"getActiveInstallationOverview",
|
|
75
|
+
"searchInstallations",
|
|
76
|
+
"getInstallationHistory",
|
|
77
|
+
] as const;
|
|
78
|
+
|
|
79
|
+
const parseAnalyticsProvider = (value: unknown): AnalyticsProvider | null => {
|
|
80
|
+
if (
|
|
81
|
+
typeof value !== "object" ||
|
|
82
|
+
value === null ||
|
|
83
|
+
Reflect.get(value, "mode") !== "bounded" ||
|
|
84
|
+
typeof Reflect.get(value, "maxMatchingRows") !== "number" ||
|
|
85
|
+
!providerMethods.every(
|
|
86
|
+
(method) => typeof Reflect.get(value, method) === "function",
|
|
87
|
+
)
|
|
88
|
+
) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
return value as AnalyticsProvider;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const getAnalyticsCapability = async (provider: unknown) => {
|
|
95
|
+
const parsed = parseAnalyticsProvider(provider);
|
|
96
|
+
return parsed === null
|
|
97
|
+
? ({
|
|
98
|
+
analytics: false,
|
|
99
|
+
analyticsQueries: false,
|
|
100
|
+
eventIngestion: false,
|
|
101
|
+
} as const)
|
|
102
|
+
: ({
|
|
103
|
+
analytics: true,
|
|
104
|
+
analyticsQueries: true,
|
|
105
|
+
eventIngestion: true,
|
|
106
|
+
maxMatchingRows: parsed.maxMatchingRows,
|
|
107
|
+
mode: "bounded",
|
|
108
|
+
} as const);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const requireAnalyticsSupport = async (
|
|
112
|
+
provider: unknown,
|
|
113
|
+
): Promise<AnalyticsProvider> => {
|
|
114
|
+
const parsedProvider = parseAnalyticsProvider(provider);
|
|
115
|
+
if (parsedProvider === null) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
"Analytics are not supported by the configured database plugin.",
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return parsedProvider;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export async function getBundleEventSummary(provider: unknown, input: unknown) {
|
|
124
|
+
const { bundleId } = parseBundleEventSummaryInput(input);
|
|
125
|
+
return (await requireAnalyticsSupport(provider)).getBundleEventSummary(
|
|
126
|
+
bundleId,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function getActiveInstallationOverview(
|
|
131
|
+
provider: unknown,
|
|
132
|
+
input: unknown,
|
|
133
|
+
) {
|
|
134
|
+
const parsed: ActiveInstallationInput = parseActiveInstallationInput(input);
|
|
135
|
+
return (
|
|
136
|
+
await requireAnalyticsSupport(provider)
|
|
137
|
+
).getActiveInstallationOverview(parsed);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function getBundleEventAnalytics(
|
|
141
|
+
provider: unknown,
|
|
142
|
+
input: unknown,
|
|
143
|
+
) {
|
|
144
|
+
const parsed = parseBundleEventAnalyticsInput(input);
|
|
145
|
+
return (await requireAnalyticsSupport(provider)).getBundleEventAnalytics(
|
|
146
|
+
parsed.bundleId,
|
|
147
|
+
parsed.window,
|
|
148
|
+
parsed.limit ?? 50,
|
|
149
|
+
parsed.offset ?? 0,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function searchInstallations(provider: unknown, input: unknown) {
|
|
154
|
+
const parsed = parseSearchInstallationsInput(input);
|
|
155
|
+
return (await requireAnalyticsSupport(provider)).searchInstallations(
|
|
156
|
+
parsed.query,
|
|
157
|
+
parsed.limit ?? 50,
|
|
158
|
+
parsed.offset ?? 0,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function getInstallationHistory(
|
|
163
|
+
provider: unknown,
|
|
164
|
+
input: unknown,
|
|
165
|
+
) {
|
|
166
|
+
const parsed = parseInstallationHistoryInput(input);
|
|
167
|
+
return (await requireAnalyticsSupport(provider)).getInstallationHistory(
|
|
168
|
+
parsed.installId,
|
|
169
|
+
parsed.limit ?? 50,
|
|
170
|
+
parsed.offset ?? 0,
|
|
171
|
+
);
|
|
172
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const sleep = (ms: number) =>
|
|
9
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
package/src/logo.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="hot-updater-layer" data-name="hot-updater-layer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 447 652"><defs><linearGradient id="hot-updater-gradient" x1="223.54" y1="0.36" x2="223.54" y2="711.23" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff7f33"/><stop offset="0.4" stop-color="#f9602b"/><stop offset="0.76" stop-color="#f54c25"/><stop offset="1" stop-color="#f44523"/></linearGradient></defs><title>Hotupdater</title><path d="M150.25,8.58c16.28,31.85,42.09,116-68.18,212.22C75.46,226.57,70,233,63.72,239.62c-28.48,30-84.71,114.89-55.82,207,.69,2.18,1.31,4.37,1.91,6.57,5.25,19.33,38.08,121,155.56,157.25a5.14,5.14,0,0,0,6-7.39c-14.72-26.53-46.07-96.89-8.89-162.55,0,0,11.5,44.5,50.5,83.5,6,6,14.72,15.64,19.53,21.23,14.47,16.77,32.24,53.55,1.82,98.18a5.63,5.63,0,0,0,6.85,8.34c75.64-32.1,333.29-164.6,128.68-410.85a7,7,0,0,0-12.46,4.15c-1.77,39.31-14.84,129-96.18,148.06a31.08,31.08,0,0,1-24.3-4.16c-13.34-8.9-25.41-32.06.53-92.3a250.9,250.9,0,0,1,20.12-37.29C282.78,220.56,356.18,79.83,157.33.77A5.64,5.64,0,0,0,150.25,8.58Z" fill="url(#hot-updater-gradient)"/></svg>
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
|
|
5
|
+
// noinspection JSUnusedGlobalSymbols
|
|
6
|
+
|
|
7
|
+
// This file was automatically generated by TanStack Router.
|
|
8
|
+
// You should NOT make any changes in this file as it will be overwritten.
|
|
9
|
+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
|
10
|
+
|
|
11
|
+
import { Route as rootRouteImport } from './routes/__root'
|
|
12
|
+
import { Route as SigningRouteImport } from './routes/signing'
|
|
13
|
+
import { Route as InstallationsRouteImport } from './routes/installations'
|
|
14
|
+
import { Route as ApiKeysRouteImport } from './routes/api-keys'
|
|
15
|
+
import { Route as AnalyticsRouteImport } from './routes/analytics'
|
|
16
|
+
import { Route as IndexRouteImport } from './routes/index'
|
|
17
|
+
import { Route as ApiAuthSplatRouteImport } from './routes/api/auth/$'
|
|
18
|
+
import { Route as ApiBundlesBundleIdDownloadRouteImport } from './routes/api/bundles/$bundleId/download'
|
|
19
|
+
|
|
20
|
+
const SigningRoute = SigningRouteImport.update({
|
|
21
|
+
id: '/signing',
|
|
22
|
+
path: '/signing',
|
|
23
|
+
getParentRoute: () => rootRouteImport,
|
|
24
|
+
} as any)
|
|
25
|
+
const InstallationsRoute = InstallationsRouteImport.update({
|
|
26
|
+
id: '/installations',
|
|
27
|
+
path: '/installations',
|
|
28
|
+
getParentRoute: () => rootRouteImport,
|
|
29
|
+
} as any)
|
|
30
|
+
const ApiKeysRoute = ApiKeysRouteImport.update({
|
|
31
|
+
id: '/api-keys',
|
|
32
|
+
path: '/api-keys',
|
|
33
|
+
getParentRoute: () => rootRouteImport,
|
|
34
|
+
} as any)
|
|
35
|
+
const AnalyticsRoute = AnalyticsRouteImport.update({
|
|
36
|
+
id: '/analytics',
|
|
37
|
+
path: '/analytics',
|
|
38
|
+
getParentRoute: () => rootRouteImport,
|
|
39
|
+
} as any)
|
|
40
|
+
const IndexRoute = IndexRouteImport.update({
|
|
41
|
+
id: '/',
|
|
42
|
+
path: '/',
|
|
43
|
+
getParentRoute: () => rootRouteImport,
|
|
44
|
+
} as any)
|
|
45
|
+
const ApiAuthSplatRoute = ApiAuthSplatRouteImport.update({
|
|
46
|
+
id: '/api/auth/$',
|
|
47
|
+
path: '/api/auth/$',
|
|
48
|
+
getParentRoute: () => rootRouteImport,
|
|
49
|
+
} as any)
|
|
50
|
+
const ApiBundlesBundleIdDownloadRoute =
|
|
51
|
+
ApiBundlesBundleIdDownloadRouteImport.update({
|
|
52
|
+
id: '/api/bundles/$bundleId/download',
|
|
53
|
+
path: '/api/bundles/$bundleId/download',
|
|
54
|
+
getParentRoute: () => rootRouteImport,
|
|
55
|
+
} as any)
|
|
56
|
+
|
|
57
|
+
export interface FileRoutesByFullPath {
|
|
58
|
+
'/': typeof IndexRoute
|
|
59
|
+
'/analytics': typeof AnalyticsRoute
|
|
60
|
+
'/api-keys': typeof ApiKeysRoute
|
|
61
|
+
'/installations': typeof InstallationsRoute
|
|
62
|
+
'/signing': typeof SigningRoute
|
|
63
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
64
|
+
'/api/bundles/$bundleId/download': typeof ApiBundlesBundleIdDownloadRoute
|
|
65
|
+
}
|
|
66
|
+
export interface FileRoutesByTo {
|
|
67
|
+
'/': typeof IndexRoute
|
|
68
|
+
'/analytics': typeof AnalyticsRoute
|
|
69
|
+
'/api-keys': typeof ApiKeysRoute
|
|
70
|
+
'/installations': typeof InstallationsRoute
|
|
71
|
+
'/signing': typeof SigningRoute
|
|
72
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
73
|
+
'/api/bundles/$bundleId/download': typeof ApiBundlesBundleIdDownloadRoute
|
|
74
|
+
}
|
|
75
|
+
export interface FileRoutesById {
|
|
76
|
+
__root__: typeof rootRouteImport
|
|
77
|
+
'/': typeof IndexRoute
|
|
78
|
+
'/analytics': typeof AnalyticsRoute
|
|
79
|
+
'/api-keys': typeof ApiKeysRoute
|
|
80
|
+
'/installations': typeof InstallationsRoute
|
|
81
|
+
'/signing': typeof SigningRoute
|
|
82
|
+
'/api/auth/$': typeof ApiAuthSplatRoute
|
|
83
|
+
'/api/bundles/$bundleId/download': typeof ApiBundlesBundleIdDownloadRoute
|
|
84
|
+
}
|
|
85
|
+
export interface FileRouteTypes {
|
|
86
|
+
fileRoutesByFullPath: FileRoutesByFullPath
|
|
87
|
+
fullPaths:
|
|
88
|
+
| '/'
|
|
89
|
+
| '/analytics'
|
|
90
|
+
| '/api-keys'
|
|
91
|
+
| '/installations'
|
|
92
|
+
| '/signing'
|
|
93
|
+
| '/api/auth/$'
|
|
94
|
+
| '/api/bundles/$bundleId/download'
|
|
95
|
+
fileRoutesByTo: FileRoutesByTo
|
|
96
|
+
to:
|
|
97
|
+
| '/'
|
|
98
|
+
| '/analytics'
|
|
99
|
+
| '/api-keys'
|
|
100
|
+
| '/installations'
|
|
101
|
+
| '/signing'
|
|
102
|
+
| '/api/auth/$'
|
|
103
|
+
| '/api/bundles/$bundleId/download'
|
|
104
|
+
id:
|
|
105
|
+
| '__root__'
|
|
106
|
+
| '/'
|
|
107
|
+
| '/analytics'
|
|
108
|
+
| '/api-keys'
|
|
109
|
+
| '/installations'
|
|
110
|
+
| '/signing'
|
|
111
|
+
| '/api/auth/$'
|
|
112
|
+
| '/api/bundles/$bundleId/download'
|
|
113
|
+
fileRoutesById: FileRoutesById
|
|
114
|
+
}
|
|
115
|
+
export interface RootRouteChildren {
|
|
116
|
+
IndexRoute: typeof IndexRoute
|
|
117
|
+
AnalyticsRoute: typeof AnalyticsRoute
|
|
118
|
+
ApiKeysRoute: typeof ApiKeysRoute
|
|
119
|
+
InstallationsRoute: typeof InstallationsRoute
|
|
120
|
+
SigningRoute: typeof SigningRoute
|
|
121
|
+
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
|
|
122
|
+
ApiBundlesBundleIdDownloadRoute: typeof ApiBundlesBundleIdDownloadRoute
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare module '@tanstack/react-router' {
|
|
126
|
+
interface FileRoutesByPath {
|
|
127
|
+
'/signing': {
|
|
128
|
+
id: '/signing'
|
|
129
|
+
path: '/signing'
|
|
130
|
+
fullPath: '/signing'
|
|
131
|
+
preLoaderRoute: typeof SigningRouteImport
|
|
132
|
+
parentRoute: typeof rootRouteImport
|
|
133
|
+
}
|
|
134
|
+
'/installations': {
|
|
135
|
+
id: '/installations'
|
|
136
|
+
path: '/installations'
|
|
137
|
+
fullPath: '/installations'
|
|
138
|
+
preLoaderRoute: typeof InstallationsRouteImport
|
|
139
|
+
parentRoute: typeof rootRouteImport
|
|
140
|
+
}
|
|
141
|
+
'/api-keys': {
|
|
142
|
+
id: '/api-keys'
|
|
143
|
+
path: '/api-keys'
|
|
144
|
+
fullPath: '/api-keys'
|
|
145
|
+
preLoaderRoute: typeof ApiKeysRouteImport
|
|
146
|
+
parentRoute: typeof rootRouteImport
|
|
147
|
+
}
|
|
148
|
+
'/analytics': {
|
|
149
|
+
id: '/analytics'
|
|
150
|
+
path: '/analytics'
|
|
151
|
+
fullPath: '/analytics'
|
|
152
|
+
preLoaderRoute: typeof AnalyticsRouteImport
|
|
153
|
+
parentRoute: typeof rootRouteImport
|
|
154
|
+
}
|
|
155
|
+
'/': {
|
|
156
|
+
id: '/'
|
|
157
|
+
path: '/'
|
|
158
|
+
fullPath: '/'
|
|
159
|
+
preLoaderRoute: typeof IndexRouteImport
|
|
160
|
+
parentRoute: typeof rootRouteImport
|
|
161
|
+
}
|
|
162
|
+
'/api/auth/$': {
|
|
163
|
+
id: '/api/auth/$'
|
|
164
|
+
path: '/api/auth/$'
|
|
165
|
+
fullPath: '/api/auth/$'
|
|
166
|
+
preLoaderRoute: typeof ApiAuthSplatRouteImport
|
|
167
|
+
parentRoute: typeof rootRouteImport
|
|
168
|
+
}
|
|
169
|
+
'/api/bundles/$bundleId/download': {
|
|
170
|
+
id: '/api/bundles/$bundleId/download'
|
|
171
|
+
path: '/api/bundles/$bundleId/download'
|
|
172
|
+
fullPath: '/api/bundles/$bundleId/download'
|
|
173
|
+
preLoaderRoute: typeof ApiBundlesBundleIdDownloadRouteImport
|
|
174
|
+
parentRoute: typeof rootRouteImport
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const rootRouteChildren: RootRouteChildren = {
|
|
180
|
+
IndexRoute: IndexRoute,
|
|
181
|
+
AnalyticsRoute: AnalyticsRoute,
|
|
182
|
+
ApiKeysRoute: ApiKeysRoute,
|
|
183
|
+
InstallationsRoute: InstallationsRoute,
|
|
184
|
+
SigningRoute: SigningRoute,
|
|
185
|
+
ApiAuthSplatRoute: ApiAuthSplatRoute,
|
|
186
|
+
ApiBundlesBundleIdDownloadRoute: ApiBundlesBundleIdDownloadRoute,
|
|
187
|
+
}
|
|
188
|
+
export const routeTree = rootRouteImport
|
|
189
|
+
._addFileChildren(rootRouteChildren)
|
|
190
|
+
._addFileTypes<FileRouteTypes>()
|
|
191
|
+
|
|
192
|
+
import type { getRouter } from './router.tsx'
|
|
193
|
+
import type { createStart } from '@tanstack/react-start'
|
|
194
|
+
declare module '@tanstack/react-start' {
|
|
195
|
+
interface Register {
|
|
196
|
+
ssr: true
|
|
197
|
+
router: Awaited<ReturnType<typeof getRouter>>
|
|
198
|
+
}
|
|
199
|
+
}
|
package/src/router.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { createRouter } from "@tanstack/react-router";
|
|
3
|
+
import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
|
|
4
|
+
// Import the route tree selected by the local or hosted Vite integration.
|
|
5
|
+
import { routeTree } from "virtual:hot-updater-console/route-tree";
|
|
6
|
+
|
|
7
|
+
// Create a new router instance
|
|
8
|
+
export const getRouter = () => {
|
|
9
|
+
const queryClient = new QueryClient();
|
|
10
|
+
const router = createRouter({
|
|
11
|
+
routeTree,
|
|
12
|
+
context: { queryClient },
|
|
13
|
+
|
|
14
|
+
scrollRestoration: true,
|
|
15
|
+
defaultPreloadStaleTime: 0,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
setupRouterSsrQueryIntegration({ router, queryClient });
|
|
19
|
+
|
|
20
|
+
return router;
|
|
21
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function validateInstallationsSearch(search: Record<string, unknown>) {
|
|
2
|
+
return {
|
|
3
|
+
query: typeof search.query === "string" ? search.query : undefined,
|
|
4
|
+
installId:
|
|
5
|
+
typeof search.installId === "string" ? search.installId : undefined,
|
|
6
|
+
searchOffset:
|
|
7
|
+
typeof search.searchOffset === "number" &&
|
|
8
|
+
Number.isSafeInteger(search.searchOffset) &&
|
|
9
|
+
search.searchOffset >= 0
|
|
10
|
+
? search.searchOffset
|
|
11
|
+
: 0,
|
|
12
|
+
historyOffset:
|
|
13
|
+
typeof search.historyOffset === "number" &&
|
|
14
|
+
Number.isSafeInteger(search.historyOffset) &&
|
|
15
|
+
search.historyOffset >= 0
|
|
16
|
+
? search.historyOffset
|
|
17
|
+
: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export interface ReleaseSearch {
|
|
2
|
+
afterReleaseId?: string;
|
|
3
|
+
beforeReleaseId?: string;
|
|
4
|
+
bundleId?: string;
|
|
5
|
+
channelId?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
page?: number;
|
|
8
|
+
platform?: "ios" | "android";
|
|
9
|
+
releaseId?: string;
|
|
10
|
+
targetAppVersion?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const optionalString = (value: unknown) =>
|
|
14
|
+
typeof value === "string" && value.trim().length > 0
|
|
15
|
+
? value.trim()
|
|
16
|
+
: undefined;
|
|
17
|
+
|
|
18
|
+
export function validateReleaseSearch(
|
|
19
|
+
search: Record<string, unknown>,
|
|
20
|
+
): ReleaseSearch {
|
|
21
|
+
const parsedPage =
|
|
22
|
+
typeof search.page === "number"
|
|
23
|
+
? search.page
|
|
24
|
+
: typeof search.page === "string"
|
|
25
|
+
? Number(search.page)
|
|
26
|
+
: undefined;
|
|
27
|
+
const beforeReleaseId = optionalString(search.beforeReleaseId);
|
|
28
|
+
const afterReleaseId = beforeReleaseId
|
|
29
|
+
? undefined
|
|
30
|
+
: optionalString(search.afterReleaseId);
|
|
31
|
+
const enabled =
|
|
32
|
+
search.enabled === true || search.enabled === "true"
|
|
33
|
+
? true
|
|
34
|
+
: search.enabled === false || search.enabled === "false"
|
|
35
|
+
? false
|
|
36
|
+
: undefined;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
afterReleaseId,
|
|
40
|
+
beforeReleaseId,
|
|
41
|
+
bundleId: optionalString(search.bundleId),
|
|
42
|
+
channelId: optionalString(search.channelId),
|
|
43
|
+
enabled,
|
|
44
|
+
page:
|
|
45
|
+
parsedPage !== undefined && Number.isInteger(parsedPage) && parsedPage > 1
|
|
46
|
+
? parsedPage
|
|
47
|
+
: undefined,
|
|
48
|
+
platform:
|
|
49
|
+
search.platform === "ios" || search.platform === "android"
|
|
50
|
+
? search.platform
|
|
51
|
+
: undefined,
|
|
52
|
+
releaseId: optionalString(search.releaseId),
|
|
53
|
+
targetAppVersion: optionalString(search.targetAppVersion),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function updateReleaseFilters(
|
|
58
|
+
current: ReleaseSearch,
|
|
59
|
+
filters: Partial<
|
|
60
|
+
Pick<
|
|
61
|
+
ReleaseSearch,
|
|
62
|
+
"bundleId" | "channelId" | "enabled" | "platform" | "targetAppVersion"
|
|
63
|
+
>
|
|
64
|
+
>,
|
|
65
|
+
): ReleaseSearch {
|
|
66
|
+
return {
|
|
67
|
+
...current,
|
|
68
|
+
...filters,
|
|
69
|
+
afterReleaseId: undefined,
|
|
70
|
+
beforeReleaseId: undefined,
|
|
71
|
+
page: undefined,
|
|
72
|
+
releaseId: undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
createRootRouteWithContext,
|
|
4
|
+
HeadContent,
|
|
5
|
+
Outlet,
|
|
6
|
+
Scripts,
|
|
7
|
+
} from "@tanstack/react-router";
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
|
|
10
|
+
import { AppSidebar } from "@/components/AppSidebar";
|
|
11
|
+
import { ConsoleAccessPage } from "@/components/ConsoleAccessPage";
|
|
12
|
+
import { AnalyticsCapabilityProvider } from "@/components/features/analytics/AnalyticsCapabilityContext";
|
|
13
|
+
import { NotFoundPage } from "@/components/NotFoundPage";
|
|
14
|
+
import { ThemeProvider } from "@/components/ThemeProvider";
|
|
15
|
+
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
|
|
16
|
+
import { Toaster } from "@/components/ui/sonner";
|
|
17
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
18
|
+
import {
|
|
19
|
+
getAnalyticsCapabilityState,
|
|
20
|
+
useAnalyticsCapabilitiesQuery,
|
|
21
|
+
} from "@/lib/analytics-api";
|
|
22
|
+
import {
|
|
23
|
+
getConsoleAccessRpc,
|
|
24
|
+
getConsoleAuthProvidersRpc,
|
|
25
|
+
} from "@/lib/auth-rpc";
|
|
26
|
+
|
|
27
|
+
import appCss from "../styles.css?url";
|
|
28
|
+
|
|
29
|
+
const LOCAL_DEBUG_HOSTS = new Set(["127.0.0.1", "localhost"]);
|
|
30
|
+
|
|
31
|
+
type LocalDevtools = {
|
|
32
|
+
readonly Devtools: typeof import("@tanstack/react-devtools").TanStackDevtools;
|
|
33
|
+
readonly RouterPanel: typeof import("@tanstack/react-router-devtools").TanStackRouterDevtoolsPanel;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Route = createRootRouteWithContext<{
|
|
37
|
+
readonly queryClient: QueryClient;
|
|
38
|
+
}>()({
|
|
39
|
+
head: () => ({
|
|
40
|
+
meta: [
|
|
41
|
+
{
|
|
42
|
+
charSet: "utf-8",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "viewport",
|
|
46
|
+
content: "width=device-width, initial-scale=1, viewport-fit=cover",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "theme-color",
|
|
50
|
+
content: "#1f1d1c",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "color-scheme",
|
|
54
|
+
content: "dark light",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
title: "Hot Updater Console",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
links: [
|
|
61
|
+
{
|
|
62
|
+
rel: "stylesheet",
|
|
63
|
+
href: appCss,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
}),
|
|
67
|
+
|
|
68
|
+
loader: async () => {
|
|
69
|
+
const [access, providers] = await Promise.all([
|
|
70
|
+
getConsoleAccessRpc(),
|
|
71
|
+
getConsoleAuthProvidersRpc(),
|
|
72
|
+
]);
|
|
73
|
+
return { access, providers };
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
component: RootLayout,
|
|
77
|
+
notFoundComponent: NotFoundPage,
|
|
78
|
+
shellComponent: RootDocument,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export function RootDocument({ children }: { children: React.ReactNode }) {
|
|
82
|
+
const [localDevtools, setLocalDevtools] = useState<LocalDevtools | null>(
|
|
83
|
+
null,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!import.meta.env.DEV || typeof window === "undefined") {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (LOCAL_DEBUG_HOSTS.has(window.location.hostname)) {
|
|
92
|
+
void Promise.all([
|
|
93
|
+
import("@tanstack/react-devtools"),
|
|
94
|
+
import("@tanstack/react-router-devtools"),
|
|
95
|
+
import("react-grab/core"),
|
|
96
|
+
]).then(
|
|
97
|
+
([{ TanStackDevtools }, { TanStackRouterDevtoolsPanel }, { init }]) => {
|
|
98
|
+
setLocalDevtools({
|
|
99
|
+
Devtools: TanStackDevtools,
|
|
100
|
+
RouterPanel: TanStackRouterDevtoolsPanel,
|
|
101
|
+
});
|
|
102
|
+
init({
|
|
103
|
+
activationKey: (event) =>
|
|
104
|
+
event.key.toLowerCase() === "c" && event.metaKey,
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}, []);
|
|
110
|
+
return (
|
|
111
|
+
<html lang="en" suppressHydrationWarning>
|
|
112
|
+
<head>
|
|
113
|
+
<HeadContent />
|
|
114
|
+
</head>
|
|
115
|
+
<body>
|
|
116
|
+
<ThemeProvider defaultTheme="dark">
|
|
117
|
+
<TooltipProvider>
|
|
118
|
+
{children}
|
|
119
|
+
<Toaster />
|
|
120
|
+
{localDevtools ? (
|
|
121
|
+
<localDevtools.Devtools
|
|
122
|
+
config={{
|
|
123
|
+
position: "bottom-right",
|
|
124
|
+
}}
|
|
125
|
+
plugins={[
|
|
126
|
+
{
|
|
127
|
+
name: "Tanstack Router",
|
|
128
|
+
render: <localDevtools.RouterPanel />,
|
|
129
|
+
},
|
|
130
|
+
]}
|
|
131
|
+
/>
|
|
132
|
+
) : null}
|
|
133
|
+
</TooltipProvider>
|
|
134
|
+
</ThemeProvider>
|
|
135
|
+
<Scripts />
|
|
136
|
+
</body>
|
|
137
|
+
</html>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function RootLayout() {
|
|
142
|
+
const { access, providers } = Route.useLoaderData();
|
|
143
|
+
|
|
144
|
+
if (access.status !== "authorized") {
|
|
145
|
+
return <ConsoleAccessPage access={access} providers={providers} />;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return <AuthorizedConsole />;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function AuthorizedConsole() {
|
|
152
|
+
const capabilityQuery = useAnalyticsCapabilitiesQuery();
|
|
153
|
+
const capability = getAnalyticsCapabilityState(capabilityQuery);
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (
|
|
157
|
+
import.meta.env.DEV &&
|
|
158
|
+
typeof window !== "undefined" &&
|
|
159
|
+
LOCAL_DEBUG_HOSTS.has(window.location.hostname)
|
|
160
|
+
) {
|
|
161
|
+
void import("react-grab");
|
|
162
|
+
}
|
|
163
|
+
}, []);
|
|
164
|
+
return (
|
|
165
|
+
<AnalyticsCapabilityProvider value={capability}>
|
|
166
|
+
<SidebarProvider>
|
|
167
|
+
<AppSidebar />
|
|
168
|
+
<SidebarInset className="min-h-0 min-w-0 overflow-hidden">
|
|
169
|
+
<Outlet />
|
|
170
|
+
</SidebarInset>
|
|
171
|
+
</SidebarProvider>
|
|
172
|
+
</AnalyticsCapabilityProvider>
|
|
173
|
+
);
|
|
174
|
+
}
|