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 }