mirror of
https://github.com/logto-io/logto.git
synced 2025-01-06 20:40:08 -05:00
Merge pull request #2359 from logto-io/charles-log-4570-improve-admin-patch-user-api
refactor(core): allowing more editable properties for patch user API
This commit is contained in:
commit
970e7a626a
10 changed files with 247 additions and 93 deletions
|
@ -1,5 +1,5 @@
|
|||
import type { CreateUser, Role, User } from '@logto/schemas';
|
||||
import { userInfoSelectFields } from '@logto/schemas';
|
||||
import { SignUpIdentifier, userInfoSelectFields } from '@logto/schemas';
|
||||
import pick from 'lodash.pick';
|
||||
|
||||
import { mockUser, mockUserList, mockUserListResponse, mockUserResponse } from '@/__mocks__';
|
||||
|
@ -23,6 +23,21 @@ const filterUsersWithSearch = (users: User[], search: string) =>
|
|||
)
|
||||
);
|
||||
|
||||
const mockFindDefaultSignInExperience = jest.fn(async () => ({
|
||||
signUp: {
|
||||
identifier: SignUpIdentifier.None,
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('@/queries/sign-in-experience', () => ({
|
||||
findDefaultSignInExperience: jest.fn(async () => mockFindDefaultSignInExperience()),
|
||||
}));
|
||||
|
||||
const mockHasUser = jest.fn(async () => false);
|
||||
const mockHasUserWithEmail = jest.fn(async () => false);
|
||||
const mockHasUserWithPhone = jest.fn(async () => false);
|
||||
jest.mock('@/queries/user', () => ({
|
||||
countUsers: jest.fn(async (search) => ({
|
||||
count: search ? filterUsersWithSearch(mockUserList, search).length : mockUserList.length,
|
||||
|
@ -32,7 +47,9 @@ jest.mock('@/queries/user', () => ({
|
|||
search ? filterUsersWithSearch(mockUserList, search) : mockUserList
|
||||
),
|
||||
findUserById: jest.fn(async (): Promise<User> => mockUser),
|
||||
hasUser: jest.fn(async () => false),
|
||||
hasUser: jest.fn(async () => mockHasUser()),
|
||||
hasUserWithEmail: jest.fn(async () => mockHasUserWithEmail()),
|
||||
hasUserWithPhone: jest.fn(async () => mockHasUserWithPhone()),
|
||||
updateUserById: jest.fn(
|
||||
async (_, data: Partial<CreateUser>): Promise<User> => ({
|
||||
...mockUser,
|
||||
|
@ -99,7 +116,7 @@ describe('adminUserRoutes', () => {
|
|||
it('POST /users', async () => {
|
||||
const username = 'MJAtLogto';
|
||||
const password = 'PASSWORD';
|
||||
const name = 'Micheal';
|
||||
const name = 'Michael';
|
||||
|
||||
const response = await userRequest.post('/users').send({ username, password, name });
|
||||
expect(response.status).toEqual(200);
|
||||
|
@ -114,7 +131,7 @@ describe('adminUserRoutes', () => {
|
|||
it('POST /users should throw with invalid input params', async () => {
|
||||
const username = 'MJAtLogto';
|
||||
const password = 'PASSWORD';
|
||||
const name = 'Micheal';
|
||||
const name = 'Michael';
|
||||
|
||||
// Missing input
|
||||
await expect(userRequest.post('/users').send({})).resolves.toHaveProperty('status', 400);
|
||||
|
@ -137,13 +154,13 @@ describe('adminUserRoutes', () => {
|
|||
).resolves.toHaveProperty('status', 400);
|
||||
});
|
||||
|
||||
it('POST /users should throw if username exist', async () => {
|
||||
it('POST /users should throw if username exists', async () => {
|
||||
const mockHasUser = hasUser as jest.Mock;
|
||||
mockHasUser.mockImplementationOnce(async () => true);
|
||||
|
||||
const username = 'MJAtLogto';
|
||||
const password = 'PASSWORD';
|
||||
const name = 'Micheal';
|
||||
const name = 'Michael';
|
||||
|
||||
await expect(
|
||||
userRequest.post('/users').send({ username, password, name })
|
||||
|
@ -151,20 +168,29 @@ describe('adminUserRoutes', () => {
|
|||
});
|
||||
|
||||
it('PATCH /users/:userId', async () => {
|
||||
const name = 'Micheal';
|
||||
const avatar = 'http://www.micheal.png';
|
||||
const name = 'Michael';
|
||||
const avatar = 'http://www.michael.png';
|
||||
const primaryEmail = 'bar@logto.io';
|
||||
const primaryPhone = '222222';
|
||||
const username = 'bar';
|
||||
|
||||
const response = await userRequest
|
||||
.patch('/users/foo')
|
||||
.send({ username, name, avatar, primaryEmail, primaryPhone });
|
||||
|
||||
const response = await userRequest.patch('/users/foo').send({ name, avatar });
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
...mockUserResponse,
|
||||
primaryEmail,
|
||||
primaryPhone,
|
||||
username,
|
||||
name,
|
||||
avatar,
|
||||
});
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should allow updated with empty avatar', async () => {
|
||||
const name = 'Micheal';
|
||||
it('PATCH /users/:userId should allow empty avatar URL', async () => {
|
||||
const name = 'Michael';
|
||||
const avatar = '';
|
||||
|
||||
const response = await userRequest.patch('/users/foo').send({ name, avatar });
|
||||
|
@ -176,8 +202,8 @@ describe('adminUserRoutes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should updated with one field if the other is undefined', async () => {
|
||||
const name = 'Micheal';
|
||||
it('PATCH /users/:userId should allow partial update', async () => {
|
||||
const name = 'Michael';
|
||||
|
||||
const updateNameResponse = await userRequest.patch('/users/foo').send({ name });
|
||||
expect(updateNameResponse.status).toEqual(200);
|
||||
|
@ -186,7 +212,7 @@ describe('adminUserRoutes', () => {
|
|||
name,
|
||||
});
|
||||
|
||||
const avatar = 'https://www.miceal.png';
|
||||
const avatar = 'https://www.michael.png';
|
||||
const updateAvatarResponse = await userRequest.patch('/users/foo').send({ avatar });
|
||||
expect(updateAvatarResponse.status).toEqual(200);
|
||||
expect(updateAvatarResponse.body).toEqual({
|
||||
|
@ -195,9 +221,9 @@ describe('adminUserRoutes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId throw with invalid input params', async () => {
|
||||
const name = 'Micheal';
|
||||
const avatar = 'http://www.micheal.png';
|
||||
it('PATCH /users/:userId should throw when avatar URL is invalid', async () => {
|
||||
const name = 'Michael';
|
||||
const avatar = 'http://www.michael.png';
|
||||
|
||||
await expect(userRequest.patch('/users/foo').send({ avatar })).resolves.toHaveProperty(
|
||||
'status',
|
||||
|
@ -209,9 +235,9 @@ describe('adminUserRoutes', () => {
|
|||
).resolves.toHaveProperty('status', 400);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId throw if user not found', async () => {
|
||||
const name = 'Micheal';
|
||||
const avatar = 'http://www.micheal.png';
|
||||
it('PATCH /users/:userId should throw if user cannot be found', async () => {
|
||||
const name = 'Michael';
|
||||
const avatar = 'http://www.michael.png';
|
||||
|
||||
const mockFindUserById = findUserById as jest.Mock;
|
||||
mockFindUserById.mockImplementationOnce(() => {
|
||||
|
@ -225,6 +251,88 @@ describe('adminUserRoutes', () => {
|
|||
expect(updateUserById).not.toBeCalled();
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if required sign-up identifier username is missing', async () => {
|
||||
mockFindDefaultSignInExperience.mockImplementationOnce(async () => ({
|
||||
signUp: {
|
||||
identifier: SignUpIdentifier.Username,
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
userRequest
|
||||
.patch('/users/foo')
|
||||
.send({ primaryEmail: 'test@abc.com', primaryPhone: '18688886666' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if required sign-up identifier email is missing', async () => {
|
||||
mockFindDefaultSignInExperience.mockImplementationOnce(async () => ({
|
||||
signUp: {
|
||||
identifier: SignUpIdentifier.Email,
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ username: 'test', primaryPhone: '18688886666' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if required sign-up identifier phone is missing', async () => {
|
||||
mockFindDefaultSignInExperience.mockImplementationOnce(async () => ({
|
||||
signUp: {
|
||||
identifier: SignUpIdentifier.Sms,
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ username: 'test', primaryEmail: 'test@abc.com' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if required sign-up identifiers email and phone are both missing', async () => {
|
||||
mockFindDefaultSignInExperience.mockImplementationOnce(async () => ({
|
||||
signUp: {
|
||||
identifier: SignUpIdentifier.EmailOrSms,
|
||||
password: false,
|
||||
verify: false,
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ username: 'test' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if new username is already in use', async () => {
|
||||
mockHasUser.mockImplementationOnce(async () => true);
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ username: 'test' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if new email has already linked to other accounts', async () => {
|
||||
mockHasUserWithEmail.mockImplementationOnce(async () => true);
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ primaryEmail: 'test@email.com' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if new phone number has already linked to other accounts', async () => {
|
||||
mockHasUserWithPhone.mockImplementationOnce(async () => true);
|
||||
|
||||
await expect(
|
||||
userRequest.patch('/users/foo').send({ primaryPhone: '18688886666' })
|
||||
).resolves.toHaveProperty('status', 422);
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId should throw if role names are invalid', async () => {
|
||||
const mockedFindRolesByRoleNames = findRolesByRoleNames as jest.Mock;
|
||||
mockedFindRolesByRoleNames.mockImplementationOnce(
|
||||
|
@ -263,7 +371,7 @@ describe('adminUserRoutes', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('PATCH /users/:userId/password throw if user not found', async () => {
|
||||
it('PATCH /users/:userId/password should throw if user cannot be found', async () => {
|
||||
const notExistedUserId = 'notExistedUserId';
|
||||
const dummyPassword = '123456';
|
||||
const mockedFindUserById = findUserById as jest.Mock;
|
||||
|
@ -293,8 +401,8 @@ describe('adminUserRoutes', () => {
|
|||
expect(deleteUserIdentity).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('DELETE /users/:userId should throw if user not found', async () => {
|
||||
const notExistedUserId = 'notExisitedUserId';
|
||||
it('DELETE /users/:userId should throw if user cannot be found', async () => {
|
||||
const notExistedUserId = 'notExistedUserId';
|
||||
const mockedFindUserById = findUserById as jest.Mock;
|
||||
mockedFindUserById.mockImplementationOnce((userId) => {
|
||||
if (userId === notExistedUserId) {
|
||||
|
@ -308,8 +416,8 @@ describe('adminUserRoutes', () => {
|
|||
expect(deleteUserById).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('DELETE /users/:userId/identities/:target should throw if user not found', async () => {
|
||||
const notExistedUserId = 'notExisitedUserId';
|
||||
it('DELETE /users/:userId/identities/:target should throw if user cannot be found', async () => {
|
||||
const notExistedUserId = 'notExistedUserId';
|
||||
const arbitraryTarget = 'arbitraryTarget';
|
||||
const mockedFindUserById = findUserById as jest.Mock;
|
||||
mockedFindUserById.mockImplementationOnce((userId) => {
|
||||
|
@ -323,9 +431,9 @@ describe('adminUserRoutes', () => {
|
|||
expect(deleteUserIdentity).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('DELETE /users/:userId/identities/:target should throw if user found and connector is not found', async () => {
|
||||
it('DELETE /users/:userId/identities/:target should throw if user is found but connector cannot be found', async () => {
|
||||
const arbitraryUserId = 'arbitraryUserId';
|
||||
const nonexistentTarget = 'nonexistentTarget';
|
||||
const nonExistedTarget = 'nonExistedTarget';
|
||||
const mockedFindUserById = findUserById as jest.Mock;
|
||||
mockedFindUserById.mockImplementationOnce((userId) => {
|
||||
if (userId === arbitraryUserId) {
|
||||
|
@ -333,7 +441,7 @@ describe('adminUserRoutes', () => {
|
|||
}
|
||||
});
|
||||
await expect(
|
||||
userRequest.delete(`/users/${arbitraryUserId}/identities/${nonexistentTarget}`)
|
||||
userRequest.delete(`/users/${arbitraryUserId}/identities/${nonExistedTarget}`)
|
||||
).resolves.toHaveProperty('status', 404);
|
||||
expect(deleteUserIdentity).not.toHaveBeenCalled();
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { passwordRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { emailRegEx, passwordRegEx, phoneRegEx, usernameRegEx } from '@logto/core-kit';
|
||||
import { arbitraryObjectGuard, userInfoSelectFields } from '@logto/schemas';
|
||||
import { has } from '@silverhand/essentials';
|
||||
import pick from 'lodash.pick';
|
||||
|
@ -21,6 +21,7 @@ import {
|
|||
} from '@/queries/user';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
import { checkExistingSignUpIdentifiers, checkRequiredSignUpIdentifiers } from './session/utils';
|
||||
import type { AuthedRouter } from './types';
|
||||
|
||||
export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
||||
|
@ -158,6 +159,9 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
koaGuard({
|
||||
params: object({ userId: string() }),
|
||||
body: object({
|
||||
username: string().regex(usernameRegEx).optional(),
|
||||
primaryEmail: string().regex(emailRegEx).optional(),
|
||||
primaryPhone: string().regex(phoneRegEx).optional(),
|
||||
name: string().nullable().optional(),
|
||||
avatar: string().url().or(literal('')).nullable().optional(),
|
||||
customData: arbitraryObjectGuard.optional(),
|
||||
|
@ -171,6 +175,8 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
} = ctx.guard;
|
||||
|
||||
await findUserById(userId);
|
||||
await checkRequiredSignUpIdentifiers(body);
|
||||
await checkExistingSignUpIdentifiers(body);
|
||||
|
||||
// Temp solution to validate the existence of input roleNames
|
||||
if (body.roleNames?.length) {
|
||||
|
@ -191,13 +197,7 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
}
|
||||
}
|
||||
|
||||
const user = await updateUserById(
|
||||
userId,
|
||||
{
|
||||
...body,
|
||||
},
|
||||
'replace'
|
||||
);
|
||||
const user = await updateUserById(userId, body, 'replace');
|
||||
|
||||
ctx.body = pick(user, ...userInfoSelectFields);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import { assignInteractionResults } from '@/lib/session';
|
|||
import { verifyUserPassword } from '@/lib/user';
|
||||
import type { LogContext } from '@/middleware/koa-log';
|
||||
import { findDefaultSignInExperience } from '@/queries/sign-in-experience';
|
||||
import { updateUserById } from '@/queries/user';
|
||||
import { hasUser, hasUserWithEmail, hasUserWithPhone, updateUserById } from '@/queries/user';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
import { continueSignInTimeout, verificationTimeout } from './consts';
|
||||
|
@ -189,8 +189,54 @@ export const checkRequiredProfile = async (
|
|||
throw new RequestError({ code: 'user.require_email_or_sms', status: 422 });
|
||||
}
|
||||
};
|
||||
|
||||
export const checkRequiredSignUpIdentifiers = async (identifiers: {
|
||||
username?: string;
|
||||
primaryEmail?: string;
|
||||
primaryPhone?: string;
|
||||
}) => {
|
||||
const { username, primaryEmail, primaryPhone } = identifiers;
|
||||
|
||||
const { signUp } = await findDefaultSignInExperience();
|
||||
|
||||
if (signUp.identifier === SignUpIdentifier.Username && !username) {
|
||||
throw new RequestError({ code: 'user.require_username', status: 422 });
|
||||
}
|
||||
|
||||
if (signUp.identifier === SignUpIdentifier.Email && !primaryEmail) {
|
||||
throw new RequestError({ code: 'user.require_email', status: 422 });
|
||||
}
|
||||
|
||||
if (signUp.identifier === SignUpIdentifier.Sms && !primaryPhone) {
|
||||
throw new RequestError({ code: 'user.require_sms', status: 422 });
|
||||
}
|
||||
|
||||
if (signUp.identifier === SignUpIdentifier.EmailOrSms && !primaryEmail && !primaryPhone) {
|
||||
throw new RequestError({ code: 'user.require_email_or_sms', status: 422 });
|
||||
}
|
||||
};
|
||||
/* eslint-enable complexity */
|
||||
|
||||
export const checkExistingSignUpIdentifiers = async (identifiers: {
|
||||
username?: string;
|
||||
primaryEmail?: string;
|
||||
primaryPhone?: string;
|
||||
}) => {
|
||||
const { username, primaryEmail, primaryPhone } = identifiers;
|
||||
|
||||
if (username && (await hasUser(username))) {
|
||||
throw new RequestError({ code: 'user.username_exists', status: 422 });
|
||||
}
|
||||
|
||||
if (primaryEmail && (await hasUserWithEmail(primaryEmail))) {
|
||||
throw new RequestError({ code: 'user.email_exists', status: 422 });
|
||||
}
|
||||
|
||||
if (primaryPhone && (await hasUserWithPhone(primaryPhone))) {
|
||||
throw new RequestError({ code: 'user.sms_exists', status: 422 });
|
||||
}
|
||||
};
|
||||
|
||||
type SignInWithPasswordParameter = {
|
||||
identifier: SignInIdentifier;
|
||||
password: string;
|
||||
|
|
|
@ -46,15 +46,15 @@ const errors = {
|
|||
sign_up_method_not_enabled: 'This sign up method is not enabled.', // UNTRANSLATED
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.', // UNTRANSLATED
|
||||
same_password: 'Das neue Passwort muss sich vom alten unterscheiden.',
|
||||
require_password: 'You need to set a password before sign in.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before signing-in.', // UNTRANSLATED
|
||||
password_exists: 'Your password has been set.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before sign in.', // UNTRANSLATED
|
||||
username_exists: 'Your username has been set.', // UNTRANSLATED
|
||||
require_email: 'You need to set an email before sign in.', // UNTRANSLATED
|
||||
email_exists: 'Your email has been set.', // UNTRANSLATED
|
||||
require_sms: 'You need to set a phone before sign in.', // UNTRANSLATED
|
||||
sms_exists: 'Your phone has been set.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before signing-in.', // UNTRANSLATED
|
||||
username_exists: 'This username is already in use.', // UNTRANSLATED
|
||||
require_email: 'You need to add an email address before signing-in.', // UNTRANSLATED
|
||||
email_exists: 'This email is associated with an existing account.', // UNTRANSLATED
|
||||
require_sms: 'You need to add a phone number before signing-in.', // UNTRANSLATED
|
||||
sms_exists: 'This phone number is associated with an existing account.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.', // UNTRANSLATED
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: 'Die Verschlüsselungsmethode {{name}} wird nicht unterstützt.',
|
||||
|
|
|
@ -45,16 +45,16 @@ const errors = {
|
|||
cannot_delete_self: 'You cannot delete yourself.',
|
||||
sign_up_method_not_enabled: 'This sign up method is not enabled.',
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.',
|
||||
same_password: 'Your new password can’t be the same as your current password.',
|
||||
require_password: 'You need to set a password before sign in.',
|
||||
same_password: 'New password cannot be the same as your old password.',
|
||||
require_password: 'You need to set a password before signing-in.',
|
||||
password_exists: 'Your password has been set.',
|
||||
require_username: 'You need to set a username before sign in.',
|
||||
username_exists: 'Your username has been set.',
|
||||
require_email: 'You need to set an email before sign in.',
|
||||
email_exists: 'Your email has been set.',
|
||||
require_sms: 'You need to set a phone before sign in.',
|
||||
sms_exists: 'Your phone has been set.',
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.',
|
||||
require_username: 'You need to set a username before signing-in.',
|
||||
username_exists: 'This username is already in use.',
|
||||
require_email: 'You need to add an email address before signing-in.',
|
||||
email_exists: 'This email is associated with an existing account.',
|
||||
require_sms: 'You need to add a phone number before signing-in.',
|
||||
sms_exists: 'This phone number is associated with an existing account.',
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: 'The encryption method {{name}} is not supported.',
|
||||
|
|
|
@ -46,16 +46,16 @@ const errors = {
|
|||
cannot_delete_self: 'You cannot delete yourself.', // UNTRANSLATED
|
||||
sign_up_method_not_enabled: 'This sign up method is not enabled.', // UNTRANSLATED
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.', // UNTRANSLATED
|
||||
same_password: 'Your new password can’t be the same as your current password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before sign in.', // UNTRANSLATED
|
||||
same_password: 'New password cannot be the same as your old password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before signing-in.', // UNTRANSLATED
|
||||
password_exists: 'Your password has been set.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before sign in.', // UNTRANSLATED
|
||||
username_exists: 'Your username has been set.', // UNTRANSLATED
|
||||
require_email: 'You need to set an email before sign in.', // UNTRANSLATED
|
||||
email_exists: 'Your email has been set.', // UNTRANSLATED
|
||||
require_sms: 'You need to set a phone before sign in.', // UNTRANSLATED
|
||||
sms_exists: 'Your phone has been set.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before signing-in.', // UNTRANSLATED
|
||||
username_exists: 'This username is already in use.', // UNTRANSLATED
|
||||
require_email: 'You need to add an email address before signing-in.', // UNTRANSLATED
|
||||
email_exists: 'This email is associated with an existing account.', // UNTRANSLATED
|
||||
require_sms: 'You need to add a phone number before signing-in.', // UNTRANSLATED
|
||||
sms_exists: 'This phone number is associated with an existing account.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.', // UNTRANSLATED
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: "La méthode de cryptage {{name}} n'est pas prise en charge.",
|
||||
|
|
|
@ -44,16 +44,16 @@ const errors = {
|
|||
cannot_delete_self: 'You cannot delete yourself.', // UNTRANSLATED
|
||||
sign_up_method_not_enabled: 'This sign up method is not enabled.', // UNTRANSLATED
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.', // UNTRANSLATED
|
||||
same_password: 'Your new password can’t be the same as your current password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before sign in.', // UNTRANSLATED
|
||||
same_password: 'New password cannot be the same as your old password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before signing-in.', // UNTRANSLATED
|
||||
password_exists: 'Your password has been set.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before sign in.', // UNTRANSLATED
|
||||
username_exists: 'Your username has been set.', // UNTRANSLATED
|
||||
require_email: 'You need to set an email before sign in.', // UNTRANSLATED
|
||||
email_exists: 'Your email has been set.', // UNTRANSLATED
|
||||
require_sms: 'You need to set a phone before sign in.', // UNTRANSLATED
|
||||
sms_exists: 'Your phone has been set.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before signing-in.', // UNTRANSLATED
|
||||
username_exists: 'This username is already in use.', // UNTRANSLATED
|
||||
require_email: 'You need to add an email address before signing-in.', // UNTRANSLATED
|
||||
email_exists: 'This email is associated with an existing account.', // UNTRANSLATED
|
||||
require_sms: 'You need to add a phone number before signing-in.', // UNTRANSLATED
|
||||
sms_exists: 'This phone number is associated with an existing account.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.', // UNTRANSLATED
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: '{{name}} 암호화 방법을 지원하지 않아요.',
|
||||
|
|
|
@ -44,16 +44,16 @@ const errors = {
|
|||
cannot_delete_self: 'Não se pode remover a si mesmo.',
|
||||
sign_up_method_not_enabled: 'This sign up method is not enabled.', // UNTRANSLATED
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.', // UNTRANSLATED
|
||||
same_password: 'Your new password can’t be the same as your current password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before sign in.', // UNTRANSLATED
|
||||
same_password: 'New password cannot be the same as your old password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before signing-in.', // UNTRANSLATED
|
||||
password_exists: 'Your password has been set.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before sign in.', // UNTRANSLATED
|
||||
username_exists: 'Your username has been set.', // UNTRANSLATED
|
||||
require_email: 'You need to set an email before sign in.', // UNTRANSLATED
|
||||
email_exists: 'Your email has been set.', // UNTRANSLATED
|
||||
require_sms: 'You need to set a phone before sign in.', // UNTRANSLATED
|
||||
sms_exists: 'Your phone has been set.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before signing-in.', // UNTRANSLATED
|
||||
username_exists: 'This username is already in use.', // UNTRANSLATED
|
||||
require_email: 'You need to add an email address before signing-in.', // UNTRANSLATED
|
||||
email_exists: 'This email is associated with an existing account.', // UNTRANSLATED
|
||||
require_sms: 'You need to add a phone number before signing-in.', // UNTRANSLATED
|
||||
sms_exists: 'This phone number is associated with an existing account.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.', // UNTRANSLATED
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: 'O método de enncriptação {{name}} não é suportado.',
|
||||
|
|
|
@ -45,16 +45,16 @@ const errors = {
|
|||
cannot_delete_self: 'You cannot delete yourself.', // UNTRANSLATED
|
||||
sign_up_method_not_enabled: 'This sign up method is not enabled.', // UNTRANSLATED
|
||||
sign_in_method_not_enabled: 'This sign in method is not enabled.', // UNTRANSLATED
|
||||
same_password: 'Your new password can’t be the same as your current password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before sign in.', // UNTRANSLATED
|
||||
same_password: 'New password cannot be the same as your old password.', // UNTRANSLATED
|
||||
require_password: 'You need to set a password before signing-in.', // UNTRANSLATED
|
||||
password_exists: 'Your password has been set.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before sign in.', // UNTRANSLATED
|
||||
username_exists: 'Your username has been set.', // UNTRANSLATED
|
||||
require_email: 'You need to set an email before sign in.', // UNTRANSLATED
|
||||
email_exists: 'Your email has been set.', // UNTRANSLATED
|
||||
require_sms: 'You need to set a phone before sign in.', // UNTRANSLATED
|
||||
sms_exists: 'Your phone has been set.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to set a phone or email before sign in.', // UNTRANSLATED
|
||||
require_username: 'You need to set a username before signing-in.', // UNTRANSLATED
|
||||
username_exists: 'This username is already in use.', // UNTRANSLATED
|
||||
require_email: 'You need to add an email address before signing-in.', // UNTRANSLATED
|
||||
email_exists: 'This email is associated with an existing account.', // UNTRANSLATED
|
||||
require_sms: 'You need to add a phone number before signing-in.', // UNTRANSLATED
|
||||
sms_exists: 'This phone number is associated with an existing account.', // UNTRANSLATED
|
||||
require_email_or_sms: 'You need to add an email address or phone number before signing-in.', // UNTRANSLATED
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: '{{name}} şifreleme metodu desteklenmiyor.',
|
||||
|
|
|
@ -48,11 +48,11 @@ const errors = {
|
|||
require_password: '请设置密码',
|
||||
password_exists: '密码已设置过',
|
||||
require_username: '请设置用户名',
|
||||
username_exists: '用户名已设置过',
|
||||
username_exists: '该用户名已存在',
|
||||
require_email: '请绑定邮箱地址',
|
||||
email_exists: '已绑定邮箱地址',
|
||||
email_exists: '该邮箱地址已被其它账户绑定',
|
||||
require_sms: '请绑定手机号码',
|
||||
sms_exists: '已绑定手机号码',
|
||||
sms_exists: '该手机号码已被其它账户绑定',
|
||||
require_email_or_sms: '请绑定邮箱地址或手机号码',
|
||||
},
|
||||
password: {
|
||||
|
|
Loading…
Reference in a new issue