Show invited people the Rule, let them copy its cards for their community, and replace an expired invitation link.

This commit is contained in:
adilallo committed 2026-11-06 09:16:06 -07:00
1 parent 727a92a9ac
commit 3c984e00c5
37 files changed
+1962 -19

No files matched your search

+105
View File
@@ -0,0 +1,105 @@
import { NextResponse } from "next/server";
import { Prisma } from "@prisma/client";
import { prisma } from "../../../../../lib/server/db";
import { isDatabaseConfigured } from "../../../../../lib/server/env";
import {
dbUnavailable,
forbidden,
notFound,
rateLimited,
unauthorized,
} from "../../../../../lib/server/responses";
import { getSessionUser } from "../../../../../lib/server/session";
import { apiRoute } from "../../../../../lib/server/apiRoute";
import { rateLimitKey } from "../../../../../lib/server/rateLimit";
import { readLimitedJson } from "../../../../../lib/server/validation/requestBody";
import { jsonFromZodError } from "../../../../../lib/server/validation/zodHttp";
import {
makeCopyRuleBodySchema,
publishedRuleDocumentSchema,
} from "../../../../../lib/server/validation/createFlowSchemas";
import {
findCopiedNameHit,
stripCopiedRuleDocument,
} from "../../../../../lib/create/copiedRuleDocument";
type RouteContext = { params: Promise<{ id: string }> };
export const POST = apiRoute<RouteContext>(
"rules.byId.makeCopy",
async (request, context) => {
if (!isDatabaseConfigured()) {
return dbUnavailable();
}
const user = await getSessionUser();
if (!user) {
return unauthorized();
}
const { id } = await context.params;
const body = await readLimitedJson(request);
if (body.ok === false) return body.response;
const parsed = makeCopyRuleBodySchema.safeParse(body.value);
if (!parsed.success) return jsonFromZodError(parsed.error);
const rl = rateLimitKey(`rule-make-copy:${user.id}:${id}`, 60_000);
if (rl.ok === false) {
return rateLimited(rl.retryAfterMs);
}
const stakeholder = await prisma.ruleStakeholder.findFirst({
where: { ruleId: id, userId: user.id },
select: {
id: true,
rule: {
select: {
id: true,
title: true,
document: true,
userId: true,
},
},
},
});
if (!stakeholder) {
return notFound();
}
if (stakeholder.rule.userId === user.id) {
return forbidden();
}
const stripped = stripCopiedRuleDocument(stakeholder.rule.document);
const documentParsed = publishedRuleDocumentSchema.safeParse(stripped);
if (!documentParsed.success) {
return jsonFromZodError(documentParsed.error);
}
const copiedNameHit = findCopiedNameHit(
documentParsed.data,
stakeholder.rule.title,
);
const newRule = await prisma.publishedRule.create({
data: {
userId: user.id,
title: parsed.data.title,
summary: null,
document: documentParsed.data as Prisma.InputJsonValue,
},
});
return NextResponse.json({
rule: {
id: newRule.id,
title: newRule.title,
summary: newRule.summary,
document: documentParsed.data,
copiedFromTitle: stakeholder.rule.title,
copiedNameHit,
createdAt: newRule.createdAt,
updatedAt: newRule.updatedAt,
},
});
},
);