Compare commits
5 commits
3a7663124d
...
0824a344e3
Author | SHA1 | Date | |
---|---|---|---|
|
0824a344e3 | ||
|
dd4809d631 | ||
|
f0745c5a75 | ||
|
b5c2987281 | ||
|
1c87380db7 |
9 changed files with 88 additions and 7 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|
||||||
bin
|
bin
|
||||||
|
database.txt
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
*.png
|
*.png
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,9 @@ migrate:
|
||||||
db.sqlite3:
|
db.sqlite3:
|
||||||
make migrate
|
make migrate
|
||||||
|
|
||||||
|
dbdump:
|
||||||
|
sqlite3 $(DB_FILE) .dump > database.txt
|
||||||
|
|
||||||
backup:
|
backup:
|
||||||
mkdir -p backups
|
mkdir -p backups
|
||||||
sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz
|
sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"embed"
|
"embed"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
@ -25,7 +27,8 @@ type Database interface {
|
||||||
// ChangeUserRole(username string, projectname string, role string) error
|
// ChangeUserRole(username string, projectname string, role string) error
|
||||||
// AddTimeReport(projectname string, start time.Time, end time.Time) error
|
// AddTimeReport(projectname string, start time.Time, end time.Time) error
|
||||||
// AddUserToProject(username string, projectname string) error
|
// AddUserToProject(username string, projectname string) error
|
||||||
// ChangeUserRole(username string, projectname string, role 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
|
// This struct is a wrapper type that holds the database connection
|
||||||
|
@ -42,7 +45,6 @@ const projectInsert = "INSERT INTO projects (name, description, owner_user_id) S
|
||||||
const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?"
|
const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?"
|
||||||
const addTimeReport = "INSERT INTO activity (report_id, activity_nbr, start_time, end_time, break, comment) VALUES (?, ?, ?, ?, ?, ?)" // WIP
|
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
|
const addUserToProject = "INSERT INTO project_member (project_id, user_id, role) VALUES (?, ?, ?)" // WIP
|
||||||
// const changeUserRole = ""
|
|
||||||
|
|
||||||
// DbConnect connects to the database
|
// DbConnect connects to the database
|
||||||
func DbConnect(dbpath string) Database {
|
func DbConnect(dbpath string) Database {
|
||||||
|
@ -61,6 +63,22 @@ func DbConnect(dbpath string) Database {
|
||||||
return &Db{db}
|
return &Db{db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
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)
|
_, err := d.Exec(addTimeReport, projectname, 0, start, end, breakTime, false)
|
||||||
return err
|
return err
|
||||||
|
@ -110,10 +128,16 @@ func (d *Db) GetUserId(username string) (int, error) {
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Db) GetProjectId(projectname string) (int, error) { // WIP, denna kan vara goof
|
func (d *Db) GetProjectId(projectname string) (int, error) {
|
||||||
var id int
|
var id int
|
||||||
err := d.Get(&id, "SELECT id FROM project WHERE project_name = ?", projectname)
|
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
|
// Creates a new project in the database, associated with a user
|
||||||
|
|
|
@ -92,6 +92,32 @@ func TestPromoteToAdmin(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDbChangeUserRole(t *testing.T) {
|
||||||
|
// Set up the initial state
|
||||||
|
db, err := setupState()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("setupState failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a user
|
||||||
|
err = db.AddUser("test", "password")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddUser failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a project
|
||||||
|
err = db.AddProject("test_project", "project description", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Error("AddProject failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 TestAddTimeReport(t *testing.T) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -5,5 +5,5 @@ CREATE TABLE IF NOT EXISTS project_role (
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Insert the possible roles a user can have in a project.
|
-- Insert the possible roles a user can have in a project.
|
||||||
INSERT OR IGNORE INTO project_role (p_role) VALUES ('admin');
|
INSERT OR IGNORE INTO project_role (p_role) VALUES ('project_manager');
|
||||||
INSERT OR IGNORE INTO project_role (p_role) VALUES ('member');
|
INSERT OR IGNORE INTO project_role (p_role) VALUES ('member');
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
CREATE TABLE IF NOT EXISTS user_roles (
|
CREATE TABLE IF NOT EXISTS user_roles (
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
project_id INTEGER NOT NULL,
|
project_id INTEGER NOT NULL,
|
||||||
p_role TEXT NOT NULL, -- 'admin' or 'member'
|
p_role TEXT NOT NULL, -- 'project_manager' or 'member'
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
FOREIGN KEY (project_id) REFERENCES projects (id)
|
FOREIGN KEY (project_id) REFERENCES projects (id)
|
||||||
FOREIGN KEY (p_role) REFERENCES project_role (p_role)
|
FOREIGN KEY (p_role) REFERENCES project_role (p_role)
|
||||||
|
|
|
@ -71,6 +71,7 @@ func main() {
|
||||||
|
|
||||||
server.Post("/api/loginrenew", gs.LoginRenew)
|
server.Post("/api/loginrenew", gs.LoginRenew)
|
||||||
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
server.Delete("/api/userdelete", gs.UserDelete) // Perhaps just use POST to avoid headaches
|
||||||
|
server.Post("/api/project", gs.CreateProject)
|
||||||
|
|
||||||
// Announce the port we are listening on and start the server
|
// Announce the port we are listening on and start the server
|
||||||
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
err = server.Listen(fmt.Sprintf(":%d", conf.Port))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { NewProject, Project } from "../Types/Project";
|
||||||
import { NewUser, User } from "../Types/Users";
|
import { NewUser, User } from "../Types/Users";
|
||||||
|
|
||||||
// Defines all the methods that an instance of the API must implement
|
// Defines all the methods that an instance of the API must implement
|
||||||
|
@ -6,6 +7,8 @@ interface API {
|
||||||
registerUser(user: NewUser): Promise<User>;
|
registerUser(user: NewUser): Promise<User>;
|
||||||
/** Remove a user */
|
/** Remove a user */
|
||||||
removeUser(username: string): Promise<User>;
|
removeUser(username: string): Promise<User>;
|
||||||
|
/** Create a project */
|
||||||
|
createProject(project: NewProject): Promise<Project>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export an instance of the API
|
// Export an instance of the API
|
||||||
|
@ -29,4 +32,14 @@ export const api: API = {
|
||||||
body: JSON.stringify(username),
|
body: JSON.stringify(username),
|
||||||
}).then((res) => res.json() as Promise<User>);
|
}).then((res) => res.json() as Promise<User>);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async createProject(project: NewProject): Promise<Project> {
|
||||||
|
return fetch("/api/project", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(project),
|
||||||
|
}).then((res) => res.json() as Promise<Project>);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
13
frontend/src/Types/Project.ts
Normal file
13
frontend/src/Types/Project.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export interface Project {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
owner: string;
|
||||||
|
created: string; // This is a date
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NewProject {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
owner: string;
|
||||||
|
}
|
Loading…
Reference in a new issue