fix: confirm email changes and enforce clean backend build
Some checks failed
deploy-socialize / deploy (push) Has been cancelled
deploy-socialize / image (push) Has been cancelled

This commit is contained in:
2026-05-07 14:39:22 -04:00
parent 9022fa7d93
commit 57abe57bc7
54 changed files with 974 additions and 206 deletions

View File

@@ -308,6 +308,22 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/users/confirm-email-change": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["SocializeApiModulesIdentityHandlersConfirmEmailChangeHandler"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/users/forgot-password": {
parameters: {
query?: never;
@@ -1262,6 +1278,9 @@ export interface components {
/** Format: date-time */
birthDate?: string;
};
SocializeApiModulesIdentityHandlersChangeEmailResponse: {
message?: string;
};
SocializeApiModulesIdentityHandlersChangeEmailRequest: {
email?: string | null;
};
@@ -1279,6 +1298,10 @@ export interface components {
/** Format: binary */
file: string;
};
SocializeApiModulesIdentityHandlersConfirmEmailChangeResponse: {
message?: string;
};
SocializeApiModulesIdentityHandlersConfirmEmailChangeRequest: Record<string, never>;
SocializeApiModulesIdentityHandlersForgotPasswordRequest: {
email?: string;
};
@@ -2529,12 +2552,14 @@ export interface operations {
};
};
responses: {
/** @description No Content */
204: {
/** @description Success */
200: {
headers: {
[name: string]: unknown;
};
content?: never;
content: {
"application/json": components["schemas"]["SocializeApiModulesIdentityHandlersChangeEmailResponse"];
};
};
/** @description Unauthorized */
401: {
@@ -2670,6 +2695,30 @@ export interface operations {
};
};
};
SocializeApiModulesIdentityHandlersConfirmEmailChangeHandler: {
parameters: {
query: {
userId: string;
email: string;
token: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Success */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["SocializeApiModulesIdentityHandlersConfirmEmailChangeResponse"];
};
};
};
};
SocializeApiModulesIdentityHandlersForgotPasswordHandler: {
parameters: {
query?: never;

View File

@@ -24,8 +24,10 @@
icon="mdi-check-circle"
size="64"
></v-icon>
<h1 class="text-2xl font-bold text-green-600">{{ t('success.title') }}</h1>
<p>{{ t('success.message') }}</p>
<h1 class="text-2xl font-bold text-green-600">
{{ t(isEmailChange ? 'success.emailChangeTitle' : 'success.title') }}
</h1>
<p>{{ t(isEmailChange ? 'success.emailChangeMessage' : 'success.message') }}</p>
<v-btn
color="primary"
@click="goToLogin"
@@ -172,6 +174,7 @@
const verificationSuccess = ref(false);
const errorMessage = ref('');
const showResendOnly = ref(false);
const isEmailChange = ref(false);
// Resend verification state
const resendEmail = ref('');
@@ -183,6 +186,8 @@
onMounted(async () => {
const userId = route.query.userId;
const token = route.query.token;
const email = route.query.email;
isEmailChange.value = route.query.changeEmail === 'true';
// Populate resend email field if it was in the URL
if (route.query.email) {
@@ -190,7 +195,7 @@
}
// Check if we have the required parameters
if (!userId || !token) {
if (!userId || !token || (isEmailChange.value && !email)) {
isLoading.value = false;
if (route.query.email || route.query.pending) {
showResendOnly.value = true;
@@ -201,8 +206,10 @@
}
try {
// Call the verification endpoint
await clientApi.get(`/api/users/verify-email?userId=${userId}&token=${token}`);
const endpoint = isEmailChange.value
? `/api/users/confirm-email-change?userId=${encodeURIComponent(userId)}&email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}`
: `/api/users/verify-email?userId=${encodeURIComponent(userId)}&token=${encodeURIComponent(token)}`;
await clientApi.get(endpoint);
verificationSuccess.value = true;
} catch (error) {
console.error('Email verification failed:', error);
@@ -252,6 +259,8 @@
"success": {
"title": "Email Verified Successfully!",
"message": "Your email has been verified. You can now log in to your account.",
"emailChangeTitle": "Email Changed Successfully!",
"emailChangeMessage": "Your account email address has been updated. Use the new email the next time you log in.",
"goToLogin": "Go to Login"
},
"error": {
@@ -279,6 +288,8 @@
"success": {
"title": "Email vérifié avec succès !",
"message": "Votre email a été vérifié. Vous pouvez maintenant vous connecter à votre compte.",
"emailChangeTitle": "Email modifié avec succès !",
"emailChangeMessage": "L'adresse email de votre compte a été mise à jour. Utilisez le nouvel email lors de votre prochaine connexion.",
"goToLogin": "Aller à la connexion"
},
"error": {

View File

@@ -155,12 +155,12 @@ export const useUserProfileStore = defineStore(
try {
const client = useClient()
await client.post(
const response = await client.post(
`/api/users/email`,
{
email: email
})
value.value.email = email;
return response.data;
} catch (updateError) {
console.error(updateError)
error.value = 'Failed to update profile.'

View File

@@ -69,12 +69,19 @@
await userProfileStore.changeAlias(nextAlias || null);
}
let emailChangeRequested = false;
if (nextEmail !== (user.email ?? '')) {
await userProfileStore.changeEmail(nextEmail);
emailChangeRequested = true;
}
settingsStatus.value = t('userSettings.saved');
syncFormFromUser(userProfileStore.user);
settingsStatus.value = emailChangeRequested
? t('userSettings.emailConfirmationSent')
: t('userSettings.saved');
if (!emailChangeRequested) {
syncFormFromUser(userProfileStore.user);
}
} catch (error) {
console.error('Failed to update user settings:', error);
settingsError.value = t('userSettings.errors.saveFailed');

View File

@@ -936,6 +936,7 @@
"accountDetailsDescription": "Edit the profile details other workspace members see.",
"saveDetails": "Save details",
"saved": "Profile details saved",
"emailConfirmationSent": "Profile details saved. Check your new email address to confirm the email change.",
"portraitSaved": "Portrait saved",
"calendarFeed": {
"title": "Private calendar feed",

View File

@@ -936,6 +936,7 @@
"accountDetailsDescription": "Modifiez les informations de profil visibles par les autres membres.",
"saveDetails": "Enregistrer les détails",
"saved": "Informations de profil enregistrées",
"emailConfirmationSent": "Informations de profil enregistrées. Consultez votre nouvelle adresse email pour confirmer le changement.",
"portraitSaved": "Portrait enregistré",
"calendarFeed": {
"title": "Flux calendrier privé",