Initial implementation of mcq — document reading queue

Single-binary service: push raw markdown via REST/gRPC API, read rendered
HTML through mobile-friendly web UI. MCIAS auth on all endpoints, SQLite
storage, goldmark rendering with GFM and syntax highlighting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 11:53:26 -07:00
commit bc1627915e
36 changed files with 3773 additions and 0 deletions

121
internal/db/documents.go Normal file
View File

@@ -0,0 +1,121 @@
package db
import (
"database/sql"
"errors"
"time"
)
// ErrNotFound is returned when a document does not exist.
var ErrNotFound = errors.New("db: not found")
// Document represents a queued document.
type Document struct {
ID int64 `json:"id"`
Slug string `json:"slug"`
Title string `json:"title"`
Body string `json:"body"`
PushedBy string `json:"pushed_by"`
PushedAt string `json:"pushed_at"`
Read bool `json:"read"`
}
// ListDocuments returns all documents ordered by most recently pushed.
func (d *DB) ListDocuments() ([]Document, error) {
rows, err := d.Query(`SELECT id, slug, title, body, pushed_by, pushed_at, read FROM documents ORDER BY pushed_at DESC`)
if err != nil {
return nil, err
}
defer rows.Close()
var docs []Document
for rows.Next() {
var doc Document
if err := rows.Scan(&doc.ID, &doc.Slug, &doc.Title, &doc.Body, &doc.PushedBy, &doc.PushedAt, &doc.Read); err != nil {
return nil, err
}
docs = append(docs, doc)
}
return docs, rows.Err()
}
// GetDocument returns a single document by slug.
func (d *DB) GetDocument(slug string) (*Document, error) {
var doc Document
err := d.QueryRow(
`SELECT id, slug, title, body, pushed_by, pushed_at, read FROM documents WHERE slug = ?`,
slug,
).Scan(&doc.ID, &doc.Slug, &doc.Title, &doc.Body, &doc.PushedBy, &doc.PushedAt, &doc.Read)
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
}
if err != nil {
return nil, err
}
return &doc, nil
}
// PutDocument creates or updates a document by slug (upsert).
func (d *DB) PutDocument(slug, title, body, pushedBy string) (*Document, error) {
now := time.Now().UTC().Format(time.RFC3339)
_, err := d.Exec(`
INSERT INTO documents (slug, title, body, pushed_by, pushed_at, read)
VALUES (?, ?, ?, ?, ?, 0)
ON CONFLICT(slug) DO UPDATE SET
title = excluded.title,
body = excluded.body,
pushed_by = excluded.pushed_by,
pushed_at = excluded.pushed_at,
read = 0`,
slug, title, body, pushedBy, now,
)
if err != nil {
return nil, err
}
return d.GetDocument(slug)
}
// DeleteDocument removes a document by slug.
func (d *DB) DeleteDocument(slug string) error {
res, err := d.Exec(`DELETE FROM documents WHERE slug = ?`, slug)
if err != nil {
return err
}
n, err := res.RowsAffected()
if err != nil {
return err
}
if n == 0 {
return ErrNotFound
}
return nil
}
// MarkRead sets the read flag on a document.
func (d *DB) MarkRead(slug string) (*Document, error) {
return d.setRead(slug, true)
}
// MarkUnread clears the read flag on a document.
func (d *DB) MarkUnread(slug string) (*Document, error) {
return d.setRead(slug, false)
}
func (d *DB) setRead(slug string, read bool) (*Document, error) {
val := 0
if read {
val = 1
}
res, err := d.Exec(`UPDATE documents SET read = ? WHERE slug = ?`, val, slug)
if err != nil {
return nil, err
}
n, err := res.RowsAffected()
if err != nil {
return nil, err
}
if n == 0 {
return nil, ErrNotFound
}
return d.GetDocument(slug)
}