/
opt
/
canhelp
/
node_modules
/
better-auth
/
dist
/
api
/
routes
/
/opt/canhelp/node_modules/better-auth/dist/api/routes
mkdir
upload
Name
Size
Mode
Actions
account.d.mts
11924
0644
edit
dl
rm
account.mjs
19527
0644
edit
dl
rm
account.mjs.map
40509
0644
edit
dl
rm
callback.d.mts
1014
0644
edit
dl
rm
callback.mjs
6979
0644
edit
dl
rm
callback.mjs.map
13572
0644
edit
dl
rm
email-verification.d.mts
4080
0644
edit
dl
rm
email-verification.mjs
11402
0644
edit
dl
rm
email-verification.mjs.map
23367
0644
edit
dl
rm
error.d.mts
603
0644
edit
dl
rm
error.mjs
12968
0644
edit
dl
rm
error.mjs.map
17009
0644
edit
dl
rm
index.d.mts
1044
0644
edit
dl
rm
index.mjs
1058
0644
edit
dl
rm
ok.d.mts
773
0644
edit
dl
rm
ok.mjs
704
0644
edit
dl
rm
ok.mjs.map
1424
0644
edit
dl
rm
password.d.mts
4514
0644
edit
dl
rm
password.mjs
8458
0644
edit
dl
rm
password.mjs.map
16864
0644
edit
dl
rm
session.d.mts
11608
0644
edit
dl
rm
session.mjs
18652
0644
edit
dl
rm
session.mjs.map
40494
0644
edit
dl
rm
sign-in.d.mts
7353
0644
edit
dl
rm
sign-in.mjs
10948
0644
edit
dl
rm
sign-in.mjs.map
25364
0644
edit
dl
rm
sign-out.d.mts
788
0644
edit
dl
rm
sign-out.mjs
1026
0644
edit
dl
rm
sign-out.mjs.map
2010
0644
edit
dl
rm
sign-up.d.mts
4657
0644
edit
dl
rm
sign-up.mjs
9719
0644
edit
dl
rm
sign-up.mjs.map
20386
0644
edit
dl
rm
update-session.d.mts
1991
0644
edit
dl
rm
update-session.mjs
1984
0644
edit
dl
rm
update-session.mjs.map
4018
0644
edit
dl
rm
update-user.d.mts
11568
0644
edit
dl
rm
update-user.mjs
19158
0644
edit
dl
rm
update-user.mjs.map
39053
0644
edit
dl
rm
Edit:
/opt/canhelp/node_modules/better-auth/dist/api/routes/update-user.mjs
(19158B)
import { originCheck } from "../middlewares/origin-check.mjs"; import "../middlewares/index.mjs"; import { parseUserInput, parseUserOutput } from "../../db/schema.mjs"; import { generateRandomString } from "../../crypto/random.mjs"; import "../../crypto/index.mjs"; import { deleteSessionCookie, setSessionCookie } from "../../cookies/index.mjs"; import { getSessionFromCtx, sensitiveSessionMiddleware, sessionMiddleware } from "./session.mjs"; import { createEmailVerificationToken } from "./email-verification.mjs"; import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error"; import { createAuthEndpoint } from "@better-auth/core/api"; import * as z from "zod"; //#region src/api/routes/update-user.ts const updateUserBodySchema = z.record(z.string().meta({ description: "Field name must be a string" }), z.any()); const updateUser = () => createAuthEndpoint("/update-user", { method: "POST", operationId: "updateUser", body: updateUserBodySchema, use: [sessionMiddleware], metadata: { $Infer: { body: {} }, openapi: { operationId: "updateUser", description: "Update the current user", requestBody: { content: { "application/json": { schema: { type: "object", properties: { name: { type: "string", description: "The name of the user" }, image: { type: "string", description: "The image of the user", nullable: true } } } } } }, responses: { "200": { description: "Success", content: { "application/json": { schema: { type: "object", properties: { user: { type: "object", $ref: "#/components/schemas/User" } } } } } } } } } }, async (ctx) => { const body = ctx.body; if (typeof body !== "object" || Array.isArray(body)) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.BODY_MUST_BE_AN_OBJECT); if (body.email) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.EMAIL_CAN_NOT_BE_UPDATED); const { name, image, ...rest } = body; const session = ctx.context.session; const additionalFields = parseUserInput(ctx.context.options, rest, "update"); if (image === void 0 && name === void 0 && Object.keys(additionalFields).length === 0) throw APIError.fromStatus("BAD_REQUEST", { message: "No fields to update" }); const updatedUser = await ctx.context.internalAdapter.updateUser(session.user.id, { name, image, ...additionalFields }) ?? { ...session.user, ...name !== void 0 && { name }, ...image !== void 0 && { image }, ...additionalFields }; /** * Update the session cookie with the new user data */ await setSessionCookie(ctx, { session: session.session, user: updatedUser }); return ctx.json({ status: true }); }); const changePassword = createAuthEndpoint("/change-password", { method: "POST", operationId: "changePassword", body: z.object({ newPassword: z.string().meta({ description: "The new password to set" }), currentPassword: z.string().meta({ description: "The current password is required" }), revokeOtherSessions: z.boolean().meta({ description: "Must be a boolean value" }).optional() }), use: [sensitiveSessionMiddleware], metadata: { openapi: { operationId: "changePassword", description: "Change the password of the user", responses: { "200": { description: "Password successfully changed", content: { "application/json": { schema: { type: "object", properties: { token: { type: "string", nullable: true, description: "New session token if other sessions were revoked" }, user: { type: "object", properties: { id: { type: "string", description: "The unique identifier of the user" }, email: { type: "string", format: "email", description: "The email address of the user" }, name: { type: "string", description: "The name of the user" }, image: { type: "string", format: "uri", nullable: true, description: "The profile image URL of the user" }, emailVerified: { type: "boolean", description: "Whether the email has been verified" }, createdAt: { type: "string", format: "date-time", description: "When the user was created" }, updatedAt: { type: "string", format: "date-time", description: "When the user was last updated" } }, required: [ "id", "email", "name", "emailVerified", "createdAt", "updatedAt" ] } }, required: ["user"] } } } } } } } }, async (ctx) => { const { newPassword, currentPassword, revokeOtherSessions } = ctx.body; const session = ctx.context.session; const minPasswordLength = ctx.context.password.config.minPasswordLength; if (newPassword.length < minPasswordLength) { ctx.context.logger.error("Password is too short"); throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT); } const maxPasswordLength = ctx.context.password.config.maxPasswordLength; if (newPassword.length > maxPasswordLength) { ctx.context.logger.error("Password is too long"); throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG); } const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account) => account.providerId === "credential" && account.password); if (!account || !account.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND); const passwordHash = await ctx.context.password.hash(newPassword); if (!await ctx.context.password.verify({ hash: account.password, password: currentPassword })) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD); await ctx.context.internalAdapter.updateAccount(account.id, { password: passwordHash }); let token = null; if (revokeOtherSessions) { await ctx.context.internalAdapter.deleteSessions(session.user.id); const newSession = await ctx.context.internalAdapter.createSession(session.user.id); if (!newSession) throw APIError.from("INTERNAL_SERVER_ERROR", BASE_ERROR_CODES.FAILED_TO_GET_SESSION); await setSessionCookie(ctx, { session: newSession, user: session.user }); token = newSession.token; } return ctx.json({ token, user: parseUserOutput(ctx.context.options, session.user) }); }); const setPassword = createAuthEndpoint({ method: "POST", body: z.object({ newPassword: z.string().meta({ description: "The new password to set is required" }) }), use: [sensitiveSessionMiddleware] }, async (ctx) => { const { newPassword } = ctx.body; const session = ctx.context.session; const minPasswordLength = ctx.context.password.config.minPasswordLength; if (newPassword.length < minPasswordLength) { ctx.context.logger.error("Password is too short"); throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT); } const maxPasswordLength = ctx.context.password.config.maxPasswordLength; if (newPassword.length > maxPasswordLength) { ctx.context.logger.error("Password is too long"); throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG); } const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account) => account.providerId === "credential" && account.password); const passwordHash = await ctx.context.password.hash(newPassword); if (!account) { await ctx.context.internalAdapter.linkAccount({ userId: session.user.id, providerId: "credential", accountId: session.user.id, password: passwordHash }); return ctx.json({ status: true }); } throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_ALREADY_SET); }); const deleteUser = createAuthEndpoint("/delete-user", { method: "POST", use: [sensitiveSessionMiddleware], body: z.object({ callbackURL: z.string().meta({ description: "The callback URL to redirect to after the user is deleted" }).optional(), password: z.string().meta({ description: "The password of the user is required to delete the user" }).optional(), token: z.string().meta({ description: "The token to delete the user is required" }).optional() }), metadata: { openapi: { operationId: "deleteUser", description: "Delete the user", requestBody: { content: { "application/json": { schema: { type: "object", properties: { callbackURL: { type: "string", description: "The callback URL to redirect to after the user is deleted" }, password: { type: "string", description: "The user's password. Required if session is not fresh" }, token: { type: "string", description: "The deletion verification token" } } } } } }, responses: { "200": { description: "User deletion processed successfully", content: { "application/json": { schema: { type: "object", properties: { success: { type: "boolean", description: "Indicates if the operation was successful" }, message: { type: "string", enum: ["User deleted", "Verification email sent"], description: "Status message of the deletion process" } }, required: ["success", "message"] } } } } } } } }, async (ctx) => { if (!ctx.context.options.user?.deleteUser?.enabled) { ctx.context.logger.error("Delete user is disabled. Enable it in the options"); throw APIError.fromStatus("NOT_FOUND"); } const session = ctx.context.session; if (ctx.body.password) { const account = (await ctx.context.internalAdapter.findAccounts(session.user.id)).find((account) => account.providerId === "credential" && account.password); if (!account || !account.password) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND); if (!await ctx.context.password.verify({ hash: account.password, password: ctx.body.password })) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD); } if (ctx.body.token) { await deleteUserCallback({ ...ctx, query: { token: ctx.body.token } }); return ctx.json({ success: true, message: "User deleted" }); } if (ctx.context.options.user.deleteUser?.sendDeleteAccountVerification) { const token = generateRandomString(32, "0-9", "a-z"); await ctx.context.internalAdapter.createVerificationValue({ value: session.user.id, identifier: `delete-account-${token}`, expiresAt: new Date(Date.now() + (ctx.context.options.user.deleteUser?.deleteTokenExpiresIn || 3600 * 24) * 1e3) }); const url = `${ctx.context.baseURL}/delete-user/callback?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`; await ctx.context.runInBackgroundOrAwait(ctx.context.options.user.deleteUser.sendDeleteAccountVerification({ user: session.user, url, token }, ctx.request)); return ctx.json({ success: true, message: "Verification email sent" }); } if (!ctx.body.password && ctx.context.sessionConfig.freshAge !== 0) { const currentAge = new Date(session.session.createdAt).getTime(); const freshAge = ctx.context.sessionConfig.freshAge * 1e3; if (Date.now() - currentAge > freshAge) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.SESSION_EXPIRED); } const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; if (beforeDelete) await beforeDelete(session.user, ctx.request); await ctx.context.internalAdapter.deleteUser(session.user.id); await ctx.context.internalAdapter.deleteSessions(session.user.id); deleteSessionCookie(ctx); const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; if (afterDelete) await afterDelete(session.user, ctx.request); return ctx.json({ success: true, message: "User deleted" }); }); const deleteUserCallback = createAuthEndpoint("/delete-user/callback", { method: "GET", query: z.object({ token: z.string().meta({ description: "The token to verify the deletion request" }), callbackURL: z.string().meta({ description: "The URL to redirect to after deletion" }).optional() }), use: [originCheck((ctx) => ctx.query.callbackURL)], metadata: { openapi: { description: "Callback to complete user deletion with verification token", responses: { "200": { description: "User successfully deleted", content: { "application/json": { schema: { type: "object", properties: { success: { type: "boolean", description: "Indicates if the deletion was successful" }, message: { type: "string", enum: ["User deleted"], description: "Confirmation message" } }, required: ["success", "message"] } } } } } } } }, async (ctx) => { if (!ctx.context.options.user?.deleteUser?.enabled) { ctx.context.logger.error("Delete user is disabled. Enable it in the options"); throw APIError.from("NOT_FOUND", { message: "Not found", code: "NOT_FOUND" }); } const session = await getSessionFromCtx(ctx); if (!session) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO); const token = await ctx.context.internalAdapter.findVerificationValue(`delete-account-${ctx.query.token}`); if (!token || token.expiresAt < /* @__PURE__ */ new Date()) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.INVALID_TOKEN); if (token.value !== session.user.id) throw APIError.from("NOT_FOUND", BASE_ERROR_CODES.INVALID_TOKEN); const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete; if (beforeDelete) await beforeDelete(session.user, ctx.request); await ctx.context.internalAdapter.deleteUser(session.user.id); await ctx.context.internalAdapter.deleteSessions(session.user.id); await ctx.context.internalAdapter.deleteAccounts(session.user.id); await ctx.context.internalAdapter.deleteVerificationByIdentifier(`delete-account-${ctx.query.token}`); deleteSessionCookie(ctx); const afterDelete = ctx.context.options.user.deleteUser?.afterDelete; if (afterDelete) await afterDelete(session.user, ctx.request); if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL || "/"); return ctx.json({ success: true, message: "User deleted" }); }); const changeEmail = createAuthEndpoint("/change-email", { method: "POST", body: z.object({ newEmail: z.email().meta({ description: "The new email address to set must be a valid email address" }), callbackURL: z.string().meta({ description: "The URL to redirect to after email verification" }).optional() }), use: [sensitiveSessionMiddleware], metadata: { openapi: { operationId: "changeEmail", responses: { "200": { description: "Email change request processed successfully", content: { "application/json": { schema: { type: "object", properties: { user: { type: "object", $ref: "#/components/schemas/User" }, status: { type: "boolean", description: "Indicates if the request was successful" }, message: { type: "string", enum: ["Email updated", "Verification email sent"], description: "Status message of the email change process", nullable: true } }, required: ["status"] } } } } } } } }, async (ctx) => { if (!ctx.context.options.user?.changeEmail?.enabled) { ctx.context.logger.error("Change email is disabled."); throw APIError.fromStatus("BAD_REQUEST", { message: "Change email is disabled" }); } const newEmail = ctx.body.newEmail.toLowerCase(); if (newEmail === ctx.context.session.user.email) { ctx.context.logger.error("Email is the same"); throw APIError.fromStatus("BAD_REQUEST", { message: "Email is the same" }); } /** * Early config check: ensure at least one email-change flow is * available for the current session state. Without this, an * existing-email lookup would return 200 while a non-existing * email would later throw 400, leaking email existence. */ const canUpdateWithoutVerification = ctx.context.session.user.emailVerified !== true && ctx.context.options.user.changeEmail.updateEmailWithoutVerification; const canSendConfirmation = ctx.context.session.user.emailVerified && ctx.context.options.user.changeEmail.sendChangeEmailConfirmation; const canSendVerification = ctx.context.options.emailVerification?.sendVerificationEmail; if (!canUpdateWithoutVerification && !canSendConfirmation && !canSendVerification) { ctx.context.logger.error("Verification email isn't enabled."); throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" }); } if (await ctx.context.internalAdapter.findUserByEmail(newEmail)) { await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn); ctx.context.logger.info("Change email attempt for existing email"); return ctx.json({ status: true }); } /** * If the email is not verified, we can update the email if the option is enabled */ if (canUpdateWithoutVerification) { await ctx.context.internalAdapter.updateUserByEmail(ctx.context.session.user.email, { email: newEmail }); await setSessionCookie(ctx, { session: ctx.context.session.session, user: { ...ctx.context.session.user, email: newEmail } }); if (canSendVerification) { const token = await createEmailVerificationToken(ctx.context.secret, newEmail, void 0, ctx.context.options.emailVerification?.expiresIn); const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`; await ctx.context.runInBackgroundOrAwait(canSendVerification({ user: { ...ctx.context.session.user, email: newEmail }, url, token }, ctx.request)); } return ctx.json({ status: true }); } /** * If the email is verified, we need to send a verification email */ if (canSendConfirmation) { const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-confirmation" }); const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`; await ctx.context.runInBackgroundOrAwait(canSendConfirmation({ user: ctx.context.session.user, newEmail, url, token }, ctx.request)); return ctx.json({ status: true }); } if (!canSendVerification) { ctx.context.logger.error("Verification email isn't enabled."); throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" }); } const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" }); const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`; await ctx.context.runInBackgroundOrAwait(canSendVerification({ user: { ...ctx.context.session.user, email: newEmail }, url, token }, ctx.request)); return ctx.json({ status: true }); }); //#endregion export { changeEmail, changePassword, deleteUser, deleteUserCallback, setPassword, updateUser }; //# sourceMappingURL=update-user.mjs.map
Save
cmd:
run