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
+6
View File
@@ -0,0 +1,6 @@
// Provider profiles & registry
pub mod registry;
pub mod types;
pub use registry::ProviderRegistry;
pub use types::{ProviderCategory, ProviderProfile, RouteResolution};
+134
View File
@@ -0,0 +1,134 @@
// Provider registry — holds all configured upstream provider profiles
use crate::proxy::providers::types::{ProviderCategory, ProviderProfile, RouteResolution};
use std::collections::HashMap;
/// Registry of all available provider profiles
#[derive(Debug, Clone)]
pub struct ProviderRegistry {
providers: HashMap<String, ProviderProfile>,
}
impl ProviderRegistry {
/// Create a new registry with default providers
pub fn new() -> Self {
let mut registry = Self {
providers: HashMap::new(),
};
registry.register_defaults();
registry
}
/// Register the built-in default providers
fn register_defaults(&mut self) {
// OpenAI
self.register(ProviderProfile::openai_compatible(
"openai",
"OpenAI",
"https://api.openai.com/v1",
));
// Anthropic
self.register(ProviderProfile::anthropic(
"anthropic",
"Anthropic",
"https://api.anthropic.com/v1",
));
// Google Gemini
self.register(ProviderProfile::gemini(
"gemini",
"Google Gemini",
"https://generativelanguage.googleapis.com/v1beta",
));
// Codex
self.register(ProviderProfile {
id: "codex".into(),
name: "OpenAI Codex".into(),
category: ProviderCategory::Codex,
base_url: "https://api.openai.com/v1".into(),
default_model: Some("codex".into()),
requires_api_key: true,
supports_streaming: true,
headers: HashMap::new(),
});
// Claude Code
self.register(ProviderProfile {
id: "claude".into(),
name: "Claude Code".into(),
category: ProviderCategory::Claude,
base_url: "https://api.anthropic.com/v1".into(),
default_model: Some("claude-sonnet-4".into()),
requires_api_key: true,
supports_streaming: true,
headers: HashMap::new(),
});
// Gemini CLI
self.register(ProviderProfile {
id: "gemini_cli".into(),
name: "Gemini CLI".into(),
category: ProviderCategory::GeminiCli,
base_url: "https://generativelanguage.googleapis.com/v1beta".into(),
default_model: Some("gemini-2.0-flash".into()),
requires_api_key: true,
supports_streaming: true,
headers: HashMap::new(),
});
}
/// Register a new provider profile
pub fn register(&mut self, profile: ProviderProfile) {
self.providers.insert(profile.id.clone(), profile);
}
/// Get a provider by ID
pub fn get(&self, id: &str) -> Option<&ProviderProfile> {
self.providers.get(id)
}
/// List all registered providers
pub fn list(&self) -> Vec<&ProviderProfile> {
self.providers.values().collect()
}
/// Remove a provider by ID
pub fn remove(&mut self, id: &str) -> Option<ProviderProfile> {
self.providers.remove(id)
}
/// Resolve a model name to an upstream route.
/// Tries to match the model to a provider category, falls back to first OpenAI-compatible provider.
pub fn resolve(&self, model: &str, api_key: &str) -> Option<RouteResolution> {
// First try to find a provider that supports this model
for provider in self.providers.values() {
if provider.supports_model(model) {
let base_url = provider.base_url.clone();
return Some(RouteResolution {
provider: provider.clone(),
api_key: api_key.to_string(),
model: model.to_string(),
base_url,
});
}
}
// Fall back to the first OpenAI-compatible provider
self.providers
.values()
.find(|p| {
matches!(
p.category,
ProviderCategory::OpenAi | ProviderCategory::GenericOpenAi
)
})
.map(|provider| RouteResolution {
provider: provider.clone(),
api_key: api_key.to_string(),
model: model.to_string(),
base_url: provider.base_url.clone(),
})
}
}
impl Default for ProviderRegistry {
fn default() -> Self {
Self::new()
}
}
+137
View File
@@ -0,0 +1,137 @@
// Provider profile types — upstream API provider definitions
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Provider category — determines request transformation and routing behavior
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ProviderCategory {
/// OpenAI-compatible chat/completions API
OpenAi,
/// Anthropic Messages API
Anthropic,
/// Google Gemini API
Gemini,
/// Codex CLI / Responses API
Codex,
/// Claude Code CLI
Claude,
/// Gemini CLI
GeminiCli,
/// Antigravity / assistant-style API
Antigravity,
/// Generic OpenAI-compatible proxy (new-api, one-api, etc.)
GenericOpenAi,
}
impl std::fmt::Display for ProviderCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::OpenAi => write!(f, "openai"),
Self::Anthropic => write!(f, "anthropic"),
Self::Gemini => write!(f, "gemini"),
Self::Codex => write!(f, "codex"),
Self::Claude => write!(f, "claude"),
Self::GeminiCli => write!(f, "gemini_cli"),
Self::Antigravity => write!(f, "antigravity"),
Self::GenericOpenAi => write!(f, "generic_openai"),
}
}
}
/// Provider configuration profile
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProviderProfile {
pub id: String,
pub name: String,
pub category: ProviderCategory,
pub base_url: String,
pub default_model: Option<String>,
pub requires_api_key: bool,
pub supports_streaming: bool,
pub headers: HashMap<String, String>,
}
impl ProviderProfile {
/// Create a standard OpenAI-compatible provider
pub fn openai_compatible(id: &str, name: &str, base_url: &str) -> Self {
let mut headers = HashMap::new();
headers.insert("Content-Type".into(), "application/json".into());
Self {
id: id.to_string(),
name: name.to_string(),
category: ProviderCategory::GenericOpenAi,
base_url: base_url.trim_end_matches('/').to_string(),
default_model: None,
requires_api_key: true,
supports_streaming: true,
headers,
}
}
/// Create an Anthropic Messages API provider
pub fn anthropic(id: &str, name: &str, base_url: &str) -> Self {
let mut headers = HashMap::new();
headers.insert("Content-Type".into(), "application/json".into());
headers.insert("anthropic-version".into(), "2023-06-01".into());
Self {
id: id.to_string(),
name: name.to_string(),
category: ProviderCategory::Anthropic,
base_url: base_url.trim_end_matches('/').to_string(),
default_model: None,
requires_api_key: true,
supports_streaming: true,
headers,
}
}
/// Create a Gemini API provider
pub fn gemini(id: &str, name: &str, base_url: &str) -> Self {
let mut headers = HashMap::new();
headers.insert("Content-Type".into(), "application/json".into());
Self {
id: id.to_string(),
name: name.to_string(),
category: ProviderCategory::Gemini,
base_url: base_url.trim_end_matches('/').to_string(),
default_model: None,
requires_api_key: true,
supports_streaming: true,
headers,
}
}
/// Check if this provider handles the given model
pub fn supports_model(&self, model: &str) -> bool {
let model_lower = model.to_lowercase();
match self.category {
ProviderCategory::OpenAi | ProviderCategory::GenericOpenAi => {
model_lower.contains("gpt")
|| model_lower.contains("o1")
|| model_lower.contains("o3")
|| model_lower.contains("dall-e")
}
ProviderCategory::Anthropic => model_lower.contains("claude"),
ProviderCategory::Gemini => model_lower.contains("gemini"),
ProviderCategory::Codex => model_lower.contains("codex"),
ProviderCategory::Claude => {
model_lower.contains("claude") && model_lower.contains("code")
}
ProviderCategory::GeminiCli => {
model_lower.contains("gemini") && model_lower.contains("cli")
}
ProviderCategory::Antigravity => {
model_lower.contains("antigravity") || model_lower.contains("assistant")
}
}
}
}
/// Result of resolving a model to an upstream route
#[derive(Debug, Clone)]
pub struct RouteResolution {
pub provider: ProviderProfile,
pub api_key: String,
pub model: String,
pub base_url: String,
}