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>, #[serde(default)]
178 pub query: Option<String>, #[serde(default)]
180 pub kind: Option<String>, #[serde(default)]
182 pub name: Option<String>, #[serde(default = "default_limit")]
184 pub limit: usize, #[serde(default)]
186 pub offset: usize, #[serde(default)]
188 pub sort: Option<String>, #[serde(default)]
190 pub order: Option<String>, #[serde(default)]
192 pub fuzzy: Option<bool>, #[serde(default)]
194 pub case_sensitive: Option<bool>, }
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}