chore: snapshot comment authors before user deletion
This commit is contained in:
parent
846e575637
commit
63d6a65334
6 changed files with 319 additions and 5976 deletions
|
|
@ -33,6 +33,9 @@ const LEGACY_STATUS_MAP: Record<string, TicketStatusNormalized> = {
|
|||
CLOSED: "RESOLVED",
|
||||
};
|
||||
|
||||
const missingRequesterLogCache = new Set<string>();
|
||||
const missingCommentAuthorLogCache = new Set<string>();
|
||||
|
||||
function normalizeStatus(status: string | null | undefined): TicketStatusNormalized {
|
||||
if (!status) return "PENDING";
|
||||
const normalized = LEGACY_STATUS_MAP[status.toUpperCase()];
|
||||
|
|
@ -140,9 +143,13 @@ function buildRequesterSummary(
|
|||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
const ticketInfo = context?.ticketId ? ` (ticket ${String(context.ticketId)})` : "";
|
||||
console.warn(
|
||||
`[tickets] requester ${idString} ausente ao hidratar resposta${ticketInfo}; usando placeholders.`,
|
||||
);
|
||||
const cacheKey = `${idString}:${context?.ticketId ? String(context.ticketId) : "unknown"}`;
|
||||
if (!missingRequesterLogCache.has(cacheKey)) {
|
||||
missingRequesterLogCache.add(cacheKey);
|
||||
console.warn(
|
||||
`[tickets] requester ${idString} ausente ao hidratar resposta${ticketInfo}; usando placeholders.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -153,6 +160,76 @@ function buildRequesterSummary(
|
|||
};
|
||||
}
|
||||
|
||||
type CommentAuthorFallbackContext = {
|
||||
ticketId?: Id<"tickets">;
|
||||
commentId?: Id<"ticketComments">;
|
||||
};
|
||||
|
||||
type CommentAuthorSnapshot = {
|
||||
name: string;
|
||||
email?: string;
|
||||
avatarUrl?: string;
|
||||
teams?: string[];
|
||||
};
|
||||
|
||||
export function buildCommentAuthorSummary(
|
||||
comment: Doc<"ticketComments">,
|
||||
author: Doc<"users"> | null,
|
||||
context?: CommentAuthorFallbackContext,
|
||||
) {
|
||||
if (author) {
|
||||
return {
|
||||
id: author._id,
|
||||
name: author.name,
|
||||
email: author.email,
|
||||
avatarUrl: author.avatarUrl,
|
||||
teams: normalizeTeams(author.teams),
|
||||
};
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "test") {
|
||||
const ticketInfo = context?.ticketId ? ` (ticket ${String(context.ticketId)})` : "";
|
||||
const commentInfo = context?.commentId ? ` (comentário ${String(context.commentId)})` : "";
|
||||
const cacheKeyParts = [String(comment.authorId), context?.ticketId ? String(context.ticketId) : "unknown"];
|
||||
if (context?.commentId) cacheKeyParts.push(String(context.commentId));
|
||||
const cacheKey = cacheKeyParts.join(":");
|
||||
if (!missingCommentAuthorLogCache.has(cacheKey)) {
|
||||
missingCommentAuthorLogCache.add(cacheKey);
|
||||
console.warn(
|
||||
`[tickets] autor ${String(comment.authorId)} ausente ao hidratar comentário${ticketInfo}${commentInfo}; usando placeholders.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const idString = String(comment.authorId);
|
||||
const fallbackName = "Usuário removido";
|
||||
const fallbackEmail = `author-${idString}@example.invalid`;
|
||||
const snapshot = comment.authorSnapshot as CommentAuthorSnapshot | undefined;
|
||||
if (snapshot) {
|
||||
const name =
|
||||
typeof snapshot.name === "string" && snapshot.name.trim().length > 0
|
||||
? snapshot.name.trim()
|
||||
: fallbackName;
|
||||
const emailCandidate =
|
||||
typeof snapshot.email === "string" && snapshot.email.includes("@") ? snapshot.email : null;
|
||||
const email = emailCandidate ?? fallbackEmail;
|
||||
return {
|
||||
id: comment.authorId,
|
||||
name,
|
||||
email,
|
||||
avatarUrl: snapshot.avatarUrl ?? undefined,
|
||||
teams: normalizeTeams(snapshot.teams ?? []),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
id: comment.authorId,
|
||||
name: fallbackName,
|
||||
email: fallbackEmail,
|
||||
teams: [],
|
||||
};
|
||||
}
|
||||
|
||||
type CustomFieldInput = {
|
||||
fieldId: Id<"ticketFields">;
|
||||
value: unknown;
|
||||
|
|
@ -536,15 +613,13 @@ export const getById = query({
|
|||
url: await ctx.storage.getUrl(att.storageId),
|
||||
}))
|
||||
);
|
||||
const authorSummary = buildCommentAuthorSummary(c, author, {
|
||||
ticketId: t._id,
|
||||
commentId: c._id,
|
||||
});
|
||||
return {
|
||||
id: c._id,
|
||||
author: {
|
||||
id: author!._id,
|
||||
name: author!.name,
|
||||
email: author!.email,
|
||||
avatarUrl: author!.avatarUrl,
|
||||
teams: author!.teams ?? [],
|
||||
},
|
||||
author: authorSummary,
|
||||
visibility: c.visibility,
|
||||
body: c.body,
|
||||
attachments,
|
||||
|
|
@ -842,12 +917,20 @@ export const addComment = mutation({
|
|||
}
|
||||
}
|
||||
|
||||
const authorSnapshot: CommentAuthorSnapshot = {
|
||||
name: author.name,
|
||||
email: author.email,
|
||||
avatarUrl: author.avatarUrl ?? undefined,
|
||||
teams: author.teams ?? undefined,
|
||||
};
|
||||
|
||||
const now = Date.now();
|
||||
const id = await ctx.db.insert("ticketComments", {
|
||||
ticketId: args.ticketId,
|
||||
authorId: args.authorId,
|
||||
visibility: requestedVisibility,
|
||||
body: args.body,
|
||||
authorSnapshot,
|
||||
attachments,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue