163 lines
4.7 KiB
TypeScript
163 lines
4.7 KiB
TypeScript
import { Controller, Get, Middleware, Post } from "@overnightjs/core";
|
|
import { IdentitySchemas, UtilitySchemas } from "@plunk/shared";
|
|
import type { Request, Response } from "express";
|
|
import signale from "signale";
|
|
import { prisma } from "../database/prisma";
|
|
import { NotFound } from "../exceptions";
|
|
import { type IJwt, isAuthenticated } from "../middleware/auth";
|
|
import { ProjectService } from "../services/ProjectService";
|
|
import { Keys } from "../services/keys";
|
|
import { redis } from "../services/redis";
|
|
import { getIdentities, getIdentityVerificationAttributes, ses, verifyIdentity } from "../util/ses";
|
|
|
|
@Controller("identities")
|
|
export class Identities {
|
|
@Get("id/:id")
|
|
@Middleware([isAuthenticated])
|
|
public async getVerification(req: Request, res: Response) {
|
|
const { id } = UtilitySchemas.id.parse(req.params);
|
|
|
|
const project = await ProjectService.id(id);
|
|
|
|
if (!project) {
|
|
throw new NotFound("project");
|
|
}
|
|
|
|
if (!project.email) {
|
|
return res.status(200).json({ success: false });
|
|
}
|
|
|
|
const attributes = await getIdentityVerificationAttributes(project.email);
|
|
|
|
if (attributes.status === "Success" && !project.verified) {
|
|
await prisma.project.update({ where: { id }, data: { verified: true } });
|
|
|
|
await redis.del(Keys.Project.id(project.id));
|
|
await redis.del(Keys.Project.secret(project.secret));
|
|
await redis.del(Keys.Project.public(project.public));
|
|
}
|
|
|
|
return res.status(200).json({ tokens: attributes.tokens });
|
|
}
|
|
|
|
@Middleware([isAuthenticated])
|
|
@Post("create")
|
|
public async addIdentity(req: Request, res: Response) {
|
|
const { id, email } = IdentitySchemas.create.parse(req.body);
|
|
|
|
const { userId } = res.locals.auth as IJwt;
|
|
|
|
const project = await ProjectService.id(id);
|
|
|
|
if (!project) {
|
|
throw new NotFound("project");
|
|
}
|
|
|
|
const existingProject = await prisma.project.findFirst({
|
|
where: { email: { endsWith: email.split("@")[1] } },
|
|
});
|
|
|
|
if (existingProject) {
|
|
throw new Error("Domain already attached to another project");
|
|
}
|
|
|
|
const tokens = await verifyIdentity(email);
|
|
|
|
await prisma.project.update({
|
|
where: { id },
|
|
data: { email, verified: false },
|
|
});
|
|
|
|
await redis.del(Keys.User.projects(userId));
|
|
await redis.del(Keys.Project.id(project.id));
|
|
|
|
return res.status(200).json({ success: true, tokens });
|
|
}
|
|
|
|
@Middleware([isAuthenticated])
|
|
@Post("reset")
|
|
public async resetIdentity(req: Request, res: Response) {
|
|
const { id } = UtilitySchemas.id.parse(req.body);
|
|
|
|
const { userId } = res.locals.auth as IJwt;
|
|
|
|
const project = await ProjectService.id(id);
|
|
|
|
if (!project) {
|
|
throw new NotFound("project");
|
|
}
|
|
|
|
await prisma.project.update({
|
|
where: { id },
|
|
data: { email: null, verified: false },
|
|
});
|
|
|
|
await redis.del(Keys.User.projects(userId));
|
|
await redis.del(Keys.Project.id(project.id));
|
|
|
|
return res.status(200).json({ success: true });
|
|
}
|
|
|
|
@Post("update")
|
|
public async updateIdentities(req: Request, res: Response) {
|
|
const count = await prisma.project.count({
|
|
where: { email: { not: null } },
|
|
});
|
|
|
|
for (let i = 0; i < count; i += 99) {
|
|
const dbIdentities = await prisma.project.findMany({
|
|
where: { email: { not: null } },
|
|
select: { id: true, email: true },
|
|
skip: i,
|
|
take: 99,
|
|
});
|
|
|
|
const awsIdentities = await getIdentities(dbIdentities.map((i) => i.email as string));
|
|
|
|
for (const identity of awsIdentities) {
|
|
const projectId = dbIdentities.find((i) => i.email?.endsWith(identity.email));
|
|
|
|
const project = await ProjectService.id(projectId?.id as string);
|
|
|
|
if (identity.status === "Failed") {
|
|
signale.info(`Restarting verification for ${identity.email}`);
|
|
try {
|
|
void verifyIdentity(identity.email);
|
|
} catch (e) {
|
|
// @ts-ignore
|
|
if (e.Code === "Throttling") {
|
|
signale.warn("Throttling detected, waiting 5 seconds");
|
|
await new Promise((r) => setTimeout(r, 5000));
|
|
}
|
|
}
|
|
}
|
|
|
|
await prisma.project.update({
|
|
where: { id: projectId?.id as string },
|
|
data: { verified: identity.status === "Success" },
|
|
});
|
|
|
|
if (project && !project.verified && identity.status === "Success") {
|
|
signale.success(`Successfully verified ${identity.email}`);
|
|
void ses.setIdentityFeedbackForwardingEnabled({
|
|
Identity: identity.email,
|
|
ForwardingEnabled: false,
|
|
});
|
|
|
|
await redis.del(Keys.Project.id(project.id));
|
|
await redis.del(Keys.Project.secret(project.secret));
|
|
await redis.del(Keys.Project.public(project.public));
|
|
}
|
|
|
|
if (project?.verified && identity.status !== "Success") {
|
|
await redis.del(Keys.Project.id(project.id));
|
|
await redis.del(Keys.Project.secret(project.secret));
|
|
await redis.del(Keys.Project.public(project.public));
|
|
}
|
|
}
|
|
}
|
|
|
|
return res.status(200).json({ success: true });
|
|
}
|
|
}
|