diff --git a/dashboard.go b/dashboard.go index c8f48ae..0099ffe 100644 --- a/dashboard.go +++ b/dashboard.go @@ -240,11 +240,35 @@ func (c *Crawler) StartDashboard(addr string) error { c.handleDashboard(w, r) }) - // URL shortener redirect handler + // URL shortener redirect handler (legacy /r/ path) http.HandleFunc("/r/", func(w http.ResponseWriter, r *http.Request) { c.handleRedirect(w, r) }) + // Root handler for url.1440.news short URLs + 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 url.1440.news, treat path as short code + if host == "url.1440.news" { + c.handleRedirect(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", func(w http.ResponseWriter, r *http.Request) { c.handleAPIStats(w, r) }) @@ -2235,15 +2259,18 @@ func (c *Crawler) handleAPIStats(w http.ResponseWriter, r *http.Request) { } // handleRedirect handles short URL redirects +// Supports both /r/{code} (legacy) and /{code} (for url.1440.news) func (c *Crawler) handleRedirect(w http.ResponseWriter, r *http.Request) { - // Extract code from path: /r/{code} path := r.URL.Path - if !strings.HasPrefix(path, "/r/") { - http.NotFound(w, r) - return + var code string + + // Support both /r/{code} and /{code} formats + if strings.HasPrefix(path, "/r/") { + code = strings.TrimPrefix(path, "/r/") + } else { + code = strings.TrimPrefix(path, "/") } - code := strings.TrimPrefix(path, "/r/") if code == "" { http.NotFound(w, r) return diff --git a/docker-compose.yml b/docker-compose.yml index 6abd36a..407b9c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,18 +21,22 @@ services: - atproto labels: - "traefik.enable=true" - # Production: HTTPS with Let's Encrypt + # Production: HTTPS with Let's Encrypt for app.1440.news - "traefik.http.routers.app-1440-news.rule=Host(`app.1440.news`)" - "traefik.http.routers.app-1440-news.entrypoints=https" - "traefik.http.routers.app-1440-news.tls.certresolver=letsencrypt-dns" - # Production: HTTP to HTTPS redirect - - "traefik.http.routers.app-1440-news-redirect.rule=Host(`app.1440.news`)" + # Production: HTTPS for url.1440.news (URL shortener) + - "traefik.http.routers.url-1440-news.rule=Host(`url.1440.news`)" + - "traefik.http.routers.url-1440-news.entrypoints=https" + - "traefik.http.routers.url-1440-news.tls.certresolver=letsencrypt-dns" + # Production: HTTP to HTTPS redirect for both domains + - "traefik.http.routers.app-1440-news-redirect.rule=Host(`app.1440.news`) || Host(`url.1440.news`)" - "traefik.http.routers.app-1440-news-redirect.entrypoints=http" - "traefik.http.routers.app-1440-news-redirect.middlewares=https-redirect" - "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https" - "traefik.http.middlewares.https-redirect.redirectscheme.permanent=true" # Local development: HTTP only - - "traefik.http.routers.app-1440-news-local.rule=Host(`app.1440.localhost`)" + - "traefik.http.routers.app-1440-news-local.rule=Host(`app.1440.localhost`) || Host(`url.1440.localhost`)" - "traefik.http.routers.app-1440-news-local.entrypoints=http" # Shared service - "traefik.http.services.app-1440-news.loadbalancer.server.port=4321" diff --git a/shortener.go b/shortener.go index 936b76c..de50143 100644 --- a/shortener.go +++ b/shortener.go @@ -162,13 +162,13 @@ func (c *Crawler) RecordClick(code string, r *http.Request) error { } // GetShortURLForPost returns the short URL string for use in posts -// Format: https://app.1440.news/r/{code} +// Format: https://url.1440.news/{code} func (c *Crawler) GetShortURLForPost(originalURL string, itemID *int64, feedURL string) (string, error) { shortURL, err := c.CreateShortURL(originalURL, itemID, feedURL) if err != nil { return "", err } - return fmt.Sprintf("https://app.1440.news/r/%s", shortURL.Code), nil + return fmt.Sprintf("https://url.1440.news/%s", shortURL.Code), nil } // GetClickStats returns click statistics for a short URL