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:
boos
2026-05-18 09:11:29 +08:00
parent 725f44699a
commit 5d29f03d50
1165 changed files with 271 additions and 323571 deletions
+12
View File
@@ -0,0 +1,12 @@
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
pub mod models;
pub mod queries;
pub async fn create_pool(database_url: &str, max_connections: u32) -> anyhow::Result<SqlitePool> {
let pool = SqlitePoolOptions::new()
.max_connections(max_connections)
.connect(database_url)
.await?;
Ok(pool)
}
+45
View File
@@ -0,0 +1,45 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents an account bound to a site.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Account {
pub id: i64,
pub site_id: i64,
pub username: Option<String>,
pub access_token: String,
pub api_token: Option<String>,
pub balance: Option<f64>,
pub balance_used: Option<f64>,
pub quota: Option<f64>,
pub unit_cost: Option<f64>,
pub value_score: Option<f64>,
pub status: String,
pub checkin_enabled: Option<bool>,
pub last_checkin_at: Option<String>,
pub last_balance_refresh: Option<String>,
pub extra_config: Option<String>,
pub is_pinned: Option<bool>,
pub sort_order: Option<i64>,
pub oauth_provider: Option<String>,
pub oauth_account_key: Option<String>,
pub oauth_project_id: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
/// Represents an account token (from account_tokens table).
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct AccountToken {
pub id: i64,
pub account_id: i64,
pub name: String,
pub token: String,
pub source: Option<String>,
pub token_group: Option<String>,
pub value_status: String,
pub enabled: Option<bool>,
pub is_default: Option<bool>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
+25
View File
@@ -0,0 +1,25 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Background task for tracking async operations (deployments, syncs, etc.)
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct BackgroundTask {
pub id: String,
pub task_type: String,
pub title: String,
pub status: String,
pub message: Option<String>,
pub error: Option<String>,
pub dedupe_key: Option<String>,
pub success_title: Option<String>,
pub failure_title: Option<String>,
pub notify_on_success: Option<bool>,
pub notify_on_failure: Option<bool>,
pub result_json: Option<String>,
pub progress: Option<i64>,
pub progress_total: Option<i64>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
pub started_at: Option<String>,
pub finished_at: Option<String>,
}
+28
View File
@@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents a downstream API key for external consumers.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct DownstreamApiKey {
pub id: i64,
pub name: String,
pub key: String,
pub description: Option<String>,
pub group_name: Option<String>,
pub tags: Option<String>,
pub enabled: Option<bool>,
pub expires_at: Option<String>,
pub max_cost: Option<f64>,
pub used_cost: Option<f64>,
pub max_requests: Option<i64>,
pub used_requests: Option<i64>,
pub supported_models: Option<String>,
pub allowed_route_ids: Option<String>,
pub site_weight_multipliers: Option<String>,
pub excluded_site_ids: Option<String>,
pub excluded_credential_refs: Option<String>,
pub last_used_at: Option<String>,
pub user_id: Option<i64>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
+18
View File
@@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// System event for admin notifications.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Event {
pub id: i64,
#[sqlx(rename = "type")]
pub event_type: String,
pub title: String,
pub message: Option<String>,
pub level: Option<String>,
#[sqlx(rename = "read")]
pub is_read: Option<bool>,
pub related_id: Option<i64>,
pub related_type: Option<String>,
pub created_at: Option<String>,
}
+13
View File
@@ -0,0 +1,13 @@
// Database models
pub mod account;
pub mod background_task;
pub mod downstream_api_key;
pub mod event;
pub mod organization;
pub mod route_channel;
pub mod setting;
pub mod site;
pub mod site_announcement;
pub mod token_route;
pub mod user;
+39
View File
@@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Organization for multi-tenant setup.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Organization {
pub id: i64,
pub name: String,
pub slug: String,
pub plan: Option<String>,
pub status: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
/// Member of an organization.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct OrganizationMember {
pub id: i64,
pub org_id: i64,
pub user_id: i64,
pub role: Option<String>,
pub status: Option<String>,
pub joined_at: Option<String>,
}
/// Invitation to join an organization.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Invitation {
pub id: i64,
pub org_id: i64,
pub email: String,
pub token: String,
pub role: Option<String>,
pub invited_by: Option<i64>,
pub status: Option<String>,
pub expires_at: Option<String>,
pub created_at: Option<String>,
}
+27
View File
@@ -0,0 +1,27 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents a route channel linking routes, accounts, and tokens.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct RouteChannel {
pub id: i64,
pub route_id: i64,
pub account_id: i64,
pub token_id: Option<i64>,
pub oauth_route_unit_id: Option<i64>,
pub source_model: Option<String>,
pub priority: Option<i64>,
pub weight: Option<i64>,
pub enabled: Option<bool>,
pub manual_override: Option<bool>,
pub success_count: Option<i64>,
pub fail_count: Option<i64>,
pub total_latency_ms: Option<i64>,
pub total_cost: Option<f64>,
pub last_used_at: Option<String>,
pub last_selected_at: Option<String>,
pub last_fail_at: Option<String>,
pub consecutive_fail_count: i64,
pub cooldown_level: i64,
pub cooldown_until: Option<String>,
}
+9
View File
@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents a key-value setting.
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Setting {
pub key: String,
pub value: Option<String>,
}
+26
View File
@@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents a site (AI provider endpoint).
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Site {
pub id: i64,
pub name: String,
pub url: String,
pub platform: String,
pub status: String,
pub api_key: Option<String>,
pub proxy_url: Option<String>,
pub use_system_proxy: Option<bool>,
pub custom_headers: Option<String>,
pub external_checkin_url: Option<String>,
pub global_weight: Option<f64>,
pub post_refresh_probe_enabled: Option<bool>,
pub post_refresh_probe_model: Option<String>,
pub post_refresh_probe_scope: Option<String>,
pub post_refresh_probe_latency_threshold_ms: Option<i64>,
pub is_pinned: Option<bool>,
pub sort_order: Option<i64>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
+24
View File
@@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Site-level announcements (notifications from upstream providers).
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct SiteAnnouncement {
pub id: i64,
pub site_id: i64,
pub platform: String,
pub source_key: String,
pub title: String,
pub content: String,
pub level: Option<String>,
pub source_url: Option<String>,
pub starts_at: Option<String>,
pub ends_at: Option<String>,
pub upstream_created_at: Option<String>,
pub upstream_updated_at: Option<String>,
pub first_seen_at: Option<String>,
pub last_seen_at: Option<String>,
pub read_at: Option<String>,
pub dismissed_at: Option<String>,
pub raw_payload: Option<String>,
}
+19
View File
@@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// Represents a token route (model routing rule).
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct TokenRoute {
pub id: i64,
pub model_pattern: String,
pub model_mapping: Option<String>,
pub display_name: Option<String>,
pub display_icon: Option<String>,
pub decision_snapshot: Option<String>,
pub decision_refreshed_at: Option<String>,
pub routing_strategy: Option<String>,
pub route_mode: Option<String>,
pub enabled: Option<bool>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
+16
View File
@@ -0,0 +1,16 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
/// A platform user (admin or regular).
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct User {
pub id: i64,
pub username: String,
pub email: String,
#[serde(skip_serializing)]
pub password_hash: String,
pub role: String,
pub status: String,
pub created_at: Option<String>,
pub updated_at: Option<String>,
}
+40
View File
@@ -0,0 +1,40 @@
use crate::{
db::models::{downstream_api_key::DownstreamApiKey, user::User},
error::AppResult,
};
use sqlx::SqlitePool;
pub async fn find_user_by_id(db: &SqlitePool, id: i64) -> AppResult<Option<User>> {
sqlx::query_as("SELECT * FROM users WHERE id = ?")
.bind(id)
.fetch_optional(db)
.await
.map_err(Into::into)
}
pub async fn find_user_by_email(db: &SqlitePool, email: &str) -> AppResult<Option<User>> {
sqlx::query_as("SELECT * FROM users WHERE email = ?")
.bind(email)
.fetch_optional(db)
.await
.map_err(Into::into)
}
pub async fn list_user_api_keys(db: &SqlitePool, user_id: i64) -> AppResult<Vec<DownstreamApiKey>> {
sqlx::query_as("SELECT * FROM downstream_api_keys WHERE user_id = ? ORDER BY created_at DESC")
.bind(user_id)
.fetch_all(db)
.await
.map_err(Into::into)
}
pub async fn find_downstream_key_by_token(
db: &SqlitePool,
token: &str,
) -> AppResult<Option<DownstreamApiKey>> {
sqlx::query_as("SELECT * FROM downstream_api_keys WHERE key = ?")
.bind(token)
.fetch_optional(db)
.await
.map_err(Into::into)
}