studiocms
Version:
Astro Native CMS for AstroDB. Built from the ground up by the Astro community.
113 lines (112 loc) • 3.98 kB
JavaScript
import { site } from "astro:config/server";
import { developerConfig } from "studiocms:config";
import { StudioCMSRoutes } from "studiocms:lib";
import { apiResponseLogger } from "studiocms:logger";
import { Mailer } from "studiocms:mailer";
import { Notifications } from "studiocms:notifier";
import { SDKCore } from "studiocms:sdk";
import templateEngine from "studiocms:template-engine";
import {
AllResponse,
appendSearchParamsToUrl,
createEffectAPIRoutes,
createJsonResponse,
Effect,
genLogger,
Layer,
OptionsResponse,
pipe
} from "../../../../effect.js";
import { AuthAPIUtils } from "./shared.js";
function generateResetLink(token) {
return pipe(
new URL(StudioCMSRoutes.mainLinks.passwordReset, site),
appendSearchParamsToUrl("userid", token.userId),
appendSearchParamsToUrl("token", token.token),
appendSearchParamsToUrl("id", token.id)
);
}
const deps = Layer.mergeAll(Mailer.Default, Notifications.Default, AuthAPIUtils.Default);
const { POST, OPTIONS, ALL } = createEffectAPIRoutes(
{
POST: (ctx) => genLogger("studiocms/routes/api/auth/forgot-password/POST")(function* () {
const [sdk, { sendMail }, { sendAdminNotification }, { readJson, validateEmail }] = yield* Effect.all([SDKCore, Mailer, Notifications, AuthAPIUtils]);
if (developerConfig.demoMode !== false) {
return apiResponseLogger(403, "Demo mode is enabled, this action is not allowed.");
}
const config = ctx.locals.StudioCMS.siteConfig.data;
if (!config.enableMailer) {
return apiResponseLogger(500, "Mailer is not enabled");
}
const jsonData = yield* readJson(ctx);
const { email } = jsonData;
if (!email) {
return apiResponseLogger(400, "Invalid form data, email is required");
}
const checkEmail = yield* validateEmail(email);
if (!checkEmail.success) {
return apiResponseLogger(400, checkEmail.error.message);
}
const { emailSearch } = yield* sdk.AUTH.user.searchUsersForUsernameOrEmail(
"",
checkEmail.data
);
if (emailSearch.length === 0) {
return apiResponseLogger(
200,
"If an account exists for this email, a reset link has been sent."
);
}
const user = emailSearch[0];
const token = yield* sdk.resetTokenBucket.new(user.id);
if (!token) {
return apiResponseLogger(500, "Failed to create reset link");
}
yield* sendAdminNotification("user_updated", user.username);
const resetLink = generateResetLink(token);
if (!user.email) {
return apiResponseLogger(500, "Failed to send email to user, no email address found");
}
const engine = yield* templateEngine;
const { title: siteTitle, description, siteIcon } = config;
const passwordResetTemplate = yield* engine.render("passwordReset", {
site: { title: siteTitle, description, icon: siteIcon ?? void 0 },
data: { link: resetLink.toString() }
});
const mailRes = yield* sendMail({
to: user.email,
subject: "Password Reset",
html: passwordResetTemplate
});
if (!mailRes) {
return apiResponseLogger(500, "Failed to send email to user");
}
if ("error" in mailRes) {
return apiResponseLogger(500, `Failed to send email to user: ${mailRes.error}`);
}
return apiResponseLogger(
200,
"If an account exists for this email, a reset link has been sent."
);
}).pipe(Effect.provide(deps)),
OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ["POST"] })),
ALL: () => Effect.try(() => AllResponse())
},
{
cors: { methods: ["POST", "OPTIONS"] },
onError: (error) => {
console.error("API Error:", error);
return createJsonResponse(
{ error: "Internal Server Error" },
{
status: 500
}
);
}
}
);
export {
ALL,
OPTIONS,
POST
};