Add routes.go and main.go - Phase 4 complete
routes.go: HTTP route registration with OAuth middleware - Converted all handlers from (c *Crawler) to (d *Dashboard) - Removed url.1440.news short URL handling (belongs in shortener service) - Removed /internal/crawl endpoint (requires crawler) main.go: Entry point for standalone dashboard service - Uses NewDashboard with connection string from DATABASE_URL - Starts stats update loop and HTTP server
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get database connection string from environment (or empty for defaults)
|
||||
connString := os.Getenv("DATABASE_URL")
|
||||
|
||||
// Create dashboard instance (handles DB connection internally)
|
||||
dashboard, err := NewDashboard(connString)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize dashboard: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer dashboard.db.Close()
|
||||
|
||||
// Start background stats update loop
|
||||
go dashboard.StartStatsLoop()
|
||||
|
||||
// Get listen address from environment or use default
|
||||
addr := os.Getenv("DASHBOARD_ADDR")
|
||||
if addr == "" {
|
||||
addr = "0.0.0.0:4321"
|
||||
}
|
||||
|
||||
// Start HTTP server
|
||||
if err := dashboard.StartServer(addr); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Server error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
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://app.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)
|
||||
}
|
||||
Reference in New Issue
Block a user