Skip to content

Commit 0bc7498

Browse files
authored
refactor(registration): remove flash drive references for internal boot support (#1880)
The registration page uses flash-drive-specific terminology that doesn't apply to internal boot or TPM-based license types; this PR replaces those references with generic boot device language, adds dynamic device type detection from GUID prefixes, and conditionally preserves flash-specific details for USB flash users. ## Why This Exists With internal boot support (single drive, multi-drive redundant) and TPM-based licensing, the registration page still referred exclusively to "USB Flash" — showing flash vendor/product fields and "Flash GUID" labels that are meaningless for non-flash boot devices. Users on internal boot or TPM setups saw confusing, inaccurate UI. ## Resolution - Replace all "flash drive" / "USB Flash" terminology in the registration UI and error messages with generic "boot device" language. - Add a `bootDeviceType` computed property that derives device type from GUID prefixes (`01-` = internal boot single-device, `02-` = internal boot multi-device, `03-` = TPM, no prefix = flash). - Display the detected boot device type in the registration page's Boot Device section. - Conditionally show flash vendor and flash product rows only when the boot device is USB flash (these fields carry real data from the API for flash users). - Remove `computedArray` and `arrayWarning` from the registration component (array status is no longer displayed). ## Implications / Review Concerns - The GUID prefix mapping (`01-` = internal boot, `02-` = internal boot multi-device, `03-` = TPM) is based on the Asana task specification. If these prefixes change, only `server.ts` needs updating. - Dead locale keys (`registration.flashDrive`, `registration.flashGuid`, `registration.arrayStatus`, `registration.transferLicenseToNewFlash`) were removed from `en.json`. Other locale files still carry the old keys — they are harmless but could be cleaned up in a follow-up. - The `GetCoreSettings` GraphQL query now also fetches `customization.activationCode.system` — verify the API schema supports this field. ## Behavior Changes - Registration page now shows "Boot Device" section header instead of "Flash Drive". - "Flash GUID" label becomes "Device GUID". - New "Boot device type" row appears showing "USB Flash", "Internal Boot", "Internal Boot (Multi-device)", or "TPM" based on the GUID prefix. - Flash vendor and flash product rows are shown **only for USB flash boot devices**, hidden for internal boot and TPM. - Array status row is no longer displayed. - "Transfer License to New Flash" becomes "Transfer License to New Device". - All error/state messages (blacklisted, GUID mismatch, missing keyfile, etc.) now use "boot device" instead of "USB Flash". ## Implementation Summary - **`web/src/store/server.ts`**: Added `bootDeviceType` computed property mapping GUID prefixes to device types. `02-` now maps to `'internalBootMulti'` (distinct from `01-` = `'internalBoot'`). - **`web/src/components/Registration.standalone.vue`**: Replaced `flashDriveItems` with `bootDeviceItems`, conditionally shows flash vendor/product only for flash boot type, removed `computedArray`/`arrayWarning` usage, updated template labels. - **`web/src/locales/en.json`**: Added new keys (`bootDevice`, `bootDeviceType.*` including `internalBootMulti`, `deviceGuid`, `transferLicenseToNewDevice`), removed dead keys (`flashDrive`, `flashGuid`, `arrayStatus`, `transferLicenseToNewFlash`), retained `flashVendor`/`flashProduct` (still used conditionally), updated all `server.state.*` messages. - **`web/__test__/components/Registration.test.ts`**: Updated assertions to match new label keys, removed `flashVendor`/`flashProduct` from initial state. - **`web/src/composables/gql/gql.ts` / `graphql.ts`**: Regenerated — `GetCoreSettings` query now includes `customization.activationCode.system`. ## Tests | Command | Environment | Result | |---|---|---| | `pnpm test` (web) | macOS / Node 22 / Vitest 3.2.4 | 517 passed, 6 skipped, 56 files | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Core settings now include activation-code system info. * UI now recognizes multiple boot device types (flash, internal, internalMulti, tpm) so device-specific rows render appropriately. * **Refactor** * Terminology updated from "Flash" to "Boot Device"; "Device GUID" replaces "Flash GUID". * License action relabeled to "Transfer License to New Device". * Translation keys updated for boot device labels and types. * **Bug Fixes** * Tests updated to reflect label changes. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent aaa0372 commit 0bc7498

File tree

6 files changed

+64
-54
lines changed

6 files changed

+64
-54
lines changed

web/__test__/components/Registration.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@ const initialServerState = {
122122
dateTimeFormat: { date: 'MMM D, YYYY', time: 'h:mm A' },
123123
deviceCount: 0,
124124
guid: '',
125-
flashVendor: '',
126-
flashProduct: '',
127125
keyfile: '',
128126
regGuid: '',
129127
regTm: '',
@@ -219,7 +217,7 @@ describe('Registration.standalone.vue', () => {
219217
expect(heading.text()).toContain("Let's Unleash Your Hardware");
220218
expect(subheading.text()).toContain('Choose an option below');
221219
expect(findItemByLabel(t('License key type'))).toBeUndefined();
222-
expect(findItemByLabel(t('Flash GUID'))).toBeUndefined();
220+
expect(findItemByLabel(t('Device GUID'))).toBeUndefined();
223221
expect(wrapper.find('[data-testid="key-actions"]').exists()).toBe(true);
224222
expect(wrapper.find('[data-testid="replace-check"]').exists()).toBe(false);
225223
expect(wrapper.find('[data-testid="key-linked-status"]').exists()).toBe(false);
@@ -269,7 +267,7 @@ describe('Registration.standalone.vue', () => {
269267

270268
expect(registeredToItem).toBeDefined();
271269
expect(registeredToItem?.props('text')).toBe('Test User');
272-
expect(findItemByLabel(t('Flash GUID'))).toBeDefined();
270+
expect(findItemByLabel(t('Device GUID'))).toBeDefined();
273271
expect(findItemByLabel(t('Attached Storage Devices'))).toBeDefined();
274272
expect(wrapper.find('[data-testid="key-actions"]').exists()).toBe(false);
275273
});

web/src/components/Registration.standalone.vue

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@ const serverStore = useServerStore();
4343
const { activationCode } = storeToRefs(useActivationCodeDataStore());
4444
4545
const {
46-
computedArray,
47-
arrayWarning,
4846
authAction,
47+
bootDeviceType,
4948
dateTimeFormat,
5049
deviceCount,
51-
guid,
52-
flashVendor,
5350
flashProduct,
51+
flashVendor,
52+
guid,
5453
keyActions,
5554
keyfile,
5655
computedRegDevs,
@@ -135,25 +134,33 @@ const showPartnerActivationCode = computed(() => {
135134
});
136135
137136
// Organize items into three sections
138-
const flashDriveItems = computed((): RegistrationItemProps[] => {
137+
const bootDeviceItems = computed((): RegistrationItemProps[] => {
139138
return [
140139
...(guid.value
141140
? [
142141
{
143-
label: t('registration.flashGuid'),
142+
label: t('registration.deviceGuid'),
144143
text: guid.value,
145144
},
146145
]
147146
: []),
148-
...(flashVendor.value
147+
...(bootDeviceType.value
148+
? [
149+
{
150+
label: t('registration.bootDeviceType'),
151+
text: t(`registration.bootDeviceType.${bootDeviceType.value}`),
152+
},
153+
]
154+
: []),
155+
...(bootDeviceType.value === 'flash' && flashVendor.value
149156
? [
150157
{
151158
label: t('registration.flashVendor'),
152159
text: flashVendor.value,
153160
},
154161
]
155162
: []),
156-
...(flashProduct.value
163+
...(bootDeviceType.value === 'flash' && flashProduct.value
157164
? [
158165
{
159166
label: t('registration.flashProduct'),
@@ -174,15 +181,6 @@ const flashDriveItems = computed((): RegistrationItemProps[] => {
174181
175182
const licenseItems = computed((): RegistrationItemProps[] => {
176183
return [
177-
...(computedArray.value
178-
? [
179-
{
180-
label: t('registration.arrayStatus'),
181-
text: computedArray.value,
182-
warning: arrayWarning.value,
183-
},
184-
]
185-
: []),
186184
...(regTy.value
187185
? [
188186
{
@@ -268,7 +266,7 @@ const actionItems = computed((): RegistrationItemProps[] => {
268266
...(showLinkedAndTransferStatus.value
269267
? [
270268
{
271-
label: t('registration.transferLicenseToNewFlash'),
269+
label: t('registration.transferLicenseToNewDevice'),
272270
component: RegistrationReplaceCheck,
273271
componentProps: { t },
274272
},
@@ -329,14 +327,14 @@ const actionItems = computed((): RegistrationItemProps[] => {
329327
</span>
330328
</header>
331329

332-
<!-- Flash Drive Section -->
330+
<!-- Boot Device Section -->
333331
<div
334-
v-if="flashDriveItems.length > 0"
332+
v-if="bootDeviceItems.length > 0"
335333
class="rounded-lg border border-gray-200 p-4 dark:border-gray-700"
336334
>
337-
<h4 class="mb-3 text-lg font-semibold">{{ t('registration.flashDrive') }}</h4>
335+
<h4 class="mb-3 text-lg font-semibold">{{ t('registration.bootDevice') }}</h4>
338336
<SettingsGrid>
339-
<template v-for="item in flashDriveItems" :key="item.label">
337+
<template v-for="item in bootDeviceItems" :key="item.label">
340338
<div class="flex items-center gap-x-2 font-semibold">
341339
<ShieldExclamationIcon v-if="item.error" class="text-unraid-red h-4 w-4" />
342340
<span v-html="item.label" />

web/src/composables/gql/gql.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type Documents = {
7272
"\n mutation UpdateServerIdentity($name: String!, $comment: String, $sysModel: String) {\n updateServerIdentity(name: $name, comment: $comment, sysModel: $sysModel) {\n id\n name\n comment\n }\n }\n": typeof types.UpdateServerIdentityDocument,
7373
"\n mutation SetLocale($locale: String!) {\n customization {\n setLocale(locale: $locale)\n }\n }\n": typeof types.SetLocaleDocument,
7474
"\n mutation UpdateSshSettings($enabled: Boolean!, $port: Int = 22) {\n updateSshSettings(input: { enabled: $enabled, port: $port }) {\n id\n useSsh\n portssh\n }\n }\n": typeof types.UpdateSshSettingsDocument,
75-
"\n query GetCoreSettings {\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n": typeof types.GetCoreSettingsDocument,
75+
"\n query GetCoreSettings {\n customization {\n activationCode {\n system {\n serverName\n comment\n }\n }\n }\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n": typeof types.GetCoreSettingsDocument,
7676
"\n mutation InstallLanguage($input: InstallPluginInput!) {\n unraidPlugins {\n installLanguage(input: $input) {\n id\n url\n name\n status\n createdAt\n updatedAt\n finishedAt\n output\n }\n }\n }\n": typeof types.InstallLanguageDocument,
7777
"\n mutation InstallPlugin($input: InstallPluginInput!) {\n unraidPlugins {\n installPlugin(input: $input) {\n id\n url\n name\n status\n createdAt\n updatedAt\n finishedAt\n output\n }\n }\n }\n": typeof types.InstallPluginDocument,
7878
"\n query InstalledUnraidPlugins {\n installedUnraidPlugins\n }\n": typeof types.InstalledUnraidPluginsDocument,
@@ -154,7 +154,7 @@ const documents: Documents = {
154154
"\n mutation UpdateServerIdentity($name: String!, $comment: String, $sysModel: String) {\n updateServerIdentity(name: $name, comment: $comment, sysModel: $sysModel) {\n id\n name\n comment\n }\n }\n": types.UpdateServerIdentityDocument,
155155
"\n mutation SetLocale($locale: String!) {\n customization {\n setLocale(locale: $locale)\n }\n }\n": types.SetLocaleDocument,
156156
"\n mutation UpdateSshSettings($enabled: Boolean!, $port: Int = 22) {\n updateSshSettings(input: { enabled: $enabled, port: $port }) {\n id\n useSsh\n portssh\n }\n }\n": types.UpdateSshSettingsDocument,
157-
"\n query GetCoreSettings {\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n": types.GetCoreSettingsDocument,
157+
"\n query GetCoreSettings {\n customization {\n activationCode {\n system {\n serverName\n comment\n }\n }\n }\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n": types.GetCoreSettingsDocument,
158158
"\n mutation InstallLanguage($input: InstallPluginInput!) {\n unraidPlugins {\n installLanguage(input: $input) {\n id\n url\n name\n status\n createdAt\n updatedAt\n finishedAt\n output\n }\n }\n }\n": types.InstallLanguageDocument,
159159
"\n mutation InstallPlugin($input: InstallPluginInput!) {\n unraidPlugins {\n installPlugin(input: $input) {\n id\n url\n name\n status\n createdAt\n updatedAt\n finishedAt\n output\n }\n }\n }\n": types.InstallPluginDocument,
160160
"\n query InstalledUnraidPlugins {\n installedUnraidPlugins\n }\n": types.InstalledUnraidPluginsDocument,
@@ -427,7 +427,7 @@ export function graphql(source: "\n mutation UpdateSshSettings($enabled: Boolea
427427
/**
428428
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
429429
*/
430-
export function graphql(source: "\n query GetCoreSettings {\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n"): (typeof documents)["\n query GetCoreSettings {\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n"];
430+
export function graphql(source: "\n query GetCoreSettings {\n customization {\n activationCode {\n system {\n serverName\n comment\n }\n }\n }\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n"): (typeof documents)["\n query GetCoreSettings {\n customization {\n activationCode {\n system {\n serverName\n comment\n }\n }\n }\n vars {\n name\n sysModel\n useSsh\n localTld\n }\n server {\n name\n comment\n }\n display {\n theme\n locale\n }\n systemTime {\n timeZone\n }\n info {\n primaryNetwork {\n ipAddress\n }\n }\n }\n"];
431431
/**
432432
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
433433
*/

0 commit comments

Comments
 (0)