// scripts/seed.ts — ensure default sources + settings exist in the local db. // Idempotent per-kind: inserts any kind not already present. Safe to re-run. // Run: pnpm db:seed (uses relative imports so plain `tsx` resolves it) import { randomUUID } from "node:crypto"; import { db } from "../db/index"; import { sources, settings } from "../db/schema"; import { SETTINGS_DEFAULTS } from "../lib/schemas/setting"; const vault = process.env.OBSIDIAN_VAULT ?? "/Users/kortum/Humdrum"; interface Seed { kind: string; label: string; enabled: boolean; refreshSeconds: number; size: string; config: Record; } // Keyless / local sources start enabled; credentialed ones start disabled (enable // them in Settings once their .env.local keys are filled). const DEFAULTS: Seed[] = [ { kind: "weather", label: "Weather", enabled: true, refreshSeconds: 1800, size: "wide", config: {} }, { kind: "todos", label: "Today", enabled: true, refreshSeconds: 300, size: "tall", config: { list: "Today" } }, { kind: "calendar", label: "Calendar", enabled: true, refreshSeconds: 1800, size: "md", config: { mode: "local", calendars: [], urls: [], daysAhead: 7 } }, { kind: "onthisday", label: "On This Day", enabled: true, refreshSeconds: 86_400, size: "tall", config: {} }, { kind: "news", label: "News", enabled: true, refreshSeconds: 1800, size: "tall", config: { feeds: [ "https://hnrss.org/frontpage", "https://www.theverge.com/rss/index.xml", "https://feeds.arstechnica.com/arstechnica/index", "http://feeds.bbci.co.uk/news/world/rss.xml", "https://www.theguardian.com/world/rss", "https://feeds.npr.org/1001/rss.xml", ], limit: 14, }, }, // Screamer news briefs — replaces raw headlines in the paper. Keyless. { kind: "briefs", label: "News Briefs", enabled: true, refreshSeconds: 3600, size: "tall", config: { baseUrl: "https://screamer.humdrum.one", categories: [ { key: "politics", name: "Politics" }, { key: "tech", name: "Technology" }, { key: "sports", name: "Sports" }, { key: "culture", name: "Culture" }, ], lookbackDays: 7, }, }, // Personal RSS — starts empty; add your own feed URLs in Settings → My Feeds. { kind: "feeds", label: "My Feeds", enabled: true, refreshSeconds: 1800, size: "tall", config: { feeds: [], limit: 14 } }, { kind: "sports", label: "Scores", enabled: false, refreshSeconds: 600, size: "md", config: { leagues: [], teams: [] } }, { kind: "github", label: "GitHub", enabled: false, refreshSeconds: 900, size: "md", config: {} }, { kind: "vercel", label: "Vercel", enabled: false, refreshSeconds: 600, size: "md", config: {} }, { kind: "mastodon", label: "Mastodon", enabled: false, refreshSeconds: 600, size: "md", config: {} }, { kind: "bluesky", label: "Bluesky", enabled: false, refreshSeconds: 600, size: "md", config: {} }, // Keyless live-stat sources. Air quality + Hacker News work out of the box; // Markets seeds a starter watchlist; Uptime starts empty (add URLs in Settings). { kind: "aqi", label: "Air Quality", enabled: true, refreshSeconds: 3600, size: "md", config: {} }, { kind: "markets", label: "Markets", enabled: true, refreshSeconds: 900, size: "md", config: { symbols: ["AAPL", "NVDA", "BTC-USD", "ETH-USD"] }, }, { kind: "hackernews", label: "Hacker News", enabled: true, refreshSeconds: 1800, size: "tall", config: { limit: 10, subreddits: [] } }, { kind: "uptime", label: "Uptime", enabled: false, refreshSeconds: 600, size: "md", config: { urls: [] } }, { kind: "obsidian", label: "Obsidian", enabled: true, refreshSeconds: 600, size: "md", config: { vault, limit: 8 } }, { kind: "links", label: "Links", enabled: true, refreshSeconds: 86_400, size: "wide", config: { links: [] } }, // Now Playing panel was removed from the dashboard; keep the source seeded but // disabled so it isn't polled for a card that no longer exists. { kind: "cider", label: "Now Playing", enabled: false, refreshSeconds: 30, size: "md", config: { host: "http://localhost:10767", appToken: "" } }, ]; function main() { const existing = db.select().from(sources).all(); const present = new Set(existing.map((s) => s.kind)); let pos = existing.length; let added = 0; for (const s of DEFAULTS) { if (present.has(s.kind)) continue; db.insert(sources) .values({ id: randomUUID(), kind: s.kind, label: s.label, enabled: s.enabled, config: s.config, refreshSeconds: s.refreshSeconds, position: pos++, size: s.size, }) .run(); added++; } db.insert(settings) .values({ key: "settings", value: SETTINGS_DEFAULTS }) .onConflictDoNothing() .run(); console.log(added > 0 ? `Seeded ${added} new source(s).` : "All source kinds already present."); } main();