From d4cc556366da340c4e19fc8a7700208bbc709613 Mon Sep 17 00:00:00 2001 From: Alexander Ek Date: Wed, 27 Mar 2024 21:18:44 +0100 Subject: [PATCH 1/3] Co-authored-by: al8763be --- backend/internal/database/db.go | 6 +++ backend/internal/database/db_test.go | 30 +++++++++++++ backend/internal/handlers/global_state.go | 1 + .../handlers/handlers_project_related.go | 26 +++++++++++ backend/main.go | 1 + frontend/src/API/API.ts | 35 +++++++++++++++ .../src/Pages/AdminPages/AdminProjectPage.tsx | 16 +++++-- testing.py | 44 ++++++++++++++++++- 8 files changed, 155 insertions(+), 4 deletions(-) diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index f871755..3aae28d 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -41,6 +41,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 @@ -549,3 +550,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 b68d446..90ef221 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -894,3 +894,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 49c8c09..b832f92 100644 --- a/backend/internal/handlers/global_state.go +++ b/backend/internal/handlers/global_state.go @@ -29,6 +29,7 @@ type GlobalState interface { ChangeUserName(c *fiber.Ctx) error // WIP GetAllUsersProject(c *fiber.Ctx) error // WIP 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..9c3ca67 100644 --- a/backend/internal/handlers/handlers_project_related.go +++ b/backend/internal/handlers/handlers_project_related.go @@ -286,4 +286,30 @@ func (gs *GState) GetProjectTimesHandler(c *fiber.Ctx) 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") } \ No newline at end of file diff --git a/backend/main.go b/backend/main.go index 1967708..0ec638b 100644 --- a/backend/main.go +++ b/backend/main.go @@ -108,6 +108,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..1e2e3e3 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -133,6 +133,11 @@ interface API { projectName: string, token: string, ): Promise>; + + removeProject( + projectName: string, + token: string, + ): Promise>; } /** An instance of the API */ @@ -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 = ( <>