▍ humdrum codex / soft
4.9 KB raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// 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<string, unknown>;
}

// 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();