diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 25dd04b..95cf8ed 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -64,10 +64,9 @@ const addWeeklyReport = `WITH UserLookup AS (SELECT id FROM users WHERE username const addUserToProject = "INSERT INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)" // WIP const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = ? AND project_id = ?" -const getProjectsForUser = `SELECT p.id, p.name, p.description FROM projects p - JOIN user_roles ur ON p.id = ur.project_id - JOIN users u ON ur.user_id = u.id - WHERE u.username = ?` +const getProjectsForUser = `SELECT projects.id, projects.name, projects.description, projects.owner_user_id + FROM projects JOIN user_roles ON projects.id = user_roles.project_id + JOIN users ON user_roles.user_id = users.id WHERE users.username = ?;` // DbConnect connects to the database func DbConnect(dbpath string) Database { diff --git a/backend/internal/database/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql index 15b1373..e7c770b 100644 --- a/backend/internal/database/migrations/0010_users.sql +++ b/backend/internal/database/migrations/0010_users.sql @@ -9,4 +9,4 @@ CREATE TABLE IF NOT EXISTS users ( ); -- Users are commonly searched by username and userId -CREATE INDEX IF NOT EXISTS users_username_index ON users (username); +CREATE INDEX IF NOT EXISTS users_username_index ON users (username); \ No newline at end of file diff --git a/backend/internal/database/sample_data/0010_sample_data.sql b/backend/internal/database/sample_data/0010_sample_data.sql index 4dac91b..f3d9412 100644 --- a/backend/internal/database/sample_data/0010_sample_data.sql +++ b/backend/internal/database/sample_data/0010_sample_data.sql @@ -1,35 +1,7 @@ -INSERT OR IGNORE INTO users(username, password) -VALUES ("admin", "123"); +INSERT OR IGNORE INTO users (username, password) VALUES + ('admin', 'password'), + ('user', 'password'); -INSERT OR IGNORE INTO users(username, password) -VALUES ("user", "123"); - -INSERT OR IGNORE INTO users(username, password) -VALUES ("user2", "123"); - -INSERT OR IGNORE INTO projects(name,description,owner_user_id) -VALUES ("projecttest","test project", 1); - -INSERT OR IGNORE INTO projects(name,description,owner_user_id) -VALUES ("projecttest2","test project2", 1); - -INSERT OR IGNORE INTO projects(name,description,owner_user_id) -VALUES ("projecttest3","test project3", 1); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (1,1,"project_manager"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (2,1,"member"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (3,1,"member"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (3,2,"member"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (3,3,"member"); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (2,1,"project_manager"); +INSERT OR IGNORE INTO projects (name, description, owner_user_id) VALUES + ('Project 1', 'Description 1', 1), + ('Project 2', 'Description 2', 2); \ No newline at end of file diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index f3a7ea0..3732249 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -5,7 +5,6 @@ import ( "ttime/internal/types" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" "github.com/golang-jwt/jwt/v5" ) @@ -68,47 +67,37 @@ func (gs *GState) GetProject(c *fiber.Ctx) error { // Extract the project ID from the request parameters or body projectID := c.Params("projectID") if projectID == "" { - log.Info("No project ID provided") return c.Status(400).SendString("No project ID provided") } - log.Info("Getting project with ID: ", projectID) + println("Getting project with ID: ", projectID) // Parse the project ID into an integer projectIDInt, err := strconv.Atoi(projectID) if err != nil { - log.Info("Invalid project ID") 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 { - log.Info("Error getting project:", err) return c.Status(500).SendString(err.Error()) } // Return the project as JSON - log.Info("Returning project: ", project.Name) + println("Returning project: ", project.Name) 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") - if projectName == "" { - log.Info("No project name provided") - return c.Status(400).SendString("No project name provided") - } // Get all users associated with the project from the database users, err := gs.Db.GetAllUsersProject(projectName) if err != nil { - log.Info("Error getting users for project:", err) return c.Status(500).SendString(err.Error()) } - log.Info("Returning users for project: ", projectName) - // Return the list of users as JSON return c.JSON(users) } @@ -122,7 +111,7 @@ func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error { Role string `json:"role"` } if err := c.BodyParser(&requestData); err != nil { - log.Info("Error parsing request body:", err) + println("Error parsing request body:", err) return c.Status(400).SendString("Bad request") } @@ -130,27 +119,27 @@ func (gs *GState) AddUserToProjectHandler(c *fiber.Ctx) error { user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) adminUsername := claims["name"].(string) - log.Info("Admin username from claims:", adminUsername) + println("Admin username from claims:", adminUsername) isAdmin, err := gs.Db.IsSiteAdmin(adminUsername) if err != nil { - log.Info("Error checking admin status:", err) + println("Error checking admin status:", err) return c.Status(500).SendString(err.Error()) } if !isAdmin { - log.Info("User is not a site admin:", adminUsername) + println("User is not a site admin:", adminUsername) return c.Status(403).SendString("User is not a site admin") } // Add the user to the project with the specified role err = gs.Db.AddUserToProject(requestData.Username, requestData.ProjectName, requestData.Role) if err != nil { - log.Info("Error adding user to project:", err) + println("Error adding user to project:", err) return c.Status(500).SendString(err.Error()) } // Return success message - log.Info("User added to project successfully:", requestData.Username) + println("User added to project successfully:", requestData.Username) return c.SendStatus(fiber.StatusOK) } diff --git a/backend/internal/handlers/handlers_report_related.go b/backend/internal/handlers/handlers_report_related.go index 85eb6e2..291d068 100644 --- a/backend/internal/handlers/handlers_report_related.go +++ b/backend/internal/handlers/handlers_report_related.go @@ -5,7 +5,6 @@ import ( "ttime/internal/types" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" "github.com/golang-jwt/jwt/v5" ) @@ -17,62 +16,50 @@ func (gs *GState) SubmitWeeklyReport(c *fiber.Ctx) error { report := new(types.NewWeeklyReport) if err := c.BodyParser(report); err != nil { - log.Info("Error parsing weekly report") return c.Status(400).SendString(err.Error()) } // Make sure all the fields of the report are valid if report.Week < 1 || report.Week > 52 { - log.Info("Invalid week number") 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 { - log.Info("Invalid time report") 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 { - log.Info("Error adding weekly report") return c.Status(500).SendString(err.Error()) } - log.Info("Weekly report added") return c.Status(200).SendString("Time report added") } // Handler for retrieving weekly report func (gs *GState) GetWeeklyReport(c *fiber.Ctx) error { // Extract the necessary parameters from the request + println("GetWeeklyReport") user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) username := claims["name"].(string) - log.Info("Getting weekly report for: ", username) - // Extract project name and week from query parameters projectName := c.Query("projectName") + println(projectName) week := c.Query("week") - - if projectName == "" || week == "" { - log.Info("Missing project name or week number") - return c.Status(400).SendString("Missing project name or week number") - } + println(week) // Convert week to integer weekInt, err := strconv.Atoi(week) if err != nil { - log.Info("Invalid week number") return c.Status(400).SendString("Invalid week number") } // Call the database function to get the weekly report report, err := gs.Db.GetWeeklyReport(username, projectName, weekInt) if err != nil { - log.Info("Error getting weekly report from db:", err) return c.Status(500).SendString(err.Error()) } - log.Info("Returning weekly report") // Return the retrieved weekly report return c.JSON(report) } @@ -82,33 +69,35 @@ type ReportId struct { } func (gs *GState) SignReport(c *fiber.Ctx) error { + println("Signing report...") // Extract the necessary parameters from the token user := c.Locals("user").(*jwt.Token) claims := user.Claims.(jwt.MapClaims) projectManagerUsername := claims["name"].(string) - log.Info("Signing report for: ", projectManagerUsername) - // Extract report ID from the request query parameters // reportID := c.Query("reportId") rid := new(ReportId) if err := c.BodyParser(rid); err != nil { return err } - log.Info("Signing report for: ", rid.ReportId) + println("Signing report for: ", rid.ReportId) + // reportIDInt, err := strconv.Atoi(rid.ReportId) + // println("Signing report for: ", rid.ReportId) + // if err != nil { + // return c.Status(400).SendString("Invalid report ID") + // } // Get the project manager's ID projectManagerID, err := gs.Db.GetUserId(projectManagerUsername) if err != nil { - log.Info("Failed to get project manager ID") return c.Status(500).SendString("Failed to get project manager ID") } - log.Info("Project manager ID: ", projectManagerID) + println("blabla", projectManagerID) // Call the database function to sign the weekly report err = gs.Db.SignWeeklyReport(rid.ReportId, projectManagerID) if err != nil { - log.Info("Error signing weekly report:", err) return c.Status(500).SendString(err.Error()) } diff --git a/backend/internal/handlers/handlers_user_related.go b/backend/internal/handlers/handlers_user_related.go index 8f4108c..0f7c047 100644 --- a/backend/internal/handlers/handlers_user_related.go +++ b/backend/internal/handlers/handlers_user_related.go @@ -1,11 +1,10 @@ package handlers import ( + "fmt" "time" "ttime/internal/types" - "github.com/gofiber/fiber/v2/log" - "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v5" ) @@ -24,17 +23,16 @@ import ( func (gs *GState) Register(c *fiber.Ctx) error { u := new(types.NewUser) if err := c.BodyParser(u); err != nil { - log.Warn("Error parsing body") + println("Error parsing body") return c.Status(400).SendString(err.Error()) } - log.Info("Adding user:", u.Username) + println("Adding user:", u.Username) if err := gs.Db.AddUser(u.Username, u.Password); err != nil { - log.Warn("Error adding user:", err) return c.Status(500).SendString(err.Error()) } - log.Info("User added:", u.Username) + println("User added:", u.Username) return c.Status(200).SendString("User added") } @@ -48,16 +46,13 @@ func (gs *GState) UserDelete(c *fiber.Ctx) error { auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string) if username != auth_username { - log.Info("User tried to delete another user") return c.Status(403).SendString("You can only delete yourself") } if err := gs.Db.RemoveUser(username); err != nil { - log.Warn("Error deleting user:", err) return c.Status(500).SendString(err.Error()) } - log.Info("User deleted:", username) return c.Status(200).SendString("User deleted") } @@ -66,13 +61,13 @@ 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 { - log.Warn("Error parsing body") + println("Error parsing body") return c.Status(400).SendString(err.Error()) } - log.Info("Username logging in:", u.Username) + println("Username:", u.Username) if !gs.Db.CheckUser(u.Username, u.Password) { - log.Info("User not found") + println("User not found") return c.SendStatus(fiber.StatusUnauthorized) } @@ -85,25 +80,23 @@ func (gs *GState) Login(c *fiber.Ctx) error { // Create token token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - log.Info("Token created for user:", u.Username) + println("Token created for user:", u.Username) // Generate encoded token and send it as response. t, err := token.SignedString([]byte("secret")) if err != nil { - log.Warn("Error signing token") + println("Error signing token") return c.SendStatus(fiber.StatusInternalServerError) } - log.Info("Successfully signed token for user:", u.Username) + println("Successfully signed token for user:", u.Username) 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 " user := c.Locals("user").(*jwt.Token) - - log.Info("Renewing token for user:", user.Claims.(jwt.MapClaims)["name"]) - claims := user.Claims.(jwt.MapClaims) claims["exp"] = time.Now().Add(time.Hour * 72).Unix() renewed := jwt.MapClaims{ @@ -114,11 +107,8 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error { token := jwt.NewWithClaims(jwt.SigningMethodHS256, renewed) t, err := token.SignedString([]byte("secret")) if err != nil { - log.Warn("Error signing token") return c.SendStatus(fiber.StatusInternalServerError) } - - log.Info("Successfully renewed token for user:", user.Claims.(jwt.MapClaims)["name"]) return c.JSON(fiber.Map{"token": t}) } @@ -127,11 +117,9 @@ func (gs *GState) ListAllUsers(c *fiber.Ctx) error { // Get all users from the database users, err := gs.Db.GetAllUsersApplication() if err != nil { - log.Info("Error getting users from db:", err) // Debug print return c.Status(500).SendString(err.Error()) } - log.Info("Returning all users") // Return the list of users as JSON return c.JSON(users) } @@ -144,15 +132,15 @@ func (gs *GState) PromoteToAdmin(c *fiber.Ctx) error { } username := newUser.Username - log.Info("Promoting user to admin:", username) // Debug print + println("Promoting user to admin:", username) // Debug print // Promote the user to a site admin in the database if err := gs.Db.PromoteToAdmin(username); err != nil { - log.Info("Error promoting user to admin:", err) // Debug print + fmt.Println("Error promoting user to admin:", err) // Debug print return c.Status(500).SendString(err.Error()) } - log.Info("User promoted to admin successfully:", username) // Debug print + println("User promoted to admin successfully:", username) // Debug print // Return a success message return c.SendStatus(fiber.StatusOK) diff --git a/backend/main.go b/backend/main.go index 9abe995..24c3702 100644 --- a/backend/main.go +++ b/backend/main.go @@ -10,7 +10,6 @@ import ( "github.com/BurntSushi/toml" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/swagger" jwtware "github.com/gofiber/contrib/jwt" @@ -47,12 +46,10 @@ func main() { // Migrate the database if err = db.Migrate(); err != nil { fmt.Println("Error migrating database: ", err) - os.Exit(1) } if err = db.MigrateSampleData(); err != nil { fmt.Println("Error migrating sample data: ", err) - os.Exit(1) } // Get our global state @@ -60,8 +57,6 @@ func main() { // Create the server server := fiber.New() - server.Use(logger.New()) - // Mounts the swagger documentation, this is available at /swagger/index.html server.Get("/swagger/*", swagger.HandlerDefault) diff --git a/testing.py b/testing.py index 1eea03b..6381afc 100644 --- a/testing.py +++ b/testing.py @@ -29,22 +29,6 @@ promoteToAdminPath = base_url + "/api/promoteToAdmin" getUserProjectsPath = base_url + "/api/getUserProjects" -def test_get_user_projects(): - - print("Testing get user projects") - loginResponse = login("user2", "123") - # Check if the user is added to the project - response = requests.get( - getUserProjectsPath, - json={"username": "user2"}, - headers={"Authorization": "Bearer " + loginResponse.json()["token"]}, - ) - print(response.text) - print(response.json()) - assert response.status_code == 200, "Get user projects failed" - print("got user projects successfully") - - # Posts the username and password to the register endpoint def register(username: string, password: string): print("Registering with username: ", username, " and password: ", password) @@ -64,7 +48,6 @@ def login(username: string, password: string): print(response.text) return response - # Test function to login def test_login(): response = login(username, "always_same") @@ -72,14 +55,12 @@ def test_login(): print("Login successful") return response.json()["token"] - # Test function to create a new user def test_create_user(): response = register(username, "always_same") assert response.status_code == 200, "Registration failed" print("Registration successful") - # Test function to add a project def test_add_project(): loginResponse = login(username, "always_same") @@ -93,7 +74,6 @@ def test_add_project(): assert response.status_code == 200, "Add project failed" print("Add project successful") - # Test function to submit a report def test_submit_report(): token = login(username, "always_same").json()["token"] @@ -115,49 +95,40 @@ def test_submit_report(): assert response.status_code == 200, "Submit report failed" print("Submit report successful") - # Test function to get a weekly report def test_get_weekly_report(): token = login(username, "always_same").json()["token"] response = requests.get( getWeeklyReportPath, headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, + params={"username": username, "projectName": projectName , "week": 1} ) print(response.text) assert response.status_code == 200, "Get weekly report failed" - # Tests getting a project by id def test_get_project(): token = login(username, "always_same").json()["token"] response = requests.get( - getProjectPath + "/1", # Assumes that the project with id 1 exists + getProjectPath + "/1", # Assumes that the project with id 1 exists headers={"Authorization": "Bearer " + token}, ) print(response.text) assert response.status_code == 200, "Get project failed" - # Test function to add a user to a project def test_add_user_to_project(): # Log in as a site admin admin_username = randomString() admin_password = "admin_password" - print( - "Registering with username: ", admin_username, " and password: ", admin_password - ) + print("Registering with username: ", admin_username, " and password: ", admin_password) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) print(response.text) admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) + response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}) print(response.text) assert response.status_code == 200, "Promote to site admin failed" print("Admin promoted to site admin successfully") @@ -177,6 +148,15 @@ def test_add_user_to_project(): assert response.status_code == 200, "Add user to project failed" print("Add user to project successful") + # Check if the user is added to the project + response = requests.get( + getUserProjectsPath, + json={"username": new_user}, + headers={"Authorization": "Bearer " + admin_token}, + ) + print(response.text) + assert response.status_code == 200, "Get user projects failed" + print("got user projects successfully") # Test function to sign a report def test_sign_report(): @@ -187,9 +167,7 @@ def test_sign_report(): # Register an admin admin_username = randomString() admin_password = "admin_password2" - print( - "Registering with username: ", admin_username, " and password: ", admin_password - ) + print("Registering with username: ", admin_username, " and password: ", admin_password) response = requests.post( registerPath, json={"username": admin_username, "password": admin_password} ) @@ -197,28 +175,18 @@ def test_sign_report(): # Log in as the admin admin_token = login(admin_username, admin_password).json()["token"] - response = requests.post( - promoteToAdminPath, - json={"username": admin_username}, - headers={"Authorization": "Bearer " + admin_token}, - ) + response = requests.post(promoteToAdminPath, json={"username": admin_username}, headers={"Authorization": "Bearer " + admin_token}) response = requests.put( addUserToProjectPath, - json={ - "projectName": projectName, - "username": project_manager, - "role": "project_manager", - }, + json={"projectName": projectName, "username": project_manager, "role": "project_manager"}, headers={"Authorization": "Bearer " + admin_token}, - ) + ) assert response.status_code == 200, "Add project manager to project failed" print("Project manager added to project successfully") - + # Log in as the project manager - project_manager_token = login(project_manager, "project_manager_password").json()[ - "token" - ] + project_manager_token = login(project_manager, "project_manager_password").json()["token"] # Submit a report for the project token = login(username, "always_same").json()["token"] @@ -243,7 +211,7 @@ def test_sign_report(): response = requests.get( getWeeklyReportPath, headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, + params={"username": username, "projectName": projectName , "week": 1} ) print(response.text) report_id = response.json()["reportId"] @@ -261,13 +229,12 @@ def test_sign_report(): response = requests.get( getWeeklyReportPath, headers={"Authorization": "Bearer " + token}, - params={"username": username, "projectName": projectName, "week": 1}, + params={"username": username, "projectName": projectName , "week": 1} ) print(response.text) if __name__ == "__main__": - test_get_user_projects() test_create_user() test_login() test_add_project()