package core import ( "crypto/rand" "database/sql" "encoding/base64" "errors" "fmt" ) func PlanCreate(db *sql.DB, user *User, name string) (*Plan, error) { join_code := make([]byte, 32) _, err := rand.Read(join_code) if err != nil { return nil, err } var plan Plan = Plan{ Name: name, Owner: user.Username, JoinCode: base64.URLEncoding.EncodeToString(join_code), } row := db.QueryRow( "INSERT INTO plans(name, owner, description, join_jode) VALUES (?, ?, '', ?) RETURNING id", plan.Name, plan.Owner, plan.JoinCode, ) if err := row.Scan(&plan.Id); err != nil { return nil, err } return &plan, nil } func PlanGet(db *sql.DB, id uint) (*Plan, error) { plan := Plan{} row := db.QueryRow("SELECT name,owner,description,join_code FROM plans WHERE id=?", id) if err := row.Scan(&plan.Name, &plan.Owner, &plan.Description, &plan.JoinCode); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, fmt.Errorf("Unexpected database error: %w", err) } return &plan, nil } func (p *Plan) GetAllUsers(db *sql.DB) ([]Member, error) { members := []Member{} rows, err := db.Query("SELECT id,plan_id,name,user_id FROM members WHERE plan_id=?", p.Id) if err != nil { return members, err } defer rows.Close() for rows.Next() { var m Member rows.Scan(&m.Id, &m.PlanId, &m.Name, &m.UserId) members = append(members, m) } return members, err } func (p *Plan) IsMember(db *sql.DB, u *User) (bool, error) { var member_count int row := db.QueryRow("SELECT count(1) FROM members WHERE plan_id=? AND user_id=?", p.Id, u.Username) if err := row.Scan(&member_count); err != nil { return false, nil } return member_count == 1, nil } func (p *Plan) GetMember(db *sql.DB, u *User) (*Member, error) { var m Member row := db.QueryRow("SELECT id,plan_id,name,user_id FROM members WHERE plan_id=? AND user_id=?", p.Id, u.Username) if err := row.Scan(&m.Id, &m.PlanId, &m.Name, &m.UserId); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotMember } return nil, fmt.Errorf("Unable to check if user %s is member in plan %d: %w", u.Username, p.Id, err) } return &m, nil } func (p *Plan) HasNonUser(db *sql.DB, name string) (bool, error) { var member_count int row := db.QueryRow("SELECT count(1) FROM members WHERE plan_id=? AND name=?", p.Id, name) if err := row.Scan(&member_count); err != nil { return false, nil } return member_count == 1, nil } func (p *Plan) AddMember(db *sql.DB, new_member *Member) error { if new_member == nil { return errors.New("Member is nil") } new_member.PlanId = p.Id if new_member.Name != "" { found, err := p.HasNonUser(db, new_member.Name) if err != nil { return nil } if found { return errors.New("Non user name taken") } _, err = db.Exec("INSERT INTO members(plan_id, name) VALUES (?,?)", new_member.PlanId, new_member.Name) return err } else if *new_member.UserId != "" { user, err := UserGet(db, *new_member.UserId) if err != nil { return err } found, err := p.IsMember(db, &user) if err != nil { return nil } if found { return errors.New("User already is member") } _, err = db.Exec("INSERT INTO members(plan_id, user_id) VALUES (?,?)", new_member.PlanId, new_member.UserId) return err } else { return errors.New("Member object requires one of Name or UserID to be filled") } }