Splitting backend endpoints into smaller bits

This commit is contained in:
Imbus 2024-03-17 16:55:40 +01:00
parent 670ed46d51
commit d6fc0594a9
4 changed files with 248 additions and 226 deletions

View file

@ -1,13 +1,9 @@
package handlers
import (
"strconv"
"time"
"ttime/internal/database"
"ttime/internal/types"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
)
// The actual interface that we will use
@ -51,50 +47,6 @@ type GState struct {
ButtonCount int
}
// Register is a simple handler that registers a new user
//
// @Summary Register a new user
// @Description Register a new user
// @Tags User
// @Accept json
// @Produce json
// @Success 200 {string} string "User added"
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /api/register [post]
func (gs *GState) Register(c *fiber.Ctx) error {
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
if err := gs.Db.AddUser(u.Username, u.Password); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("User added")
}
// This path should obviously be protected in the future
// UserDelete deletes a user from the database
func (gs *GState) UserDelete(c *fiber.Ctx) error {
// Read from path parameters
username := c.Params("username")
// Read username from Locals
auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string)
if username != auth_username {
return c.Status(403).SendString("You can only delete yourself")
}
if err := gs.Db.RemoveUser(username); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("User deleted")
}
func (gs *GState) GetButtonCount(c *fiber.Ctx) error {
return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount})
}
@ -103,181 +55,3 @@ func (gs *GState) IncrementButtonCount(c *fiber.Ctx) error {
gs.ButtonCount++
return c.Status(200).JSON(fiber.Map{"pressCount": gs.ButtonCount})
}
// Login is a simple login handler that returns a JWT token
func (gs *GState) Login(c *fiber.Ctx) error {
// The body type is identical to a NewUser
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
if !gs.Db.CheckUser(u.Username, u.Password) {
println("User not found")
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create the Claims
claims := jwt.MapClaims{
"name": u.Username,
"admin": false,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
// LoginRenew is a simple handler that renews the token
func (gs *GState) LoginRenew(c *fiber.Ctx) error {
// For testing: curl localhost:3000/restricted -H "Authorization: Bearer <token>"
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
renewed := jwt.MapClaims{
"name": claims["name"],
"admin": claims["admin"],
"exp": claims["exp"],
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed)
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
// CreateProject is a simple handler that creates a new project
func (gs *GState) CreateProject(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
p := new(types.NewProject)
if err := c.BodyParser(p); err != nil {
return c.Status(400).SendString(err.Error())
}
// Get the username from the token and set it as the owner of the project
// This is ugly but
claims := user.Claims.(jwt.MapClaims)
owner := claims["name"].(string)
if err := gs.Db.AddProject(p.Name, p.Description, owner); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("Project added")
}
// GetUserProjects returns all projects that the user is a member of
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
// First we get the username from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
// Then dip into the database to get the projects
projects, err := gs.Db.GetProjectsForUser(username)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return a json serialized list of projects
return c.JSON(projects)
}
// ListAllUsers is a handler that returns a list of all users in the application database
func (gs *GState) ListAllUsers(c *fiber.Ctx) error {
// Get all users from the database
users, err := gs.Db.GetAllUsersApplication()
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the list of users as JSON
return c.JSON(users)
}
func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error {
// Extract the project name from the request parameters or body
projectName := c.Params("projectName")
// Get all users associated with the project from the database
users, err := gs.Db.GetAllUsersProject(projectName)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the list of users as JSON
return c.JSON(users)
}
// ProjectRoleChange is a handler that changes a user's role within a project
func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {
// Extract the necessary parameters from the request
username := c.Params("username")
projectName := c.Params("projectName")
role := c.Params("role")
// Change the user's role within the project in the database
if err := gs.Db.ChangeUserRole(username, projectName, role); err != nil {
return c.Status(500).SendString(err.Error())
}
// Return a success message
return c.SendStatus(fiber.StatusOK)
}
// GetProject retrieves a specific project by its ID
func (gs *GState) GetProject(c *fiber.Ctx) error {
// Extract the project ID from the request parameters or body
projectID := c.Params("projectID")
// Parse the project ID into an integer
projectIDInt, err := strconv.Atoi(projectID)
if err != nil {
return c.Status(400).SendString("Invalid project ID")
}
// Get the project from the database by its ID
project, err := gs.Db.GetProject(projectIDInt)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the project as JSON
return c.JSON(project)
}
func (gs *GState) SubmitWeeklyReport(c *fiber.Ctx) error {
// Extract the necessary parameters from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
report := new(types.NewWeeklyReport)
if err := c.BodyParser(report); err != nil {
return c.Status(400).SendString(err.Error())
}
// Make sure all the fields of the report are valid
if report.Week < 1 || report.Week > 52 {
return c.Status(400).SendString("Invalid week number")
}
if report.DevelopmentTime < 0 || report.MeetingTime < 0 || report.AdminTime < 0 || report.OwnWorkTime < 0 || report.StudyTime < 0 || report.TestingTime < 0 {
return c.Status(400).SendString("Invalid time report")
}
if err := gs.Db.AddWeeklyReport(report.ProjectName, username, report.Week, report.DevelopmentTime, report.MeetingTime, report.AdminTime, report.OwnWorkTime, report.StudyTime, report.TestingTime); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("Time report added")
}

View file

@ -0,0 +1,98 @@
package handlers
import (
"strconv"
"ttime/internal/types"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
)
// CreateProject is a simple handler that creates a new project
func (gs *GState) CreateProject(c *fiber.Ctx) error {
user := c.Locals("user").(*jwt.Token)
p := new(types.NewProject)
if err := c.BodyParser(p); err != nil {
return c.Status(400).SendString(err.Error())
}
// Get the username from the token and set it as the owner of the project
// This is ugly but
claims := user.Claims.(jwt.MapClaims)
owner := claims["name"].(string)
if err := gs.Db.AddProject(p.Name, p.Description, owner); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("Project added")
}
// GetUserProjects returns all projects that the user is a member of
func (gs *GState) GetUserProjects(c *fiber.Ctx) error {
// First we get the username from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
// Then dip into the database to get the projects
projects, err := gs.Db.GetProjectsForUser(username)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return a json serialized list of projects
return c.JSON(projects)
}
// ProjectRoleChange is a handler that changes a user's role within a project
func (gs *GState) ProjectRoleChange(c *fiber.Ctx) error {
// Extract the necessary parameters from the request
username := c.Params("username")
projectName := c.Params("projectName")
role := c.Params("role")
// Change the user's role within the project in the database
if err := gs.Db.ChangeUserRole(username, projectName, role); err != nil {
return c.Status(500).SendString(err.Error())
}
// Return a success message
return c.SendStatus(fiber.StatusOK)
}
// GetProject retrieves a specific project by its ID
func (gs *GState) GetProject(c *fiber.Ctx) error {
// Extract the project ID from the request parameters or body
projectID := c.Params("projectID")
// Parse the project ID into an integer
projectIDInt, err := strconv.Atoi(projectID)
if err != nil {
return c.Status(400).SendString("Invalid project ID")
}
// Get the project from the database by its ID
project, err := gs.Db.GetProject(projectIDInt)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the project as JSON
return c.JSON(project)
}
func (gs *GState) ListAllUsersProject(c *fiber.Ctx) error {
// Extract the project name from the request parameters or body
projectName := c.Params("projectName")
// Get all users associated with the project from the database
users, err := gs.Db.GetAllUsersProject(projectName)
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the list of users as JSON
return c.JSON(users)
}

View file

@ -0,0 +1,34 @@
package handlers
import (
"ttime/internal/types"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
)
func (gs *GState) SubmitWeeklyReport(c *fiber.Ctx) error {
// Extract the necessary parameters from the token
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
username := claims["name"].(string)
report := new(types.NewWeeklyReport)
if err := c.BodyParser(report); err != nil {
return c.Status(400).SendString(err.Error())
}
// Make sure all the fields of the report are valid
if report.Week < 1 || report.Week > 52 {
return c.Status(400).SendString("Invalid week number")
}
if report.DevelopmentTime < 0 || report.MeetingTime < 0 || report.AdminTime < 0 || report.OwnWorkTime < 0 || report.StudyTime < 0 || report.TestingTime < 0 {
return c.Status(400).SendString("Invalid time report")
}
if err := gs.Db.AddWeeklyReport(report.ProjectName, username, report.Week, report.DevelopmentTime, report.MeetingTime, report.AdminTime, report.OwnWorkTime, report.StudyTime, report.TestingTime); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("Time report added")
}

View file

@ -0,0 +1,116 @@
package handlers
import (
"time"
"ttime/internal/types"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
)
// Register is a simple handler that registers a new user
//
// @Summary Register a new user
// @Description Register a new user
// @Tags User
// @Accept json
// @Produce json
// @Success 200 {string} string "User added"
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /api/register [post]
func (gs *GState) Register(c *fiber.Ctx) error {
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
if err := gs.Db.AddUser(u.Username, u.Password); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("User added")
}
// This path should obviously be protected in the future
// UserDelete deletes a user from the database
func (gs *GState) UserDelete(c *fiber.Ctx) error {
// Read from path parameters
username := c.Params("username")
// Read username from Locals
auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string)
if username != auth_username {
return c.Status(403).SendString("You can only delete yourself")
}
if err := gs.Db.RemoveUser(username); err != nil {
return c.Status(500).SendString(err.Error())
}
return c.Status(200).SendString("User deleted")
}
// Login is a simple login handler that returns a JWT token
func (gs *GState) Login(c *fiber.Ctx) error {
// The body type is identical to a NewUser
u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
if !gs.Db.CheckUser(u.Username, u.Password) {
println("User not found")
return c.SendStatus(fiber.StatusUnauthorized)
}
// Create the Claims
claims := jwt.MapClaims{
"name": u.Username,
"admin": false,
"exp": time.Now().Add(time.Hour * 72).Unix(),
}
// Create token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
// LoginRenew is a simple handler that renews the token
func (gs *GState) LoginRenew(c *fiber.Ctx) error {
// For testing: curl localhost:3000/restricted -H "Authorization: Bearer <token>"
user := c.Locals("user").(*jwt.Token)
claims := user.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
renewed := jwt.MapClaims{
"name": claims["name"],
"admin": claims["admin"],
"exp": claims["exp"],
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed)
t, err := token.SignedString([]byte("secret"))
if err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
return c.JSON(fiber.Map{"token": t})
}
// ListAllUsers is a handler that returns a list of all users in the application database
func (gs *GState) ListAllUsers(c *fiber.Ctx) error {
// Get all users from the database
users, err := gs.Db.GetAllUsersApplication()
if err != nil {
return c.Status(500).SendString(err.Error())
}
// Return the list of users as JSON
return c.JSON(users)
}