add auth system: users, login, JWT, protected routes
Domain: User entity, AuthPort/PasswordHashPort/SecretStore ports. Adapters: auth (argon2 hashing, JWT tokens), secret-store (env-based), config-sqlite user repository, http-api auth routes + extractors. Application: auth_service. SPA: login page, auth client, protected router.
This commit is contained in:
42
crates/adapters/http-api/src/extractors.rs
Normal file
42
crates/adapters/http-api/src/extractors.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use axum::{
|
||||
extract::FromRequestParts,
|
||||
http::{StatusCode, request::Parts},
|
||||
};
|
||||
use domain::{AuthPort, UserId};
|
||||
|
||||
pub struct AuthUser(pub UserId);
|
||||
|
||||
impl<C, E, W, B, R, A, H> FromRequestParts<crate::AppState<C, E, W, B, R, A, H>> for AuthUser
|
||||
where
|
||||
A: AuthPort + Send + Sync + 'static,
|
||||
C: Send + Sync + 'static,
|
||||
E: Send + Sync + 'static,
|
||||
W: Send + Sync + 'static,
|
||||
B: Send + Sync + 'static,
|
||||
R: Send + Sync + 'static,
|
||||
H: Send + Sync + 'static,
|
||||
{
|
||||
type Rejection = StatusCode;
|
||||
|
||||
async fn from_request_parts(
|
||||
parts: &mut Parts,
|
||||
state: &crate::AppState<C, E, W, B, R, A, H>,
|
||||
) -> Result<Self, Self::Rejection> {
|
||||
let header = parts
|
||||
.headers
|
||||
.get("authorization")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let token = header
|
||||
.strip_prefix("Bearer ")
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
let user_id = state
|
||||
.auth
|
||||
.validate_token(token)
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
Ok(AuthUser(user_id))
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,27 @@
|
||||
pub mod extractors;
|
||||
mod routes;
|
||||
|
||||
use axum::Router;
|
||||
use domain::{BroadcastPort, ClientRegistry, ConfigRepository, EventPublisher, WidgetStateReader};
|
||||
use domain::{
|
||||
AuthPort, BroadcastPort, ClientRegistry, ConfigRepository, EventPublisher, PasswordHashPort,
|
||||
WidgetStateReader,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
|
||||
pub struct AppState<C, E, W, B, R> {
|
||||
pub struct AppState<C, E, W, B, R, A, H> {
|
||||
pub config: Arc<C>,
|
||||
pub events: Arc<E>,
|
||||
pub widget_states: Arc<W>,
|
||||
pub broadcaster: Arc<B>,
|
||||
pub clients: Arc<R>,
|
||||
pub auth: Arc<A>,
|
||||
pub hasher: Arc<H>,
|
||||
pub spa_dir: Option<String>,
|
||||
}
|
||||
|
||||
impl<C, E, W, B, R> Clone for AppState<C, E, W, B, R> {
|
||||
impl<C, E, W, B, R, A, H> Clone for AppState<C, E, W, B, R, A, H> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
config: self.config.clone(),
|
||||
@@ -23,12 +29,14 @@ impl<C, E, W, B, R> Clone for AppState<C, E, W, B, R> {
|
||||
widget_states: self.widget_states.clone(),
|
||||
broadcaster: self.broadcaster.clone(),
|
||||
clients: self.clients.clone(),
|
||||
auth: self.auth.clone(),
|
||||
hasher: self.hasher.clone(),
|
||||
spa_dir: self.spa_dir.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn router<C, E, W, B, R>(state: AppState<C, E, W, B, R>) -> Router
|
||||
pub fn router<C, E, W, B, R, A, H>(state: AppState<C, E, W, B, R, A, H>) -> Router
|
||||
where
|
||||
C: ConfigRepository + Send + Sync + 'static,
|
||||
C::Error: std::fmt::Debug + Send,
|
||||
@@ -38,6 +46,8 @@ where
|
||||
B: BroadcastPort + Send + Sync + 'static,
|
||||
B::Error: std::fmt::Debug + Send,
|
||||
R: ClientRegistry + Send + Sync + 'static,
|
||||
A: AuthPort + Send + Sync + 'static,
|
||||
H: PasswordHashPort + Send + Sync + 'static,
|
||||
{
|
||||
let spa_dir = state.spa_dir.clone();
|
||||
|
||||
@@ -54,9 +64,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve<C, E, W, B, R>(
|
||||
pub async fn serve<C, E, W, B, R, A, H>(
|
||||
addr: &str,
|
||||
state: AppState<C, E, W, B, R>,
|
||||
state: AppState<C, E, W, B, R, A, H>,
|
||||
) -> Result<(), std::io::Error>
|
||||
where
|
||||
C: ConfigRepository + Send + Sync + 'static,
|
||||
@@ -67,6 +77,8 @@ where
|
||||
B: BroadcastPort + Send + Sync + 'static,
|
||||
B::Error: std::fmt::Debug + Send,
|
||||
R: ClientRegistry + Send + Sync + 'static,
|
||||
A: AuthPort + Send + Sync + 'static,
|
||||
H: PasswordHashPort + Send + Sync + 'static,
|
||||
{
|
||||
let app = router(state);
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
|
||||
85
crates/adapters/http-api/src/routes/auth.rs
Normal file
85
crates/adapters/http-api/src/routes/auth.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::AppState;
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::Json;
|
||||
use domain::{AuthPort, ConfigRepository, PasswordHashPort};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LoginResponse {
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct StatusResponse {
|
||||
pub needs_setup: bool,
|
||||
}
|
||||
|
||||
pub async fn login<C, E, W, B, R, A, H>(
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<LoginRequest>,
|
||||
) -> Result<Json<LoginResponse>, (StatusCode, String)>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
C::Error: std::fmt::Debug,
|
||||
A: AuthPort,
|
||||
H: PasswordHashPort,
|
||||
{
|
||||
let token = application::auth_service::login(
|
||||
state.config.as_ref(),
|
||||
state.auth.as_ref(),
|
||||
state.hasher.as_ref(),
|
||||
&body.username,
|
||||
&body.password,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::UNAUTHORIZED, e.to_string()))?;
|
||||
|
||||
Ok(Json(LoginResponse { token }))
|
||||
}
|
||||
|
||||
pub async fn register<C, E, W, B, R, A, H>(
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<LoginRequest>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
C::Error: std::fmt::Debug,
|
||||
H: PasswordHashPort,
|
||||
{
|
||||
application::auth_service::register(
|
||||
state.config.as_ref(),
|
||||
state.hasher.as_ref(),
|
||||
&body.username,
|
||||
&body.password,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
|
||||
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
pub async fn auth_status<C, E, W, B, R, A, H>(
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Result<Json<StatusResponse>, StatusCode>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
C::Error: std::fmt::Debug,
|
||||
{
|
||||
let count = state
|
||||
.config
|
||||
.count_users()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(StatusResponse {
|
||||
needs_setup: count == 0,
|
||||
}))
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
use crate::AppState;
|
||||
use crate::extractors::AuthUser;
|
||||
use api_types::ClientDto;
|
||||
use axum::extract::State;
|
||||
use axum::response::Json;
|
||||
use domain::{ClientRegistry, ConfigRepository, EventPublisher};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn list_clients<C, E, W, B, R>(State(state): S<C, E, W, B, R>) -> Json<Vec<ClientDto>>
|
||||
pub async fn list_clients<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Json<Vec<ClientDto>>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
C::Error: std::fmt::Debug,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::AppState;
|
||||
use crate::extractors::AuthUser;
|
||||
use api_types::DataSourceDto;
|
||||
use application::ConfigService;
|
||||
use axum::{
|
||||
@@ -8,10 +9,11 @@ use axum::{
|
||||
};
|
||||
use domain::{ConfigRepository, EventPublisher};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn list_data_sources<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn list_data_sources<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Result<Json<Vec<DataSourceDto>>, StatusCode>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
@@ -27,8 +29,9 @@ where
|
||||
Ok(Json(sources.iter().map(DataSourceDto::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn get_data_source<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn get_data_source<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<Json<DataSourceDto>, StatusCode>
|
||||
where
|
||||
@@ -48,8 +51,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_data_source<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn create_data_source<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<DataSourceDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
@@ -68,8 +72,9 @@ where
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
pub async fn update_data_source<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn update_data_source<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(_id): Path<u16>,
|
||||
Json(body): Json<DataSourceDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
@@ -89,8 +94,9 @@ where
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub async fn delete_data_source<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn delete_data_source<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<StatusCode, StatusCode>
|
||||
where
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::AppState;
|
||||
use crate::extractors::AuthUser;
|
||||
use api_types::LayoutDto;
|
||||
use application::ConfigService;
|
||||
use axum::{extract::State, http::StatusCode, response::Json};
|
||||
use domain::{ConfigRepository, EventPublisher};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn get_layout<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn get_layout<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Result<Json<Option<LayoutDto>>, StatusCode>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
@@ -23,8 +25,9 @@ where
|
||||
Ok(Json(layout.as_ref().map(LayoutDto::from)))
|
||||
}
|
||||
|
||||
pub async fn update_layout<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn update_layout<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<LayoutDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod auth;
|
||||
mod clients;
|
||||
mod data_sources;
|
||||
mod layout;
|
||||
@@ -8,9 +9,12 @@ mod widgets;
|
||||
use crate::AppState;
|
||||
use axum::Router;
|
||||
use axum::routing::{get, post};
|
||||
use domain::{BroadcastPort, ClientRegistry, ConfigRepository, EventPublisher, WidgetStateReader};
|
||||
use domain::{
|
||||
AuthPort, BroadcastPort, ClientRegistry, ConfigRepository, EventPublisher, PasswordHashPort,
|
||||
WidgetStateReader,
|
||||
};
|
||||
|
||||
pub fn api_routes<C, E, W, B, R>() -> Router<AppState<C, E, W, B, R>>
|
||||
pub fn api_routes<C, E, W, B, R, A, H>() -> Router<AppState<C, E, W, B, R, A, H>>
|
||||
where
|
||||
C: ConfigRepository + Send + Sync + 'static,
|
||||
C::Error: std::fmt::Debug + Send,
|
||||
@@ -20,55 +24,72 @@ where
|
||||
B: BroadcastPort + Send + Sync + 'static,
|
||||
B::Error: std::fmt::Debug + Send,
|
||||
R: ClientRegistry + Send + Sync + 'static,
|
||||
A: AuthPort + Send + Sync + 'static,
|
||||
H: PasswordHashPort + Send + Sync + 'static,
|
||||
{
|
||||
Router::new()
|
||||
// Public auth routes
|
||||
.route(
|
||||
"/auth/status",
|
||||
get(auth::auth_status::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route("/auth/login", post(auth::login::<C, E, W, B, R, A, H>))
|
||||
.route(
|
||||
"/auth/register",
|
||||
post(auth::register::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
// Protected routes
|
||||
.route(
|
||||
"/widgets",
|
||||
get(widgets::list_widgets::<C, E, W, B, R>)
|
||||
.post(widgets::create_widget::<C, E, W, B, R>),
|
||||
get(widgets::list_widgets::<C, E, W, B, R, A, H>)
|
||||
.post(widgets::create_widget::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/widgets/{id}",
|
||||
get(widgets::get_widget::<C, E, W, B, R>)
|
||||
.put(widgets::update_widget::<C, E, W, B, R>)
|
||||
.delete(widgets::delete_widget::<C, E, W, B, R>),
|
||||
get(widgets::get_widget::<C, E, W, B, R, A, H>)
|
||||
.put(widgets::update_widget::<C, E, W, B, R, A, H>)
|
||||
.delete(widgets::delete_widget::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/widgets/{id}/preview",
|
||||
get(widgets::preview_widget::<C, E, W, B, R>),
|
||||
get(widgets::preview_widget::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/data-sources",
|
||||
get(data_sources::list_data_sources::<C, E, W, B, R>)
|
||||
.post(data_sources::create_data_source::<C, E, W, B, R>),
|
||||
get(data_sources::list_data_sources::<C, E, W, B, R, A, H>)
|
||||
.post(data_sources::create_data_source::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/data-sources/{id}",
|
||||
get(data_sources::get_data_source::<C, E, W, B, R>)
|
||||
.put(data_sources::update_data_source::<C, E, W, B, R>)
|
||||
.delete(data_sources::delete_data_source::<C, E, W, B, R>),
|
||||
get(data_sources::get_data_source::<C, E, W, B, R, A, H>)
|
||||
.put(data_sources::update_data_source::<C, E, W, B, R, A, H>)
|
||||
.delete(data_sources::delete_data_source::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/layout",
|
||||
get(layout::get_layout::<C, E, W, B, R>).put(layout::update_layout::<C, E, W, B, R>),
|
||||
get(layout::get_layout::<C, E, W, B, R, A, H>)
|
||||
.put(layout::update_layout::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/presets",
|
||||
get(presets::list_presets::<C, E, W, B, R>)
|
||||
.post(presets::create_preset::<C, E, W, B, R>),
|
||||
get(presets::list_presets::<C, E, W, B, R, A, H>)
|
||||
.post(presets::create_preset::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/presets/{id}",
|
||||
get(presets::get_preset::<C, E, W, B, R>)
|
||||
.delete(presets::delete_preset::<C, E, W, B, R>),
|
||||
get(presets::get_preset::<C, E, W, B, R, A, H>)
|
||||
.delete(presets::delete_preset::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/presets/{id}/load",
|
||||
post(presets::load_preset::<C, E, W, B, R>),
|
||||
post(presets::load_preset::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route(
|
||||
"/clients",
|
||||
get(clients::list_clients::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
.route("/clients", get(clients::list_clients::<C, E, W, B, R>))
|
||||
.route(
|
||||
"/webhook/{source_id}",
|
||||
post(webhook::receive_webhook::<C, E, W, B, R>),
|
||||
post(webhook::receive_webhook::<C, E, W, B, R, A, H>),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::AppState;
|
||||
use crate::extractors::AuthUser;
|
||||
use api_types::{CreatePresetDto, PresetDto};
|
||||
use application::ConfigService;
|
||||
use axum::{
|
||||
@@ -8,10 +9,11 @@ use axum::{
|
||||
};
|
||||
use domain::{ConfigRepository, EventPublisher};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn list_presets<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn list_presets<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Result<Json<Vec<PresetDto>>, StatusCode>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
@@ -27,8 +29,9 @@ where
|
||||
Ok(Json(presets.iter().map(PresetDto::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn get_preset<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn get_preset<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<Json<PresetDto>, StatusCode>
|
||||
where
|
||||
@@ -48,8 +51,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_preset<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn create_preset<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<CreatePresetDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
@@ -68,8 +72,9 @@ where
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
pub async fn delete_preset<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn delete_preset<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<StatusCode, StatusCode>
|
||||
where
|
||||
@@ -85,8 +90,9 @@ where
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
pub async fn load_preset<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn load_preset<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
|
||||
@@ -4,10 +4,10 @@ use axum::http::StatusCode;
|
||||
use axum::response::Json;
|
||||
use domain::{BroadcastPort, ConfigRepository, EventPublisher, WidgetStateReader};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn receive_webhook<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn receive_webhook<C, E, W, B, R, A, H>(
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(source_id): Path<u16>,
|
||||
Json(body): Json<serde_json::Value>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::AppState;
|
||||
use crate::extractors::AuthUser;
|
||||
use api_types::{CreateWidgetDto, WidgetDto};
|
||||
use application::ConfigService;
|
||||
use axum::{
|
||||
@@ -8,10 +9,11 @@ use axum::{
|
||||
};
|
||||
use domain::{ConfigRepository, EventPublisher, WidgetStateReader};
|
||||
|
||||
type S<C, E, W, B, R> = State<AppState<C, E, W, B, R>>;
|
||||
type S<C, E, W, B, R, A, H> = State<AppState<C, E, W, B, R, A, H>>;
|
||||
|
||||
pub async fn list_widgets<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn list_widgets<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
) -> Result<Json<Vec<WidgetDto>>, StatusCode>
|
||||
where
|
||||
C: ConfigRepository,
|
||||
@@ -27,8 +29,9 @@ where
|
||||
Ok(Json(widgets.iter().map(WidgetDto::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn get_widget<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn get_widget<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<Json<WidgetDto>, StatusCode>
|
||||
where
|
||||
@@ -48,8 +51,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_widget<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn create_widget<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Json(body): Json<CreateWidgetDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
where
|
||||
@@ -68,8 +72,9 @@ where
|
||||
Ok(StatusCode::CREATED)
|
||||
}
|
||||
|
||||
pub async fn update_widget<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn update_widget<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(_id): Path<u16>,
|
||||
Json(body): Json<CreateWidgetDto>,
|
||||
) -> Result<StatusCode, (StatusCode, String)>
|
||||
@@ -89,8 +94,9 @@ where
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
pub async fn delete_widget<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn delete_widget<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<StatusCode, StatusCode>
|
||||
where
|
||||
@@ -106,8 +112,9 @@ where
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
pub async fn preview_widget<C, E, W, B, R>(
|
||||
State(state): S<C, E, W, B, R>,
|
||||
pub async fn preview_widget<C, E, W, B, R, A, H>(
|
||||
_auth: AuthUser,
|
||||
State(state): S<C, E, W, B, R, A, H>,
|
||||
Path(id): Path<u16>,
|
||||
) -> Result<Json<serde_json::Value>, StatusCode>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user