9e9124ecb0
Build and Deploy / build-and-deploy (push) Successful in 56s
Enables users to set a podcast contact email from the dashboard settings. This email is included in the RSS feed as itunes:owner/itunes:email, which podcast directories like Spotify and Apple Podcasts require for indexing and verification.
196 lines
5.7 KiB
Go
196 lines
5.7 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type User struct {
|
|
ID int64
|
|
Email string
|
|
PasswordHash string
|
|
PodcastTitle string
|
|
PodcastSlug string
|
|
PodcastImage string
|
|
PodcastAuthor string
|
|
PodcastEmail string
|
|
CreatedAt string
|
|
}
|
|
|
|
type Episode struct {
|
|
ID int64
|
|
UserID int64
|
|
Title string
|
|
Description string
|
|
AudioPath string
|
|
ImagePath string
|
|
PublishedAt string
|
|
CreatedAt string
|
|
}
|
|
|
|
type Store struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func New(path string) (*Store, error) {
|
|
db, err := sql.Open("sqlite", path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db.SetMaxOpenConns(1)
|
|
|
|
if err := migrate(db); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Store{db: db}, nil
|
|
}
|
|
|
|
func migrate(db *sql.DB) error {
|
|
schema := `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
podcast_title TEXT NOT NULL DEFAULT '',
|
|
podcast_slug TEXT NOT NULL DEFAULT '',
|
|
podcast_image TEXT NOT NULL DEFAULT '',
|
|
podcast_author TEXT NOT NULL DEFAULT '',
|
|
podcast_email TEXT NOT NULL DEFAULT '',
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS episodes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
title TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
audio_path TEXT NOT NULL DEFAULT '',
|
|
image_path TEXT NOT NULL DEFAULT '',
|
|
published_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`
|
|
_, err := db.Exec(schema)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) Close() error {
|
|
return s.db.Close()
|
|
}
|
|
|
|
// --- Users ---
|
|
|
|
func (s *Store) UserCount() (int, error) {
|
|
var n int
|
|
err := s.db.QueryRow("SELECT COUNT(*) FROM users").Scan(&n)
|
|
return n, err
|
|
}
|
|
|
|
func (s *Store) CreateUser(email, passwordHash string) (int64, error) {
|
|
res, err := s.db.Exec("INSERT INTO users (email, password_hash) VALUES (?, ?)", email, passwordHash)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.LastInsertId()
|
|
}
|
|
|
|
func (s *Store) UserByEmail(email string) (*User, error) {
|
|
u := &User{}
|
|
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, podcast_email, created_at FROM users WHERE email = ?", email).
|
|
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.PodcastEmail, &u.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *Store) GetUser(id int64) (*User, error) {
|
|
u := &User{}
|
|
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, podcast_email, created_at FROM users WHERE id = ?", id).
|
|
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.PodcastEmail, &u.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *Store) UserBySlug(slug string) (*User, error) {
|
|
u := &User{}
|
|
err := s.db.QueryRow("SELECT id, email, password_hash, podcast_title, podcast_slug, podcast_image, podcast_author, podcast_email, created_at FROM users WHERE podcast_slug = ?", slug).
|
|
Scan(&u.ID, &u.Email, &u.PasswordHash, &u.PodcastTitle, &u.PodcastSlug, &u.PodcastImage, &u.PodcastAuthor, &u.PodcastEmail, &u.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (s *Store) SlugTaken(slug string, excludeUserID int64) (bool, error) {
|
|
var id int64
|
|
err := s.db.QueryRow("SELECT id FROM users WHERE podcast_slug = ? AND id != ?", slug, excludeUserID).Scan(&id)
|
|
if err == sql.ErrNoRows {
|
|
return false, nil
|
|
}
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (s *Store) UpdateUserPodcast(id int64, title, slug, image, author, email string) error {
|
|
_, err := s.db.Exec("UPDATE users SET podcast_title=?, podcast_slug=?, podcast_image=?, podcast_author=?, podcast_email=? WHERE id=?",
|
|
title, slug, image, author, email, id)
|
|
return err
|
|
}
|
|
|
|
// --- Episodes ---
|
|
|
|
func (s *Store) ListEpisodes(userID int64) ([]Episode, error) {
|
|
rows, err := s.db.Query("SELECT id, user_id, title, description, audio_path, image_path, published_at, created_at FROM episodes WHERE user_id = ? ORDER BY published_at DESC", userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var eps []Episode
|
|
for rows.Next() {
|
|
var e Episode
|
|
if err := rows.Scan(&e.ID, &e.UserID, &e.Title, &e.Description, &e.AudioPath, &e.ImagePath, &e.PublishedAt, &e.CreatedAt); err != nil {
|
|
return nil, err
|
|
}
|
|
eps = append(eps, e)
|
|
}
|
|
return eps, rows.Err()
|
|
}
|
|
|
|
func (s *Store) GetEpisode(id int64) (*Episode, error) {
|
|
e := &Episode{}
|
|
err := s.db.QueryRow("SELECT id, user_id, title, description, audio_path, image_path, published_at, created_at FROM episodes WHERE id = ?", id).
|
|
Scan(&e.ID, &e.UserID, &e.Title, &e.Description, &e.AudioPath, &e.ImagePath, &e.PublishedAt, &e.CreatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
func (s *Store) CreateEpisode(userID int64, title, description, audioPath, imagePath, publishedAt string) (int64, error) {
|
|
res, err := s.db.Exec("INSERT INTO episodes (user_id, title, description, audio_path, image_path, published_at) VALUES (?, ?, ?, ?, ?, ?)",
|
|
userID, title, description, audioPath, imagePath, publishedAt)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.LastInsertId()
|
|
}
|
|
|
|
func (s *Store) UpdateEpisode(id int64, title, description, audioPath, imagePath, publishedAt string) error {
|
|
_, err := s.db.Exec("UPDATE episodes SET title=?, description=?, audio_path=?, image_path=?, published_at=? WHERE id=?",
|
|
title, description, audioPath, imagePath, publishedAt, id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) DeleteEpisode(id int64) error {
|
|
_, err := s.db.Exec("DELETE FROM episodes WHERE id = ?", id)
|
|
return err
|
|
}
|