From 2a8828d8a10fb126a7cca6ce730b94e184285d5e Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 5 Feb 2026 07:41:09 +0100 Subject: [PATCH 1/3] feat(frontend): access key type display and kind emulator --- .../lib/api/mission-control.deprecated.api.ts | 18 ++++++++++--- .../components/access-keys/AccessKeys.svelte | 19 ++++++++----- .../modals/setup/AccessKeyCreateModal.svelte | 1 - src/frontend/src/lib/i18n/en.json | 4 ++- src/frontend/src/lib/i18n/zh-cn.json | 4 ++- .../access-keys/key.admin.services.ts | 4 +-- .../mission-control.key.services.ts | 4 +-- .../access-keys/orbiter.key.add.services.ts | 10 +++---- .../satellites.key.add.services.ts | 10 +++---- src/frontend/src/lib/services/cli.services.ts | 14 ++++++---- .../src/lib/services/emulator.services.ts | 4 ++- src/frontend/src/lib/types/access-keys.ts | 7 +++-- src/frontend/src/lib/types/i18n.d.ts | 2 ++ .../src/lib/utils/controllers.utils.ts | 27 +++++++++++++------ 14 files changed, 84 insertions(+), 44 deletions(-) diff --git a/src/frontend/src/lib/api/mission-control.deprecated.api.ts b/src/frontend/src/lib/api/mission-control.deprecated.api.ts index ad2afa82d8..165976ecdd 100644 --- a/src/frontend/src/lib/api/mission-control.deprecated.api.ts +++ b/src/frontend/src/lib/api/mission-control.deprecated.api.ts @@ -9,10 +9,19 @@ import type { MissionControlId } from '$lib/types/mission-control'; import { nonNullish, toNullable } from '@dfinity/utils'; import { Principal } from '@icp-sdk/core/principal'; -const toSetController = ({ - profile +/** + * @deprecated TODO: to be remove - backwards compatibility + */ +const toSetController004 = ({ + metadata }: Omit): MissionControlDid004.SetController => ({ - metadata: nonNullish(profile) && profile !== '' ? [['profile', profile]] : [], + metadata: + nonNullish(metadata) && + 'profile' in metadata && + nonNullish(metadata.profile) && + metadata.profile !== '' + ? [['profile', metadata.profile]] + : [], expires_at: toNullable(undefined) }); @@ -30,9 +39,10 @@ export const setMissionControlController004 = async ({ } & AddAccessKeyParams) => { try { const actor = await getMissionControlActor004({ missionControlId, identity }); + await actor.set_mission_control_controllers( [Principal.from(accessKeyId)], - toSetController(rest) + toSetController004(rest) ); } catch (err: unknown) { console.error('setMissionControlController004:', missionControlId.toText()); diff --git a/src/frontend/src/lib/components/access-keys/AccessKeys.svelte b/src/frontend/src/lib/components/access-keys/AccessKeys.svelte index 04bbf7e596..d818228a27 100644 --- a/src/frontend/src/lib/components/access-keys/AccessKeys.svelte +++ b/src/frontend/src/lib/components/access-keys/AccessKeys.svelte @@ -69,6 +69,7 @@ {$i18n.controllers.title} {$i18n.controllers.profile} + {$i18n.controllers.type} {$i18n.controllers.scope} @@ -76,6 +77,7 @@ {#each controllers as [controllerId, controller] (controllerId.toText())} {@const dev = isDev(controllerId)} {@const mic = isMissionControl(controllerId)} + {@const kind = controller?.kind} @@ -98,7 +100,7 @@ - + + + {nonNullish(kind) && 'emulator' in kind + ? `👾 ${$i18n.emulator.emulator}` + : nonNullish(kind) && 'automation' in kind + ? `🤖 ${$i18n.controllers.automation}` + : ''} + + {#if nonNullish(controller)} {#if 'Write' in controller.scope} @@ -141,12 +151,7 @@ width: 48px; } - .controller { - @include media.min-width(small) { - width: 60%; - } - } - + .type, .profile, .scope { display: none; diff --git a/src/frontend/src/lib/components/modals/setup/AccessKeyCreateModal.svelte b/src/frontend/src/lib/components/modals/setup/AccessKeyCreateModal.svelte index fdbf9cfa4f..d164b6e39c 100644 --- a/src/frontend/src/lib/components/modals/setup/AccessKeyCreateModal.svelte +++ b/src/frontend/src/lib/components/modals/setup/AccessKeyCreateModal.svelte @@ -66,7 +66,6 @@ const { result } = await add({ accessKeyId, - profile: undefined, scope }); diff --git a/src/frontend/src/lib/i18n/en.json b/src/frontend/src/lib/i18n/en.json index 24d293076f..fa7c12118f 100644 --- a/src/frontend/src/lib/i18n/en.json +++ b/src/frontend/src/lib/i18n/en.json @@ -798,7 +798,9 @@ }, "controllers": { "title": "Access Keys", - "profile": "Profile / Type", + "type": "Type", + "automation": "Automation", + "profile": "Profile", "scope": "Role", "delete": "Delete an access key", "info": "Information", diff --git a/src/frontend/src/lib/i18n/zh-cn.json b/src/frontend/src/lib/i18n/zh-cn.json index 74fbf46a81..ca0ee2e760 100644 --- a/src/frontend/src/lib/i18n/zh-cn.json +++ b/src/frontend/src/lib/i18n/zh-cn.json @@ -800,7 +800,9 @@ }, "controllers": { "title": "访问密钥", - "profile": "配置/类型", + "type": "类型", + "automation": "自动化", + "profile": "配置", "scope": "权限", "delete": "删除访问密钥", "info": "信息", diff --git a/src/frontend/src/lib/services/access-keys/key.admin.services.ts b/src/frontend/src/lib/services/access-keys/key.admin.services.ts index 00b9813ec0..4111fb48e9 100644 --- a/src/frontend/src/lib/services/access-keys/key.admin.services.ts +++ b/src/frontend/src/lib/services/access-keys/key.admin.services.ts @@ -37,7 +37,7 @@ export const setAdminAccessKey = async ({ setAccessKeysFn, attachFn, canisterId, - profile, + metadata, ...rest }: { setAccessKeysFn: SetAccessKeysFn; @@ -49,7 +49,7 @@ export const setAdminAccessKey = async ({ await setAccessKeysFn({ args: { controller: toSetController({ - profile, + metadata, scope: 'admin' }), controllers: [controllerId] diff --git a/src/frontend/src/lib/services/access-keys/mission-control.key.services.ts b/src/frontend/src/lib/services/access-keys/mission-control.key.services.ts index 0cc9f27723..2a82b4ca49 100644 --- a/src/frontend/src/lib/services/access-keys/mission-control.key.services.ts +++ b/src/frontend/src/lib/services/access-keys/mission-control.key.services.ts @@ -21,7 +21,7 @@ export const addMissionControlAccessKey = async ({ missionControlId, accessKeyId, scope, - profile, + metadata, ...rest }: { missionControlId: MissionControlId; @@ -33,7 +33,7 @@ export const addMissionControlAccessKey = async ({ missionControlId, accessKeyId, scope, - profile + metadata }); }; diff --git a/src/frontend/src/lib/services/access-keys/orbiter.key.add.services.ts b/src/frontend/src/lib/services/access-keys/orbiter.key.add.services.ts index 44b0b212e6..7d40dde832 100644 --- a/src/frontend/src/lib/services/access-keys/orbiter.key.add.services.ts +++ b/src/frontend/src/lib/services/access-keys/orbiter.key.add.services.ts @@ -19,7 +19,7 @@ export const addOrbiterAccessKey = async ({ orbiterIds, accessKeyId, scope, - profile + metadata }: { orbiterIds: OrbiterId[]; identity: Identity; @@ -29,7 +29,7 @@ export const addOrbiterAccessKey = async ({ identity, orbiterIds, accessKeyId, - profile + metadata }); return; } @@ -38,7 +38,7 @@ export const addOrbiterAccessKey = async ({ identity, orbiterIds, accessKeyId, - profile, + metadata, scope }); }; @@ -79,7 +79,7 @@ const setOrbitersNonAdminAccessKey = async ({ orbiterIds, accessKeyId, identity, - profile, + metadata, scope }: { orbiterIds: OrbiterId[]; @@ -90,7 +90,7 @@ const setOrbitersNonAdminAccessKey = async ({ await setOrbiterControllers({ args: { controller: toSetController({ - profile, + metadata, scope: scope as AddAccessKeyScope // Safe case, the param is explicit to avoid passing admin to the function }), controllers: [Principal.from(accessKeyId)] diff --git a/src/frontend/src/lib/services/access-keys/satellites.key.add.services.ts b/src/frontend/src/lib/services/access-keys/satellites.key.add.services.ts index d8a575d1f6..ffbcab361c 100644 --- a/src/frontend/src/lib/services/access-keys/satellites.key.add.services.ts +++ b/src/frontend/src/lib/services/access-keys/satellites.key.add.services.ts @@ -20,7 +20,7 @@ export const addSatellitesAccessKey = async ({ satelliteIds: satelliteIdsParam, accessKeyId, scope, - profile + metadata }: { satelliteIds: Principal[]; identity: Identity; @@ -32,7 +32,7 @@ export const addSatellitesAccessKey = async ({ identity, satelliteIds, accessKeyId, - profile + metadata }); return; } @@ -41,7 +41,7 @@ export const addSatellitesAccessKey = async ({ identity, satelliteIds, accessKeyId, - profile, + metadata, scope }); }; @@ -118,7 +118,7 @@ const setSatellitesNonAdminAccessKey = async ({ satelliteIds, accessKeyId, identity, - profile, + metadata, scope }: { satelliteIds: SatelliteId[]; @@ -129,7 +129,7 @@ const setSatellitesNonAdminAccessKey = async ({ await setSatelliteControllers({ args: { controller: toSetController({ - profile, + metadata, scope: scope as AddAccessKeyScope // Safe case, the param is explicit to avoid passing admin to the function }), controllers: [Principal.from(accessKeyId)] diff --git a/src/frontend/src/lib/services/cli.services.ts b/src/frontend/src/lib/services/cli.services.ts index a6216ca6ed..222fd1bdaf 100644 --- a/src/frontend/src/lib/services/cli.services.ts +++ b/src/frontend/src/lib/services/cli.services.ts @@ -6,6 +6,7 @@ import { setMissionControlControllerForVersion, setSatellitesControllerForVersion as setSatellitesControllerForVersionWithMctrl } from '$lib/services/mission-control/mission-control.services'; +import type { AddAccessKeyParams } from '$lib/types/access-keys'; import type { MissionControlId } from '$lib/types/mission-control'; import type { Option } from '$lib/types/utils'; import { bigintStringify } from '$lib/utils/number.utils'; @@ -50,6 +51,9 @@ export const setCliControllers = async ({ } }; +const toMetadata = (profile: Option): Pick => + notEmptyString(profile) ? { metadata: { profile } } : {}; + const setCliControllersWithoutMissionControl = async ({ controllerId, profile, @@ -63,7 +67,7 @@ const setCliControllersWithoutMissionControl = async ({ addSatellitesAdminAccessKey({ accessKeyId: controllerId, satelliteIds: selectedSatellites.map((s) => s[0]), - profile, + ...toMetadata(profile), identity }) ] @@ -73,7 +77,7 @@ const setCliControllersWithoutMissionControl = async ({ addOrbitersAdminAccessKey({ accessKeyId: controllerId, orbiterIds: selectedOrbiters.map((s) => s[0]), - profile, + ...toMetadata(profile), identity }) ] @@ -96,7 +100,7 @@ const setCliControllersWithMissionControl = async ({ setMissionControlControllerForVersion({ missionControlId, accessKeyId: controllerId, - profile, + ...toMetadata(profile), scope: 'admin', identity }) @@ -108,7 +112,7 @@ const setCliControllersWithMissionControl = async ({ missionControlId, accessKeyId: controllerId, satelliteIds: selectedSatellites.map((s) => s[0]), - profile, + ...toMetadata(profile), scope: 'admin', identity }) @@ -120,7 +124,7 @@ const setCliControllersWithMissionControl = async ({ missionControlId, accessKeyId: controllerId, orbiterIds: selectedOrbiters.map((s) => s[0]), - profile, + ...toMetadata(profile), scope: 'admin', identity }) diff --git a/src/frontend/src/lib/services/emulator.services.ts b/src/frontend/src/lib/services/emulator.services.ts index 40cc4b4c54..d2addaa5d8 100644 --- a/src/frontend/src/lib/services/emulator.services.ts +++ b/src/frontend/src/lib/services/emulator.services.ts @@ -50,7 +50,9 @@ const unsafeSetEmulatorController = async ({ await addController({ accessKeyId: mainIdentity, - profile: `👾 ${get(i18n).emulator.emulator}` + metadata: { + kind: 'emulator' + } }); }; diff --git a/src/frontend/src/lib/types/access-keys.ts b/src/frontend/src/lib/types/access-keys.ts index 0f336cf40a..b0a06ce69f 100644 --- a/src/frontend/src/lib/types/access-keys.ts +++ b/src/frontend/src/lib/types/access-keys.ts @@ -1,13 +1,16 @@ import type { MissionControlId } from '$lib/types/mission-control'; -import type { Option } from '$lib/types/utils'; import type { Identity } from '@icp-sdk/core/agent'; import type { Principal } from '@icp-sdk/core/principal'; export type AddAccessKeyScope = 'write' | 'admin' | 'submit'; +export type AddAccessKeyKind = 'automation' | 'emulator'; + +export type AddAccessKeyMetadataParams = { profile: string } | { kind: AddAccessKeyKind }; + export interface AddAccessKeyParams { accessKeyId: string | Principal; - profile: Option; + metadata?: AddAccessKeyMetadataParams; scope: AddAccessKeyScope; } diff --git a/src/frontend/src/lib/types/i18n.d.ts b/src/frontend/src/lib/types/i18n.d.ts index c172362982..01b715b705 100644 --- a/src/frontend/src/lib/types/i18n.d.ts +++ b/src/frontend/src/lib/types/i18n.d.ts @@ -820,6 +820,8 @@ interface I18nAdmin { interface I18nControllers { title: string; + type: string; + automation: string; profile: string; scope: string; delete: string; diff --git a/src/frontend/src/lib/utils/controllers.utils.ts b/src/frontend/src/lib/utils/controllers.utils.ts index a3060c4a91..a18a7004fa 100644 --- a/src/frontend/src/lib/utils/controllers.utils.ts +++ b/src/frontend/src/lib/utils/controllers.utils.ts @@ -3,12 +3,23 @@ import type { AddAccessKeyParams } from '$lib/types/access-keys'; import { nonNullish, toNullable } from '@dfinity/utils'; export const toSetController = ({ - profile, + metadata, scope -}: Omit): MissionControlDid.SetController => ({ - metadata: nonNullish(profile) && profile !== '' ? [['profile', profile]] : [], - expires_at: toNullable(undefined), - kind: toNullable(), - scope: - scope === 'admin' ? { Admin: null } : scope === 'submit' ? { Submit: null } : { Write: null } -}); +}: Omit): MissionControlDid.SetController => { + const profile = nonNullish(metadata) && 'profile' in metadata ? metadata.profile : undefined; + const kind = nonNullish(metadata) && 'kind' in metadata ? metadata.kind : undefined; + + return { + metadata: nonNullish(profile) && profile !== '' ? [['profile', profile]] : [], + expires_at: toNullable(undefined), + kind: toNullable( + kind === 'automation' + ? { Automation: null } + : kind === 'emulator' + ? { Emulator: null } + : undefined + ), + scope: + scope === 'admin' ? { Admin: null } : scope === 'submit' ? { Submit: null } : { Write: null } + }; +}; From 49072b26b66d6d6341d10b52ea020663f8f73f6c Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 5 Feb 2026 08:00:30 +0100 Subject: [PATCH 2/3] chore: lint and test --- .../lib/services/factory/_factory.attach.services.ts | 4 +--- .../mission-control/mission-control.services.ts | 10 +++++----- .../tests/lib/services/emulator.services.test.ts | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/frontend/src/lib/services/factory/_factory.attach.services.ts b/src/frontend/src/lib/services/factory/_factory.attach.services.ts index cbea1212ea..2a9e7388ed 100644 --- a/src/frontend/src/lib/services/factory/_factory.attach.services.ts +++ b/src/frontend/src/lib/services/factory/_factory.attach.services.ts @@ -17,9 +17,7 @@ import type { Identity } from '@icp-sdk/core/agent'; import type { Principal } from '@icp-sdk/core/principal'; import { get } from 'svelte/store'; -const CONTROLLER_PARAMS: Omit = { - profile: undefined -}; +const CONTROLLER_PARAMS: Omit = {}; export class AttachToMissionControlError extends Error { // eslint-disable-next-line local-rules/prefer-object-params diff --git a/src/frontend/src/lib/services/mission-control/mission-control.services.ts b/src/frontend/src/lib/services/mission-control/mission-control.services.ts index 8bc2834133..fed6132a2c 100644 --- a/src/frontend/src/lib/services/mission-control/mission-control.services.ts +++ b/src/frontend/src/lib/services/mission-control/mission-control.services.ts @@ -43,7 +43,7 @@ import { get } from 'svelte/store'; export const setMissionControlControllerForVersion = async ({ missionControlId, accessKeyId, - profile, + metadata, identity }: { missionControlId: MissionControlId; @@ -70,7 +70,7 @@ export const setMissionControlControllerForVersion = async ({ await missionControlController({ missionControlId, accessKeyId, - profile, + metadata, scope: 'admin', identity }); @@ -80,7 +80,7 @@ export const setSatellitesControllerForVersion = async ({ missionControlId, satelliteIds, accessKeyId, - profile, + metadata, identity }: { missionControlId: MissionControlId; @@ -99,7 +99,7 @@ export const setSatellitesControllerForVersion = async ({ satelliteIds: setSatelliteIds, missionControlId, accessKeyId, - profile, + metadata, scope: 'admin', identity }) @@ -111,7 +111,7 @@ export const setSatellitesControllerForVersion = async ({ satelliteIds: addSatellitesIds, missionControlId, accessKeyId, - profile, + metadata, scope: 'admin', identity }) diff --git a/src/frontend/tests/lib/services/emulator.services.test.ts b/src/frontend/tests/lib/services/emulator.services.test.ts index 070ff1b929..318e12a901 100644 --- a/src/frontend/tests/lib/services/emulator.services.test.ts +++ b/src/frontend/tests/lib/services/emulator.services.test.ts @@ -68,8 +68,8 @@ describe('emulator.services', () => { args: { controller: { expires_at: [], - kind: [], - metadata: [['profile', '👾 Emulator']], + kind: [{ Emulator: null }], + metadata: [], scope: { Admin: null } From aef78fd593fadf8620e4433bd695ea6d0ecdf806 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 5 Feb 2026 08:05:38 +0100 Subject: [PATCH 3/3] chore: lint --- .../src/lib/components/access-keys/AccessKeys.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/lib/components/access-keys/AccessKeys.svelte b/src/frontend/src/lib/components/access-keys/AccessKeys.svelte index d818228a27..a7b91895bb 100644 --- a/src/frontend/src/lib/components/access-keys/AccessKeys.svelte +++ b/src/frontend/src/lib/components/access-keys/AccessKeys.svelte @@ -115,10 +115,10 @@ {nonNullish(kind) && 'emulator' in kind - ? `👾 ${$i18n.emulator.emulator}` - : nonNullish(kind) && 'automation' in kind - ? `🤖 ${$i18n.controllers.automation}` - : ''} + ? `👾 ${$i18n.emulator.emulator}` + : nonNullish(kind) && 'automation' in kind + ? `🤖 ${$i18n.controllers.automation}` + : ''}