package main import ( "context" "fmt" "log/slog" "os" "os/signal" "github.com/getsentry/sentry-go" "github.com/rjNemo/rentease/assets" "github.com/rjNemo/rentease/internal/config" "github.com/rjNemo/rentease/internal/driver/database" "github.com/rjNemo/rentease/internal/driver/logger" "github.com/rjNemo/rentease/internal/driver/parser" "github.com/rjNemo/rentease/internal/driver/pdf" "github.com/rjNemo/rentease/internal/driver/storage" stripeclient "github.com/rjNemo/rentease/internal/driver/stripe" bookingRepo "github.com/rjNemo/rentease/internal/repository/booking" "github.com/rjNemo/rentease/internal/server" "github.com/rjNemo/rentease/internal/service/auth" "github.com/rjNemo/rentease/internal/service/booking" "github.com/rjNemo/rentease/internal/service/payment" ) func main() { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() baseLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true})) appConfig, err := config.New(ctx) if err != nil { baseLogger.Error("configuration error", slog.Any("error", err)) } appLogger := logger.New(appConfig.LogLevel) slog.SetDefault(appLogger) if err := run(ctx, appConfig, appLogger); err != nil { appLogger.Error("server exited", slog.Any("error", err)) os.Exit(1) } } func run(ctx context.Context, appConfig *config.Config, appLogger *slog.Logger) error { // init sentry if err := sentry.Init(sentry.ClientOptions{ Dsn: appConfig.SentryDsn, EnableTracing: true, TracesSampleRate: 1.0, }); err != nil { return fmt.Errorf("error initializing sentry %w", err) } // init database db, err := database.New(appConfig.DatabaseURL) if err != nil { return fmt.Errorf("error connecting to the database %w", err) } if err = database.Migrate(db, &booking.Booking{}, &booking.Item{}, &booking.Payment{}, &booking.InvoiceDocument{}); err != nil { return fmt.Errorf("error migrating the database %w", err) } bookingStore := bookingRepo.NewPgStore(db) // build pdf client pc, err := pdf.NewPdfClient() if err != nil { return fmt.Errorf("error starting pdf client %w", err) } invoiceStorage, err := storage.NewMinioInvoiceStorage(appConfig) if err != nil { return fmt.Errorf("error starting invoice storage %w", err) } parsingClient := parser.NewBookingAgentParser(appConfig.OpenAIModel) var stripeClient payment.StripeClient if appConfig.StripeSecretKey != "" { opts := []stripeclient.Option{} client, err := stripeclient.New(appConfig.StripeSecretKey, opts...) if err != nil { return fmt.Errorf("error creating stripe client: %w", err) } stripeClient = client } bookingService, err := booking.NewService(appLogger, bookingStore, parsingClient, pc, invoiceStorage, appConfig.InvoiceShareURLTTL) if err != nil { return fmt.Errorf("error creating booking service: %w", err) } paymentService, err := payment.NewService(appLogger, bookingStore, stripeClient) if err != nil { return fmt.Errorf("error creating payment service: %w", err) } // build authentication service as, err := auth.NewService( appConfig.SessionSecret, appConfig.Admin, appConfig.AdminSecret, appConfig.APIKey, ) if err != nil { return fmt.Errorf("error starting auth service %w", err) } port := appConfig.Port origins := appConfig.Origins srv, err := server.New( bookingService, paymentService, as, config.NewHost(), // TODO: move to the database at some point server.WithPort(port), server.WithFileSystem(assets.Static), server.WithDebug(appConfig.Debug), server.WithSecretKey(appConfig.SecretKey), server.WithOrigins(origins), server.WithStripeWebhookSecret(appConfig.StripeWebhookSecret), ) if err != nil { return fmt.Errorf("error starting server %w", err) } srv.Start(ctx) return nil }