c949204662
借鉴 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 应用迁移并烟测。
167 lines
4.2 KiB
Go
167 lines
4.2 KiB
Go
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 基础 URL(OpenAI 兼容格式)
|
||
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
|
||
}
|