Menu
Public repository / master
AkurAI-Notes / crates/notes-cli/src/cmd_seed.rs
Latest change 7f7b543549487ff8ff6802fb420feff1a197c507 - Checkpoint WIP + add AkurAI Build CI/CD pipeline by Γlafur BΓΊi Γlafsson
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)),
])?;
// Dev-only session so the web UI can be exercised locally without the OAuth
// round-trip. Present only in manually-seeded databases; never created by the
// deploy path. Use cookie `notes_session=dev` (or Authorization: Bearer dev).
{
let session = Value::Object(vec![
("sub".into(), Value::Str(owner_sub.into())),
("email".into(), Value::Str(owner_sub.into())),
("createdAt".into(), Value::Int(0)),
]);
db.insert(b"_session:dev", session.to_json().as_bytes())
.map_err(|e| format!("insert _session:dev: {e}"))?;
println!(" β dev session created (cookie notes_session=dev)");
}
db.commit().map_err(|e| format!("commit: {e}"))?;
println!(" β 12 notes created");
println!("β
Database seeded successfully!");
Ok(())
}