package core import ( "context" "database/sql" "fmt" "time" "github.com/google/uuid" "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/driver/pgdriver" ) var DB *bun.DB type DSN struct { Hostname string Port string DBName string User string Password string } func (dsn *DSN) ToString() string { return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable", dsn.User, dsn.Password, dsn.Hostname, dsn.Port, dsn.DBName) } type Role string const ( AdminRole Role = "admin" UserRole Role = "user" ) type Status string const ( Active Status = "Active" Inactive Status = "Inactive" ) type User struct { bun.BaseModel `bun:"table:users"` UserID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"userId"` FirstName string `bun:"firstname,notnull" json:"firstname"` LastName string `bun:"lastname,notnull" json:"lastname"` Email string `bun:"email,unique,notnull" json:"email"` Password string `bun:"password,notnull" json:"password,omitempty"` Phone string `bun:"phone,notnull" json:"phone"` Role Role `bun:"role,notnull,default:'user'" json:"role"` CreatedAt time.Time `bun:"created_at,default:current_timestamp" json:"createdAt"` UpdatedAt time.Time `bun:"updated_at,default:current_timestamp" json:"updatedAt"` Events []Event `bun:"m2m:events_to_users,join:User=Event" json:"events,omitempty"` Articles []*Blog `bun:"rel:has-many,join:user_id=blog_id" json:"articles,omitempty"` } func (u *User) Insert(ctx context.Context) (sql.Result, error) { u.Password = fmt.Sprintf("crypt('%s', gen_salt('bf'))", u.Password) return DB.NewInsert(). Model(u). Value("password", u.Password). Exec(ctx) } func Verify(ctx context.Context, email, password string) (*User, error) { // var user User // query := ` // SELECT * // FROM users // WHERE email = ? AND password = crypt(?, password) // ` // // err := DB.NewRaw(query, email, password).Scan(ctx, user) var user User count, err := DB.NewSelect(). Model(&user). Where("email = ? AND password = crypt(?, password)", email, password). Limit(1). ScanAndCount(context.Background()) if count == 0 { return nil, fmt.Errorf("invalid email or password") } if err != nil { if err == sql.ErrNoRows { return nil, fmt.Errorf("invalid email or password") } return nil, err } return &user, nil } type Event struct { bun.BaseModel `bun:"table:events"` EventID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"eventID"` CreationDate time.Time `bun:"creation_date,notnull,default:current_timestamp" json:"creationDate"` ScheduleStart time.Time `bun:"schedule_start,notnull" json:"scheduleStart"` ScheduleEnd time.Time `bun:"schedule_end,notnull" json:"scheduleEnd"` Status Status `bun:"status,notnull,default:Inactive" json:"status"` } type EventToUser struct { bun.BaseModel `bun:"table:events_to_users"` EventID uuid.UUID `bun:"type:uuid,pk"` UserID uuid.UUID `bun:"type:uuid,pk"` Event *Event `bun:"rel:belongs-to,join:event_id=event_id"` User *User `bun:"rel:belongs-to,join:user_id=user_id"` } type Blog struct { bun.BaseModel `bun:"table:blogs"` BlogID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"blogID"` Slug string `bun:"slug,unique,notnull" json:"slug"` Content string `bun:"content,notnull" json:"content"` Label string `bun:"label" json:"label"` AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` Published time.Time `bun:"published,default:current_timestamp" json:"published"` Summary string `bun:"summary" json:"summary"` Image string `bun:"image" json:"image"` Href string `bun:"href" json:"href"` Author User `bun:"rel:belongs-to,join:author_id=user_id" json:"author"` } type WebsiteSettings struct { bun.BaseModel `bun:"table:website_settings"` ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` AutoAcceptDemand bool `bun:"auto_accept_demand,default:false" json:"autoAcceptDemand"` } type Media struct { ID uuid.UUID `bun:"type:uuid,pk,default:gen_random_uuid()" json:"id"` AuthorID uuid.UUID `bun:"author_id,type:uuid,notnull" json:"authorID"` Author *User `bun:"rel:belongs-to,join:author_id=user_id" json:"author,omitempty"` Type string `bun:"media_type" json:"type"` // Image, Video, GIF etc. Add support for PDFs? Alt string `bun:"media_alt" json:"alt"` Path string `bun:"media_path" json:"path"` Size int64 `bun:"media_size" json:"size"` URL string `bun:"-" json:"url"` } func InitDatabase(dsn DSN) (*bun.DB, error) { sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dsn.ToString()))) db := bun.NewDB(sqldb, pgdialect.New()) ctx := context.Background() _, err := db.ExecContext(ctx, "CREATE EXTENSION IF NOT EXISTS pgcrypto;") if err != nil { return nil, err } db.RegisterModel((*EventToUser)(nil)) _, err = db.NewCreateTable().Model((*User)(nil)).IfNotExists().Exec(ctx) _, err = db.NewCreateTable().Model((*Event)(nil)).IfNotExists().Exec(ctx) _, err = db.NewCreateTable().Model((*EventToUser)(nil)).IfNotExists().Exec(ctx) _, err = db.NewCreateTable().Model((*Blog)(nil)).IfNotExists().Exec(ctx) _, err = db.NewCreateTable().Model((*WebsiteSettings)(nil)).IfNotExists().Exec(ctx) _, err = db.NewCreateTable().Model((*Media)(nil)).IfNotExists().Exec(ctx) if err != nil { return nil, err } return db, nil }