commit 817f63eaad17ed22317eec173382fb73c1c94ce9
parent ef9ed069961be450b71e869edb46eb00f683448e
Author: brookjeynes <me@brookjeynes.dev>
Date: Sun, 7 Jun 2026 17:26:09 +1000
refactor: clean up server startup and error handling
Signed-off-by: brookjeynes <me@brookjeynes.dev>
Diffstat:
13 files changed, 128 insertions(+), 14 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -3,3 +3,4 @@
static/files
*.db
*.db-*
+build/
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@
all: build
build: fmt generate assets security vet
- go build -o ./build/$(BINARY_NAME) $(CMD)
+ go build -o ./build/lyrics ./cmd/server/main.go
run: fmt generate assets
set -a && source ./.env && set +a && go run cmd/server/main.go
diff --git a/cmd/server/main.go b/cmd/server/main.go
@@ -7,6 +7,7 @@ import (
"lyrics/internal/server"
"net/http"
"os"
+ "time"
)
func main() {
@@ -32,7 +33,16 @@ func main() {
logger.Info("Starting server", "addr", config.Core.ListenAddr)
- err = http.ListenAndServe(config.Core.ListenAddr, state.Router())
+ srv := &http.Server{
+ Addr: config.Core.ListenAddr,
+ Handler: state.Router(),
+ ReadHeaderTimeout: 5 * time.Second,
+ ReadTimeout: 10 * time.Second,
+ WriteTimeout: 30 * time.Second,
+ IdleTimeout: 120 * time.Second,
+ }
+
+ err = srv.ListenAndServe()
if err != nil {
logger.Error("failed to start server", "err", err)
}
diff --git a/internal/server/album.go b/internal/server/album.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -41,7 +43,16 @@ func (ss *ServerState) Album(w http.ResponseWriter, r *http.Request) {
return
}
- albumview.Album(albumview.AlbumParams{
+ err = albumview.Album(albumview.AlbumParams{
Album: album,
}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render album page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
}
diff --git a/internal/server/artist.go b/internal/server/artist.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -41,10 +43,19 @@ func (ss *ServerState) Artist(w http.ResponseWriter, r *http.Request) {
return
}
- artistview.Artist(artistview.ArtistParams{
+ err = artistview.Artist(artistview.ArtistParams{
Artist: artist,
Albums: albums,
Singles: singles,
Featured: featured,
}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render artist page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
}
diff --git a/internal/server/htmx/htmx.go b/internal/server/htmx/htmx.go
@@ -15,7 +15,7 @@ func HxError(w http.ResponseWriter, status int, msg string) {
w.Header().Set("HX-Trigger", string(toastJSON))
w.Header().Set("HX-Reswap", "none")
w.WriteHeader(status)
- w.Write([]byte(msg))
+ _, _ = w.Write([]byte(msg))
}
func HxRedirect(w http.ResponseWriter, status int, location string) {
diff --git a/internal/server/index.go b/internal/server/index.go
@@ -1,10 +1,23 @@
package server
import (
+ "context"
+ "errors"
"lyrics/internal/server/ui/views/index"
"net/http"
)
func (ss *ServerState) Index(w http.ResponseWriter, r *http.Request) {
- index.Index(index.IndexParams{}).Render(r.Context(), w)
+ logger := ss.logger.With("handler", "Index")
+
+ err := index.Index(index.IndexParams{}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render index page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
}
diff --git a/internal/server/new-album.go b/internal/server/new-album.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -87,7 +89,16 @@ func (ss *ServerState) NewAlbum(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
- newalbum.NewAlbum(newalbum.NewAlbumParams{}).Render(r.Context(), w)
+ err := newalbum.NewAlbum(newalbum.NewAlbumParams{}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render new album page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
case http.MethodPost:
err := r.ParseForm()
if err != nil {
diff --git a/internal/server/new-artist.go b/internal/server/new-artist.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -61,7 +63,16 @@ func (ss *ServerState) NewArtist(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
- newartist.NewArtist(newartist.NewArtistParams{}).Render(r.Context(), w)
+ err := newartist.NewArtist(newartist.NewArtistParams{}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render new artist page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
case http.MethodPost:
err := r.ParseForm()
if err != nil {
diff --git a/internal/server/new-song.go b/internal/server/new-song.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -142,7 +144,16 @@ func (ss *ServerState) NewSong(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
- newsong.NewSong(newsong.NewSongParams{}).Render(r.Context(), w)
+ err := newsong.NewSong(newsong.NewSongParams{}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render new song page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
case http.MethodPost:
err := r.ParseForm()
if err != nil {
diff --git a/internal/server/search-album.go b/internal/server/search-album.go
@@ -1,7 +1,10 @@
package server
import (
+ "context"
+ "errors"
"lyrics/internal/db"
+ "lyrics/internal/server/htmx"
albumsearchresults "lyrics/internal/server/ui/partials/album-search-results"
similaralbumslist "lyrics/internal/server/ui/partials/similar-albums-list"
"net/http"
@@ -33,12 +36,21 @@ func (ss *ServerState) SearchAlbum(w http.ResponseWriter, r *http.Request) {
switch format {
case SearchAlbumFormatSimilar:
- similaralbumslist.SimilarAlbumsList(similaralbumslist.SimilarAlbumsListParams{
+ err = similaralbumslist.SimilarAlbumsList(similaralbumslist.SimilarAlbumsListParams{
Albums: results,
}).Render(r.Context(), w)
case SearchAlbumFormatSuggest:
- albumsearchresults.AlbumSearchResults(albumsearchresults.AlbumSearchResultsParams{
+ err = albumsearchresults.AlbumSearchResults(albumsearchresults.AlbumSearchResultsParams{
Albums: results,
}).Render(r.Context(), w)
}
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render search album partial", "err", err)
+ htmx.HxError(w, http.StatusInternalServerError, "Failed to render response.")
+ return
+ }
}
diff --git a/internal/server/search-artist.go b/internal/server/search-artist.go
@@ -1,7 +1,10 @@
package server
import (
+ "context"
+ "errors"
"lyrics/internal/db"
+ "lyrics/internal/server/htmx"
artistsearchresults "lyrics/internal/server/ui/partials/artist-search-results"
similarartistslist "lyrics/internal/server/ui/partials/similar-artists-list"
"net/http"
@@ -40,12 +43,21 @@ func (ss *ServerState) SearchArtist(w http.ResponseWriter, r *http.Request) {
switch format {
case SearchArtistFormatSimilar:
- similarartistslist.SimilarArtistsList(similarartistslist.SimilarArtistsListParams{
+ err = similarartistslist.SimilarArtistsList(similarartistslist.SimilarArtistsListParams{
Artists: results,
}).Render(r.Context(), w)
case SearchArtistFormatSuggest:
- artistsearchresults.ArtistSearchResults(artistsearchresults.ArtistSearchResultsParams{
+ err = artistsearchresults.ArtistSearchResults(artistsearchresults.ArtistSearchResultsParams{
Artists: results,
}).Render(r.Context(), w)
}
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render search artist partial", "err", err)
+ htmx.HxError(w, http.StatusInternalServerError, "Failed to render response.")
+ return
+ }
}
diff --git a/internal/server/song.go b/internal/server/song.go
@@ -1,6 +1,8 @@
package server
import (
+ "context"
+ "errors"
"fmt"
"lyrics/internal/db"
"lyrics/internal/domain"
@@ -41,5 +43,14 @@ func (ss *ServerState) Song(w http.ResponseWriter, r *http.Request) {
return
}
- song.Song(song.SongParams{Song: s}).Render(r.Context(), w)
+ err = song.Song(song.SongParams{Song: s}).Render(r.Context(), w)
+ if err != nil {
+ if errors.Is(err, context.Canceled) {
+ return
+ }
+
+ logger.Error("failed to render song page", "err", err)
+ http.Error(w, "Internal Server Error", http.StatusInternalServerError)
+ return
+ }
}