diff --git a/Makefile b/Makefile index 9309a29..8506ea7 100644 --- a/Makefile +++ b/Makefile @@ -84,3 +84,4 @@ watch/sync_assets: .PHONY: watch watch: make -j4 watch/templ watch/server watch/tailwind watch/sync_assets + diff --git a/cmd/main.go b/cmd/main.go index 0e84fbc..e569949 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -3,6 +3,7 @@ package main import ( "database/sql" "fmt" + "log" "net/http" "os" @@ -19,8 +20,7 @@ func main() { db, err := sql.Open("libsql", os.Getenv("DB_URL")) if err != nil { - fmt.Fprintf(os.Stderr, "failed to open db: %s", err) - os.Exit(1) + log.Fatalf("failed to open db: %s", err.Error()) } defer db.Close() diff --git a/database/employee_repository.go b/database/employee_repository.go index 1439b27..c0a8db3 100644 --- a/database/employee_repository.go +++ b/database/employee_repository.go @@ -3,6 +3,7 @@ package database import ( "context" "database/sql" + "time" "github.com/henriquepw/imperium-tattoo/web" "github.com/henriquepw/imperium-tattoo/web/types" @@ -11,7 +12,7 @@ import ( type EmployeeRepo interface { Insert(ctx context.Context, payload types.EmployeeCreateDTO) (*string, error) List(ctx context.Context) ([]types.Employee, error) - CheckEmail(ctx context.Context, email string) bool + HasEmail(ctx context.Context, email string) bool } type repo struct { @@ -34,13 +35,16 @@ func (r repo) Insert(ctx context.Context, payload types.EmployeeCreateDTO) (*str } defer tx.Rollback() + now := time.Now().UnixMilli() _, err = tx.QueryContext( ctx, - "INSERT INTO employee (id, name, email, roles) VALUES ($1, $2, $3, $4)", + "INSERT INTO employee (id, name, email, roles, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", id, payload.Name, payload.Email, payload.Roles, + now, + now, ) if err != nil { return nil, err @@ -48,14 +52,13 @@ func (r repo) Insert(ctx context.Context, payload types.EmployeeCreateDTO) (*str _, err = tx.QueryContext( ctx, - "INSERT INTO credential (id, secret) VALUES ($1, $2)", + "INSERT INTO credential (id, secret) VALUES (?, ?)", payload.Email, payload.Password, ) if err != nil { return nil, err } - if err = tx.Commit(); err != nil { return nil, err } @@ -83,7 +86,11 @@ func (r repo) List(ctx context.Context) ([]types.Employee, error) { return items, nil } -func (r repo) CheckEmail(ctx context.Context, email string) bool { - row := r.db.QueryRowContext(ctx, "SELECT COUNT(1) FROM employee WHERE email = ?", email) - return row.Err() == nil +func (r repo) HasEmail(ctx context.Context, email string) bool { + rows, err := r.db.QueryContext(ctx, "SELECT id FROM employee WHERE email = ?", email) + if err != nil { + return false + } + + return rows.Next() } diff --git a/go.mod b/go.mod index 7bca2b7..1ec7431 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/henriquepw/imperium-tattoo go 1.23.1 require ( - github.com/a-h/templ v0.2.747 + github.com/a-h/templ v0.2.771 github.com/go-playground/validator/v10 v10.22.0 github.com/joho/godotenv v1.5.1 github.com/matoous/go-nanoid/v2 v2.1.0 github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.26.0 ) require ( @@ -19,7 +19,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect ) diff --git a/go.sum b/go.sum index f706bb5..a8e2b92 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= -github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= +github.com/a-h/templ v0.2.771 h1:4KH5ykNigYGGpCe0fRJ7/hzwz72k3qFqIiiLLJskbSo= +github.com/a-h/templ v0.2.771/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= @@ -30,15 +30,15 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535 h1:iLjJLq2A5J6L9zrhyNn+fpmxFvtEpYB4XLMr0rX3epI= github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/web/handler/employee.go b/web/handler/employee.go index 304d7ce..5ee8046 100644 --- a/web/handler/employee.go +++ b/web/handler/employee.go @@ -46,5 +46,5 @@ func (h EmployeeHandler) EmployeeCreateAction(w http.ResponseWriter, r *http.Req return } - http.Redirect(w, r, "/employees", http.StatusSeeOther) + web.Redirect(w, "/employees") } diff --git a/web/renders.go b/web/renders.go index ae186fc..f238f69 100644 --- a/web/renders.go +++ b/web/renders.go @@ -1,6 +1,7 @@ package web import ( + "log/slog" "net/http" "github.com/a-h/templ" @@ -19,6 +20,7 @@ func Render(w http.ResponseWriter, r *http.Request, statusCode int, t templ.Comp } func RenderError(w http.ResponseWriter, r *http.Request, err error, t func(e ServerError) templ.Component) error { + slog.Error("render error", "error", err.Error()) if e, ok := err.(ServerError); ok { if e.Errors != nil { return Render(w, r, e.StatusCode, t(e)) @@ -35,3 +37,7 @@ func RenderError(w http.ResponseWriter, r *http.Request, err error, t func(e Ser w.Write([]byte("Houve um erro inesperado")) return nil } + +func Redirect(w http.ResponseWriter, to string) { + w.Header().Add("HX-Location", to) +} diff --git a/web/service/employee.go b/web/service/employee.go index e8fdc9b..0d73b83 100644 --- a/web/service/employee.go +++ b/web/service/employee.go @@ -25,7 +25,7 @@ func (s *EmployeeSvc) CreateEmployee(ctx context.Context, payload types.Employee return nil, err } - if s.repo.CheckEmail(ctx, payload.Email) { + if s.repo.HasEmail(ctx, payload.Email) { return nil, web.InvalidRequestDataError(map[string]string{"email": "Email já cadastrado"}) } diff --git a/web/view/layout/dashboard.templ b/web/view/layout/dashboard.templ index aade234..0162066 100644 --- a/web/view/layout/dashboard.templ +++ b/web/view/layout/dashboard.templ @@ -17,17 +17,17 @@ templ Dashbaord(title, route string, boosted bool) { { children... } }