forked from boos/BoosAPI
refactor: purge Node.js, Rust is the one true backend
- Moved boosapi-rs/* to repo root - Deleted all Node.js source (src/server/, src/web/, scripts/) - Deleted node_modules, package.json, tsconfig, vite.config - Updated .gitignore and AGENTS.md for Rust-only stack - Fixed static file path for frontend dist/web/ - Dockerfile stays the same (already Rust-only) - Kept dist/web/ for Rust static serving
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
use crate::error::{AppError, AppResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use sqlx::SqlitePool;
|
||||
use std::sync::Arc;
|
||||
|
||||
const UPDATE_CONFIG_KEY: &str = "update_center_config";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UpdateCenter {
|
||||
db: Arc<SqlitePool>,
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpdateInfo {
|
||||
pub current_version: String,
|
||||
pub latest_version: Option<String>,
|
||||
pub update_available: bool,
|
||||
pub release_notes: Option<String>,
|
||||
pub download_url: Option<String>,
|
||||
}
|
||||
|
||||
impl UpdateCenter {
|
||||
pub fn new(db: Arc<SqlitePool>) -> Self {
|
||||
Self {
|
||||
db,
|
||||
client: reqwest::Client::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_for_updates(&self) -> AppResult<UpdateInfo> {
|
||||
let config = self.load_config().await?;
|
||||
let current_version = env!("CARGO_PKG_VERSION").to_string();
|
||||
let Some(endpoint) = config
|
||||
.get("check_url")
|
||||
.and_then(Value::as_str)
|
||||
.filter(|url| !url.trim().is_empty())
|
||||
else {
|
||||
return Ok(UpdateInfo {
|
||||
current_version,
|
||||
latest_version: None,
|
||||
update_available: false,
|
||||
release_notes: None,
|
||||
download_url: None,
|
||||
});
|
||||
};
|
||||
|
||||
let payload: Value = self
|
||||
.client
|
||||
.get(endpoint)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| AppError::UpstreamError(format!("Update check failed: {err}")))?
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| AppError::UpstreamError(format!("Invalid update response: {err}")))?;
|
||||
|
||||
let latest_version = payload
|
||||
.get("version")
|
||||
.or_else(|| payload.get("latestVersion"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string);
|
||||
let update_available = latest_version
|
||||
.as_deref()
|
||||
.map(|latest| latest != current_version)
|
||||
.unwrap_or(false);
|
||||
|
||||
Ok(UpdateInfo {
|
||||
current_version,
|
||||
latest_version,
|
||||
update_available,
|
||||
release_notes: payload
|
||||
.get("release_notes")
|
||||
.or_else(|| payload.get("releaseNotes"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string),
|
||||
download_url: payload
|
||||
.get("download_url")
|
||||
.or_else(|| payload.get("downloadUrl"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn apply_update(&self, version: &str) -> AppResult<String> {
|
||||
let task_id = format!("update-{}", uuid::Uuid::new_v4());
|
||||
sqlx::query(
|
||||
"INSERT INTO background_tasks
|
||||
(id, task_type, title, status, message, dedupe_key, created_at, updated_at)
|
||||
VALUES (?, 'update-center-deploy', ?, 'pending', ?, ?, datetime('now'), datetime('now'))",
|
||||
)
|
||||
.bind(&task_id)
|
||||
.bind(format!("Apply BoosAPI {version}"))
|
||||
.bind("Update request accepted; an external supervisor should perform download and restart")
|
||||
.bind(format!("update-center:deploy:{version}"))
|
||||
.execute(&*self.db)
|
||||
.await?;
|
||||
Ok(task_id)
|
||||
}
|
||||
|
||||
async fn load_config(&self) -> AppResult<Value> {
|
||||
let raw: Option<String> = sqlx::query_scalar("SELECT value FROM settings WHERE key = ?")
|
||||
.bind(UPDATE_CONFIG_KEY)
|
||||
.fetch_optional(&*self.db)
|
||||
.await?;
|
||||
|
||||
raw.as_deref()
|
||||
.map(serde_json::from_str)
|
||||
.transpose()
|
||||
.map_err(|err| AppError::BadRequest(format!("Invalid update center config: {err}")))?
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| Ok(Value::Object(Default::default())))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user