Menu
Public repository / 1733e7c7f9cb6fd1bd4760c59b7727822aab6036
AkurAI-Notes / crates/notes-cli/src/cmd_seed.rs
Latest change 21fd83f4a6eb769a0ee34aa30f721b5cdc6881af - Fix IDP OAuth: split public issuer vs internal loopback URL for token exchange by Claude Code
use akurai_json::Value;
use notes_api::auth::idp_client::IdpConfig;
use notes_api::router_setup::NotesState;
pub fn run(db_path: &str) -> Result<(), String> {
println!("🌱 Seeding Notes database: {db_path}");
let frontend_dir = std::path::PathBuf::from("site/frontend");
let state = NotesState::new(db_path, frontend_dir, IdpConfig::from_env())?;
let mut db = state.db.lock().map_err(|e| format!("lock: {e}"))?;
let owner_sub = "olibuijr@olibuijr.com";
let mut insert = |entity: &str, id: u64, fields: Vec<(&str, Value)>| -> Result<(), String> {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs() as i64;
let mut obj = Vec::new();
obj.push(("id".into(), Value::Int(id as i64)));
obj.push(("createdAt".into(), Value::Int(now)));
obj.push(("updatedAt".into(), Value::Int(now)));
for (k, v) in fields {
obj.push((k.to_string(), v));
}
let record = Value::Object(obj);
let mut key = format!("{}:", entity).into_bytes();
key.extend_from_slice(&id.to_be_bytes());
let json = record.to_json();
db.insert(&key, json.as_bytes())
.map_err(|e| format!("insert {entity}:{id}: {e}"))
};
// Tags
insert(
"tags",
1,
vec![
("name", Value::Str("important".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#ef4444".into())),
],
)?;
insert(
"tags",
2,
vec![
("name", Value::Str("todo".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#f59e0b".into())),
],
)?;
insert(
"tags",
3,
vec![
("name", Value::Str("meeting".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#8b5cf6".into())),
],
)?;
insert(
"tags",
4,
vec![
("name", Value::Str("idea".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#22c55e".into())),
],
)?;
insert(
"tags",
5,
vec![
("name", Value::Str("reference".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#3b82f6".into())),
],
)?;
insert(
"tags",
6,
vec![
("name", Value::Str("archive".into())),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#64748b".into())),
],
)?;
println!(" ✓ 6 tags created");
// Notebooks
insert(
"notebooks",
1,
vec![
("name", Value::Str("Personal".into())),
(
"description",
Value::Str("Personal notes and journal entries".into()),
),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#5b8cff".into())),
],
)?;
insert(
"notebooks",
2,
vec![
("name", Value::Str("Work".into())),
(
"description",
Value::Str("Work-related notes and projects".into()),
),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#f59e0b".into())),
],
)?;
insert(
"notebooks",
3,
vec![
("name", Value::Str("Ideas".into())),
(
"description",
Value::Str("Random ideas and inspiration".into()),
),
("ownerSub", Value::Str(owner_sub.into())),
("color", Value::Str("#22c55e".into())),
],
)?;
println!(" ✓ 3 notebooks created");
// Notes — 12 total across 3 notebooks
insert("notes", 1, vec![
("title", Value::Str("Weekly review — w24".into())),
("body", Value::Str("Completed the Q2 planning doc. Key outcomes: revenue target of $2.5M, 3 new hires approved, product launch pushed to August. Need to follow up with legal on the partnership agreement.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(2)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("1,3".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 2, vec![
("title", Value::Str("Book notes — The Mom Test".into())),
("body", Value::Str("Key insight: people lie to be nice. Don't ask 'would you buy this?' — ask about their current behavior. Real signal comes from commitments: time, money, reputation. Three rules: talk about their life (not your idea), ask about specifics (not hypotheticals), listen for generics (they're hiding the truth).".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(1)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("5".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 3, vec![
("title", Value::Str("Product idea — AI meeting summarizer".into())),
("body", Value::Str("A Slack bot that joins meetings, transcribes them, and posts a summary + action items to the thread. Key differentiator: it learns each team's categories and formats the output to match. Could monetize per-seat or per-meeting.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(3)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("4".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 4, vec![
("title", Value::Str("Meeting notes — All-hands 2026-06-01".into())),
("body", Value::Str("CEO announced restructuring: engineering splits into platform and product teams. Timeline is Q3. No layoffs — mostly re-org. New CTO starts July 1. Action: update my team's OKRs to reflect the new structure.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(2)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("1,3".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 5, vec![
("title", Value::Str("Journal — June 15".into())),
("body", Value::Str("Woke up early, went for a run by the lake. Felt good. Reading 'Deep Work' by Cal Newport — the argument that deep concentration is becoming a稀缺 resource resonates. Need to block 3 hours every morning for focused work.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(1)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str(String::new())),
("archived", Value::Bool(false)),
])?;
insert("notes", 6, vec![
("title", Value::Str("Architecture sketch — offline-first sync".into())),
("body", Value::Str("CRDT-based sync engine. Clients have local SQLite + remote BTree. Two ops: push local changes (with vector clock) and pull remote changes (with merge). Conflict resolution: last-write-wins for scalar fields, CRDT merge for arrays. Design doc needed before implementation.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(3)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("4,5".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 7, vec![
("title", Value::Str("Grocery list".into())),
("body", Value::Str("Milk, eggs, bread, avocados, chicken, broccoli, rice, olive oil, coffee filters, dark chocolate.".into())),
("format", Value::Str("plain".into())),
("notebookId", Value::Int(1)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("2".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 8, vec![
("title", Value::Str("OKR review — Q2".into())),
("body", Value::Str("KR1: Ship v0.1 of the API — on track. KR2: 10 beta users — 7 signed up, need 3 more. KR3: Blog post about the architecture — drafted, needs editing. Overall: yellow (needs attention on KR2).".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(2)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("1".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 9, vec![
("title", Value::Str("Deep Work — chapter summaries".into())),
("body", Value::Str("Ch 1: Deep work is rare because it's hard and because the culture of connectivity rewards shallow work. Ch 2: The deep life is a good life — meaning comes from attention. Ch 3: Decide on your depth philosophy (monastic, bimodal, rhythmic, journalistic).".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(1)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("5".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 10, vec![
("title", Value::Str("Startup idea — developer billing API".into())),
("body", Value::Str("Stripe for B2B SaaS billing. Companies waste weeks building invoicing, dunning, metered billing. API-first, usage-based pricing, Stripe Connect under the hood. Target market: early-stage B2B SaaS. Revenue model: 1% + $0.10 per transaction.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(3)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("4".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 11, vec![
("title", Value::Str("Performance review prep".into())),
("body", Value::Str("Achievements this half: shipped the collections API (E04), documented the deployment workflow, mentored 2 juniors. Areas to improve: code review turnaround time, async communication in Slack. Goals for next half: lead the vector search work (E05), write 3 ADRs.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(2)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("1".into())),
("archived", Value::Bool(false)),
])?;
insert("notes", 12, vec![
("title", Value::Str("Old notes — project archive".into())),
("body", Value::Str("These are notes from the previous project that are no longer relevant but kept for reference.".into())),
("format", Value::Str("markdown".into())),
("notebookId", Value::Int(1)),
("ownerSub", Value::Str(owner_sub.into())),
("tagIds", Value::Str("6".into())),
("archived", Value::Bool(true)),
])?;
println!(" ✓ 12 notes created");
println!("✅ Database seeded successfully!");
Ok(())
}