package main import ( "crypto/sha256" "crypto/subtle" "os" "strings" "git.odit.services/lfk/document-server/docs" // Correct import path for docs "git.odit.services/lfk/document-server/handlers" "git.odit.services/lfk/document-server/models" "git.odit.services/lfk/document-server/services" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/keyauth" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/swagger" "github.com/redis/go-redis/v9" "github.com/spf13/viper" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( config *models.Config logger *zap.SugaredLogger ) func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { hashedAPIKey := sha256.Sum256([]byte(config.APIKey)) hashedKey := sha256.Sum256([]byte(key)) if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { return true, nil } return false, keyauth.ErrMissingOrMalformedAPIKey } func loadEnv() error { viper.SetDefault("LOGLEVEL", "INFO") viper.SetDefault("PRODUCION", false) viper.SetDefault("PORT", "3000") viper.SetDefault("APIKEY", "lfk") viper.SetDefault("EVENTNAME", "Demo Event") viper.SetDefault("CURRENCYSYMBOL", "€") viper.SetDefault("CURRENCYIDENTIFIER", "EUR") viper.SetDefault("CARD_SUBTITLE", "Runner Card") viper.SetDefault("CARD_BARCODEFORMAT", "ean13") viper.SetDefault("CARD_BARCODEPREFIX", "") viper.SetDefault("SPONSORING_RECEIPTMINIMUM", 0) viper.SetDefault("SPONSORING_DISCLAIMER", "Disclaimer") viper.SetDefault("SPONSORING_BARCODEFORMAT", "code128") viper.SetDefault("SPONSORING_BARCODEPREFIX", "") viper.SetDefault("CERTIFICATE_FOOTER", "Footer") viper.SetDefault("GOTENBERG_BASEURL", "") viper.SetDefault("REDIS_ADDR", "") viper.SetDefault("SEPA_BIC", "") viper.SetDefault("SEPA_NAME", "") viper.SetDefault("SEPA_IBAN", "") // Load .env file viper.SetConfigFile(".env") // Load environment variables viper.AutomaticEnv() err := viper.ReadInConfig() if err != nil { logger.Warn("No .env file found") } // Unmarshal the config from file and env into the config struct err = viper.Unmarshal(&config) if err != nil { return err } logger.Infow("Loaded config", "config", &config) return nil } func initLogger() error { logLevel := os.Getenv("LOGLEVEL") if logLevel == "" { logLevel = "INFO" } var zapLogLevel zapcore.Level err := zapLogLevel.UnmarshalText([]byte(strings.ToLower(logLevel))) if err != nil { zapLogLevel = zapcore.InfoLevel } zapConfig := zap.NewProductionConfig() zapConfig.Level = zap.NewAtomicLevelAt(zapLogLevel) zapLogger, err := zapConfig.Build() if err != nil { return err } defer zapLogger.Sync() logger = zapLogger.Sugar() logger.Debug("Initialized logger") return nil } // @title LfK Document Server API // @description This is the API documentation for the LfK Document Server - a tool for pdf generation. // @license.name CC BY-NC-SA 4.0 // @termsOfService https://lauf-fuer-kaya.de/datenschutz // @contact.name ODIT.Services UG (haftungsbeschränkt) // @contact.url https://odit.services // @contact.email info@odit.services // @securityDefinitions.apiKey ApiKeyAuth // @in query // @name key func main() { // Init the logger err := initLogger() if err != nil { return } err = loadEnv() if err != nil { logger.Error(err) return } var redisClient *redis.Client if config.RedisAddr != "" { logger.Infow("Using redis", "redisAddr", config.RedisAddr) redisClient = redis.NewClient(&redis.Options{ Addr: config.RedisAddr, }) } barcodeGenerator := &services.DefaultBarcodeService{ RedisClient: redisClient, Logger: logger.Named("DefaultBarcodeService"), } staticService := &services.DefaultStaticService{ Cache: make(map[string]string), Logger: logger.Named("DefaultStaticService"), } handler := handlers.DefaultHandler{ Config: config, BarcodeService: barcodeGenerator, StaticService: staticService, Templater: &services.DefaultTemplater{ BarcodeService: barcodeGenerator, StaticService: staticService, Logger: logger.Named("DefaultTemplater"), }, Converter: &services.GotenbergConverter{ BaseUrl: config.GotenbergBaseUrl, Logger: logger.Named("GotenbergConverter"), }, Logger: logger.Named("DefaultHandler"), } logger.Debug("Initialized services") // Create a new Fiber instance app := fiber.New(fiber.Config{ Prefork: config.Prod, }) app.Use(cors.New()) app.Use(requestid.New()) // Swagger documentation route app.Get("/swagger/*", swagger.HandlerDefault) v1 := app.Group("/v1") pdfv1 := v1.Group("/pdfs") pdfv1.Use(keyauth.New(keyauth.Config{ KeyLookup: "query:key", Validator: validateAPIKey, })) pdfv1.Post("/contracts", handler.GenerateContract) pdfv1.Post("/cards", handler.GenerateCard) pdfv1.Post("/certificates", handler.GenerateCertificate) v1.Get("/barcodes/:type/:content", handler.GenerateBarcode) logger.Debug("Initialized routes") app.Use(handler.NotFoundHandler) docs.SwaggerInfo.BasePath = "/" logger.Infow("Starting server", "port", config.Port) logger.Error(app.Listen("0.0.0.0:" + config.Port)) }