feat: extract account to domain package
All checks were successful
Build and Push Docker Image / Build-And-Push-Docker-Image (push) Successful in 1m17s

This commit is contained in:
2025-12-24 07:45:44 +01:00
parent 1e61b765ae
commit f9a5a9e5f9
24 changed files with 281 additions and 284 deletions

View File

@@ -1,44 +1,36 @@
package handler
package account
import (
"net/http"
"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"
"net/http"
"spend-sparrow/internal/core"
"spend-sparrow/internal/utils"
)
type Account interface {
Handle(router *http.ServeMux)
type Handler struct {
s Service
r *core.Render
}
type AccountImpl struct {
s service.Account
r *Render
}
func NewAccount(s service.Account, r *Render) Account {
return AccountImpl{
func NewHandler(s Service, r *core.Render) Handler {
return Handler{
s: s,
r: r,
}
}
func (h AccountImpl) Handle(r *http.ServeMux) {
func (h Handler) Handle(r *http.ServeMux) {
r.Handle("GET /account", h.handleAccountPage())
r.Handle("GET /account/{id}", h.handleAccountItemComp())
r.Handle("POST /account/{id}", h.handleUpdateAccount())
r.Handle("DELETE /account/{id}", h.handleDeleteAccount())
}
func (h AccountImpl) handleAccountPage() http.HandlerFunc {
func (h Handler) handleAccountPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -46,20 +38,20 @@ func (h AccountImpl) handleAccountPage() http.HandlerFunc {
accounts, err := h.s.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
comp := t.Account(accounts)
comp := template(accounts)
h.r.RenderLayout(r, w, comp, user)
}
}
func (h AccountImpl) handleAccountItemComp() http.HandlerFunc {
func (h Handler) handleAccountItemComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -67,39 +59,39 @@ func (h AccountImpl) handleAccountItemComp() http.HandlerFunc {
id := r.PathValue("id")
if id == "new" {
comp := t.EditAccount(nil)
comp := editAccount(nil)
h.r.Render(r, w, comp)
return
}
account, err := h.s.Get(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
var comp templ.Component
if r.URL.Query().Get("edit") == "true" {
comp = t.EditAccount(account)
comp = editAccount(account)
} else {
comp = t.AccountItem(account)
comp = accountItem(account)
}
h.r.Render(r, w, comp)
}
}
func (h AccountImpl) handleUpdateAccount() http.HandlerFunc {
func (h Handler) handleUpdateAccount() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
}
var (
account *types.Account
account *Account
err error
)
id := r.PathValue("id")
@@ -107,27 +99,27 @@ func (h AccountImpl) handleUpdateAccount() http.HandlerFunc {
if id == "new" {
account, err = h.s.Add(r.Context(), user, name)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
} else {
account, err = h.s.UpdateName(r.Context(), user, id, name)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}
comp := t.AccountItem(account)
comp := accountItem(account)
h.r.Render(r, w, comp)
}
}
func (h AccountImpl) handleDeleteAccount() http.HandlerFunc {
func (h Handler) handleDeleteAccount() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -137,7 +129,7 @@ func (h AccountImpl) handleDeleteAccount() http.HandlerFunc {
err := h.s.Delete(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}

View File

@@ -1,42 +1,42 @@
package service
package account
import (
"context"
"errors"
"fmt"
"log/slog"
"spend-sparrow/internal/db"
"spend-sparrow/internal/types"
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
"log/slog"
"spend-sparrow/internal/db"
"spend-sparrow/internal/service"
"spend-sparrow/internal/types"
)
type Account interface {
Add(ctx context.Context, user *types.User, name string) (*types.Account, error)
UpdateName(ctx context.Context, user *types.User, id string, name string) (*types.Account, error)
Get(ctx context.Context, user *types.User, id string) (*types.Account, error)
GetAll(ctx context.Context, user *types.User) ([]*types.Account, error)
type Service interface {
Add(ctx context.Context, user *types.User, name string) (*Account, error)
UpdateName(ctx context.Context, user *types.User, id string, name string) (*Account, error)
Get(ctx context.Context, user *types.User, id string) (*Account, error)
GetAll(ctx context.Context, user *types.User) ([]*Account, error)
Delete(ctx context.Context, user *types.User, id string) error
}
type AccountImpl struct {
type ServiceImpl struct {
db *sqlx.DB
clock Clock
random Random
clock service.Clock
random service.Random
}
func NewAccount(db *sqlx.DB, random Random, clock Clock) Account {
return AccountImpl{
func NewServiceImpl(db *sqlx.DB, random service.Random, clock service.Clock) Service {
return ServiceImpl{
db: db,
clock: clock,
random: random,
}
}
func (s AccountImpl) Add(ctx context.Context, user *types.User, name string) (*types.Account, error) {
func (s ServiceImpl) Add(ctx context.Context, user *types.User, name string) (*Account, error) {
if user == nil {
return nil, ErrUnauthorized
return nil, types.ErrUnauthorized
}
newId, err := s.random.UUID(ctx)
@@ -44,12 +44,12 @@ func (s AccountImpl) Add(ctx context.Context, user *types.User, name string) (*t
return nil, types.ErrInternal
}
err = validateString(name, "name")
err = service.ValidateString(name, "name")
if err != nil {
return nil, err
}
account := &types.Account{
account := &Account{
Id: newId,
UserId: user.Id,
@@ -76,18 +76,18 @@ func (s AccountImpl) Add(ctx context.Context, user *types.User, name string) (*t
return account, nil
}
func (s AccountImpl) UpdateName(ctx context.Context, user *types.User, id string, name string) (*types.Account, error) {
func (s ServiceImpl) UpdateName(ctx context.Context, user *types.User, id string, name string) (*Account, error) {
if user == nil {
return nil, ErrUnauthorized
return nil, types.ErrUnauthorized
}
err := validateString(name, "name")
err := service.ValidateString(name, "name")
if err != nil {
return nil, err
}
uuid, err := uuid.Parse(id)
if err != nil {
slog.ErrorContext(ctx, "account update", "err", err)
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
return nil, fmt.Errorf("could not parse Id: %w", service.ErrBadRequest)
}
tx, err := s.db.BeginTxx(ctx, nil)
@@ -99,12 +99,12 @@ func (s AccountImpl) UpdateName(ctx context.Context, user *types.User, id string
_ = tx.Rollback()
}()
var account types.Account
var account Account
err = tx.GetContext(ctx, &account, `SELECT * FROM account WHERE user_id = ? AND id = ?`, user.Id, uuid)
err = db.TransformAndLogDbError(ctx, "account Update", nil, err)
if err != nil {
if errors.Is(err, db.ErrNotFound) {
return nil, fmt.Errorf("account %v not found: %w", id, ErrBadRequest)
return nil, fmt.Errorf("account %v not found: %w", id, service.ErrBadRequest)
}
return nil, types.ErrInternal
}
@@ -136,17 +136,17 @@ func (s AccountImpl) UpdateName(ctx context.Context, user *types.User, id string
return &account, nil
}
func (s AccountImpl) Get(ctx context.Context, user *types.User, id string) (*types.Account, error) {
func (s ServiceImpl) Get(ctx context.Context, user *types.User, id string) (*Account, error) {
if user == nil {
return nil, ErrUnauthorized
return nil, service.ErrUnauthorized
}
uuid, err := uuid.Parse(id)
if err != nil {
slog.ErrorContext(ctx, "account get", "err", err)
return nil, fmt.Errorf("could not parse Id: %w", ErrBadRequest)
return nil, fmt.Errorf("could not parse Id: %w", service.ErrBadRequest)
}
var account types.Account
var account Account
err = s.db.GetContext(ctx, &account, `
SELECT * FROM account WHERE user_id = ? AND id = ?`, user.Id, uuid)
err = db.TransformAndLogDbError(ctx, "account Get", nil, err)
@@ -158,12 +158,12 @@ func (s AccountImpl) Get(ctx context.Context, user *types.User, id string) (*typ
return &account, nil
}
func (s AccountImpl) GetAll(ctx context.Context, user *types.User) ([]*types.Account, error) {
func (s ServiceImpl) GetAll(ctx context.Context, user *types.User) ([]*Account, error) {
if user == nil {
return nil, ErrUnauthorized
return nil, service.ErrUnauthorized
}
accounts := make([]*types.Account, 0)
accounts := make([]*Account, 0)
err := s.db.SelectContext(ctx, &accounts, `
SELECT * FROM account WHERE user_id = ? ORDER BY name`, user.Id)
err = db.TransformAndLogDbError(ctx, "account GetAll", nil, err)
@@ -174,14 +174,14 @@ func (s AccountImpl) GetAll(ctx context.Context, user *types.User) ([]*types.Acc
return accounts, nil
}
func (s AccountImpl) Delete(ctx context.Context, user *types.User, id string) error {
func (s ServiceImpl) Delete(ctx context.Context, user *types.User, id string) error {
if user == nil {
return ErrUnauthorized
return service.ErrUnauthorized
}
uuid, err := uuid.Parse(id)
if err != nil {
slog.ErrorContext(ctx, "account delete", "err", err)
return fmt.Errorf("could not parse Id: %w", ErrBadRequest)
return fmt.Errorf("could not parse Id: %w", service.ErrBadRequest)
}
tx, err := s.db.BeginTxx(ctx, nil)
@@ -200,7 +200,7 @@ func (s AccountImpl) Delete(ctx context.Context, user *types.User, id string) er
return err
}
if transactionsCount > 0 {
return fmt.Errorf("account has transactions, cannot delete: %w", ErrBadRequest)
return fmt.Errorf("account has transactions, cannot delete: %w", service.ErrBadRequest)
}
res, err := tx.ExecContext(ctx, "DELETE FROM account WHERE id = ? and user_id = ?", uuid, user.Id)

View File

@@ -3,7 +3,7 @@ package account
import "spend-sparrow/internal/template/svg"
import "spend-sparrow/internal/types"
templ Account(accounts []*types.Account) {
templ template(accounts []*Account) {
<div class="max-w-6xl mt-10 mx-auto">
<button
hx-get="/account/new"
@@ -16,28 +16,28 @@ templ Account(accounts []*types.Account) {
</button>
<div id="account-items" class="my-6 flex flex-col items-center">
for _, account := range accounts {
@AccountItem(account)
@accountItem(account)
}
</div>
</div>
}
templ EditAccount(account *types.Account) {
templ editAccount(account *Account) {
{{
var (
name string
id string
cancelUrl string
)
if account == nil {
name = ""
id = "new"
cancelUrl = "/empty"
} else {
name = account.Name
id = account.Id.String()
cancelUrl = "/account/" + id
}
var (
name string
id string
cancelUrl string
)
if account == nil {
name = ""
id = "new"
cancelUrl = "/empty"
} else {
name = account.Name
id = account.Id.String()
cancelUrl = "/account/" + id
}
}}
<div id="account" class="border-1 border-gray-300 w-full my-4 p-4 bg-gray-50 rounded-lg">
<form
@@ -77,7 +77,7 @@ templ EditAccount(account *types.Account) {
</div>
}
templ AccountItem(account *types.Account) {
templ accountItem(account *Account) {
<div id="account" class="border-1 border-gray-300 w-full my-4 p-4 bg-gray-50 rounded-lg">
<div class="text-xl flex justify-end gap-4">
<p class="mr-auto">{ account.Name }</p>

View File

@@ -1,9 +1,8 @@
package types
package account
import (
"time"
"github.com/google/uuid"
"time"
)
// The Account holds money.

39
internal/core/auth.go Normal file
View File

@@ -0,0 +1,39 @@
package core
import (
"net/http"
"spend-sparrow/internal/types"
)
type ContextKey string
var SessionKey ContextKey = "session"
var UserKey ContextKey = "user"
func GetUser(r *http.Request) *types.User {
obj := r.Context().Value(UserKey)
if obj == nil {
return nil
}
user, ok := obj.(*types.User)
if !ok {
return nil
}
return user
}
func GetSession(r *http.Request) *types.Session {
obj := r.Context().Value(SessionKey)
if obj == nil {
return nil
}
session, ok := obj.(*types.Session)
if !ok {
return nil
}
return session
}

View File

@@ -1,4 +1,4 @@
package handler
package core
import (
"errors"
@@ -12,7 +12,7 @@ import (
"go.opentelemetry.io/otel/trace"
)
func handleError(w http.ResponseWriter, r *http.Request, err error) {
func HandleError(w http.ResponseWriter, r *http.Request, err error) {
switch {
case errors.Is(err, service.ErrUnauthorized):
utils.TriggerToastWithStatus(r.Context(), w, r, "error", "You are not autorized to perform this operation.", http.StatusUnauthorized)
@@ -37,7 +37,7 @@ func extractErrorMessage(err error) string {
return strings.SplitN(errMsg, ":", 2)[0]
}
func updateSpan(r *http.Request) {
func UpdateSpan(r *http.Request) {
currentSpan := trace.SpanFromContext(r.Context())
if currentSpan != nil {
currentSpan.SetAttributes(attribute.String("http.pattern", r.Pattern))

View File

@@ -1,4 +1,4 @@
package template
package core
import "spend-sparrow/internal/template/svg"

View File

@@ -1,13 +1,10 @@
package handler
package core
import (
"github.com/a-h/templ"
"log/slog"
"net/http"
"spend-sparrow/internal/template"
"spend-sparrow/internal/template/auth"
"spend-sparrow/internal/types"
"github.com/a-h/templ"
)
type Render struct {
@@ -37,15 +34,15 @@ func (render *Render) RenderLayout(r *http.Request, w http.ResponseWriter, slot
func (render *Render) RenderLayoutWithStatus(r *http.Request, w http.ResponseWriter, slot templ.Component, user *types.User, status int) {
userComp := render.getUserComp(user)
layout := template.Layout(slot, userComp, user != nil, r.URL.Path)
layout := Layout(slot, userComp, user != nil, r.URL.Path)
render.RenderWithStatus(r, w, layout, status)
}
func (render *Render) getUserComp(user *types.User) templ.Component {
if user != nil {
return auth.UserComp(user.Email)
return UserComp(user.Email)
} else {
return auth.UserComp("")
return UserComp("")
}
}

View File

@@ -1,4 +1,4 @@
package auth
package core
templ UserComp(user string) {
<div id="user-info" class="flex items-center gap-2 text-nowrap">

View File

@@ -1,19 +1,20 @@
package internal
import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"os/signal"
"spend-sparrow/internal/account"
"spend-sparrow/internal/core"
"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"
@@ -113,17 +114,17 @@ func createHandlerWithServices(ctx context.Context, d *sqlx.DB, serverSettings *
mailService := service.NewMail(serverSettings)
authService := service.NewAuth(authDb, randomService, clockService, mailService, serverSettings)
accountService := service.NewAccount(d, randomService, clockService)
accountService := account.NewServiceImpl(d, randomService, clockService)
treasureChestService := service.NewTreasureChest(d, randomService, clockService)
transactionService := service.NewTransaction(d, randomService, clockService)
transactionRecurringService := service.NewTransactionRecurring(d, randomService, clockService, transactionService)
dashboardService := service.NewDashboard(d)
render := handler.NewRender()
render := core.NewRender()
indexHandler := handler.NewIndex(render, clockService)
dashboardHandler := handler.NewDashboard(render, dashboardService, treasureChestService)
authHandler := handler.NewAuth(authService, render)
accountHandler := handler.NewAccount(accountService, render)
accountHandler := account.NewHandler(accountService, render)
treasureChestHandler := handler.NewTreasureChest(treasureChestService, transactionRecurringService, render)
transactionHandler := handler.NewTransaction(transactionService, accountService, treasureChestService, render)
transactionRecurringHandler := handler.NewTransactionRecurring(transactionRecurringService, render)

View File

@@ -5,6 +5,7 @@ import (
"log/slog"
"net/http"
"net/url"
"spend-sparrow/internal/core"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/service"
"spend-sparrow/internal/template/auth"
@@ -19,10 +20,10 @@ type Auth interface {
type AuthImpl struct {
service service.Auth
render *Render
render *core.Render
}
func NewAuth(service service.Auth, render *Render) Auth {
func NewAuth(service service.Auth, render *core.Render) Auth {
return AuthImpl{
service: service,
render: render,
@@ -58,9 +59,9 @@ var (
func (handler AuthImpl) handleSignInPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user != nil {
if !user.EmailVerified {
utils.DoRedirect(w, r, "/auth/verify")
@@ -78,10 +79,10 @@ func (handler AuthImpl) handleSignInPage() http.HandlerFunc {
func (handler AuthImpl) handleSignIn() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user, err := utils.WaitMinimumTime(securityWaitDuration, func() (*types.User, error) {
session := middleware.GetSession(r)
session := core.GetSession(r)
email := r.FormValue("email")
password := r.FormValue("password")
@@ -115,9 +116,9 @@ func (handler AuthImpl) handleSignIn() http.HandlerFunc {
func (handler AuthImpl) handleSignUpPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user != nil {
if !user.EmailVerified {
@@ -135,9 +136,9 @@ func (handler AuthImpl) handleSignUpPage() http.HandlerFunc {
func (handler AuthImpl) handleSignUpVerifyPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -155,9 +156,9 @@ func (handler AuthImpl) handleSignUpVerifyPage() http.HandlerFunc {
func (handler AuthImpl) handleVerifyResendComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -174,7 +175,7 @@ func (handler AuthImpl) handleVerifyResendComp() http.HandlerFunc {
func (handler AuthImpl) handleSignUpVerifyResponsePage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
token := r.URL.Query().Get("token")
@@ -196,7 +197,7 @@ func (handler AuthImpl) handleSignUpVerifyResponsePage() http.HandlerFunc {
func (handler AuthImpl) handleSignUp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
var email = r.FormValue("email")
var password = r.FormValue("password")
@@ -234,9 +235,9 @@ func (handler AuthImpl) handleSignUp() http.HandlerFunc {
func (handler AuthImpl) handleSignOut() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
session := middleware.GetSession(r)
session := core.GetSession(r)
if session != nil {
err := handler.service.SignOut(r.Context(), session.Id)
@@ -263,9 +264,9 @@ func (handler AuthImpl) handleSignOut() http.HandlerFunc {
func (handler AuthImpl) handleDeleteAccountPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -278,9 +279,9 @@ func (handler AuthImpl) handleDeleteAccountPage() http.HandlerFunc {
func (handler AuthImpl) handleDeleteAccountComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -304,11 +305,11 @@ func (handler AuthImpl) handleDeleteAccountComp() http.HandlerFunc {
func (handler AuthImpl) handleChangePasswordPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
isPasswordReset := r.URL.Query().Has("token")
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil && !isPasswordReset {
utils.DoRedirect(w, r, "/auth/signin")
@@ -322,10 +323,10 @@ func (handler AuthImpl) handleChangePasswordPage() http.HandlerFunc {
func (handler AuthImpl) handleChangePasswordComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
session := middleware.GetSession(r)
user := middleware.GetUser(r)
session := core.GetSession(r)
user := core.GetUser(r)
if session == nil || user == nil {
utils.TriggerToastWithStatus(r.Context(), w, r, "error", "Unathorized", http.StatusUnauthorized)
return
@@ -346,9 +347,9 @@ func (handler AuthImpl) handleChangePasswordComp() http.HandlerFunc {
func (handler AuthImpl) handleForgotPasswordPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user != nil {
utils.DoRedirect(w, r, "/")
return
@@ -361,7 +362,7 @@ func (handler AuthImpl) handleForgotPasswordPage() http.HandlerFunc {
func (handler AuthImpl) handleForgotPasswordComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
email := r.FormValue("email")
if email == "" {
@@ -384,7 +385,7 @@ func (handler AuthImpl) handleForgotPasswordComp() http.HandlerFunc {
func (handler AuthImpl) handleForgotPasswordResponseComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
pageUrl, err := url.Parse(r.Header.Get("Hx-Current-Url"))
if err != nil {

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"log/slog"
"net/http"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
"spend-sparrow/internal/template/dashboard"
"spend-sparrow/internal/utils"
@@ -19,12 +19,12 @@ type Dashboard interface {
}
type DashboardImpl struct {
r *Render
r *core.Render
d *service.Dashboard
treasureChest service.TreasureChest
}
func NewDashboard(r *Render, d *service.Dashboard, treasureChest service.TreasureChest) Dashboard {
func NewDashboard(r *core.Render, d *service.Dashboard, treasureChest service.TreasureChest) Dashboard {
return DashboardImpl{
r: r,
d: d,
@@ -41,9 +41,9 @@ func (handler DashboardImpl) Handle(router *http.ServeMux) {
func (handler DashboardImpl) handleDashboard() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -51,7 +51,7 @@ func (handler DashboardImpl) handleDashboard() http.HandlerFunc {
treasureChests, err := handler.treasureChest.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -62,13 +62,13 @@ func (handler DashboardImpl) handleDashboard() http.HandlerFunc {
func (handler DashboardImpl) handleDashboardMainChart() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
series, err := handler.d.MainChart(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -128,13 +128,13 @@ func (handler DashboardImpl) handleDashboardMainChart() http.HandlerFunc {
func (handler DashboardImpl) handleDashboardTreasureChests() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
treeList, err := handler.d.TreasureChests(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -183,9 +183,9 @@ func (handler DashboardImpl) handleDashboardTreasureChests() http.HandlerFunc {
func (handler DashboardImpl) handleDashboardTreasureChest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
var treasureChestId *uuid.UUID
@@ -193,7 +193,7 @@ func (handler DashboardImpl) handleDashboardTreasureChest() http.HandlerFunc {
if treasureChestStr != "" {
id, err := uuid.Parse(treasureChestStr)
if err != nil {
handleError(w, r, fmt.Errorf("could not parse treasure chest: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse treasure chest: %w", service.ErrBadRequest))
return
}
@@ -202,7 +202,7 @@ func (handler DashboardImpl) handleDashboardTreasureChest() http.HandlerFunc {
series, err := handler.d.TreasureChest(r.Context(), user, treasureChestId)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}

View File

@@ -3,17 +3,11 @@ package middleware
import (
"context"
"net/http"
"strings"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
"spend-sparrow/internal/types"
"strings"
)
type ContextKey string
var SessionKey ContextKey = "session"
var UserKey ContextKey = "user"
func Authenticate(service service.Auth) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -41,42 +35,14 @@ func Authenticate(service service.Auth) func(http.Handler) http.Handler {
http.SetCookie(w, &cookie)
}
ctx = context.WithValue(ctx, UserKey, user)
ctx = context.WithValue(ctx, SessionKey, session)
ctx = context.WithValue(ctx, core.UserKey, user)
ctx = context.WithValue(ctx, core.SessionKey, session)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func GetUser(r *http.Request) *types.User {
obj := r.Context().Value(UserKey)
if obj == nil {
return nil
}
user, ok := obj.(*types.User)
if !ok {
return nil
}
return user
}
func GetSession(r *http.Request) *types.Session {
obj := r.Context().Value(SessionKey)
if obj == nil {
return nil
}
session, ok := obj.(*types.Session)
if !ok {
return nil
}
return session
}
func getSessionID(r *http.Request) string {
cookie, err := r.Cookie("id")
if err != nil {

View File

@@ -3,6 +3,7 @@ package middleware
import (
"log/slog"
"net/http"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
"spend-sparrow/internal/utils"
"strings"
@@ -40,7 +41,7 @@ func CrossSiteRequestForgery(auth service.Auth) func(http.Handler) http.Handler
return
}
session := GetSession(r)
session := core.GetSession(r)
if r.Method == http.MethodPost ||
r.Method == http.MethodPut ||

View File

@@ -2,7 +2,7 @@ package handler
import (
"net/http"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
"spend-sparrow/internal/template"
"spend-sparrow/internal/utils"
@@ -15,11 +15,11 @@ type Index interface {
}
type IndexImpl struct {
r *Render
r *core.Render
c service.Clock
}
func NewIndex(r *Render, c service.Clock) Index {
func NewIndex(r *core.Render, c service.Clock) Index {
return IndexImpl{
r: r,
c: c,
@@ -33,9 +33,9 @@ func (handler IndexImpl) Handle(router *http.ServeMux) {
func (handler IndexImpl) handleRootAnd404() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
htmx := utils.IsHtmx(r)
@@ -65,7 +65,7 @@ func (handler IndexImpl) handleRootAnd404() http.HandlerFunc {
func (handler IndexImpl) handleEmpty() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
// Return nothing
}

View File

@@ -4,7 +4,8 @@ import (
"fmt"
"math"
"net/http"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/account"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
t "spend-sparrow/internal/template/transaction"
"spend-sparrow/internal/types"
@@ -22,12 +23,12 @@ type Transaction interface {
type TransactionImpl struct {
s service.Transaction
account service.Account
account account.Service
treasureChest service.TreasureChest
r *Render
r *core.Render
}
func NewTransaction(s service.Transaction, account service.Account, treasureChest service.TreasureChest, r *Render) Transaction {
func NewTransaction(s service.Transaction, account account.Service, treasureChest service.TreasureChest, r *core.Render) Transaction {
return TransactionImpl{
s: s,
account: account,
@@ -46,9 +47,9 @@ func (h TransactionImpl) Handle(r *http.ServeMux) {
func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -63,19 +64,19 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
transactions, err := h.s.GetAll(r.Context(), user, filter)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
accounts, err := h.account.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
treasureChests, err := h.treasureChest.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -93,9 +94,9 @@ func (h TransactionImpl) handleTransactionPage() http.HandlerFunc {
func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -103,13 +104,13 @@ func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
accounts, err := h.account.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
treasureChests, err := h.treasureChest.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -122,7 +123,7 @@ func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
transaction, err := h.s.Get(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -139,9 +140,9 @@ func (h TransactionImpl) handleTransactionItemComp() http.HandlerFunc {
func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -156,7 +157,7 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
if idStr != "new" {
id, err = uuid.Parse(idStr)
if err != nil {
handleError(w, r, fmt.Errorf("could not parse Id: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse Id: %w", service.ErrBadRequest))
return
}
}
@@ -166,7 +167,7 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
if accountIdStr != "" {
i, err := uuid.Parse(accountIdStr)
if err != nil {
handleError(w, r, fmt.Errorf("could not parse account id: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse account id: %w", service.ErrBadRequest))
return
}
accountId = &i
@@ -177,7 +178,7 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
if treasureChestIdStr != "" {
i, err := uuid.Parse(treasureChestIdStr)
if err != nil {
handleError(w, r, fmt.Errorf("could not parse treasure chest id: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse treasure chest id: %w", service.ErrBadRequest))
return
}
treasureChestId = &i
@@ -185,14 +186,14 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
valueF, err := strconv.ParseFloat(r.FormValue("value"), 64)
if err != nil {
handleError(w, r, fmt.Errorf("could not parse value: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse value: %w", service.ErrBadRequest))
return
}
value := int64(math.Round(valueF * service.DECIMALS_MULTIPLIER))
timestamp, err := time.Parse("2006-01-02", r.FormValue("timestamp"))
if err != nil {
handleError(w, r, fmt.Errorf("could not parse timestamp: %w", service.ErrBadRequest))
core.HandleError(w, r, fmt.Errorf("could not parse timestamp: %w", service.ErrBadRequest))
return
}
@@ -210,26 +211,26 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
if idStr == "new" {
transaction, err = h.s.Add(r.Context(), nil, user, input)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
} else {
transaction, err = h.s.Update(r.Context(), user, input)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}
accounts, err := h.account.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
treasureChests, err := h.treasureChest.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -241,9 +242,9 @@ func (h TransactionImpl) handleUpdateTransaction() http.HandlerFunc {
func (h TransactionImpl) handleRecalculate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -251,7 +252,7 @@ func (h TransactionImpl) handleRecalculate() http.HandlerFunc {
err := h.s.RecalculateBalances(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -261,9 +262,9 @@ func (h TransactionImpl) handleRecalculate() http.HandlerFunc {
func (h TransactionImpl) handleDeleteTransaction() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -273,13 +274,13 @@ func (h TransactionImpl) handleDeleteTransaction() http.HandlerFunc {
err := h.s.Delete(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}
}
func (h TransactionImpl) getTransactionData(accounts []*types.Account, treasureChests []*types.TreasureChest) (map[uuid.UUID]string, map[uuid.UUID]string) {
func (h TransactionImpl) getTransactionData(accounts []*account.Account, treasureChests []*types.TreasureChest) (map[uuid.UUID]string, map[uuid.UUID]string) {
accountMap := make(map[uuid.UUID]string, 0)
for _, account := range accounts {
accountMap[account.Id] = account.Name

View File

@@ -2,7 +2,7 @@ package handler
import (
"net/http"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
t "spend-sparrow/internal/template/transaction_recurring"
"spend-sparrow/internal/types"
@@ -15,10 +15,10 @@ type TransactionRecurring interface {
type TransactionRecurringImpl struct {
s service.TransactionRecurring
r *Render
r *core.Render
}
func NewTransactionRecurring(s service.TransactionRecurring, r *Render) TransactionRecurring {
func NewTransactionRecurring(s service.TransactionRecurring, r *core.Render) TransactionRecurring {
return TransactionRecurringImpl{
s: s,
r: r,
@@ -33,9 +33,9 @@ func (h TransactionRecurringImpl) Handle(r *http.ServeMux) {
func (h TransactionRecurringImpl) handleTransactionRecurringItemComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -50,9 +50,9 @@ func (h TransactionRecurringImpl) handleTransactionRecurringItemComp() http.Hand
func (h TransactionRecurringImpl) handleUpdateTransactionRecurring() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -72,13 +72,13 @@ func (h TransactionRecurringImpl) handleUpdateTransactionRecurring() http.Handle
if input.Id == "new" {
_, err := h.s.Add(r.Context(), user, input)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
} else {
_, err := h.s.Update(r.Context(), user, input)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}
@@ -89,9 +89,9 @@ func (h TransactionRecurringImpl) handleUpdateTransactionRecurring() http.Handle
func (h TransactionRecurringImpl) handleDeleteTransactionRecurring() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -103,7 +103,7 @@ func (h TransactionRecurringImpl) handleDeleteTransactionRecurring() http.Handle
err := h.s.Delete(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -120,13 +120,13 @@ func (h TransactionRecurringImpl) renderItems(w http.ResponseWriter, r *http.Req
if accountId != "" {
transactionsRecurring, err = h.s.GetAllByAccount(r.Context(), user, accountId)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
} else {
transactionsRecurring, err = h.s.GetAllByTreasureChest(r.Context(), user, treasureChestId)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}

View File

@@ -2,7 +2,7 @@ package handler
import (
"net/http"
"spend-sparrow/internal/handler/middleware"
"spend-sparrow/internal/core"
"spend-sparrow/internal/service"
tr "spend-sparrow/internal/template/transaction_recurring"
t "spend-sparrow/internal/template/treasurechest"
@@ -20,10 +20,10 @@ type TreasureChest interface {
type TreasureChestImpl struct {
s service.TreasureChest
transactionRecurring service.TransactionRecurring
r *Render
r *core.Render
}
func NewTreasureChest(s service.TreasureChest, transactionRecurring service.TransactionRecurring, r *Render) TreasureChest {
func NewTreasureChest(s service.TreasureChest, transactionRecurring service.TransactionRecurring, r *core.Render) TreasureChest {
return TreasureChestImpl{
s: s,
transactionRecurring: transactionRecurring,
@@ -40,9 +40,9 @@ func (h TreasureChestImpl) Handle(r *http.ServeMux) {
func (h TreasureChestImpl) handleTreasureChestPage() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -50,13 +50,13 @@ func (h TreasureChestImpl) handleTreasureChestPage() http.HandlerFunc {
treasureChests, err := h.s.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
transactionsRecurring, err := h.transactionRecurring.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -69,9 +69,9 @@ func (h TreasureChestImpl) handleTreasureChestPage() http.HandlerFunc {
func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -79,7 +79,7 @@ func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
treasureChests, err := h.s.GetAll(r.Context(), user)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -92,13 +92,13 @@ func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
treasureChest, err := h.s.Get(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
transactionsRecurring, err := h.transactionRecurring.GetAllByTreasureChest(r.Context(), user, treasureChest.Id.String())
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
transactionsRec := tr.TransactionRecurringItems(transactionsRecurring, "", "", treasureChest.Id.String())
@@ -116,9 +116,9 @@ func (h TreasureChestImpl) handleTreasureChestItemComp() http.HandlerFunc {
func (h TreasureChestImpl) handleUpdateTreasureChest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -134,20 +134,20 @@ func (h TreasureChestImpl) handleUpdateTreasureChest() http.HandlerFunc {
if id == "new" {
treasureChest, err = h.s.Add(r.Context(), user, parentId, name)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
} else {
treasureChest, err = h.s.Update(r.Context(), user, id, parentId, name)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}
transactionsRecurring, err := h.transactionRecurring.GetAllByTreasureChest(r.Context(), user, treasureChest.Id.String())
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
@@ -161,9 +161,9 @@ func (h TreasureChestImpl) handleUpdateTreasureChest() http.HandlerFunc {
func (h TreasureChestImpl) handleDeleteTreasureChest() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
updateSpan(r)
core.UpdateSpan(r)
user := middleware.GetUser(r)
user := core.GetUser(r)
if user == nil {
utils.DoRedirect(w, r, "/auth/signin")
return
@@ -173,7 +173,7 @@ func (h TreasureChestImpl) handleDeleteTreasureChest() http.HandlerFunc {
err := h.s.Delete(r.Context(), user, id)
if err != nil {
handleError(w, r, err)
core.HandleError(w, r, err)
return
}
}

View File

@@ -13,7 +13,7 @@ var (
safeInputRegex = regexp.MustCompile(`^[a-zA-Z0-9ÄÖÜäöüß,&'". \-\?]+$`)
)
func validateString(value string, fieldName string) error {
func ValidateString(value string, fieldName string) error {
switch {
case value == "":
return fmt.Errorf("field \"%s\" needs to be set: %w", fieldName, ErrBadRequest)

View File

@@ -499,13 +499,13 @@ func (s TransactionImpl) validateAndEnrichTransaction(ctx context.Context, tx *s
}
if input.Party != "" {
err = validateString(input.Party, "party")
err = ValidateString(input.Party, "party")
if err != nil {
return nil, err
}
}
if input.Description != "" {
err = validateString(input.Description, "description")
err = ValidateString(input.Description, "description")
if err != nil {
return nil, err
}

View File

@@ -471,13 +471,13 @@ func (s TransactionRecurringImpl) validateAndEnrichTransactionRecurring(
value := int64(math.Round(valueFloat * DECIMALS_MULTIPLIER))
if input.Party != "" {
err = validateString(input.Party, "party")
err = ValidateString(input.Party, "party")
if err != nil {
return nil, err
}
}
if input.Description != "" {
err = validateString(input.Description, "description")
err = ValidateString(input.Description, "description")
if err != nil {
return nil, err
}

View File

@@ -45,7 +45,7 @@ func (s TreasureChestImpl) Add(ctx context.Context, user *types.User, parentId,
return nil, types.ErrInternal
}
err = validateString(name, "name")
err = ValidateString(name, "name")
if err != nil {
return nil, err
}
@@ -92,7 +92,7 @@ func (s TreasureChestImpl) Update(ctx context.Context, user *types.User, idStr,
if user == nil {
return nil, ErrUnauthorized
}
err := validateString(name, "name")
err := ValidateString(name, "name")
if err != nil {
return nil, err
}

View File

@@ -1 +0,0 @@
package account

View File

@@ -4,9 +4,10 @@ import "fmt"
import "time"
import "spend-sparrow/internal/template/svg"
import "spend-sparrow/internal/types"
import "spend-sparrow/internal/account"
import "github.com/google/uuid"
templ Transaction(items templ.Component, filter types.TransactionItemsFilter, accounts []*types.Account, treasureChests []*types.TreasureChest) {
templ Transaction(items templ.Component, filter types.TransactionItemsFilter, accounts []*account.Account, treasureChests []*types.TreasureChest) {
<div class="max-w-6xl mt-10 mx-auto">
<div class="flex items-center gap-4">
<form
@@ -95,7 +96,7 @@ templ TransactionItems(transactions []*types.Transaction, accounts, treasureChes
</div>
}
templ EditTransaction(transaction *types.Transaction, accounts []*types.Account, treasureChests []*types.TreasureChest) {
templ EditTransaction(transaction *types.Transaction, accounts []*account.Account, treasureChests []*types.TreasureChest) {
{{
var (
timestamp time.Time