diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 5221e4d..9e63395 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -1,7 +1,9 @@ package database import ( + "database/sql" "embed" + "fmt" "os" "path/filepath" "time" @@ -14,15 +16,19 @@ import ( type Database interface { // Insert a new user into the database, password should be hashed before calling AddUser(username string, password string) error + RemoveUser(username string) error PromoteToAdmin(username string) error GetUserId(username string) (int, error) AddProject(name string, description string, username string) error Migrate(dirname string) error - GetProjectId(projectname string) (int, error) - AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error - AddUserToProject(username string, projectname string, role string) error + // AddTimeReport(projectname string, start time.Time, end time.Time) error + // AddUserToProject(username string, projectname string) error + // ChangeUserRole(username string, projectname string, role string) error + // AddTimeReport(projectname string, start time.Time, end time.Time) error + // AddUserToProject(username string, projectname string) error ChangeUserRole(username string, projectname string, role string) error + GetProjectId(projectname string) (int, error) } // This struct is a wrapper type that holds the database connection @@ -34,16 +40,11 @@ type Db struct { //go:embed migrations var scripts embed.FS -// TODO: Possibly break these out into separate files bundled with the embed package? const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" const projectInsert = "INSERT INTO projects (name, description, owner_user_id) SELECT ?, ?, id FROM users WHERE username = ?" const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?" -const addTimeReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?), - ProjectLookup AS (SELECT id FROM projects WHERE name = ?) - INSERT INTO time_reports (project_id, user_id, start, end) - VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup), ?, ?);` -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 addTimeReport = "INSERT INTO activity (report_id, activity_nbr, start_time, end_time, break, comment) VALUES (?, ?, ?, ?, ?, ?)" // WIP +const addUserToProject = "INSERT INTO project_member (project_id, user_id, role) VALUES (?, ?, ?)" // WIP // DbConnect connects to the database func DbConnect(dbpath string) Database { @@ -62,8 +63,24 @@ func DbConnect(dbpath string) Database { return &Db{db} } -func (d *Db) AddTimeReport(projectName string, userName string, start time.Time, end time.Time) error { // WIP - _, err := d.Exec(addTimeReport, userName, projectName, start, end) +func (d *Db) ChangeUserRole(username string, projectname string, role string) error { + userID, err := d.GetUserId(username) + if err != nil { + return err + } + + projectID, err := d.GetProjectId(projectname) + if err != nil { + return err + } + + // Update user role in the project using the correct table name + _, err = d.Exec("INSERT OR REPLACE INTO user_roles (user_id, project_id, p_role) VALUES (?, ?, ?)", userID, projectID, role) + return err +} + +func (d *Db) AddTimeReport(projectname string, start time.Time, end time.Time, breakTime uint32) error { // WIP + _, err := d.Exec(addTimeReport, projectname, 0, start, end, breakTime, false) return err } @@ -80,26 +97,13 @@ func (d *Db) AddUserToProject(username string, projectname string, role string) panic(err2) } - _, err3 := d.Exec(addUserToProject, userid, projectid, role) + _, err3 := d.Exec(addUserToProject, projectid, userid, role) return err3 } -func (d *Db) ChangeUserRole(username string, projectname string, role string) error { - var userid int - userid, err := d.GetUserId(username) - if err != nil { - panic(err) - } +// func (d *Db) ChangeUserRole(username string, projectname string, role string) error { - var projectid int - projectid, err2 := d.GetProjectId(projectname) - if err2 != nil { - panic(err2) - } - - _, err3 := d.Exec(changeUserRole, role, userid, projectid) - return err3 -} +// } // AddUser adds a user to the database func (d *Db) AddUser(username string, password string) error { @@ -127,7 +131,13 @@ func (d *Db) GetUserId(username string) (int, error) { func (d *Db) GetProjectId(projectname string) (int, error) { var id int err := d.Get(&id, "SELECT id FROM projects WHERE name = ?", projectname) - return id, err + if err != nil { + if err == sql.ErrNoRows { + return 0, fmt.Errorf("project '%s' not found", projectname) + } + return 0, err + } + return id, nil } // Creates a new project in the database, associated with a user diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index b5fe49f..08eee32 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -2,7 +2,6 @@ package database import ( "testing" - "time" ) // Tests are not guaranteed to be sequential @@ -93,84 +92,40 @@ func TestPromoteToAdmin(t *testing.T) { } } -func TestAddTimeReport(t *testing.T) { +func TestDbChangeUserRole(t *testing.T) { + // Set up the initial state db, err := setupState() if err != nil { t.Error("setupState failed:", err) } - err = db.AddUser("testuser", "password") + // Add a user + err = db.AddUser("test", "password") if err != nil { t.Error("AddUser failed:", err) } - err = db.AddProject("testproject", "description", "testuser") + // Add a project + err = db.AddProject("test_project", "project description", "test") if err != nil { t.Error("AddProject failed:", err) } - var now = time.Now() - var then = now.Add(time.Hour) - - err = db.AddTimeReport("testproject", "testuser", now, then) - if err != nil { - t.Error("AddTimeReport failed:", err) - } -} - -func TestAddUserToProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - var now = time.Now() - var then = now.Add(time.Hour) - - err = db.AddTimeReport("testproject", "testuser", now, then) - if err != nil { - t.Error("AddTimeReport failed:", err) - } - - err = db.AddUserToProject("testuser", "testproject", "user") - if err != nil { - t.Error("AddUserToProject failed:", err) - } -} - -func TestChangeUserRole(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddUserToProject("testuser", "testproject", "user") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - err = db.ChangeUserRole("testuser", "testproject", "admin") + // Change user role + err = db.ChangeUserRole("test", "test_project", "project_manager") if err != nil { t.Error("ChangeUserRole failed:", err) } } + +// func TestAddTimeReport(t *testing.T) { + +// } + +// func TestAddUserToProject(t *testing.T) { + +// } + +// func TestChangeUserRole(t *testing.T) { + +// } diff --git a/backend/internal/database/migrations/0020_projects.sql b/backend/internal/database/migrations/0020_projects.sql index 58d8e97..adfb818 100644 --- a/backend/internal/database/migrations/0020_projects.sql +++ b/backend/internal/database/migrations/0020_projects.sql @@ -1,9 +1,11 @@ CREATE TABLE IF NOT EXISTS projects ( id INTEGER PRIMARY KEY, + projectId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL UNIQUE, description TEXT NOT NULL, owner_user_id INTEGER NOT NULL, FOREIGN KEY (owner_user_id) REFERENCES users (id) ); +CREATE INDEX IF NOT EXISTS projects_projectId_index ON projects (projectId); CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id); \ No newline at end of file diff --git a/backend/internal/database/migrations/0030_time_reports.sql b/backend/internal/database/migrations/0030_time_reports.sql index 76812a1..e8f3ec1 100644 --- a/backend/internal/database/migrations/0030_time_reports.sql +++ b/backend/internal/database/migrations/0030_time_reports.sql @@ -1,11 +1,10 @@ CREATE TABLE IF NOT EXISTS time_reports ( id INTEGER PRIMARY KEY, + reportId TEXT DEFAULT (HEX(RANDOMBLOB(6))) NOT NULL UNIQUE, project_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, start DATETIME NOT NULL, end DATETIME NOT NULL, FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE - FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts index f33c87c..24854c0 100644 --- a/frontend/src/API/API.ts +++ b/frontend/src/API/API.ts @@ -9,8 +9,6 @@ interface API { removeUser(username: string): Promise; /** Create a project */ createProject(project: NewProject): Promise; - /** Renew the token */ - renewToken(token: string): Promise; } // Export an instance of the API @@ -44,14 +42,4 @@ export const api: API = { body: JSON.stringify(project), }).then((res) => res.json() as Promise); }, - - async renewToken(token: string): Promise { - return fetch("/api/loginrenew", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }).then((res) => res.json() as Promise); - }, }; diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx deleted file mode 100644 index 8181774..0000000 --- a/frontend/src/Components/Register.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useState } from "react"; -import { NewUser, User } from "../Types/Users"; -import { api } from "../API/API"; - -export default function Register(): JSX.Element { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - - const handleRegister = async (): Promise => { - const newUser: NewUser = { userName: username, password }; - const user = await api.registerUser(newUser); - }; - - return ( -
-
-
{ - e.preventDefault(); - void handleRegister(); - }} - > -

Register new user

-
- - { - setUsername(e.target.value); - }} - /> -
-
- - { - setPassword(e.target.value); - }} - /> -
-
- -
-
-

-
-
- ); -} diff --git a/frontend/src/Pages/LoginPage.tsx b/frontend/src/Pages/LoginPage.tsx index 11a7da2..d8ea651 100644 --- a/frontend/src/Pages/LoginPage.tsx +++ b/frontend/src/Pages/LoginPage.tsx @@ -3,7 +3,6 @@ import Logo from "/src/assets/TTIMElogo.png"; import "./LoginPage.css"; import { useEffect } from "react"; import { Link } from "react-router-dom"; -import Register from "../Components/Register"; const PreloadBackgroundAnimation = (): JSX.Element => { useEffect(() => { @@ -70,14 +69,6 @@ function LoginPage(): JSX.Element { }} /> - -