starpod_auth/types.rs
1//! Core types for the authentication system.
2//!
3//! All types derive `Serialize`/`Deserialize` for API responses and are
4//! independent of the storage layer.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9/// User role controlling access to protected endpoints.
10///
11/// - `Admin` — full access, including settings and user management.
12/// - `User` — standard chat access; cannot modify settings or manage users.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15pub enum Role {
16 Admin,
17 User,
18}
19
20impl Role {
21 /// Convert to a lowercase string for database storage.
22 pub fn as_str(&self) -> &'static str {
23 match self {
24 Role::Admin => "admin",
25 Role::User => "user",
26 }
27 }
28
29 /// Parse from a lowercase string (as stored in the database).
30 /// Returns `None` for unrecognized values.
31 #[allow(clippy::should_implement_trait)]
32 pub fn from_str(s: &str) -> Option<Self> {
33 match s {
34 "admin" => Some(Role::Admin),
35 "user" => Some(Role::User),
36 _ => None,
37 }
38 }
39}
40
41impl std::fmt::Display for Role {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.write_str(self.as_str())
44 }
45}
46
47/// A registered user in the auth database.
48///
49/// Users are identified by UUID. Deactivated users (`is_active == false`)
50/// cannot authenticate via API keys or Telegram.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct User {
53 pub id: String,
54 /// Optional email (unique if set).
55 pub email: Option<String>,
56 pub display_name: Option<String>,
57 pub role: Role,
58 /// `false` means the user is soft-deleted — all auth attempts will fail.
59 pub is_active: bool,
60 /// Whether this user can browse the instance filesystem via the web UI.
61 pub filesystem_enabled: bool,
62 pub created_at: DateTime<Utc>,
63 pub updated_at: DateTime<Utc>,
64}
65
66/// Metadata about an API key (never includes the actual key or hash).
67///
68/// Returned by listing endpoints. The `prefix` field (first 8 hex chars of the
69/// random part) is safe to display — it helps users identify which key is which
70/// without revealing the full key.
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ApiKeyMeta {
73 pub id: String,
74 pub user_id: String,
75 /// First 8 hex chars of the key (after `sp_live_`), used for DB lookup.
76 pub prefix: String,
77 /// Optional human-readable label.
78 pub label: Option<String>,
79 pub expires_at: Option<DateTime<Utc>>,
80 pub revoked_at: Option<DateTime<Utc>>,
81 /// Updated on each successful authentication.
82 pub last_used_at: Option<DateTime<Utc>>,
83 pub created_at: DateTime<Utc>,
84}
85
86/// Returned only at API key creation time — contains the full plaintext key.
87///
88/// The key is never stored or retrievable after creation. The caller must
89/// save it immediately.
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ApiKeyCreated {
92 pub meta: ApiKeyMeta,
93 /// The full API key — shown only once, never stored.
94 pub key: String,
95}
96
97/// A linked Telegram account.
98///
99/// A link can be created with just a username (telegram_id filled when the
100/// user first messages the bot) or with a numeric Telegram ID directly.
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct TelegramLink {
103 pub telegram_id: Option<i64>,
104 pub user_id: String,
105 pub username: Option<String>,
106 pub linked_at: DateTime<Utc>,
107}
108
109/// An entry in the auth audit log.
110///
111/// `user_id` is `None` for events where the user could not be identified
112/// (e.g. failed authentication with an unknown key).
113#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct AuditEntry {
115 pub id: i64,
116 pub user_id: Option<String>,
117 pub event_type: String,
118 pub detail: Option<String>,
119 pub ip_address: Option<String>,
120 pub created_at: DateTime<Utc>,
121}