diff --git a/.gitignore b/.gitignore index 6334372..eac16b3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ skouter assets/app.js package-lock.json config.go +*~ diff --git a/components/app.vue b/components/app.vue index 2256ce4..fc61e15 100644 --- a/components/app.vue +++ b/components/app.vue @@ -78,7 +78,7 @@ export default { computed: { active }, data() { return { - loading: false, user: user, hash: window.location.hash, + loading: true, user: user, hash: window.location.hash, fees: fees } }, diff --git a/migrations/0_29092022_create_main_tables.sql b/migrations/0_29092022_setup_tables.sql similarity index 99% rename from migrations/0_29092022_create_main_tables.sql rename to migrations/0_29092022_setup_tables.sql index ea86d4a..9b4a269 100644 --- a/migrations/0_29092022_create_main_tables.sql +++ b/migrations/0_29092022_setup_tables.sql @@ -27,6 +27,7 @@ CREATE TABLE user ( 'Subscribed', 'Branch', 'Admin'), + role ENUM('User', 'Manager', 'Admin'), PRIMARY KEY (`id`), FOREIGN KEY (branch_id) REFERENCES branch(id) ); diff --git a/skouter.go b/skouter.go index 6f35aef..9542148 100644 --- a/skouter.go +++ b/skouter.go @@ -129,9 +129,9 @@ var pages = map[string]Page { } var roles = map[string]int{ - "guest": 1, - "employee": 2, - "admin": 3, + "User": 1, + "Manager": 2, + "Admin": 3, } // Used to validate claim in JWT token body. Checks if user id is greater than @@ -624,6 +624,152 @@ func guard(r *http.Request, required int) bool { return true } +func queryUsers(db *sql.DB, id int) ( []User, error ) { + var users []User + var query string + var rows *sql.Rows + var err error + + query = `SELECT + u.id, + u.email, + u.first_name, + u.last_name, + u.branch_id, + u.country, + u.title, + u.status, + u.verified, + u.last_login, + u.role + FROM user u WHERE u.id = CASE @e := ? WHEN 0 THEN u.id ELSE @e END + ` + rows, err = db.Query(query, id) + + + if err != nil { + return users, err + } + + defer rows.Close() + + for rows.Next() { + var user User + + if err := rows.Scan( + &user.Id, + &user.Email, + &user.FirstName, + &user.LastName, + &user.BranchId, + &user.Country, + &user.Title, + &user.Status, + &user.Verified, + &user.LastLogin, + &user.Role, + ) + err != nil { + return users, err + } + users = append(users, user) + } + + // Prevents runtime panics + if len(users) == 0 { return users, errors.New("User not found.") } + + return users, nil +} + +func insertUser(db *sql.DB, user User) (User, error){ + var query string + var row *sql.Row + var err error + var id int // Inserted user's id + + query = `INSERT INTO user + ( + email, + first_name, + last_name, + password, + created, + role, + verified, + last_login + ) + VALUES (?, ?, ?, sha2(?, 256), NOW(), ?, ?, NOW()) + RETURNING id + ` + row = db.QueryRow(query, + user.Email, + user.FirstName, + user.LastName, + user.Password, + user.Role, + user.Verified, + ) + + err = row.Scan(&id) + if err != nil { return User{}, err } + + users, err := queryUsers(db, id) + if err != nil { return User{}, err } + + return users[0], nil +} + +func updateUser(user User, db *sql.DB) error { + query := ` + UPDATE user + SET + email = CASE @e := ? WHEN '' THEN email ELSE @e END, + first_name = CASE @fn := ? WHEN '' THEN first_name ELSE @fn END, + last_name = CASE @ln := ? WHEN '' THEN last_name ELSE @ln END, + role = CASE @r := ? WHEN '' THEN role ELSE @r END, + password = CASE @p := ? WHEN '' THEN password ELSE sha2(@p, 256) END + WHERE id = ? + ` + + _, err := db.Exec(query, + user.Email, + user.FirstName, + user.LastName, + user.Role, + user.Password, + user.Id, + ) + + return err +} + + +func getUser(w http.ResponseWriter, db *sql.DB, r *http.Request) { + claims, err := getClaims(r) + if err != nil { w.WriteHeader(500); return } + users, err := queryUsers(db, claims.Id) + if err != nil { w.WriteHeader(422); return } + json.NewEncoder(w).Encode(users) +} + +func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) { + var user User + err := json.NewDecoder(r.Body).Decode(&user) + if err != nil { http.Error(w, "Invalid fields.", 422); return } + + _, err = mail.ParseAddress(user.Email) + if err != nil { http.Error(w, "Invalid email.", 422); return } + + if roles[user.Role] == 0 { + http.Error(w, "Invalid role.", 422) + } + + user, err = insertUser(db, user) + if err != nil { http.Error(w, "Error creating user.", 422); return } + + json.NewEncoder(w).Encode(user) +} + func api(w http.ResponseWriter, r *http.Request) { var args []string @@ -649,10 +795,10 @@ func api(w http.ResponseWriter, r *http.Request) { r.Method == http.MethodGet && guard(r, 1): getToken(w, db, r) case match(p, "/api/users", &args) && // Array of all users - r.Method == http.MethodGet && guard(r, 2): + r.Method == http.MethodGet && guard(r, 3): getUsers(w, db, r) case match(p, "/api/user", &args) && - r.Method == http.MethodGet, && guard(r, 1): + r.Method == http.MethodGet && guard(r, 1): getUser(w, db, r) case match(p, "/api/user", &args) && r.Method == http.MethodPost && @@ -670,54 +816,6 @@ func api(w http.ResponseWriter, r *http.Request) { r.Method == http.MethodDelete && guard(r, 3): deleteUser(w, db, r) - case match(p, "/api/batch", &args) && - r.Method == http.MethodGet && - guard(r, 1): - getBatch(w, db, r) - case match(p, "/api/batch", &args) && - r.Method == http.MethodPost && - guard(r, 2): - openBatch(w, db, r) - case match(p, "/api/batch", &args) && - r.Method == http.MethodPatch && - guard(r, 2): - closeBatch(w, db, r) - case match(p, "/api/client", &args) && - r.Method == http.MethodGet && - guard(r, 1): - getClient(w, db, r) - case match(p, "/api/client", &args) && - r.Method == http.MethodPost && - guard(r, 2): - createClient(w, db, r) - case match(p, "/api/client", &args) && - r.Method == http.MethodPatch && - guard(r, 2): - patchClient(w, db, r) - case match(p, "/api/client", &args) && - r.Method == http.MethodDelete && - guard(r, 2): - deleteClient(w, db, r) - case match(p, "/api/ticket", &args) && - r.Method == http.MethodPost && - guard(r, 2): - openTicket(w, db, r) - case match(p, "/api/ticket", &args) && - r.Method == http.MethodPatch && - guard(r, 2): - closeTicket(w, db, r) - case match(p, "/api/ticket", &args) && - r.Method == http.MethodDelete && - guard(r, 2): - voidTicket(w, db, r) - case match(p, "/api/report/batch", &args) && - r.Method == http.MethodGet && - guard(r, 2): - reportBatch(w, db, r) - case match(p, "/api/report/summary", &args) && - r.Method == http.MethodPost && - guard(r, 2): - reportSummary(w, db, r) } db.Close()