pinakes-server: wire backup, session refresh, webhooks, and rate limit config
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: If2855d44cc700c0f65a5f5ac850ee3866a6a6964
This commit is contained in:
parent
4e91cb6679
commit
52f0b5defc
8 changed files with 257 additions and 105 deletions
|
|
@ -39,8 +39,8 @@ struct Cli {
|
|||
}
|
||||
|
||||
/// Resolve the configuration file path.
|
||||
/// Returns (path, was_explicit) where was_explicit indicates if the path was
|
||||
/// explicitly provided by the user (vs discovered).
|
||||
/// Returns (path, `was_explicit`) where `was_explicit` indicates if the path
|
||||
/// was explicitly provided by the user (vs discovered).
|
||||
fn resolve_config_path(explicit: Option<&std::path::Path>) -> (PathBuf, bool) {
|
||||
if let Some(path) = explicit {
|
||||
return (path.to_path_buf(), true);
|
||||
|
|
@ -219,16 +219,34 @@ async fn main() -> Result<()> {
|
|||
None
|
||||
};
|
||||
|
||||
// Initialize webhook dispatcher early so the job queue executor can use it
|
||||
let webhook_dispatcher: Option<
|
||||
std::sync::Arc<pinakes_core::webhooks::WebhookDispatcher>,
|
||||
> = if config.webhooks.is_empty() {
|
||||
None
|
||||
} else {
|
||||
tracing::info!(
|
||||
count = config.webhooks.len(),
|
||||
"webhook dispatcher initialized"
|
||||
);
|
||||
Some(pinakes_core::webhooks::WebhookDispatcher::new(
|
||||
config.webhooks.clone(),
|
||||
))
|
||||
};
|
||||
|
||||
// Initialize job queue with executor
|
||||
let job_storage = storage.clone();
|
||||
let job_config = config.clone();
|
||||
let job_transcode = transcode_service.clone();
|
||||
let job_webhooks = webhook_dispatcher.clone();
|
||||
let job_queue = pinakes_core::jobs::JobQueue::new(
|
||||
config.jobs.worker_count,
|
||||
config.jobs.job_timeout_secs,
|
||||
move |job_id, kind, cancel, jobs| {
|
||||
let storage = job_storage.clone();
|
||||
let config = job_config.clone();
|
||||
let transcode_svc = job_transcode.clone();
|
||||
let webhooks = job_webhooks.clone();
|
||||
tokio::spawn(async move {
|
||||
use pinakes_core::jobs::{JobKind, JobQueue};
|
||||
match kind {
|
||||
|
|
@ -257,6 +275,14 @@ async fn main() -> Result<()> {
|
|||
};
|
||||
match res {
|
||||
Ok(status) => {
|
||||
if let Some(ref dispatcher) = webhooks {
|
||||
dispatcher.dispatch(
|
||||
pinakes_core::webhooks::WebhookEvent::ScanCompleted {
|
||||
files_found: status.files_found,
|
||||
files_processed: status.files_processed,
|
||||
},
|
||||
);
|
||||
}
|
||||
JobQueue::complete(
|
||||
&jobs,
|
||||
job_id,
|
||||
|
|
@ -287,7 +313,7 @@ async fn main() -> Result<()> {
|
|||
&jobs,
|
||||
job_id,
|
||||
i as f32 / total as f32,
|
||||
format!("{}/{}", i, total),
|
||||
format!("{i}/{total}"),
|
||||
)
|
||||
.await;
|
||||
match storage.get_media(*mid).await {
|
||||
|
|
@ -299,7 +325,7 @@ async fn main() -> Result<()> {
|
|||
let tc = thumb_config.clone();
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
pinakes_core::thumbnail::generate_thumbnail_with_config(
|
||||
id, &source, mt, &td, &tc,
|
||||
id, &source, &mt, &td, &tc,
|
||||
)
|
||||
})
|
||||
.await;
|
||||
|
|
@ -311,11 +337,11 @@ async fn main() -> Result<()> {
|
|||
generated += 1;
|
||||
},
|
||||
Ok(Ok(None)) => {},
|
||||
Ok(Err(e)) => errors.push(format!("{}: {}", mid, e)),
|
||||
Err(e) => errors.push(format!("{}: {}", mid, e)),
|
||||
Ok(Err(e)) => errors.push(format!("{mid}: {e}")),
|
||||
Err(e) => errors.push(format!("{mid}: {e}")),
|
||||
}
|
||||
},
|
||||
Err(e) => errors.push(format!("{}: {}", mid, e)),
|
||||
Err(e) => errors.push(format!("{mid}: {e}")),
|
||||
}
|
||||
}
|
||||
JobQueue::complete(
|
||||
|
|
@ -422,7 +448,7 @@ async fn main() -> Result<()> {
|
|||
.await;
|
||||
},
|
||||
Err(e) => {
|
||||
JobQueue::fail(&jobs, job_id, e.to_string()).await
|
||||
JobQueue::fail(&jobs, job_id, e.to_string()).await;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
@ -593,7 +619,7 @@ async fn main() -> Result<()> {
|
|||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
drop(cancel);
|
||||
})
|
||||
},
|
||||
|
|
@ -714,6 +740,7 @@ async fn main() -> Result<()> {
|
|||
transcode_service,
|
||||
managed_storage,
|
||||
chunked_upload_manager,
|
||||
webhook_dispatcher,
|
||||
session_semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(
|
||||
pinakes_server::state::MAX_SESSION_BACKGROUND_TASKS,
|
||||
)),
|
||||
|
|
@ -725,7 +752,7 @@ async fn main() -> Result<()> {
|
|||
let cancel = shutdown_token.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval =
|
||||
tokio::time::interval(std::time::Duration::from_secs(15 * 60));
|
||||
tokio::time::interval(std::time::Duration::from_mins(15));
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => {
|
||||
|
|
@ -739,7 +766,7 @@ async fn main() -> Result<()> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = cancel.cancelled() => {
|
||||
() = cancel.cancelled() => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -753,7 +780,7 @@ async fn main() -> Result<()> {
|
|||
let cancel = shutdown_token.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval =
|
||||
tokio::time::interval(std::time::Duration::from_secs(60 * 60));
|
||||
tokio::time::interval(std::time::Duration::from_hours(1));
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => {
|
||||
|
|
@ -767,7 +794,7 @@ async fn main() -> Result<()> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
_ = cancel.cancelled() => {
|
||||
() = cancel.cancelled() => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -777,13 +804,14 @@ async fn main() -> Result<()> {
|
|||
|
||||
let config_read = config_arc.read().await;
|
||||
let tls_config = config_read.server.tls.clone();
|
||||
let rate_limits = config_read.rate_limits.clone();
|
||||
drop(config_read);
|
||||
|
||||
// Create router with TLS config for HSTS headers
|
||||
let router = if tls_config.enabled {
|
||||
app::create_router_with_tls(state, Some(&tls_config))
|
||||
app::create_router_with_tls(state, &rate_limits, Some(&tls_config))
|
||||
} else {
|
||||
app::create_router(state)
|
||||
app::create_router(state, &rate_limits)
|
||||
};
|
||||
|
||||
if tls_config.enabled {
|
||||
|
|
@ -836,7 +864,7 @@ async fn main() -> Result<()> {
|
|||
tracing::warn!(error = %e, "HTTP redirect server error");
|
||||
}
|
||||
}
|
||||
_ = shutdown.cancelled() => {
|
||||
() = shutdown.cancelled() => {
|
||||
info!("HTTP redirect server shutting down");
|
||||
}
|
||||
}
|
||||
|
|
@ -884,13 +912,14 @@ fn create_https_redirect_router(https_host: String, https_port: u16) -> Router {
|
|||
Router::new().fallback(any(move |uri: axum::http::Uri| {
|
||||
let https_host = https_host.clone();
|
||||
async move {
|
||||
let path_and_query =
|
||||
uri.path_and_query().map(|pq| pq.as_str()).unwrap_or("/");
|
||||
let path_and_query = uri
|
||||
.path_and_query()
|
||||
.map_or("/", axum::http::uri::PathAndQuery::as_str);
|
||||
|
||||
let https_url = if https_port == 443 {
|
||||
format!("https://{}{}", https_host, path_and_query)
|
||||
format!("https://{https_host}{path_and_query}")
|
||||
} else {
|
||||
format!("https://{}:{}{}", https_host, https_port, path_and_query)
|
||||
format!("https://{https_host}:{https_port}{path_and_query}")
|
||||
};
|
||||
|
||||
Redirect::permanent(&https_url)
|
||||
|
|
@ -928,7 +957,7 @@ async fn shutdown_signal() {
|
|||
let terminate = std::future::pending::<()>();
|
||||
|
||||
tokio::select! {
|
||||
_ = ctrl_c => info!("received Ctrl+C, shutting down"),
|
||||
_ = terminate => info!("received SIGTERM, shutting down"),
|
||||
() = ctrl_c => info!("received Ctrl+C, shutting down"),
|
||||
() = terminate => info!("received SIGTERM, shutting down"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue