UNPKG

studiocms

Version:

Astro Native CMS for AstroDB. Built from the ground up by the Astro community.

113 lines (112 loc) 3.98 kB
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 };