1use serde::{Deserialize, Serialize};
2use serde_valid::Validate;
3pub use tetratto_core::model::{ApiReturn, Error, Result};
4use tetratto_shared::{
5 hash::{hash_salted, salt},
6 snow::Snowflake,
7 unix_epoch_timestamp,
8};
9use totp_rs::TOTP;
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
12pub enum ThemePreference {
13 #[default]
14 Auto,
15 Light,
16 Dark,
17}
18
19#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
20pub enum UserPermission {
21 ManageUsers,
23 ManageVerified,
25 ManageAuditLog,
27 ManageReports,
29 ManageMiscAppData,
31 ManageNotifications,
33 Banned,
35 ManageBans,
37 DeleteUsers,
39 ManageWarnings,
41 Premium,
43 PremiumPlus,
45 ManageProperties,
47 ManageOrganizations,
49 ManagePermissionsLists,
51}
52
53#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
54pub enum UserBadge {
55 Staff,
57}
58
59#[derive(Clone, Debug, Serialize, Deserialize, Validate, Default)]
60pub struct UserSettings {
61 #[serde(default)]
62 #[validate(max_length = 32)]
63 pub display_name: String,
64 #[serde(default)]
65 #[validate(max_length = 4096)]
66 pub biography: String,
67 #[serde(default)]
69 #[validate(max_items = 15)]
70 #[validate(unique_items)]
71 pub links: Vec<(String, String)>,
72 #[serde(default)]
74 #[validate(max_items = 5)]
75 #[validate(unique_items)]
76 pub contacts: Vec<(String, String)>,
77 #[serde(default)]
79 pub profile_theme: ThemePreference,
80}
81
82impl UserSettings {
83 pub fn verify_values(&self) -> Result<()> {
84 if let Err(e) = self.validate() {
85 return Err(Error::MiscError(e.to_string()));
86 }
87
88 Ok(())
89 }
90}
91
92#[derive(Clone, Debug, Serialize, Deserialize)]
93pub struct TetrattoLinkedAccount {
94 pub id: usize,
95 pub username: String,
96 pub added: usize,
97}
98
99#[derive(Clone, Debug, Serialize, Deserialize, Default)]
100pub struct UserLinkedAccounts {
101 #[serde(default)]
102 pub tetratto_multiple: Vec<TetrattoLinkedAccount>,
103}
104
105pub type Token = (String, String, usize);
107
108#[derive(Clone, Debug, Serialize, Deserialize)]
109pub struct User {
110 pub id: usize,
111 pub created: usize,
112 pub username: String,
113 pub password: String,
114 pub salt: String,
115 pub settings: UserSettings,
116 pub tokens: Vec<Token>,
117 pub permissions: Vec<UserPermission>,
118 pub is_verified: bool,
119 pub notification_count: usize,
120 #[serde(default)]
122 pub totp: String,
123 #[serde(default)]
125 pub recovery_codes: Vec<String>,
126 #[serde(default)]
128 pub stripe_id: String,
129 #[serde(default)]
131 pub ban_reason: String,
132 #[serde(default)]
134 pub ban_expire: usize,
135 #[serde(default)]
138 pub is_deactivated: bool,
139 #[serde(default)]
144 pub checkouts: Vec<String>,
145 #[serde(default)]
147 pub last_policy_consent: usize,
148 #[serde(default)]
150 pub linked_accounts: UserLinkedAccounts,
151 #[serde(default)]
153 pub badges: Vec<UserBadge>,
154 #[serde(default)]
156 pub principal_org: usize,
157 #[serde(default)]
160 pub org_as_tenant: bool,
161 #[serde(default)]
163 pub org_creation_credits: i32,
164}
165
166impl Default for User {
167 fn default() -> Self {
168 Self::new("<unknown>".to_string(), String::new())
169 }
170}
171
172impl User {
173 pub fn new(username: String, password: String) -> Self {
175 let salt = salt();
176 let password = hash_salted(password, salt.clone());
177 let created = unix_epoch_timestamp();
178
179 Self {
180 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
181 created,
182 username,
183 password,
184 salt,
185 settings: UserSettings::default(),
186 tokens: Vec::new(),
187 permissions: Vec::new(),
188 is_verified: false,
189 notification_count: 0,
190 totp: String::new(),
191 recovery_codes: Vec::new(),
192 stripe_id: String::new(),
193 ban_reason: String::new(),
194 is_deactivated: false,
195 ban_expire: 0,
196 checkouts: Vec::new(),
197 last_policy_consent: created,
198 linked_accounts: UserLinkedAccounts::default(),
199 badges: Vec::new(),
200 principal_org: 0,
201 org_as_tenant: false,
202 org_creation_credits: 0,
203 }
204 }
205
206 pub fn deleted() -> Self {
208 Self {
209 username: "<deleted>".to_string(),
210 id: 0,
211 ..Default::default()
212 }
213 }
214
215 pub fn banned() -> Self {
217 Self {
218 username: "<banned>".to_string(),
219 id: 0,
220 permissions: vec![UserPermission::Banned],
221 ..Default::default()
222 }
223 }
224
225 pub fn create_token(ip: &str) -> (String, Token) {
230 let unhashed = tetratto_shared::hash::uuid();
231 (
232 unhashed.clone(),
233 (
234 ip.to_string(),
235 tetratto_shared::hash::hash(unhashed),
236 unix_epoch_timestamp(),
237 ),
238 )
239 }
240
241 pub fn check_password(&self, against: String) -> bool {
243 self.password == hash_salted(against, self.salt.clone())
244 }
245
246 pub fn totp(&self, issuer: Option<String>) -> Option<TOTP> {
248 if self.totp.is_empty() {
249 return None;
250 }
251
252 TOTP::new(
253 totp_rs::Algorithm::SHA1,
254 6,
255 1,
256 30,
257 self.totp.as_bytes().to_owned(),
258 Some(issuer.unwrap_or("tetratto!".to_string())),
259 self.username.clone(),
260 )
261 .ok()
262 }
263
264 pub fn clean(&mut self) {
266 self.password = String::new();
267 self.salt = String::new();
268
269 self.tokens = Vec::new();
270
271 self.recovery_codes = Vec::new();
272 self.totp = String::new();
273
274 self.settings = UserSettings::default();
275 self.stripe_id = String::new();
276 }
277}
278
279#[derive(Debug, Serialize, Deserialize)]
280pub struct Notification {
281 pub id: usize,
282 pub created: usize,
283 pub title: String,
284 pub content: String,
285 pub owner: usize,
286 pub read: bool,
287 pub tag: String,
288}
289
290impl Notification {
291 pub fn new(title: String, content: String, owner: usize) -> Self {
293 Self {
294 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
295 created: unix_epoch_timestamp(),
296 title,
297 content,
298 owner,
299 read: false,
300 tag: String::new(),
301 }
302 }
303}
304
305#[derive(Serialize, Deserialize)]
306pub struct AuditLogEntry {
307 pub id: usize,
308 pub created: usize,
309 pub moderator: usize,
310 pub content: String,
311}
312
313impl AuditLogEntry {
314 pub fn new(moderator: usize, content: String) -> Self {
316 Self {
317 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
318 created: unix_epoch_timestamp(),
319 moderator,
320 content,
321 }
322 }
323}
324
325#[derive(Serialize, Deserialize)]
326pub struct IpBan {
327 pub ip: String,
328 pub created: usize,
329 pub reason: String,
330 pub moderator: usize,
331}
332
333impl IpBan {
334 pub fn new(ip: String, moderator: usize, reason: String) -> Self {
336 Self {
337 ip,
338 created: unix_epoch_timestamp(),
339 reason,
340 moderator,
341 }
342 }
343}
344
345#[derive(Serialize, Deserialize)]
346pub struct UserWarning {
347 pub id: usize,
348 pub created: usize,
349 pub receiver: usize,
350 pub moderator: usize,
351 pub content: String,
352}
353
354impl UserWarning {
355 pub fn new(user: usize, moderator: usize, content: String) -> Self {
357 Self {
358 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
359 created: unix_epoch_timestamp(),
360 receiver: user,
361 moderator,
362 content,
363 }
364 }
365}
366
367pub mod socket {
369 use serde::{Deserialize, Serialize, de::DeserializeOwned};
370
371 #[derive(Serialize, Deserialize, PartialEq, Eq)]
372 pub enum CrudMessageType {
373 Create,
374 Delete,
375 }
376
377 #[derive(Serialize, Deserialize, PartialEq, Eq)]
378 pub enum PacketType {
379 Ping,
381 Text,
383 Crud(CrudMessageType),
385 Key,
387 }
388
389 #[derive(Serialize, Deserialize, PartialEq, Eq)]
390 pub enum SocketMethod {
391 Headers,
393 Message,
395 Delete,
397 Forward(PacketType),
399 Misc(PacketType),
401 Packet(PacketType),
403 }
404
405 #[derive(Serialize, Deserialize)]
406 pub struct SocketMessage {
407 pub method: SocketMethod,
408 pub data: String,
409 }
410
411 impl SocketMessage {
412 pub fn data<T: DeserializeOwned>(&self) -> T {
413 serde_json::from_str(&self.data).unwrap()
414 }
415 }
416
417 #[derive(Serialize, Deserialize, PartialEq, Eq)]
419 pub struct TextMessage {
420 pub text: String,
421 }
422}
423
424pub mod properties {
426 use serde::{Deserialize, Serialize};
427 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
428
429 #[derive(Serialize, Deserialize, PartialEq, Eq)]
430 pub enum Product {
431 Tetratto,
433 Organization,
437 }
438
439 #[derive(Serialize, Deserialize)]
440 pub struct Property {
441 pub id: usize,
442 pub created: usize,
443 pub owner: usize,
444 pub name: String,
447 pub product: Product,
448 pub custom_domain: String,
449 }
450
451 impl Property {
452 pub fn new(owner: usize, name: String, product: Product) -> Self {
454 Self {
455 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
456 created: unix_epoch_timestamp(),
457 owner,
458 name,
459 product,
460 custom_domain: String::new(),
461 }
462 }
463 }
464}
465
466pub mod organizations {
468 use serde::{Deserialize, Serialize};
469 use std::collections::HashMap;
470 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
471
472 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
473 pub enum OrganizationRole {
474 Owner,
475 Custom(usize),
476 #[default]
477 Member,
478 }
479
480 #[derive(Debug, Clone, Serialize, Deserialize)]
481 pub struct Organization {
482 pub id: usize,
483 pub created: usize,
484 pub owner: usize,
485 pub name: String,
486 pub members: i32,
487 pub roles: HashMap<usize, String>,
488 }
489
490 impl Organization {
491 pub fn new(name: String, owner: usize) -> Self {
493 Self {
494 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
495 created: unix_epoch_timestamp(),
496 owner,
497 name,
498 members: 0,
499 roles: HashMap::new(),
500 }
501 }
502 }
503
504 #[derive(Debug, Clone, Serialize, Deserialize)]
505 pub struct OrganizationMembership {
506 pub id: usize,
507 pub created: usize,
508 pub organization: usize,
509 pub owner: usize,
510 pub role: OrganizationRole,
511 }
512
513 impl OrganizationMembership {
514 pub fn new(organization: usize, owner: usize) -> Self {
516 Self {
517 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
518 created: unix_epoch_timestamp(),
519 organization,
520 owner,
521 role: OrganizationRole::Member,
522 }
523 }
524 }
525
526 #[derive(Debug, Clone, Serialize, Deserialize)]
527 pub struct PermissionsList {
528 pub id: usize,
529 pub created: usize,
530 pub owner_org: usize,
531 pub name: String,
532 pub roles: Vec<usize>,
533 }
534
535 impl PermissionsList {
536 pub fn new(owner_org: usize, name: String) -> Self {
538 Self {
539 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
540 created: unix_epoch_timestamp(),
541 owner_org,
542 name,
543 roles: Vec::new(),
544 }
545 }
546 }
547}