Files
2025-07-07 01:44:12 +02:00

214 lines
8.0 KiB
Go

package models
import (
"time"
"golang.org/x/crypto/bcrypt"
)
// User represents a user account
type User struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
Username string `json:"username" gorm:"uniqueIndex;not null;size:50"`
Email string `json:"email" gorm:"uniqueIndex;not null;size:255"`
Password string `json:"-" gorm:"-"` // Only used for input, never stored
PasswordHash string `json:"-" gorm:"column:password_hash;not null"`
BaseCurrency string `json:"base_currency" gorm:"not null;default:EUR;size:3"`
FuelStops []FuelStop `json:"fuel_stops,omitempty" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
Vehicles []Vehicle `json:"vehicles,omitempty" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}
// CheckPassword verifies a password against the stored hash
func (u *User) CheckPassword(password string) bool {
// Defensive programming - check for nil user
if u == nil {
return false
}
// Check for empty password or password hash
if password == "" || u.PasswordHash == "" {
return false
}
err := bcrypt.CompareHashAndPassword([]byte(u.PasswordHash), []byte(password))
return err == nil
}
// Vehicle represents a user's vehicle
type Vehicle struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
UserID uint `json:"user_id" gorm:"not null;index"`
User User `json:"-" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
Name string `json:"name" gorm:"not null;size:100"`
Make string `json:"make" gorm:"size:50"`
Model string `json:"model" gorm:"size:50"`
Year int `json:"year" gorm:"default:0"`
LicensePlate string `json:"license_plate" gorm:"size:20"`
FuelType string `json:"fuel_type" gorm:"size:50"`
Notes string `json:"notes" gorm:"type:text"`
IsActive bool `json:"is_active" gorm:"default:true"`
FuelStops []FuelStop `json:"fuel_stops,omitempty" gorm:"foreignKey:VehicleID;constraint:OnDelete:CASCADE"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}
// FuelStop represents a fuel station stop record
type FuelStop struct {
ID uint `json:"id" gorm:"primaryKey;autoIncrement"`
UserID uint `json:"user_id" gorm:"not null;index"`
User User `json:"-" gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE"`
VehicleID uint `json:"vehicle_id" gorm:"not null;index"`
Vehicle Vehicle `json:"vehicle,omitempty" gorm:"foreignKey:VehicleID;constraint:OnDelete:CASCADE"`
Date time.Time `json:"date" gorm:"not null;type:date"`
StationName string `json:"station_name" gorm:"not null;size:100"`
Location string `json:"location" gorm:"not null;size:255"`
FuelType string `json:"fuel_type" gorm:"not null;size:50"`
Liters float64 `json:"liters" gorm:"not null;type:decimal(10,3)"`
PricePerL float64 `json:"price_per_l" gorm:"not null;type:decimal(10,4)"`
TotalPrice float64 `json:"total_price" gorm:"not null;type:decimal(10,2)"`
Currency string `json:"currency" gorm:"not null;default:EUR;size:3"`
Odometer int `json:"odometer" gorm:"default:0"`
TripLength float64 `json:"trip_length" gorm:"default:0;type:decimal(8,2);comment:Distance traveled since last fillup in km"`
Notes string `json:"notes" gorm:"type:text"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}
// FuelStopStats represents statistics for fuel consumption
type FuelStopStats struct {
TotalStops int `json:"total_stops"`
TotalLiters float64 `json:"total_liters"`
TotalSpent float64 `json:"total_spent"`
AveragePrice float64 `json:"average_price"`
AverageConsumption float64 `json:"average_consumption"`
LastFillup *FuelStop `json:"last_fillup"`
}
// FuelStopForm represents form data for creating/updating fuel stops
type FuelStopForm struct {
Date string `json:"date" form:"date"`
VehicleID uint `json:"vehicle_id" form:"vehicle_id"`
StationName string `json:"station_name" form:"station_name"`
Location string `json:"location" form:"location"`
FuelType string `json:"fuel_type" form:"fuel_type"`
Liters float64 `json:"liters" form:"liters"`
PricePerL float64 `json:"price_per_l" form:"price_per_l"`
TotalPrice float64 `json:"total_price" form:"total_price"`
Currency string `json:"currency" form:"currency"`
Odometer int `json:"odometer" form:"odometer"`
TripLength float64 `json:"trip_length" form:"trip_length"`
Notes string `json:"notes" form:"notes"`
}
// UserRegistrationForm represents form data for user registration
type UserRegistrationForm struct {
Username string `json:"username" form:"username"`
Email string `json:"email" form:"email"`
Password string `json:"password" form:"password"`
ConfirmPassword string `json:"confirm_password" form:"confirm_password"`
BaseCurrency string `json:"base_currency" form:"base_currency"`
}
// UserLoginForm represents form data for user login
type UserLoginForm struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
}
// UserSettingsForm represents form data for user settings
type UserSettingsForm struct {
Email string `json:"email" form:"email"`
BaseCurrency string `json:"base_currency" form:"base_currency"`
}
// MonthlyStats represents monthly statistics for fuel consumption
type MonthlyStats struct {
Month string `json:"month"`
Year string `json:"year"`
TotalStops int `json:"total_stops"`
TotalLiters float64 `json:"total_liters"`
TotalSpent float64 `json:"total_spent"`
AveragePrice float64 `json:"average_price"`
}
// VehicleForm represents form data for creating/updating vehicles
type VehicleForm struct {
Name string `json:"name" form:"name"`
Make string `json:"make" form:"make"`
Model string `json:"model" form:"model"`
Year int `json:"year" form:"year"`
LicensePlate string `json:"license_plate" form:"license_plate"`
FuelType string `json:"fuel_type" form:"fuel_type"`
Notes string `json:"notes" form:"notes"`
IsActive bool `json:"is_active" form:"is_active"`
}
// ToFuelStop converts a FuelStopForm to a FuelStop
func (f *FuelStopForm) ToFuelStop(userID uint) (*FuelStop, error) {
date, err := time.Parse("2006-01-02", f.Date)
if err != nil {
return nil, err
}
// Use EUR as default currency if not provided
currency := f.Currency
if currency == "" {
currency = "EUR"
}
return &FuelStop{
UserID: userID,
VehicleID: f.VehicleID,
Date: date,
StationName: f.StationName,
Location: f.Location,
FuelType: f.FuelType,
Liters: f.Liters,
PricePerL: f.PricePerL,
TotalPrice: f.TotalPrice,
Currency: currency,
Odometer: f.Odometer,
TripLength: f.TripLength,
Notes: f.Notes,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
}
// ToVehicle converts a VehicleForm to a Vehicle
func (f *VehicleForm) ToVehicle(userID uint) *Vehicle {
return &Vehicle{
UserID: userID,
Name: f.Name,
Make: f.Make,
Model: f.Model,
Year: f.Year,
LicensePlate: f.LicensePlate,
FuelType: f.FuelType,
Notes: f.Notes,
IsActive: f.IsActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
// ToUser converts a UserRegistrationForm to a User
func (f *UserRegistrationForm) ToUser() (*User, error) {
// Use EUR as default currency if not provided
currency := f.BaseCurrency
if currency == "" {
currency = "EUR"
}
return &User{
Username: f.Username,
Email: f.Email,
Password: f.Password,
BaseCurrency: currency,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}, nil
}