import { initContract } from '@ts-rest/core'; import { z } from 'zod'; import { restoreBackupResponseSchema } from '../schemas/shopkeeper-backup.schemas'; import { errorResponseSchema } from '../schemas/common.schemas'; const c = initContract(); const backupMetadataSchema = z.object({ id: z.string().uuid(), shopId: z.string().uuid(), sizeBytes: z.number(), checksum: z.string(), createdAt: z.coerce.date(), }); export const shopkeeperBackupContract = c.router({ uploadBackup: { method: 'POST', path: '/shopkeeper/backup/upload', contentType: 'multipart/form-data', body: z.object({ file: z.any() }), responses: { 200: backupMetadataSchema, 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Upload encrypted SQLite backup to R2', }, getLatestBackup: { method: 'GET', path: '/shopkeeper/backup/latest', query: z.object({ shopId: z.string().uuid() }), responses: { 200: backupMetadataSchema.nullable(), 401: errorResponseSchema, }, summary: 'Get latest backup metadata', }, downloadBackup: { method: 'GET', path: '/shopkeeper/backup/download', query: z.object({ shopId: z.string().uuid(), backupId: z.string().uuid().optional() }), responses: { 200: restoreBackupResponseSchema, 404: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Download encrypted backup for restore', }, listBackups: { method: 'GET', path: '/shopkeeper/backup/list', query: z.object({ shopId: z.string().uuid() }).merge(z.object({ limit: z.coerce.number().int().min(1).max(100).optional() })), responses: { 200: z.object({ backups: z.array(backupMetadataSchema) }), 401: errorResponseSchema, }, summary: 'List all backups with dates', }, requestBackup: { method: 'POST', path: '/shopkeeper/backup/request', body: z.object({}), responses: { 200: backupMetadataSchema, 400: errorResponseSchema, 401: errorResponseSchema, }, summary: 'Create server-side backup (export shop data and upload to storage)', }, });