Initial Commit
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user