diff --git a/.github/resource/Image.jpg b/.github/resource/Image.jpg
index 120b5d4..0da2dc4 100644
Binary files a/.github/resource/Image.jpg and b/.github/resource/Image.jpg differ
diff --git a/Cargo.lock b/Cargo.lock
index 361d578..7a51997 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3617,7 +3617,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stream-gears"
-version = "1.1.2"
+version = "1.1.3"
dependencies = [
"anyhow",
"async-channel",
diff --git a/Cargo.toml b/Cargo.toml
index a98c51d..4818af1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"] }
diff --git a/README.md b/README.md
index ba35e24..71b6422 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@
## 💴捐赠
-
+
[爱发电 »](https://afdian.com/a/biliup)
## ⭐Stars
diff --git a/crates/stream-gears/src/server/api/endpoints.rs b/crates/stream-gears/src/server/api/endpoints.rs
index 82c9907..c599bde 100644
--- a/crates/stream-gears/src/server/api/endpoints.rs
+++ b/crates/stream-gears/src/server/api/endpoints.rs
@@ -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>>,
+ State(pool): State,
+ Json(json_data): Json,
+) -> Result, 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 =
+ 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,
State(pool): State,
@@ -248,14 +336,24 @@ pub async fn add_user_endpoint(
State(pool): State,
Json(user): Json,
) -> Result, 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, State(pool): State) -> Result, Response> {
+pub async fn delete_user_endpoint(
+ Path(id): Path,
+ State(pool): State,
+) -> Result, 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>, 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())
diff --git a/crates/stream-gears/src/server/router.rs b/crates/stream-gears/src/server/router.rs
index 7b220fa..4c05a9c 100644
--- a/crates/stream-gears/src/server/router.rs
+++ b/crates/stream-gears/src/server/router.rs
@@ -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))