feat: use sqlx
Some checks failed
Build Docker Image / Build-Docker-Image (push) Failing after 6m59s
Some checks failed
Build Docker Image / Build-Docker-Image (push) Failing after 6m59s
This commit is contained in:
@@ -4,7 +4,7 @@ import (
|
|||||||
"spend-sparrow/log"
|
"spend-sparrow/log"
|
||||||
"spend-sparrow/types"
|
"spend-sparrow/types"
|
||||||
|
|
||||||
"database/sql"
|
"github.com/jmoiron/sqlx"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@@ -18,10 +18,10 @@ type Account interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AccountSqlite struct {
|
type AccountSqlite struct {
|
||||||
db *sql.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccountSqlite(db *sql.DB) *AccountSqlite {
|
func NewAccountSqlite(db *sqlx.DB) *AccountSqlite {
|
||||||
return &AccountSqlite{db: db}
|
return &AccountSqlite{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,8 @@ func (db AccountSqlite) Update(account *types.Account) error {
|
|||||||
|
|
||||||
func (db AccountSqlite) GetAll(groupId uuid.UUID) ([]*types.Account, error) {
|
func (db AccountSqlite) GetAll(groupId uuid.UUID) ([]*types.Account, error) {
|
||||||
|
|
||||||
rows, err := db.db.Query(`
|
accounts := make([]*types.Account, 0)
|
||||||
|
err := db.db.Select(&accounts, `
|
||||||
SELECT
|
SELECT
|
||||||
id, name,
|
id, name,
|
||||||
current_balance, last_transaction, oink_balance,
|
current_balance, last_transaction, oink_balance,
|
||||||
@@ -73,23 +74,13 @@ func (db AccountSqlite) GetAll(groupId uuid.UUID) ([]*types.Account, error) {
|
|||||||
return nil, types.ErrInternal
|
return nil, types.ErrInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
var accounts = make([]*types.Account, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
|
|
||||||
account, err := scanAccount(rows)
|
|
||||||
if err != nil {
|
|
||||||
return nil, types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts = append(accounts, account)
|
|
||||||
}
|
|
||||||
|
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db AccountSqlite) Get(groupId uuid.UUID, id uuid.UUID) (*types.Account, error) {
|
func (db AccountSqlite) Get(groupId uuid.UUID, id uuid.UUID) (*types.Account, error) {
|
||||||
|
|
||||||
rows, err := db.db.Query(`
|
account := &types.Account{}
|
||||||
|
err := db.db.Get(account, `
|
||||||
SELECT
|
SELECT
|
||||||
id, name,
|
id, name,
|
||||||
current_balance, last_transaction, oink_balance,
|
current_balance, last_transaction, oink_balance,
|
||||||
@@ -102,25 +93,7 @@ func (db AccountSqlite) Get(groupId uuid.UUID, id uuid.UUID) (*types.Account, er
|
|||||||
return nil, types.ErrInternal
|
return nil, types.ErrInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rows.Next() {
|
return account, nil
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
return scanAccount(rows)
|
|
||||||
}
|
|
||||||
|
|
||||||
func scanAccount(rows *sql.Rows) (*types.Account, error) {
|
|
||||||
var (
|
|
||||||
account types.Account
|
|
||||||
)
|
|
||||||
|
|
||||||
err := rows.Scan(&account.Id, &account.Name, &account.CurrentBalance, &account.LastTransaction, &account.OinkBalance, &account.CreatedAt, &account.CreatedBy, &account.UpdatedAt, &account.UpdatedBy)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Could not scan account: %v", err)
|
|
||||||
return nil, types.ErrInternal
|
|
||||||
}
|
|
||||||
|
|
||||||
return &account, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db AccountSqlite) Delete(id uuid.UUID) error {
|
func (db AccountSqlite) Delete(id uuid.UUID) error {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth interface {
|
type Auth interface {
|
||||||
@@ -32,10 +33,10 @@ type Auth interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AuthSqlite struct {
|
type AuthSqlite struct {
|
||||||
db *sql.DB
|
db *sqlx.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthSqlite(db *sql.DB) *AuthSqlite {
|
func NewAuthSqlite(db *sqlx.DB) *AuthSqlite {
|
||||||
return &AuthSqlite{db: db}
|
return &AuthSqlite{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import (
|
|||||||
"spend-sparrow/log"
|
"spend-sparrow/log"
|
||||||
"spend-sparrow/types"
|
"spend-sparrow/types"
|
||||||
|
|
||||||
"database/sql"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/golang-migrate/migrate/v4"
|
"github.com/golang-migrate/migrate/v4"
|
||||||
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -17,8 +17,8 @@ var (
|
|||||||
ErrAlreadyExists = errors.New("row already exists")
|
ErrAlreadyExists = errors.New("row already exists")
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunMigrations(db *sql.DB, pathPrefix string) error {
|
func RunMigrations(db *sqlx.DB, pathPrefix string) error {
|
||||||
driver, err := sqlite3.WithInstance(db, &sqlite3.Config{})
|
driver, err := sqlite3.WithInstance(db.DB, &sqlite3.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Could not create Migration instance: %v", err)
|
log.Error("Could not create Migration instance: %v", err)
|
||||||
return types.ErrInternal
|
return types.ErrInternal
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -8,6 +8,7 @@ require (
|
|||||||
github.com/a-h/templ v0.3.865
|
github.com/a-h/templ v0.3.865
|
||||||
github.com/golang-migrate/migrate/v4 v4.18.3
|
github.com/golang-migrate/migrate/v4 v4.18.3
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mattn/go-sqlite3 v1.14.28
|
github.com/mattn/go-sqlite3 v1.14.28
|
||||||
github.com/prometheus/client_golang v1.22.0
|
github.com/prometheus/client_golang v1.22.0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -17,6 +17,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||||
|
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
|||||||
126
handler/account.go
Normal file
126
handler/account.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"spend-sparrow/handler/middleware"
|
||||||
|
"spend-sparrow/service"
|
||||||
|
"spend-sparrow/template/account"
|
||||||
|
"spend-sparrow/utils"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Account interface {
|
||||||
|
Handle(router *http.ServeMux)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccountImpl struct {
|
||||||
|
service service.Account
|
||||||
|
auth service.Auth
|
||||||
|
render *Render
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAccount(service service.Account, auth service.Auth, render *Render) Account {
|
||||||
|
return AccountImpl{
|
||||||
|
service: service,
|
||||||
|
auth: auth,
|
||||||
|
render: render,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler AccountImpl) Handle(router *http.ServeMux) {
|
||||||
|
router.Handle("/account", handler.handleAccountPage())
|
||||||
|
// router.Handle("POST /account", handler.handleAddAccount())
|
||||||
|
// router.Handle("GET /account", handler.handleGetAccount())
|
||||||
|
// router.Handle("DELETE /account/{id}", handler.handleDeleteAccount())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (handler AccountImpl) handleAccountPage() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := middleware.GetUser(r)
|
||||||
|
if user == nil {
|
||||||
|
utils.DoRedirect(w, r, "/auth/signin")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := account.AccountListComp(nil)
|
||||||
|
handler.render.RenderLayout(r, w, comp, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (handler AccountImpl) handleAddAccount() http.HandlerFunc {
|
||||||
|
// return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// user := middleware.GetUser(r)
|
||||||
|
// if user == nil {
|
||||||
|
// utils.DoRedirect(w, r, "/auth/signin")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var dateStr = r.FormValue("date")
|
||||||
|
// var typeStr = r.FormValue("type")
|
||||||
|
// var setsStr = r.FormValue("sets")
|
||||||
|
// var repsStr = r.FormValue("reps")
|
||||||
|
//
|
||||||
|
// wo := service.NewAccountDto("", dateStr, typeStr, setsStr, repsStr)
|
||||||
|
// wo, err := handler.service.AddAccount(user, wo)
|
||||||
|
// if err != nil {
|
||||||
|
// utils.TriggerToast(w, r, "error", "Invalid input values", http.StatusBadRequest)
|
||||||
|
// http.Error(w, "Invalid input values", http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// wor := account.Account{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps}
|
||||||
|
//
|
||||||
|
// comp := account.AccountItemComp(wor, true)
|
||||||
|
// handler.render.Render(r, w, comp)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (handler AccountImpl) handleGetAccount() http.HandlerFunc {
|
||||||
|
// return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// user := middleware.GetUser(r)
|
||||||
|
// if user == nil {
|
||||||
|
// utils.DoRedirect(w, r, "/auth/signin")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// workouts, err := handler.service.GetAccounts(user)
|
||||||
|
// if err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// wos := make([]*types.Account, 0)
|
||||||
|
// for _, wo := range workouts {
|
||||||
|
// wos = append(wos, *types.Account{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps})
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// comp := account.AccountListComp(wos)
|
||||||
|
// handler.render.Render(r, w, comp)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (handler AccountImpl) handleDeleteAccount() http.HandlerFunc {
|
||||||
|
// return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// user := middleware.GetUser(r)
|
||||||
|
// if user == nil {
|
||||||
|
// utils.DoRedirect(w, r, "/auth/signin")
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// rowId := r.PathValue("id")
|
||||||
|
// if rowId == "" {
|
||||||
|
// utils.TriggerToast(w, r, "error", "Missing ID field", http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// rowIdInt, err := strconv.Atoi(rowId)
|
||||||
|
// if err != nil {
|
||||||
|
// utils.TriggerToast(w, r, "error", "Invalid ID", http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// err = handler.service.DeleteAccount(user, rowIdInt)
|
||||||
|
// if err != nil {
|
||||||
|
// utils.TriggerToast(w, r, "error", "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
package handler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"spend-sparrow/handler/middleware"
|
|
||||||
"spend-sparrow/service"
|
|
||||||
"spend-sparrow/template/workout"
|
|
||||||
"spend-sparrow/utils"
|
|
||||||
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Workout interface {
|
|
||||||
Handle(router *http.ServeMux)
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkoutImpl struct {
|
|
||||||
service service.Workout
|
|
||||||
auth service.Auth
|
|
||||||
render *Render
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWorkout(service service.Workout, auth service.Auth, render *Render) Workout {
|
|
||||||
return WorkoutImpl{
|
|
||||||
service: service,
|
|
||||||
auth: auth,
|
|
||||||
render: render,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler WorkoutImpl) Handle(router *http.ServeMux) {
|
|
||||||
router.Handle("/workout", handler.handleWorkoutPage())
|
|
||||||
router.Handle("POST /api/workout", handler.handleAddWorkout())
|
|
||||||
router.Handle("GET /api/workout", handler.handleGetWorkout())
|
|
||||||
router.Handle("DELETE /api/workout/{id}", handler.handleDeleteWorkout())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler WorkoutImpl) handleWorkoutPage() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user := middleware.GetUser(r)
|
|
||||||
if user == nil {
|
|
||||||
utils.DoRedirect(w, r, "/auth/signin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDate := time.Now().Format("2006-01-02")
|
|
||||||
comp := workout.WorkoutComp(currentDate)
|
|
||||||
handler.render.RenderLayout(r, w, comp, user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler WorkoutImpl) handleAddWorkout() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user := middleware.GetUser(r)
|
|
||||||
if user == nil {
|
|
||||||
utils.DoRedirect(w, r, "/auth/signin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var dateStr = r.FormValue("date")
|
|
||||||
var typeStr = r.FormValue("type")
|
|
||||||
var setsStr = r.FormValue("sets")
|
|
||||||
var repsStr = r.FormValue("reps")
|
|
||||||
|
|
||||||
wo := service.NewWorkoutDto("", dateStr, typeStr, setsStr, repsStr)
|
|
||||||
wo, err := handler.service.AddWorkout(user, wo)
|
|
||||||
if err != nil {
|
|
||||||
utils.TriggerToast(w, r, "error", "Invalid input values", http.StatusBadRequest)
|
|
||||||
http.Error(w, "Invalid input values", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
wor := workout.Workout{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps}
|
|
||||||
|
|
||||||
comp := workout.WorkoutItemComp(wor, true)
|
|
||||||
handler.render.Render(r, w, comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler WorkoutImpl) handleGetWorkout() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user := middleware.GetUser(r)
|
|
||||||
if user == nil {
|
|
||||||
utils.DoRedirect(w, r, "/auth/signin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
workouts, err := handler.service.GetWorkouts(user)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wos := make([]workout.Workout, 0)
|
|
||||||
for _, wo := range workouts {
|
|
||||||
wos = append(wos, workout.Workout{Id: wo.RowId, Date: wo.Date, Type: wo.Type, Sets: wo.Sets, Reps: wo.Reps})
|
|
||||||
}
|
|
||||||
|
|
||||||
comp := workout.WorkoutListComp(wos)
|
|
||||||
handler.render.Render(r, w, comp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (handler WorkoutImpl) handleDeleteWorkout() http.HandlerFunc {
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user := middleware.GetUser(r)
|
|
||||||
if user == nil {
|
|
||||||
utils.DoRedirect(w, r, "/auth/signin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rowId := r.PathValue("id")
|
|
||||||
if rowId == "" {
|
|
||||||
utils.TriggerToast(w, r, "error", "Missing ID field", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rowIdInt, err := strconv.Atoi(rowId)
|
|
||||||
if err != nil {
|
|
||||||
utils.TriggerToast(w, r, "error", "Invalid ID", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = handler.service.DeleteWorkout(user, rowIdInt)
|
|
||||||
if err != nil {
|
|
||||||
utils.TriggerToast(w, r, "error", "Internal Server Error", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
main.go
16
main.go
@@ -9,7 +9,6 @@ import (
|
|||||||
"spend-sparrow/types"
|
"spend-sparrow/types"
|
||||||
|
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -17,6 +16,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
@@ -28,7 +28,7 @@ func main() {
|
|||||||
log.Fatal("Error loading .env file")
|
log.Fatal("Error loading .env file")
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "./data.db")
|
db, err := sqlx.Open("sqlite3", "./data.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not open Database data.db: %v", err)
|
log.Fatal("Could not open Database data.db: %v", err)
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ func main() {
|
|||||||
run(context.Background(), db, os.Getenv)
|
run(context.Background(), db, os.Getenv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(ctx context.Context, database *sql.DB, env func(string) string) {
|
func run(ctx context.Context, database *sqlx.DB, env func(string) string) {
|
||||||
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -100,26 +100,26 @@ func shutdownServer(s *http.Server, ctx context.Context, wg *sync.WaitGroup) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHandler(d *sql.DB, serverSettings *types.Settings) http.Handler {
|
func createHandler(d *sqlx.DB, serverSettings *types.Settings) http.Handler {
|
||||||
var router = http.NewServeMux()
|
var router = http.NewServeMux()
|
||||||
|
|
||||||
authDb := db.NewAuthSqlite(d)
|
authDb := db.NewAuthSqlite(d)
|
||||||
workoutDb := db.NewWorkoutDbSqlite(d)
|
accountDb := db.NewAccountSqlite(d)
|
||||||
|
|
||||||
randomService := service.NewRandomImpl()
|
randomService := service.NewRandomImpl()
|
||||||
clockService := service.NewClockImpl()
|
clockService := service.NewClockImpl()
|
||||||
mailService := service.NewMailImpl(serverSettings)
|
mailService := service.NewMailImpl(serverSettings)
|
||||||
|
|
||||||
authService := service.NewAuthImpl(authDb, randomService, clockService, mailService, serverSettings)
|
authService := service.NewAuthImpl(authDb, randomService, clockService, mailService, serverSettings)
|
||||||
workoutService := service.NewWorkoutImpl(workoutDb, randomService, clockService, mailService, serverSettings)
|
accountService := service.NewAccountImpl(accountDb, randomService, clockService, serverSettings)
|
||||||
|
|
||||||
render := handler.NewRender()
|
render := handler.NewRender()
|
||||||
indexHandler := handler.NewIndex(authService, render)
|
indexHandler := handler.NewIndex(authService, render)
|
||||||
authHandler := handler.NewAuth(authService, render)
|
authHandler := handler.NewAuth(authService, render)
|
||||||
workoutHandler := handler.NewWorkout(workoutService, authService, render)
|
accountHandler := handler.NewAccount(accountService, authService, render)
|
||||||
|
|
||||||
indexHandler.Handle(router)
|
indexHandler.Handle(router)
|
||||||
workoutHandler.Handle(router)
|
accountHandler.Handle(router)
|
||||||
authHandler.Handle(router)
|
authHandler.Handle(router)
|
||||||
|
|
||||||
// Serve static files (CSS, JS and images)
|
// Serve static files (CSS, JS and images)
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ CREATE TABLE account (
|
|||||||
created_at DATETIME NOT NULL,
|
created_at DATETIME NOT NULL,
|
||||||
created_by TEXT NOT NULL,
|
created_by TEXT NOT NULL,
|
||||||
updated_at DATETIME,
|
updated_at DATETIME,
|
||||||
updated_by TEXT,
|
updated_by TEXT
|
||||||
) WITHOUT ROWID;
|
) WITHOUT ROWID;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type AccountImpl struct {
|
|||||||
settings *types.Settings
|
settings *types.Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccountImpl(db db.Account, clock Clock, random Random, settings *types.Settings) Account {
|
func NewAccountImpl(db db.Account, random Random, clock Clock, settings *types.Settings) Account {
|
||||||
return AccountImpl{
|
return AccountImpl{
|
||||||
db: db,
|
db: db,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><path fill="currentColor" d="M165.906 18.688C15.593 59.28-42.187 198.55 92.72 245.375h-1.095c.635.086 1.274.186 1.906.28c8.985 3.077 18.83 5.733 29.532 7.94C173.36 273.35 209.74 321.22 212.69 368c-33.514 23.096-59.47 62.844-59.47 62.844l26.28 38.686L138.28 493h81.97c-40.425-40.435-11.76-85.906 36.125-85.906c48.54 0 73.945 48.112 36.156 85.906h81.126l-40.375-23.47l26.283-38.686s-26.376-40.4-60.282-63.406c3.204-46.602 39.5-94.167 89.595-113.844c10.706-2.207 20.546-4.86 29.53-7.938c.633-.095 1.273-.195 1.908-.28h-1.125c134.927-46.82 77.163-186.094-73.157-226.69c-40.722 39.37 6.54 101.683 43.626 56.877c36.9 69.08 8.603 127.587-72.28 83.406c-11.88 24.492-34.213 41.374-60.688 41.374c-26.703 0-49.168-17.167-60.97-42c-81.774 45.38-110.512-13.372-73.437-82.78c37.09 44.805 84.35-17.508 43.626-56.876zm90.79 35.92c-27.388 0-51.33 27.556-51.33 63.61c0 36.056 23.942 62.995 51.33 62.995s51.327-26.94 51.327-62.994c0-36.058-23.94-63.61-51.328-63.61z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 128 128"><path d="M93.46 39.45c6.71-1.49 15.45-8.15 16.78-11.43.78-1.92-3.11-4.92-4.15-6.13-2.38-2.76-1.42-4.12-.5-7.41 1.05-3.74-1.44-7.87-4.97-9.49s-7.75-1.11-11.3.47-6.58 4.12-9.55 6.62c-2.17-1.37-5.63-7.42-11.23-3.49-3.87 2.71-4.22 8.61-3.72 13.32 1.17 10.87 3.85 16.51 8.9 18.03 6.38 1.92 13.44.91 19.74-.49" style="fill:#ffca28"/><path d="M104.36 8.18c-.85 14.65-15.14 24.37-21.92 28.65l4.4 3.78s2.79.06 6.61-1.16c6.55-2.08 16.12-7.96 16.78-11.43.97-5.05-4.21-3.95-5.38-7.94-.61-2.11 2.97-6.1-.49-11.9M79.78 12.09s-2.55-2.61-4.44-3.8c-.94 1.77-1.61 3.69-1.94 5.67-.59 3.48 0 8.42 1.39 12.1.22.57 1.04.48 1.13-.12 1.2-7.91 3.86-13.85 3.86-13.85" style="fill:#e2a610"/><path d="M61.96 38.16S30.77 41.53 16.7 68.61s-2.11 43.5 10.55 49.48 44.56 8.09 65.31 3.17 25.94-15.12 24.97-24.97c-1.41-14.38-14.77-23.22-14.77-23.22s.53-17.76-13.25-29.29c-12.23-10.24-27.55-5.62-27.55-5.62" style="fill:#ffca28"/><path d="M74.76 83.73c-6.69-8.44-14.59-9.57-17.12-12.6-1.38-1.65-2.19-3.32-1.88-5.39.33-2.2 2.88-3.72 4.86-4.09 2.31-.44 7.82-.21 12.45 4.2 1.1 1.04.7 2.66.67 4.11-.08 3.11 4.37 6.13 7.97 3.53 3.61-2.61.84-8.42-1.49-11.24-1.76-2.13-8.14-6.82-16.07-7.56-2.23-.21-11.2-1.54-16.38 8.31-1.49 2.83-2.04 9.67 5.76 15.45 1.63 1.21 10.09 5.51 12.44 8.3 4.07 4.83 1.28 9.08-1.9 9.64-8.67 1.52-13.58-3.17-14.49-5.74-.65-1.83.03-3.81-.81-5.53-.86-1.77-2.62-2.47-4.48-1.88-6.1 1.94-4.16 8.61-1.46 12.28 2.89 3.93 6.44 6.3 10.43 7.6 14.89 4.85 22.05-2.81 23.3-8.42.92-4.11.82-7.67-1.8-10.97" style="fill:#6b4b46"/><path d="M71.16 48.99c-12.67 27.06-14.85 61.23-14.85 61.23" style="fill:none;stroke:#6b4b46;stroke-width:5;stroke-miterlimit:10"/><path d="M81.67 31.96c8.44 2.75 10.31 10.38 9.7 12.46-.73 2.44-10.08-7.06-23.98-6.49-4.86.2-3.45-2.78-1.2-4.5 2.97-2.27 7.96-3.91 15.48-1.47" style="fill:#6d4c41"/><path d="M81.67 31.96c8.44 2.75 10.31 10.38 9.7 12.46-.73 2.44-10.08-7.06-23.98-6.49-4.86.2-3.45-2.78-1.2-4.5 2.97-2.27 7.96-3.91 15.48-1.47" style="fill:#6b4b46"/><path d="M96.49 58.86c1.06-.73 4.62.53 5.62 7.5.49 3.41.64 6.71.64 6.71s-4.2-3.77-5.59-6.42c-1.75-3.35-2.43-6.59-.67-7.79" style="fill:#e2a610"/></svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 2.1 KiB |
1
template/account/default.go
Normal file
1
template/account/default.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package account
|
||||||
7
template/account/workout.templ
Normal file
7
template/account/workout.templ
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package account
|
||||||
|
|
||||||
|
import "spend-sparrow/types"
|
||||||
|
|
||||||
|
templ AccountListComp(accounts []*types.Account) {
|
||||||
|
<main class="mx-2"></main>
|
||||||
|
}
|
||||||
@@ -3,14 +3,7 @@ package template
|
|||||||
templ Index() {
|
templ Index() {
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="max-w-md">
|
<h1 class="text-5xl font-bold">Spend Sparrow - Your personal finance</h1>
|
||||||
<h1 class="text-5xl font-bold">Next Level Workout Tracker</h1>
|
|
||||||
<p class="py-6">
|
|
||||||
Ever wanted to track your workouts and see your progress over time? spend-sparrow is the perfect
|
|
||||||
solution for you.
|
|
||||||
</p>
|
|
||||||
<a href="/workout" class="">Get Started</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ templ Layout(slot templ.Component, user templ.Component) {
|
|||||||
<div class="h-screen flex flex-col">
|
<div class="h-screen flex flex-col">
|
||||||
<div class="flex justify-end items-center gap-2 py-1 px-2 h-12 md:gap-10 md:px-10 md:py-2 shadow-sm">
|
<div class="flex justify-end items-center gap-2 py-1 px-2 h-12 md:gap-10 md:px-10 md:py-2 shadow-sm">
|
||||||
<a href="/" class="flex-1 flex gap-2">
|
<a href="/" class="flex-1 flex gap-2">
|
||||||
<img src="/static/favicon.svg" alt="spend-sparrow logo"/>
|
<img class="w-6" src="/static/favicon.svg" alt="Spend Sparrow logo"/>
|
||||||
<span>spend-sparrow</span>
|
<span class="text-xl font-bold">SpendSparrow</span>
|
||||||
</a>
|
</a>
|
||||||
@user
|
@user
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user