package store import ( "context" "encoding/json" "time" "github.com/jackc/pgx/v5" ) type Blog struct { ID int64 OwnerID int64 Subdomain string Title string Tagline string ThemeJSON json.RawMessage CreatedAt time.Time UpdatedAt time.Time } const blogCols = `id, owner_id, subdomain, title, tagline, theme, created_at, updated_at` func scanBlog(row interface{ Scan(...any) error }) (*Blog, error) { var b Blog err := row.Scan(&b.ID, &b.OwnerID, &b.Subdomain, &b.Title, &b.Tagline, &b.ThemeJSON, &b.CreatedAt, &b.UpdatedAt) if err != nil { return nil, wrap(err) } return &b, nil } // CreateBlogger creates a user, their blog and a default home page in one transaction. func (s *Store) CreateBlogger(ctx context.Context, username, passwordHash, subdomain, title string) (*User, *Blog, error) { tx, err := s.db.Begin(ctx) if err != nil { return nil, nil, err } defer tx.Rollback(ctx) u, err := scanUser(tx.QueryRow(ctx, `INSERT INTO users (username, password_hash, role) VALUES ($1,$2,$3) RETURNING `+userCols, username, passwordHash, RoleBlogger)) if err != nil { return nil, nil, err } b, err := createBlog(ctx, tx, u.ID, subdomain, title) if err != nil { return nil, nil, err } if err := tx.Commit(ctx); err != nil { return nil, nil, err } return u, b, nil } // CreateBlog creates a blog with a default home page for an existing user. func (s *Store) CreateBlog(ctx context.Context, ownerID int64, subdomain, title string) (*Blog, error) { tx, err := s.db.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) b, err := createBlog(ctx, tx, ownerID, subdomain, title) if err != nil { return nil, err } return b, tx.Commit(ctx) } func createBlog(ctx context.Context, tx pgx.Tx, ownerID int64, subdomain, title string) (*Blog, error) { b, err := scanBlog(tx.QueryRow(ctx, `INSERT INTO blogs (owner_id, subdomain, title) VALUES ($1,$2,$3) RETURNING `+blogCols, ownerID, subdomain, title)) if err != nil { return nil, err } _, err = tx.Exec(ctx, `INSERT INTO pages (blog_id, slug, title, nav_order, is_home) VALUES ($1,'home','Home',0,true)`, b.ID) if err != nil { return nil, wrap(err) } return b, nil } func (s *Store) BlogByID(ctx context.Context, id int64) (*Blog, error) { return scanBlog(s.db.QueryRow(ctx, `SELECT `+blogCols+` FROM blogs WHERE id=$1`, id)) } func (s *Store) BlogBySubdomain(ctx context.Context, sub string) (*Blog, error) { return scanBlog(s.db.QueryRow(ctx, `SELECT `+blogCols+` FROM blogs WHERE subdomain=$1`, sub)) } func (s *Store) BlogByOwner(ctx context.Context, ownerID int64) (*Blog, error) { return scanBlog(s.db.QueryRow(ctx, `SELECT `+blogCols+` FROM blogs WHERE owner_id=$1`, ownerID)) } func (s *Store) UpdateBlogSettings(ctx context.Context, id int64, title, tagline string) error { _, err := s.db.Exec(ctx, `UPDATE blogs SET title=$2, tagline=$3, updated_at=now() WHERE id=$1`, id, title, tagline) return err } func (s *Store) UpdateBlogTheme(ctx context.Context, id int64, theme json.RawMessage) error { _, err := s.db.Exec(ctx, `UPDATE blogs SET theme=$2, updated_at=now() WHERE id=$1`, id, theme) return err } func (s *Store) DeleteBlog(ctx context.Context, id int64) error { _, err := s.db.Exec(ctx, `DELETE FROM blogs WHERE id=$1`, id) return err }