package main import ( "embed" "flag" "fmt" "html/template" "log" "net/http" "os" "strings" "git.chandlerswift.com/chandlerswift/svs-services-server/completion" llamaserver "git.chandlerswift.com/chandlerswift/svs-services-server/completion/llama-server" "git.chandlerswift.com/chandlerswift/svs-services-server/completion/openrouter" "git.chandlerswift.com/chandlerswift/svs-services-server/db" "git.chandlerswift.com/chandlerswift/svs-services-server/site" ) //go:embed templates/index.html var indexTemplate string //go:embed templates/404.html var notFoundTemplate string //go:embed headshots/* var headshots embed.FS // Used for testimonial slider var funcMap = template.FuncMap{ "add": func(a, b int) int { return a + b }, "sub": func(a, b int) int { return a - b }, "mod": func(a, b int) int { if b == 0 { return 0 } return a % b }, } var tmpl = template.Must(template.New("index").Funcs(funcMap).Parse(indexTemplate)) var notFoundTmpl = template.Must(template.New("404").Funcs(funcMap).Parse(notFoundTemplate)) var completionProviders map[string]completion.CompletionProvider = map[string]completion.CompletionProvider{ "openrouter": openrouter.OpenRouterProvider{ Token: os.Getenv("OPENROUTER_API_KEY"), Model: "openai/gpt-oss-120b", }, "llama-server": llamaserver.LlamaServerProvider{ Host: "http://localhost:8080", Model: "gpt-oss-20b-Q4_K_M", }, } func parseService(host string) (h string, err error) { parts := strings.Split(host, ".") if len(parts) != 3 { return "", fmt.Errorf("Unexpected number of parts in hostname; expected 3, got %v", len(parts)) } return parts[0], nil } func main() { // use flag package to parse args: port := flag.String("port", "64434", "Port to listen on") // echo "svs" | base64 -d | od -An -t u2 address := flag.String("address", "", "Address to listen on") completionProviderName := flag.String("completion-provider", "llama-server", "Completion provider to use (openrouter, llama-server, …)") dbpath := flag.String("database", "svs-services.db", "Path to SQLite database file") flag.Parse() err := db.InitDatabase(*dbpath) if err != nil { log.Fatalf("Failed to initialize database: %v", err) } addr := fmt.Sprintf("%s:%s", *address, *port) fmt.Println("Using completion provider", *completionProviderName) defaultCompletionProvider := completionProviders[*completionProviderName] // for err := defaultCompletionProvider.Health(); err != nil; err = defaultCompletionProvider.Health() { // fmt.Println("Waiting for Llama server to be healthy... (%v)", err) // time.Sleep(1 * time.Second) // } http.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { service, err := parseService(r.Host) if err != nil { http.Error(w, err.Error(), 500) return } siteData, err := site.GetSiteData(service, defaultCompletionProvider) if err != nil { http.Error(w, "Could not generate site", 500) log.Printf("Getting site data for service %v: %v", service, err) return } if r.URL.Path != "/" { w.WriteHeader(http.StatusNotFound) notFoundTmpl.Execute(w, siteData) return } tmpl.Execute(w, siteData) }) // Serve embedded headshots file http.Handle("GET /headshots/", http.FileServer(http.FS(headshots))) fmt.Println("Starting server on", addr) panic(http.ListenAndServe(addr, nil)) }