package model import ( "time" "github.com/google/uuid" "gorm.io/gorm" ) // Base gives every persisted model a PostgreSQL uuid primary key. // GORM's naming strategy maps Go fields to snake_case columns. type Base struct { ID string `gorm:"type:uuid;primaryKey" json:"id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } func (b *Base) BeforeCreate(_ *gorm.DB) error { if b.ID == "" { b.ID = uuid.NewString() } return nil } type File struct { Base Bucket string `gorm:"size:128;not null" json:"bucket"` ObjectKey string `gorm:"size:512;uniqueIndex;not null" json:"object_key"` MD5 string `gorm:"column:md5;size:32;uniqueIndex;not null" json:"md5"` URL string `gorm:"size:1024;not null" json:"url"` Name string `gorm:"size:255;not null" json:"name"` ContentType string `gorm:"size:128;not null" json:"content_type"` Size int64 `gorm:"not null" json:"size"` } type User struct { Base OpenID string `gorm:"size:128;uniqueIndex;not null" json:"-"` Nickname string `gorm:"size:64;not null" json:"nickname"` AvatarFileID *string `gorm:"type:uuid;index" json:"avatar_file_id"` Disabled bool `gorm:"not null;default:false" json:"disabled"` } type Admin struct { Base Username string `gorm:"size:64;uniqueIndex;not null" json:"username"` PasswordHash string `gorm:"size:255;not null" json:"-"` } type Pet struct { Base UserID string `gorm:"type:uuid;index;not null" json:"user_id"` Name string `gorm:"size:64;not null" json:"name"` Species string `gorm:"size:16;not null;default:dog" json:"species"` Breed string `gorm:"size:64" json:"breed"` Gender string `gorm:"size:16" json:"gender"` BirthDate *time.Time `gorm:"type:date" json:"birth_date"` WeightKg float64 `gorm:"type:numeric(6,2)" json:"weight_kg"` AvatarFileID *string `gorm:"type:uuid;index" json:"avatar_file_id"` Coat string `gorm:"size:64" json:"coat"` Allergies string `gorm:"size:255" json:"allergies"` CareNotes string `gorm:"size:1000" json:"care_notes"` Archived bool `gorm:"not null;default:false" json:"archived"` } type CareTask struct { Base UserID string `gorm:"type:uuid;index;not null" json:"user_id"` PetID string `gorm:"type:uuid;index;not null" json:"pet_id"` Title string `gorm:"size:128;not null" json:"title"` Category string `gorm:"size:32;not null" json:"category"` TimeOfDay string `gorm:"size:5;not null" json:"time_of_day"` RepeatRule string `gorm:"size:16;not null;default:daily" json:"repeat_rule"` ReminderEnabled bool `gorm:"not null;default:false" json:"reminder_enabled"` } type TaskCompletion struct { Base TaskID string `gorm:"type:uuid;uniqueIndex:idx_task_date;not null" json:"task_id"` UserID string `gorm:"type:uuid;index;not null" json:"user_id"` Date time.Time `gorm:"type:date;uniqueIndex:idx_task_date;not null" json:"date"` CompletedAt time.Time `gorm:"not null" json:"completed_at"` } type Walk struct { Base UserID string `gorm:"type:uuid;index;not null" json:"user_id"` Status string `gorm:"size:16;index;not null" json:"status"` StartedAt time.Time `gorm:"not null" json:"started_at"` SegmentStartedAt time.Time `gorm:"not null" json:"segment_started_at"` PausedAt *time.Time `json:"paused_at"` PausedSeconds int `gorm:"not null;default:0" json:"paused_seconds"` EndedAt *time.Time `json:"ended_at"` DurationSeconds int `gorm:"not null;default:0" json:"duration_seconds"` DistanceMeters float64 `gorm:"type:numeric(12,2);not null;default:0" json:"distance_meters"` ShareEnabled bool `gorm:"not null;default:false" json:"share_enabled"` ShareToken string `gorm:"type:uuid;uniqueIndex" json:"-"` ShareExpiresAt *time.Time `json:"share_expires_at"` } type WalkPet struct { Base WalkID string `gorm:"type:uuid;uniqueIndex:idx_walk_pet;not null" json:"walk_id"` PetID string `gorm:"type:uuid;uniqueIndex:idx_walk_pet;index;not null" json:"pet_id"` } type WalkPoint struct { Base WalkID string `gorm:"type:uuid;index;not null" json:"walk_id"` Latitude float64 `gorm:"type:numeric(10,7);not null" json:"latitude"` Longitude float64 `gorm:"type:numeric(10,7);not null" json:"longitude"` RecordedAt time.Time `gorm:"index;not null" json:"recorded_at"` } type Meetup struct { Base HostUserID string `gorm:"type:uuid;index;not null" json:"host_user_id"` Title string `gorm:"size:128;not null" json:"title"` Description string `gorm:"size:1000" json:"description"` PlaceName string `gorm:"size:255;not null" json:"place_name"` Latitude float64 `gorm:"type:numeric(10,7)" json:"latitude"` Longitude float64 `gorm:"type:numeric(10,7)" json:"longitude"` StartsAt time.Time `gorm:"index;not null" json:"starts_at"` MaxPeople int `gorm:"not null;default:8" json:"max_people"` Status string `gorm:"size:16;index;not null;default:open" json:"status"` CoverFileID *string `gorm:"type:uuid;index" json:"cover_file_id"` } type MeetupParticipant struct { Base MeetupID string `gorm:"type:uuid;uniqueIndex:idx_meetup_user;not null" json:"meetup_id"` UserID string `gorm:"type:uuid;uniqueIndex:idx_meetup_user;index;not null" json:"user_id"` } type MeetupPet struct { Base ParticipantID string `gorm:"type:uuid;uniqueIndex:idx_participant_pet;not null" json:"participant_id"` PetID string `gorm:"type:uuid;uniqueIndex:idx_participant_pet;index;not null" json:"pet_id"` } func All() []any { return []any{ &File{}, &User{}, &Admin{}, &Pet{}, &CareTask{}, &TaskCompletion{}, &Walk{}, &WalkPet{}, &WalkPoint{}, &Meetup{}, &MeetupParticipant{}, &MeetupPet{}, } }