Files
GovAI/server/internal/config/config.go
T
freedakgmail c949204662 feat(govai): 0617 优化首批 — 安全/私有化/深度研究/服务层/可观测性
借鉴 odysseus 的能力设计,全程净室实现、零 AGPL 代码、不引入 AGPL 依赖。

T1 提示注入防护: pkg/promptguard 包裹外部/知识库内容为不可信数据,buildMessages 移出 system 指令区。 T2 安全 CI: .github/workflows(ci+security: govulncheck/gitleaks/actionlint/hadolint/trivy)+dependabot+.hadolint.yaml;go.mod 加 toolchain go1.25.11 修复 20 个 stdlib CVE。 T3 管理员 2FA: 迁移 000016 + RFC6238 TOTP/备份码(pkg/auth, 零依赖) + 登录流程集成(后端)。 T4 本地模型: LLM/embedding 支持本地 vLLM/Ollama(OpenAI 兼容, 鉴权头条件发送, NoAuth) + docs/local-deploy.md。 T6 深度研究: 迁移 000017 + Python research-worker(净室多步流水线, 检索避开 SearXNG) + Go research 服务/handler/路由。 T7 service 层: 新增 internal/service/{research,twofa}, 2FA 业务逻辑从胖 handler 下沉, 接口注入可单测。 T10 缓存/可观测性: internal/cache(Redis+内存, 优雅降级) 接入 store 热点列表; Prometheus 指标+/metrics; docs/openapi.yaml。 验证: go build/vet/test ./... 全绿(8 包); research-worker 12 单测过; 真实 PG 应用迁移并烟测。
2026-06-17 17:52:47 +08:00

167 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"os"
"strconv"
"strings"
"time"
)
type Config struct {
Server ServerConfig
Database DatabaseConfig
Redis RedisConfig
JWT JWTConfig
Dify DifyConfig
LLM LLMConfig
Embedding EmbeddingConfig
Gateway GatewayConfig
MinIO MinIOConfig
PPTWorker PPTWorkerConfig
}
type PPTWorkerConfig struct {
URL string // PPT Worker 微服务地址
}
type LLMConfig struct {
Provider string // "openai" / "anthropic" / "local"
OpenAIKey string
OpenAIBaseURL string
OpenAIModel string
AnthropicKey string
AnthropicBaseURL string
AnthropicModel string
// 本地推理(私有化):OpenAI 兼容端点,如 vLLM(http://host:8000/v1) / Ollama(http://host:11434/v1)
LocalBaseURL string
LocalModel string
LocalKey string // 多数本地服务无需密钥,可留空
}
type EmbeddingConfig struct {
APIKey string // Embedding API 密钥
BaseURL string // Embedding API 基础 URLOpenAI 兼容格式)
Model string // 向量模型名称
Dimensions int // 向量维度(必须与所用模型及 pgvector 列维度一致)
NoAuth bool // 本地无鉴权端点时置 true
}
type ServerConfig struct {
Host string
Port string
}
type DatabaseConfig struct {
URL string
}
type RedisConfig struct {
URL string
}
type JWTConfig struct {
Secret string
AccessExpiry time.Duration
RefreshExpiry time.Duration
}
type DifyConfig struct {
APIURL string
APIKey string
}
type GatewayConfig struct {
URL string
}
type MinIOConfig struct {
Endpoint string
AccessKey string
SecretKey string
Bucket string
UseSSL bool
}
func Load() *Config {
return &Config{
Server: ServerConfig{
Host: getEnv("SERVER_HOST", "0.0.0.0"),
Port: getEnv("SERVER_PORT", "8080"),
},
Database: DatabaseConfig{
URL: getEnv("DATABASE_URL", "postgres://localhost:5432/govai_portal?sslmode=disable"),
},
Redis: RedisConfig{
URL: getEnv("REDIS_URL", "redis://localhost:6379"),
},
JWT: JWTConfig{
Secret: getEnv("JWT_SECRET", "dev-secret-change-in-production"),
AccessExpiry: 24 * time.Hour,
RefreshExpiry: 7 * 24 * time.Hour,
},
Dify: DifyConfig{
APIURL: getEnv("DIFY_API_URL", "http://localhost:5001/v1"),
APIKey: getEnv("DIFY_API_KEY", ""),
},
LLM: LLMConfig{
Provider: getEnv("LLM_PROVIDER", "openai"),
OpenAIKey: getEnv("OPENAI_API_KEY", ""),
OpenAIBaseURL: getEnv("OPENAI_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
OpenAIModel: getEnv("OPENAI_MODEL", "qwen-plus"),
AnthropicKey: getEnv("ANTHROPIC_API_KEY", ""),
AnthropicBaseURL: getEnv("ANTHROPIC_BASE_URL", "https://api.anthropic.com"),
AnthropicModel: getEnv("ANTHROPIC_MODEL", "claude-sonnet-4-20250514"),
LocalBaseURL: getEnv("LOCAL_LLM_BASE_URL", ""),
LocalModel: getEnv("LOCAL_LLM_MODEL", ""),
LocalKey: getEnv("LOCAL_LLM_API_KEY", ""),
},
Embedding: EmbeddingConfig{
APIKey: getEnv("EMBEDDING_API_KEY", ""),
BaseURL: getEnv("EMBEDDING_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
Model: getEnv("EMBEDDING_MODEL", "text-embedding-v3"),
Dimensions: getEnvInt("EMBEDDING_DIMENSIONS", 1024),
NoAuth: getEnvBool("EMBEDDING_NO_AUTH", false),
},
Gateway: GatewayConfig{
URL: getEnv("MODEL_GATEWAY_URL", "http://localhost:8081"),
},
MinIO: MinIOConfig{
Endpoint: getEnv("MINIO_ENDPOINT", "localhost:9000"),
AccessKey: getEnv("MINIO_ACCESS_KEY", "minioadmin"),
SecretKey: getEnv("MINIO_SECRET_KEY", "minioadmin"),
Bucket: getEnv("MINIO_BUCKET", "aily-files"),
UseSSL: false,
},
PPTWorker: PPTWorkerConfig{
URL: getEnv("PPT_WORKER_URL", "http://localhost:8090"),
},
}
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
func getEnvBool(key string, fallback bool) bool {
switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
default:
return fallback
}
}
func getEnvInt(key string, fallback int) int {
if v := os.Getenv(key); v != "" {
if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil {
return n
}
}
return fallback
}