diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go
index 6bb113b..5a88873 100644
--- a/backend/internal/database/db.go
+++ b/backend/internal/database/db.go
@@ -4,6 +4,7 @@ import (
"embed"
"os"
"path/filepath"
+ "time"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
@@ -11,7 +12,9 @@ import (
// Interface for the database
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)
@@ -20,6 +23,9 @@ type Database interface {
// 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
}
// This struct is a wrapper type that holds the database connection
@@ -34,9 +40,8 @@ var scripts embed.FS
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 = ""
-// const addUserToProject = ""
+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 changeUserRole = ""
// DbConnect connects to the database
@@ -56,13 +61,27 @@ func DbConnect(dbpath string) Database {
return &Db{db}
}
-// func (d *Db) AddTimeReport(projectname string, start time.Time, end time.Time) error {
+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
+}
-// }
+func (d *Db) AddUserToProject(username string, projectname string, role string) error { // WIP
+ var userid int
+ userid, err := d.GetUserId(username)
+ if err != nil {
+ panic(err)
+ }
-// func (d *Db) AddUserToProject(username string, projectname string) error {
+ var projectid int
+ projectid, err2 := d.GetProjectId(projectname)
+ if err2 != nil {
+ panic(err2)
+ }
-// }
+ _, err3 := d.Exec(addUserToProject, projectid, userid, role)
+ return err3
+}
// func (d *Db) ChangeUserRole(username string, projectname string, role string) error {
@@ -87,7 +106,13 @@ func (d *Db) PromoteToAdmin(username string) error {
func (d *Db) GetUserId(username string) (int, error) {
var id int
- err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username)
+ err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username) // Borde det inte vara "user" i singular
+ return id, err
+}
+
+func (d *Db) GetProjectId(projectname string) (int, error) { // WIP, denna kan vara goof
+ var id int
+ err := d.Get(&id, "SELECT id FROM project WHERE project_name = ?", projectname)
return id, err
}
diff --git a/backend/internal/database/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql
index 7cb23fd..5c9d329 100644
--- a/backend/internal/database/migrations/0010_users.sql
+++ b/backend/internal/database/migrations/0010_users.sql
@@ -1,3 +1,7 @@
+-- Id is a surrogate key for in ternal use
+-- userId is what is used for external id
+-- username is what is used for login
+-- password is the hashed password
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
userId TEXT DEFAULT (HEX(RANDOMBLOB(4))) NOT NULL UNIQUE,
@@ -5,5 +9,6 @@ CREATE TABLE IF NOT EXISTS users (
password VARCHAR(255) NOT NULL
);
+-- 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_userId_index ON users (userId);
\ No newline at end of file
diff --git a/backend/internal/database/migrations/0070_salts.sql b/backend/internal/database/migrations/0070_salts.sql
new file mode 100644
index 0000000..b84dfac
--- /dev/null
+++ b/backend/internal/database/migrations/0070_salts.sql
@@ -0,0 +1,16 @@
+-- It is unclear weather this table will be used
+
+-- Create the table to store hash salts
+CREATE TABLE salts (
+ id INTEGER PRIMARY KEY,
+ salt TEXT NOT NULL
+);
+
+-- Commented out for now, no time for good practices, which is atrocious
+-- Create a trigger to automatically generate a salt when inserting a new user record
+-- CREATE TRIGGER generate_salt_trigger
+-- AFTER INSERT ON users
+-- BEGIN
+-- INSERT INTO salts (salt) VALUES (randomblob(16));
+-- UPDATE users SET salt_id = (SELECT last_insert_rowid()) WHERE id = new.id;
+-- END;
diff --git a/backend/internal/handlers/global_state.go b/backend/internal/handlers/global_state.go
index 689759b..9c42133 100644
--- a/backend/internal/handlers/global_state.go
+++ b/backend/internal/handlers/global_state.go
@@ -11,11 +11,11 @@ import (
// The actual interface that we will use
type GlobalState interface {
- Register(c *fiber.Ctx) error // To register a new user
- UserDelete(c *fiber.Ctx) error // To delete a user
- Login(c *fiber.Ctx) error // To get the token
- LoginRenew(c *fiber.Ctx) error // To renew the token
- // CreateProject(c *fiber.Ctx) error // To create a new project
+ Register(c *fiber.Ctx) error // To register a new user
+ UserDelete(c *fiber.Ctx) error // To delete a user
+ Login(c *fiber.Ctx) error // To get the token
+ LoginRenew(c *fiber.Ctx) error // To renew the token
+ CreateProject(c *fiber.Ctx) error // To create a new project
// GetProjects(c *fiber.Ctx) error // To get all projects
// GetProject(c *fiber.Ctx) error // To get a specific project
// UpdateProject(c *fiber.Ctx) error // To update a project
@@ -58,7 +58,7 @@ type GState struct {
// @Failure 500 {string} string "Internal server error"
// @Router /api/register [post]
func (gs *GState) Register(c *fiber.Ctx) error {
- u := new(types.User)
+ u := new(types.NewUser)
if err := c.BodyParser(u); err != nil {
return c.Status(400).SendString(err.Error())
}
@@ -142,3 +142,24 @@ func (gs *GState) LoginRenew(c *fiber.Ctx) error {
}
return c.JSON(fiber.Map{"token": t})
}
+
+// CreateProject is a simple handler that creates a new project
+func (gs *GState) CreateProject(c *fiber.Ctx) error {
+ user := c.Locals("user").(*jwt.Token)
+
+ p := new(types.NewProject)
+ if err := c.BodyParser(p); err != nil {
+ return c.Status(400).SendString(err.Error())
+ }
+
+ // Get the username from the token and set it as the owner of the project
+ // This is ugly but
+ claims := user.Claims.(jwt.MapClaims)
+ p.Owner = claims["name"].(string)
+
+ if err := gs.Db.AddProject(p.Name, p.Description, p.Owner); err != nil {
+ return c.Status(500).SendString(err.Error())
+ }
+
+ return c.Status(200).SendString("Project added")
+}
diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go
new file mode 100644
index 0000000..cabf6c6
--- /dev/null
+++ b/backend/internal/types/project.go
@@ -0,0 +1,21 @@
+package types
+
+import (
+ "time"
+)
+
+// Project is a struct that holds the information about a project
+type Project struct {
+ ID int `json:"id" db:"id"`
+ Name string `json:"name" db:"name"`
+ Description string `json:"description" db:"description"`
+ Owner string `json:"owner" db:"owner"`
+ Created time.Time `json:"created" db:"created"`
+}
+
+// As it arrives from the client
+type NewProject struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Owner string `json:"owner"`
+}
diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go
index fa735d7..233ec71 100644
--- a/backend/internal/types/users.go
+++ b/backend/internal/types/users.go
@@ -16,6 +16,11 @@ func (u *User) ToPublicUser() (*PublicUser, error) {
}, nil
}
+type NewUser struct {
+ Username string `json:"username"`
+ Password string `json:"password"`
+}
+
// PublicUser represents a user that is safe to send over the API (no password)
type PublicUser struct {
UserId string `json:"userId"`
diff --git a/frontend/index.html b/frontend/index.html
index e4b78ea..573ba58 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,9 +2,9 @@