Skip to content

Commit 541db58

Browse files
narrowizardclaude
andcommitted
feat(services): implement case-insensitive handling for account and api_key
Replace citext extension behavior with application-layer handling: - account.name and account.email: use LOWER() for login, toLowerCase() for INSERT/UPDATE - api_key.name: use toLowerCase() for INSERT and existence check Fixes test validation for merico_metric_system type. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2fdc372 commit 541db58

File tree

2 files changed

+16
-14
lines changed

2 files changed

+16
-14
lines changed

api/src/services/account.service.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ export class AccountService {
5858
) {
5959
const accountRepo = dashboardDataSource.getRepository(Account);
6060
const where: { [field: string]: string }[] = [];
61-
if (name !== undefined && account.name !== name) {
62-
where.push({ name });
61+
if (name !== undefined && account.name !== name.toLowerCase()) {
62+
where.push({ name: name.toLowerCase() });
6363
}
64-
if (email !== undefined && account.email !== email) {
65-
where.push({ email });
64+
if (email !== undefined && account.email !== email.toLowerCase()) {
65+
where.push({ email: email.toLowerCase() });
6666
}
6767
if (where.length && (await accountRepo.exist({ where }))) {
6868
throw new ApiError(BAD_REQUEST, { message: translate('ACCOUNT_NAME_EMAIL_ALREADY_EXISTS', locale) });
@@ -71,7 +71,8 @@ export class AccountService {
7171

7272
async login(name: string, password: string, locale: string): Promise<AccountLoginResponse> {
7373
const account = await AccountService.accountDetailsQuery()
74-
.where('account.name = :name or account.email = :name', { name })
74+
.where('LOWER(account.name) = LOWER(:name)', { name })
75+
.orWhere('LOWER(account.email) = LOWER(:name)', { name })
7576
.getRawOne<AccountAPIModel & { password: string }>();
7677
if (!account) {
7778
throw new ApiError(INVALID_CREDENTIALS, { message: translate('ACCOUNT_INVALID_CREDENTIALS', locale) });
@@ -130,16 +131,16 @@ export class AccountService {
130131
): Promise<AccountAPIModel> {
131132
const accountRepo = dashboardDataSource.getRepository(Account);
132133
const roleRepo = dashboardDataSource.getRepository(Role);
133-
const where = [{ name }, { email }];
134+
const where = [{ name: name.toLowerCase() }, { email: email?.toLowerCase() }];
134135
if (await accountRepo.exist({ where })) {
135136
throw new ApiError(BAD_REQUEST, { message: translate('ACCOUNT_NAME_EMAIL_ALREADY_EXISTS', locale) });
136137
}
137138
if (!(await roleRepo.exist({ where: { id: role_id } }))) {
138139
throw new ApiError(BAD_REQUEST, { message: translate('ROLE_NOT_FOUND', locale) });
139140
}
140141
const account = new Account();
141-
account.name = name;
142-
account.email = email === undefined ? null : email;
142+
account.name = name.toLowerCase();
143+
account.email = email === undefined ? null : email.toLowerCase();
143144
account.role_id = role_id;
144145
account.password = await bcrypt.hash(password, SALT_ROUNDS);
145146
const { id } = await accountRepo.save(account);
@@ -176,8 +177,8 @@ export class AccountService {
176177
if (account.role_id === FIXED_ROLE_TYPES.SUPERADMIN) {
177178
throw new ApiError(BAD_REQUEST, { message: translate('ACCOUNT_NO_EDIT_SUPERADMIN', locale) });
178179
}
179-
account.name = name ?? account.name;
180-
account.email = email === undefined ? account.email : email;
180+
account.name = name === undefined ? account.name : name.toLowerCase();
181+
account.email = email === undefined ? account.email : email.toLowerCase();
181182
await accountRepo.save(account);
182183
const result = await AccountService.accountDetailsQuery()
183184
.where('account.id = :id', { id })
@@ -214,8 +215,8 @@ export class AccountService {
214215
}
215216
account.password = await bcrypt.hash(new_password, SALT_ROUNDS);
216217
}
217-
account.name = name === undefined ? account.name : name;
218-
account.email = email === undefined ? account.email : email;
218+
account.name = name === undefined ? account.name : name.toLowerCase();
219+
account.email = email === undefined ? account.email : email.toLowerCase();
219220
account.role_id = role_id === undefined ? account.role_id : role_id;
220221
await accountRepo.save(account);
221222
const result = await AccountService.accountDetailsQuery()

api/src/services/api.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,15 @@ export class ApiService {
8080

8181
async createKey(name: string, role_id: string, locale: string): Promise<{ app_id: string; app_secret: string }> {
8282
const apiKeyRepo = dashboardDataSource.getRepository(ApiKey);
83-
if (await apiKeyRepo.exist({ where: { name, is_preset: false } })) {
83+
const normalizedName = name.toLowerCase();
84+
if (await apiKeyRepo.exist({ where: { name: normalizedName, is_preset: false } })) {
8485
throw new ApiError(BAD_REQUEST, { message: translate('APIKEY_NAME_ALREADY_EXISTS', locale) });
8586
}
8687
if (!(await dashboardDataSource.getRepository(Role).exist({ where: { id: role_id } }))) {
8788
throw new ApiError(BAD_REQUEST, { message: translate('ROLE_NOT_FOUND', locale) });
8889
}
8990
const apiKey = new ApiKey();
90-
apiKey.name = name;
91+
apiKey.name = normalizedName;
9192
apiKey.role_id = role_id;
9293
apiKey.app_id = crypto.randomBytes(8).toString('hex');
9394
apiKey.app_secret = crypto.randomBytes(16).toString('hex');

0 commit comments

Comments
 (0)