| 43 | } |
| 44 | |
| 45 | return null; |
| 46 | } |
| 47 | |
| 48 | export class UserNotesDurableObject extends DurableObject { |
| 49 | private sql: SqlStorage; |
| 50 | private docs = new Map<string, Y.Doc>(); |
| 51 | private saveTimers = new Map<string, ReturnType<typeof setTimeout>>(); |
| 52 | private bucket: R2Bucket; |
| 53 | |
| 54 | constructor(ctx: DurableObjectState, env: Env) { |
| 55 | super(ctx, env); |
| 56 | this.sql = ctx.storage.sql; |
| 57 | this.bucket = env.MEDIA_BUCKET; |
| 58 | this.ctx.blockConcurrencyWhile(async () => { |
| 59 | this.sql.exec(` |
| 60 | CREATE TABLE IF NOT EXISTS notes ( |
| 61 | id TEXT PRIMARY KEY, |
| 62 | title TEXT NOT NULL DEFAULT 'Untitled', |
| 63 | content TEXT NOT NULL DEFAULT '', |
| 64 | yjs_state BLOB, |
| 65 | folder_id TEXT, |
| 66 | sync_mode TEXT NOT NULL DEFAULT 'cloud', |
| 67 | created_at INTEGER NOT NULL DEFAULT (unixepoch()), |
| 68 | updated_at INTEGER NOT NULL DEFAULT (unixepoch()) |
| 69 | ) |
| 70 | `); |
| 71 | this.sql.exec(` |
| 72 | CREATE TABLE IF NOT EXISTS folders ( |
| 73 | id TEXT PRIMARY KEY, |
| 74 | name TEXT NOT NULL, |
| 75 | color TEXT NOT NULL DEFAULT '#8A8473', |
| 76 | description TEXT NOT NULL DEFAULT '', |
| 77 | sort_order INTEGER NOT NULL DEFAULT 0, |
| 78 | created_at INTEGER DEFAULT (unixepoch()), |
| 79 | updated_at INTEGER DEFAULT (unixepoch()) |
| 80 | ) |
| 81 | `); |
| 82 | // Migrations for existing tables — only suppress "duplicate column" errors |
| 83 | const migrate = (sql: string) => { |
| 84 | try { this.sql.exec(sql); } catch (e: any) { |
| 85 | if (!e.message?.includes("duplicate column")) throw e; |
| 86 | } |
| 87 | }; |
| 88 | migrate("ALTER TABLE notes ADD COLUMN yjs_state BLOB"); |
| 89 | migrate("ALTER TABLE notes ADD COLUMN folder_id TEXT"); |
| 90 | migrate("ALTER TABLE notes ADD COLUMN sync_mode TEXT NOT NULL DEFAULT 'cloud'"); |
| 91 | migrate("ALTER TABLE notes ADD COLUMN locked INTEGER NOT NULL DEFAULT 0"); |
| 92 | migrate("ALTER TABLE notes ADD COLUMN published INTEGER NOT NULL DEFAULT 0"); |
| 93 | migrate("ALTER TABLE notes ADD COLUMN published_at INTEGER"); |
| 94 | migrate("ALTER TABLE folders ADD COLUMN description TEXT NOT NULL DEFAULT ''"); |
| 95 | |
| 96 | // Git-style versioning: checkpoints (full content) + patches (diffs) |
| 97 | this.sql.exec(` |
| 98 | CREATE TABLE IF NOT EXISTS note_versions ( |
| 99 | id TEXT PRIMARY KEY, |
| 100 | note_id TEXT NOT NULL, |
| 101 | parent_id TEXT, |
| 102 | title TEXT NOT NULL, |
nothing calls this directly
no outgoing calls
no test coverage detected