feat(auth): Implement database-backed user authentication and
Build and Test / build-and-test-backend (push) Successful in 40s
Build and Test / build-and-test-frontend (push) Successful in 36s

persistence

This commit introduces a major architectural change by replacing the
previous hardcoded user and data system with a robust, database-driven
approach. It lays the foundation for persistent data storage for all
application models.

Key Changes:

- **Database Integration:** - Added `GORM` and `SQLite` for the database
layer. - The database is now initialized and migrated on application
startup. - Configuration is managed through environment variables
(`DATABASE`, `USERS_FILE`).

- **User Authentication & Management:** - The login endpoint
(`/api/token`) now authenticates users against the database. - JWT
claims are now populated with real user data (`id`, `username`,
`is_superuser`). - Implemented a system to seed the database with
initial users from a `users.json` file.

- **New User API Endpoints:** - `GET /api/users`: Retrieves a list of
all users (superuser access required). - `GET /api/users/user`: Fetches
the data for the currently authenticated user based on their JWT.

- **Data Model Overhaul:** - Refactored all data models (`User`,
`Category`, `Operation`, `Subcategory`) to include GORM tags,
relationships, and a `BaseModel` with auto-generated UUIDs for primary
keys. - Separated user-related structs into `User` (database model),
`AuthRequest` (login payload), and `UserResponse` (safe API response).

- **Middleware & Routing:** - Improved JWT error handling for clearer
client-side messages. - Added a new middleware to allow passing the JWT
in the request body for more flexible client integration.
This commit is contained in:
wt committed 2025-08-08 23:47:36 +07:00
1 parent c6ed1d66b0
commit 599142af80
23 files changed
+458 -108

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
package database
import (
"backend/model"
"os"
"github.com/glebarez/sqlite"
"github.com/gofiber/fiber/v2/log"
"gorm.io/gorm"
)
var Db *gorm.DB
func Init() error {
if os.Getenv("DATABASE") == "" {
log.Fatal("Error no DATABASE path or url")
}
var err error
Db, err = gorm.Open(sqlite.Open(os.Getenv("DATABASE")), &gorm.Config{})
if err != nil {
log.Error("Failed to connect database")
return err
}
return nil
}
func Migrate() {
Db.AutoMigrate(
&model.User{},
&model.Category{},
&model.Subcategory{},
&model.Operation{},
)
}
func InitDatabase() {
Init()
Migrate()
if os.Getenv("USERS_FILE") != "" {
allusers := GetUsersFromFile(os.Getenv("USERS_FILE"))
for i := range allusers {
CreateUser(&allusers[i])
}
}
}
+85
View File
@@ -0,0 +1,85 @@
package database
import (
"backend/model"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"gorm.io/gorm"
)
func CreateUser(user *model.User) error {
e := Db.Where(model.User{
Username: user.Username,
}).FirstOrCreate(&user).Error
if e != nil {
return e
}
return nil
}
func GetUserById(id string) (*model.User, error) {
var user model.User
e := Db.First(&user, "id = ?", id).Error
if e != nil {
if errors.Is(e, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, e
}
return &user, nil
}
func GetUserByUsername(username string) (*model.User, error) {
var user model.User
e := Db.First(&user, "username = ?", username).Error
if e != nil {
if errors.Is(e, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("user not found")
}
return nil, e
}
return &user, nil
}
func DeleteUser(id string) error {
var user model.User
e := Db.First(&user, "id = ?", id).Error
if e != nil {
if errors.Is(e, gorm.ErrRecordNotFound) {
return fmt.Errorf("user not found")
}
return e
}
e = Db.Unscoped().Delete(&user).Error
if e != nil {
if errors.Is(e, gorm.ErrRecordNotFound) {
return fmt.Errorf("user not found")
}
return e
}
return nil
}
func GetUsersFromFile(filename string) []model.User {
data, e := os.ReadFile(filename)
if e != nil {
panic(e)
}
decoder := json.NewDecoder(strings.NewReader(string(data)))
var res []model.User
decoder.Decode(&res)
return res
}
func GetAllUsers() ([]model.User, error) {
var users []model.User
e := Db.Find(&users).Error
if e != nil {
return nil, e
}
return users, nil
}