Skip to main content

max / makenotwork

7.3 KB · 199 lines History Blame Raw
1 //! SyncKit app, device, log, blob, and OTA models.
2
3 use chrono::{DateTime, Utc};
4 use serde::Serialize;
5 use sqlx::FromRow;
6
7 use super::super::id_types::*;
8
9 /// A registered sync app with a hashed API key.
10 #[derive(Debug, Clone, FromRow, Serialize)]
11 pub struct DbSyncApp {
12 /// Database primary key.
13 pub id: SyncAppId,
14 /// User who created and owns this app.
15 pub creator_id: UserId,
16 /// Human-readable app name (e.g. "GoingsOn", "AudioFiles").
17 pub name: String,
18 /// SHA-256 hash of the API key (hex-encoded). The plaintext key is never stored.
19 pub api_key_hash: String,
20 /// First 8 hex chars of the original API key, for display only.
21 pub api_key_prefix: String,
22 /// Whether this app is active and can accept sync requests.
23 pub is_active: bool,
24 /// When the app was created.
25 pub created_at: DateTime<Utc>,
26 /// Optional link to an MNW project for dashboard grouping.
27 pub project_id: Option<ProjectId>,
28 /// Optional link to an MNW item for dashboard grouping.
29 pub item_id: Option<ItemId>,
30 /// URL-friendly slug for OTA update endpoints.
31 pub slug: Option<String>,
32 }
33
34 /// A device registered for sync per user per app.
35 #[derive(Debug, Clone, FromRow, Serialize)]
36 pub struct DbSyncDevice {
37 /// Database primary key.
38 pub id: SyncDeviceId,
39 /// Parent sync app this device belongs to.
40 pub app_id: SyncAppId,
41 /// User who owns this device registration.
42 pub user_id: UserId,
43 /// Human-readable device name (e.g. "Max's MacBook Pro").
44 pub device_name: String,
45 /// Operating system / platform (macos, windows, linux, ios, android).
46 pub platform: super::super::SyncPlatform,
47 /// Updated on each push or pull to track device activity.
48 pub last_seen_at: DateTime<Utc>,
49 /// When this device was first registered.
50 pub created_at: DateTime<Utc>,
51 }
52
53 /// An entry in the append-only sync change log.
54 #[derive(Debug, Clone, FromRow, Serialize)]
55 pub struct DbSyncLogEntry {
56 /// Server-assigned monotonic sequence number, used as a cursor for pull.
57 pub seq: i64,
58 /// Sync app this entry belongs to.
59 pub app_id: SyncAppId,
60 /// User who pushed this change.
61 pub user_id: UserId,
62 /// Device that originated this change.
63 pub device_id: SyncDeviceId,
64 /// Opaque table name from the client (e.g. "tasks", "contacts").
65 pub table_name: String,
66 /// Type of change (insert, update, or delete).
67 pub operation: super::super::SyncOperation,
68 /// Client-side row identifier (opaque to the server).
69 pub row_id: String,
70 /// Timestamp assigned by the client when the change was made.
71 pub client_timestamp: DateTime<Utc>,
72 /// Encrypted row data (JSON blob). Null for delete operations.
73 pub data: Option<serde_json::Value>,
74 /// When the server received and recorded this entry.
75 pub created_at: DateTime<Utc>,
76 /// Which encryption key was used. NULL means key_id 1 (pre-rotation entries).
77 pub key_id: Option<i32>,
78 }
79
80 /// An in-progress key rotation for a user within a sync app.
81 #[derive(Debug, Clone, FromRow)]
82 pub struct DbSyncKeyRotation {
83 pub id: uuid::Uuid,
84 pub app_id: SyncAppId,
85 pub user_id: UserId,
86 pub device_id: SyncDeviceId,
87 pub new_encrypted_key: String,
88 pub old_key_version: i32,
89 pub new_key_id: i32,
90 pub re_encrypted_through_seq: i64,
91 pub target_seq: i64,
92 pub created_at: DateTime<Utc>,
93 pub updated_at: DateTime<Utc>,
94 }
95
96 /// A blob uploaded to S3 via SyncKit, tracked for dedup and cleanup.
97 #[derive(Debug, Clone, FromRow, Serialize)]
98 pub struct DbSyncBlob {
99 /// Database primary key.
100 pub id: SyncBlobId,
101 /// Sync app this blob belongs to.
102 pub app_id: SyncAppId,
103 /// User who uploaded this blob.
104 pub user_id: UserId,
105 /// Content-address hash provided by the client (used for deduplication).
106 pub hash: String,
107 /// S3 object key where the blob is stored (`{app_id}/{user_id}/{hash}`).
108 pub s3_key: String,
109 /// Size of the blob in bytes.
110 pub size_bytes: i64,
111 /// Developer-defined SDK key this blob was uploaded under. Used to
112 /// attribute storage against the right per-key counter (per_key mode).
113 pub key: String,
114 /// When the blob upload was confirmed.
115 pub uploaded_at: DateTime<Utc>,
116 }
117
118 /// Sync app + billing columns + live usage counters (joined view).
119 ///
120 /// Mirrors columns added in migration 117 (`117_synckit_v2_billing.sql`).
121 /// 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
122 /// missing).
123 #[derive(Debug, Clone, FromRow, Serialize)]
124 pub struct DbSyncAppBilling {
125 // sync_apps base
126 pub id: SyncAppId,
127 pub creator_id: UserId,
128 pub name: String,
129 /// First-party app; bypasses all billing logic.
130 pub is_internal: bool,
131 /// Stripe Customer ID for this app's developer (one customer per app).
132 pub stripe_customer_id: Option<String>,
133 /// Stripe Subscription ID; set once billing activates.
134 pub stripe_subscription_id: Option<String>,
135 /// 'draft' | 'active' | 'suspended_unpaid' | 'canceled'
136 pub billing_status: String,
137 /// Storage cap in GB. Set in bulk mode; NULL in per_key mode (capacity is
138 /// derived from `key_cap × gb_per_key`) and in draft.
139 pub storage_gb_cap: Option<i32>,
140 /// `'per_key'` | `'bulk'`. Drives both pricing and degradation behavior.
141 pub enforcement_mode: String,
142 /// Max active keys. Set in per_key mode; NULL in bulk mode.
143 pub key_cap: Option<i32>,
144 /// GB allotment per active key (per_key mode only). Total storage
145 /// capacity = `key_cap × gb_per_key`.
146 pub gb_per_key: Option<i32>,
147 pub current_period_start: Option<DateTime<Utc>>,
148 pub current_period_end: Option<DateTime<Utc>>,
149 // sync_app_usage_current (LEFT-joined, may be missing if row absent)
150 pub bytes_stored: Option<i64>,
151 pub bytes_egress_period: Option<i64>,
152 pub keys_claimed: Option<i32>,
153 pub last_warning_pct: Option<i16>,
154 pub period_started_at: Option<DateTime<Utc>>,
155 // projects (LEFT-joined). Some when the app is linked to a project — used
156 // to route the Stripe billing portal back to the project dashboard.
157 pub project_slug: Option<String>,
158 }
159
160 /// A single active key claim (row in `sync_app_keys` with `released_at IS NULL`).
161 ///
162 /// See migration 117 for the full table definition. This is the projection
163 /// used by the dashboard "Active keys" list; only the columns the UI needs.
164 #[derive(Debug, Clone, FromRow, Serialize)]
165 pub struct DbSyncAppKey {
166 pub id: uuid::Uuid,
167 pub key: String,
168 pub claimed_at: DateTime<Utc>,
169 /// Bytes stored under this key (LEFT JOIN against
170 /// `sync_key_usage_current` — `0` when no upload has landed yet).
171 pub bytes_stored: i64,
172 }
173
174 // ── OTA models ──
175
176 /// An OTA release for a sync app.
177 #[derive(Debug, Clone, FromRow)]
178 pub struct DbOtaRelease {
179 pub id: OtaReleaseId,
180 pub app_id: SyncAppId,
181 pub version: String,
182 pub notes: String,
183 pub signature: String,
184 pub pub_date: DateTime<Utc>,
185 pub created_at: DateTime<Utc>,
186 }
187
188 /// An artifact (platform-specific binary) within an OTA release.
189 #[derive(Debug, Clone, FromRow)]
190 pub struct DbOtaArtifact {
191 pub id: OtaArtifactId,
192 pub release_id: OtaReleaseId,
193 pub target: String,
194 pub arch: String,
195 pub s3_key: String,
196 pub file_size: i64,
197 pub created_at: DateTime<Utc>,
198 }
199