feat: implement feed agg and browse

This commit is contained in:
Stevan Freeborn
2026-08-03 07:14:33 -05:00
parent f51b1b941a
commit 667f5f112b
12 changed files with 444 additions and 40 deletions
+54 -11
View File
@@ -7,31 +7,34 @@ package database
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const createFeed = `-- name: CreateFeed :one
INSERT INTO feeds (id, created_at, updated_at, name, url, user_id)
INSERT INTO feeds (id, created_at, updated_at, name, url, user_id, last_fetched_at)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6
$6,
$7
)
RETURNING id, created_at, updated_at, name, url, user_id
RETURNING id, created_at, updated_at, name, url, user_id, last_fetched_at
`
type CreateFeedParams struct {
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Name string
Url string
UserID uuid.UUID
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Name string
Url string
UserID uuid.UUID
LastFetchedAt sql.NullTime
}
func (q *Queries) CreateFeed(ctx context.Context, arg CreateFeedParams) (Feed, error) {
@@ -42,6 +45,7 @@ func (q *Queries) CreateFeed(ctx context.Context, arg CreateFeedParams) (Feed, e
arg.Name,
arg.Url,
arg.UserID,
arg.LastFetchedAt,
)
var i Feed
err := row.Scan(
@@ -51,12 +55,13 @@ func (q *Queries) CreateFeed(ctx context.Context, arg CreateFeedParams) (Feed, e
&i.Name,
&i.Url,
&i.UserID,
&i.LastFetchedAt,
)
return i, err
}
const getAllFeeds = `-- name: GetAllFeeds :many
SELECT id, created_at, updated_at, name, url, user_id FROM feeds
SELECT id, created_at, updated_at, name, url, user_id, last_fetched_at FROM feeds
`
func (q *Queries) GetAllFeeds(ctx context.Context) ([]Feed, error) {
@@ -75,6 +80,7 @@ func (q *Queries) GetAllFeeds(ctx context.Context) ([]Feed, error) {
&i.Name,
&i.Url,
&i.UserID,
&i.LastFetchedAt,
); err != nil {
return nil, err
}
@@ -90,7 +96,7 @@ func (q *Queries) GetAllFeeds(ctx context.Context) ([]Feed, error) {
}
const getFeedByUrl = `-- name: GetFeedByUrl :one
SELECT id, created_at, updated_at, name, url, user_id FROM feeds
SELECT id, created_at, updated_at, name, url, user_id, last_fetched_at FROM feeds
WHERE url = $1
`
@@ -104,6 +110,43 @@ func (q *Queries) GetFeedByUrl(ctx context.Context, url string) (Feed, error) {
&i.Name,
&i.Url,
&i.UserID,
&i.LastFetchedAt,
)
return i, err
}
const getNextFeedToFetch = `-- name: GetNextFeedToFetch :one
SELECT id, created_at, updated_at, name, url, user_id, last_fetched_at FROM feeds
ORDER BY last_fetched_at ASC NULLS FIRST
`
func (q *Queries) GetNextFeedToFetch(ctx context.Context) (Feed, error) {
row := q.db.QueryRowContext(ctx, getNextFeedToFetch)
var i Feed
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Name,
&i.Url,
&i.UserID,
&i.LastFetchedAt,
)
return i, err
}
const markFeedAsFetched = `-- name: MarkFeedAsFetched :exec
UPDATE feeds
SET last_fetched_at = $1
WHERE feeds.id = $2
`
type MarkFeedAsFetchedParams struct {
LastFetchedAt sql.NullTime
ID uuid.UUID
}
func (q *Queries) MarkFeedAsFetched(ctx context.Context, arg MarkFeedAsFetchedParams) error {
_, err := q.db.ExecContext(ctx, markFeedAsFetched, arg.LastFetchedAt, arg.ID)
return err
}
+19 -6
View File
@@ -5,18 +5,20 @@
package database
import (
"database/sql"
"time"
"github.com/google/uuid"
)
type Feed struct {
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Name string
Url string
UserID uuid.UUID
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Name string
Url string
UserID uuid.UUID
LastFetchedAt sql.NullTime
}
type Follow struct {
@@ -27,6 +29,17 @@ type Follow struct {
FeedID uuid.UUID
}
type Post struct {
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Title string
Url string
Description string
PublishedAt sql.NullTime
FeedID uuid.UUID
}
type User struct {
ID uuid.UUID
CreatedAt time.Time
+136
View File
@@ -0,0 +1,136 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: posts.sql
package database
import (
"context"
"database/sql"
"time"
"github.com/google/uuid"
)
const createPost = `-- name: CreatePost :one
INSERT INTO posts (id, created_at, updated_at, title, url, description, published_at, feed_id)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8
)
RETURNING id, created_at, updated_at, title, url, description, published_at, feed_id
`
type CreatePostParams struct {
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Title string
Url string
Description string
PublishedAt sql.NullTime
FeedID uuid.UUID
}
func (q *Queries) CreatePost(ctx context.Context, arg CreatePostParams) (Post, error) {
row := q.db.QueryRowContext(ctx, createPost,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.Title,
arg.Url,
arg.Description,
arg.PublishedAt,
arg.FeedID,
)
var i Post
err := row.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Title,
&i.Url,
&i.Description,
&i.PublishedAt,
&i.FeedID,
)
return i, err
}
const getPostsForUser = `-- name: GetPostsForUser :many
SELECT
posts.id,
posts.created_at,
posts.updated_at,
posts.title,
posts.url,
posts.description,
posts.published_at,
posts.feed_id,
feeds.name as feed_name
FROM posts
INNER JOIN feeds
ON posts.feed_id = feeds.id
INNER JOIN follows
ON posts.feed_id = follows.feed_id
WHERE follows.user_id = $1
ORDER BY posts.published_at DESC, posts.title ASC
LIMIT $2
`
type GetPostsForUserParams struct {
UserID uuid.UUID
Limit int32
}
type GetPostsForUserRow struct {
ID uuid.UUID
CreatedAt time.Time
UpdatedAt time.Time
Title string
Url string
Description string
PublishedAt sql.NullTime
FeedID uuid.UUID
FeedName string
}
func (q *Queries) GetPostsForUser(ctx context.Context, arg GetPostsForUserParams) ([]GetPostsForUserRow, error) {
rows, err := q.db.QueryContext(ctx, getPostsForUser, arg.UserID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetPostsForUserRow
for rows.Next() {
var i GetPostsForUserRow
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.UpdatedAt,
&i.Title,
&i.Url,
&i.Description,
&i.PublishedAt,
&i.FeedID,
&i.FeedName,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}