@hot-updater/console 0.36.7 → 1.0.0-rc.1
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/BundleIdDisplay-D4ErdU5_.js +1 -0
- package/.output/public/assets/InsightsCapabilityContext-B42VqvP6.js +1 -0
- package/.output/public/assets/api-keys-api-DGVTTvTJ.js +1 -0
- package/.output/public/assets/api-keys-z16OZIP4.js +1 -0
- package/.output/public/assets/badge-Br_KDO0J.js +1 -0
- package/.output/public/assets/card-DzuEXvyJ.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-BeAENgez.js +1 -0
- package/.output/public/assets/dist-Bssh_cv0.js +1 -0
- package/.output/public/assets/field-Buq148-A.js +1 -0
- package/.output/public/assets/index-uOpQAIC4.js +10 -0
- package/.output/public/assets/input-group-B3p5UpyW.js +1 -0
- package/.output/public/assets/insights-DJTr7Ozn.js +1 -0
- package/.output/public/assets/insights-api-DVMOoRMS.js +1 -0
- package/.output/public/assets/installations-BY64iIAK.js +2 -0
- package/.output/public/assets/installations-D5v9i3AL.js +1 -0
- package/.output/public/assets/lazyRouteComponent-DKYu5cZJ.js +1 -0
- package/.output/public/assets/preload-helper-CQ36nxok.js +1 -0
- package/.output/public/assets/refresh-cw-BkbfZ62O.js +1 -0
- package/.output/public/assets/routes-BuoI5-Cr.js +2 -0
- package/.output/public/assets/routes-C8fvgETO.js +1 -0
- package/.output/public/assets/search-qsIDvS2I.js +1 -0
- package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
- package/.output/public/assets/signing-mJD88Y4C.js +1 -0
- package/.output/public/assets/styles-BZIG7t42.css +2 -0
- package/.output/public/assets/table-RamFEclP.js +1 -0
- package/.output/public/assets/useCompositeListItem-ChzDqdpc.js +42 -0
- package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
- package/.output/public/assets/useOpenInteractionType-Dwfy-SpA.js +1 -0
- package/.output/server/__23tanstack-start-server-fn-resolver-Dftxlm2E.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/BundleActivityChart-CWcdPuUn.mjs +288 -0
- package/.output/server/_ssr/BundleIdDisplay-BEvB3MvA.mjs +13 -0
- package/.output/server/_ssr/InsightsCapabilityContext-Cx8fjm2s.mjs +15 -0
- package/.output/server/_ssr/api-keys-CpBqHpuC.mjs +300 -0
- package/.output/server/_ssr/api-keys-api-Czkp57UV.mjs +60 -0
- package/.output/server/_ssr/api-keys-rpc-BTtMDSoR.mjs +136 -0
- package/.output/server/_ssr/api-rpc-BEX8ISai.mjs +1468 -0
- package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
- package/.output/server/_ssr/auth-rpc-BFMEC-TT.mjs +24 -0
- package/.output/server/_ssr/auth.server-DW71WzsD.mjs +47 -0
- package/.output/server/_ssr/badge-CpWXQwyF.mjs +26 -0
- package/.output/server/_ssr/bundle-signing-rpc-BbCFUQSu.mjs +16 -0
- package/.output/server/_ssr/bundle-signing.server-C4NW5mWx.mjs +12 -0
- package/.output/server/_ssr/card-Bsc8mRis.mjs +86 -0
- package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
- package/.output/server/_ssr/config.server-DQYEr_5X.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-DhfrxEjg.mjs} +68 -50
- package/.output/server/_ssr/databaseRows-QlCYLi0H.mjs +166 -0
- package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
- package/.output/server/_ssr/dialog-C0vKUp8R.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-DASCh-5d.mjs +74 -0
- package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
- package/.output/server/_ssr/input-group-etIsBUqv.mjs +288 -0
- package/.output/server/_ssr/insights-api-C1F7UreR.mjs +71 -0
- package/.output/server/_ssr/insights-input-9fEJF-ZG.mjs +96 -0
- package/.output/server/_ssr/insights-rpc-C-8R6kED.mjs +155 -0
- package/.output/server/_ssr/insights-ygT_aIfo.mjs +858 -0
- package/.output/server/_ssr/installations-DqRT1QTd.mjs +535 -0
- package/.output/server/_ssr/installations-K8TCYTYh.mjs +19 -0
- package/.output/server/_ssr/router-BxBEvtF1.mjs +513 -0
- package/.output/server/_ssr/routes-BC7rn-tf.mjs +37 -0
- package/.output/server/_ssr/routes-CM5KAwpB.mjs +2285 -0
- package/.output/server/_ssr/runtime.server-la7TRAH_.mjs +527 -0
- package/.output/server/_ssr/server-CpzW--lT.mjs +7468 -0
- package/.output/server/_ssr/signing-jk4HANnl.mjs +134 -0
- package/.output/server/_ssr/ssr.mjs +4 -5740
- package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
- package/.output/server/_ssr/table-CU58HeI7.mjs +53 -0
- package/.output/server/_ssr/uuidv7-CaKuKXrV.mjs +31 -0
- package/.output/server/_tanstack-start-manifest_v-Mz9MmSlD.mjs +103 -0
- package/.output/server/index.mjs +229 -47
- package/README.md +29 -8
- package/lib/index.d.mts +35 -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/api-keys/ApiKeysPage.tsx +377 -0
- package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
- package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
- package/src/components/features/bundles/BundleInsightsSummary.tsx +123 -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/insights/ActivityChart.tsx +150 -0
- package/src/components/features/insights/BundleDistribution.tsx +133 -0
- package/src/components/features/insights/BundleSelector.tsx +114 -0
- package/src/components/features/insights/InsightsCapabilityContext.tsx +24 -0
- package/src/components/features/insights/InsightsControls.tsx +115 -0
- package/src/components/features/insights/InsightsErrorAlert.tsx +21 -0
- package/src/components/features/insights/InsightsOverview.tsx +208 -0
- package/src/components/features/insights/InstallationHistoryCard.tsx +248 -0
- package/src/components/features/insights/InstallationMatchesCard.tsx +132 -0
- package/src/components/features/insights/InstallationPageHeader.tsx +138 -0
- package/src/components/features/insights/InstallationPagination.tsx +48 -0
- package/src/components/features/insights/InstallationSearch.tsx +230 -0
- package/src/components/features/insights/UpdateOutcomes.tsx +147 -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 +139 -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 +47 -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/insights-api.ts +144 -0
- package/src/lib/insights-error.ts +19 -0
- package/src/lib/insights-input.ts +187 -0
- package/src/lib/insights-overview.ts +170 -0
- package/src/lib/insights-rpc.ts +218 -0
- package/src/lib/server/auth.server.ts +43 -0
- package/src/lib/server/bundle-signing.server.ts +23 -0
- package/src/lib/server/config.server.ts +109 -0
- package/src/lib/server/console-runtime.server.ts +61 -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/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/insights.tsx +139 -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,87 @@
|
|
|
1
|
+
import type { ApiKeyRow } from "@hot-updater/plugin-core";
|
|
2
|
+
import { createApiKey } from "@hot-updater/server";
|
|
3
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
4
|
+
|
|
5
|
+
export type ApiKeyView = Omit<ApiKeyRow, "hash">;
|
|
6
|
+
|
|
7
|
+
export const toApiKeyView = ({
|
|
8
|
+
hash: _hash,
|
|
9
|
+
...record
|
|
10
|
+
}: ApiKeyRow): ApiKeyView => record;
|
|
11
|
+
|
|
12
|
+
const parseName = (input: unknown): { readonly name: string } => {
|
|
13
|
+
const name =
|
|
14
|
+
typeof input === "object" && input !== null
|
|
15
|
+
? Reflect.get(input, "name")
|
|
16
|
+
: undefined;
|
|
17
|
+
if (typeof name !== "string") {
|
|
18
|
+
throw new TypeError("API key name must be a string.");
|
|
19
|
+
}
|
|
20
|
+
return { name };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const parseId = (input: unknown): { readonly id: string } => {
|
|
24
|
+
const id =
|
|
25
|
+
typeof input === "object" && input !== null
|
|
26
|
+
? Reflect.get(input, "id")
|
|
27
|
+
: undefined;
|
|
28
|
+
if (typeof id !== "string" || !/^api-[A-Za-z0-9_-]{43}$/u.test(id)) {
|
|
29
|
+
throw new TypeError("Invalid API key id.");
|
|
30
|
+
}
|
|
31
|
+
return { id };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const requireStore = async () => {
|
|
35
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
36
|
+
const { apiKeyStore } = await prepareConfig();
|
|
37
|
+
if (apiKeyStore === null) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
"API keys are not supported by the configured database plugin.",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return apiKeyStore;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getApiKeyCapabilityRpc = createServerFn({
|
|
46
|
+
method: "GET",
|
|
47
|
+
}).handler(async () => {
|
|
48
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
49
|
+
const { apiKeyStore } = await prepareConfig();
|
|
50
|
+
return { apiKeys: apiKeyStore !== null } as const;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const listApiKeysRpc = createServerFn({
|
|
54
|
+
method: "GET",
|
|
55
|
+
}).handler(async () => {
|
|
56
|
+
const store = await requireStore();
|
|
57
|
+
const records = await store.list();
|
|
58
|
+
return [...records]
|
|
59
|
+
.sort((left, right) => right.created_at_ms - left.created_at_ms)
|
|
60
|
+
.map(toApiKeyView);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const createApiKeyRpc = createServerFn({ method: "POST" })
|
|
64
|
+
.validator(parseName)
|
|
65
|
+
.handler(async ({ data }) => {
|
|
66
|
+
const store = await requireStore();
|
|
67
|
+
const created = await createApiKey({
|
|
68
|
+
apiKeys: store,
|
|
69
|
+
name: data.name,
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
apiKey: created.apiKey,
|
|
73
|
+
record: created.record,
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const revokeApiKeyRpc = createServerFn({ method: "POST" })
|
|
78
|
+
.validator(parseId)
|
|
79
|
+
.handler(async ({ data }) => {
|
|
80
|
+
const store = await requireStore();
|
|
81
|
+
const revoked = await store.revoke({
|
|
82
|
+
id: data.id,
|
|
83
|
+
revokedAtMs: Date.now(),
|
|
84
|
+
});
|
|
85
|
+
if (revoked === null) throw new Error("API key not found.");
|
|
86
|
+
return toApiKeyView(revoked);
|
|
87
|
+
});
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DatabasePluginInputError,
|
|
3
|
+
type Bundle,
|
|
4
|
+
type ChannelDeleteInput,
|
|
5
|
+
type ChannelInsertInput,
|
|
6
|
+
type DatabaseBundleQueryOptions,
|
|
7
|
+
deleteRelease as deleteReleaseMutation,
|
|
8
|
+
preflightReleasePolicy,
|
|
9
|
+
promoteRelease as promoteReleaseMutation,
|
|
10
|
+
type ReleasePolicyPatch,
|
|
11
|
+
updateReleasePolicy,
|
|
12
|
+
} from "@hot-updater/plugin-core";
|
|
13
|
+
import { createServerFn } from "@tanstack/react-start";
|
|
14
|
+
|
|
15
|
+
import { DEFAULT_PAGE_LIMIT } from "./constants";
|
|
16
|
+
import {
|
|
17
|
+
parseBundleEventInsightsInput,
|
|
18
|
+
parseBundleEventSummaryInput,
|
|
19
|
+
parseInstallationHistoryInput,
|
|
20
|
+
parseSearchInstallationsInput,
|
|
21
|
+
} from "./insights-input";
|
|
22
|
+
import { listReleases } from "./server/listReleases";
|
|
23
|
+
import { getReleaseActivity30d } from "./server/releaseActivity";
|
|
24
|
+
import { addReleaseReachability } from "./server/releaseReachability";
|
|
25
|
+
|
|
26
|
+
type GetBundlesInput = {
|
|
27
|
+
platform?: "ios" | "android";
|
|
28
|
+
page?: number;
|
|
29
|
+
limit?: string;
|
|
30
|
+
after?: string;
|
|
31
|
+
before?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type GetBundleInput = {
|
|
35
|
+
bundleId: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type GetBundleChildrenInput = {
|
|
39
|
+
baseBundleId: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type GetBundleChildCountsInput = {
|
|
43
|
+
bundleIds: string[];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type DeleteBundleInput = {
|
|
47
|
+
bundleId: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type DeleteBundlesInput = {
|
|
51
|
+
bundleIds: string[];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type GetReleasesInput = {
|
|
55
|
+
afterReleaseId?: string;
|
|
56
|
+
beforeReleaseId?: string;
|
|
57
|
+
bundleId?: string;
|
|
58
|
+
channelId?: string;
|
|
59
|
+
enabled?: boolean;
|
|
60
|
+
platform?: "ios" | "android";
|
|
61
|
+
limit?: number;
|
|
62
|
+
page?: number;
|
|
63
|
+
targetAppVersion?: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type ReleaseMutationInput = {
|
|
67
|
+
expectedRevision?: number;
|
|
68
|
+
patch: ReleasePolicyPatch;
|
|
69
|
+
releaseId: string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type DeleteReleaseInput = {
|
|
73
|
+
expectedRevision?: number;
|
|
74
|
+
releaseId: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const getReleases = createServerFn({ method: "GET" })
|
|
78
|
+
.inputValidator((input: GetReleasesInput | undefined) => input)
|
|
79
|
+
.handler(async ({ data }) => {
|
|
80
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
81
|
+
const { config, hotUpdater } = await prepareConfig();
|
|
82
|
+
const result = await listReleases(config.database.models.releases, {
|
|
83
|
+
...(data?.afterReleaseId === undefined
|
|
84
|
+
? {}
|
|
85
|
+
: { afterReleaseId: data.afterReleaseId }),
|
|
86
|
+
...(data?.beforeReleaseId === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { beforeReleaseId: data.beforeReleaseId }),
|
|
89
|
+
...(data?.bundleId === undefined ? {} : { bundleId: data.bundleId }),
|
|
90
|
+
...(data?.channelId === undefined ? {} : { channelId: data.channelId }),
|
|
91
|
+
...(data?.enabled === undefined ? {} : { enabled: data.enabled }),
|
|
92
|
+
...(data?.page === undefined ? {} : { page: data.page }),
|
|
93
|
+
...(data?.platform === undefined ? {} : { platform: data.platform }),
|
|
94
|
+
...(data?.targetAppVersion === undefined
|
|
95
|
+
? {}
|
|
96
|
+
: { targetAppVersion: data.targetAppVersion }),
|
|
97
|
+
limit: data?.limit ?? DEFAULT_PAGE_LIMIT,
|
|
98
|
+
});
|
|
99
|
+
const [releases, activityByBundleId] = await Promise.all([
|
|
100
|
+
addReleaseReachability(
|
|
101
|
+
config.database.models.releaseCatalogs,
|
|
102
|
+
result.data,
|
|
103
|
+
),
|
|
104
|
+
getReleaseActivity30d(hotUpdater, result.data),
|
|
105
|
+
]);
|
|
106
|
+
return {
|
|
107
|
+
...result,
|
|
108
|
+
data: releases.map((release) => ({
|
|
109
|
+
...release,
|
|
110
|
+
activity30d:
|
|
111
|
+
release.bundle_id === null
|
|
112
|
+
? null
|
|
113
|
+
: (activityByBundleId.get(release.bundle_id) ?? null),
|
|
114
|
+
})),
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export const getRelease = createServerFn({ method: "GET" })
|
|
119
|
+
.inputValidator((input: { releaseId: string }) => input)
|
|
120
|
+
.handler(async ({ data }) => {
|
|
121
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
122
|
+
const { config } = await prepareConfig();
|
|
123
|
+
return config.database.models.releases.findById(data.releaseId);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
export const updateRelease = createServerFn({ method: "POST" })
|
|
127
|
+
.inputValidator((input: ReleaseMutationInput) => input)
|
|
128
|
+
.handler(async ({ data }) => {
|
|
129
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
130
|
+
const { config } = await prepareConfig();
|
|
131
|
+
return updateReleasePolicy({ database: config.database, ...data });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
export const preflightRelease = createServerFn({ method: "POST" })
|
|
135
|
+
.inputValidator((input: ReleaseMutationInput) => input)
|
|
136
|
+
.handler(async ({ data }) => {
|
|
137
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
138
|
+
const { config } = await prepareConfig();
|
|
139
|
+
return preflightReleasePolicy({ database: config.database, ...data });
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export const deleteRelease = createServerFn({ method: "POST" })
|
|
143
|
+
.inputValidator((input: DeleteReleaseInput) => input)
|
|
144
|
+
.handler(async ({ data }) => {
|
|
145
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
146
|
+
const { config } = await prepareConfig();
|
|
147
|
+
return deleteReleaseMutation({ database: config.database, ...data });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export const promoteRelease = createServerFn({ method: "POST" })
|
|
151
|
+
.inputValidator(
|
|
152
|
+
(input: {
|
|
153
|
+
action: "copy" | "move";
|
|
154
|
+
expectedRevision?: number;
|
|
155
|
+
releaseId: string;
|
|
156
|
+
targetChannel: string;
|
|
157
|
+
}) => input,
|
|
158
|
+
)
|
|
159
|
+
.handler(async ({ data }) => {
|
|
160
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
161
|
+
const { config } = await prepareConfig();
|
|
162
|
+
return promoteReleaseMutation({ database: config.database, ...data });
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export const getReleaseCatalogDiagnostics = createServerFn({ method: "GET" })
|
|
166
|
+
.inputValidator((input: { scopeKey: string }) => input)
|
|
167
|
+
.handler(async ({ data }) => {
|
|
168
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
169
|
+
const { config } = await prepareConfig();
|
|
170
|
+
return config.database.models.releaseCatalogs.findByScopeKey(data.scopeKey);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// GET /api/config
|
|
174
|
+
export const getConfig = createServerFn().handler(async () => {
|
|
175
|
+
try {
|
|
176
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
177
|
+
const { config } = await prepareConfig();
|
|
178
|
+
return { console: config.console };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error("Error during config retrieval:", error);
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// GET /api/channels
|
|
186
|
+
export const getChannels = createServerFn().handler(async () => {
|
|
187
|
+
try {
|
|
188
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
189
|
+
const { databaseClient } = await prepareConfig();
|
|
190
|
+
const channels = await databaseClient.getChannels();
|
|
191
|
+
return channels ?? [];
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error("Error during channel retrieval:", error);
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// POST /api/channels
|
|
199
|
+
export const createChannel = createServerFn({ method: "POST" })
|
|
200
|
+
.inputValidator((input: ChannelInsertInput) => input)
|
|
201
|
+
.handler(async ({ data }) => {
|
|
202
|
+
try {
|
|
203
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
204
|
+
const { databaseClient } = await prepareConfig();
|
|
205
|
+
return { data: await databaseClient.insertChannel(data) };
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error("Error during channel creation:", error);
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// DELETE /api/channels/:id
|
|
213
|
+
export const deleteChannel = createServerFn({ method: "POST" })
|
|
214
|
+
.inputValidator((input: ChannelDeleteInput) => input)
|
|
215
|
+
.handler(async ({ data }) => {
|
|
216
|
+
try {
|
|
217
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
218
|
+
const { databaseClient } = await prepareConfig();
|
|
219
|
+
return { data: await databaseClient.deleteChannel(data) };
|
|
220
|
+
} catch (error) {
|
|
221
|
+
console.error("Error during channel deletion:", error);
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// GET /api/config-loaded
|
|
227
|
+
export const getConfigLoaded = createServerFn().handler(async () => {
|
|
228
|
+
try {
|
|
229
|
+
const [{ getRequest }, { requireConsoleAccess }] = await Promise.all([
|
|
230
|
+
import("@tanstack/react-start/server"),
|
|
231
|
+
import("./server/auth.server"),
|
|
232
|
+
]);
|
|
233
|
+
await requireConsoleAccess(getRequest());
|
|
234
|
+
const { isConfigLoaded } = await import("./server/config.server");
|
|
235
|
+
const configLoaded = isConfigLoaded();
|
|
236
|
+
return { configLoaded };
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.error("Error during config loaded retrieval:", error);
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// GET /api/bundles
|
|
244
|
+
export const getBundles = createServerFn({ method: "GET" })
|
|
245
|
+
.inputValidator((input: GetBundlesInput | undefined) => input)
|
|
246
|
+
.handler(async ({ data }) => {
|
|
247
|
+
try {
|
|
248
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
249
|
+
const query = {
|
|
250
|
+
platform: data?.platform ?? undefined,
|
|
251
|
+
page:
|
|
252
|
+
typeof data?.page === "number" &&
|
|
253
|
+
Number.isInteger(data.page) &&
|
|
254
|
+
data.page > 1
|
|
255
|
+
? data.page
|
|
256
|
+
: undefined,
|
|
257
|
+
limit: data?.limit ? Number(data.limit) : DEFAULT_PAGE_LIMIT,
|
|
258
|
+
after: data?.after ?? undefined,
|
|
259
|
+
before: data?.before ?? undefined,
|
|
260
|
+
};
|
|
261
|
+
if (
|
|
262
|
+
(query.after !== undefined && query.before !== undefined) ||
|
|
263
|
+
(query.page !== undefined &&
|
|
264
|
+
(query.after !== undefined || query.before !== undefined))
|
|
265
|
+
) {
|
|
266
|
+
throw new DatabasePluginInputError("invalid-pagination");
|
|
267
|
+
}
|
|
268
|
+
const pagination =
|
|
269
|
+
query.page !== undefined
|
|
270
|
+
? { page: query.page }
|
|
271
|
+
: query.after !== undefined
|
|
272
|
+
? { cursor: { after: query.after } }
|
|
273
|
+
: query.before !== undefined
|
|
274
|
+
? { cursor: { before: query.before } }
|
|
275
|
+
: {};
|
|
276
|
+
|
|
277
|
+
const { databaseClient } = await prepareConfig();
|
|
278
|
+
const bundleQueryOptions: DatabaseBundleQueryOptions = {
|
|
279
|
+
where: {
|
|
280
|
+
platform: query.platform,
|
|
281
|
+
},
|
|
282
|
+
limit: query.limit,
|
|
283
|
+
...pagination,
|
|
284
|
+
};
|
|
285
|
+
const bundles = await databaseClient.getBundles(bundleQueryOptions);
|
|
286
|
+
|
|
287
|
+
return (
|
|
288
|
+
bundles ?? {
|
|
289
|
+
data: [],
|
|
290
|
+
pagination: {
|
|
291
|
+
total: 0,
|
|
292
|
+
hasNextPage: false,
|
|
293
|
+
hasPreviousPage: false,
|
|
294
|
+
currentPage: 1,
|
|
295
|
+
totalPages: 0,
|
|
296
|
+
},
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error("Error during bundle retrieval:", error);
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// GET /api/bundles/:bundleId
|
|
306
|
+
export const getBundle = createServerFn({ method: "GET" })
|
|
307
|
+
.inputValidator((input: GetBundleInput) => input)
|
|
308
|
+
.handler(async ({ data }) => {
|
|
309
|
+
try {
|
|
310
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
311
|
+
const { databaseClient } = await prepareConfig();
|
|
312
|
+
const bundle = await databaseClient.getBundleById(data.bundleId);
|
|
313
|
+
return bundle ?? null;
|
|
314
|
+
} catch (error) {
|
|
315
|
+
console.error("Error during bundle retrieval:", error);
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
export const getBundleEventSummary = createServerFn({ method: "GET" })
|
|
321
|
+
.inputValidator(parseBundleEventSummaryInput)
|
|
322
|
+
.handler(async ({ data }) => {
|
|
323
|
+
try {
|
|
324
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
325
|
+
const { getBundleEventSummary: getBundleEventSummaryWithRuntime } =
|
|
326
|
+
await import("./server/runtime.server");
|
|
327
|
+
const { hotUpdater } = await prepareConfig();
|
|
328
|
+
|
|
329
|
+
return await getBundleEventSummaryWithRuntime(hotUpdater, data);
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error("Error during bundle event summary retrieval:", error);
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
export const getBundleEventInsights = createServerFn({ method: "GET" })
|
|
337
|
+
.inputValidator(parseBundleEventInsightsInput)
|
|
338
|
+
.handler(async ({ data }) => {
|
|
339
|
+
try {
|
|
340
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
341
|
+
const { getBundleEventInsights: getBundleEventInsightsWithRuntime } =
|
|
342
|
+
await import("./server/runtime.server");
|
|
343
|
+
const { hotUpdater } = await prepareConfig();
|
|
344
|
+
|
|
345
|
+
return await getBundleEventInsightsWithRuntime(hotUpdater, data);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error("Error during bundle event insights retrieval:", error);
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
export const searchInstallations = createServerFn({ method: "GET" })
|
|
353
|
+
.inputValidator(parseSearchInstallationsInput)
|
|
354
|
+
.handler(async ({ data }) => {
|
|
355
|
+
try {
|
|
356
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
357
|
+
const { searchInstallations: searchInstallationsWithRuntime } =
|
|
358
|
+
await import("./server/runtime.server");
|
|
359
|
+
const { hotUpdater } = await prepareConfig();
|
|
360
|
+
|
|
361
|
+
return await searchInstallationsWithRuntime(hotUpdater, data);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error("Error during installation search:", error);
|
|
364
|
+
throw error;
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
export const getInstallationHistory = createServerFn({ method: "GET" })
|
|
369
|
+
.inputValidator(parseInstallationHistoryInput)
|
|
370
|
+
.handler(async ({ data }) => {
|
|
371
|
+
try {
|
|
372
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
373
|
+
const { getInstallationHistory: getInstallationHistoryWithRuntime } =
|
|
374
|
+
await import("./server/runtime.server");
|
|
375
|
+
const { hotUpdater } = await prepareConfig();
|
|
376
|
+
|
|
377
|
+
return await getInstallationHistoryWithRuntime(hotUpdater, data);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error("Error during installation history retrieval:", error);
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
export const getBundleChildren = createServerFn({ method: "GET" })
|
|
385
|
+
.inputValidator((input: GetBundleChildrenInput) => input)
|
|
386
|
+
.handler(async ({ data }) => {
|
|
387
|
+
try {
|
|
388
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
389
|
+
const { getBundleChildren: getBundleChildrenWithConfig } =
|
|
390
|
+
await import("./server/getBundleChildren");
|
|
391
|
+
const { databaseClient } = await prepareConfig();
|
|
392
|
+
|
|
393
|
+
return await getBundleChildrenWithConfig(data, {
|
|
394
|
+
databaseClient,
|
|
395
|
+
});
|
|
396
|
+
} catch (error) {
|
|
397
|
+
console.error("Error during bundle children retrieval:", error);
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
export const getBundleChildCounts = createServerFn({ method: "GET" })
|
|
403
|
+
.inputValidator((input: GetBundleChildCountsInput) => input)
|
|
404
|
+
.handler(async ({ data }) => {
|
|
405
|
+
try {
|
|
406
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
407
|
+
const { getBundleChildCounts: getBundleChildCountsWithConfig } =
|
|
408
|
+
await import("./server/getBundleChildren");
|
|
409
|
+
const { databaseClient } = await prepareConfig();
|
|
410
|
+
|
|
411
|
+
return await getBundleChildCountsWithConfig(data.bundleIds, {
|
|
412
|
+
databaseClient,
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error("Error during bundle child count retrieval:", error);
|
|
416
|
+
throw error;
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// POST /api/bundles
|
|
421
|
+
export const createBundle = createServerFn({ method: "POST" })
|
|
422
|
+
.inputValidator((input: Bundle) => input)
|
|
423
|
+
.handler(async ({ data }) => {
|
|
424
|
+
try {
|
|
425
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
426
|
+
const { databaseClient } = await prepareConfig();
|
|
427
|
+
await databaseClient.insertBundle(data);
|
|
428
|
+
return { success: true, bundleId: data.id };
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error("Error during bundle creation:", error);
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// DELETE /api/bundles/:bundleId
|
|
436
|
+
export const deleteBundle = createServerFn({ method: "POST" })
|
|
437
|
+
.inputValidator((input: DeleteBundleInput) => input)
|
|
438
|
+
.handler(async ({ data }) => {
|
|
439
|
+
try {
|
|
440
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
441
|
+
const { deleteBundle: deleteBundleWithStorage } =
|
|
442
|
+
await import("./server/deleteBundle");
|
|
443
|
+
const { databaseClient, storagePlugin } = await prepareConfig();
|
|
444
|
+
|
|
445
|
+
await deleteBundleWithStorage(data, {
|
|
446
|
+
databaseClient,
|
|
447
|
+
storagePlugin,
|
|
448
|
+
waitForStorageCleanup: false,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
return { success: true };
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error("Error during bundle deletion:", error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
export const deleteBundles = createServerFn({ method: "POST" })
|
|
459
|
+
.inputValidator((input: DeleteBundlesInput) => input)
|
|
460
|
+
.handler(async ({ data }) => {
|
|
461
|
+
try {
|
|
462
|
+
const { prepareConfig } = await import("./server/config.server");
|
|
463
|
+
const { deleteBundles: deleteBundlesWithStorage } =
|
|
464
|
+
await import("./server/deleteBundle");
|
|
465
|
+
const { databaseClient, storagePlugin } = await prepareConfig();
|
|
466
|
+
|
|
467
|
+
const result = await deleteBundlesWithStorage(data, {
|
|
468
|
+
databaseClient,
|
|
469
|
+
storagePlugin,
|
|
470
|
+
waitForStorageCleanup: false,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
return { success: true, ...result };
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.error("Error during bundle deletion:", error);
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
});
|