updates
This commit is contained in:
21
server/internal/models/base.go
Normal file
21
server/internal/models/base.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type BaseModel struct {
|
||||
ID ULID `gorm:"primaryKey" json:"id" db:"id" `
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
|
||||
}
|
||||
|
||||
// BeforeCreate is a GORM hook that runs before creating a record
|
||||
func (b *BaseModel) BeforeCreate(tx *gorm.DB) error {
|
||||
if b.ID.IsZero() {
|
||||
b.ID = GenerateULID()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
11
server/internal/models/category.go
Normal file
11
server/internal/models/category.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
func (Category) TableName() string {
|
||||
return "categories"
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
BaseModel
|
||||
Name string `json:"name" db:"name"`
|
||||
Description string `json:"description" db:"description"`
|
||||
}
|
||||
13
server/internal/models/nutrient.go
Normal file
13
server/internal/models/nutrient.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
func (Nutrient) TableName() string {
|
||||
return "nutrients"
|
||||
}
|
||||
|
||||
type Nutrient struct {
|
||||
BaseModel
|
||||
Name string `json:"name" db:"name"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Supplements []Supplement `json:"supplements" db:"supplements" gorm:"many2many:supplement_nutrients;"`
|
||||
Categories []Category `json:"categories" db:"categories" gorm:"many2many:nutrient_categories;"`
|
||||
}
|
||||
13
server/internal/models/nutrient_category.go
Normal file
13
server/internal/models/nutrient_category.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
func (NutrientCategory) TableName() string {
|
||||
return "nutrient_categories"
|
||||
}
|
||||
|
||||
type NutrientCategory struct {
|
||||
BaseModel
|
||||
CategoryID ULID `json:"categoryId" db:"category_id"`
|
||||
NutrientID ULID `json:"nutrientId" db:"nutrient_id"`
|
||||
Category Category `json:"category" db:"category" gorm:"foreignKey:CategoryID"`
|
||||
Nutrient Nutrient `json:"nutrient" db:"nutrient" gorm:"foreignKey:NutrientID"`
|
||||
}
|
||||
21
server/internal/models/supplement.go
Normal file
21
server/internal/models/supplement.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
func (Supplement) TableName() string {
|
||||
return "supplements"
|
||||
}
|
||||
|
||||
type Supplement struct {
|
||||
BaseModel
|
||||
Name string `json:"name" db:"name"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Price int `json:"price" db:"price"`
|
||||
Image string `json:"image" db:"image"`
|
||||
Nutrients []Nutrient `json:"nutrients" db:"nutrients" gorm:"many2many:supplement_nutrients;"`
|
||||
SupplementNutrients []SupplementNutrient `json:"supplementNutrients" db:"supplementNutrients" gorm:"foreignKey:SupplementID"`
|
||||
}
|
||||
|
||||
type DailySupplementsOverview struct {
|
||||
Supplements []*Supplement `json:"supplements"`
|
||||
Nutrients []*Nutrient `json:"nutrients"`
|
||||
Overview []*SupplementNutrientOverview `json:"overview"`
|
||||
}
|
||||
22
server/internal/models/supplement_nutrient.go
Normal file
22
server/internal/models/supplement_nutrient.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
func (SupplementNutrient) TableName() string {
|
||||
return "supplement_nutrients"
|
||||
}
|
||||
|
||||
type SupplementNutrient struct {
|
||||
BaseModel
|
||||
SupplementID ULID `json:"supplementId" db:"supplement_id"`
|
||||
NutrientID ULID `json:"nutrientId" db:"nutrient_id"`
|
||||
ServingSize string `json:"servingSize" db:"serving_size"`
|
||||
PerServing string `json:"perServing" db:"per_serving"`
|
||||
PerServingReferenceIntake string `json:"perServingReferenceIntake" db:"per_serving_reference_intake"`
|
||||
}
|
||||
|
||||
type SupplementNutrientOverview struct {
|
||||
SupplementID ULID `json:"supplementId" db:"supplement_id"`
|
||||
NutrientID ULID `json:"nutrientId" db:"nutrient_id"`
|
||||
ServingSize string `json:"servingSize" db:"serving_size"`
|
||||
PerServing string `json:"perServing" db:"per_serving"`
|
||||
PerServingReferenceIntake string `json:"perServingReferenceIntake" db:"per_serving_reference_intake"`
|
||||
}
|
||||
56
server/internal/models/todo.go
Normal file
56
server/internal/models/todo.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func (Todo) TableName() string {
|
||||
return "todos"
|
||||
}
|
||||
|
||||
type Todo struct {
|
||||
BaseModel
|
||||
UserID ULID `json:"userId" db:"user_id" gorm:"not null"`
|
||||
Title string `json:"title" db:"title" gorm:"not null"`
|
||||
Description string `json:"description" db:"description"`
|
||||
Color string `json:"color" db:"color" gorm:"default:'#3B82F6'"` // Default blue color
|
||||
IsActive bool `json:"isActive" db:"is_active" gorm:"default:true"`
|
||||
|
||||
// Relationships
|
||||
User User `json:"user" gorm:"foreignKey:UserID"`
|
||||
Completions []TodoCompletion `json:"completions" gorm:"foreignKey:TodoID"`
|
||||
}
|
||||
|
||||
func (TodoCompletion) TableName() string {
|
||||
return "todo_completions"
|
||||
}
|
||||
|
||||
type TodoCompletion struct {
|
||||
BaseModel
|
||||
TodoID ULID `json:"todoId" db:"todo_id" gorm:"not null"`
|
||||
UserID ULID `json:"userId" db:"user_id" gorm:"not null"`
|
||||
CompletedAt time.Time `json:"completedAt" db:"completed_at" gorm:"not null"`
|
||||
Description string `json:"description" db:"description"` // User's notes about how they completed it
|
||||
|
||||
// Relationships
|
||||
Todo Todo `json:"todo" gorm:"foreignKey:TodoID"`
|
||||
User User `json:"user" gorm:"foreignKey:UserID"`
|
||||
}
|
||||
|
||||
// TodoWithStats represents a todo with completion statistics
|
||||
type TodoWithStats struct {
|
||||
Todo
|
||||
CurrentStreak int `json:"currentStreak"`
|
||||
LongestStreak int `json:"longestStreak"`
|
||||
TotalCompletions int `json:"totalCompletions"`
|
||||
LastCompletedAt *time.Time `json:"lastCompletedAt"`
|
||||
CompletedToday bool `json:"completedToday"`
|
||||
}
|
||||
|
||||
// DailyTodoSummary represents todos for a specific day
|
||||
type DailyTodoSummary struct {
|
||||
Date time.Time `json:"date"`
|
||||
Todos []TodoWithStats `json:"todos"`
|
||||
CompletedCount int `json:"completedCount"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
}
|
||||
141
server/internal/models/ulid.go
Normal file
141
server/internal/models/ulid.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/oklog/ulid/v2"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
// ULID is a custom type that wraps oklog/ulid/v2.ULID and implements
|
||||
// GORM's Scanner and Valuer interfaces for database operations
|
||||
type ULID struct {
|
||||
ulid.ULID
|
||||
}
|
||||
|
||||
// NewULID creates a new ULID from a ulid.ULID
|
||||
func NewULID(u ulid.ULID) ULID {
|
||||
return ULID{ULID: u}
|
||||
}
|
||||
|
||||
// ParseULID parses a string into a ULID
|
||||
func ParseULID(s string) (ULID, error) {
|
||||
u, err := ulid.Parse(s)
|
||||
if err != nil {
|
||||
return ULID{}, err
|
||||
}
|
||||
return ULID{ULID: u}, nil
|
||||
}
|
||||
|
||||
// MustParseULID parses a string into a ULID and panics on error
|
||||
func MustParseULID(s string) ULID {
|
||||
u, err := ParseULID(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// GenerateULID creates a new ULID with the current timestamp
|
||||
func GenerateULID() ULID {
|
||||
return ULID{ULID: ulid.MustNew(ulid.Timestamp(time.Now()), rand.Reader)}
|
||||
}
|
||||
|
||||
// GenerateULIDWithTime creates a new ULID with the specified timestamp
|
||||
func GenerateULIDWithTime(t time.Time) ULID {
|
||||
return ULID{ULID: ulid.MustNew(ulid.Timestamp(t), rand.Reader)}
|
||||
}
|
||||
|
||||
// Scan implements the sql.Scanner interface for reading from database
|
||||
func (u *ULID) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*u = ULID{}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
parsed, err := ulid.Parse(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse ULID from string: %w", err)
|
||||
}
|
||||
u.ULID = parsed
|
||||
return nil
|
||||
case []byte:
|
||||
parsed, err := ulid.Parse(string(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse ULID from bytes: %w", err)
|
||||
}
|
||||
u.ULID = parsed
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("cannot scan %T into ULID", value)
|
||||
}
|
||||
}
|
||||
|
||||
// Value implements the driver.Valuer interface for writing to database
|
||||
func (u ULID) Value() (driver.Value, error) {
|
||||
if u.ULID == (ulid.ULID{}) {
|
||||
return nil, nil
|
||||
}
|
||||
return u.ULID.String(), nil
|
||||
}
|
||||
|
||||
// GormDataType returns the data type for GORM
|
||||
func (ULID) GormDataType() string {
|
||||
return "char(26)"
|
||||
}
|
||||
|
||||
// GormDBDataType returns the database-specific data type for GORM
|
||||
func (ULID) GormDBDataType(db *gorm.DB, field *schema.Field) string {
|
||||
switch db.Dialector.Name() {
|
||||
case "postgres":
|
||||
return "char(26)"
|
||||
case "mysql":
|
||||
return "char(26)"
|
||||
case "sqlite":
|
||||
return "text"
|
||||
default:
|
||||
return "char(26)"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (u ULID) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + u.ULID.String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (u *ULID) UnmarshalJSON(data []byte) error {
|
||||
if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' {
|
||||
return fmt.Errorf("invalid JSON string for ULID")
|
||||
}
|
||||
|
||||
str := string(data[1 : len(data)-1])
|
||||
if str == "" {
|
||||
*u = ULID{}
|
||||
return nil
|
||||
}
|
||||
|
||||
parsed, err := ulid.Parse(str)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse ULID from JSON: %w", err)
|
||||
}
|
||||
|
||||
u.ULID = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string representation of the ULID
|
||||
func (u ULID) String() string {
|
||||
return u.ULID.String()
|
||||
}
|
||||
|
||||
// IsZero returns true if the ULID is zero value
|
||||
func (u ULID) IsZero() bool {
|
||||
return u.ULID == ulid.ULID{}
|
||||
}
|
||||
25
server/internal/models/user.go
Executable file
25
server/internal/models/user.go
Executable file
@@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type PlatformRole string
|
||||
|
||||
const (
|
||||
PlatformRoleUser PlatformRole = "USER"
|
||||
PlatformRoleAdmin PlatformRole = "ADMIN"
|
||||
)
|
||||
|
||||
func (User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID []byte `gorm:"primaryKey;type:bytea" json:"id" db:"id"`
|
||||
CreatedAt time.Time `json:"createdAt" db:"created_at"`
|
||||
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"`
|
||||
FirstName string `json:"firstName" db:"first_name"`
|
||||
LastName string `json:"lastName" db:"last_name"`
|
||||
Email string `gorm:"uniqueIndex;not null" json:"email" db:"email"`
|
||||
Password string `gorm:"not null" json:"password" db:"password"`
|
||||
PlatformRole PlatformRole `gorm:"not null" json:"platformRole" db:"platform_role"`
|
||||
}
|
||||
Reference in New Issue
Block a user