// main.go
package main
import (
"html/template"
"log"
"net/http"
"os"
"path/filepath"
"time"
"siteproxy/auth"
"siteproxy/cache"
"siteproxy/config"
"siteproxy/proxy"
"siteproxy/security"
)
func main() {
cfg := config.LoadFromEnv()
log.Printf("Starting Secure Site Proxy...")
log.Printf("Session timeout: %v", cfg.SessionTimeout)
log.Printf("Rate limit: %d requests per %v", cfg.RateLimit, cfg.RateLimitWindow)
log.Printf("Cache enabled: %v (max: %d MB)", cfg.CacheEnabled, cfg.CacheMaxSize/1024/1024)
templates, err := loadTemplates()
if err != nil {
log.Fatalf("Failed to load templates: %v", err)
}
authSessionMgr := auth.NewSessionManager(cfg.SessionTimeout)
authMw := auth.NewAuthMiddleware(cfg.Username, cfg.Password, authSessionMgr, templates)
blockedDomainsMap := make(map[string]bool)
for _, domain := range cfg.BlockedDomains {
blockedDomainsMap[domain] = true
}
validator := security.NewRequestValidator(
blockedDomainsMap,
cfg.BlockedCIDRs,
cfg.AllowedSchemes,
)
rateLimiter := security.NewRateLimiter(cfg.RateLimit, cfg.RateLimitWindow)
var memCache *cache.MemoryCache
if cfg.CacheEnabled {
memCache = cache.NewMemoryCache(cfg.CacheMaxSize, cfg.CacheTTL)
} else {
memCache = cache.NewMemoryCache(0, 0)
}
proxySessionMgr := proxy.NewProxySessionManager(30 * time.Minute)
proxyHandler := proxy.NewHandler(
validator,
rateLimiter,
memCache,
proxySessionMgr,
cfg.UserAgent,
cfg.MaxResponseSize,
)
indexHandler := proxy.NewIndexHandler(templates)
statsHandler := proxy.NewStatsHandler(memCache)
mux := http.NewServeMux()
mux.HandleFunc("/login", authMw.Login)
mux.HandleFunc("/health", healthCheck)
mux.Handle("/", authMw.Require(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.NotFound(w, r)
return
}
if r.Method == "POST" {
targetURL := r.FormValue("url")
if targetURL == "" {
http.Error(w, "URL required", http.StatusBadRequest)
return
}
token := proxySessionMgr.Create(targetURL)
http.Redirect(w, r, "/p/"+token, http.StatusSeeOther)
return
}
indexHandler.ServeHTTP(w, r)
})))
mux.Handle("/p/", authMw.Require(proxyHandler))
mux.Handle("/stats", authMw.Require(statsHandler))
mux.HandleFunc("/logout", authMw.Logout)
addr := ":" + cfg.Port
log.Printf("Server listening on %s", addr)
log.Printf("Login with username: %s", cfg.Username)
log.Printf("Access at: http://localhost:%s", cfg.Port)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatal(err)
}
}
func loadTemplates() (*template.Template, error) {
templateDirs := []string{"templates", "./templates", "/app/templates"}
var templateDir string
for _, dir := range templateDirs {
if _, err := os.Stat(dir); err == nil {
templateDir = dir
break
}
}
if templateDir == "" {
return nil, os.ErrNotExist
}
log.Printf("Loading templates from: %s", templateDir)
pattern := filepath.Join(templateDir, "*.html")
tmpl, err := template.ParseGlob(pattern)
if err != nil {
return nil, err
}
log.Printf("Loaded templates: %v", tmpl.DefinedTemplates())
return tmpl, nil
}
func healthCheck(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"status":"ok","version":"1.0.0"}`))
}