1 line
19 KiB
JSON
1 line
19 KiB
JSON
{"repo": "akcayfx/sentrix", "files": {"backend/internal/config/config.go": {"0e83d2fbd9c17aa860cc50700bf824e0e3921e72": {"t": 1785745642.235369, "n": 8291, "h": "e01c9afabf5ff4ed", "c": "package config\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Config struct {\n\tServer ServerConfig\n\tDatabase DatabaseConfig\n\tAuth AuthConfig\n\tLog LogConfig\n\tLLM LLMConfig\n\tAgent AgentConfig\n\tSearch SearchConfig\n\tDocker DockerConfig\n\tEmbedding EmbeddingConfig\n\tObservability ObservabilityConfig\n\tScraper ScraperConfig\n}\n\ntype ScraperConfig struct {\n\tPublicURL string // Full base URL for public targets, may include credentials\n\tPrivateURL string // Full base URL for private/internal targets, may include credentials\n}\n\ntype DockerConfig struct {\n\tEnabled bool\n\tSocketPath string\n\tDefaultImage string\n\tNetwork string\n\tDataDir string\n\tCPULimit float64\n\tMemoryLimitMB int\n\tContainerTimeout int // seconds\n\tInside bool // true when sentrix itself runs inside Docker\n}\n\ntype EmbeddingConfig struct {\n\tProvider string // \"openai\", \"ollama\", \"none\"\n\tModel string\n\tAPIKey string\n\tBaseURL string\n\tDimensions int\n\tBatchSize int\n}\n\ntype ObservabilityConfig struct {\n\tEnabled bool\n\tServiceName string\n\tOTLPEndpoint string\n\tTraceSampleRate float64\n\tLangfuseEnabled bool\n\tLangfusePublicKey string\n\tLangfuseSecretKey string\n\tLangfuseHost string\n}\n\ntype AgentConfig struct {\n\tWorkers int\n\tSameToolLimit int\n\tTotalToolLimit int\n\tTaskToolLimit int // max total tool calls across all subtasks in one task (0 = TotalToolLimit * 3)\n\tSqlmapTimeoutSeconds int\n}\n\ntype LLMConfig struct {\n\tOpenAIKey string\n\tAnthropicKey string\n\tGeminiKey string\n\tDeepSeekKey string\n\tOllamaURL string\n\tCustomURL string\n\tCustomModel string\n\tCustomAPIKey string\n\tDefaultProvider string\n}\n\ntype SearchConfig struct {\n\tDefaultMaxResults int\n\tTimeoutSeconds int\n\tProviderPriority []string\n\n\tDuckDuckGoEnabled bool\n\n\tGoogleAPIKey string\n\tGoogleCX string\n\n\tTavilyAPIKey string\n\n\tTraversaalAPIKey string\n\n\tPerplexityAPIKey string\n\tPerplexityModel string\n\n\tSearxngURL string\n\tSearxngLanguage string\n\tSearxngCategories string\n\tSearxngSafeSearch string\n\n\tSploitusEnabled bool\n\n\tBrowserUserAgent string\n}\n\ntype ServerConfig struct {\n\tHost string\n\tPort int\n\tAllowedOrigins string\n}\n\ntype DatabaseConfig struct {\n\tHost string\n\tPort int\n\tUser string\n\tPassword string\n\tName string\n\tSSLMode string\n}\n\ntype AuthConfig struct {\n\tJWTSecret string\n\tTokenExpiry int\n}\n\ntype LogConfig struct {\n\tLevel string\n\tFormat string\n}\n\nfunc Load() (*Config, error) {\n\tcfg := &Config{\n\t\tServer: ServerConfig{\n\t\t\tHost: env(\"SERVER_HOST\", \"0.0.0.0\"),\n\t\t\tPort: envInt(\"SERVER_PORT\", 8080),\n\t\t\tAllowedOrigins: env(\"ALLOWED_ORIGINS\", \"*\"),\n\t\t},\n\t\tDatabase: DatabaseConfig{\n\t\t\tHost: env(\"DB_HOST\", \"localhost\"),\n\t\t\tPort: envInt(\"DB_PORT\", 5432),\n\t\t\tUser: env(\"DB_USER\", \"sentrix\"),\n\t\t\tPassword: env(\"DB_PASSWORD\", \"sentrix\"),\n\t\t\tName: env(\"DB_NAME\", \"sentrix\"),\n\t\t\tSSLMode: env(\"DB_SSLMODE\", \"disable\"),\n\t\t},\n\t\tAuth: AuthConfig{\n\t\t\tJWTSecret: env(\"JWT_SECRET\", \"\"),\n\t\t\tTokenExpiry: envInt(\"JWT_EXPIRY_HOURS\", 24),\n\t\t},\n\t\tLog: LogConfig{\n\t\t\tLevel: env(\"LOG_LEVEL\", \"info\"),\n\t\t\tFormat: env(\"LOG_FORMAT\", \"text\"),\n\t\t},\n\t\tLLM: LLMConfig{\n\t\t\tOpenAIKey: env(\"OPENAI_API_KEY\", \"\"),\n\t\t\tAnthropicKey: env(\"ANTHROPIC_API_KEY\", \"\"),\n\t\t\tGeminiKey: env(\"GEMINI_API_KEY\", \"\"),\n\t\t\tDeepSeekKey: env(\"DEEPSEEK_API_KEY\", \"\"),\n\t\t\tOllamaURL: env(\"OLLAMA_URL\", \"\"),\n\t\t\tCustomURL: env(\"CUSTOM_LLM_URL\", \"\"),\n\t\t\tCustomModel: env(\"CUSTOM_LLM_MODEL\", \"\"),\n\t\t\tCustomAPIKey: env(\"CUSTOM_LLM_API_KEY\", \"\"),\n\t\t\tDefaultProvider: env(\"DEFAULT_LLM_PROVIDER\", \"\"),\n\t\t},\n\t\tAgent: AgentConfig{\n\t\t\tWorkers: envInt(\"AGENT_WORKERS\", 2),\n\t\t\tSameToolLimit: envInt(\"AGENT_SAME_TOOL_LIMIT\", 4),\n\t\t\tTotalToolLimit: envInt(\"AGENT_TOTAL_TOOL_LIMIT\", 20),\n\t\t\tTaskToolLimit: envInt(\"AGENT_TASK_TOOL_LIMIT\", 60),\n\t\t\tSqlmapTimeoutSeconds: envInt(\"SQLMAP_DEFAULT_TIMEOUT_SECONDS\", 180),\n\t\t},\n\t\tSearch: SearchConfig{\n\t\t\tDefaultMaxResults: envInt(\"SEARCH_DEFAULT_MAX_RESULTS\", 5),\n\t\t\tTimeoutSeconds: envInt(\"SEARCH_TIMEOUT_SECONDS\", 30),\n\t\t\tProviderPriority: envCSV(\n\t\t\t\t\"SEARCH_PROVIDER_PRIORITY\",\n\t\t\t\t[]string{\"duckduckgo\", \"tavily\", \"searxng\", \"google\", \"perplexity\", \"traversaal\", \"sploitus\"},\n\t\t\t),\n\t\t\tDuckDuckGoEnabled: envBool(\"DUCKDUCKGO_ENABLED\", true),\n\t\t\tGoogleAPIKey: env(\"GOOGLE_SEARCH_API_KEY\", \"\"),\n\t\t\tGoogleCX: env(\"GOOGLE_SEARCH_CX\", \"\"),\n\t\t\tTavilyAPIKey: env(\"TAVILY_API_KEY\", \"\"),\n\t\t\tTraversaalAPIKey: env(\"TRAVERSAAL_API_KEY\", \"\"),\n\t\t\tPerplexityAPIKey: env(\"PERPLEXITY_API_KEY\", \"\"),\n\t\t\tPerplexityModel: env(\"PERPLEXITY_MODEL\", \"sonar\"),\n\t\t\tSearxngURL: env(\"SEARXNG_URL\", \"\"),\n\t\t\tSearxngLanguage: env(\"SEARXNG_LANGUAGE\", \"en\"),\n\t\t\tSearxngCategories: env(\"SEARXNG_CATEGORIES\", \"general\"),\n\t\t\tSearxngSafeSearch: env(\"SEARXNG_SAFE_SEARCH\", \"1\"),\n\t\t\tSploitusEnabled: envBool(\"SPLOITUS_ENABLED\", true),\n\t\t\tBrowserUserAgent: env(\"BROWSER_USER_AGENT\", \"Sentrix/0.7\"),\n\t\t},\n\t\tDocker: DockerConfig{\n\t\t\tEnabled: envBool(\"DOCKER_ENABLED\", false),\n\t\t\tSocketPath: env(\"DOCKER_SOCKET\", \"/var/run/docker.sock\"),\n\t\t\tDefaultImage: env(\"DOCKER_DEFAULT_IMAGE\", \"sentrix-tools:latest\"),\n\t\t\tNetwork: env(\"DOCKER_NETWORK\", \"sentrix-sandbox\"),\n\t\t\tDataDir: env(\"DOCKER_DATA_DIR\", \"./data/sandbox\"),\n\t\t\tCPULimit: envFloat(\"DOCKER_CPU_LIMIT\", 1.0),\n\t\t\tMemoryLimitMB: envInt(\"DOCKER_MEMORY_LIMIT_MB\", 512),\n\t\t\tContainerTimeout: envInt(\"DOCKER_CONTAINER_TIMEOUT\", 1800),\n\t\t\tInside: envBool(\"DOCKER_INSIDE\", false),\n\t\t},\n\t\tEmbedding: EmbeddingConfig{\n\t\t\tProvider: env(\"EMBEDDING_PROVIDER\", \"none\"),\n\t\t\tModel: env(\"EMBEDDING_MODEL\", \"text-embedding-3-small\"),\n\t\t\tAPIKey: env(\"EMBEDDING_API_KEY\", \"\"),\n\t\t\tBaseURL: env(\"EMBEDDING_BASE_URL\", \"\"),\n\t\t\tDimensions: envInt(\"EMBEDDING_DIMENSIONS\", 1536),\n\t\t\tBatchSize: envInt(\"EMBEDDING_BATCH_SIZE\", 100),\n\t\t},\n\t\tScraper: ScraperConfig{\n\t\t\tPublicURL: env(\"SCRAPER_PUBLIC_URL\", \"\"),\n\t\t\tPrivateURL: env(\"SCRAPER_PRIVATE_URL\", \"\"),\n\t\t},\n\t\tObservability: ObservabilityConfig{\n\t\t\tEnabled: envBool(\"OBSERVABILITY_ENABLED\", false),\n\t\t\tServiceName: env(\"OTEL_SERVICE_NAME\", \"sentrix\"),\n\t\t\tOTLPEndpoint: env(\"OTEL_EXPORTER_OTLP_ENDPOINT\", \"localhost:4317\"),\n\t\t\tTraceSampleRate: envFloat(\"OTEL_TRACE_SAMPLE_RATE\", 1.0),\n\t\t\tLangfuseEnabled: envBool(\"LANGFUSE_ENABLED\", false),\n\t\t\tLangfusePublicKey: env(\"LANGFUSE_PUBLIC_KEY\", \"\"),\n\t\t\tLangfuseSecretKey: env(\"LANGFUSE_SECRET_KEY\", \"\"),\n\t\t\tLangfuseHost: env(\"LANGFUSE_HOST\", \"https://cloud.langfuse.com\"),\n\t\t},\n\t}\n\n\t// Fall back embedding API key to OpenAI key if not set.\n\tif cfg.Embedding.APIKey == \"\" {\n\t\tcfg.Embedding.APIKey = cfg.LLM.OpenAIKey\n\t}\n\n\tif cfg.Auth.JWTSecret == \"\" {\n\t\treturn nil, fmt.Errorf(\"JWT_SECRET environment variable is required\")\n\t}\n\n\treturn cfg, nil\n}\n\nfunc (d DatabaseConfig) DSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s\",\n\t\td.Host, d.Port, d.User, d.Password, d.Name, d.SSLMode,\n\t)\n}\n\nfunc env(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc envInt(key string, fallback int) int {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tif i, err := strconv.Atoi(v); err == nil {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envFloat(key string, fallback float64) float64 {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tif f, err := strconv.ParseFloat(v, 64); err == nil {\n\t\t\treturn f\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envBool(key string, fallback bool) bool {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tswitch v {\n\t\tcase \"1\", \"true\", \"TRUE\", \"True\", \"yes\", \"YES\", \"on\", \"ON\":\n\t\t\treturn true\n\t\tcase \"0\", \"false\", \"FALSE\", \"False\", \"no\", \"NO\", \"off\", \"OFF\":\n\t\t\treturn false\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envCSV(key string, fallback []string) []string {\n\tv := os.Getenv(key)\n\tif v == \"\" {\n\t\treturn append([]string{}, fallback...)\n\t}\n\n\tparts := strings.Split(v, \",\")\n\tout := make([]string, 0, len(parts))\n\tfor _, part := range parts {\n\t\tpart = strings.TrimSpace(part)\n\t\tif part != \"\" {\n\t\t\tout = append(out, part)\n\t\t}\n\t}\n\tif len(out) == 0 {\n\t\treturn append([]string{}, fallback...)\n\t}\n\treturn out\n}\n"}, "4ada86e2edd3114e68e926abdaf26a21a4eb82c7": {"t": 1785755220.3041015, "n": 8291, "h": "e01c9afabf5ff4ed", "c": "package config\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Config struct {\n\tServer ServerConfig\n\tDatabase DatabaseConfig\n\tAuth AuthConfig\n\tLog LogConfig\n\tLLM LLMConfig\n\tAgent AgentConfig\n\tSearch SearchConfig\n\tDocker DockerConfig\n\tEmbedding EmbeddingConfig\n\tObservability ObservabilityConfig\n\tScraper ScraperConfig\n}\n\ntype ScraperConfig struct {\n\tPublicURL string // Full base URL for public targets, may include credentials\n\tPrivateURL string // Full base URL for private/internal targets, may include credentials\n}\n\ntype DockerConfig struct {\n\tEnabled bool\n\tSocketPath string\n\tDefaultImage string\n\tNetwork string\n\tDataDir string\n\tCPULimit float64\n\tMemoryLimitMB int\n\tContainerTimeout int // seconds\n\tInside bool // true when sentrix itself runs inside Docker\n}\n\ntype EmbeddingConfig struct {\n\tProvider string // \"openai\", \"ollama\", \"none\"\n\tModel string\n\tAPIKey string\n\tBaseURL string\n\tDimensions int\n\tBatchSize int\n}\n\ntype ObservabilityConfig struct {\n\tEnabled bool\n\tServiceName string\n\tOTLPEndpoint string\n\tTraceSampleRate float64\n\tLangfuseEnabled bool\n\tLangfusePublicKey string\n\tLangfuseSecretKey string\n\tLangfuseHost string\n}\n\ntype AgentConfig struct {\n\tWorkers int\n\tSameToolLimit int\n\tTotalToolLimit int\n\tTaskToolLimit int // max total tool calls across all subtasks in one task (0 = TotalToolLimit * 3)\n\tSqlmapTimeoutSeconds int\n}\n\ntype LLMConfig struct {\n\tOpenAIKey string\n\tAnthropicKey string\n\tGeminiKey string\n\tDeepSeekKey string\n\tOllamaURL string\n\tCustomURL string\n\tCustomModel string\n\tCustomAPIKey string\n\tDefaultProvider string\n}\n\ntype SearchConfig struct {\n\tDefaultMaxResults int\n\tTimeoutSeconds int\n\tProviderPriority []string\n\n\tDuckDuckGoEnabled bool\n\n\tGoogleAPIKey string\n\tGoogleCX string\n\n\tTavilyAPIKey string\n\n\tTraversaalAPIKey string\n\n\tPerplexityAPIKey string\n\tPerplexityModel string\n\n\tSearxngURL string\n\tSearxngLanguage string\n\tSearxngCategories string\n\tSearxngSafeSearch string\n\n\tSploitusEnabled bool\n\n\tBrowserUserAgent string\n}\n\ntype ServerConfig struct {\n\tHost string\n\tPort int\n\tAllowedOrigins string\n}\n\ntype DatabaseConfig struct {\n\tHost string\n\tPort int\n\tUser string\n\tPassword string\n\tName string\n\tSSLMode string\n}\n\ntype AuthConfig struct {\n\tJWTSecret string\n\tTokenExpiry int\n}\n\ntype LogConfig struct {\n\tLevel string\n\tFormat string\n}\n\nfunc Load() (*Config, error) {\n\tcfg := &Config{\n\t\tServer: ServerConfig{\n\t\t\tHost: env(\"SERVER_HOST\", \"0.0.0.0\"),\n\t\t\tPort: envInt(\"SERVER_PORT\", 8080),\n\t\t\tAllowedOrigins: env(\"ALLOWED_ORIGINS\", \"*\"),\n\t\t},\n\t\tDatabase: DatabaseConfig{\n\t\t\tHost: env(\"DB_HOST\", \"localhost\"),\n\t\t\tPort: envInt(\"DB_PORT\", 5432),\n\t\t\tUser: env(\"DB_USER\", \"sentrix\"),\n\t\t\tPassword: env(\"DB_PASSWORD\", \"sentrix\"),\n\t\t\tName: env(\"DB_NAME\", \"sentrix\"),\n\t\t\tSSLMode: env(\"DB_SSLMODE\", \"disable\"),\n\t\t},\n\t\tAuth: AuthConfig{\n\t\t\tJWTSecret: env(\"JWT_SECRET\", \"\"),\n\t\t\tTokenExpiry: envInt(\"JWT_EXPIRY_HOURS\", 24),\n\t\t},\n\t\tLog: LogConfig{\n\t\t\tLevel: env(\"LOG_LEVEL\", \"info\"),\n\t\t\tFormat: env(\"LOG_FORMAT\", \"text\"),\n\t\t},\n\t\tLLM: LLMConfig{\n\t\t\tOpenAIKey: env(\"OPENAI_API_KEY\", \"\"),\n\t\t\tAnthropicKey: env(\"ANTHROPIC_API_KEY\", \"\"),\n\t\t\tGeminiKey: env(\"GEMINI_API_KEY\", \"\"),\n\t\t\tDeepSeekKey: env(\"DEEPSEEK_API_KEY\", \"\"),\n\t\t\tOllamaURL: env(\"OLLAMA_URL\", \"\"),\n\t\t\tCustomURL: env(\"CUSTOM_LLM_URL\", \"\"),\n\t\t\tCustomModel: env(\"CUSTOM_LLM_MODEL\", \"\"),\n\t\t\tCustomAPIKey: env(\"CUSTOM_LLM_API_KEY\", \"\"),\n\t\t\tDefaultProvider: env(\"DEFAULT_LLM_PROVIDER\", \"\"),\n\t\t},\n\t\tAgent: AgentConfig{\n\t\t\tWorkers: envInt(\"AGENT_WORKERS\", 2),\n\t\t\tSameToolLimit: envInt(\"AGENT_SAME_TOOL_LIMIT\", 4),\n\t\t\tTotalToolLimit: envInt(\"AGENT_TOTAL_TOOL_LIMIT\", 20),\n\t\t\tTaskToolLimit: envInt(\"AGENT_TASK_TOOL_LIMIT\", 60),\n\t\t\tSqlmapTimeoutSeconds: envInt(\"SQLMAP_DEFAULT_TIMEOUT_SECONDS\", 180),\n\t\t},\n\t\tSearch: SearchConfig{\n\t\t\tDefaultMaxResults: envInt(\"SEARCH_DEFAULT_MAX_RESULTS\", 5),\n\t\t\tTimeoutSeconds: envInt(\"SEARCH_TIMEOUT_SECONDS\", 30),\n\t\t\tProviderPriority: envCSV(\n\t\t\t\t\"SEARCH_PROVIDER_PRIORITY\",\n\t\t\t\t[]string{\"duckduckgo\", \"tavily\", \"searxng\", \"google\", \"perplexity\", \"traversaal\", \"sploitus\"},\n\t\t\t),\n\t\t\tDuckDuckGoEnabled: envBool(\"DUCKDUCKGO_ENABLED\", true),\n\t\t\tGoogleAPIKey: env(\"GOOGLE_SEARCH_API_KEY\", \"\"),\n\t\t\tGoogleCX: env(\"GOOGLE_SEARCH_CX\", \"\"),\n\t\t\tTavilyAPIKey: env(\"TAVILY_API_KEY\", \"\"),\n\t\t\tTraversaalAPIKey: env(\"TRAVERSAAL_API_KEY\", \"\"),\n\t\t\tPerplexityAPIKey: env(\"PERPLEXITY_API_KEY\", \"\"),\n\t\t\tPerplexityModel: env(\"PERPLEXITY_MODEL\", \"sonar\"),\n\t\t\tSearxngURL: env(\"SEARXNG_URL\", \"\"),\n\t\t\tSearxngLanguage: env(\"SEARXNG_LANGUAGE\", \"en\"),\n\t\t\tSearxngCategories: env(\"SEARXNG_CATEGORIES\", \"general\"),\n\t\t\tSearxngSafeSearch: env(\"SEARXNG_SAFE_SEARCH\", \"1\"),\n\t\t\tSploitusEnabled: envBool(\"SPLOITUS_ENABLED\", true),\n\t\t\tBrowserUserAgent: env(\"BROWSER_USER_AGENT\", \"Sentrix/0.7\"),\n\t\t},\n\t\tDocker: DockerConfig{\n\t\t\tEnabled: envBool(\"DOCKER_ENABLED\", false),\n\t\t\tSocketPath: env(\"DOCKER_SOCKET\", \"/var/run/docker.sock\"),\n\t\t\tDefaultImage: env(\"DOCKER_DEFAULT_IMAGE\", \"sentrix-tools:latest\"),\n\t\t\tNetwork: env(\"DOCKER_NETWORK\", \"sentrix-sandbox\"),\n\t\t\tDataDir: env(\"DOCKER_DATA_DIR\", \"./data/sandbox\"),\n\t\t\tCPULimit: envFloat(\"DOCKER_CPU_LIMIT\", 1.0),\n\t\t\tMemoryLimitMB: envInt(\"DOCKER_MEMORY_LIMIT_MB\", 512),\n\t\t\tContainerTimeout: envInt(\"DOCKER_CONTAINER_TIMEOUT\", 1800),\n\t\t\tInside: envBool(\"DOCKER_INSIDE\", false),\n\t\t},\n\t\tEmbedding: EmbeddingConfig{\n\t\t\tProvider: env(\"EMBEDDING_PROVIDER\", \"none\"),\n\t\t\tModel: env(\"EMBEDDING_MODEL\", \"text-embedding-3-small\"),\n\t\t\tAPIKey: env(\"EMBEDDING_API_KEY\", \"\"),\n\t\t\tBaseURL: env(\"EMBEDDING_BASE_URL\", \"\"),\n\t\t\tDimensions: envInt(\"EMBEDDING_DIMENSIONS\", 1536),\n\t\t\tBatchSize: envInt(\"EMBEDDING_BATCH_SIZE\", 100),\n\t\t},\n\t\tScraper: ScraperConfig{\n\t\t\tPublicURL: env(\"SCRAPER_PUBLIC_URL\", \"\"),\n\t\t\tPrivateURL: env(\"SCRAPER_PRIVATE_URL\", \"\"),\n\t\t},\n\t\tObservability: ObservabilityConfig{\n\t\t\tEnabled: envBool(\"OBSERVABILITY_ENABLED\", false),\n\t\t\tServiceName: env(\"OTEL_SERVICE_NAME\", \"sentrix\"),\n\t\t\tOTLPEndpoint: env(\"OTEL_EXPORTER_OTLP_ENDPOINT\", \"localhost:4317\"),\n\t\t\tTraceSampleRate: envFloat(\"OTEL_TRACE_SAMPLE_RATE\", 1.0),\n\t\t\tLangfuseEnabled: envBool(\"LANGFUSE_ENABLED\", false),\n\t\t\tLangfusePublicKey: env(\"LANGFUSE_PUBLIC_KEY\", \"\"),\n\t\t\tLangfuseSecretKey: env(\"LANGFUSE_SECRET_KEY\", \"\"),\n\t\t\tLangfuseHost: env(\"LANGFUSE_HOST\", \"https://cloud.langfuse.com\"),\n\t\t},\n\t}\n\n\t// Fall back embedding API key to OpenAI key if not set.\n\tif cfg.Embedding.APIKey == \"\" {\n\t\tcfg.Embedding.APIKey = cfg.LLM.OpenAIKey\n\t}\n\n\tif cfg.Auth.JWTSecret == \"\" {\n\t\treturn nil, fmt.Errorf(\"JWT_SECRET environment variable is required\")\n\t}\n\n\treturn cfg, nil\n}\n\nfunc (d DatabaseConfig) DSN() string {\n\treturn fmt.Sprintf(\n\t\t\"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s\",\n\t\td.Host, d.Port, d.User, d.Password, d.Name, d.SSLMode,\n\t)\n}\n\nfunc env(key, fallback string) string {\n\tif v := os.Getenv(key); v != \"\" {\n\t\treturn v\n\t}\n\treturn fallback\n}\n\nfunc envInt(key string, fallback int) int {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tif i, err := strconv.Atoi(v); err == nil {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envFloat(key string, fallback float64) float64 {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tif f, err := strconv.ParseFloat(v, 64); err == nil {\n\t\t\treturn f\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envBool(key string, fallback bool) bool {\n\tif v := os.Getenv(key); v != \"\" {\n\t\tswitch v {\n\t\tcase \"1\", \"true\", \"TRUE\", \"True\", \"yes\", \"YES\", \"on\", \"ON\":\n\t\t\treturn true\n\t\tcase \"0\", \"false\", \"FALSE\", \"False\", \"no\", \"NO\", \"off\", \"OFF\":\n\t\t\treturn false\n\t\t}\n\t}\n\treturn fallback\n}\n\nfunc envCSV(key string, fallback []string) []string {\n\tv := os.Getenv(key)\n\tif v == \"\" {\n\t\treturn append([]string{}, fallback...)\n\t}\n\n\tparts := strings.Split(v, \",\")\n\tout := make([]string, 0, len(parts))\n\tfor _, part := range parts {\n\t\tpart = strings.TrimSpace(part)\n\t\tif part != \"\" {\n\t\t\tout = append(out, part)\n\t\t}\n\t}\n\tif len(out) == 0 {\n\t\treturn append([]string{}, fallback...)\n\t}\n\treturn out\n}\n"}}}} |