Files
sleepy/lib/db.ts

87 lines
3.1 KiB
TypeScript

import Database from 'better-sqlite3';
import { mkdirSync } from 'node:fs';
import { join } from 'node:path';
import type { SleepEntry } from './sleep';
const dataDirectory = process.env.DATA_DIR ?? join(process.cwd(), 'data');
mkdirSync(dataDirectory, { recursive: true });
const database = new Database(join(dataDirectory, 'sleepy.db'));
database.pragma('journal_mode = WAL');
database.exec(`
CREATE TABLE IF NOT EXISTS sleep_entries (
date TEXT PRIMARY KEY NOT NULL,
napped INTEGER NOT NULL DEFAULT 0,
nap_start TEXT,
nap_duration_minutes INTEGER,
bedtime TEXT NOT NULL,
sleep_latency_minutes INTEGER NOT NULL,
awakenings INTEGER NOT NULL,
waso_minutes INTEGER NOT NULL,
rise_time TEXT NOT NULL,
alarm_type TEXT NOT NULL,
rating INTEGER NOT NULL,
notes TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`);
type SleepEntryRow = Omit<SleepEntry, 'napped' | 'napStart' | 'napDurationMinutes' | 'sleepLatencyMinutes' | 'wasoMinutes' | 'riseTime' | 'alarmType' | 'createdAt' | 'updatedAt'> & {
napped: number;
nap_start: string | null;
nap_duration_minutes: number | null;
sleep_latency_minutes: number;
waso_minutes: number;
rise_time: string;
alarm_type: SleepEntry['alarmType'];
created_at: string;
updated_at: string;
};
function mapEntry(row: SleepEntryRow): SleepEntry {
return {
date: row.date,
napped: Boolean(row.napped),
napStart: row.nap_start,
napDurationMinutes: row.nap_duration_minutes,
bedtime: row.bedtime,
sleepLatencyMinutes: row.sleep_latency_minutes,
awakenings: row.awakenings,
wasoMinutes: row.waso_minutes,
riseTime: row.rise_time,
alarmType: row.alarm_type,
rating: row.rating,
notes: row.notes,
createdAt: row.created_at,
updatedAt: row.updated_at,
};
}
export function getEntry(date: string): SleepEntry | null {
const row = database.prepare('SELECT * FROM sleep_entries WHERE date = ?').get(date) as SleepEntryRow | undefined;
return row ? mapEntry(row) : null;
}
export function saveEntry(entry: SleepEntry): SleepEntry {
database.prepare(`
INSERT INTO sleep_entries (
date, napped, nap_start, nap_duration_minutes, bedtime, sleep_latency_minutes,
awakenings, waso_minutes, rise_time, alarm_type, rating, notes
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(date) DO UPDATE SET
napped = excluded.napped, nap_start = excluded.nap_start,
nap_duration_minutes = excluded.nap_duration_minutes, bedtime = excluded.bedtime,
sleep_latency_minutes = excluded.sleep_latency_minutes, awakenings = excluded.awakenings,
waso_minutes = excluded.waso_minutes, rise_time = excluded.rise_time,
alarm_type = excluded.alarm_type, rating = excluded.rating, notes = excluded.notes,
updated_at = CURRENT_TIMESTAMP
`).run(
entry.date, Number(entry.napped), entry.napStart, entry.napDurationMinutes, entry.bedtime,
entry.sleepLatencyMinutes, entry.awakenings, entry.wasoMinutes, entry.riseTime,
entry.alarmType, entry.rating, entry.notes,
);
return getEntry(entry.date) ?? entry;
}