diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea27da0b87..03b5eb3b35 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1139,8 +1139,8 @@ importers: specifier: 8.21.3 version: 8.21.3(vue@3.5.20(typescript@5.9.2)) '@unraid/shared-callbacks': - specifier: 3.1.0 - version: 3.1.0 + specifier: 3.2.0 + version: 3.2.0 '@unraid/ui': specifier: link:../unraid-ui version: link:../unraid-ui @@ -4943,8 +4943,8 @@ packages: cpu: [x64, arm64] os: [linux, darwin] - '@unraid/shared-callbacks@3.1.0': - resolution: {integrity: sha512-Zvz9nlvLSjbTstplvarbH2rwZrsl4ns7djpDmqPi4xH35PvYeK9ut1A/Hkd2Rbl/+nvAMF77d5ZzZkSTb4IoSg==} + '@unraid/shared-callbacks@3.2.0': + resolution: {integrity: sha512-MNkKkbjJLt9lkT5xk7ndBk1zQGHYwR+MxbWTcauc5jQpsEy4t2h8HLhOIFdU2ORgKT9q1f0KmPjCMSEnwT2hIA==} '@unraid/tailwind-rem-to-rem@2.0.0': resolution: {integrity: sha512-zccpQx5fvEBkAB0JkRwwtyRrT9l26LsjkozLy44LGv0NdZGaxgscniIqJRM+OQj5pSpsWDzExebAtUKdE98Flg==} @@ -16218,7 +16218,7 @@ snapshots: - encoding - supports-color - '@unraid/shared-callbacks@3.1.0': + '@unraid/shared-callbacks@3.2.0': dependencies: crypto-js: 4.2.0 diff --git a/web/__test__/components/Registration.test.ts b/web/__test__/components/Registration.test.ts index 788542a161..36661f6ea1 100644 --- a/web/__test__/components/Registration.test.ts +++ b/web/__test__/components/Registration.test.ts @@ -351,9 +351,27 @@ describe('Registration.standalone.vue', () => { expect(moveButton.exists()).toBe(true); expect(moveButton.attributes('disabled')).toBeUndefined(); + expect(findItemByLabel(t('registration.flashGuid'))?.props('text')).toBe( + '058F-6387-0000-0000F1F1E1C6' + ); expect(findItemByLabel(t('TPM GUID'))?.props('text')).toBe('01-V35H8S0L1QHK1SBG1XHXJNH7'); }); + it('shows both licensing GUIDs when no key is installed', async () => { + serverStore.state = 'ENOKEYFILE'; + serverStore.guid = '058F-6387-0000-0000F1F1E1C6'; + serverStore.flashGuid = '058F-6387-0000-0000F1F1E1C6'; + serverStore.tpmGuid = '01-V35H8S0L1QHK1SBG1XHXJNH7'; + + await wrapper.vm.$nextTick(); + + expect(findItemByLabel(t('registration.flashGuid'))?.props('text')).toBe( + '058F-6387-0000-0000F1F1E1C6' + ); + expect(findItemByLabel(t('TPM GUID'))?.props('text')).toBe('01-V35H8S0L1QHK1SBG1XHXJNH7'); + expect(wrapper.find('[data-testid="move-license-to-tpm"]').exists()).toBe(false); + }); + it('shows Move License to TPM when flashGuid is missing but the active GUID is still a flash GUID', async () => { serverStore.state = 'PRO'; serverStore.guid = '058F-6387-0000-0000F1F1E1C6'; diff --git a/web/__test__/store/server.test.ts b/web/__test__/store/server.test.ts index 9d1127c73c..31b2a47d6b 100644 --- a/web/__test__/store/server.test.ts +++ b/web/__test__/store/server.test.ts @@ -188,6 +188,7 @@ const getStore = () => { description: store.description, deviceCount: store.deviceCount, expireTime: store.expireTime, + flashGuid: store.flashGuid || undefined, flashProduct: store.flashProduct, flashVendor: store.flashVendor, guid: store.guid, @@ -203,6 +204,7 @@ const getStore = () => { regTy: store.regTy, regUpdatesExpired: store.regUpdatesExpired, state: store.state, + tpmGuid: store.tpmGuid || undefined, wanFQDN: store.wanFQDN, }; @@ -218,6 +220,7 @@ const getStore = () => { deviceCount: store.deviceCount, description: store.description, expireTime: store.expireTime, + flashGuid: store.flashGuid || undefined, flashProduct: store.flashProduct, flashVendor: store.flashVendor, guid: store.guid, @@ -233,6 +236,7 @@ const getStore = () => { regTy: store.regTy, regUpdatesExpired: store.regUpdatesExpired, state: store.state, + tpmGuid: store.tpmGuid || undefined, wanFQDN: store.wanFQDN, }; @@ -292,6 +296,14 @@ const getStore = () => { return store; }; +const getActualStore = () => + useServerStore( + createTestingPinia({ + createSpy: vi.fn, + stubActions: false, + }) + ); + // Mock dependent stores vi.mock('~/store/account', () => ({ useAccountStore: vi.fn(() => ({ @@ -651,6 +663,7 @@ describe('useServerStore', () => { deviceCount: 6, description: 'Test Server', expireTime: 123, + flashGuid: 'flash-guid-1', flashProduct: 'TestFlash', flashVendor: 'TestVendor', guid: '123456', @@ -675,6 +688,7 @@ describe('useServerStore', () => { expect(payload.description).toBe('Test Server'); expect(payload.deviceCount).toBe(6); expect(payload.expireTime).toBe(123); + expect(payload.flashGuid).toBe('flash-guid-1'); expect(payload.flashProduct).toBe('TestFlash'); expect(payload.flashVendor).toBe('TestVendor'); expect(payload.guid).toBe('123456'); @@ -767,6 +781,7 @@ describe('useServerStore', () => { regGuid: 'reg-guid-1', regTy: 'Plus', state: 'PLUS' as ServerState, + tpmGuid: '01-TPM-GUID-1', wanFQDN: 'test.myunraid.net', }); @@ -777,6 +792,7 @@ describe('useServerStore', () => { expect(payload.deviceCount).toBe(6); expect(payload.description).toBe('Test Server'); expect(payload.expireTime).toBe(123); + expect(payload.flashGuid).toBe('flash-guid-1'); expect(payload.flashProduct).toBe('TestFlash'); expect(payload.flashVendor).toBe('TestVendor'); expect(payload.guid).toBe('123456'); @@ -792,9 +808,27 @@ describe('useServerStore', () => { expect(payload.regTy).toBe('Plus'); expect(payload.regUpdatesExpired).toBe(true); expect(payload.state).toBe('PLUS'); + expect(payload.tpmGuid).toBe('01-TPM-GUID-1'); expect(payload.wanFQDN).toBe('test.myunraid.net'); }); + it('should include physical identities in the actual callback payloads', () => { + const store = getActualStore(); + + store.setServer({ + flashGuid: '058F-6387-0000-0000F1F1E1C6', + guid: '058F-6387-0000-0000F1F1E1C6', + tpmGuid: '01-V35H8S0L1QHK1SBG1XHXJNH7', + } as Server); + + expect(store.serverPurchasePayload.flashGuid).toBe('058F-6387-0000-0000F1F1E1C6'); + expect(store.serverPurchasePayload.tpmGuid).toBe('01-V35H8S0L1QHK1SBG1XHXJNH7'); + expect(store.serverAccountPayload.flashGuid).toBe('058F-6387-0000-0000F1F1E1C6'); + expect(store.serverAccountPayload.tpmGuid).toBe('01-V35H8S0L1QHK1SBG1XHXJNH7'); + expect(store.serverReplacePayload.flashGuid).toBe('058F-6387-0000-0000F1F1E1C6'); + expect(store.serverReplacePayload.tpmGuid).toBe('01-V35H8S0L1QHK1SBG1XHXJNH7'); + }); + it('should create serverReplacePayload with TPM guid when available on flash boot', () => { const store = getStore(); diff --git a/web/package.json b/web/package.json index cd1bdf037e..a8a1559297 100644 --- a/web/package.json +++ b/web/package.json @@ -116,7 +116,7 @@ "@jsonforms/vue-vuetify": "3.6.0", "@nuxt/ui": "4.8.2", "@tanstack/vue-table": "8.21.3", - "@unraid/shared-callbacks": "3.1.0", + "@unraid/shared-callbacks": "3.2.0", "@unraid/ui": "link:../unraid-ui", "@vue/apollo-composable": "4.2.2", "@vueuse/components": "13.8.0", diff --git a/web/src/components/Registration.standalone.vue b/web/src/components/Registration.standalone.vue index d68331d4d2..0554e6ac09 100644 --- a/web/src/components/Registration.standalone.vue +++ b/web/src/components/Registration.standalone.vue @@ -46,6 +46,7 @@ const { hasDistinctTpmGuid, dateTimeFormat, deviceCount, + flashGuid, flashProduct, flashVendor, guid, @@ -133,18 +134,24 @@ const showTpmTransferButton = computed((): boolean => ); const disableTpmTransferButton = computed((): boolean => showTrialExpiration.value); -// Organize items into three sections -const bootDeviceItems = computed((): RegistrationItemProps[] => { - return [ - ...(guid.value +const licensingGuidItems = computed((): RegistrationItemProps[] => { + const items: RegistrationItemProps[] = [ + ...(flashGuid.value ? [ { - label: t('registration.deviceGuid'), - text: guid.value, + label: t('registration.flashGuid'), + text: flashGuid.value, }, ] - : []), - ...(showTpmTransferButton.value && tpmGuid.value + : guid.value + ? [ + { + label: t('registration.deviceGuid'), + text: guid.value, + }, + ] + : []), + ...(tpmGuid.value && (flashGuid.value || tpmGuid.value !== guid.value) ? [ { label: t('registration.tpmGuid'), @@ -152,6 +159,15 @@ const bootDeviceItems = computed((): RegistrationItemProps[] => { }, ] : []), + ]; + + return items; +}); + +// Organize items into three sections +const bootDeviceItems = computed((): RegistrationItemProps[] => { + return [ + ...licensingGuidItems.value, ...(bootDeviceType.value ? [ { diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 0214592618..255f45f758 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -692,6 +692,7 @@ "registration.bootDeviceType.internalBootMulti": "Internal Boot (Multi-device)", "registration.bootDeviceType.tpm": "TPM", "registration.deviceGuid": "Device GUID", + "registration.flashGuid": "USB Flash GUID", "registration.flashProduct": "Flash Product", "registration.flashVendor": "Flash Vendor", "registration.general.goToToolsRegistrationToLearn": "Go to Tools > Registration to Learn More", diff --git a/web/src/store/server.ts b/web/src/store/server.ts index 5bde8e4b53..ad7e88c32c 100644 --- a/web/src/store/server.ts +++ b/web/src/store/server.ts @@ -253,6 +253,7 @@ export const useServerStore = defineStore('server', () => { description: description.value, deviceCount: deviceCount.value, expireTime: expireTime.value, + flashGuid: flashGuid.value || undefined, flashProduct: flashProduct.value, flashVendor: flashVendor.value, guid: guid.value, @@ -268,6 +269,7 @@ export const useServerStore = defineStore('server', () => { regTy: regTy.value, regUpdatesExpired: regUpdatesExpired.value, state: state.value, + tpmGuid: tpmGuid.value || undefined, wanFQDN: wanFQDN.value, ...overrides, };