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 Seedling,
43 SeedlingPlus,
45 ManageProperties,
47 ManageOrganizations,
49 ManagePermissionsLists,
51 Administrator,
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
56pub enum UserBadge {
57 Staff,
59}
60
61#[derive(Clone, Debug, Serialize, Deserialize, Validate, Default)]
62pub struct UserSettings {
63 #[serde(default)]
64 #[validate(max_length = 32)]
65 pub display_name: String,
66 #[serde(default)]
67 #[validate(max_length = 4096)]
68 pub biography: String,
69 #[serde(default)]
71 #[validate(max_items = 15)]
72 #[validate(unique_items)]
73 pub links: Vec<(String, String)>,
74 #[serde(default)]
76 #[validate(max_items = 5)]
77 #[validate(unique_items)]
78 pub contacts: Vec<(String, String)>,
79 #[serde(default)]
81 pub profile_theme: ThemePreference,
82}
83
84impl UserSettings {
85 pub fn verify_values(&self) -> Result<()> {
86 if let Err(e) = self.validate() {
87 return Err(Error::MiscError(e.to_string()));
88 }
89
90 Ok(())
91 }
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize)]
95pub struct TetrattoLinkedAccount {
96 pub id: usize,
97 pub username: String,
98 pub added: usize,
99}
100
101#[derive(Clone, Debug, Serialize, Deserialize, Default)]
102pub struct UserLinkedAccounts {
103 #[serde(default)]
104 pub tetratto_multiple: Vec<TetrattoLinkedAccount>,
105}
106
107pub type Token = (String, String, usize);
109
110#[derive(Clone, Debug, Serialize, Deserialize)]
111pub struct User {
112 pub id: usize,
113 pub created: usize,
114 pub username: String,
115 pub password: String,
116 pub salt: String,
117 pub settings: UserSettings,
118 pub tokens: Vec<Token>,
119 pub permissions: Vec<UserPermission>,
120 pub is_verified: bool,
121 pub notification_count: usize,
122 #[serde(default)]
124 pub totp: String,
125 #[serde(default)]
127 pub recovery_codes: Vec<String>,
128 #[serde(default)]
130 pub stripe_id: String,
131 #[serde(default)]
133 pub ban_reason: String,
134 #[serde(default)]
136 pub ban_expire: usize,
137 #[serde(default)]
140 pub is_deactivated: bool,
141 #[serde(default)]
146 pub checkouts: Vec<String>,
147 #[serde(default)]
149 pub last_policy_consent: usize,
150 #[serde(default)]
152 pub linked_accounts: UserLinkedAccounts,
153 #[serde(default)]
155 pub badges: Vec<UserBadge>,
156 #[serde(default)]
158 pub principal_org: usize,
159 #[serde(default)]
162 pub org_as_tenant: bool,
163 #[serde(default)]
165 pub org_creation_credits: i32,
166 #[serde(default)]
168 pub org_user_register_credits: i32,
169 #[serde(default)]
173 pub is_super_verified: bool,
174}
175
176impl Default for User {
177 fn default() -> Self {
178 Self::new("<unknown>".to_string(), String::new())
179 }
180}
181
182impl User {
183 pub fn new(username: String, password: String) -> Self {
185 let salt = salt();
186 let password = hash_salted(password, salt.clone());
187 let created = unix_epoch_timestamp();
188
189 Self {
190 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
191 created,
192 username,
193 password,
194 salt,
195 settings: UserSettings::default(),
196 tokens: Vec::new(),
197 permissions: Vec::new(),
198 is_verified: false,
199 notification_count: 0,
200 totp: String::new(),
201 recovery_codes: Vec::new(),
202 stripe_id: String::new(),
203 ban_reason: String::new(),
204 is_deactivated: false,
205 ban_expire: 0,
206 checkouts: Vec::new(),
207 last_policy_consent: created,
208 linked_accounts: UserLinkedAccounts::default(),
209 badges: Vec::new(),
210 principal_org: 0,
211 org_as_tenant: false,
212 org_creation_credits: 0,
213 org_user_register_credits: 0,
214 is_super_verified: false,
215 }
216 }
217
218 pub fn deleted() -> Self {
220 Self {
221 username: "<deleted>".to_string(),
222 id: 0,
223 ..Default::default()
224 }
225 }
226
227 pub fn banned() -> Self {
229 Self {
230 username: "<banned>".to_string(),
231 id: 0,
232 permissions: vec![UserPermission::Banned],
233 ..Default::default()
234 }
235 }
236
237 pub fn create_token(ip: &str) -> (String, Token) {
242 let unhashed = tetratto_shared::hash::uuid();
243 (
244 unhashed.clone(),
245 (
246 ip.to_string(),
247 tetratto_shared::hash::hash(unhashed),
248 unix_epoch_timestamp(),
249 ),
250 )
251 }
252
253 pub fn check_password(&self, against: String) -> bool {
255 self.password == hash_salted(against, self.salt.clone())
256 }
257
258 pub fn totp(&self, issuer: Option<String>) -> Option<TOTP> {
260 if self.totp.is_empty() {
261 return None;
262 }
263
264 TOTP::new(
265 totp_rs::Algorithm::SHA1,
266 6,
267 1,
268 30,
269 self.totp.as_bytes().to_owned(),
270 Some(issuer.unwrap_or("tetratto!".to_string())),
271 self.username.clone(),
272 )
273 .ok()
274 }
275
276 pub fn clean(&mut self) {
278 self.password = String::new();
279 self.salt = String::new();
280
281 self.tokens = Vec::new();
282
283 self.recovery_codes = Vec::new();
284 self.totp = String::new();
285
286 self.settings = UserSettings::default();
287 self.stripe_id = String::new();
288 }
289}
290
291#[derive(Debug, Serialize, Deserialize)]
292pub struct Notification {
293 pub id: usize,
294 pub created: usize,
295 pub title: String,
296 pub content: String,
297 pub owner: usize,
298 pub read: bool,
299 pub tag: String,
300}
301
302impl Notification {
303 pub fn new(title: String, content: String, owner: usize) -> Self {
305 Self {
306 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
307 created: unix_epoch_timestamp(),
308 title,
309 content,
310 owner,
311 read: false,
312 tag: String::new(),
313 }
314 }
315}
316
317#[derive(Serialize, Deserialize)]
318pub struct AuditLogEntry {
319 pub id: usize,
320 pub created: usize,
321 pub moderator: usize,
322 pub content: String,
323}
324
325impl AuditLogEntry {
326 pub fn new(moderator: usize, content: String) -> Self {
328 Self {
329 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
330 created: unix_epoch_timestamp(),
331 moderator,
332 content,
333 }
334 }
335}
336
337#[derive(Serialize, Deserialize)]
338pub struct IpBan {
339 pub ip: String,
340 pub created: usize,
341 pub reason: String,
342 pub moderator: usize,
343}
344
345impl IpBan {
346 pub fn new(ip: String, moderator: usize, reason: String) -> Self {
348 Self {
349 ip,
350 created: unix_epoch_timestamp(),
351 reason,
352 moderator,
353 }
354 }
355}
356
357#[derive(Serialize, Deserialize)]
358pub struct UserWarning {
359 pub id: usize,
360 pub created: usize,
361 pub receiver: usize,
362 pub moderator: usize,
363 pub content: String,
364}
365
366impl UserWarning {
367 pub fn new(user: usize, moderator: usize, content: String) -> Self {
369 Self {
370 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
371 created: unix_epoch_timestamp(),
372 receiver: user,
373 moderator,
374 content,
375 }
376 }
377}
378
379pub mod socket {
381 use serde::{Deserialize, Serialize, de::DeserializeOwned};
382
383 #[derive(Serialize, Deserialize, PartialEq, Eq)]
384 pub enum CrudMessageType {
385 Create,
386 Delete,
387 }
388
389 #[derive(Serialize, Deserialize, PartialEq, Eq)]
390 pub enum PacketType {
391 Ping,
393 Text,
395 Crud(CrudMessageType),
397 Key,
399 }
400
401 #[derive(Serialize, Deserialize, PartialEq, Eq)]
402 pub enum SocketMethod {
403 Headers,
405 Message,
407 Delete,
409 Forward(PacketType),
411 Misc(PacketType),
413 Packet(PacketType),
415 }
416
417 #[derive(Serialize, Deserialize)]
418 pub struct SocketMessage {
419 pub method: SocketMethod,
420 pub data: String,
421 }
422
423 impl SocketMessage {
424 pub fn data<T: DeserializeOwned>(&self) -> T {
425 serde_json::from_str(&self.data).unwrap()
426 }
427 }
428
429 #[derive(Serialize, Deserialize, PartialEq, Eq)]
431 pub struct TextMessage {
432 pub text: String,
433 }
434}
435
436pub mod properties {
438 use serde::{Deserialize, Serialize};
439 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
440
441 #[derive(Serialize, Deserialize, PartialEq, Eq)]
442 pub enum Product {
443 Tetratto,
445 Organization,
449 UserReg,
453 Seedling,
457 }
458
459 #[derive(Serialize, Deserialize)]
460 pub struct Property {
461 pub id: usize,
462 pub created: usize,
463 pub owner: usize,
464 pub name: String,
467 pub product: Product,
468 pub custom_domain: String,
469 }
470
471 impl Property {
472 pub fn new(owner: usize, name: String, product: Product) -> Self {
474 Self {
475 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
476 created: unix_epoch_timestamp(),
477 owner,
478 name,
479 product,
480 custom_domain: String::new(),
481 }
482 }
483 }
484}
485
486pub mod organizations {
488 use serde::{Deserialize, Serialize};
489 use std::collections::HashMap;
490 use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
491
492 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
493 pub enum OrganizationRole {
494 Owner,
495 Custom(usize),
496 #[default]
497 Member,
498 }
499
500 #[derive(Debug, Clone, Serialize, Deserialize)]
501 pub struct Organization {
502 pub id: usize,
503 pub created: usize,
504 pub owner: usize,
505 pub name: String,
506 pub members: i32,
507 pub roles: HashMap<usize, String>,
508 }
509
510 impl Organization {
511 pub fn new(name: String, owner: usize) -> Self {
513 Self {
514 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
515 created: unix_epoch_timestamp(),
516 owner,
517 name,
518 members: 0,
519 roles: HashMap::new(),
520 }
521 }
522 }
523
524 #[derive(Debug, Clone, Serialize, Deserialize)]
525 pub struct OrganizationMembership {
526 pub id: usize,
527 pub created: usize,
528 pub organization: usize,
529 pub owner: usize,
530 pub role: OrganizationRole,
531 }
532
533 impl OrganizationMembership {
534 pub fn new(organization: usize, owner: usize) -> Self {
536 Self {
537 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
538 created: unix_epoch_timestamp(),
539 organization,
540 owner,
541 role: OrganizationRole::Member,
542 }
543 }
544 }
545
546 #[derive(Debug, Clone, Serialize, Deserialize)]
547 pub struct PermissionsList {
548 pub id: usize,
549 pub created: usize,
550 pub owner_org: usize,
551 pub name: String,
552 pub roles: Vec<usize>,
553 }
554
555 impl PermissionsList {
556 pub fn new(owner_org: usize, name: String) -> Self {
558 Self {
559 id: Snowflake::new().to_string().parse::<usize>().unwrap(),
560 created: unix_epoch_timestamp(),
561 owner_org,
562 name,
563 roles: Vec::new(),
564 }
565 }
566 }
567}