mirror of
https://github.com/unraid/api.git
synced 2026-01-04 07:29:48 -06:00
feat(onboarding): implement upgrade step completion tracking
- Introduced `OnboardingMutations` to manage the completion of upgrade onboarding steps. - Added `completeUpgradeStep` mutation to mark specific onboarding steps as completed, returning relevant upgrade information. - Enhanced `UpgradeInfo` type to include completed steps for better tracking of user progress during OS upgrades. - Updated `OsVersionTracker` to persist completed onboarding steps alongside OS version tracking. - Integrated new GraphQL queries and mutations in the frontend to support the onboarding process. This update improves the user experience by providing a structured way to track and manage onboarding steps during OS upgrades, ensuring users receive appropriate guidance throughout the process.
This commit is contained in:
@@ -5,6 +5,5 @@
|
||||
"ssoSubIds": [],
|
||||
"plugins": [
|
||||
"unraid-api-plugin-connect"
|
||||
],
|
||||
"lastSeenOsVersion": "6.11.2"
|
||||
]
|
||||
}
|
||||
@@ -781,6 +781,70 @@ type Settings implements Node {
|
||||
api: ApiConfig!
|
||||
}
|
||||
|
||||
type CoreVersions {
|
||||
"""Unraid version"""
|
||||
unraid: String
|
||||
|
||||
"""Unraid API version"""
|
||||
api: String
|
||||
|
||||
"""Kernel version"""
|
||||
kernel: String
|
||||
}
|
||||
|
||||
type PackageVersions {
|
||||
"""OpenSSL version"""
|
||||
openssl: String
|
||||
|
||||
"""Node.js version"""
|
||||
node: String
|
||||
|
||||
"""npm version"""
|
||||
npm: String
|
||||
|
||||
"""pm2 version"""
|
||||
pm2: String
|
||||
|
||||
"""Git version"""
|
||||
git: String
|
||||
|
||||
"""nginx version"""
|
||||
nginx: String
|
||||
|
||||
"""PHP version"""
|
||||
php: String
|
||||
|
||||
"""Docker version"""
|
||||
docker: String
|
||||
}
|
||||
|
||||
type UpgradeInfo {
|
||||
"""Whether the OS version has changed since last boot"""
|
||||
isUpgrade: Boolean!
|
||||
|
||||
"""Previous OS version before upgrade"""
|
||||
previousVersion: String
|
||||
|
||||
"""Current OS version"""
|
||||
currentVersion: String
|
||||
|
||||
"""Onboarding step identifiers completed for the current OS version"""
|
||||
completedSteps: [String!]!
|
||||
}
|
||||
|
||||
type InfoVersions implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Core system versions"""
|
||||
core: CoreVersions!
|
||||
|
||||
"""Software package versions"""
|
||||
packages: PackageVersions
|
||||
|
||||
"""OS upgrade information"""
|
||||
upgrade: UpgradeInfo!
|
||||
}
|
||||
|
||||
type RCloneDrive {
|
||||
"""Provider name"""
|
||||
name: String!
|
||||
@@ -1029,6 +1093,20 @@ input DeleteRCloneRemoteInput {
|
||||
name: String!
|
||||
}
|
||||
|
||||
"""Onboarding related mutations"""
|
||||
type OnboardingMutations {
|
||||
"""
|
||||
Mark an upgrade onboarding step as completed for the current OS version
|
||||
"""
|
||||
completeUpgradeStep(input: CompleteUpgradeStepInput!): UpgradeInfo!
|
||||
}
|
||||
|
||||
"""Input for marking an upgrade onboarding step as completed"""
|
||||
input CompleteUpgradeStepInput {
|
||||
"""Identifier of the onboarding step to mark completed"""
|
||||
stepId: String!
|
||||
}
|
||||
|
||||
type Config implements Node {
|
||||
id: PrefixedID!
|
||||
valid: Boolean
|
||||
@@ -1927,67 +2005,6 @@ type InfoBaseboard implements Node {
|
||||
memSlots: Float
|
||||
}
|
||||
|
||||
type CoreVersions {
|
||||
"""Unraid version"""
|
||||
unraid: String
|
||||
|
||||
"""Unraid API version"""
|
||||
api: String
|
||||
|
||||
"""Kernel version"""
|
||||
kernel: String
|
||||
}
|
||||
|
||||
type PackageVersions {
|
||||
"""OpenSSL version"""
|
||||
openssl: String
|
||||
|
||||
"""Node.js version"""
|
||||
node: String
|
||||
|
||||
"""npm version"""
|
||||
npm: String
|
||||
|
||||
"""pm2 version"""
|
||||
pm2: String
|
||||
|
||||
"""Git version"""
|
||||
git: String
|
||||
|
||||
"""nginx version"""
|
||||
nginx: String
|
||||
|
||||
"""PHP version"""
|
||||
php: String
|
||||
|
||||
"""Docker version"""
|
||||
docker: String
|
||||
}
|
||||
|
||||
type UpgradeInfo {
|
||||
"""Whether the OS version has changed since last boot"""
|
||||
isUpgrade: Boolean!
|
||||
|
||||
"""Previous OS version before upgrade"""
|
||||
previousVersion: String
|
||||
|
||||
"""Current OS version"""
|
||||
currentVersion: String
|
||||
}
|
||||
|
||||
type InfoVersions implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
"""Core system versions"""
|
||||
core: CoreVersions!
|
||||
|
||||
"""Software package versions"""
|
||||
packages: PackageVersions
|
||||
|
||||
"""OS upgrade information"""
|
||||
upgrade: UpgradeInfo!
|
||||
}
|
||||
|
||||
type Info implements Node {
|
||||
id: PrefixedID!
|
||||
|
||||
@@ -2709,6 +2726,7 @@ type Mutation {
|
||||
apiKey: ApiKeyMutations!
|
||||
customization: CustomizationMutations!
|
||||
rclone: RCloneMutations!
|
||||
onboarding: OnboardingMutations!
|
||||
createDockerFolder(name: String!, parentId: String, childrenIds: [String!]): ResolvedOrganizerV1!
|
||||
setDockerFolderChildren(folderId: String, childrenIds: [String!]!): ResolvedOrganizerV1!
|
||||
deleteDockerEntries(entryIds: [String!]!): ResolvedOrganizerV1!
|
||||
|
||||
@@ -434,6 +434,12 @@ export type CloudResponse = {
|
||||
status: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
/** Input for marking an upgrade onboarding step as completed */
|
||||
export type CompleteUpgradeStepInput = {
|
||||
/** Identifier of the onboarding step to mark completed */
|
||||
stepId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Config = Node & {
|
||||
__typename?: 'Config';
|
||||
error?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1464,6 +1470,7 @@ export type Mutation = {
|
||||
moveDockerItemsToPosition: ResolvedOrganizerV1;
|
||||
/** Creates a notification if an equivalent unread notification does not already exist. */
|
||||
notifyIfUnique?: Maybe<Notification>;
|
||||
onboarding: OnboardingMutations;
|
||||
parityCheck: ParityCheckMutations;
|
||||
rclone: RCloneMutations;
|
||||
/** Reads each notification to recompute & update the overview. */
|
||||
@@ -1774,6 +1781,19 @@ export type OidcSessionValidation = {
|
||||
valid: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
/** Onboarding related mutations */
|
||||
export type OnboardingMutations = {
|
||||
__typename?: 'OnboardingMutations';
|
||||
/** Mark an upgrade onboarding step as completed for the current OS version */
|
||||
completeUpgradeStep: UpgradeInfo;
|
||||
};
|
||||
|
||||
|
||||
/** Onboarding related mutations */
|
||||
export type OnboardingMutationsCompleteUpgradeStepArgs = {
|
||||
input: CompleteUpgradeStepInput;
|
||||
};
|
||||
|
||||
export type Owner = {
|
||||
__typename?: 'Owner';
|
||||
avatar: Scalars['String']['output'];
|
||||
@@ -2614,6 +2634,8 @@ export type UpdateSystemTimeInput = {
|
||||
|
||||
export type UpgradeInfo = {
|
||||
__typename?: 'UpgradeInfo';
|
||||
/** Onboarding step identifiers completed for the current OS version */
|
||||
completedSteps: Array<Scalars['String']['output']>;
|
||||
/** Current OS version */
|
||||
currentVersion?: Maybe<Scalars['String']['output']>;
|
||||
/** Whether the OS version has changed since last boot */
|
||||
|
||||
@@ -8,7 +8,7 @@ import { csvStringToArray } from '@unraid/shared/util/data.js';
|
||||
|
||||
import { isConnectPluginInstalled } from '@app/connect-plugin-cleanup.js';
|
||||
import { API_VERSION, PATHS_CONFIG_MODULES } from '@app/environment.js';
|
||||
import { OsVersionTrackerModule } from '@app/unraid-api/config/os-version-tracker.module.js';
|
||||
import { OnboardingTrackerModule } from '@app/unraid-api/config/onboarding-tracker.module.js';
|
||||
|
||||
export { type ApiConfig };
|
||||
|
||||
@@ -120,8 +120,8 @@ export class ApiConfigPersistence
|
||||
|
||||
// apiConfig should be registered in root config in app.module.ts, not here.
|
||||
@Module({
|
||||
imports: [OsVersionTrackerModule],
|
||||
imports: [OnboardingTrackerModule],
|
||||
providers: [ApiConfigPersistence],
|
||||
exports: [ApiConfigPersistence, OsVersionTrackerModule],
|
||||
exports: [ApiConfigPersistence, OnboardingTrackerModule],
|
||||
})
|
||||
export class ApiConfigModule {}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { API_VERSION, PATHS_CONFIG_MODULES } from '@app/environment.js';
|
||||
import { ApiConfigPersistence, loadApiConfig } from '@app/unraid-api/config/api-config.module.js';
|
||||
import { OsVersionTracker } from '@app/unraid-api/config/os-version-tracker.module.js';
|
||||
import { OnboardingTracker } from '@app/unraid-api/config/onboarding-tracker.module.js';
|
||||
|
||||
vi.mock('@app/core/utils/files/file-exists.js', () => ({
|
||||
fileExists: vi.fn(),
|
||||
@@ -104,16 +104,20 @@ describe('ApiConfigPersistence', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('OsVersionTracker', () => {
|
||||
const trackerPath = path.join(PATHS_CONFIG_MODULES, 'os-version-tracker.json');
|
||||
describe('OnboardingTracker', () => {
|
||||
const trackerPath = path.join(PATHS_CONFIG_MODULES, 'onboarding-tracker.json');
|
||||
let configService: ConfigService;
|
||||
let setMock: ReturnType<typeof vi.fn>;
|
||||
let configStore: Record<string, unknown>;
|
||||
|
||||
beforeEach(() => {
|
||||
setMock = vi.fn();
|
||||
configStore = {};
|
||||
setMock = vi.fn((key: string, value: unknown) => {
|
||||
configStore[key] = value;
|
||||
});
|
||||
configService = {
|
||||
set: setMock,
|
||||
get: vi.fn(),
|
||||
get: vi.fn((key: string) => configStore[key]),
|
||||
getOrThrow: vi.fn(),
|
||||
} as any;
|
||||
|
||||
@@ -121,7 +125,7 @@ describe('OsVersionTracker', () => {
|
||||
mockAtomicWriteFile.mockReset();
|
||||
});
|
||||
|
||||
it('persists current version when tracker is missing', async () => {
|
||||
it('defers persisting last seen version until shutdown', async () => {
|
||||
mockReadFile.mockImplementation(async (filePath) => {
|
||||
if (filePath === '/etc/unraid-version') {
|
||||
return 'version="7.2.0-beta.3.4"\n';
|
||||
@@ -129,12 +133,17 @@ describe('OsVersionTracker', () => {
|
||||
throw Object.assign(new Error('Not found'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const tracker = new OsVersionTracker(configService);
|
||||
const tracker = new OnboardingTracker(configService);
|
||||
await tracker.onApplicationBootstrap();
|
||||
|
||||
expect(setMock).toHaveBeenCalledWith('api.currentOsVersion', '7.2.0-beta.3.4');
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.currentVersion', '7.2.0-beta.3.4');
|
||||
expect(setMock).toHaveBeenCalledWith('store.emhttp.var.version', '7.2.0-beta.3.4');
|
||||
expect(setMock).toHaveBeenCalledWith('api.lastSeenOsVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.lastTrackedVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.completedSteps', {});
|
||||
expect(configStore['api.lastSeenOsVersion']).toBeUndefined();
|
||||
expect(mockAtomicWriteFile).not.toHaveBeenCalled();
|
||||
|
||||
await tracker.onApplicationShutdown();
|
||||
|
||||
expect(mockAtomicWriteFile).toHaveBeenCalledWith(
|
||||
trackerPath,
|
||||
@@ -152,30 +161,130 @@ describe('OsVersionTracker', () => {
|
||||
return JSON.stringify({
|
||||
lastTrackedVersion: '6.12.0',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
completedSteps: {
|
||||
timezone: {
|
||||
version: '6.12.0',
|
||||
completedAt: '2024-01-02T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
throw Object.assign(new Error('Not found'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const tracker = new OsVersionTracker(configService);
|
||||
const tracker = new OnboardingTracker(configService);
|
||||
await tracker.onApplicationBootstrap();
|
||||
|
||||
expect(setMock).toHaveBeenCalledWith('api.currentOsVersion', '6.12.0');
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.currentVersion', '6.12.0');
|
||||
expect(setMock).toHaveBeenCalledWith('store.emhttp.var.version', '6.12.0');
|
||||
expect(setMock).toHaveBeenCalledWith('api.lastSeenOsVersion', '6.12.0');
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.lastTrackedVersion', '6.12.0');
|
||||
expect(setMock).toHaveBeenCalledWith(
|
||||
'onboardingTracker.completedSteps',
|
||||
expect.objectContaining({
|
||||
timezone: expect.objectContaining({ version: '6.12.0' }),
|
||||
})
|
||||
);
|
||||
expect(configStore['api.lastSeenOsVersion']).toBe('6.12.0');
|
||||
expect(mockAtomicWriteFile).not.toHaveBeenCalled();
|
||||
|
||||
await tracker.onApplicationShutdown();
|
||||
|
||||
expect(mockAtomicWriteFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('keeps previous version available to signal upgrade until shutdown', async () => {
|
||||
mockReadFile.mockImplementation(async (filePath) => {
|
||||
if (filePath === '/etc/unraid-version') {
|
||||
return 'version="7.1.0"\n';
|
||||
}
|
||||
if (filePath === trackerPath) {
|
||||
return JSON.stringify({
|
||||
lastTrackedVersion: '7.0.0',
|
||||
updatedAt: '2025-01-01T00:00:00.000Z',
|
||||
completedSteps: {},
|
||||
});
|
||||
}
|
||||
throw Object.assign(new Error('Not found'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const tracker = new OnboardingTracker(configService);
|
||||
await tracker.onApplicationBootstrap();
|
||||
|
||||
const snapshot = tracker.getUpgradeSnapshot();
|
||||
expect(snapshot.currentVersion).toBe('7.1.0');
|
||||
expect(snapshot.lastTrackedVersion).toBe('7.0.0');
|
||||
expect(snapshot.completedSteps).toEqual([]);
|
||||
|
||||
expect(configStore['onboardingTracker.lastTrackedVersion']).toBe('7.0.0');
|
||||
expect(configStore['store.emhttp.var.version']).toBe('7.1.0');
|
||||
expect(configStore['onboardingTracker.completedSteps']).toEqual({});
|
||||
expect(configStore['api.lastSeenOsVersion']).toBe('7.0.0');
|
||||
|
||||
expect(mockAtomicWriteFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles missing version file gracefully', async () => {
|
||||
mockReadFile.mockRejectedValue(new Error('permission denied'));
|
||||
|
||||
const tracker = new OsVersionTracker(configService);
|
||||
const tracker = new OnboardingTracker(configService);
|
||||
await tracker.onApplicationBootstrap();
|
||||
|
||||
expect(setMock).toHaveBeenCalledWith('api.currentOsVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.currentVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('store.emhttp.var.version', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('api.lastSeenOsVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.lastTrackedVersion', undefined);
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.completedSteps', {});
|
||||
expect(mockAtomicWriteFile).not.toHaveBeenCalled();
|
||||
expect(configStore['api.lastSeenOsVersion']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('marks onboarding steps complete for the current version without clearing upgrade flag', async () => {
|
||||
mockReadFile.mockImplementation(async (filePath) => {
|
||||
if (filePath === '/etc/unraid-version') {
|
||||
return 'version="7.2.0"\n';
|
||||
}
|
||||
if (filePath === trackerPath) {
|
||||
return JSON.stringify({
|
||||
lastTrackedVersion: '6.12.0',
|
||||
updatedAt: '2025-01-01T00:00:00.000Z',
|
||||
completedSteps: {},
|
||||
});
|
||||
}
|
||||
throw Object.assign(new Error('Not found'), { code: 'ENOENT' });
|
||||
});
|
||||
|
||||
const tracker = new OnboardingTracker(configService);
|
||||
await tracker.onApplicationBootstrap();
|
||||
|
||||
expect(configStore['store.emhttp.var.version']).toBe('7.2.0');
|
||||
expect(configStore['onboardingTracker.lastTrackedVersion']).toBe('6.12.0');
|
||||
expect(configStore['api.lastSeenOsVersion']).toBe('6.12.0');
|
||||
|
||||
setMock.mockClear();
|
||||
mockAtomicWriteFile.mockReset();
|
||||
|
||||
const snapshot = await tracker.markStepCompleted('timezone');
|
||||
|
||||
expect(snapshot.currentVersion).toBe('7.2.0');
|
||||
expect(snapshot.completedSteps).toContain('timezone');
|
||||
expect(snapshot.lastTrackedVersion).toBe('6.12.0');
|
||||
|
||||
expect(mockAtomicWriteFile).toHaveBeenCalledWith(
|
||||
trackerPath,
|
||||
expect.stringContaining('"timezone"'),
|
||||
{ mode: 0o644 }
|
||||
);
|
||||
|
||||
expect(setMock).toHaveBeenCalledWith(
|
||||
'onboardingTracker.completedSteps',
|
||||
expect.objectContaining({
|
||||
timezone: expect.objectContaining({ version: '7.2.0' }),
|
||||
})
|
||||
);
|
||||
|
||||
expect(setMock).toHaveBeenCalledWith('onboardingTracker.lastTrackedVersion', '6.12.0');
|
||||
|
||||
const postSnapshot = tracker.getUpgradeSnapshot();
|
||||
expect(postSnapshot.lastTrackedVersion).toBe('6.12.0');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
224
api/src/unraid-api/config/onboarding-tracker.module.ts
Normal file
224
api/src/unraid-api/config/onboarding-tracker.module.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
Injectable,
|
||||
Logger,
|
||||
Module,
|
||||
OnApplicationBootstrap,
|
||||
OnApplicationShutdown,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { readFile } from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { writeFile } from 'atomically';
|
||||
|
||||
import { PATHS_CONFIG_MODULES } from '@app/environment.js';
|
||||
|
||||
const TRACKER_FILE_NAME = 'onboarding-tracker.json';
|
||||
const LEGACY_TRACKER_FILE_NAME = 'os-version-tracker.json';
|
||||
const CONFIG_PREFIX = 'onboardingTracker';
|
||||
const OS_VERSION_FILE_PATH = '/etc/unraid-version';
|
||||
|
||||
type CompletedStepState = {
|
||||
version: string;
|
||||
completedAt: string;
|
||||
};
|
||||
|
||||
type TrackerState = {
|
||||
lastTrackedVersion?: string;
|
||||
updatedAt?: string;
|
||||
completedSteps?: Record<string, CompletedStepState>;
|
||||
};
|
||||
|
||||
export type UpgradeProgressSnapshot = {
|
||||
currentVersion?: string;
|
||||
lastTrackedVersion?: string;
|
||||
completedSteps: string[];
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class OnboardingTracker implements OnApplicationBootstrap, OnApplicationShutdown {
|
||||
private readonly logger = new Logger(OnboardingTracker.name);
|
||||
private readonly trackerPath = path.join(PATHS_CONFIG_MODULES, TRACKER_FILE_NAME);
|
||||
private readonly legacyTrackerPath = path.join(PATHS_CONFIG_MODULES, LEGACY_TRACKER_FILE_NAME);
|
||||
private state: TrackerState = {};
|
||||
private sessionLastTrackedVersion?: string;
|
||||
private currentVersion?: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
this.currentVersion = await this.readCurrentVersion();
|
||||
if (!this.currentVersion) {
|
||||
this.state = {};
|
||||
this.sessionLastTrackedVersion = undefined;
|
||||
this.syncConfig(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const previousState = await this.readTrackerState();
|
||||
this.state = previousState ?? {};
|
||||
this.sessionLastTrackedVersion = previousState?.lastTrackedVersion;
|
||||
|
||||
this.syncConfig(this.currentVersion);
|
||||
}
|
||||
|
||||
async onApplicationShutdown() {
|
||||
if (!this.currentVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.ensureStateLoaded();
|
||||
if (this.state.lastTrackedVersion === this.currentVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedState: TrackerState = {
|
||||
...this.state,
|
||||
lastTrackedVersion: this.currentVersion,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await this.writeTrackerState(updatedState);
|
||||
this.sessionLastTrackedVersion = this.currentVersion;
|
||||
}
|
||||
|
||||
getUpgradeSnapshot(): UpgradeProgressSnapshot {
|
||||
const currentVersion =
|
||||
this.configService.get<string>(`${CONFIG_PREFIX}.currentVersion`) ??
|
||||
this.configService.get<string>('store.emhttp.var.version') ??
|
||||
undefined;
|
||||
|
||||
const lastTrackedVersion =
|
||||
this.configService.get<string>(`${CONFIG_PREFIX}.lastTrackedVersion`) ?? undefined;
|
||||
|
||||
const completedSteps =
|
||||
currentVersion && this.state.completedSteps
|
||||
? this.completedStepsForVersion(currentVersion)
|
||||
: [];
|
||||
|
||||
return {
|
||||
currentVersion,
|
||||
lastTrackedVersion,
|
||||
completedSteps,
|
||||
};
|
||||
}
|
||||
|
||||
async markStepCompleted(stepId: string): Promise<UpgradeProgressSnapshot> {
|
||||
const currentVersion =
|
||||
this.configService.get<string>(`${CONFIG_PREFIX}.currentVersion`) ??
|
||||
this.configService.get<string>('store.emhttp.var.version') ??
|
||||
undefined;
|
||||
|
||||
if (!currentVersion) {
|
||||
this.logger.warn(
|
||||
`Unable to mark onboarding step '${stepId}' as completed; current OS version unknown`
|
||||
);
|
||||
return this.getUpgradeSnapshot();
|
||||
}
|
||||
|
||||
await this.ensureStateLoaded();
|
||||
const completedSteps = this.state.completedSteps ?? {};
|
||||
const existing = completedSteps[stepId];
|
||||
|
||||
if (existing?.version === currentVersion) {
|
||||
return this.getUpgradeSnapshot();
|
||||
}
|
||||
|
||||
completedSteps[stepId] = {
|
||||
version: currentVersion,
|
||||
completedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
const updatedState: TrackerState = {
|
||||
...this.state,
|
||||
completedSteps,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await this.writeTrackerState(updatedState);
|
||||
this.syncConfig(currentVersion);
|
||||
|
||||
return this.getUpgradeSnapshot();
|
||||
}
|
||||
|
||||
private async ensureStateLoaded() {
|
||||
if (Object.keys(this.state).length > 0) {
|
||||
return;
|
||||
}
|
||||
this.state = (await this.readTrackerState()) ?? {};
|
||||
}
|
||||
|
||||
private completedStepsForVersion(version: string): string[] {
|
||||
const completedEntries = this.state.completedSteps ?? {};
|
||||
return Object.entries(completedEntries)
|
||||
.filter(([, value]) => value?.version === version)
|
||||
.map(([stepId]) => stepId);
|
||||
}
|
||||
|
||||
private syncConfig(currentVersion: string | undefined) {
|
||||
const completedStepsMap = this.state.completedSteps ?? {};
|
||||
this.configService.set(`${CONFIG_PREFIX}.currentVersion`, currentVersion);
|
||||
this.configService.set('store.emhttp.var.version', currentVersion);
|
||||
this.configService.set(`${CONFIG_PREFIX}.lastTrackedVersion`, this.sessionLastTrackedVersion);
|
||||
this.configService.set(`${CONFIG_PREFIX}.completedSteps`, completedStepsMap);
|
||||
this.configService.set('api.lastSeenOsVersion', this.sessionLastTrackedVersion);
|
||||
}
|
||||
|
||||
private async readCurrentVersion(): Promise<string | undefined> {
|
||||
try {
|
||||
const contents = await readFile(OS_VERSION_FILE_PATH, 'utf8');
|
||||
const match = contents.match(/^\s*version\s*=\s*"([^"]+)"\s*$/m);
|
||||
return match?.[1]?.trim() || undefined;
|
||||
} catch (error) {
|
||||
this.logger.error(error, `Failed to read current OS version from ${OS_VERSION_FILE_PATH}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async readTrackerState(): Promise<TrackerState | undefined> {
|
||||
try {
|
||||
const content = await readFile(this.trackerPath, 'utf8');
|
||||
return JSON.parse(content) as TrackerState;
|
||||
} catch (error) {
|
||||
this.logger.debug(error, `Unable to read onboarding tracker state at ${this.trackerPath}`);
|
||||
|
||||
const legacyState = await this.readLegacyTrackerState();
|
||||
if (legacyState) {
|
||||
return legacyState;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async readLegacyTrackerState(): Promise<TrackerState | undefined> {
|
||||
try {
|
||||
const content = await readFile(this.legacyTrackerPath, 'utf8');
|
||||
this.logger.log(
|
||||
`Loaded legacy onboarding tracker state from ${LEGACY_TRACKER_FILE_NAME}; will persist to ${TRACKER_FILE_NAME}`
|
||||
);
|
||||
return JSON.parse(content) as TrackerState;
|
||||
} catch (error) {
|
||||
this.logger.debug(
|
||||
error,
|
||||
`Unable to read legacy onboarding tracker state at ${this.legacyTrackerPath}`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async writeTrackerState(state: TrackerState): Promise<void> {
|
||||
try {
|
||||
await writeFile(this.trackerPath, JSON.stringify(state, null, 2), { mode: 0o644 });
|
||||
this.state = state;
|
||||
} catch (error) {
|
||||
this.logger.error(error, 'Failed to persist onboarding tracker state');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [OnboardingTracker],
|
||||
exports: [OnboardingTracker],
|
||||
})
|
||||
export class OnboardingTrackerModule {}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Injectable, Logger, Module, OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { readFile } from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { writeFile } from 'atomically';
|
||||
|
||||
import { PATHS_CONFIG_MODULES } from '@app/environment.js';
|
||||
|
||||
const OS_VERSION_FILE_PATH = '/etc/unraid-version';
|
||||
const VERSION_TRACKER_FILE = 'os-version-tracker.json';
|
||||
|
||||
type OsVersionTrackerState = {
|
||||
lastTrackedVersion?: string;
|
||||
updatedAt?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class OsVersionTracker implements OnApplicationBootstrap {
|
||||
private readonly logger = new Logger(OsVersionTracker.name);
|
||||
private readonly trackerPath = path.join(PATHS_CONFIG_MODULES, VERSION_TRACKER_FILE);
|
||||
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
const currentVersion = await this.readCurrentVersion();
|
||||
if (!currentVersion) {
|
||||
this.configService.set('api.currentOsVersion', undefined);
|
||||
this.configService.set('store.emhttp.var.version', undefined);
|
||||
this.configService.set('api.lastSeenOsVersion', undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const previousState = await this.readTrackerState();
|
||||
const lastTrackedVersion = previousState?.lastTrackedVersion;
|
||||
|
||||
this.configService.set('api.currentOsVersion', currentVersion);
|
||||
this.configService.set('store.emhttp.var.version', currentVersion);
|
||||
this.configService.set('api.lastSeenOsVersion', lastTrackedVersion);
|
||||
|
||||
if (lastTrackedVersion !== currentVersion) {
|
||||
await this.writeTrackerState({
|
||||
lastTrackedVersion: currentVersion,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async readCurrentVersion(): Promise<string | undefined> {
|
||||
try {
|
||||
const contents = await readFile(OS_VERSION_FILE_PATH, 'utf8');
|
||||
const match = contents.match(/^\s*version\s*=\s*"([^"]+)"\s*$/m);
|
||||
return match?.[1]?.trim() || undefined;
|
||||
} catch (error) {
|
||||
this.logger.error(error, `Failed to read current OS version from ${OS_VERSION_FILE_PATH}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async readTrackerState(): Promise<OsVersionTrackerState | undefined> {
|
||||
try {
|
||||
const content = await readFile(this.trackerPath, 'utf8');
|
||||
return JSON.parse(content) as OsVersionTrackerState;
|
||||
} catch (error) {
|
||||
this.logger.debug(error, `Unable to read OS version tracker state at ${this.trackerPath}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async writeTrackerState(state: OsVersionTrackerState): Promise<void> {
|
||||
try {
|
||||
await writeFile(this.trackerPath, JSON.stringify(state, null, 2), { mode: 0o644 });
|
||||
} catch (error) {
|
||||
this.logger.error(error, 'Failed to persist OS version tracker state');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [OsVersionTracker],
|
||||
exports: [OsVersionTracker],
|
||||
})
|
||||
export class OsVersionTrackerModule {}
|
||||
@@ -51,6 +51,12 @@ export class UpgradeInfo {
|
||||
|
||||
@Field(() => String, { nullable: true, description: 'Current OS version' })
|
||||
currentVersion?: string;
|
||||
|
||||
@Field(() => [String], {
|
||||
description: 'Onboarding step identifiers completed for the current OS version',
|
||||
defaultValue: [],
|
||||
})
|
||||
completedSteps!: string[];
|
||||
}
|
||||
|
||||
@ObjectType({ implements: () => Node })
|
||||
|
||||
@@ -49,8 +49,23 @@ export class VersionsResolver {
|
||||
|
||||
@ResolveField(() => UpgradeInfo)
|
||||
upgrade(): UpgradeInfo {
|
||||
const currentVersion = this.configService.get<string>('store.emhttp.var.version');
|
||||
const lastSeenVersion = this.configService.get<string>('api.lastSeenOsVersion');
|
||||
const currentVersion =
|
||||
this.configService.get<string>('onboardingTracker.currentVersion') ??
|
||||
this.configService.get<string>('store.emhttp.var.version');
|
||||
const lastSeenVersion =
|
||||
this.configService.get<string>('onboardingTracker.lastTrackedVersion') ??
|
||||
this.configService.get<string>('api.lastSeenOsVersion');
|
||||
const completedStepsMap =
|
||||
this.configService.get<Record<string, { version: string }>>(
|
||||
'onboardingTracker.completedSteps'
|
||||
) ?? {};
|
||||
|
||||
const completedSteps =
|
||||
currentVersion && completedStepsMap
|
||||
? Object.entries(completedStepsMap)
|
||||
.filter(([, value]) => value?.version === currentVersion)
|
||||
.map(([stepId]) => stepId)
|
||||
: [];
|
||||
|
||||
const isUpgrade = Boolean(
|
||||
lastSeenVersion && currentVersion && lastSeenVersion !== currentVersion
|
||||
@@ -60,6 +75,7 @@ export class VersionsResolver {
|
||||
isUpgrade,
|
||||
previousVersion: isUpgrade ? lastSeenVersion : undefined,
|
||||
currentVersion: currentVersion || undefined,
|
||||
completedSteps,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { UpgradeInfo } from '@app/unraid-api/graph/resolvers/info/versions/versions.model.js';
|
||||
import { RCloneRemote } from '@app/unraid-api/graph/resolvers/rclone/rclone.model.js';
|
||||
|
||||
/**
|
||||
@@ -45,6 +46,16 @@ export class RCloneMutations {
|
||||
deleteRCloneRemote!: boolean;
|
||||
}
|
||||
|
||||
@ObjectType({
|
||||
description: 'Onboarding related mutations',
|
||||
})
|
||||
export class OnboardingMutations {
|
||||
@Field(() => UpgradeInfo, {
|
||||
description: 'Mark an upgrade onboarding step as completed for the current OS version',
|
||||
})
|
||||
completeUpgradeStep!: UpgradeInfo;
|
||||
}
|
||||
|
||||
@ObjectType()
|
||||
export class RootMutations {
|
||||
@Field(() => ArrayMutations, { description: 'Array related mutations' })
|
||||
@@ -67,4 +78,7 @@ export class RootMutations {
|
||||
|
||||
@Field(() => RCloneMutations, { description: 'RClone related mutations' })
|
||||
rclone: RCloneMutations = new RCloneMutations();
|
||||
|
||||
@Field(() => OnboardingMutations, { description: 'Onboarding related mutations' })
|
||||
onboarding: OnboardingMutations = new OnboardingMutations();
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ArrayMutations,
|
||||
CustomizationMutations,
|
||||
DockerMutations,
|
||||
OnboardingMutations,
|
||||
ParityCheckMutations,
|
||||
RCloneMutations,
|
||||
RootMutations,
|
||||
@@ -47,4 +48,9 @@ export class RootMutationsResolver {
|
||||
rclone(): RCloneMutations {
|
||||
return new RCloneMutations();
|
||||
}
|
||||
|
||||
@Mutation(() => OnboardingMutations, { name: 'onboarding' })
|
||||
onboarding(): OnboardingMutations {
|
||||
return new OnboardingMutations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
@InputType({
|
||||
description: 'Input for marking an upgrade onboarding step as completed',
|
||||
})
|
||||
export class CompleteUpgradeStepInput {
|
||||
@Field(() => String, { description: 'Identifier of the onboarding step to mark completed' })
|
||||
stepId!: string;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Args, ResolveField, Resolver } from '@nestjs/graphql';
|
||||
|
||||
import { OnboardingTracker } from '@app/unraid-api/config/onboarding-tracker.module.js';
|
||||
import { UpgradeInfo } from '@app/unraid-api/graph/resolvers/info/versions/versions.model.js';
|
||||
import { OnboardingMutations } from '@app/unraid-api/graph/resolvers/mutation/mutation.model.js';
|
||||
import { CompleteUpgradeStepInput } from '@app/unraid-api/graph/resolvers/onboarding/onboarding.model.js';
|
||||
|
||||
@Resolver(() => OnboardingMutations)
|
||||
export class OnboardingMutationsResolver {
|
||||
constructor(
|
||||
private readonly onboardingTracker: OnboardingTracker,
|
||||
private readonly configService: ConfigService
|
||||
) {}
|
||||
|
||||
@ResolveField(() => UpgradeInfo, {
|
||||
description: 'Marks an upgrade onboarding step as completed for the current OS version',
|
||||
})
|
||||
async completeUpgradeStep(@Args('input') input: CompleteUpgradeStepInput): Promise<UpgradeInfo> {
|
||||
await this.onboardingTracker.markStepCompleted(input.stepId);
|
||||
return this.buildUpgradeInfo();
|
||||
}
|
||||
|
||||
private buildUpgradeInfo(): UpgradeInfo {
|
||||
const currentVersion =
|
||||
this.configService.get<string>('onboardingTracker.currentVersion') ??
|
||||
this.configService.get<string>('store.emhttp.var.version');
|
||||
const lastSeenVersion =
|
||||
this.configService.get<string>('onboardingTracker.lastTrackedVersion') ??
|
||||
this.configService.get<string>('api.lastSeenOsVersion');
|
||||
const completedStepsMap =
|
||||
this.configService.get<Record<string, { version: string }>>(
|
||||
'onboardingTracker.completedSteps'
|
||||
) ?? {};
|
||||
|
||||
const completedSteps =
|
||||
currentVersion && completedStepsMap
|
||||
? Object.entries(completedStepsMap)
|
||||
.filter(([, value]) => value?.version === currentVersion)
|
||||
.map(([stepId]) => stepId)
|
||||
: [];
|
||||
|
||||
const isUpgrade = Boolean(
|
||||
lastSeenVersion && currentVersion && lastSeenVersion !== currentVersion
|
||||
);
|
||||
|
||||
return {
|
||||
isUpgrade,
|
||||
previousVersion: isUpgrade ? lastSeenVersion : undefined,
|
||||
currentVersion: currentVersion || undefined,
|
||||
completedSteps,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { MetricsModule } from '@app/unraid-api/graph/resolvers/metrics/metrics.m
|
||||
import { RootMutationsResolver } from '@app/unraid-api/graph/resolvers/mutation/mutation.resolver.js';
|
||||
import { NotificationsModule } from '@app/unraid-api/graph/resolvers/notifications/notifications.module.js';
|
||||
import { NotificationsResolver } from '@app/unraid-api/graph/resolvers/notifications/notifications.resolver.js';
|
||||
import { OnboardingMutationsResolver } from '@app/unraid-api/graph/resolvers/onboarding/onboarding.mutation.js';
|
||||
import { OnlineResolver } from '@app/unraid-api/graph/resolvers/online/online.resolver.js';
|
||||
import { OwnerResolver } from '@app/unraid-api/graph/resolvers/owner/owner.resolver.js';
|
||||
import { RCloneModule } from '@app/unraid-api/graph/resolvers/rclone/rclone.module.js';
|
||||
@@ -63,6 +64,7 @@ import { MeResolver } from '@app/unraid-api/graph/user/user.resolver.js';
|
||||
NotificationsResolver,
|
||||
OnlineResolver,
|
||||
OwnerResolver,
|
||||
OnboardingMutationsResolver,
|
||||
RegistrationResolver,
|
||||
RootMutationsResolver,
|
||||
ServerResolver,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useMutation } from '@vue/apollo-composable';
|
||||
|
||||
import { ArrowTopRightOnSquareIcon } from '@heroicons/vue/24/solid';
|
||||
import { BrandButton, Dialog } from '@unraid/ui';
|
||||
@@ -12,6 +13,7 @@ import ActivationPartnerLogo from '~/components/Activation/ActivationPartnerLogo
|
||||
import ActivationPluginsStep from '~/components/Activation/ActivationPluginsStep.vue';
|
||||
import ActivationSteps from '~/components/Activation/ActivationSteps.vue';
|
||||
import ActivationTimezoneStep from '~/components/Activation/ActivationTimezoneStep.vue';
|
||||
import { COMPLETE_UPGRADE_STEP_MUTATION } from '~/components/Activation/completeUpgradeStep.mutation';
|
||||
import { useActivationCodeDataStore } from '~/components/Activation/store/activationCodeData';
|
||||
import { useActivationCodeModalStore } from '~/components/Activation/store/activationCodeModal';
|
||||
import { useUpgradeOnboardingStore } from '~/components/Activation/store/upgradeOnboarding';
|
||||
@@ -26,6 +28,7 @@ const { partnerInfo, activationCode, isFreshInstall } = storeToRefs(useActivatio
|
||||
const upgradeStore = useUpgradeOnboardingStore();
|
||||
const { shouldShowUpgradeOnboarding, upgradeSteps, currentVersion, previousVersion } =
|
||||
storeToRefs(upgradeStore);
|
||||
const { refetchUpgradeInfo } = upgradeStore;
|
||||
const purchaseStore = usePurchaseStore();
|
||||
|
||||
useThemeStore();
|
||||
@@ -131,7 +134,33 @@ const closeModal = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const goToNextStep = () => {
|
||||
const { mutate: completeUpgradeStepMutation } = useMutation(COMPLETE_UPGRADE_STEP_MUTATION);
|
||||
|
||||
const markUpgradeStepCompleted = async (stepId: string | null) => {
|
||||
if (!isUpgradeMode.value || !stepId) return;
|
||||
|
||||
try {
|
||||
await completeUpgradeStepMutation({ input: { stepId } });
|
||||
await refetchUpgradeInfo();
|
||||
} catch (error) {
|
||||
console.error('[ActivationModal] Failed to mark upgrade step completed', error);
|
||||
}
|
||||
};
|
||||
|
||||
const goToNextStep = async () => {
|
||||
if (isUpgradeMode.value) {
|
||||
await markUpgradeStepCompleted(currentStep.value);
|
||||
|
||||
if (upgradeSteps.value.length === 0) {
|
||||
closeModal();
|
||||
currentStepIndex.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
currentStepIndex.value = Math.min(currentStepIndex.value, upgradeSteps.value.length - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStepIndex.value < availableSteps.value.length - 1) {
|
||||
currentStepIndex.value++;
|
||||
} else if (hasActivationCode.value && !isUpgradeMode.value) {
|
||||
@@ -149,21 +178,21 @@ const goToPreviousStep = () => {
|
||||
|
||||
const canGoBack = computed(() => currentStepIndex.value > 0);
|
||||
|
||||
const handleTimezoneComplete = () => {
|
||||
const handleTimezoneComplete = async () => {
|
||||
console.log('[ActivationModal] Timezone complete, moving to next step');
|
||||
goToNextStep();
|
||||
await goToNextStep();
|
||||
};
|
||||
|
||||
const handleTimezoneSkip = () => {
|
||||
goToNextStep();
|
||||
const handleTimezoneSkip = async () => {
|
||||
await goToNextStep();
|
||||
};
|
||||
|
||||
const handlePluginsComplete = () => {
|
||||
goToNextStep();
|
||||
const handlePluginsComplete = async () => {
|
||||
await goToNextStep();
|
||||
};
|
||||
|
||||
const handlePluginsSkip = () => {
|
||||
goToNextStep();
|
||||
const handlePluginsSkip = async () => {
|
||||
await goToNextStep();
|
||||
};
|
||||
|
||||
const currentStepConfig = computed(() => {
|
||||
@@ -172,6 +201,21 @@ const currentStepConfig = computed(() => {
|
||||
}
|
||||
return upgradeSteps.value[currentStepIndex.value];
|
||||
});
|
||||
|
||||
watch(
|
||||
() => upgradeSteps.value.length,
|
||||
(length) => {
|
||||
if (!isUpgradeMode.value) return;
|
||||
if (length === 0) {
|
||||
currentStepIndex.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStepIndex.value >= length) {
|
||||
currentStepIndex.value = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { graphql } from '~/composables/gql';
|
||||
|
||||
export const COMPLETE_UPGRADE_STEP_MUTATION = graphql(/* GraphQL */ `
|
||||
mutation CompleteUpgradeStep($input: CompleteUpgradeStepInput!) {
|
||||
onboarding {
|
||||
completeUpgradeStep(input: $input) {
|
||||
isUpgrade
|
||||
previousVersion
|
||||
currentVersion
|
||||
completedSteps
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
@@ -11,11 +11,11 @@ import { UPGRADE_INFO_QUERY } from '~/components/Activation/upgradeInfo.query';
|
||||
const UPGRADE_ONBOARDING_HIDDEN_KEY = 'upgrade-onboarding-hidden';
|
||||
|
||||
export const useUpgradeOnboardingStore = defineStore('upgradeOnboarding', () => {
|
||||
const { result: upgradeInfoResult, loading: upgradeInfoLoading } = useQuery(
|
||||
UPGRADE_INFO_QUERY,
|
||||
{},
|
||||
{ errorPolicy: 'all' }
|
||||
);
|
||||
const {
|
||||
result: upgradeInfoResult,
|
||||
loading: upgradeInfoLoading,
|
||||
refetch,
|
||||
} = useQuery(UPGRADE_INFO_QUERY, {}, { errorPolicy: 'all' });
|
||||
|
||||
const isHidden = useSessionStorage<boolean>(UPGRADE_ONBOARDING_HIDDEN_KEY, false);
|
||||
|
||||
@@ -26,21 +26,28 @@ export const useUpgradeOnboardingStore = defineStore('upgradeOnboarding', () =>
|
||||
const currentVersion = computed(
|
||||
() => upgradeInfoResult.value?.info?.versions?.upgrade?.currentVersion
|
||||
);
|
||||
const completedSteps = computed(
|
||||
() => upgradeInfoResult.value?.info?.versions?.upgrade?.completedSteps ?? []
|
||||
);
|
||||
|
||||
const upgradeSteps = ref<ReleaseStepConfig[]>([]);
|
||||
const allUpgradeSteps = ref<ReleaseStepConfig[]>([]);
|
||||
|
||||
watch(
|
||||
[isUpgrade, previousVersion, currentVersion],
|
||||
async ([isUpgradeValue, prevVersion, currVersion]) => {
|
||||
if (isUpgradeValue && prevVersion && currVersion) {
|
||||
upgradeSteps.value = await getUpgradeSteps(prevVersion, currVersion);
|
||||
allUpgradeSteps.value = await getUpgradeSteps(prevVersion, currVersion);
|
||||
} else {
|
||||
upgradeSteps.value = [];
|
||||
allUpgradeSteps.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const upgradeSteps = computed(() =>
|
||||
allUpgradeSteps.value.filter((step) => !completedSteps.value.includes(step.id))
|
||||
);
|
||||
|
||||
const shouldShowUpgradeOnboarding = computed(() => {
|
||||
return !isHidden.value && isUpgrade.value && upgradeSteps.value.length > 0;
|
||||
});
|
||||
@@ -54,9 +61,11 @@ export const useUpgradeOnboardingStore = defineStore('upgradeOnboarding', () =>
|
||||
isUpgrade,
|
||||
previousVersion,
|
||||
currentVersion,
|
||||
completedSteps,
|
||||
upgradeSteps,
|
||||
shouldShowUpgradeOnboarding,
|
||||
isHidden,
|
||||
setIsHidden,
|
||||
refetchUpgradeInfo: refetch,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ export const UPGRADE_INFO_QUERY = graphql(/* GraphQL */ `
|
||||
isUpgrade
|
||||
previousVersion
|
||||
currentVersion
|
||||
completedSteps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-
|
||||
* Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
|
||||
*/
|
||||
type Documents = {
|
||||
"\n mutation CompleteUpgradeStep($input: CompleteUpgradeStepInput!) {\n onboarding {\n completeUpgradeStep(input: $input) {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n": typeof types.CompleteUpgradeStepDocument,
|
||||
"\n query PartnerInfo {\n publicPartnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n }\n": typeof types.PartnerInfoDocument,
|
||||
"\n query PublicWelcomeData {\n publicPartnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n isInitialSetup\n }\n": typeof types.PublicWelcomeDataDocument,
|
||||
"\n query ActivationCode {\n vars {\n regState\n }\n customization {\n activationCode {\n code\n partnerName\n serverName\n sysModel\n comment\n header\n headermetacolor\n background\n showBannerGradient\n theme\n }\n partnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n }\n }\n": typeof types.ActivationCodeDocument,
|
||||
"\n mutation UpdateSystemTime($input: UpdateSystemTimeInput!) {\n updateSystemTime(input: $input) {\n currentTime\n timeZone\n useNtp\n ntpServers\n }\n }\n": typeof types.UpdateSystemTimeDocument,
|
||||
"\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n }\n }\n }\n }\n": typeof types.UpgradeInfoDocument,
|
||||
"\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n }\n": typeof types.UpgradeInfoDocument,
|
||||
"\n query GetApiKeyCreationFormSchema {\n getApiKeyCreationFormSchema {\n id\n dataSchema\n uiSchema\n values\n }\n }\n": typeof types.GetApiKeyCreationFormSchemaDocument,
|
||||
"\n mutation CreateApiKey($input: CreateApiKeyInput!) {\n apiKey {\n create(input: $input) {\n ...ApiKey\n }\n }\n }\n": typeof types.CreateApiKeyDocument,
|
||||
"\n mutation UpdateApiKey($input: UpdateApiKeyInput!) {\n apiKey {\n update(input: $input) {\n ...ApiKey\n }\n }\n }\n": typeof types.UpdateApiKeyDocument,
|
||||
@@ -86,11 +87,12 @@ type Documents = {
|
||||
"\n query getTheme {\n publicTheme {\n name\n showBannerImage\n showBannerGradient\n headerBackgroundColor\n showHeaderDescription\n headerPrimaryTextColor\n headerSecondaryTextColor\n }\n }\n": typeof types.GetThemeDocument,
|
||||
};
|
||||
const documents: Documents = {
|
||||
"\n mutation CompleteUpgradeStep($input: CompleteUpgradeStepInput!) {\n onboarding {\n completeUpgradeStep(input: $input) {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n": types.CompleteUpgradeStepDocument,
|
||||
"\n query PartnerInfo {\n publicPartnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n }\n": types.PartnerInfoDocument,
|
||||
"\n query PublicWelcomeData {\n publicPartnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n isInitialSetup\n }\n": types.PublicWelcomeDataDocument,
|
||||
"\n query ActivationCode {\n vars {\n regState\n }\n customization {\n activationCode {\n code\n partnerName\n serverName\n sysModel\n comment\n header\n headermetacolor\n background\n showBannerGradient\n theme\n }\n partnerInfo {\n hasPartnerLogo\n partnerName\n partnerUrl\n partnerLogoUrl\n }\n }\n }\n": types.ActivationCodeDocument,
|
||||
"\n mutation UpdateSystemTime($input: UpdateSystemTimeInput!) {\n updateSystemTime(input: $input) {\n currentTime\n timeZone\n useNtp\n ntpServers\n }\n }\n": types.UpdateSystemTimeDocument,
|
||||
"\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n }\n }\n }\n }\n": types.UpgradeInfoDocument,
|
||||
"\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n }\n": types.UpgradeInfoDocument,
|
||||
"\n query GetApiKeyCreationFormSchema {\n getApiKeyCreationFormSchema {\n id\n dataSchema\n uiSchema\n values\n }\n }\n": types.GetApiKeyCreationFormSchemaDocument,
|
||||
"\n mutation CreateApiKey($input: CreateApiKeyInput!) {\n apiKey {\n create(input: $input) {\n ...ApiKey\n }\n }\n }\n": types.CreateApiKeyDocument,
|
||||
"\n mutation UpdateApiKey($input: UpdateApiKeyInput!) {\n apiKey {\n update(input: $input) {\n ...ApiKey\n }\n }\n }\n": types.UpdateApiKeyDocument,
|
||||
@@ -172,6 +174,10 @@ const documents: Documents = {
|
||||
*/
|
||||
export function graphql(source: string): unknown;
|
||||
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n mutation CompleteUpgradeStep($input: CompleteUpgradeStepInput!) {\n onboarding {\n completeUpgradeStep(input: $input) {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n"): (typeof documents)["\n mutation CompleteUpgradeStep($input: CompleteUpgradeStepInput!) {\n onboarding {\n completeUpgradeStep(input: $input) {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -191,7 +197,7 @@ export function graphql(source: "\n mutation UpdateSystemTime($input: UpdateSys
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n }\n }\n }\n }\n"): (typeof documents)["\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n }\n }\n }\n }\n"];
|
||||
export function graphql(source: "\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n }\n"): (typeof documents)["\n query UpgradeInfo {\n info {\n id\n versions {\n id\n upgrade {\n isUpgrade\n previousVersion\n currentVersion\n completedSteps\n }\n }\n }\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
@@ -434,6 +434,12 @@ export type CloudResponse = {
|
||||
status: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
/** Input for marking an upgrade onboarding step as completed */
|
||||
export type CompleteUpgradeStepInput = {
|
||||
/** Identifier of the onboarding step to mark completed */
|
||||
stepId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type Config = Node & {
|
||||
__typename?: 'Config';
|
||||
error?: Maybe<Scalars['String']['output']>;
|
||||
@@ -1464,6 +1470,7 @@ export type Mutation = {
|
||||
moveDockerItemsToPosition: ResolvedOrganizerV1;
|
||||
/** Creates a notification if an equivalent unread notification does not already exist. */
|
||||
notifyIfUnique?: Maybe<Notification>;
|
||||
onboarding: OnboardingMutations;
|
||||
parityCheck: ParityCheckMutations;
|
||||
rclone: RCloneMutations;
|
||||
/** Reads each notification to recompute & update the overview. */
|
||||
@@ -1774,6 +1781,19 @@ export type OidcSessionValidation = {
|
||||
valid: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
/** Onboarding related mutations */
|
||||
export type OnboardingMutations = {
|
||||
__typename?: 'OnboardingMutations';
|
||||
/** Mark an upgrade onboarding step as completed for the current OS version */
|
||||
completeUpgradeStep: UpgradeInfo;
|
||||
};
|
||||
|
||||
|
||||
/** Onboarding related mutations */
|
||||
export type OnboardingMutationsCompleteUpgradeStepArgs = {
|
||||
input: CompleteUpgradeStepInput;
|
||||
};
|
||||
|
||||
export type Owner = {
|
||||
__typename?: 'Owner';
|
||||
avatar: Scalars['String']['output'];
|
||||
@@ -2614,6 +2634,8 @@ export type UpdateSystemTimeInput = {
|
||||
|
||||
export type UpgradeInfo = {
|
||||
__typename?: 'UpgradeInfo';
|
||||
/** Onboarding step identifiers completed for the current OS version */
|
||||
completedSteps: Array<Scalars['String']['output']>;
|
||||
/** Current OS version */
|
||||
currentVersion?: Maybe<Scalars['String']['output']>;
|
||||
/** Whether the OS version has changed since last boot */
|
||||
@@ -2913,6 +2935,13 @@ export enum RegistrationType {
|
||||
UNLEASHED = 'UNLEASHED'
|
||||
}
|
||||
|
||||
export type CompleteUpgradeStepMutationVariables = Exact<{
|
||||
input: CompleteUpgradeStepInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CompleteUpgradeStepMutation = { __typename?: 'Mutation', onboarding: { __typename?: 'OnboardingMutations', completeUpgradeStep: { __typename?: 'UpgradeInfo', isUpgrade: boolean, previousVersion?: string | null, currentVersion?: string | null, completedSteps: Array<string> } } };
|
||||
|
||||
export type PartnerInfoQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
@@ -2938,7 +2967,7 @@ export type UpdateSystemTimeMutation = { __typename?: 'Mutation', updateSystemTi
|
||||
export type UpgradeInfoQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type UpgradeInfoQuery = { __typename?: 'Query', info: { __typename?: 'Info', id: string, versions: { __typename?: 'InfoVersions', id: string, upgrade: { __typename?: 'UpgradeInfo', isUpgrade: boolean, previousVersion?: string | null, currentVersion?: string | null } } } };
|
||||
export type UpgradeInfoQuery = { __typename?: 'Query', info: { __typename?: 'Info', id: string, versions: { __typename?: 'InfoVersions', id: string, upgrade: { __typename?: 'UpgradeInfo', isUpgrade: boolean, previousVersion?: string | null, currentVersion?: string | null, completedSteps: Array<string> } } } };
|
||||
|
||||
export type GetApiKeyCreationFormSchemaQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -3385,11 +3414,12 @@ export const ApiKeyFragmentDoc = {"kind":"Document","definitions":[{"kind":"Frag
|
||||
export const NotificationFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Notification"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"subject"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"importance"}},{"kind":"Field","name":{"kind":"Name","value":"link"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"formattedTimestamp"}}]}}]} as unknown as DocumentNode<NotificationFragmentFragment, unknown>;
|
||||
export const NotificationCountFragmentFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"NotificationCountFragment"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationCounts"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"info"}},{"kind":"Field","name":{"kind":"Name","value":"warning"}},{"kind":"Field","name":{"kind":"Name","value":"alert"}}]}}]} as unknown as DocumentNode<NotificationCountFragmentFragment, unknown>;
|
||||
export const PartialCloudFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PartialCloud"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Cloud"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"minigraphql"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"relay"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode<PartialCloudFragment, unknown>;
|
||||
export const CompleteUpgradeStepDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CompleteUpgradeStep"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CompleteUpgradeStepInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"onboarding"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completeUpgradeStep"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isUpgrade"}},{"kind":"Field","name":{"kind":"Name","value":"previousVersion"}},{"kind":"Field","name":{"kind":"Name","value":"currentVersion"}},{"kind":"Field","name":{"kind":"Name","value":"completedSteps"}}]}}]}}]}}]} as unknown as DocumentNode<CompleteUpgradeStepMutation, CompleteUpgradeStepMutationVariables>;
|
||||
export const PartnerInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PartnerInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publicPartnerInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasPartnerLogo"}},{"kind":"Field","name":{"kind":"Name","value":"partnerName"}},{"kind":"Field","name":{"kind":"Name","value":"partnerUrl"}},{"kind":"Field","name":{"kind":"Name","value":"partnerLogoUrl"}}]}}]}}]} as unknown as DocumentNode<PartnerInfoQuery, PartnerInfoQueryVariables>;
|
||||
export const PublicWelcomeDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PublicWelcomeData"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publicPartnerInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasPartnerLogo"}},{"kind":"Field","name":{"kind":"Name","value":"partnerName"}},{"kind":"Field","name":{"kind":"Name","value":"partnerUrl"}},{"kind":"Field","name":{"kind":"Name","value":"partnerLogoUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"isInitialSetup"}}]}}]} as unknown as DocumentNode<PublicWelcomeDataQuery, PublicWelcomeDataQueryVariables>;
|
||||
export const ActivationCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ActivationCode"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"vars"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regState"}}]}},{"kind":"Field","name":{"kind":"Name","value":"customization"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activationCode"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"partnerName"}},{"kind":"Field","name":{"kind":"Name","value":"serverName"}},{"kind":"Field","name":{"kind":"Name","value":"sysModel"}},{"kind":"Field","name":{"kind":"Name","value":"comment"}},{"kind":"Field","name":{"kind":"Name","value":"header"}},{"kind":"Field","name":{"kind":"Name","value":"headermetacolor"}},{"kind":"Field","name":{"kind":"Name","value":"background"}},{"kind":"Field","name":{"kind":"Name","value":"showBannerGradient"}},{"kind":"Field","name":{"kind":"Name","value":"theme"}}]}},{"kind":"Field","name":{"kind":"Name","value":"partnerInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasPartnerLogo"}},{"kind":"Field","name":{"kind":"Name","value":"partnerName"}},{"kind":"Field","name":{"kind":"Name","value":"partnerUrl"}},{"kind":"Field","name":{"kind":"Name","value":"partnerLogoUrl"}}]}}]}}]}}]} as unknown as DocumentNode<ActivationCodeQuery, ActivationCodeQueryVariables>;
|
||||
export const UpdateSystemTimeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSystemTime"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateSystemTimeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSystemTime"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentTime"}},{"kind":"Field","name":{"kind":"Name","value":"timeZone"}},{"kind":"Field","name":{"kind":"Name","value":"useNtp"}},{"kind":"Field","name":{"kind":"Name","value":"ntpServers"}}]}}]}}]} as unknown as DocumentNode<UpdateSystemTimeMutation, UpdateSystemTimeMutationVariables>;
|
||||
export const UpgradeInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UpgradeInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isUpgrade"}},{"kind":"Field","name":{"kind":"Name","value":"previousVersion"}},{"kind":"Field","name":{"kind":"Name","value":"currentVersion"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UpgradeInfoQuery, UpgradeInfoQueryVariables>;
|
||||
export const UpgradeInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UpgradeInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"info"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"upgrade"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"isUpgrade"}},{"kind":"Field","name":{"kind":"Name","value":"previousVersion"}},{"kind":"Field","name":{"kind":"Name","value":"currentVersion"}},{"kind":"Field","name":{"kind":"Name","value":"completedSteps"}}]}}]}}]}}]}}]} as unknown as DocumentNode<UpgradeInfoQuery, UpgradeInfoQueryVariables>;
|
||||
export const GetApiKeyCreationFormSchemaDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetApiKeyCreationFormSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getApiKeyCreationFormSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSchema"}},{"kind":"Field","name":{"kind":"Name","value":"uiSchema"}},{"kind":"Field","name":{"kind":"Name","value":"values"}}]}}]}}]} as unknown as DocumentNode<GetApiKeyCreationFormSchemaQuery, GetApiKeyCreationFormSchemaQueryVariables>;
|
||||
export const CreateApiKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateApiKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateApiKeyInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ApiKey"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ApiKey"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ApiKey"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resource"}},{"kind":"Field","name":{"kind":"Name","value":"actions"}}]}}]}}]} as unknown as DocumentNode<CreateApiKeyMutation, CreateApiKeyMutationVariables>;
|
||||
export const UpdateApiKeyDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateApiKey"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateApiKeyInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ApiKey"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ApiKey"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ApiKey"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"permissions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"resource"}},{"kind":"Field","name":{"kind":"Name","value":"actions"}}]}}]}}]} as unknown as DocumentNode<UpdateApiKeyMutation, UpdateApiKeyMutationVariables>;
|
||||
|
||||
Reference in New Issue
Block a user