diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 30a672a..f4c0f6e 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -42,6 +42,7 @@ type Database interface { IsProjectManager(username string, projectname string) (bool, error) GetProjectTimes(projectName string) (map[string]int, error) UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error + RemoveProject(projectname string) error } // This struct is a wrapper type that holds the database connection @@ -595,3 +596,8 @@ func (d *Db) GetProjectTimes(projectName string) (map[string]int, error) { return totalTime, nil } + +func (d *Db) RemoveProject(projectname string) error { + _, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) + return err +} diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 275836b..fe3e6cd 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -935,3 +935,33 @@ func TestUpdateWeeklyReport(t *testing.T) { t.Error("UpdateWeeklyReport failed: report not updated correctly") } } + +func TestRemoveProject(t *testing.T) { + db, err := setupAdvancedState() + if err != nil { + t.Error("setupState failed:", err) + } + + // Promote user to Admin + err = db.PromoteToAdmin("demouser") + if err != nil { + t.Error("PromoteToAdmin failed:", err) + } + + // Remove project + err = db.RemoveProject("projecttest") + if err != nil { + t.Error("RemoveProject failed:", err) + } + + // Check if the project was removed + projects, err := db.GetAllProjects() + if err != nil { + t.Error("GetAllProjects failed:", err) + } + if len(projects) != 0 { + t.Error("RemoveProject failed: expected 0, got", len(projects)) + } + +} + \ No newline at end of file diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go index 9190ea4..0db4340 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -30,6 +30,7 @@ type GlobalState interface { GetAllUsersProject(c *fiber.Ctx) error // WIP GetUnsignedReports(c *fiber.Ctx) error // UpdateWeeklyReport(c *fiber.Ctx) error + RemoveProject(c *fiber.Ctx) error } // "Constructor" diff --git a/backend/internal/handlers/handlers_project_related.go b/backend/internal/handlers/handlers_project_related.go index f64d013..bdcabd7 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -219,7 +219,7 @@ func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error { username := claims["name"].(string) // Extract necessary parameters from the request query string - projectName := c.Params("projectName") + projectName := c.Query("projectName") log.Info("Checking if user ", username, " is a project manager for project ", projectName) @@ -231,59 +231,85 @@ func (gs *GState) IsProjectManagerHandler(c *fiber.Ctx) error { } // Return the result as JSON - return c.JSON(fiber.Map{"isProjectManager": isManager}) + return c.JSON(map[string]bool{"isProjectManager": isManager}) } func (gs *GState) GetProjectTimesHandler(c *fiber.Ctx) error { - // Get the username from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Get project - projectName := c.Params("projectName") - if projectName == "" { - log.Info("No project name provided") - return c.Status(400).SendString("No project name provided") - } - - // Get all users in the project and roles - userProjects, err := gs.Db.GetAllUsersProject(projectName) - if err != nil { - log.Info("Error getting users in project:", err) - return c.Status(500).SendString(err.Error()) - } - - // If the user is member - isMember := false - for _, userProject := range userProjects { - if userProject.Username == username { - isMember = true - break - } - } - - // If the user is admin - if !isMember { - isAdmin, err := gs.Db.IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - if !isAdmin { - log.Info("User is neither a project member nor a site admin:", username) - return c.Status(403).SendString("User is neither a project member nor a site admin") - } - } - - // Get project times - projectTimes, err := gs.Db.GetProjectTimes(projectName) - if err != nil { - log.Info("Error getting project times:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return project times as JSON - log.Info("Returning project times for project:", projectName) - return c.JSON(projectTimes) -} \ No newline at end of file + // Get the username from the token + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + username := claims["name"].(string) + + // Get project + projectName := c.Params("projectName") + if projectName == "" { + log.Info("No project name provided") + return c.Status(400).SendString("No project name provided") + } + + // Get all users in the project and roles + userProjects, err := gs.Db.GetAllUsersProject(projectName) + if err != nil { + log.Info("Error getting users in project:", err) + return c.Status(500).SendString(err.Error()) + } + + // If the user is member + isMember := false + for _, userProject := range userProjects { + if userProject.Username == username { + isMember = true + break + } + } + + // If the user is admin + if !isMember { + isAdmin, err := gs.Db.IsSiteAdmin(username) + if err != nil { + log.Info("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) + } + if !isAdmin { + log.Info("User is neither a project member nor a site admin:", username) + return c.Status(403).SendString("User is neither a project member nor a site admin") + } + } + + // Get project times + projectTimes, err := gs.Db.GetProjectTimes(projectName) + if err != nil { + log.Info("Error getting project times:", err) + return c.Status(500).SendString(err.Error()) + } + + // Return project times as JSON + log.Info("Returning project times for project:", projectName) + return c.JSON(projectTimes) +} + +func (gs *GState) RemoveProject(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + username := claims["name"].(string) + + // Check if the user is a site admin + isAdmin, err := gs.Db.IsSiteAdmin(username) + if err != nil { + log.Info("Error checking admin status:", err) + return c.Status(500).SendString(err.Error()) + } + + if !isAdmin { + log.Info("User is not a site admin:", username) + return c.Status(403).SendString("User is not a site admin") + } + + projectName := c.Params("projectName") + + if err := gs.Db.RemoveProject(projectName); err != nil { + return c.Status(500).SendString((err.Error())) + } + + return c.Status(200).SendString("Project deleted") +} diff --git a/backend/main.go b/backend/main.go index 5d0a9af..669bbc7 100644 --- a/backend/main.go +++ b/backend/main.go @@ -109,6 +109,7 @@ func main() { server.Post("/api/ProjectRoleChange", gs.ProjectRoleChange) server.Get("/api/getUsersProject/:projectName", gs.ListAllUsersProject) server.Put("/api/updateWeeklyReport", gs.UpdateWeeklyReport) + server.Delete("/api/removeProject/:projectName", gs.RemoveProject) // Announce the port we are listening on and start the server err = server.Listen(fmt.Sprintf(":%d", conf.Port)) diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index c6cef66..e7aab0c 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -47,7 +47,6 @@ interface API { * @returns {Promise>} A promise containing the API response indicating if the user is a project manager. */ checkIfProjectManager( - username: string, projectName: string, token: string, ): Promise>; @@ -133,6 +132,11 @@ interface API { projectName: string, token: string, ): Promise>; + + removeProject( + projectName: string, + token: string, + ): Promise>; } /** An instance of the API */ @@ -190,19 +194,20 @@ export const api: API = { }, async checkIfProjectManager( - username: string, projectName: string, token: string, ): Promise> { try { - const response = await fetch("/api/checkIfProjectManager", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, + const response = await fetch( + `/api/checkIfProjectManager?projectName=${projectName}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, }, - body: JSON.stringify({ username, projectName }), - }); + ); if (!response.ok) { return { @@ -214,7 +219,7 @@ export const api: API = { return { success: true, data }; } } catch (e) { - return { success: false, message: "fuck" }; + return { success: false, message: "Failed to check if project manager" }; } }, @@ -484,4 +489,34 @@ export const api: API = { }); } }, + + async removeProject( + projectName: string, + token: string, + ): Promise> { + try { + const response = await fetch(`/api/projectdelete/${projectName}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + token, + }, + }); + + if (!response.ok) { + return Promise.resolve({ + success: false, + message: "Failed to remove project", + }); + } else { + const data = await response.text(); + return Promise.resolve({ success: true, message: data }); + } + } catch (e) { + return Promise.resolve({ + success: false, + message: "Failed to remove project", + }); + } + }, }; diff --git a/frontend/src/Pages/AdminPages/AdminProjectPage.tsx b/frontend/src/Pages/AdminPages/AdminProjectPage.tsx index 0faae7e..db51319 100644 --- a/frontend/src/Pages/AdminPages/AdminProjectPage.tsx +++ b/frontend/src/Pages/AdminPages/AdminProjectPage.tsx @@ -1,17 +1,26 @@ +import { useParams } from "react-router-dom"; +import { api } from "../../API/API"; import BackButton from "../../Components/BackButton"; import BasicWindow from "../../Components/BasicWindow"; import Button from "../../Components/Button"; +async function handleDeleteProject( + projectName: string, + token: string, +): Promise { + await api.removeProject(projectName, token); +} + function AdminProjectPage(): JSX.Element { const content = <>; + const { projectName } = useParams(); + const token = localStorage.getItem("accessToken"); const buttons = ( <>