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 #[serde(default)]
166 pub org_user_register_credits: i32,
167}
168
169impl Default for User {
170 fn default() -> Self {
171 Self::new("<unknown>".to_string(), String::new())
172 }
173}
174
175impl User {
176 pub fn new(username: String, password: String) -> Self {
178 let salt = salt();
179 let password = hash_salted(password, salt.clone());
180 let created = unix_epoch_timestamp();
181
182 Self {
183 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
184 created,
185 username,
186 password,
187 salt,
188 settings: UserSettings::default(),
189 tokens: Vec::new(),
190 permissions: Vec::new(),
191 is_verified: false,
192 notification_count: 0,
193 totp: String::new(),
194 recovery_codes: Vec::new(),
195 stripe_id: String::new(),
196 ban_reason: String::new(),
197 is_deactivated: false,
198 ban_expire: 0,
199 checkouts: Vec::new(),
200 last_policy_consent: created,
201 linked_accounts: UserLinkedAccounts::default(),
202 badges: Vec::new(),
203 principal_org: 0,
204 org_as_tenant: false,
205 org_creation_credits: 0,
206 org_user_register_credits: 0,
207 }
208 }
209
210 pub fn deleted() -> Self {
212 Self {
213 username: "<deleted>".to_string(),
214 id: 0,
215 ..Default::default()
216 }
217 }
218
219 pub fn banned() -> Self {
221 Self {
222 username: "<banned>".to_string(),
223 id: 0,
224 permissions: vec![UserPermission::Banned],
225 ..Default::default()
226 }
227 }
228
229 pub fn create_token(ip: &str) -> (String, Token) {
234 let unhashed = tetratto_shared::hash::uuid();
235 (
236 unhashed.clone(),
237 (
238 ip.to_string(),
239 tetratto_shared::hash::hash(unhashed),
240 unix_epoch_timestamp(),
241 ),
242 )
243 }
244
245 pub fn check_password(&self, against: String) -> bool {
247 self.password == hash_salted(against, self.salt.clone())
248 }
249
250 pub fn totp(&self, issuer: Option<String>) -> Option<TOTP> {
252 if self.totp.is_empty() {
253 return None;
254 }
255
256 TOTP::new(
257 totp_rs::Algorithm::SHA1,
258 6,
259 1,
260 30,
261 self.totp.as_bytes().to_owned(),
262 Some(issuer.unwrap_or("tetratto!".to_string())),
263 self.username.clone(),
264 )
265 .ok()
266 }
267
268 pub fn clean(&mut self) {
270 self.password = String::new();
271 self.salt = String::new();
272
273 self.tokens = Vec::new();
274
275 self.recovery_codes = Vec::new();
276 self.totp = String::new();
277
278 self.settings = UserSettings::default();
279 self.stripe_id = String::new();
280 }
281}
282
283#[derive(Debug, Serialize, Deserialize)]
284pub struct Notification {
285 pub id: usize,
286 pub created: usize,
287 pub title: String,
288 pub content: String,
289 pub owner: usize,
290 pub read: bool,
291 pub tag: String,
292}
293
294impl Notification {
295 pub fn new(title: String, content: String, owner: usize) -> Self {
297 Self {
298 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
299 created: unix_epoch_timestamp(),
300 title,
301 content,
302 owner,
303 read: false,
304 tag: String::new(),
305 }
306 }
307}
308
309#[derive(Serialize, Deserialize)]
310pub struct AuditLogEntry {
311 pub id: usize,
312 pub created: usize,
313 pub moderator: usize,
314 pub content: String,
315}
316
317impl AuditLogEntry {
318 pub fn new(moderator: usize, content: String) -> Self {
320 Self {
321 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
322 created: unix_epoch_timestamp(),
323 moderator,
324 content,
325 }
326 }
327}
328
329#[derive(Serialize, Deserialize)]
330pub struct IpBan {
331 pub ip: String,
332 pub created: usize,
333 pub reason: String,
334 pub moderator: usize,
335}
336
337impl IpBan {
338 pub fn new(ip: String, moderator: usize, reason: String) -> Self {
340 Self {
341 ip,
342 created: unix_epoch_timestamp(),
343 reason,
344 moderator,
345 }
346 }
347}
348
349#[derive(Serialize, Deserialize)]
350pub struct UserWarning {
351 pub id: usize,
352 pub created: usize,
353 pub receiver: usize,
354 pub moderator: usize,
355 pub content: String,
356}
357
358impl UserWarning {
359 pub fn new(user: usize, moderator: usize, content: String) -> Self {
361 Self {
362 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
363 created: unix_epoch_timestamp(),
364 receiver: user,
365 moderator,
366 content,
367 }
368 }
369}
370
371pub mod socket {
373 use serde::{Deserialize, Serialize, de::DeserializeOwned};
374
375 #[derive(Serialize, Deserialize, PartialEq, Eq)]
376 pub enum CrudMessageType {
377 Create,
378 Delete,
379 }
380
381 #[derive(Serialize, Deserialize, PartialEq, Eq)]
382 pub enum PacketType {
383 Ping,
385 Text,
387 Crud(CrudMessageType),
389 Key,
391 }
392
393 #[derive(Serialize, Deserialize, PartialEq, Eq)]
394 pub enum SocketMethod {
395 Headers,
397 Message,
399 Delete,
401 Forward(PacketType),
403 Misc(PacketType),
405 Packet(PacketType),
407 }
408
409 #[derive(Serialize, Deserialize)]
410 pub struct SocketMessage {
411 pub method: SocketMethod,
412 pub data: String,
413 }
414
415 impl SocketMessage {
416 pub fn data<T: DeserializeOwned>(&self) -> T {
417 serde_json::from_str(&self.data).unwrap()
418 }
419 }
420
421 #[derive(Serialize, Deserialize, PartialEq, Eq)]
423 pub struct TextMessage {
424 pub text: String,
425 }
426}
427
428pub mod properties {
430 use serde::{Deserialize, Serialize};
431 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
432
433 #[derive(Serialize, Deserialize, PartialEq, Eq)]
434 pub enum Product {
435 Tetratto,
437 Organization,
441 UserReg,
445 }
446
447 #[derive(Serialize, Deserialize)]
448 pub struct Property {
449 pub id: usize,
450 pub created: usize,
451 pub owner: usize,
452 pub name: String,
455 pub product: Product,
456 pub custom_domain: String,
457 }
458
459 impl Property {
460 pub fn new(owner: usize, name: String, product: Product) -> Self {
462 Self {
463 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
464 created: unix_epoch_timestamp(),
465 owner,
466 name,
467 product,
468 custom_domain: String::new(),
469 }
470 }
471 }
472}
473
474pub mod organizations {
476 use serde::{Deserialize, Serialize};
477 use std::collections::HashMap;
478 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
479
480 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
481 pub enum OrganizationRole {
482 Owner,
483 Custom(usize),
484 #[default]
485 Member,
486 }
487
488 #[derive(Debug, Clone, Serialize, Deserialize)]
489 pub struct Organization {
490 pub id: usize,
491 pub created: usize,
492 pub owner: usize,
493 pub name: String,
494 pub members: i32,
495 pub roles: HashMap<usize, String>,
496 }
497
498 impl Organization {
499 pub fn new(name: String, owner: usize) -> Self {
501 Self {
502 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
503 created: unix_epoch_timestamp(),
504 owner,
505 name,
506 members: 0,
507 roles: HashMap::new(),
508 }
509 }
510 }
511
512 #[derive(Debug, Clone, Serialize, Deserialize)]
513 pub struct OrganizationMembership {
514 pub id: usize,
515 pub created: usize,
516 pub organization: usize,
517 pub owner: usize,
518 pub role: OrganizationRole,
519 }
520
521 impl OrganizationMembership {
522 pub fn new(organization: usize, owner: usize) -> Self {
524 Self {
525 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
526 created: unix_epoch_timestamp(),
527 organization,
528 owner,
529 role: OrganizationRole::Member,
530 }
531 }
532 }
533
534 #[derive(Debug, Clone, Serialize, Deserialize)]
535 pub struct PermissionsList {
536 pub id: usize,
537 pub created: usize,
538 pub owner_org: usize,
539 pub name: String,
540 pub roles: Vec<usize>,
541 }
542
543 impl PermissionsList {
544 pub fn new(owner_org: usize, name: String) -> Self {
546 Self {
547 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
548 created: unix_epoch_timestamp(),
549 owner_org,
550 name,
551 roles: Vec::new(),
552 }
553 }
554 }
555}