8205a22972
Ollama adapter (internal/chat/ollama.go): - Implements model.LLM interface for ADK Go - Talks to Ollama's OpenAI-compatible API (/v1/chat/completions) - Full tool/function calling support (tested with Mistral Small 3.2) - Converts ADK types to OpenAI format (messages, tools, tool_calls) - Configurable via OLLAMA_HOST and OLLAMA_MODEL env vars Multi-provider handler: - MODEL_PROVIDER env: "gemini" (default) or "ollama" - Gemini: requires GOOGLE_API_KEY (pay-as-you-go recommended) - Ollama: connects to local or Tailscale-remote instance Rate limiter: - 30 requests/hour per IP on /api/chat endpoint - Uses existing middleware.NewRateLimiter pattern Tested: Ollama + Mistral Small 3.2 on M4 Pro 64GB — correct answers
297 lines
8.7 KiB
Go
297 lines
8.7 KiB
Go
// Package constants provides global constants used across the application.
|
|
package constants
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// HTTP CONTENT TYPES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
ContentTypeJSON = "application/json"
|
|
ContentTypeHTML = "text/html; charset=utf-8"
|
|
ContentTypeHTMLFragment = "text/html" // For HTMX fragments
|
|
ContentTypePlainText = "text/plain; charset=utf-8"
|
|
ContentTypePlainSimple = "text/plain" // For Accept header matching
|
|
ContentTypePDF = "application/pdf"
|
|
ContentTypeFormURLEnc = "application/x-www-form-urlencoded"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// HTTP HEADERS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
HeaderContentType = "Content-Type"
|
|
HeaderContentDisposition = "Content-Disposition"
|
|
HeaderContentLength = "Content-Length"
|
|
HeaderCacheControl = "Cache-Control"
|
|
HeaderXContentTypeOpts = "X-Content-Type-Options"
|
|
|
|
// HTMX headers
|
|
HeaderHXRequest = "HX-Request"
|
|
HeaderHXTrigger = "HX-Trigger"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// CACHE CONTROL VALUES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
// CachePublic1Hour is for relatively static content (1 hour)
|
|
CachePublic1Hour = "public, max-age=3600"
|
|
|
|
// CachePublic1Day is for static files in production (1 day)
|
|
CachePublic1Day = "public, max-age=86400"
|
|
|
|
// CachePublic5Min is for dynamic content that can be cached briefly
|
|
CachePublic5Min = "public, max-age=300, must-revalidate"
|
|
|
|
// CacheNoStore prevents caching entirely
|
|
CacheNoStore = "no-cache, no-store, must-revalidate"
|
|
|
|
// CacheStatic is for truly static assets (1 year)
|
|
CacheStatic = "public, max-age=31536000, immutable"
|
|
)
|
|
|
|
// Cache durations in seconds
|
|
const (
|
|
CacheDuration1Hour = 3600
|
|
CacheDuration5Min = 300
|
|
CacheDuration1Year = 31536000
|
|
CacheDuration1Day = 86400
|
|
CacheDuration1Week = 604800
|
|
CacheDuration1Month = 2592000
|
|
)
|
|
|
|
// ==============================================================================
|
|
// LANGUAGE CODES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
LangEnglish = "en"
|
|
LangSpanish = "es"
|
|
LangDefault = LangEnglish
|
|
)
|
|
|
|
// SupportedLanguages is the set of valid language codes
|
|
var SupportedLanguages = map[string]bool{
|
|
LangEnglish: true,
|
|
LangSpanish: true,
|
|
}
|
|
|
|
// AllLangs returns all supported language codes
|
|
func AllLangs() []string {
|
|
return []string{LangEnglish, LangSpanish}
|
|
}
|
|
|
|
// IsValidLang checks if a language code is supported
|
|
func IsValidLang(lang string) bool {
|
|
return SupportedLanguages[lang]
|
|
}
|
|
|
|
// ValidateLang returns an error if the language code is unsupported.
|
|
// It provides helpful error messages showing all supported languages.
|
|
func ValidateLang(lang string) error {
|
|
if !IsValidLang(lang) {
|
|
return fmt.Errorf("unsupported language: %s (supported: %v)", lang, AllLangs())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ==============================================================================
|
|
// CV PREFERENCES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
CVLengthShort = "short"
|
|
CVLengthLong = "long"
|
|
CVIconsShow = "show"
|
|
CVIconsHide = "hide"
|
|
CVThemeDefault = "default"
|
|
CVThemeClean = "clean"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// COOKIE SETTINGS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
CookieMaxAge = 365 * 24 * 60 * 60 // 1 year in seconds
|
|
CookiePath = "/"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// RATE LIMITING
|
|
// ==============================================================================
|
|
|
|
const (
|
|
RateLimitPDFRequests = 3
|
|
RateLimitPDFWindow = 1 * time.Minute
|
|
RateLimitGeneralRequests = 100
|
|
RateLimitGeneralWindow = 1 * time.Minute
|
|
RateLimitContactRequests = 5
|
|
RateLimitContactWindow = 1 * time.Hour
|
|
RateLimitChatRequests = 30
|
|
RateLimitChatWindow = 1 * time.Hour
|
|
)
|
|
|
|
// ==============================================================================
|
|
// TIMEOUTS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
TimeoutPDFGeneration = 30 * time.Second
|
|
TimeoutHTTPRequest = 10 * time.Second
|
|
TimeoutIdleConnection = 120 * time.Second
|
|
TimeoutGracefulShutdown = 30 * time.Second
|
|
FormMinSubmitTime = 2 * time.Second // Min time form must be displayed (bot protection)
|
|
)
|
|
|
|
// ==============================================================================
|
|
// DIRECTORIES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
DirData = "data"
|
|
DirTemplates = "templates"
|
|
DirPartials = "templates/partials"
|
|
DirStatic = "static"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// PDF DIMENSIONS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
A4WidthInches = 8.27
|
|
A4HeightInches = 11.69
|
|
)
|
|
|
|
// ==============================================================================
|
|
// CSRF PROTECTION
|
|
// ==============================================================================
|
|
|
|
const (
|
|
CSRFTokenLength = 32
|
|
CSRFTokenTTL = 24 * time.Hour
|
|
CSRFCookieName = "csrf_token"
|
|
CSRFFormField = "csrf_token"
|
|
CSRFCleanupPeriod = 1 * time.Hour
|
|
)
|
|
|
|
// ==============================================================================
|
|
// CLEANUP INTERVALS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
RateLimitCleanupPeriod = 10 * time.Minute // For contact rate limiter
|
|
RateLimitGeneralCleanupPeriod = 1 * time.Minute // For general rate limiter
|
|
)
|
|
|
|
// ==============================================================================
|
|
// SECURITY
|
|
// ==============================================================================
|
|
|
|
const (
|
|
// HSTS max-age (1 year)
|
|
HSTSMaxAge = "max-age=31536000; includeSubDomains; preload"
|
|
|
|
// Content type options
|
|
NoSniff = "nosniff"
|
|
|
|
// Frame options
|
|
FrameOptionsSameOrigin = "SAMEORIGIN"
|
|
|
|
// XSS Protection
|
|
XSSProtection = "1; mode=block"
|
|
|
|
// Referrer Policy
|
|
ReferrerPolicy = "strict-origin-when-cross-origin"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// SECURITY HEADERS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
HeaderXFrameOptions = "X-Frame-Options"
|
|
HeaderXXSSProtection = "X-XSS-Protection"
|
|
HeaderReferrerPolicy = "Referrer-Policy"
|
|
HeaderPermissionsPolicy = "Permissions-Policy"
|
|
HeaderCSP = "Content-Security-Policy"
|
|
HeaderHSTS = "Strict-Transport-Security"
|
|
HeaderRetryAfter = "Retry-After"
|
|
HeaderXForwardedFor = "X-Forwarded-For"
|
|
HeaderXRealIP = "X-Real-IP"
|
|
HeaderXCSRFToken = "X-CSRF-Token"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// REQUEST HEADERS
|
|
// ==============================================================================
|
|
|
|
const (
|
|
HeaderUserAgent = "User-Agent"
|
|
HeaderAccept = "Accept"
|
|
HeaderOrigin = "Origin"
|
|
HeaderReferer = "Referer"
|
|
HeaderXRequestedWith = "X-Requested-With"
|
|
HeaderXBrowserReq = "X-Browser-Request"
|
|
)
|
|
|
|
// Header values
|
|
const (
|
|
HeaderValueXMLHTTPRequest = "XMLHttpRequest"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// ENVIRONMENT
|
|
// ==============================================================================
|
|
|
|
const (
|
|
EnvProduction = "production"
|
|
EnvDevelopment = "development"
|
|
|
|
EnvVarGOEnv = "GO_ENV"
|
|
EnvVarPort = "PORT"
|
|
|
|
DefaultPort = "1999"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// COOKIE NAMES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
CookieCVLength = "cv-length"
|
|
CookieCVIcons = "cv-icons"
|
|
CookieCVLanguage = "cv-language"
|
|
CookieCVTheme = "cv-theme"
|
|
CookieColorTheme = "color-theme"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// COLOR THEMES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
ColorThemeLight = "light"
|
|
ColorThemeDark = "dark"
|
|
)
|
|
|
|
// ==============================================================================
|
|
// ROUTES
|
|
// ==============================================================================
|
|
|
|
const (
|
|
RouteHome = "/"
|
|
RouteHealth = "/health"
|
|
RouteExportPDF = "/export/pdf"
|
|
RouteAPIContact = "/api/contact"
|
|
RouteAPICmdK = "/api/cmd-k"
|
|
)
|