Skip to main content

chamber_api/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[derive(Debug, Serialize, Deserialize)]
6pub struct ApiResponse<T> {
7    pub data: T,
8    #[serde(skip_serializing_if = "Option::is_none")]
9    pub meta: Option<serde_json::Value>,
10}
11
12impl<T> ApiResponse<T> {
13    pub const fn new(data: T) -> Self {
14        Self { data, meta: None }
15    }
16
17    pub const fn with_meta(data: T, meta: serde_json::Value) -> Self {
18        Self { data, meta: Some(meta) }
19    }
20}
21
22#[derive(Debug, Serialize, Deserialize)]
23pub struct ItemResponse {
24    pub id: u64,
25    pub name: String,
26    pub kind: String,
27    pub created_at: DateTime<Utc>,
28    pub updated_at: DateTime<Utc>,
29    pub has_value: bool,
30    pub value_length: usize,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub preview: Option<String>,
33}
34
35#[derive(Debug, Serialize, Deserialize)]
36pub struct ItemWithValueResponse {
37    pub id: u64,
38    pub name: String,
39    pub kind: String,
40    pub value: String,
41    pub created_at: DateTime<Utc>,
42    pub updated_at: DateTime<Utc>,
43}
44
45#[derive(Debug, Deserialize, Serialize)]
46pub struct CreateItemRequest {
47    pub name: String,
48    pub kind: String,
49    pub value: String,
50}
51
52#[derive(Debug, Serialize, Deserialize)]
53pub struct UpdateItemRequest {
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub name: Option<String>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub kind: Option<String>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub value: Option<String>,
60}
61
62#[derive(Debug, Deserialize, Serialize)]
63pub struct LoginRequest {
64    pub master_password: String,
65}
66
67#[derive(Debug, Serialize, Deserialize)]
68pub struct LoginResponse {
69    pub token: String,
70    pub expires_at: DateTime<Utc>,
71    pub scopes: Vec<String>,
72}
73
74#[allow(clippy::struct_excessive_bools)]
75#[derive(Debug, Deserialize, Serialize)]
76pub struct GeneratePasswordRequest {
77    #[serde(default = "default_length")]
78    pub length: usize,
79    #[serde(default = "default_true")]
80    pub include_uppercase: bool,
81    #[serde(default = "default_true")]
82    pub include_lowercase: bool,
83    #[serde(default = "default_true")]
84    pub include_digits: bool,
85    #[serde(default = "default_true")]
86    pub include_symbols: bool,
87    #[serde(default = "default_true")]
88    pub exclude_ambiguous: bool,
89}
90
91#[derive(Debug, Serialize, Deserialize)]
92pub struct PasswordResponse {
93    pub password: String,
94    pub strength: String,
95}
96
97#[derive(Debug, Serialize, Deserialize)]
98pub struct HealthResponse {
99    pub status: String,
100    pub version: String,
101    pub vault_status: String,
102}
103
104#[derive(Debug, Serialize, Deserialize)]
105pub struct CountsResponse {
106    pub total: usize,
107    pub by_kind: std::collections::HashMap<String, usize>,
108}
109
110#[derive(Debug, Deserialize)]
111pub struct QueryParams {
112    #[serde(default)]
113    pub query: Option<String>,
114    #[serde(default)]
115    pub kind: Option<String>,
116    #[serde(default = "default_limit")]
117    pub limit: usize,
118    #[serde(default)]
119    pub offset: usize,
120    #[serde(default)]
121    pub sort: Option<String>,
122    #[serde(default)]
123    pub order: Option<String>,
124}
125
126const fn default_length() -> usize {
127    16
128}
129const fn default_true() -> bool {
130    true
131}
132const fn default_limit() -> usize {
133    50
134}
135
136#[derive(Debug, Serialize, Deserialize)]
137pub struct HealthReportResponse {
138    pub weak_passwords: Vec<String>,
139    pub reused_passwords: Vec<ReusedPasswordGroup>,
140    pub old_passwords: Vec<OldPasswordItem>,
141    pub short_passwords: Vec<String>,
142    pub common_passwords: Vec<String>,
143    pub total_items: usize,
144    pub password_items: usize,
145    pub security_score: f32,
146}
147
148#[derive(Debug, Serialize, Deserialize)]
149pub struct ReusedPasswordGroup {
150    pub password_hash: String,
151    pub item_names: Vec<String>,
152}
153
154#[derive(Debug, Serialize, Deserialize)]
155pub struct OldPasswordItem {
156    pub item_name: String,
157    pub days_old: i64,
158}
159
160#[derive(Debug, Serialize, Deserialize)]
161pub struct StatsResponse {
162    pub total_items: usize,
163    pub password_items: usize,
164    pub note_items: usize,
165    pub card_items: usize,
166    pub other_items: usize,
167    pub vault_size_bytes: u64,
168    pub oldest_item_age_days: Option<i64>,
169    pub newest_item_age_days: Option<i64>,
170    pub average_password_length: Option<f32>,
171}
172
173#[derive(Debug, Deserialize)]
174pub struct SearchParams {
175    #[serde(default)]
176    pub q: Option<String>, // Search query (matches name, kind, or value preview)
177    #[serde(default)]
178    pub query: Option<String>, // Alias for 'q' for compatibility
179    #[serde(default)]
180    pub kind: Option<String>, // Filter by item type
181    #[serde(default)]
182    pub name: Option<String>, // Search in item names only
183    #[serde(default = "default_limit")]
184    pub limit: usize, // Maximum results to return
185    #[serde(default)]
186    pub offset: usize, // Pagination offset
187    #[serde(default)]
188    pub sort: Option<String>, // Sort field (name, created_at, updated_at, kind)
189    #[serde(default)]
190    pub order: Option<String>, // Sort order (asc, desc)
191    #[serde(default)]
192    pub fuzzy: Option<bool>, // Enable fuzzy matching
193    #[serde(default)]
194    pub case_sensitive: Option<bool>, // Case sensitive search
195}
196
197#[derive(Debug, Serialize, Deserialize)]
198pub struct SearchResponse {
199    pub items: Vec<ItemResponse>,
200    pub total_found: usize,
201    pub total_available: usize,
202    pub query_time_ms: u64,
203    pub has_more: bool,
204    pub next_offset: Option<usize>,
205}
206
207#[derive(Debug, Deserialize)]
208pub struct CreateVaultRequest {
209    pub name: String,
210    pub description: Option<String>,
211    pub category: Option<String>,
212    pub master_password: String,
213    pub path: Option<PathBuf>,
214}
215
216#[derive(Debug, Deserialize)]
217pub struct UpdateVaultRequest {
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub name: Option<String>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub description: Option<String>,
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub category: Option<String>,
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub favorite: Option<bool>,
226}
227
228#[derive(Debug, Deserialize)]
229pub struct SwitchVaultRequest {
230    pub master_password: String,
231}
232
233#[derive(Debug, Deserialize)]
234pub struct DeleteVaultRequest {
235    pub confirm_name: String,
236    pub master_password: String,
237}
238
239#[derive(Debug, Serialize)]
240pub struct VaultInfo {
241    pub id: String,
242    pub name: String,
243    pub description: Option<String>,
244    pub category: String,
245    pub path: String,
246    pub is_favorite: bool,
247    pub created_at: DateTime<Utc>,
248    pub updated_at: DateTime<Utc>,
249    pub item_count: usize,
250    pub is_current: bool,
251}
252
253#[derive(Debug, Serialize)]
254pub struct VaultListResponse {
255    pub vaults: Vec<VaultInfo>,
256    pub current_vault_id: Option<String>,
257    pub total: usize,
258}
259
260#[derive(Debug, Serialize)]
261pub struct VaultOperationResponse {
262    pub success: bool,
263    pub message: String,
264    pub vault_id: Option<String>,
265}
266
267#[derive(Debug, Serialize, Deserialize)]
268pub struct ListItemsResponse {
269    pub items: Vec<ItemResponse>,
270    pub total: usize,
271}