Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
13 KiB
JSON
1 line
13 KiB
JSON
{"repo": "0furkancolak/fur-chat", "files": {"server/src/config.rs": {"37ab503aa25e293a670df0a963f78ce4d9403160": {"t": 1785745878.8566713, "n": 2140, "h": "7b66073ecaa486cd", "c": "use std::env;\n\n#[derive(Clone, Debug)]\npub struct Config {\n pub host: String,\n pub port: u16,\n pub database_url: String,\n pub deepseek_api_key: String,\n pub deepseek_base_url: String,\n pub deepseek_model: String,\n pub available_models: Vec<String>,\n pub session_secret: String,\n pub cookie_secure: bool,\n pub client_origin: String,\n /// When set, serve built Astro static files (production single-process deploy).\n pub serve_client_dist: Option<String>,\n}\n\nimpl Config {\n pub fn from_env() -> Result<Self, String> {\n Ok(Self {\n host: env::var(\"HOST\").unwrap_or_else(|_| \"127.0.0.1\".into()),\n port: env::var(\"PORT\")\n .unwrap_or_else(|_| \"8080\".into())\n .parse()\n .map_err(|_| \"PORT must be a number\")?,\n database_url: env::var(\"DATABASE_URL\")\n .unwrap_or_else(|_| \"sqlite:data/app.db?mode=rwc\".into()),\n deepseek_api_key: env::var(\"DEEPSEEK_API_KEY\")\n .map_err(|_| \"DEEPSEEK_API_KEY is required\")?,\n deepseek_base_url: env::var(\"DEEPSEEK_BASE_URL\")\n .unwrap_or_else(|_| \"https://api.deepseek.com\".into()),\n deepseek_model: env::var(\"DEEPSEEK_MODEL\")\n .unwrap_or_else(|_| \"deepseek-chat\".into()),\n available_models: env::var(\"DEEPSEEK_MODELS\")\n .unwrap_or_else(|_| \"deepseek-chat,deepseek-reasoner\".into())\n .split(',')\n .map(|s| s.trim().to_string())\n .filter(|s| !s.is_empty())\n .collect(),\n session_secret: env::var(\"SESSION_SECRET\")\n .map_err(|_| \"SESSION_SECRET is required\")?,\n cookie_secure: env::var(\"COOKIE_SECURE\")\n .map(|v| v == \"true\" || v == \"1\")\n .unwrap_or(false),\n client_origin: env::var(\"CLIENT_ORIGIN\")\n .or_else(|_| env::var(\"FRONTEND_ORIGIN\"))\n .unwrap_or_else(|_| \"http://localhost:4321\".into()),\n serve_client_dist: env::var(\"SERVE_CLIENT_DIST\").ok().filter(|s| !s.is_empty()),\n })\n }\n}\n"}, "48f1f4dcb0066a377528fd5d4b4eb401f93ac0ab": {"t": 1785757484.9178479, "n": 2140, "h": "7b66073ecaa486cd", "c": "use std::env;\n\n#[derive(Clone, Debug)]\npub struct Config {\n pub host: String,\n pub port: u16,\n pub database_url: String,\n pub deepseek_api_key: String,\n pub deepseek_base_url: String,\n pub deepseek_model: String,\n pub available_models: Vec<String>,\n pub session_secret: String,\n pub cookie_secure: bool,\n pub client_origin: String,\n /// When set, serve built Astro static files (production single-process deploy).\n pub serve_client_dist: Option<String>,\n}\n\nimpl Config {\n pub fn from_env() -> Result<Self, String> {\n Ok(Self {\n host: env::var(\"HOST\").unwrap_or_else(|_| \"127.0.0.1\".into()),\n port: env::var(\"PORT\")\n .unwrap_or_else(|_| \"8080\".into())\n .parse()\n .map_err(|_| \"PORT must be a number\")?,\n database_url: env::var(\"DATABASE_URL\")\n .unwrap_or_else(|_| \"sqlite:data/app.db?mode=rwc\".into()),\n deepseek_api_key: env::var(\"DEEPSEEK_API_KEY\")\n .map_err(|_| \"DEEPSEEK_API_KEY is required\")?,\n deepseek_base_url: env::var(\"DEEPSEEK_BASE_URL\")\n .unwrap_or_else(|_| \"https://api.deepseek.com\".into()),\n deepseek_model: env::var(\"DEEPSEEK_MODEL\")\n .unwrap_or_else(|_| \"deepseek-chat\".into()),\n available_models: env::var(\"DEEPSEEK_MODELS\")\n .unwrap_or_else(|_| \"deepseek-chat,deepseek-reasoner\".into())\n .split(',')\n .map(|s| s.trim().to_string())\n .filter(|s| !s.is_empty())\n .collect(),\n session_secret: env::var(\"SESSION_SECRET\")\n .map_err(|_| \"SESSION_SECRET is required\")?,\n cookie_secure: env::var(\"COOKIE_SECURE\")\n .map(|v| v == \"true\" || v == \"1\")\n .unwrap_or(false),\n client_origin: env::var(\"CLIENT_ORIGIN\")\n .or_else(|_| env::var(\"FRONTEND_ORIGIN\"))\n .unwrap_or_else(|_| \"http://localhost:4321\".into()),\n serve_client_dist: env::var(\"SERVE_CLIENT_DIST\").ok().filter(|s| !s.is_empty()),\n })\n }\n}\n"}}, "server/.env.example": {"37ab503aa25e293a670df0a963f78ce4d9403160": {"t": 1785747139.5637527, "n": 1698, "h": "dafec208784db2b1", "c": "# ==============================================================================\n# Fur Chat — Server environment\n# ==============================================================================\n# Copy this file to `.env` in the same directory and fill in required values.\n# Never commit `.env` to version control.\n# ==============================================================================\n\n# --- HTTP server ----------------------------------------------------------------\nHOST=127.0.0.1\nPORT=8080\n\n# --- Client app (CORS + cookie scope) -------------------------------------------\n# Development (Astro dev server):\nCLIENT_ORIGIN=http://localhost:4321\n# Production (same host as server when using make start):\n# CLIENT_ORIGIN=http://127.0.0.1:8080\n\n# --- Production static client (optional) --------------------------------------\n# Path to `client/dist` — server serves UI + API on one port.\n# SERVE_CLIENT_DIST=../client/dist\n\n# --- Database -------------------------------------------------------------------\n# SQLite file is created under server/data/ when missing.\nDATABASE_URL=sqlite:data/app.db?mode=rwc\n\n# --- Authentication / sessions --------------------------------------------------\n# Use a long random string (32+ characters) in production.\nSESSION_SECRET=change-me-to-a-long-random-string\n# true = Secure cookie flag (requires HTTPS in production)\nCOOKIE_SECURE=false\n\n# --- DeepSeek LLM ---------------------------------------------------------------\n# API key: https://platform.deepseek.com/\nDEEPSEEK_API_KEY=\nDEEPSEEK_BASE_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n# Comma-separated list for client model picker\nDEEPSEEK_MODELS=deepseek-chat,deepseek-reasoner\n"}, "48f1f4dcb0066a377528fd5d4b4eb401f93ac0ab": {"t": 1785757484.7118914, "n": 1697, "h": "5096c52fbd544518", "c": "# ==============================================================================\n# Fur Chat — Sunucu ortam değişkenleri\n# ==============================================================================\n# Bu dosyayı aynı dizinde `.env` olarak kopyalayın ve zorunlu alanları doldurun.\n# `.env` dosyasını asla sürüm kontrolüne eklemeyin.\n# ==============================================================================\n\n# --- HTTP sunucu --------------------------------------------------------------\nHOST=127.0.0.1\nPORT=8080\n\n# --- İstemci uygulaması (CORS + çerez kapsamı) ----------------------------------\n# Geliştirme (Astro dev sunucusu):\nCLIENT_ORIGIN=http://localhost:4321\n# Üretim (make start ile tek port):\n# CLIENT_ORIGIN=http://127.0.0.1:8080\n\n# --- Statik client (isteğe bağlı, üretim) --------------------------------------\n# `client/dist` yolu — sunucu arayüz + API’yi tek portta sunar.\n# SERVE_CLIENT_DIST=../client/dist\n\n# --- Veritabanı ---------------------------------------------------------------\n# SQLite dosyası yoksa server/data/ altında oluşturulur.\nDATABASE_URL=sqlite:data/app.db?mode=rwc\n\n# --- Kimlik doğrulama / oturum ------------------------------------------------\n# Üretimde uzun rastgele bir dize kullanın (32+ karakter).\nSESSION_SECRET=change-me-to-a-long-random-string\n# true = Secure çerez bayrağı (üretimde HTTPS gerekir)\nCOOKIE_SECURE=false\n\n# --- DeepSeek LLM -------------------------------------------------------------\n# API anahtarı: https://platform.deepseek.com/\nDEEPSEEK_API_KEY=\nDEEPSEEK_BASE_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n# İstemci model seçici için virgülle ayrılmış liste\nDEEPSEEK_MODELS=deepseek-chat,deepseek-reasoner\n"}}, "server/src/deepseek.rs": {"37ab503aa25e293a670df0a963f78ce4d9403160": {"t": 1786067493.071253, "n": 4634, "h": "d4277fce099d5019", "c": "use std::pin::Pin;\n\nuse futures::{Stream, StreamExt};\nuse reqwest::Client;\nuse serde::{Deserialize, Serialize};\nuse serde_json::Value;\n\nuse crate::config::Config;\nuse crate::error::{AppError, AppResult};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ChatMessage {\n pub role: String,\n pub content: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\npub struct TokenUsage {\n pub prompt_tokens: i64,\n pub completion_tokens: i64,\n pub total_tokens: i64,\n}\n\n#[derive(Debug, Clone)]\npub struct StreamChunk {\n pub delta: String,\n pub usage: Option<TokenUsage>,\n}\n\npub async fn stream_chat_completion(\n config: &Config,\n model: &str,\n messages: Vec<ChatMessage>,\n) -> AppResult<Pin<Box<dyn Stream<Item = AppResult<StreamChunk>> + Send>>> {\n let client = Client::new();\n let url = format!(\n \"{}/v1/chat/completions\",\n config.deepseek_base_url.trim_end_matches('/')\n );\n\n let body = serde_json::json!({\n \"model\": model,\n \"messages\": messages,\n \"stream\": true,\n \"stream_options\": { \"include_usage\": true }\n });\n\n let response = client\n .post(&url)\n .bearer_auth(&config.deepseek_api_key)\n .json(&body)\n .send()\n .await?;\n\n if !response.status().is_success() {\n let status = response.status();\n let text = response.text().await.unwrap_or_default();\n return Err(AppError::Internal(format!(\n \"DeepSeek error {status}: {text}\"\n )));\n }\n\n let byte_stream = response.bytes_stream();\n let stream = async_stream::stream! {\n let mut buffer = String::new();\n futures::pin_mut!(byte_stream);\n while let Some(chunk) = byte_stream.next().await {\n let chunk = chunk.map_err(AppError::Reqwest)?;\n buffer.push_str(&String::from_utf8_lossy(&chunk));\n while let Some(pos) = buffer.find(\"\\n\\n\") {\n let frame = buffer[..pos].to_string();\n buffer.drain(..pos + 2);\n if let Some(parsed) = parse_sse_frame(&(frame + \"\\n\\n\")) {\n yield Ok(parsed);\n }\n }\n }\n if !buffer.trim().is_empty() {\n if let Some(parsed) = parse_sse_frame(&buffer) {\n yield Ok(parsed);\n }\n }\n };\n\n Ok(Box::pin(stream))\n}\n\npub async fn complete_chat(\n config: &Config,\n model: &str,\n messages: Vec<ChatMessage>,\n) -> AppResult<(String, TokenUsage)> {\n let client = Client::new();\n let url = format!(\n \"{}/v1/chat/completions\",\n config.deepseek_base_url.trim_end_matches('/')\n );\n\n let body = serde_json::json!({\n \"model\": model,\n \"messages\": messages,\n \"stream\": false\n });\n\n let response = client\n .post(&url)\n .bearer_auth(&config.deepseek_api_key)\n .json(&body)\n .send()\n .await?;\n\n if !response.status().is_success() {\n let status = response.status();\n let text = response.text().await.unwrap_or_default();\n return Err(AppError::Internal(format!(\n \"DeepSeek error {status}: {text}\"\n )));\n }\n\n let value: Value = response.json().await?;\n let content = value[\"choices\"][0][\"message\"][\"content\"]\n .as_str()\n .unwrap_or(\"\")\n .trim()\n .to_string();\n let usage = parse_usage(&value[\"usage\"]).unwrap_or_default();\n Ok((content, usage))\n}\n\nfn parse_sse_frame(frame: &str) -> Option<StreamChunk> {\n for line in frame.lines() {\n let line = line.trim();\n if let Some(data) = line.strip_prefix(\"data: \") {\n if data == \"[DONE]\" {\n return Some(StreamChunk {\n delta: String::new(),\n usage: None,\n });\n }\n if let Ok(value) = serde_json::from_str::<Value>(data) {\n let delta = value[\"choices\"][0][\"delta\"][\"content\"]\n .as_str()\n .unwrap_or(\"\")\n .to_string();\n let usage = parse_usage(&value[\"usage\"]);\n return Some(StreamChunk {\n delta,\n usage,\n });\n }\n }\n }\n None\n}\n\nfn parse_usage(value: &Value) -> Option<TokenUsage> {\n if value.is_null() {\n return None;\n }\n Some(TokenUsage {\n prompt_tokens: value[\"prompt_tokens\"].as_i64().unwrap_or(0),\n completion_tokens: value[\"completion_tokens\"].as_i64().unwrap_or(0),\n total_tokens: value[\"total_tokens\"].as_i64().unwrap_or(0),\n })\n}\n"}}}} |