o134's picture
Upload full TeamTasker system with all fixes
8314cf4 verified
Raw
History Blame Contribute Delete
5.13 kB
import { Router } from "express";
import { AuthRequest, requireAuth } from "../middleware/auth.js";
import { canAccessTask, User } from "../lib/auth-utils.js";
import { db } from "@workspace/db";
import { commentsTable, usersTable, notificationsTable, tasksTable } from "@workspace/db";
import { eq } from "drizzle-orm";
import { ListTaskCommentsParams } from "@workspace/api-zod";
import { logAudit } from "../lib/audit-logger.js";
import { z } from "zod";
const router = Router();
router.get("/tasks/:id/comments", requireAuth, async (req: AuthRequest, res) => {
try {
const user = req.user as User;
if (!user) { res.status(401).json({ error: "Unauthorized" }); return; }
const { id } = ListTaskCommentsParams.parse({ id: Number(req.params.id) });
const [task] = await db.select().from(tasksTable).where(eq(tasksTable.id, id));
if (!task) { res.status(404).json({ error: "Task not found" }); return; }
if (!canAccessTask(user, task)) {
res.status(403).json({ error: "Forbidden: You do not have access to this task's comments" });
return;
}
const comments = await db.select().from(commentsTable)
.where(eq(commentsTable.taskId, id))
.orderBy(commentsTable.createdAt);
const userIds = [...new Set(comments.map(c => c.userId).filter(Boolean))] as number[];
const users = userIds.length > 0
? await db.select().from(usersTable)
.then(all => all.filter(u => userIds.includes(u.id)))
: [];
const userMap = Object.fromEntries(users.map(u => [u.id, { ...u, createdAt: u.createdAt.toISOString() }]));
res.json(comments.map(c => ({
...c,
createdAt: c.createdAt.toISOString(),
user: c.userId ? userMap[c.userId] ?? null : null,
})));
} catch (err) {
req.log.error({ err }, "Failed to list comments");
res.status(500).json({ error: "Internal server error" });
}
});
const createCommentSchema = z.object({
taskId: z.coerce.number().int().positive("معرف المهمة غير صالح"),
content: z.string().trim().min(1, "محتوى التعليق مطلوب"),
mentionedUserIds: z.array(z.coerce.number().int().positive("معرف المستخدم غير صالح")).optional(),
}).strict();
async function handleCreateComment(req: AuthRequest, res: any) {
try {
const requestingUser = req.user as User;
if (!requestingUser) { res.status(401).json({ error: "Unauthorized" }); return; }
const bodyData = req.params.id !== undefined ? { taskId: req.params.id, ...req.body } : req.body;
const parseResult = createCommentSchema.safeParse(bodyData);
if (!parseResult.success) {
res.status(400).json({ error: parseResult.error.errors[0].message });
return;
}
const { taskId, content, mentionedUserIds } = parseResult.data;
const [task] = await db.select().from(tasksTable).where(eq(tasksTable.id, taskId));
if (!task) { res.status(404).json({ error: "Task not found" }); return; }
if (!canAccessTask(requestingUser, task)) {
res.status(403).json({ error: "Forbidden: You do not have permission to comment on this task" });
return;
}
const [comment] = await db.insert(commentsTable).values({
taskId,
userId: requestingUser.id,
content,
}).returning();
logAudit({
userId: requestingUser.id,
action: "comment_created",
entityType: "comment",
entityId: comment.id,
details: {
commentId: comment.id,
taskId: comment.taskId,
content: comment.content,
mentionedUserIds: mentionedUserIds ?? [],
},
req,
});
if (task) {
const notifyUserIds = new Set<number>();
if (task.createdByUserId && task.createdByUserId !== requestingUser.id) notifyUserIds.add(task.createdByUserId);
if (task.assignedUserId && task.assignedUserId !== requestingUser.id) notifyUserIds.add(task.assignedUserId);
if (mentionedUserIds && mentionedUserIds.length > 0) {
mentionedUserIds.forEach(uid => { if (uid !== requestingUser.id) notifyUserIds.add(uid); });
}
if (notifyUserIds.size > 0) {
await db.insert(notificationsTable).values(
[...notifyUserIds].map(uid => ({
userId: uid,
taskId,
type: "comment_added",
message: `تعليق جديد على المهمة: "${task.title}"`,
}))
);
}
}
let commentAuthor = null;
if (comment.userId) {
const [u] = await db.select().from(usersTable).where(eq(usersTable.id, comment.userId));
if (u) commentAuthor = { ...u, createdAt: u.createdAt.toISOString() };
}
res.status(201).json({
...comment,
createdAt: comment.createdAt.toISOString(),
user: commentAuthor,
});
} catch (err) {
req.log.error({ err: err instanceof Error ? err.message : String(err) }, "Failed to create comment");
res.status(500).json({ error: "Internal server error" });
}
}
router.post("/tasks/:id/comments", requireAuth, handleCreateComment);
router.post("/comments", requireAuth, handleCreateComment);
export default router;