feat: add threaded comments, activity feeds, and task dependencies
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { db, activityFeed, comments, tasks } from "@project-e/db";
|
||||
import { and, asc, eq, isNull } from "drizzle-orm";
|
||||
|
||||
// Backfill legacy comments from activity_feed (entity_type='comment') into the
|
||||
// new dedicated comments table. Comments were only ever created for tasks, so
|
||||
// every row maps to entity_type='task' with no parent.
|
||||
//
|
||||
// Idempotent: rows that already exist in `comments` (same entity_id + content +
|
||||
// created_at) are skipped, so this script is safe to re-run.
|
||||
|
||||
try {
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(activityFeed)
|
||||
.where(eq(activityFeed.entityType, "comment"))
|
||||
.orderBy(asc(activityFeed.createdAt));
|
||||
|
||||
let inserted = 0;
|
||||
let alreadyPresent = 0;
|
||||
let missingContent = 0;
|
||||
let orphaned = 0;
|
||||
|
||||
for (const row of rows) {
|
||||
const content = row.changes?.content;
|
||||
if (typeof content !== "string" || content.trim() === "") {
|
||||
missingContent++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only backfill comments whose task still exists (skip soft-deleted/absent).
|
||||
const [task] = await db
|
||||
.select({ id: tasks.id })
|
||||
.from(tasks)
|
||||
.where(and(eq(tasks.id, row.entityId), isNull(tasks.deletedAt)))
|
||||
.limit(1);
|
||||
|
||||
if (!task) {
|
||||
orphaned++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Idempotency guard: skip if this comment was already migrated.
|
||||
const [existing] = await db
|
||||
.select({ id: comments.id })
|
||||
.from(comments)
|
||||
.where(and(
|
||||
eq(comments.entityId, row.entityId),
|
||||
eq(comments.content, content),
|
||||
eq(comments.createdAt, row.createdAt),
|
||||
))
|
||||
.limit(1);
|
||||
|
||||
if (existing) {
|
||||
alreadyPresent++;
|
||||
continue;
|
||||
}
|
||||
|
||||
await db.insert(comments).values({
|
||||
entityType: "task",
|
||||
entityId: row.entityId,
|
||||
workspaceId: row.workspaceId,
|
||||
author: row.actor,
|
||||
content,
|
||||
createdAt: row.createdAt,
|
||||
parentId: null,
|
||||
deletedAt: null,
|
||||
});
|
||||
|
||||
inserted++;
|
||||
}
|
||||
|
||||
console.log(`[backfill-comments] rows found: ${rows.length}`);
|
||||
console.log(`[backfill-comments] inserted: ${inserted}`);
|
||||
console.log(`[backfill-comments] skipped (already present): ${alreadyPresent}`);
|
||||
console.log(`[backfill-comments] skipped (missing content): ${missingContent}`);
|
||||
console.log(`[backfill-comments] skipped (orphaned entity): ${orphaned}`);
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("[backfill-comments] failed:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user