package main import ( "fmt" "net/http" "os" "strings" ) func (d *Dashboard) StartServer(addr string) error { // Determine base URL for OAuth baseURL := os.Getenv("OAUTH_BASE_URL") if baseURL == "" { // Default based on whether we're in production if strings.Contains(addr, "0.0.0.0") { baseURL = "https://dashboard.1440.news" } else { baseURL = "http://" + addr } } // Initialize OAuth manager oauthCfg, err := LoadOAuthConfig(baseURL) var oauth *OAuthManager if err != nil { fmt.Printf("OAuth not configured: %v (dashboard will be unprotected)\n", err) } else { oauth, err = NewOAuthManager(*oauthCfg, d.db) if err != nil { fmt.Printf("Failed to initialize OAuth: %v (dashboard will be unprotected)\n", err) oauth = nil } else { fmt.Println("OAuth authentication enabled for dashboard") } } // OAuth endpoints (always public) if oauth != nil { http.HandleFunc("/.well-known/oauth-client-metadata", oauth.HandleClientMetadata) http.HandleFunc("/.well-known/jwks.json", oauth.HandleJWKS) http.HandleFunc("/auth/login", oauth.HandleLogin) http.HandleFunc("/auth/callback", oauth.HandleCallback) http.HandleFunc("/auth/logout", oauth.HandleLogout) http.HandleFunc("/auth/session", oauth.HandleSessionInfo) } // Helper to wrap handlers with auth if OAuth is enabled withAuth := func(h http.HandlerFunc) http.HandlerFunc { if oauth != nil { return oauth.RequireAuth(h) } return h } http.HandleFunc("/dashboard", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleDashboard(w, r) })) // Root handler for 1440.news accounts directory http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { host := r.Host // Strip port if present if idx := strings.Index(host, ":"); idx != -1 { host = host[:idx] } // If this is 1440.news (apex), serve accounts directory if host == "1440.news" || host == "1440.localhost" { if r.URL.Path == "/" || r.URL.Path == "" { d.handleAccountsDirectory(w, r) return } } // Otherwise, redirect to dashboard for root path if r.URL.Path == "/" { http.Redirect(w, r, "/dashboard", http.StatusFound) return } // Unknown path http.NotFound(w, r) }) http.HandleFunc("/api/stats", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIStats(w, r) })) http.HandleFunc("/api/allDomains", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIAllDomains(w, r) })) http.HandleFunc("/api/domainFeeds", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDomainFeeds(w, r) })) http.HandleFunc("/api/feedInfo", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIFeedInfo(w, r) })) http.HandleFunc("/api/feedItems", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIFeedItems(w, r) })) http.HandleFunc("/api/search", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPISearch(w, r) })) http.HandleFunc("/api/tlds", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPITLDs(w, r) })) http.HandleFunc("/api/searchStats", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPISearchStats(w, r) })) http.HandleFunc("/api/tldDomains", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPITLDDomains(w, r) })) http.HandleFunc("/api/revisitDomain", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIRevisitDomain(w, r) })) http.HandleFunc("/api/priorityCrawl", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPriorityCrawl(w, r) })) http.HandleFunc("/api/checkFeed", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPICheckFeed(w, r) })) http.HandleFunc("/api/domainsByStatus", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDomainsByStatus(w, r) })) http.HandleFunc("/api/feedsByStatus", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIFeedsByStatus(w, r) })) http.HandleFunc("/api/domains", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDomains(w, r) })) http.HandleFunc("/api/feeds", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIFeeds(w, r) })) http.HandleFunc("/api/setDomainStatus", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPISetDomainStatus(w, r) })) http.HandleFunc("/api/filter", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIFilter(w, r) })) http.HandleFunc("/api/enablePublish", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIEnablePublish(w, r) })) http.HandleFunc("/api/disablePublish", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDisablePublish(w, r) })) http.HandleFunc("/api/publishEnabled", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPublishEnabled(w, r) })) http.HandleFunc("/api/publishDenied", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPublishDenied(w, r) })) http.HandleFunc("/api/publishCandidates", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPublishCandidates(w, r) })) http.HandleFunc("/api/setPublishStatus", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPISetPublishStatus(w, r) })) http.HandleFunc("/api/unpublishedItems", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIUnpublishedItems(w, r) })) http.HandleFunc("/api/testPublish", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPITestPublish(w, r) })) http.HandleFunc("/api/deriveHandle", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDeriveHandle(w, r) })) http.HandleFunc("/api/publishFeed", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPublishFeed(w, r) })) http.HandleFunc("/api/createAccount", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPICreateAccount(w, r) })) http.HandleFunc("/api/publishFeedFull", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIPublishFeedFull(w, r) })) http.HandleFunc("/api/updateProfile", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIUpdateProfile(w, r) })) http.HandleFunc("/api/languages", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPILanguages(w, r) })) http.HandleFunc("/api/denyDomain", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDenyDomain(w, r) })) http.HandleFunc("/api/undenyDomain", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIUndenyDomain(w, r) })) http.HandleFunc("/api/dropDomain", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIDropDomain(w, r) })) http.HandleFunc("/api/tldStats", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPITLDStats(w, r) })) http.HandleFunc("/api/resetAllPublishing", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIResetAllPublishing(w, r) })) http.HandleFunc("/api/refreshProfiles", withAuth(func(w http.ResponseWriter, r *http.Request) { d.handleAPIRefreshProfiles(w, r) })) http.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) { http.StripPrefix("/static/", http.FileServer(http.Dir("static"))).ServeHTTP(w, r) }) fmt.Printf("Dashboard running at http://%s\n", addr) return http.ListenAndServe(addr, nil) }