Add PUT /v1/configuration endpoint for config updates

Introduces a new put_configuration handler to allow updating configuration via the /v1/configuration endpoint. Updates the router to support PUT requests, modifies the README to improve image rendering, and bumps the workspace version to 1.1.3.
This commit is contained in:
ForgQi committed 2025-09-20 23:10:36 +08:00
1 parent 89c9b709c7
commit 2f309ba8a0
6 files changed
+126 -19

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 201 KiB

Generated
+1 -1
View File
@@ -3617,7 +3617,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stream-gears"
version = "1.1.2"
version = "1.1.3"
dependencies = [
"anyhow",
"async-channel",
+1 -1
View File
@@ -7,7 +7,7 @@ members = [
resolver = "3"
[workspace.package]
version = "1.1.2"
version = "1.1.3"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
+1 -1
View File
@@ -74,7 +74,7 @@
## 💴捐赠
![支付宝](.github/resource/Image.jpg)
<img src=".github/resource/Image.jpg" width="200" />
[爱发电 »](https://afdian.com/a/biliup)
## ⭐Stars
+111 -9
View File
@@ -1,17 +1,20 @@
use crate::server::config::Config;
use crate::server::errors::{AppError, report_to_response, AppResult};
use crate::server::errors::{AppError, AppResult, report_to_response};
use crate::server::infrastructure::connection_pool::ConnectionPool;
use crate::server::infrastructure::context::Worker;
use crate::server::infrastructure::dto::LiveStreamerResponse;
use crate::server::infrastructure::models::{Configuration, FileItem, InsertConfiguration, InsertLiveStreamer, InsertUploadStreamer, LiveStreamer, StreamerInfo, UploadStreamer};
use crate::server::infrastructure::models::{
Configuration, FileItem, InsertConfiguration, InsertLiveStreamer, InsertUploadStreamer,
LiveStreamer, StreamerInfo, UploadStreamer,
};
use crate::server::infrastructure::repositories::{del_streamer, get_all_streamer};
use crate::server::infrastructure::service_register::ServiceRegister;
use axum::Json;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::{Json, debug_handler};
use biliup::credential::{Credential, LoginInfo};
use error_stack::ResultExt;
use error_stack::{Report, ResultExt};
use ormlite::{Insert, Model};
use serde_json::json;
use std::sync::{Arc, RwLock};
@@ -128,6 +131,91 @@ pub async fn get_configuration(
Ok(Json(config.read().unwrap().clone()))
}
// #[axum_macros::debug_handler(state = ServiceRegister)]
pub async fn put_configuration(
State(config): State<Arc<RwLock<Config>>>,
State(pool): State<ConnectionPool>,
Json(json_data): Json<Config>,
) -> Result<Json<Config>, Response> {
// 将 JSON 序列化为 TEXT 存库
let value_txt = serde_json::to_string(&json_data)
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
let mut tx = pool
.begin()
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
// 最多取 2 条判断是否多行
let ids: Vec<i64> =
sqlx::query_scalar::<_, i64>("SELECT id FROM configuration WHERE key = ?1 LIMIT 2")
.bind("config")
.fetch_all(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
let saved: Configuration = if ids.is_empty() {
// 插入
sqlx::query("INSERT INTO configuration (key, value) VALUES (?1, ?2)")
.bind("config")
.bind(&value_txt)
.execute(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
// 取 last_insert_rowid 并读回整行
let id: i64 = sqlx::query_scalar::<_, i64>("SELECT last_insert_rowid()")
.fetch_one(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
sqlx::query_as::<_, Configuration>("SELECT id, key, value FROM configuration WHERE id = ?1")
.bind(id)
.fetch_one(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?
} else if ids.len() == 1 {
// 更新
let id = ids[0];
sqlx::query("UPDATE configuration SET value = ?1 WHERE id = ?2")
.bind(&value_txt)
.bind(id)
.execute(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
sqlx::query_as::<_, Configuration>("SELECT id, key, value FROM configuration WHERE id = ?1")
.bind(id)
.fetch_one(&mut *tx)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?
} else {
// 多行报错
return Err(report_to_response(Report::new(AppError::Custom(
format!("有多个空间配置同时存在 (key='config'): {} 行", ids.len()).to_string(),
))));
};
tx.commit()
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
// 提交后从 DB 重新加载配置
let saved_config: Config = serde_json::from_str(&saved.value)
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
*config.write().unwrap() = saved_config;
Ok(Json(config.read().unwrap().clone()))
}
pub async fn get_streamer_info(
// Extension(streamers_service): Extension<DynUploadStreamersRepository>,
State(pool): State<ConnectionPool>,
@@ -248,14 +336,24 @@ pub async fn add_user_endpoint(
State(pool): State<ConnectionPool>,
Json(user): Json<InsertConfiguration>,
) -> Result<Json<Configuration>, Response> {
let res = user.insert(&pool).await.change_context(AppError::Unknown).map_err(report_to_response)?;
let res = user
.insert(&pool)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
Ok(Json(res))
}
pub async fn delete_user_endpoint(Path(id): Path<i64>, State(pool): State<ConnectionPool>) -> Result<Json<()>, Response> {
pub async fn delete_user_endpoint(
Path(id): Path<i64>,
State(pool): State<ConnectionPool>,
) -> Result<Json<()>, Response> {
let x = sqlx::query("DELETE FROM configuration WHERE id = ?")
.bind(id)
.execute(&pool).await.change_context(AppError::Unknown).map_err(report_to_response)?;
.execute(&pool)
.await
.change_context(AppError::Unknown)
.map_err(report_to_response)?;
info!("{:?}", x);
Ok(Json(()))
}
@@ -317,9 +415,13 @@ pub async fn get_videos() -> Result<Json<Vec<serde_json::Value>>, Response> {
}
if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
if media_extensions.iter().any(|allowed| &ext == &allowed.trim_start_matches('.')) {
if media_extensions
.iter()
.any(|allowed| &ext == &allowed.trim_start_matches('.'))
{
if let Ok(metadata) = entry.metadata().await {
let mtime = metadata.modified()
let mtime = metadata
.modified()
.ok()
.and_then(|time| time.duration_since(UNIX_EPOCH).ok())
.map(|d| d.as_secs())
+12 -7
View File
@@ -1,7 +1,13 @@
use crate::server::api::bilibili_endpoints::{
archive_pre_endpoint, get_myinfo_endpoint, get_proxy_endpoint,
};
use crate::server::api::endpoints::{add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint, delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode, get_streamer_info, get_streamers_endpoint, get_upload_streamer_endpoint, get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode, post_streamers_endpoint, put_streamers_endpoint};
use crate::server::api::endpoints::{
add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint,
delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode,
get_streamer_info, get_streamers_endpoint, get_upload_streamer_endpoint,
get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode,
post_streamers_endpoint, put_configuration, put_streamers_endpoint,
};
use crate::server::infrastructure::service_register::ServiceRegister;
use axum::Router;
use axum::routing::{delete, get, post};
@@ -16,7 +22,10 @@ pub fn router(service_register: ServiceRegister) -> Router<()> {
.put(put_streamers_endpoint),
)
.route("/v1/streamers/{id}", delete(delete_streamers_endpoint))
.route("/v1/configuration", get(get_configuration))
.route(
"/v1/configuration",
get(get_configuration).put(put_configuration),
)
.route("/v1/streamer-info", get(get_streamer_info))
.route("/v1/upload/streamers", get(get_upload_streamers_endpoint))
.route(
@@ -35,11 +44,7 @@ pub fn router(service_register: ServiceRegister) -> Router<()> {
// )
// .route("/v1/upload/streamers/:id", )
.route("/v1/upload/streamers", post(add_upload_streamer_endpoint))
.route(
"/v1/users",
get(get_users_endpoint)
.post(add_user_endpoint),
)
.route("/v1/users", get(get_users_endpoint).post(add_user_endpoint))
.route("/v1/users/{id}", delete(delete_user_endpoint))
.route("/bili/archive/pre", get(archive_pre_endpoint))
.route("/bili/space/myinfo", get(get_myinfo_endpoint))