diff --git a/.mockery.yaml b/.mockery.yaml index 3b0170e..1fd18b8 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -3,11 +3,11 @@ dir: mocks/ outpkg: mocks issue-845-fix: True packages: - spend-sparrow/service: + spend-sparrow/internal/service: interfaces: Random: Clock: Mail: - spend-sparrow/db: + spend-sparrow/internal/db: interfaces: Auth: diff --git a/db/auth.go b/internal/db/auth.go similarity index 99% rename from db/auth.go rename to internal/db/auth.go index d83f659..049003e 100644 --- a/db/auth.go +++ b/internal/db/auth.go @@ -2,8 +2,8 @@ package db import ( "errors" - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "database/sql" "strings" diff --git a/db/error.go b/internal/db/error.go similarity index 90% rename from db/error.go rename to internal/db/error.go index 194b0e8..11091ae 100644 --- a/db/error.go +++ b/internal/db/error.go @@ -3,8 +3,8 @@ package db import ( "database/sql" "errors" - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" ) var ( diff --git a/db/migration.go b/internal/db/migration.go similarity index 94% rename from db/migration.go rename to internal/db/migration.go index 9f56bcb..17f75c7 100644 --- a/db/migration.go +++ b/internal/db/migration.go @@ -1,8 +1,8 @@ package db import ( - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "errors" diff --git a/internal/default.go b/internal/default.go new file mode 100644 index 0000000..54203c9 --- /dev/null +++ b/internal/default.go @@ -0,0 +1,135 @@ +package internal + +import ( + "errors" + "fmt" + "spend-sparrow/internal/db" + "spend-sparrow/internal/handler" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/log" + "spend-sparrow/internal/service" + "spend-sparrow/internal/types" + + "context" + "net/http" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/jmoiron/sqlx" + _ "github.com/mattn/go-sqlite3" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func Run(ctx context.Context, database *sqlx.DB, migrationsPrefix string, env func(string) string) error { + ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) + defer cancel() + + log.Info("Starting server...") + + // init server settings + serverSettings := types.NewSettingsFromEnv(env) + + // init db + err := db.RunMigrations(database, migrationsPrefix) + if err != nil { + return fmt.Errorf("could not run migrations: %w", err) + } + + // init servers + var prometheusServer *http.Server + if serverSettings.PrometheusEnabled { + prometheusServer := &http.Server{ + Addr: ":8081", + Handler: promhttp.Handler(), + ReadHeaderTimeout: 10 * time.Second, + } + go startServer(prometheusServer) + } + + httpServer := &http.Server{ + Addr: ":" + serverSettings.Port, + Handler: createHandler(database, serverSettings), + ReadHeaderTimeout: 10 * time.Second, + } + go startServer(httpServer) + + // graceful shutdown + var wg sync.WaitGroup + wg.Add(2) + go shutdownServer(httpServer, ctx, &wg) + go shutdownServer(prometheusServer, ctx, &wg) + wg.Wait() + + return nil +} + +func startServer(s *http.Server) { + log.Info("Starting server on %q", s.Addr) + if err := s.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Error("error listening and serving: %v", err) + } +} + +func shutdownServer(s *http.Server, ctx context.Context, wg *sync.WaitGroup) { + defer wg.Done() + if s == nil { + return + } + + <-ctx.Done() + shutdownCtx := context.Background() + shutdownCtx, cancel := context.WithTimeout(shutdownCtx, 10*time.Second) + defer cancel() + if err := s.Shutdown(shutdownCtx); err != nil { + log.Error("error shutting down http server: %v", err) + } else { + log.Info("Gracefully stopped http server on %v", s.Addr) + } +} + +func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler { + var router = http.NewServeMux() + + authDb := db.NewAuthSqlite(d) + + randomService := service.NewRandom() + clockService := service.NewClock() + mailService := service.NewMail(serverSettings) + + authService := service.NewAuth(authDb, randomService, clockService, mailService, serverSettings) + accountService := service.NewAccount(d, randomService, clockService) + treasureChestService := service.NewTreasureChest(d, randomService, clockService) + transactionService := service.NewTransaction(d, randomService, clockService) + transactionRecurringService := service.NewTransactionRecurring(d, randomService, clockService, transactionService) + + render := handler.NewRender() + indexHandler := handler.NewIndex(render) + authHandler := handler.NewAuth(authService, render) + accountHandler := handler.NewAccount(accountService, render) + treasureChestHandler := handler.NewTreasureChest(treasureChestService, transactionRecurringService, render) + transactionHandler := handler.NewTransaction(transactionService, accountService, treasureChestService, render) + transactionRecurringHandler := handler.NewTransactionRecurring(transactionRecurringService, render) + + indexHandler.Handle(router) + accountHandler.Handle(router) + treasureChestHandler.Handle(router) + authHandler.Handle(router) + transactionHandler.Handle(router) + transactionRecurringHandler.Handle(router) + + // Serve static files (CSS, JS and images) + router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))) + + return middleware.Wrapper( + router, + middleware.GenerateRecurringTransactions(transactionRecurringService), + middleware.SecurityHeaders(serverSettings), + middleware.CacheControl, + middleware.CrossSiteRequestForgery(authService), + middleware.Authenticate(authService), + middleware.Gzip, + middleware.Log, + ) +} diff --git a/handler/account.go b/internal/handler/account.go similarity index 93% rename from handler/account.go rename to internal/handler/account.go index fedcc2e..85765c5 100644 --- a/handler/account.go +++ b/internal/handler/account.go @@ -2,11 +2,11 @@ package handler import ( "net/http" - "spend-sparrow/handler/middleware" - "spend-sparrow/service" - t "spend-sparrow/template/account" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/service" + t "spend-sparrow/internal/template/account" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" "github.com/a-h/templ" ) diff --git a/handler/auth.go b/internal/handler/auth.go similarity index 97% rename from handler/auth.go rename to internal/handler/auth.go index 88f99cc..75bcb9c 100644 --- a/handler/auth.go +++ b/internal/handler/auth.go @@ -1,12 +1,12 @@ package handler import ( - "spend-sparrow/handler/middleware" - "spend-sparrow/log" - "spend-sparrow/service" - "spend-sparrow/template/auth" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/log" + "spend-sparrow/internal/service" + "spend-sparrow/internal/template/auth" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" "errors" "net/http" diff --git a/handler/error.go b/internal/handler/error.go similarity index 90% rename from handler/error.go rename to internal/handler/error.go index b5fd1d9..cbf7a77 100644 --- a/handler/error.go +++ b/internal/handler/error.go @@ -3,9 +3,9 @@ package handler import ( "errors" "net/http" - "spend-sparrow/db" - "spend-sparrow/service" - "spend-sparrow/utils" + "spend-sparrow/internal/db" + "spend-sparrow/internal/service" + "spend-sparrow/internal/utils" "strings" ) diff --git a/handler/middleware/authenticate.go b/internal/handler/middleware/authenticate.go similarity index 95% rename from handler/middleware/authenticate.go rename to internal/handler/middleware/authenticate.go index 2742f49..bc7928a 100644 --- a/handler/middleware/authenticate.go +++ b/internal/handler/middleware/authenticate.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - "spend-sparrow/service" - "spend-sparrow/types" + "spend-sparrow/internal/service" + "spend-sparrow/internal/types" ) type ContextKey string diff --git a/handler/middleware/cache_control.go b/internal/handler/middleware/cache_control.go similarity index 100% rename from handler/middleware/cache_control.go rename to internal/handler/middleware/cache_control.go diff --git a/handler/middleware/cross_site_request_forgery.go b/internal/handler/middleware/cross_site_request_forgery.go similarity index 92% rename from handler/middleware/cross_site_request_forgery.go rename to internal/handler/middleware/cross_site_request_forgery.go index d165978..307b886 100644 --- a/handler/middleware/cross_site_request_forgery.go +++ b/internal/handler/middleware/cross_site_request_forgery.go @@ -4,10 +4,10 @@ import ( "net/http" "strings" - "spend-sparrow/log" - "spend-sparrow/service" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/log" + "spend-sparrow/internal/service" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" ) type csrfResponseWriter struct { diff --git a/handler/middleware/default.go b/internal/handler/middleware/default.go similarity index 100% rename from handler/middleware/default.go rename to internal/handler/middleware/default.go diff --git a/handler/middleware/generate_recurring_transactions.go b/internal/handler/middleware/generate_recurring_transactions.go similarity index 93% rename from handler/middleware/generate_recurring_transactions.go rename to internal/handler/middleware/generate_recurring_transactions.go index fe8fdee..5a5f1c9 100644 --- a/handler/middleware/generate_recurring_transactions.go +++ b/internal/handler/middleware/generate_recurring_transactions.go @@ -2,7 +2,7 @@ package middleware import ( "net/http" - "spend-sparrow/service" + "spend-sparrow/internal/service" ) func GenerateRecurringTransactions(transactionRecurring service.TransactionRecurring) func(http.Handler) http.Handler { diff --git a/handler/middleware/gzip.go b/internal/handler/middleware/gzip.go similarity index 96% rename from handler/middleware/gzip.go rename to internal/handler/middleware/gzip.go index 1d73f8e..626bba1 100644 --- a/handler/middleware/gzip.go +++ b/internal/handler/middleware/gzip.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "spend-sparrow/log" + "spend-sparrow/internal/log" ) type gzipResponseWriter struct { diff --git a/handler/middleware/logger.go b/internal/handler/middleware/logger.go similarity index 97% rename from handler/middleware/logger.go rename to internal/handler/middleware/logger.go index f0ec9a0..c374929 100644 --- a/handler/middleware/logger.go +++ b/internal/handler/middleware/logger.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - "spend-sparrow/log" + "spend-sparrow/internal/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" diff --git a/handler/middleware/security_headers.go b/internal/handler/middleware/security_headers.go similarity index 97% rename from handler/middleware/security_headers.go rename to internal/handler/middleware/security_headers.go index 948a159..c024295 100644 --- a/handler/middleware/security_headers.go +++ b/internal/handler/middleware/security_headers.go @@ -3,7 +3,7 @@ package middleware import ( "net/http" - "spend-sparrow/types" + "spend-sparrow/internal/types" ) func SecurityHeaders(serverSettings *types.Settings) func(http.Handler) http.Handler { diff --git a/handler/middleware/wrapper.go b/internal/handler/middleware/wrapper.go similarity index 100% rename from handler/middleware/wrapper.go rename to internal/handler/middleware/wrapper.go diff --git a/handler/render.go b/internal/handler/render.go similarity index 90% rename from handler/render.go rename to internal/handler/render.go index 7a447a1..fd2ac0e 100644 --- a/handler/render.go +++ b/internal/handler/render.go @@ -1,10 +1,10 @@ package handler import ( - "spend-sparrow/log" - "spend-sparrow/template" - "spend-sparrow/template/auth" - "spend-sparrow/types" + "spend-sparrow/internal/log" + "spend-sparrow/internal/template" + "spend-sparrow/internal/template/auth" + "spend-sparrow/internal/types" "net/http" diff --git a/handler/root_and_404.go b/internal/handler/root_and_404.go similarity index 92% rename from handler/root_and_404.go rename to internal/handler/root_and_404.go index 2638244..eab58e6 100644 --- a/handler/root_and_404.go +++ b/internal/handler/root_and_404.go @@ -2,8 +2,8 @@ package handler import ( "net/http" - "spend-sparrow/handler/middleware" - "spend-sparrow/template" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/template" "github.com/a-h/templ" ) diff --git a/handler/transaction.go b/internal/handler/transaction.go similarity index 97% rename from handler/transaction.go rename to internal/handler/transaction.go index 22a8d59..d335bbf 100644 --- a/handler/transaction.go +++ b/internal/handler/transaction.go @@ -3,11 +3,11 @@ package handler import ( "fmt" "net/http" - "spend-sparrow/handler/middleware" - "spend-sparrow/service" - t "spend-sparrow/template/transaction" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/service" + t "spend-sparrow/internal/template/transaction" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" "strconv" "time" diff --git a/handler/transaction_recurring.go b/internal/handler/transaction_recurring.go similarity index 94% rename from handler/transaction_recurring.go rename to internal/handler/transaction_recurring.go index cd17f3f..4f8392e 100644 --- a/handler/transaction_recurring.go +++ b/internal/handler/transaction_recurring.go @@ -2,11 +2,11 @@ package handler import ( "net/http" - "spend-sparrow/handler/middleware" - "spend-sparrow/service" - t "spend-sparrow/template/transaction_recurring" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/service" + t "spend-sparrow/internal/template/transaction_recurring" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" ) type TransactionRecurring interface { diff --git a/handler/treasure_chest.go b/internal/handler/treasure_chest.go similarity index 94% rename from handler/treasure_chest.go rename to internal/handler/treasure_chest.go index 0d016d2..438fd06 100644 --- a/handler/treasure_chest.go +++ b/internal/handler/treasure_chest.go @@ -2,12 +2,12 @@ package handler import ( "net/http" - "spend-sparrow/handler/middleware" - "spend-sparrow/service" - tr "spend-sparrow/template/transaction_recurring" - t "spend-sparrow/template/treasurechest" - "spend-sparrow/types" - "spend-sparrow/utils" + "spend-sparrow/internal/handler/middleware" + "spend-sparrow/internal/service" + tr "spend-sparrow/internal/template/transaction_recurring" + t "spend-sparrow/internal/template/treasurechest" + "spend-sparrow/internal/types" + "spend-sparrow/internal/utils" "github.com/a-h/templ" "github.com/google/uuid" diff --git a/log/default.go b/internal/log/default.go similarity index 100% rename from log/default.go rename to internal/log/default.go diff --git a/service/account.go b/internal/service/account.go similarity index 98% rename from service/account.go rename to internal/service/account.go index ce5174b..0749fc3 100644 --- a/service/account.go +++ b/internal/service/account.go @@ -3,10 +3,9 @@ package service import ( "errors" "fmt" - - "spend-sparrow/db" - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/db" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "github.com/google/uuid" "github.com/jmoiron/sqlx" diff --git a/service/auth.go b/internal/service/auth.go similarity index 98% rename from service/auth.go rename to internal/service/auth.go index 77408cb..f9d80c4 100644 --- a/service/auth.go +++ b/internal/service/auth.go @@ -5,14 +5,13 @@ import ( "crypto/subtle" "errors" "net/mail" + "spend-sparrow/internal/db" + "spend-sparrow/internal/log" + mailTemplate "spend-sparrow/internal/template/mail" + "spend-sparrow/internal/types" "strings" "time" - "spend-sparrow/db" - "spend-sparrow/log" - mailTemplate "spend-sparrow/template/mail" - "spend-sparrow/types" - "github.com/google/uuid" "golang.org/x/crypto/argon2" ) diff --git a/service/clock.go b/internal/service/clock.go similarity index 100% rename from service/clock.go rename to internal/service/clock.go diff --git a/service/default.go b/internal/service/default.go similarity index 100% rename from service/default.go rename to internal/service/default.go diff --git a/service/error.go b/internal/service/error.go similarity index 100% rename from service/error.go rename to internal/service/error.go diff --git a/service/mail.go b/internal/service/mail.go similarity index 94% rename from service/mail.go rename to internal/service/mail.go index 4efa662..7bca066 100644 --- a/service/mail.go +++ b/internal/service/mail.go @@ -1,11 +1,10 @@ package service import ( - "spend-sparrow/log" - "spend-sparrow/types" - "fmt" "net/smtp" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" ) type Mail interface { diff --git a/service/random_generator.go b/internal/service/random_generator.go similarity index 94% rename from service/random_generator.go rename to internal/service/random_generator.go index 9dbc269..bce038a 100644 --- a/service/random_generator.go +++ b/internal/service/random_generator.go @@ -1,11 +1,10 @@ package service import ( - "spend-sparrow/log" - "spend-sparrow/types" - "crypto/rand" "encoding/base64" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "github.com/google/uuid" ) diff --git a/service/transaction.go b/internal/service/transaction.go similarity index 99% rename from service/transaction.go rename to internal/service/transaction.go index ab0a130..9415871 100644 --- a/service/transaction.go +++ b/internal/service/transaction.go @@ -3,9 +3,9 @@ package service import ( "errors" "fmt" - "spend-sparrow/db" - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/db" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "time" "github.com/google/uuid" diff --git a/service/transaction_recurring.go b/internal/service/transaction_recurring.go similarity index 99% rename from service/transaction_recurring.go rename to internal/service/transaction_recurring.go index f5a776c..f010e4d 100644 --- a/service/transaction_recurring.go +++ b/internal/service/transaction_recurring.go @@ -3,13 +3,12 @@ package service import ( "errors" "fmt" + "spend-sparrow/internal/db" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "strconv" "time" - "spend-sparrow/db" - "spend-sparrow/log" - "spend-sparrow/types" - "github.com/google/uuid" "github.com/jmoiron/sqlx" "github.com/prometheus/client_golang/prometheus" diff --git a/service/treasure_chest.go b/internal/service/treasure_chest.go similarity index 98% rename from service/treasure_chest.go rename to internal/service/treasure_chest.go index 482db4d..d955482 100644 --- a/service/treasure_chest.go +++ b/internal/service/treasure_chest.go @@ -4,10 +4,9 @@ import ( "errors" "fmt" "slices" - - "spend-sparrow/db" - "spend-sparrow/log" - "spend-sparrow/types" + "spend-sparrow/internal/db" + "spend-sparrow/internal/log" + "spend-sparrow/internal/types" "github.com/google/uuid" "github.com/jmoiron/sqlx" diff --git a/internal/service/treasure_chest_test.go b/internal/service/treasure_chest_test.go new file mode 100644 index 0000000..f5d7fe1 --- /dev/null +++ b/internal/service/treasure_chest_test.go @@ -0,0 +1,10 @@ +package service_test + +// import ( +// "spned-sparrow" +// ) +// +// func TestTreasureChestProhibitDeleteIfTransactionRecurringExists(t *testing.T) { +// service := main.Setup +// +// } diff --git a/template/account/account.templ b/internal/template/account/account.templ similarity index 97% rename from template/account/account.templ rename to internal/template/account/account.templ index 8ad3e07..f0706ae 100644 --- a/template/account/account.templ +++ b/internal/template/account/account.templ @@ -1,8 +1,8 @@ package account import "fmt" -import "spend-sparrow/template/svg" -import "spend-sparrow/types" +import "spend-sparrow/internal/template/svg" +import "spend-sparrow/internal/types" templ Account(accounts []*types.Account) {