//! SyncKit app, device, log, blob, and OTA models. use chrono::{DateTime, Utc}; use serde::Serialize; use sqlx::FromRow; use super::super::id_types::*; /// A registered sync app with a hashed API key. #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncApp { /// Database primary key. pub id: SyncAppId, /// User who created and owns this app. pub creator_id: UserId, /// Human-readable app name (e.g. "GoingsOn", "AudioFiles"). pub name: String, /// SHA-256 hash of the API key (hex-encoded). The plaintext key is never stored. pub api_key_hash: String, /// First 8 hex chars of the original API key, for display only. pub api_key_prefix: String, /// Whether this app is active and can accept sync requests. pub is_active: bool, /// When the app was created. pub created_at: DateTime, /// Optional link to an MNW project for dashboard grouping. pub project_id: Option, /// Optional link to an MNW item for dashboard grouping. pub item_id: Option, /// URL-friendly slug for OTA update endpoints. pub slug: Option, } /// A device registered for sync per user per app. #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncDevice { /// Database primary key. pub id: SyncDeviceId, /// Parent sync app this device belongs to. pub app_id: SyncAppId, /// User who owns this device registration. pub user_id: UserId, /// Human-readable device name (e.g. "Max's MacBook Pro"). pub device_name: String, /// Operating system / platform (macos, windows, linux, ios, android). pub platform: super::super::SyncPlatform, /// Updated on each push or pull to track device activity. pub last_seen_at: DateTime, /// When this device was first registered. pub created_at: DateTime, } /// An entry in the append-only sync change log. #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncLogEntry { /// Server-assigned monotonic sequence number, used as a cursor for pull. pub seq: i64, /// Sync app this entry belongs to. pub app_id: SyncAppId, /// User who pushed this change. pub user_id: UserId, /// Device that originated this change. pub device_id: SyncDeviceId, /// Opaque table name from the client (e.g. "tasks", "contacts"). pub table_name: String, /// Type of change (insert, update, or delete). pub operation: super::super::SyncOperation, /// Client-side row identifier (opaque to the server). pub row_id: String, /// Timestamp assigned by the client when the change was made. pub client_timestamp: DateTime, /// Encrypted row data (JSON blob). Null for delete operations. pub data: Option, /// When the server received and recorded this entry. pub created_at: DateTime, /// Which encryption key was used. NULL means key_id 1 (pre-rotation entries). pub key_id: Option, } /// An in-progress key rotation for a user within a sync app. #[derive(Debug, Clone, FromRow)] pub struct DbSyncKeyRotation { pub id: uuid::Uuid, pub app_id: SyncAppId, pub user_id: UserId, pub device_id: SyncDeviceId, pub new_encrypted_key: String, pub old_key_version: i32, pub new_key_id: i32, pub re_encrypted_through_seq: i64, pub target_seq: i64, pub created_at: DateTime, pub updated_at: DateTime, } /// A blob uploaded to S3 via SyncKit, tracked for dedup and cleanup. #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncBlob { /// Database primary key. pub id: SyncBlobId, /// Sync app this blob belongs to. pub app_id: SyncAppId, /// User who uploaded this blob. pub user_id: UserId, /// Content-address hash provided by the client (used for deduplication). pub hash: String, /// S3 object key where the blob is stored (`{app_id}/{user_id}/{hash}`). pub s3_key: String, /// Size of the blob in bytes. pub size_bytes: i64, /// Developer-defined SDK key this blob was uploaded under. Used to /// attribute storage against the right per-key counter (per_key mode). pub key: String, /// When the blob upload was confirmed. pub uploaded_at: DateTime, } /// Sync app + billing columns + live usage counters (joined view). /// /// Mirrors columns added in migration 117 (`117_synckit_v2_billing.sql`). /// Built by joining `sync_apps` against `sync_app_usage_current` (LEFT JOIN; /// the usage row is inserted on app create, but guards against the row going /// missing). #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncAppBilling { // sync_apps base pub id: SyncAppId, pub creator_id: UserId, pub name: String, /// First-party app; bypasses all billing logic. pub is_internal: bool, /// Stripe Customer ID for this app's developer (one customer per app). pub stripe_customer_id: Option, /// Stripe Subscription ID; set once billing activates. pub stripe_subscription_id: Option, /// 'draft' | 'active' | 'suspended_unpaid' | 'canceled' pub billing_status: String, /// Storage cap in GB. Set in bulk mode; NULL in per_key mode (capacity is /// derived from `key_cap × gb_per_key`) and in draft. pub storage_gb_cap: Option, /// `'per_key'` | `'bulk'`. Drives both pricing and degradation behavior. pub enforcement_mode: String, /// Max active keys. Set in per_key mode; NULL in bulk mode. pub key_cap: Option, /// GB allotment per active key (per_key mode only). Total storage /// capacity = `key_cap × gb_per_key`. pub gb_per_key: Option, pub current_period_start: Option>, pub current_period_end: Option>, // sync_app_usage_current (LEFT-joined, may be missing if row absent) pub bytes_stored: Option, pub bytes_egress_period: Option, pub keys_claimed: Option, pub last_warning_pct: Option, pub period_started_at: Option>, // projects (LEFT-joined). Some when the app is linked to a project — used // to route the Stripe billing portal back to the project dashboard. pub project_slug: Option, } /// A single active key claim (row in `sync_app_keys` with `released_at IS NULL`). /// /// See migration 117 for the full table definition. This is the projection /// used by the dashboard "Active keys" list; only the columns the UI needs. #[derive(Debug, Clone, FromRow, Serialize)] pub struct DbSyncAppKey { pub id: uuid::Uuid, pub key: String, pub claimed_at: DateTime, /// Bytes stored under this key (LEFT JOIN against /// `sync_key_usage_current` — `0` when no upload has landed yet). pub bytes_stored: i64, } // ── OTA models ── /// An OTA release for a sync app. #[derive(Debug, Clone, FromRow)] pub struct DbOtaRelease { pub id: OtaReleaseId, pub app_id: SyncAppId, pub version: String, pub notes: String, pub signature: String, pub pub_date: DateTime, pub created_at: DateTime, } /// An artifact (platform-specific binary) within an OTA release. #[derive(Debug, Clone, FromRow)] pub struct DbOtaArtifact { pub id: OtaArtifactId, pub release_id: OtaReleaseId, pub target: String, pub arch: String, pub s3_key: String, pub file_size: i64, pub created_at: DateTime, }