From 3c838ddb26c1e86d3f201ce71f13c834be705f69 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Wed, 4 Feb 2026 09:44:18 +0000 Subject: feat: Import workflow v3 (#2378) * feat: import workflow v3 * batch stage * revert migration * cleanups * pr comments * move to models * add allowed workers * e2e tests * import list ids * add missing indicies * merge test * more fixes * add resume/pause to UI * fix ui states * fix tests * simplify progress tracking * remove backpressure * fix list imports * fix race on claiming bookmarks * remove the codex file --- packages/db/schema.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) (limited to 'packages/db/schema.ts') diff --git a/packages/db/schema.ts b/packages/db/schema.ts index 8ed91e28..217456c4 100644 --- a/packages/db/schema.ts +++ b/packages/db/schema.ts @@ -833,10 +833,19 @@ export const importSessions = sqliteTable( rootListId: text("rootListId").references(() => bookmarkLists.id, { onDelete: "set null", }), + status: text("status", { + enum: ["staging", "pending", "running", "paused", "completed", "failed"], + }) + .notNull() + .default("staging"), + lastProcessedAt: integer("lastProcessedAt", { mode: "timestamp" }), createdAt: createdAtField(), modifiedAt: modifiedAtField(), }, - (is) => [index("importSessions_userId_idx").on(is.userId)], + (is) => [ + index("importSessions_userId_idx").on(is.userId), + index("importSessions_status_idx").on(is.status), + ], ); export const importSessionBookmarks = sqliteTable( @@ -861,6 +870,63 @@ export const importSessionBookmarks = sqliteTable( ], ); +export const importStagingBookmarks = sqliteTable( + "importStagingBookmarks", + { + id: text("id") + .notNull() + .primaryKey() + .$defaultFn(() => createId()), + importSessionId: text("importSessionId") + .notNull() + .references(() => importSessions.id, { onDelete: "cascade" }), + + // Bookmark data to create + type: text("type", { enum: ["link", "text", "asset"] }).notNull(), + url: text("url"), + title: text("title"), + content: text("content"), + note: text("note"), + tags: text("tags", { mode: "json" }).$type(), + listIds: text("listIds", { mode: "json" }).$type(), + sourceAddedAt: integer("sourceAddedAt", { mode: "timestamp" }), + + // Processing state + status: text("status", { + enum: ["pending", "processing", "completed", "failed"], + }) + .notNull() + .default("pending"), + processingStartedAt: integer("processingStartedAt", { + mode: "timestamp", + }), + + // Result (for observability) + result: text("result", { + enum: ["accepted", "rejected", "skipped_duplicate"], + }), + resultReason: text("resultReason"), + resultBookmarkId: text("resultBookmarkId").references(() => bookmarks.id, { + onDelete: "set null", + }), + + createdAt: createdAtField(), + completedAt: integer("completedAt", { mode: "timestamp" }), + }, + (isb) => [ + index("importStaging_session_status_idx").on( + isb.importSessionId, + isb.status, + ), + index("importStaging_completedAt_idx").on(isb.completedAt), + index("importStaging_status_idx").on(isb.status), + index("importStaging_status_processingStartedAt_idx").on( + isb.status, + isb.processingStartedAt, + ), + ], +); + // Relations export const userRelations = relations(users, ({ many, one }) => ({ -- cgit v1.2.3-70-g09d2