185 lines
5.4 KiB
Rust
185 lines
5.4 KiB
Rust
use anyhow::Result;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use std::env;
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct Config {
|
|
pub site: SiteConfig,
|
|
pub build: BuildConfig,
|
|
pub ai: Option<AiConfig>,
|
|
pub oauth: Option<OAuthConfig>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct SiteConfig {
|
|
pub title: String,
|
|
pub description: String,
|
|
pub base_url: String,
|
|
pub language: String,
|
|
pub author: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct BuildConfig {
|
|
pub highlight_code: bool,
|
|
pub highlight_theme: Option<String>,
|
|
pub minify: bool,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct AiConfig {
|
|
pub enabled: bool,
|
|
pub auto_translate: bool,
|
|
pub comment_moderation: bool,
|
|
pub ask_ai: Option<bool>,
|
|
pub provider: Option<String>,
|
|
pub model: Option<String>,
|
|
pub host: Option<String>,
|
|
pub system_prompt: Option<String>,
|
|
pub handle: Option<String>,
|
|
pub ai_did: Option<String>,
|
|
pub api_key: Option<String>,
|
|
pub gpt_endpoint: Option<String>,
|
|
pub atproto_config: Option<AtprotoConfig>,
|
|
pub num_predict: Option<i32>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct OAuthConfig {
|
|
pub json: Option<String>,
|
|
pub redirect: Option<String>,
|
|
pub admin: Option<String>,
|
|
pub collection: Option<String>,
|
|
pub pds: Option<String>,
|
|
pub handle_list: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct AtprotoConfig {
|
|
pub client_id: String,
|
|
pub redirect_uri: String,
|
|
pub handle_resolver: String,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn load(path: &Path) -> Result<Self> {
|
|
let config_path = path.join("config.toml");
|
|
let content = fs::read_to_string(config_path)?;
|
|
let mut config: Config = toml::from_str(&content)?;
|
|
|
|
// Load global config and merge
|
|
if let Ok(global_config) = Self::load_global_config() {
|
|
config = config.merge(global_config);
|
|
}
|
|
|
|
// Override with environment variables
|
|
config.override_from_env();
|
|
|
|
Ok(config)
|
|
}
|
|
|
|
fn load_global_config() -> Result<Config> {
|
|
let config_dir = Self::global_config_dir();
|
|
let config_path = config_dir.join("config.toml");
|
|
|
|
if config_path.exists() {
|
|
let content = fs::read_to_string(config_path)?;
|
|
let config: Config = toml::from_str(&content)?;
|
|
Ok(config)
|
|
} else {
|
|
anyhow::bail!("Global config not found")
|
|
}
|
|
}
|
|
|
|
pub fn global_config_dir() -> PathBuf {
|
|
if let Ok(home) = env::var("HOME") {
|
|
PathBuf::from(home).join(".config").join("syui").join("ai").join("log")
|
|
} else {
|
|
PathBuf::from("~/.config/syui/ai/log")
|
|
}
|
|
}
|
|
|
|
fn merge(mut self, global: Config) -> Self {
|
|
// Merge AI config
|
|
if let Some(global_ai) = global.ai {
|
|
if let Some(ref mut ai) = self.ai {
|
|
if ai.api_key.is_none() {
|
|
ai.api_key = global_ai.api_key;
|
|
}
|
|
if ai.gpt_endpoint.is_none() {
|
|
ai.gpt_endpoint = global_ai.gpt_endpoint;
|
|
}
|
|
if ai.atproto_config.is_none() {
|
|
ai.atproto_config = global_ai.atproto_config;
|
|
}
|
|
} else {
|
|
self.ai = Some(global_ai);
|
|
}
|
|
}
|
|
self
|
|
}
|
|
|
|
fn override_from_env(&mut self) {
|
|
if let Ok(api_key) = env::var("AILOG_API_KEY") {
|
|
if let Some(ref mut ai) = self.ai {
|
|
ai.api_key = Some(api_key);
|
|
}
|
|
}
|
|
|
|
if let Ok(endpoint) = env::var("AILOG_GPT_ENDPOINT") {
|
|
if let Some(ref mut ai) = self.ai {
|
|
ai.gpt_endpoint = Some(endpoint);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn save_global(&self) -> Result<()> {
|
|
let config_dir = Self::global_config_dir();
|
|
fs::create_dir_all(&config_dir)?;
|
|
|
|
let config_path = config_dir.join("config.toml");
|
|
let content = toml::to_string_pretty(self)?;
|
|
fs::write(config_path, content)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Self {
|
|
site: SiteConfig {
|
|
title: "My Blog".to_string(),
|
|
description: "A blog powered by ailog".to_string(),
|
|
base_url: "https://example.com".to_string(),
|
|
language: "ja".to_string(),
|
|
author: None,
|
|
},
|
|
build: BuildConfig {
|
|
highlight_code: true,
|
|
highlight_theme: Some("Monokai".to_string()),
|
|
minify: false,
|
|
},
|
|
ai: Some(AiConfig {
|
|
enabled: false,
|
|
auto_translate: false,
|
|
comment_moderation: false,
|
|
ask_ai: Some(false),
|
|
provider: Some("ollama".to_string()),
|
|
model: Some("gemma3:4b".to_string()),
|
|
host: None,
|
|
system_prompt: Some("You are a helpful AI assistant trained on this blog's content.".to_string()),
|
|
handle: None,
|
|
ai_did: None,
|
|
api_key: None,
|
|
gpt_endpoint: None,
|
|
atproto_config: None,
|
|
num_predict: None,
|
|
}),
|
|
oauth: None,
|
|
}
|
|
}
|
|
} |