Show pending account invites and polish sign editor recovery
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { and, desc, eq, gt, isNull, lt, or } from "drizzle-orm";
|
||||
import {
|
||||
auditEvents,
|
||||
eventMemberships,
|
||||
@@ -55,6 +55,34 @@ async function validateInviteGrants(userId: string, input: {
|
||||
}
|
||||
|
||||
export const groupRouter = createTRPCRouter({
|
||||
pendingInvites: protectedProcedure
|
||||
.input(z.object({ groupId: z.string().uuid(), eventId: z.string().uuid().optional() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const platformRole = await getPlatformRole(ctx.session.user.id);
|
||||
if (input.eventId) {
|
||||
const { event, access } = await loadEventAccess(ctx.session.user.id, input.eventId, platformRole);
|
||||
if (event.groupId !== input.groupId) throw new TRPCError({ code: "NOT_FOUND" });
|
||||
requireEventPermission(access.permissions, EVENT_PERMISSIONS.PEOPLE_MANAGE);
|
||||
} else {
|
||||
const { access } = await loadGroupAccess(ctx.session.user.id, input.groupId, platformRole);
|
||||
requireGroupPermission(access.permissions, GROUP_PERMISSIONS.PEOPLE_MANAGE);
|
||||
}
|
||||
return getDb().select({
|
||||
id: invites.id, kind: invites.kind, email: invites.email,
|
||||
groupRole: invites.groupRole, eventRole: invites.eventRole,
|
||||
eventId: invites.eventId, eventTitle: events.title,
|
||||
reusable: invites.reusable, usedCount: invites.usedCount, maxUses: invites.maxUses,
|
||||
createdAt: invites.createdAt, expiresAt: invites.expiresAt,
|
||||
}).from(invites).leftJoin(events, and(eq(events.id, invites.eventId), eq(events.groupId, input.groupId)))
|
||||
.where(and(
|
||||
eq(invites.groupId, input.groupId),
|
||||
input.eventId ? eq(invites.eventId, input.eventId) : undefined,
|
||||
eq(invites.status, "pending"),
|
||||
or(isNull(invites.expiresAt), gt(invites.expiresAt, new Date())),
|
||||
lt(invites.usedCount, invites.maxUses),
|
||||
)).orderBy(desc(invites.createdAt));
|
||||
}),
|
||||
|
||||
list: protectedProcedure.query(async ({ ctx }) => {
|
||||
return getDb()
|
||||
.select({
|
||||
|
||||
@@ -28,6 +28,18 @@ test.skipIf(process.env.POLISH_INTEGRATION !== "1")("invitation isolation, atomi
|
||||
const [used] = await db.select().from(invites).where(eq(invites.tokenHash, hashToken(token)));
|
||||
expect(used!.usedCount).toBe(1);
|
||||
const workspace = groupRouter.createCaller(ctx(0));
|
||||
const [pending] = await db.insert(invites).values({ kind: "email", email: people[1]!.email, tokenHash: hashToken(crypto.randomUUID()), groupId: group!.id, eventId: event!.id, eventRole: "manager" }).returning();
|
||||
await db.insert(invites).values([
|
||||
{ kind: "email", tokenHash: hashToken(crypto.randomUUID()), groupId: group!.id, expiresAt: new Date(0) },
|
||||
{ kind: "email", tokenHash: hashToken(crypto.randomUUID()), groupId: group!.id, usedCount: 1, maxUses: 1 },
|
||||
{ kind: "email", tokenHash: hashToken(crypto.randomUUID()), groupId: group!.id, status: "revoked" },
|
||||
]);
|
||||
const listed = await workspace.pendingInvites({ groupId: group!.id, eventId: event!.id });
|
||||
expect(listed.map(invite => invite.id)).toEqual([pending!.id]);
|
||||
expect(listed[0]).not.toHaveProperty("tokenHash");
|
||||
expect(await workspace.pendingInvites({ groupId: group!.id })).toHaveLength(1);
|
||||
await expect(groupRouter.createCaller(ctx(1)).pendingInvites({ groupId: group!.id })).rejects.toThrow();
|
||||
await expect(workspace.pendingInvites({ groupId: crypto.randomUUID(), eventId: event!.id })).rejects.toThrow();
|
||||
await expect(workspace.createCode({ groupId: group!.id, grantUnlimitedEvents: true })).rejects.toThrow("platform administrators");
|
||||
const signs = signsRouter.createCaller(ctx(0));
|
||||
await expect(signsRouter.createCaller(ctx(1)).prepare({ eventId: event!.id })).rejects.toThrow();
|
||||
|
||||
Reference in New Issue
Block a user