Refactor schedule and user repositories into modular structure

- Moved schedule repository logic into separate modules for SQLite and PostgreSQL implementations.
- Created a mapping module for shared data structures and mapping functions in the schedule repository.
- Added new mapping module for user repository to handle user data transformations.
- Implemented PostgreSQL and SQLite user repository adapters with necessary CRUD operations.
- Added tests for user repository functionality, including saving, finding, and deleting users.
This commit is contained in:
2026-03-13 01:35:14 +01:00
parent 79ced7b77b
commit eeb4e2cb41
39 changed files with 2288 additions and 2194 deletions

View File

@@ -0,0 +1,55 @@
//! Channel routes
//!
//! CRUD + schedule generation require authentication (Bearer JWT).
//! Viewing endpoints (list, now, epg, stream) are intentionally public so the
//! TV page works without login.
use axum::{Router, routing::{get, post}};
use chrono::{DateTime, Utc};
use uuid::Uuid;
use crate::{error::ApiError, state::AppState};
mod broadcast;
mod crud;
mod schedule;
pub fn router() -> Router<AppState> {
Router::new()
.route("/", get(crud::list_channels).post(crud::create_channel))
.route(
"/{id}",
get(crud::get_channel).put(crud::update_channel).delete(crud::delete_channel),
)
.route(
"/{id}/schedule",
post(schedule::generate_schedule).get(schedule::get_active_schedule),
)
.route("/{id}/now", get(broadcast::get_current_broadcast))
.route("/{id}/epg", get(broadcast::get_epg))
.route("/{id}/stream", get(broadcast::get_stream))
}
// ============================================================================
// Shared helpers
// ============================================================================
pub(super) fn require_owner(channel: &domain::Channel, user_id: Uuid) -> Result<(), ApiError> {
if channel.owner_id != user_id {
Err(ApiError::Forbidden("You don't own this channel".into()))
} else {
Ok(())
}
}
pub(super) fn parse_optional_dt(
s: Option<String>,
default: DateTime<Utc>,
) -> Result<DateTime<Utc>, ApiError> {
match s {
None => Ok(default),
Some(raw) => DateTime::parse_from_rfc3339(&raw)
.map(|dt| dt.with_timezone(&Utc))
.map_err(|_| ApiError::validation(format!("Invalid datetime '{}' — use RFC3339", raw))),
}
}