Files
documenso/packages/trpc/server/signature-router/qr/get-qr-signature.ts
2026-08-18 11:49:15 +10:00

58 lines
1.5 KiB
TypeScript

import { prisma } from '@documenso/prisma';
import { AnonymousVerificationTokenType } from '@prisma/client';
import { procedure } from '../../trpc';
import { ZGetQrSignatureRequestSchema, ZGetQrSignatureResponseSchema } from './get-qr-signature.types';
/**
* NOTE: THIS IS A PUBLIC (UNAUTHENTICATED) PROCEDURE.
*
* Polled by the desktop signature pad while waiting for a mobile signature.
*
* A missing row is indistinguishable from an expired one by design, so we
* return EXPIRED for both. Once the signature is returned the row is deleted,
* making the token single-use.
*/
export const getQrSignatureRoute = procedure
.input(ZGetQrSignatureRequestSchema)
.output(ZGetQrSignatureResponseSchema)
.query(async ({ input }) => {
const { token } = input;
const qrSignatureSession = await prisma.anonymousVerificationToken.findUnique({
where: {
token,
type: AnonymousVerificationTokenType.QR_SIGNATURE,
},
});
if (!qrSignatureSession || qrSignatureSession.expiresAt < new Date()) {
return {
status: 'EXPIRED',
} as const;
}
if (!qrSignatureSession.value) {
return {
status: 'PENDING',
} as const;
}
const { count: deletedCount } = await prisma.anonymousVerificationToken.deleteMany({
where: {
id: qrSignatureSession.id,
},
});
if (deletedCount === 0) {
return {
status: 'EXPIRED',
} as const;
}
return {
status: 'COMPLETED',
signature: qrSignatureSession.value,
} as const;
});