package users import ( "database/sql" "fmt" "path/filepath" "time" "github.com/google/uuid" _ "github.com/mattn/go-sqlite3" "golang.org/x/crypto/bcrypt" ) type UserDatabase struct { Connection *sql.DB databaseDirectory string } func (db *UserDatabase) Initialize() error { connection, error := sql.Open("sqlite3", filepath.Join(db.databaseDirectory, "users.db")) if error != nil { return error } db.Connection = connection _, error = db.Connection.Exec(`CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE, password TEXT, is_admin INTEGER )`) if error != nil { return fmt.Errorf("error creating users table: %s", error) } _, error = db.Connection.Exec(`CREATE TABLE IF NOT EXISTS sessions ( token TEXT PRIMARY KEY, user_id INTEGER, expiry_date TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) )`) if error != nil { return fmt.Errorf("error creating sessions table: %s", error) } return nil } func (db *UserDatabase) Close() error { return db.Connection.Close() } func (db *UserDatabase) CreateUser(username, password string, isAdmin bool) (string, error) { userID := uuid.New() hashedPassword, err := hashPassword(password) if err != nil { return "", err } _, err = db.Connection.Exec("INSERT INTO users (id, username, password, is_admin) VALUES (?, ?, ?, ?)", userID.String(), username, hashedPassword, isAdmin) return userID.String(), err } func (db *UserDatabase) CreateSession(userID string, expiryDate time.Time) (string, error) { sessionToken := uuid.New() _, err := db.Connection.Exec("INSERT INTO sessions (user_id, token, expiry_date) VALUES (?, ?, ?)", userID, sessionToken.String(), expiryDate) if err != nil { return "", err } return sessionToken.String(), nil } func (db *UserDatabase) GetUserByUsername(username string) (*User, error) { var user User err := db.Connection.QueryRow("SELECT id, username, password, is_admin FROM users WHERE username = ?", username).Scan(&user.ID, &user.Username, &user.Password, &user.IsAdmin) if err != nil { return nil, err } return &user, nil } func (db *UserDatabase) GetUserById(id string) (*User, error) { var user User err := db.Connection.QueryRow("SELECT id, username, password, is_admin FROM users WHERE id = ?", id).Scan(&user.ID, &user.Username, &user.Password, &user.IsAdmin) if err != nil { return nil, err } return &user, nil } func (db *UserDatabase) UsernameExists(username string) (bool, error) { var exists bool err := db.Connection.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE username = ?)", username).Scan(&exists) if err != nil { return false, err } return exists, nil } func (db *UserDatabase) UserIdExists(id string) (bool, error) { var exists bool err := db.Connection.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ?)", id).Scan(&exists) if err != nil { return false, err } return exists, nil } func (db *UserDatabase) UpdateUser(user *User) error { _, err := db.Connection.Exec("UPDATE users SET username = ?, is_admin = ? WHERE id = ?", user.Username, user.IsAdmin, user.ID) return err } func (db *UserDatabase) UpdatePassword(userId string, newPassword string) error { hashedPassword, err := hashPassword(newPassword) if err != nil { return err } _, err = db.Connection.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userId) return err } func (db *UserDatabase) CheckCredentials(username, password string) (bool, error) { var hashedPassword string err := db.Connection.QueryRow("SELECT password FROM users WHERE username = ?", username).Scan(&hashedPassword) if err != nil { if err == sql.ErrNoRows { return false, nil } return false, err } err = bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)) if err != nil { return false, nil } return true, nil } func (db *UserDatabase) GetSession(sessionToken string) (*Session, error) { var session Session row := db.Connection.QueryRow("SELECT token, user_id, expiry_date FROM sessions WHERE token = ?", sessionToken) err := row.Scan(&session.Token, &session.UserID, &session.ExpiryDate) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return &session, nil } func (db *UserDatabase) DeleteSessionByToken(token string) error { _, err := db.Connection.Exec("DELETE FROM sessions WHERE token = ?", token) return err } func (db *UserDatabase) GetUsers() (*[]User, error) { var users []User rows, err := db.Connection.Query("SELECT id, username, is_admin FROM users") if err != nil { return nil, err } defer rows.Close() for rows.Next() { var user User err := rows.Scan(&user.ID, &user.Username, &user.IsAdmin) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } users = append(users, user) } return &users, nil } func (db *UserDatabase) DeleteUser(ID string) error { _, err := db.Connection.Exec("DELETE FROM users WHERE id = ?", ID) return err } func hashPassword(password string) (string, error) { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { return "", err } return string(hashedPassword), nil } func (db *UserDatabase) SetDirectory(directory string) { db.databaseDirectory = directory }