diff --git a/.env.example b/.env.example index e7a0bf9..908fcab 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,3 @@ AUTO_MIGRATE=true # Mandatory DATABASE_URL=db.sqlite -JWT_SECRET=secrettt diff --git a/go.mod b/go.mod index baa7536..3939d7e 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,9 @@ toolchain go1.24.1 require ( github.com/a-h/templ v0.3.857 - github.com/gofiber/fiber/v2 v2.49.0 - github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/gofiber/fiber/v2 v2.52.6 github.com/golang-migrate/migrate/v4 v4.16.2 - github.com/google/uuid v1.3.1 + github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/spf13/cobra v1.7.0 golang.org/x/crypto v0.32.0 @@ -24,17 +23,17 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.48.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/mod v0.20.0 // indirect diff --git a/go.sum b/go.sum index 223bff2..4b7a798 100644 --- a/go.sum +++ b/go.sum @@ -7,18 +7,16 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/gofiber/fiber/v2 v2.49.0 h1:xBVG2c66GDcWfww56xHvMn52Q0XX7UrSvjj6MD8/5EE= -github.com/gofiber/fiber/v2 v2.49.0/go.mod h1:oxpt7wQaEYgdDmq7nMxCGhilYicBLFnZ+jQSJcQDlSE= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= +github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA= github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -30,8 +28,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -39,8 +37,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= @@ -59,8 +57,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc= -github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= diff --git a/internal/components/design.templ b/internal/components/design.templ index c4bf0b0..84389b7 100644 --- a/internal/components/design.templ +++ b/internal/components/design.templ @@ -55,13 +55,13 @@ templ ErrorPage() { } } -templ authenticatedPage(title string, breadcrumb, description string) { +templ headedPage(title string, breadcrumb, description string) { @rawPage(title) {
if description != "" {

{description}

@@ -74,79 +74,10 @@ templ authenticatedPage(title string, breadcrumb, description string) { } } -templ PostLogoutPage() { - @rawPage("Logout") { -
-
-
- Home -
- Login - Register -
-
-

You need to login to proceed

-
-
- } -} - -templ LoginPage() { - @rawPage("Login") { -
-
-
- Login -
- Register -
-
-
-
- - -
-
- - -
- -
-
-
- } -} - -templ RegisterPage() { - @rawPage("Register") { -
-
-
- Register -
- Login -
-
-
-
- - -
-
- - -
- -
-
-
- } -} - // Tasks Views templ TaskListPage(tasks []models.Task) { - @authenticatedPage("Tasks List", "Tasks", "Open Tasks To Do") { + @headedPage("Tasks List", "Tasks", "Open Tasks To Do") {
    if len(tasks) > 0 { @@ -169,7 +100,7 @@ templ TaskListPage(tasks []models.Task) { } templ TaskEditPage(task models.Task) { - @authenticatedPage("Task Editor", fmt.Sprintf("Tasks > %s", task.Title), "Edit this task") { + @headedPage("Task Editor", fmt.Sprintf("Tasks > %s", task.Title), "Edit this task") {
    diff --git a/internal/config/config.go b/internal/config/config.go index 0113ef7..2ecd3af 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,7 +11,6 @@ type ServerConfigs struct { ServerAddress string DataBaseURL string - JwtSecret string } func GetServerConfigs() ServerConfigs { @@ -24,11 +23,6 @@ func GetServerConfigs() ServerConfigs { panic("Missing DATABASE_URL") } - config.JwtSecret = os.Getenv("JWT_SECRET") - if config.JwtSecret == "" { - panic("Missing JWT_SECRET") - } - // optional - with defaults envAutoMigrate := os.Getenv("AUTO_MIGRATE") diff --git a/internal/controllers/controllers.go b/internal/controllers/controllers.go deleted file mode 100644 index 152828d..0000000 --- a/internal/controllers/controllers.go +++ /dev/null @@ -1,5 +0,0 @@ -package controllers - -type Controllers interface { - UserController | TaskController -} diff --git a/internal/controllers/tasks.go b/internal/controllers/tasks.go index edfebc9..7e86286 100644 --- a/internal/controllers/tasks.go +++ b/internal/controllers/tasks.go @@ -18,21 +18,20 @@ func NewTaskController(dbRepo database.Database) *TaskController { } } -func (tc *TaskController) CreateTask(ownerId string) (models.Task, error) { +func (tc *TaskController) CreateTask() (models.Task, error) { task := models.Task{ Id: models.GenerateId(), Title: "New Task", Description: "New Task Description", - OwnerId: ownerId, } return task, tc.DbRepo.CreateTask(task) } -func (tc *TaskController) ListTasks(ownerId string) ([]models.Task, error) { - return tc.DbRepo.ListTasksByOwner(ownerId) +func (tc *TaskController) ListTasks() ([]models.Task, error) { + return tc.DbRepo.ListTasks() } -func (tc *TaskController) RetrieveTask(ownerId, taskId string) (models.Task, error) { +func (tc *TaskController) RetrieveTask(taskId string) (models.Task, error) { task, err := tc.DbRepo.RetrieveTaskById(taskId) if err != nil { if err == sql.ErrNoRows { @@ -42,15 +41,11 @@ func (tc *TaskController) RetrieveTask(ownerId, taskId string) (models.Task, err return models.EmptyTask, err } - if task.OwnerId != ownerId { - return models.EmptyTask, fiber.ErrNotFound - } - return task, nil } -func (tc *TaskController) DeleteTask(ownerId, taskId string) error { - _, err := tc.RetrieveTask(ownerId, taskId) +func (tc *TaskController) DeleteTask(taskId string) error { + _, err := tc.RetrieveTask(taskId) if err != nil { return err } @@ -63,8 +58,8 @@ type TaskChange struct { Description string `json:"description"` } -func (tc *TaskController) UpdateTask(ownerId, taskId string, changes TaskChange) error { - task, err := tc.RetrieveTask(ownerId, taskId) +func (tc *TaskController) UpdateTask(taskId string, changes TaskChange) error { + task, err := tc.RetrieveTask(taskId) if err != nil { return err } diff --git a/internal/controllers/users.go b/internal/controllers/users.go deleted file mode 100644 index 22a12e0..0000000 --- a/internal/controllers/users.go +++ /dev/null @@ -1,89 +0,0 @@ -package controllers - -import ( - "errors" - "goth/internal/models" - "goth/internal/repositories/database" - "goth/internal/repositories/jwt" - "goth/internal/utils" - "time" - - "github.com/gofiber/fiber/v2" -) - -type UserController struct { - dbRepo database.Database - jwtRepo jwt.JWT -} - -func NewUserController(dbRepo database.Database, jwtRepo jwt.JWT) *UserController { - return &UserController{dbRepo, jwtRepo} -} - -func (uc *UserController) VerifyJWTCookie(token string) (models.User, error) { - id, err := uc.jwtRepo.ParseJWT(token) - if err != nil { - return models.EmptyUser, err - } - - return uc.dbRepo.RetrieveUserById(id) -} - -type UserRequest struct { - Username string `json:"username"` - Password string `json:"password"` -} - -func (uc *UserController) Login(req UserRequest, cookieName string) (*fiber.Cookie, error) { - user, err := uc.dbRepo.RetrieveUserByName(req.Username) - if err != nil { - return nil, err - } - - // slow checking -> design feature of bcrypt - if !utils.CheckPasswordHash(req.Password, user.PswdHash) { - return nil, errors.New("bad username or password") - } - - expiration := time.Now().Add(7 * 24 * time.Hour) - jwt, err := uc.jwtRepo.GenerateJWT(user.ID, expiration) - if err != nil { - return nil, err - } - - return &fiber.Cookie{ - Expires: expiration, - Name: cookieName, - Value: jwt, - }, nil -} - -func (uc *UserController) Register(req UserRequest, cookieName string) (*fiber.Cookie, error) { - hashedPassword, err := utils.HashPassword(req.Password) - if err != nil { - return nil, err - } - - user := models.User{ - ID: models.GenerateId(), - Username: req.Username, - PswdHash: hashedPassword, - } - - err = uc.dbRepo.InsertUser(user) - if err != nil { - return nil, err - } - - expiration := time.Now().Add(7 * 24 * time.Hour) - jwt, err := uc.jwtRepo.GenerateJWT(user.ID, expiration) - if err != nil { - return nil, err - } - - return &fiber.Cookie{ - Expires: expiration, - Name: cookieName, - Value: jwt, - }, nil -} diff --git a/internal/embeded/migrations/01_initial.down.sql b/internal/embeded/migrations/01_initial.down.sql deleted file mode 100644 index cc1f647..0000000 --- a/internal/embeded/migrations/01_initial.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE users; diff --git a/internal/embeded/migrations/01_initial.up.sql b/internal/embeded/migrations/01_initial.up.sql deleted file mode 100644 index 7ce5571..0000000 --- a/internal/embeded/migrations/01_initial.up.sql +++ /dev/null @@ -1,8 +0,0 @@ --- SQLITE - -CREATE TABLE users ( - id UUID PRIMARY KEY, - pswd_hash TEXT NOT NULL, - username TEXT NOT NULL UNIQUE, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file diff --git a/internal/embeded/migrations/02_tasks.down.sql b/internal/embeded/migrations/01_tasks.down.sql similarity index 100% rename from internal/embeded/migrations/02_tasks.down.sql rename to internal/embeded/migrations/01_tasks.down.sql diff --git a/internal/embeded/migrations/01_tasks.up.sql b/internal/embeded/migrations/01_tasks.up.sql new file mode 100644 index 0000000..edd8b4c --- /dev/null +++ b/internal/embeded/migrations/01_tasks.up.sql @@ -0,0 +1,8 @@ +-- SQLITE + +CREATE TABLE tasks ( + id UUID PRIMARY KEY, + title TEXT NOT NULL, + description TEXT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/internal/embeded/migrations/02_tasks.up.sql b/internal/embeded/migrations/02_tasks.up.sql deleted file mode 100644 index dd98c06..0000000 --- a/internal/embeded/migrations/02_tasks.up.sql +++ /dev/null @@ -1,10 +0,0 @@ --- SQLITE - -CREATE TABLE tasks ( - id UUID PRIMARY KEY, - title TEXT NOT NULL, - owner_id UUID NOT NULL, - description TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (owner_id) REFERENCES users (id) -); diff --git a/internal/models/tasks.go b/internal/models/tasks.go index 758b6ef..0f59fc9 100644 --- a/internal/models/tasks.go +++ b/internal/models/tasks.go @@ -4,7 +4,6 @@ type Task struct { Id string `json:"id"` Title string `json:"title"` Description string `json:"description"` - OwnerId string `json:"owner_id"` } var EmptyTask = Task{} diff --git a/internal/models/users.go b/internal/models/users.go deleted file mode 100644 index c885856..0000000 --- a/internal/models/users.go +++ /dev/null @@ -1,15 +0,0 @@ -package models - -import "strings" - -type User struct { - ID string `json:"id"` - Username string `json:"username"` - PswdHash string `json:"pswd_hash"` -} - -var EmptyUser = User{} - -func GenerateNameFromEmail(email string) string { - return strings.Split(email, "@")[0] -} diff --git a/internal/repositories/database/database.go b/internal/repositories/database/database.go index 702cb2f..e4659ec 100644 --- a/internal/repositories/database/database.go +++ b/internal/repositories/database/database.go @@ -26,14 +26,14 @@ func (db *database) Close() error { } func (db *database) CreateTask(task models.Task) error { - query := `INSERT INTO tasks (id, title, description, owner_id) VALUES (?, ?, ?, ?)` - _, err := db.conn.Exec(query, task.Id, task.Title, task.Description, task.OwnerId) + query := `INSERT INTO tasks (id, title, description) VALUES (?, ?, ?)` + _, err := db.conn.Exec(query, task.Id, task.Title, task.Description) return err } -func (db *database) ListTasksByOwner(ownerId string) ([]models.Task, error) { - query := `SELECT id, title, owner_id, description FROM tasks WHERE owner_id = ?` - rows, err := db.conn.Query(query, ownerId) +func (db *database) ListTasks() ([]models.Task, error) { + query := `SELECT id, title, description FROM tasks` + rows, err := db.conn.Query(query) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func (db *database) ListTasksByOwner(ownerId string) ([]models.Task, error) { var tasks []models.Task for rows.Next() { var task models.Task - err := rows.Scan(&task.Id, &task.Title, &task.OwnerId, &task.Description) + err := rows.Scan(&task.Id, &task.Title, &task.Description) if err != nil { return nil, err } @@ -53,11 +53,11 @@ func (db *database) ListTasksByOwner(ownerId string) ([]models.Task, error) { } func (db *database) RetrieveTaskById(taskId string) (models.Task, error) { - query := `SELECT id, title, owner_id, description FROM tasks WHERE id = ?` + query := `SELECT id, title, description FROM tasks WHERE id = ?` row := db.conn.QueryRow(query, taskId) var task models.Task - err := row.Scan(&task.Id, &task.Title, &task.OwnerId, &task.Description) + err := row.Scan(&task.Id, &task.Title, &task.Description) if err != nil { return models.EmptyTask, err } @@ -76,35 +76,3 @@ func (db *database) UpdateTask(task models.Task) error { _, err := db.conn.Exec(query, task.Title, task.Description, task.Id) return err } - -func (db *database) InsertUser(user models.User) error { - query := `INSERT INTO users (id, username, pswd_hash) VALUES (?, ?, ?)` - _, err := db.conn.Exec(query, user.ID, user.Username, user.PswdHash) - return err -} - -func (db *database) RetrieveUserByName(username string) (models.User, error) { - query := `SELECT id, username, pswd_hash FROM users WHERE username = ?` - row := db.conn.QueryRow(query, username) - - var user models.User - err := row.Scan(&user.ID, &user.Username, &user.PswdHash) - if err != nil { - return models.EmptyUser, err - } - - return user, nil -} - -func (db *database) RetrieveUserById(id string) (models.User, error) { - query := `SELECT id, username, pswd_hash FROM users WHERE id = ?` - row := db.conn.QueryRow(query, id) - - var user models.User - err := row.Scan(&user.ID, &user.Username, &user.PswdHash) - if err != nil { - return models.EmptyUser, err - } - - return user, nil -} diff --git a/internal/repositories/database/fake.go b/internal/repositories/database/fake.go index b703246..ce4b1a5 100644 --- a/internal/repositories/database/fake.go +++ b/internal/repositories/database/fake.go @@ -2,7 +2,6 @@ package database import ( "database/sql" - "fmt" "goth/internal/models" _ "modernc.org/sqlite" @@ -10,13 +9,11 @@ import ( type fakeDatabase struct { tasks map[string]models.Task - users map[string]models.User } func NewFakeDatabaseRepo() (Database, error) { return &fakeDatabase{ tasks: make(map[string]models.Task), - users: make(map[string]models.User), }, nil } @@ -29,13 +26,11 @@ func (db *fakeDatabase) CreateTask(task models.Task) error { return nil } -func (db *fakeDatabase) ListTasksByOwner(ownerId string) ([]models.Task, error) { +func (db *fakeDatabase) ListTasks() ([]models.Task, error) { tasks := make([]models.Task, 0) for _, task := range db.tasks { - if task.OwnerId == ownerId { - tasks = append(tasks, task) - } + tasks = append(tasks, task) } return tasks, nil @@ -59,32 +54,3 @@ func (db *fakeDatabase) UpdateTask(task models.Task) error { db.tasks[task.Id] = task return nil } - -func (db *fakeDatabase) InsertUser(user models.User) error { - _, ok := db.users[user.ID] - if ok { - return fmt.Errorf("non unique email") - } - - db.users[user.ID] = user - return nil -} - -func (db *fakeDatabase) RetrieveUserById(id string) (models.User, error) { - user, ok := db.users[id] - if !ok { - return models.EmptyUser, sql.ErrNoRows - } - - return user, nil -} - -func (db *fakeDatabase) RetrieveUserByName(username string) (models.User, error) { - for _, u := range db.users { - if u.Username == username { - return u, nil - } - } - - return models.EmptyUser, sql.ErrNoRows -} diff --git a/internal/repositories/database/interface.go b/internal/repositories/database/interface.go index 459501b..9d540a2 100644 --- a/internal/repositories/database/interface.go +++ b/internal/repositories/database/interface.go @@ -13,9 +13,5 @@ type Database interface { RetrieveTaskById(taskId string) (models.Task, error) UpdateTask(task models.Task) error DeleteTask(taskId string) error - ListTasksByOwner(ownerId string) ([]models.Task, error) - - InsertUser(user models.User) error - RetrieveUserById(id string) (models.User, error) - RetrieveUserByName(username string) (models.User, error) + ListTasks() ([]models.Task, error) } diff --git a/internal/repositories/jwt/interface.go b/internal/repositories/jwt/interface.go deleted file mode 100644 index 723ca49..0000000 --- a/internal/repositories/jwt/interface.go +++ /dev/null @@ -1,15 +0,0 @@ -package jwt - -import ( - "time" -) - -// type JWTClaims struct { -// Id string `json:"id"` -// gojwt.RegisteredClaims -// } - -type JWT interface { - GenerateJWT(sub string, expiration time.Time) (string, error) - ParseJWT(token string) (string, error) -} diff --git a/internal/repositories/jwt/jwt.go b/internal/repositories/jwt/jwt.go deleted file mode 100644 index fbd1582..0000000 --- a/internal/repositories/jwt/jwt.go +++ /dev/null @@ -1,47 +0,0 @@ -package jwt - -import ( - "goth/internal/config" - "time" - - gojwt "github.com/golang-jwt/jwt/v5" -) - -type jwt struct { - bsecret []byte -} - -func NewJWTRepo(cfg config.ServerConfigs) JWT { - return &jwt{ - bsecret: []byte(cfg.JwtSecret), - } -} - -func (j *jwt) GenerateJWT(sub string, expiration time.Time) (string, error) { - claims := &gojwt.RegisteredClaims{ - Subject: sub, - IssuedAt: gojwt.NewNumericDate(time.Now()), - ExpiresAt: gojwt.NewNumericDate(expiration), - } - - jwtoken := gojwt.NewWithClaims(gojwt.SigningMethodHS256, claims) - return jwtoken.SignedString(j.bsecret) -} - -func (j *jwt) ParseJWT(token string) (string, error) { - claims := gojwt.RegisteredClaims{} - - _, err := gojwt.ParseWithClaims(token, &claims, func(token *gojwt.Token) (interface{}, error) { - return j.bsecret, nil - }) - - if err != nil { - return "", err - } - - if claims.Subject == "" { - return "", gojwt.ErrInvalidKey - } - - return claims.Subject, nil -} diff --git a/internal/routes/bind.go b/internal/routes/bind.go new file mode 100644 index 0000000..2e421e5 --- /dev/null +++ b/internal/routes/bind.go @@ -0,0 +1,15 @@ +package routes + +import ( + "goth/internal/controllers" + + "github.com/gofiber/fiber/v2" +) + +const cookieName = "goth:jwt" + +func controllerBind(controller *controllers.TaskController, handler func(*controllers.TaskController, *fiber.Ctx) error) fiber.Handler { + return func(ctx *fiber.Ctx) error { + return handler(controller, ctx) + } +} diff --git a/internal/routes/init.go b/internal/routes/init.go index cf794a5..f6ced97 100644 --- a/internal/routes/init.go +++ b/internal/routes/init.go @@ -5,7 +5,6 @@ import ( "goth/internal/config" "goth/internal/controllers" "goth/internal/repositories/database" - "goth/internal/repositories/jwt" "github.com/gofiber/fiber/v2" ) @@ -15,28 +14,17 @@ func healthzHandler(c *fiber.Ctx) error { } func Init(app *fiber.App, cfg config.ServerConfigs) error { - jwtRepo := jwt.NewJWTRepo(cfg) - dbRepo, err := database.NewDatabaseRepo(cfg) if err != nil { return fmt.Errorf("[Init] failed to get database: %w", err) } - uc := controllers.NewUserController(dbRepo, jwtRepo) tc := controllers.NewTaskController(dbRepo) - app.Get("/auth/login", getLoginHandler) - app.Post("/auth/login", postLoginHandler(uc)) - - app.Get("/auth/register", getRegisterHandler) - app.Post("/auth/register", postRegisterHandler(uc)) - - app.Get("/auth/logout", getLogoutHandler) - - app.Get("/", withAuth(uc, tc, taskList)) - app.Get("/new", withAuth(uc, tc, taskNew)) - app.Get("/edit/:id", withAuth(uc, tc, taskEdit)) - app.Post("/edit/:id", withAuth(uc, tc, taskSave)) + app.Get("/", controllerBind(tc, taskList)) + app.Get("/new", controllerBind(tc, taskNew)) + app.Get("/edit/:id", controllerBind(tc, taskEdit)) + app.Post("/edit/:id", controllerBind(tc, taskSave)) app.Use("/statics", staticsHandler) app.Use("/healthz", healthzHandler) diff --git a/internal/routes/tasks.go b/internal/routes/tasks.go index 02f33d5..f6ce150 100644 --- a/internal/routes/tasks.go +++ b/internal/routes/tasks.go @@ -3,13 +3,12 @@ package routes import ( "goth/internal/components" "goth/internal/controllers" - "goth/internal/models" "github.com/gofiber/fiber/v2" ) -func taskList(tc *controllers.TaskController, c *fiber.Ctx, user models.User) error { - tasks, err := tc.ListTasks(user.ID) +func taskList(tc *controllers.TaskController, c *fiber.Ctx) error { + tasks, err := tc.ListTasks() if err != nil { return err } @@ -17,8 +16,8 @@ func taskList(tc *controllers.TaskController, c *fiber.Ctx, user models.User) er return sendPage(c, components.TaskListPage(tasks)) } -func taskNew(tc *controllers.TaskController, c *fiber.Ctx, user models.User) error { - task, err := tc.CreateTask(user.ID) +func taskNew(tc *controllers.TaskController, c *fiber.Ctx) error { + task, err := tc.CreateTask() if err != nil { return err } @@ -26,9 +25,9 @@ func taskNew(tc *controllers.TaskController, c *fiber.Ctx, user models.User) err return c.Redirect("/edit/" + task.Id) } -func taskEdit(tc *controllers.TaskController, c *fiber.Ctx, user models.User) error { +func taskEdit(tc *controllers.TaskController, c *fiber.Ctx) error { taskId := c.Params("id") - task, err := tc.RetrieveTask(user.ID, taskId) + task, err := tc.RetrieveTask(taskId) if err != nil { return err @@ -37,7 +36,7 @@ func taskEdit(tc *controllers.TaskController, c *fiber.Ctx, user models.User) er return sendPage(c, components.TaskEditPage(task)) } -func taskSave(tc *controllers.TaskController, c *fiber.Ctx, user models.User) error { +func taskSave(tc *controllers.TaskController, c *fiber.Ctx) error { var taskId = c.Params("id") var taskChange controllers.TaskChange err := c.BodyParser(&taskChange) @@ -45,7 +44,7 @@ func taskSave(tc *controllers.TaskController, c *fiber.Ctx, user models.User) er return err } - if err := tc.UpdateTask(user.ID, taskId, taskChange); err != nil { + if err := tc.UpdateTask(taskId, taskChange); err != nil { return err } diff --git a/internal/routes/users.go b/internal/routes/users.go deleted file mode 100644 index aa30857..0000000 --- a/internal/routes/users.go +++ /dev/null @@ -1,81 +0,0 @@ -package routes - -import ( - "goth/internal/components" - "goth/internal/controllers" - "goth/internal/models" - "goth/internal/utils" - - "github.com/gofiber/fiber/v2" -) - -const cookieName = "goth:jwt" - -func withAuth[C controllers.Controllers](uController *controllers.UserController, controller *C, handler func(*C, *fiber.Ctx, models.User) error) fiber.Handler { - return func(ctx *fiber.Ctx) error { - jwt := ctx.Cookies(cookieName) - if jwt == "" { - return ctx.Redirect("/auth/login") - } - - user, err := uController.VerifyJWTCookie(jwt) - if err != nil { - return ctx.Redirect("/auth/login") - } - - return handler(controller, ctx, user) - } -} - -func getLogoutHandler(c *fiber.Ctx) error { - utils.ClearCookie(c, cookieName) - return sendPage(c, components.PostLogoutPage()) -} - -func getRegisterHandler(c *fiber.Ctx) error { - utils.ClearCookie(c, cookieName) - return sendPage(c, components.RegisterPage()) -} - -func getLoginHandler(c *fiber.Ctx) error { - utils.ClearCookie(c, cookieName) - return sendPage(c, components.LoginPage()) -} - -func postLoginHandler(uc *controllers.UserController) fiber.Handler { - return func(c *fiber.Ctx) error { - var req controllers.UserRequest - err := c.BodyParser(&req) - if err != nil { - return err - } - - cookie, err := uc.Login(req, cookieName) - if err != nil { - return err - } - - c.Cookie(cookie) - c.Set("HX-Redirect", "/") - return c.SendStatus(fiber.StatusOK) - } -} - -func postRegisterHandler(uc *controllers.UserController) fiber.Handler { - return func(c *fiber.Ctx) error { - var req controllers.UserRequest - err := c.BodyParser(&req) - if err != nil { - return err - } - - cookie, err := uc.Register(req, cookieName) - if err != nil { - return err - } - - c.Cookie(cookie) - c.Set("HX-Redirect", "/") - return c.SendStatus(fiber.StatusOK) - } -}