domain models: User, Channel, ScheduleConfig, MediaItem, PlaybackRecord

This commit is contained in:
2026-07-12 01:11:11 +02:00
parent b2e71403d8
commit 528b155327
6 changed files with 1104 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::value_objects::{Email, UserId};
/// A user in the system.
///
/// Designed to be OIDC-ready: the `subject` field stores the OIDC subject claim.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
id: UserId,
subject: String,
email: Email,
password_hash: Option<String>,
is_admin: bool,
created_at: DateTime<Utc>,
}
impl User {
/// Create a new OIDC user (no local password).
pub fn new(subject: impl Into<String>, email: Email) -> Self {
Self {
id: UserId::generate(),
subject: subject.into(),
email,
password_hash: None,
is_admin: false,
created_at: Utc::now(),
}
}
/// Create a new local user with a password hash.
pub fn new_local(email: Email, password_hash: impl Into<String>) -> Self {
Self {
id: UserId::generate(),
subject: format!("local|{}", uuid::Uuid::new_v4()),
email,
password_hash: Some(password_hash.into()),
is_admin: false,
created_at: Utc::now(),
}
}
/// Hydrate from persistence — no validation, accepts all fields.
pub fn from_persistence(
id: UserId,
subject: String,
email: Email,
password_hash: Option<String>,
is_admin: bool,
created_at: DateTime<Utc>,
) -> Self {
Self {
id,
subject,
email,
password_hash,
is_admin,
created_at,
}
}
// -- Getters --
pub fn id(&self) -> UserId {
self.id
}
pub fn subject(&self) -> &str {
&self.subject
}
pub fn email(&self) -> &Email {
&self.email
}
pub fn password_hash(&self) -> Option<&str> {
self.password_hash.as_deref()
}
pub fn is_admin(&self) -> bool {
self.is_admin
}
pub fn created_at(&self) -> DateTime<Utc> {
self.created_at
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_generates_id_and_timestamp() {
let email = Email::new("test@example.com").unwrap();
let user = User::new("oidc|123", email);
assert!(!user.is_admin());
assert!(user.password_hash().is_none());
assert_eq!(user.subject(), "oidc|123");
}
#[test]
fn new_local_sets_password_and_subject() {
let email = Email::new("local@example.com").unwrap();
let user = User::new_local(email, "hashed_pw");
assert!(user.password_hash().is_some());
assert!(user.subject().starts_with("local|"));
}
#[test]
fn from_persistence_round_trip() {
let email = Email::new("stored@example.com").unwrap();
let id = UserId::generate();
let now = Utc::now();
let user = User::from_persistence(
id,
"sub".into(),
email.clone(),
Some("hash".into()),
true,
now,
);
assert_eq!(user.id(), id);
assert_eq!(user.subject(), "sub");
assert!(user.is_admin());
assert_eq!(user.password_hash(), Some("hash"));
assert_eq!(user.created_at(), now);
}
}