isl-api/main.go

135 lines
3.6 KiB
Go

package main
import (
"context"
"fmt"
"log"
"log/slog"
"net/http"
"os"
"time"
"forgejo.merr.is/annika/isl-api/Controllers"
"forgejo.merr.is/annika/isl-api/Routes"
"forgejo.merr.is/annika/isl-api/Services"
"forgejo.merr.is/annika/isl-api/sql/powerItem"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/go-chi/httplog/v2"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/lmittmann/tint"
"github.com/moosetheory/jwtauth/v5"
"github.com/spf13/viper"
)
var conf *EnvConfigs
var logger *slog.Logger
func init() {
w := os.Stderr
logger = slog.New(
tint.NewHandler(w, &tint.Options{
TimeFormat: time.RFC3339Nano,
}),
)
logger.Info("Initializing isl-api")
setupConfig()
logger = slog.New(
tint.NewHandler(w, &tint.Options{
Level: conf.GetLogLevel(),
TimeFormat: time.RFC3339Nano,
}),
)
}
func main() {
logger.Info("Starting isl-api")
deps := dependencies{}
deps.initializeDependencies()
fmt.Printf("Preparing to listen on `:%v`\n", conf.HttpPort)
err := http.ListenAndServe(fmt.Sprintf(":%v", conf.HttpPort), deps.router)
log.Fatal(err)
}
func setupConfig() {
logger.Info("Loading config")
viper.AddConfigPath(".")
viper.SetConfigName(".env")
viper.SetConfigType("env")
viper.SetEnvPrefix("isl_api")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
logger.Error("Error reading env file, exiting", "error", err)
panic(err)
}
if err := viper.Unmarshal(&conf); err != nil {
logger.Error("Unable to unmarshal configuration, exiting", "error", err)
panic(err)
}
logger.Info("Finished loading config")
}
type dependencies struct {
router *chi.Mux
postgresConnection *pgxpool.Pool
context context.Context
jwkContext context.Context
powerItemQuerier *powerItem.DBQuerier
powerItemService *Services.PowerItemService
powerItemController *Controllers.PowerItemController
jwkCache *jwk.Cache
}
func (d *dependencies) initializeDependencies() error {
logger.Info("Initializing dependencies")
var err error
d.jwkContext = context.Background()
d.jwkCache = jwk.NewCache(d.jwkContext)
d.jwkCache.Register(conf.JWKSURI, jwk.WithMinRefreshInterval(15*time.Minute))
_, err = d.jwkCache.Refresh(d.jwkContext, conf.JWKSURI)
if err != nil {
logger.Error("Error setting up JWK cache", "error", err)
return err
}
d.router = chi.NewRouter()
httpLogger := httplog.NewLogger("isl-api", httplog.Options{
Concise: true,
RequestHeaders: true,
})
httpLogger.Logger = logger
d.router.Use(httplog.RequestLogger(httpLogger))
d.router.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Cache-Control", "Expires", "Pragma"},
}))
d.context = context.Background()
d.postgresConnection, err = pgxpool.Connect(d.context, conf.ConnectionString)
if err != nil {
logger.Error("Error setting up database connection", "error", err)
return err
}
d.powerItemQuerier = powerItem.NewQuerier(d.postgresConnection)
d.powerItemService = Services.NewPowerItemService(d.powerItemQuerier)
d.powerItemController = Controllers.NewPowerItemController(d.powerItemService)
jwkSet, err := d.jwkCache.Get(d.jwkContext, conf.JWKSURI)
if err != nil {
logger.Error("Error getting JWKS", "error", err)
return err
}
tokenAuth := jwtauth.New("HS256", nil, nil, jwkSet, jwt.WithAcceptableSkew(30*time.Second))
d.router.Mount("/powerItems", Routes.SetupPowerItemRoutes(*d.powerItemController, tokenAuth))
logger.Info("Finished initializing dependencies")
return nil
}