chore: add golangci-lint config and fix all lint issues

Enable 15 additional linters (gosec, errorlint, gocritic, modernize,
misspell, bodyclose, sqlclosecheck, nilerr, unconvert, durationcheck,
sloglint, wastedassign, usestdlibvars) with sensible exclusion rules.

Fix all findings: errors.Is for error comparisons, run() pattern in
main to avoid exitAfterDefer, ReadHeaderTimeout for Slowloris
protection, bounds check in escape sequence reader, WaitGroup.Go,
slices.Contains, range-over-int loops, and http.MethodGet constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 21:43:49 +01:00
parent 0ad6f4cb6a
commit d4380c0aea
10 changed files with 134 additions and 62 deletions

View File

@@ -4,12 +4,14 @@ import (
"context"
"errors"
"flag"
"fmt"
"log/slog"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
"git.t-juice.club/torjus/oubliette/internal/config"
"git.t-juice.club/torjus/oubliette/internal/server"
@@ -20,13 +22,19 @@ import (
const Version = "0.2.0"
func main() {
if err := run(); err != nil {
slog.Error("fatal error", "err", err)
os.Exit(1)
}
}
func run() error {
configPath := flag.String("config", "oubliette.toml", "path to config file")
flag.Parse()
cfg, err := config.Load(*configPath)
if err != nil {
slog.Error("failed to load config", "err", err)
os.Exit(1)
return fmt.Errorf("load config: %w", err)
}
level := new(slog.LevelVar)
@@ -53,8 +61,7 @@ func main() {
store, err := storage.NewSQLiteStore(cfg.Storage.DBPath)
if err != nil {
logger.Error("failed to open database", "err", err)
os.Exit(1)
return fmt.Errorf("open database: %w", err)
}
defer store.Close()
@@ -65,8 +72,7 @@ func main() {
srv, err := server.New(*cfg, store, logger)
if err != nil {
logger.Error("failed to create server", "err", err)
os.Exit(1)
return fmt.Errorf("create server: %w", err)
}
var wg sync.WaitGroup
@@ -75,23 +81,21 @@ func main() {
if cfg.Web.Enabled {
webHandler, err := web.NewServer(store, logger.With("component", "web"))
if err != nil {
logger.Error("failed to create web server", "err", err)
os.Exit(1)
return fmt.Errorf("create web server: %w", err)
}
httpServer := &http.Server{
Addr: cfg.Web.ListenAddr,
Handler: webHandler,
Addr: cfg.Web.ListenAddr,
Handler: webHandler,
ReadHeaderTimeout: 10 * time.Second,
}
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
logger.Info("web server listening", "addr", cfg.Web.ListenAddr)
if err := httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
logger.Error("web server error", "err", err)
}
}()
})
// Graceful shutdown on context cancellation.
go func() {
@@ -103,10 +107,10 @@ func main() {
}
if err := srv.ListenAndServe(ctx); err != nil {
logger.Error("server error", "err", err)
os.Exit(1)
return fmt.Errorf("server: %w", err)
}
wg.Wait()
logger.Info("server stopped")
return nil
}