wip: refresh design

This commit is contained in:
Mythie
2023-06-09 18:21:18 +10:00
parent 5ec97657c1
commit 803ebccee3
432 changed files with 19755 additions and 26866 deletions

View File

@ -0,0 +1,128 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { randomUUID } from 'crypto';
import { hashSync } from '@documenso/lib/server-only/auth/hash';
import { redis } from '@documenso/lib/server-only/redis';
import { stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma';
import { TClaimPlanResponseSchema, ZClaimPlanRequestSchema } from '~/api/claim-plan/types';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<TClaimPlanResponseSchema>,
) {
try {
const { method } = req;
if (method?.toUpperCase() !== 'POST') {
return res.status(405).json({
error: 'Method not allowed',
});
}
const safeBody = ZClaimPlanRequestSchema.safeParse(req.body);
if (!safeBody.success) {
return res.status(400).json({
error: 'Bad request',
});
}
const { email, name, planId, signatureDataUrl, signatureText } = safeBody.data;
const user = await prisma.user.findFirst({
where: {
email: email.toLowerCase(),
},
include: {
Subscription: true,
},
});
if (user && user.Subscription.length > 0) {
return res.status(200).json({
// eslint-disable-next-line turbo/no-undeclared-env-vars
redirectUrl: `${process.env.NEXT_PUBLIC_APP_URL}/login`,
});
}
const password = Math.random().toString(36).slice(2, 9);
const passwordHash = hashSync(password);
const { id: userId } = await prisma.user.upsert({
where: {
email: email.toLowerCase(),
},
create: {
email: email.toLowerCase(),
name,
password: passwordHash,
},
update: {
name,
password: passwordHash,
},
});
await redis.set(`user:${userId}:temp-password`, password, {
// expire in 24 hours
ex: 60 * 60 * 24,
});
const signatureDataUrlKey = randomUUID();
if (signatureDataUrl) {
await redis.set(`signature:${signatureDataUrlKey}`, signatureDataUrl, {
// expire in 7 days
ex: 60 * 60 * 24 * 7,
});
}
const metadata: Record<string, string> = {
name,
email,
signatureText: signatureText || name,
source: 'landing',
};
if (signatureDataUrl) {
metadata.signatureDataUrl = signatureDataUrlKey;
}
const checkout = await stripe.checkout.sessions.create({
customer_email: email,
client_reference_id: userId.toString(),
payment_method_types: ['card'],
line_items: [
{
price: planId,
quantity: 1,
},
],
mode: 'subscription',
metadata,
allow_promotion_codes: true,
// eslint-disable-next-line turbo/no-undeclared-env-vars
success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/claimed?sessionId={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing?email=${encodeURIComponent(
email,
)}&name=${encodeURIComponent(name)}&planId=${planId}&cancelled=true`,
});
if (!checkout.url) {
throw new Error('Checkout URL not found');
}
return res.json({
redirectUrl: checkout.url,
});
} catch (error) {
console.error(error);
return res.status(500).json({
error: 'Internal server error',
});
}
}

View File

@ -0,0 +1,173 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { randomBytes } from 'crypto';
import { readFileSync } from 'fs';
import { buffer } from 'micro';
import { insertImageInPDF } from '@documenso/lib/server-only/pdf/insert-image-in-pdf';
import { insertTextInPDF } from '@documenso/lib/server-only/pdf/insert-text-in-pdf';
import { redis } from '@documenso/lib/server-only/redis';
import { Stripe, stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma';
import {
DocumentStatus,
FieldType,
ReadStatus,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
const log = (...args: any[]) => console.log('[stripe]', ...args);
export const config = {
api: { bodyParser: false },
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
// if (!process.env.NEXT_PUBLIC_ALLOW_SUBSCRIPTIONS) {
// return res.status(500).json({
// success: false,
// message: 'Subscriptions are not enabled',
// });
// }
const sig =
typeof req.headers['stripe-signature'] === 'string' ? req.headers['stripe-signature'] : '';
if (!sig) {
return res.status(400).json({
success: false,
message: 'No signature found in request',
});
}
log('constructing body...');
const body = await buffer(req);
log('constructed body');
const event = stripe.webhooks.constructEvent(
body,
sig,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, turbo/no-undeclared-env-vars
process.env.NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET!,
);
log('event-type:', event.type);
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session;
if (session.metadata?.source === 'landing') {
const user = await prisma.user.findFirst({
where: {
id: Number(session.client_reference_id),
},
});
if (!user) {
return res.status(500).json({
success: false,
message: 'User not found',
});
}
const signatureText = session.metadata?.signatureText || user.name;
let signatureDataUrl = '';
if (session.metadata?.signatureDataUrl) {
const result = await redis.get<string>(`signature:${session.metadata.signatureDataUrl}`);
if (result) {
signatureDataUrl = result;
}
}
const now = new Date();
const document = await prisma.document.create({
data: {
title: 'Documenso Supporter Pledge.pdf',
status: DocumentStatus.COMPLETED,
userId: user.id,
document: readFileSync('./public/documenso-supporter-pledge.pdf').toString('base64'),
created: now,
},
});
const recipient = await prisma.recipient.create({
data: {
name: user.name ?? '',
email: user.email,
token: randomBytes(16).toString('hex'),
signedAt: now,
readStatus: ReadStatus.OPENED,
sendStatus: SendStatus.SENT,
signingStatus: SigningStatus.SIGNED,
documentId: document.id,
},
});
const field = await prisma.field.create({
data: {
documentId: document.id,
recipientId: recipient.id,
type: FieldType.SIGNATURE,
page: 0,
positionX: 77,
positionY: 638,
inserted: false,
customText: '',
},
});
if (signatureDataUrl) {
document.document = await insertImageInPDF(
document.document,
signatureDataUrl,
field.positionX,
field.positionY,
field.page,
);
} else {
document.document = await insertTextInPDF(
document.document,
signatureText ?? '',
field.positionX,
field.positionY,
field.page,
);
}
await Promise.all([
prisma.signature.create({
data: {
fieldId: field.id,
recipientId: recipient.id,
signatureImageAsBase64: signatureDataUrl || undefined,
typedSignature: signatureDataUrl ? '' : signatureText,
},
}),
prisma.document.update({
where: {
id: document.id,
},
data: {
document: document.document,
},
}),
]);
}
return res.status(200).json({
success: true,
message: 'Webhook received',
});
}
log('Unhandled webhook event', event.type);
return res.status(400).json({
success: false,
message: 'Unhandled webhook event',
});
}