Skip to content

Wire main with config, db, auth, jellyfin adapter #12

@koinsaari

Description

@koinsaari

Summary

Replace the placeholder cmd/api-proxy/main.go with the full wiring that loads config, opens the SQLite DB, constructs the Jellyfin client, wraps it in an adapter that satisfies auth.JellyfinAuthenticator, builds the auth service, and serves the HTTP handler with graceful shutdown. Also flatten the empty db.DB wrapper struct.

Context

This is the first task that makes the binary actually do something — every prior PR built isolated packages with their own tests. Until this lands the binary still serves the original toy /healthz from cmd/api-proxy/main.go and ignores all the implemented auth / HTTP / DB code.

Two design notes:

  • Flatten internal/db: The type DB struct { *sql.DB } wrapper has no methods and no foreseeable second consumer beyond internal/auth (auth owns SQLite tables; catalog will never persist). Returning *sql.DB directly from Open removes a useless .DB field-access at every call site.
  • Adapter direction: internal/clients/jellyfin/adapter.go must import internal/auth (not the other way around). auth defines JellyfinAuthenticator as an interface and never imports jellyfin, so no cycle.

Scope

  • Flatten internal/db/db.go: delete the DB struct, change Open's signature to func Open(ctx context.Context, path string) (*sql.DB, error). Update test callers in internal/auth/ that pass d.DB to drop the field access.
  • Add internal/clients/jellyfin/adapter.go exporting AsAuthAdapter(*Client) auth.JellyfinAuthenticator. It must:
    • Translate result types between the jellyfin and auth packages (AuthResultauth.JFAuthResult, QuickConnectInitiationauth.JFQuickConnectInit).
    • Translate sentinel errors: jellyfin.ErrInvalidCredentialsauth.ErrInvalidCredentials, jellyfin.ErrUpstreamUnavailableauth.ErrJellyfinUnavailable, jellyfin.ErrQuickConnectPendingauth.ErrQuickConnectPending.
    • Include a compile-time assertion: var _ auth.JellyfinAuthenticator = (*authAdapter)(nil).
  • Rewrite cmd/api-proxy/main.go:
    • Load config via config.LoadFromEnv(), exit non-zero on error.
    • Open DB via db.Open(ctx, cfg.DBPath), defer close.
    • Build jellyfin.New(cfg.JellyfinURL, cfg.JellyfinAPIKey) and wrap with jellyfin.AsAuthAdapter.
    • Build auth.NewService(auth.Options{DB: database, Jellyfin: adapter, SignKey: cfg.JWTSigningKey}).
    • Build the HTTP handler via apihttp.NewServer(authSvc, logger) (already returns http.Handler).
    • Serve via http.Server with ReadHeaderTimeout: 5 * time.Second.
    • Graceful shutdown on SIGINT/SIGTERM via signal.NotifyContext + http.Server.Shutdown.
  • Delete cmd/api-proxy/main_test.go.

Out of scope: Docker image, deploy workflow, host scripts, README.

Acceptance criteria

  • grep -rn "db\.DB\b" --include="*.go" . returns no matches in non-test code.
  • go build ./... succeeds with no import-cycle errors.
  • go test -race -count=1 ./... passes.
  • golangci-lint run ./... reports 0 issues.
  • Smoke test against a fake Jellyfin (e.g. httptest.Server returning {AccessToken, User: {Id, Name}} for /Users/AuthenticateByName): POST /auth/login returns 200 with a JSON body containing access_token, refresh_token, and user.
  • cmd/api-proxy/main_test.go is deleted.

Notes

  • internal/http.NewServer already returns http.Handler. Plug it straight into http.Server.Handler — no wrapping needed.
  • The package alias apihttp is needed in main since internal/http would otherwise collide with net/http.
  • The adapter file is the only place that bridges the two packages' error vocabularies; do not duplicate the translation in main.go or anywhere else.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions