feat: complete account-bound profile switching and hardening (#78)

This commit is contained in:
vin authored and GitHub committed 2026-02-08 01:10:07 +08:00
1 parent ee641799c2
commit a93c6d0cea
31 files changed
+3612 -197

No files matched your search

+111
View File
@@ -2,9 +2,17 @@ import { z } from 'zod';
import { os } from '@orpc/server';
import {
addGoogleAccount,
bindCloudIdentityProfile,
bindCloudIdentityProfileWithPayload,
deleteCloudIdentityProfileRevision,
listCloudAccounts,
deleteCloudAccount,
getCloudIdentityProfiles,
openCloudIdentityStorageFolder,
previewGenerateCloudIdentityProfile,
refreshAccountQuota,
restoreCloudIdentityProfileRevision,
restoreCloudBaselineProfile,
switchCloudAccount,
getAutoSwitchEnabled,
setAutoSwitchEnabled,
@@ -12,8 +20,53 @@ import {
startAuthFlow,
} from './handler';
import { CloudAccountSchema } from '../../types/cloudAccount';
import {
DeviceProfileSchema,
DeviceProfilesSnapshotSchema,
} from '../../types/account';
import { CloudAccountRepo } from '../database/cloudHandler';
import { logger } from '../../utils/logger';
import { getSwitchMetricsSnapshot } from '../switchMetrics';
import { getSwitchGuardSnapshot } from '../switchGuard';
import { getDeviceHardeningSnapshot } from '../device/handler';
const switchOwnerSchema = z.enum(['local-account-switch', 'cloud-account-switch']);
const switchMetricBucketSchema = z.object({
switchSuccess: z.number(),
switchFailure: z.number(),
rollbackAttempt: z.number(),
rollbackSuccess: z.number(),
rollbackFailure: z.number(),
failureReasons: z.record(z.string(), z.number()),
lastFailure: z
.object({
reason: z.string(),
message: z.string(),
occurredAt: z.number(),
})
.nullable(),
});
const switchMetricsSnapshotSchema = z.object({
local: switchMetricBucketSchema,
cloud: switchMetricBucketSchema,
});
const switchGuardSnapshotSchema = z.object({
activeOwner: switchOwnerSchema.nullable(),
pendingOwners: z.array(switchOwnerSchema),
pendingCount: z.number(),
});
const switchStatusSnapshotSchema = z.object({
metrics: switchMetricsSnapshotSchema,
guard: switchGuardSnapshotSchema,
hardening: z.object({
consecutiveApplyFailures: z.number(),
safeModeActive: z.boolean(),
safeModeUntil: z.number().nullable(),
lastFailureReason: z.string().nullable(),
lastFailureStage: z.string().nullable(),
lastFailureAt: z.number().nullable(),
}),
});
export const cloudRouter = os.router({
addGoogleAccount: os
@@ -77,4 +130,62 @@ export const cloudRouter = os.router({
throw error;
}
}),
getSwitchStatus: os.output(switchStatusSnapshotSchema).handler(async () => {
return {
metrics: getSwitchMetricsSnapshot(),
guard: getSwitchGuardSnapshot(),
hardening: getDeviceHardeningSnapshot(),
};
}),
getIdentityProfiles: os
.input(z.object({ accountId: z.string() }))
.output(DeviceProfilesSnapshotSchema)
.handler(async ({ input }) => {
return getCloudIdentityProfiles(input.accountId);
}),
previewIdentityProfile: os.output(DeviceProfileSchema).handler(async () => {
return previewGenerateCloudIdentityProfile();
}),
bindIdentityProfile: os
.input(z.object({ accountId: z.string(), mode: z.enum(['capture', 'generate']) }))
.output(DeviceProfileSchema)
.handler(async ({ input }) => {
return bindCloudIdentityProfile(input.accountId, input.mode);
}),
bindIdentityProfileWithPayload: os
.input(z.object({ accountId: z.string(), profile: DeviceProfileSchema }))
.output(DeviceProfileSchema)
.handler(async ({ input }) => {
return bindCloudIdentityProfileWithPayload(input.accountId, input.profile);
}),
restoreIdentityProfileRevision: os
.input(z.object({ accountId: z.string(), versionId: z.string() }))
.output(DeviceProfileSchema)
.handler(async ({ input }) => {
return restoreCloudIdentityProfileRevision(input.accountId, input.versionId);
}),
restoreBaselineProfile: os
.input(z.object({ accountId: z.string() }))
.output(DeviceProfileSchema)
.handler(async ({ input }) => {
return restoreCloudBaselineProfile(input.accountId);
}),
deleteIdentityProfileRevision: os
.input(z.object({ accountId: z.string(), versionId: z.string() }))
.output(z.void())
.handler(async ({ input }) => {
await deleteCloudIdentityProfileRevision(input.accountId, input.versionId);
}),
openIdentityStorageFolder: os.output(z.void()).handler(async () => {
await openCloudIdentityStorageFolder();
}),
});