Define backup model

This commit is contained in:
Sosthene 2025-07-16 17:16:07 +02:00
parent d3e207c6da
commit 7823c4c172

View File

@ -1,7 +1,304 @@
import { Device, Process, SecretsStore } from "pkg/sdk_client";
export interface BackUp {
device: Device,
secrets: SecretsStore,
processes: Record<string, Process>,
export interface UserDataBackUp {
version: number,
exported_at: Date,
user_data: {
device: Device | string, // string if encrypted
secrets: SecretsStore | string, // string if encrypted
processes: Record<string, Process> | string, // string if encrypted
},
metadata: {
encrypted: boolean,
checksum: string,
}
}
/**
* Validates a UserDataBackUp object
* @param backup - The backup object to validate
* @returns null if valid, error message string if invalid
*/
export function isValid(backup: any): string | null {
// Check if backup is an object
if (!backup || typeof backup !== 'object') {
return 'Backup must be a valid object';
}
// Validate version
if (typeof backup.version !== 'number') {
return 'Version must be a number';
}
if (backup.version <= 0) {
return 'Version must be greater than 0';
}
// Validate exported_at
if (!(backup.exported_at instanceof Date)) {
return 'exported_at must be a valid Date object';
}
if (isNaN(backup.exported_at.getTime())) {
return 'exported_at must be a valid date';
}
// Validate user_data
if (!backup.user_data || typeof backup.user_data !== 'object') {
return 'user_data must be a valid object';
}
// Validate metadata
if (!backup.metadata || typeof backup.metadata !== 'object') {
return 'metadata must be a valid object';
}
if (typeof backup.metadata.encrypted !== 'boolean') {
return 'metadata.encrypted must be a boolean';
}
if (typeof backup.metadata.checksum !== 'string') {
return 'metadata.checksum must be a string';
}
if (backup.metadata.checksum.length === 0) {
return 'metadata.checksum cannot be empty';
}
// Validate user_data fields based on encryption status
const { encrypted } = backup.metadata;
const { device, secrets, processes } = backup.user_data;
// Validate device
if (encrypted) {
if (typeof device !== 'string') {
return 'device must be a string when backup is encrypted';
}
if (device.length === 0) {
return 'device cannot be empty when backup is encrypted';
}
} else {
if (!device || typeof device !== 'object') {
return 'device must be a valid object when backup is not encrypted';
}
// Validate Device structure
const deviceError = validateDevice(device);
if (deviceError) {
return `Invalid device: ${deviceError}`;
}
}
// Validate secrets
if (encrypted) {
if (typeof secrets !== 'string') {
return 'secrets must be a string when backup is encrypted';
}
if (secrets.length === 0) {
return 'secrets cannot be empty when backup is encrypted';
}
} else {
if (!secrets || typeof secrets !== 'object') {
return 'secrets must be a valid object when backup is not encrypted';
}
// Validate SecretsStore structure
const secretsError = validateSecretsStore(secrets);
if (secretsError) {
return `Invalid secrets: ${secretsError}`;
}
}
// Validate processes
if (encrypted) {
if (typeof processes !== 'string') {
return 'processes must be a string when backup is encrypted';
}
if (processes.length === 0) {
return 'processes cannot be empty when backup is encrypted';
}
} else {
if (!processes || typeof processes !== 'object') {
return 'processes must be a valid object when backup is not encrypted';
}
// Validate processes Record structure
const processesError = validateProcessesRecord(processes);
if (processesError) {
return `Invalid processes: ${processesError}`;
}
}
return null; // Valid
}
/**
* Validates a Device object
*/
function validateDevice(device: any): string | null {
if (!device || typeof device !== 'object') {
return 'Device must be a valid object';
}
// Check for required Device properties
if (!device.sp_wallet || typeof device.sp_wallet !== 'object') {
return 'sp_wallet must be a valid object';
}
// Validate sp_wallet structure (basic validation)
if (!device.sp_wallet.sp_address || typeof device.sp_wallet.sp_address !== 'string') {
return 'sp_wallet.sp_address must be a non-empty string';
}
// pairing_process_commitment can be null or an object
if (device.pairing_process_commitment !== null &&
(typeof device.pairing_process_commitment !== 'object' ||
typeof device.pairing_process_commitment.txid !== 'string' ||
typeof device.pairing_process_commitment.vout !== 'number')) {
return 'pairing_process_commitment must be null or a valid OutPoint object with txid (string) and vout (number)';
}
// Validate paired_member
if (!device.paired_member || typeof device.paired_member !== 'object') {
return 'paired_member must be a valid object';
}
if (!Array.isArray(device.paired_member.sp_addresses)) {
return 'paired_member.sp_addresses must be an array';
}
if (!device.paired_member.sp_addresses.every((addr: any) => typeof addr === 'string')) {
return 'All sp_addresses must be strings';
}
return null; // Valid
}
/**
* Validates a SecretsStore object
*/
function validateSecretsStore(secrets: any): string | null {
if (!secrets || typeof secrets !== 'object') {
return 'Secrets must be a valid object';
}
// Validate shared_secrets
if (!secrets.shared_secrets || typeof secrets.shared_secrets !== 'object') {
return 'shared_secrets must be a valid object';
}
// Validate unconfirmed_secrets
if (!Array.isArray(secrets.unconfirmed_secrets)) {
return 'unconfirmed_secrets must be an array';
}
// Basic validation of shared_secrets entries
for (const [key, value] of Object.entries(secrets.shared_secrets)) {
if (typeof key !== 'string') {
return 'All shared_secrets keys must be strings';
}
if (typeof value !== 'string') {
return 'All shared_secrets values must be strings';
}
}
// Basic validation of unconfirmed_secrets entries
for (const secret of secrets.unconfirmed_secrets) {
if (typeof secret !== 'string') {
return 'All unconfirmed_secrets must be strings';
}
}
return null; // Valid
}
/**
* Validates a Record<string, Process> object
*/
function validateProcessesRecord(processes: any): string | null {
if (!processes || typeof processes !== 'object') {
return 'Processes must be a valid object';
}
for (const [key, process] of Object.entries(processes)) {
if (typeof key !== 'string') {
return 'All process keys must be strings';
}
const processError = validateProcess(process);
if (processError) {
return `Invalid process '${key}': ${processError}`;
}
}
return null; // Valid
}
/**
* Validates a Process object
*/
function validateProcess(process: any): string | null {
if (!process || typeof process !== 'object') {
return 'Process must be a valid object';
}
if (!Array.isArray(process.states)) {
return 'Process states must be an array';
}
// Validate each state in the process
for (let i = 0; i < process.states.length; i++) {
const stateError = validateProcessState(process.states[i]);
if (stateError) {
return `Invalid state at index ${i}: ${stateError}`;
}
}
return null; // Valid
}
/**
* Validates a ProcessState object
*/
function validateProcessState(state: any): string | null {
if (!state || typeof state !== 'object') {
return 'Process state must be a valid object';
}
// Validate commited_in (OutPoint)
if (!state.commited_in || typeof state.commited_in !== 'object') {
return 'commited_in must be a valid object';
}
if (typeof state.commited_in.txid !== 'string') {
return 'commited_in.txid must be a string';
}
if (typeof state.commited_in.vout !== 'number') {
return 'commited_in.vout must be a number';
}
// Validate pcd_commitment
if (!state.pcd_commitment || typeof state.pcd_commitment !== 'object') {
return 'pcd_commitment must be a valid object';
}
// Validate state_id
if (typeof state.state_id !== 'string') {
return 'state_id must be a string';
}
// Validate keys
if (!state.keys || typeof state.keys !== 'object') {
return 'keys must be a valid object';
}
// Validate validation_tokens
if (!Array.isArray(state.validation_tokens)) {
return 'validation_tokens must be an array';
}
// Validate public_data
if (!state.public_data || typeof state.public_data !== 'object') {
return 'public_data must be a valid object';
}
// Validate roles
if (!state.roles || typeof state.roles !== 'object') {
return 'roles must be a valid object';
}
return null; // Valid
}