104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import type { ActionFunctionArgs } from "@remix-run/server-runtime";
|
|
import { json } from "@remix-run/server-runtime";
|
|
import { z } from "zod";
|
|
import {
|
|
ApiAlertChannelPresenter,
|
|
ApiCreateAlertChannel,
|
|
} from "~/presenters/v3/ApiAlertChannelPresenter.server";
|
|
import { logger } from "~/services/logger.server";
|
|
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
|
|
import { CreateAlertChannelService } from "~/v3/services/alerts/createAlertChannel.server";
|
|
import { ServiceValidationError } from "~/v3/services/baseService.server";
|
|
|
|
const ParamsSchema = z.object({
|
|
projectRef: z.string(),
|
|
});
|
|
|
|
export async function action({ request, params }: ActionFunctionArgs) {
|
|
try {
|
|
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
|
|
|
|
if (!authenticationResult) {
|
|
return json({ error: "Invalid or Missing Access Token" }, { status: 401 });
|
|
}
|
|
|
|
const parsedParams = ParamsSchema.safeParse(params);
|
|
|
|
if (!parsedParams.success) {
|
|
return json({ error: "Invalid Params" }, { status: 400 });
|
|
}
|
|
|
|
const { projectRef } = parsedParams.data;
|
|
|
|
const rawBody = await request.json();
|
|
|
|
const body = ApiCreateAlertChannel.safeParse(rawBody);
|
|
|
|
if (!body.success) {
|
|
return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 });
|
|
}
|
|
|
|
const service = new CreateAlertChannelService();
|
|
|
|
try {
|
|
if (body.data.channel === "email") {
|
|
if (!body.data.channelData.email) {
|
|
return json({ error: "Email is required" }, { status: 422 });
|
|
}
|
|
|
|
const alertChannel = await service.call(projectRef, authenticationResult.userId, {
|
|
name: body.data.name,
|
|
alertTypes: body.data.alertTypes.map((type) =>
|
|
ApiAlertChannelPresenter.alertTypeFromApi(type)
|
|
),
|
|
channel: {
|
|
type: "EMAIL",
|
|
email: body.data.channelData.email,
|
|
},
|
|
deduplicationKey: body.data.deduplicationKey,
|
|
environmentTypes: body.data.environmentTypes,
|
|
});
|
|
|
|
return json(await ApiAlertChannelPresenter.alertChannelToApi(alertChannel));
|
|
}
|
|
|
|
if (body.data.channel === "webhook") {
|
|
if (!body.data.channelData.url) {
|
|
return json({ error: "webhook url is required" }, { status: 422 });
|
|
}
|
|
|
|
// The webhook URL is validated in CreateAlertChannelService.call();
|
|
// an unsafe URL surfaces as a ServiceValidationError -> 422 below.
|
|
const alertChannel = await service.call(projectRef, authenticationResult.userId, {
|
|
name: body.data.name,
|
|
alertTypes: body.data.alertTypes.map((type) =>
|
|
ApiAlertChannelPresenter.alertTypeFromApi(type)
|
|
),
|
|
channel: {
|
|
type: "WEBHOOK",
|
|
url: body.data.channelData.url,
|
|
secret: body.data.channelData.secret,
|
|
},
|
|
deduplicationKey: body.data.deduplicationKey,
|
|
environmentTypes: body.data.environmentTypes,
|
|
});
|
|
|
|
return json(await ApiAlertChannelPresenter.alertChannelToApi(alertChannel));
|
|
}
|
|
|
|
return json({ error: "Invalid channel type" }, { status: 422 });
|
|
} catch (error) {
|
|
if (error instanceof ServiceValidationError) {
|
|
return json({ error: error.message }, { status: 422 });
|
|
}
|
|
|
|
logger.error("Failed to create alert channel", { error });
|
|
return json({ error: "Something went wrong, please try again." }, { status: 500 });
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof Response) throw error;
|
|
logger.error("Failed to create alert channel (outer)", { error });
|
|
return json({ error: "Internal Server Error" }, { status: 500 });
|
|
}
|
|
}
|