fix(number-fields): избегаем потери точности длинных чисел
- number-поля теперь рендерятся как text + inputMode=numeric, чтобы браузер не округлял значения через input type=number - пробелы при вставке в number-поля удаляются - бэкенд нормализует значения number-полей в строку перед сохранением - добавлен хелпер normalizeFieldValueForStorage Closes: искажение расчётного счёта и других длинных числовых полей
This commit is contained in:
51
server/utils/password.ts
Normal file
51
server/utils/password.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import bcrypt from 'bcrypt';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const SALT_ROUNDS = 12;
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
if (password.length < 8) {
|
||||
throw new Error('Пароль должен содержать минимум 8 символов');
|
||||
}
|
||||
|
||||
const hasUpperCase = /[A-Z]/.test(password);
|
||||
const hasLowerCase = /[a-z]/.test(password);
|
||||
const hasNumbers = /\d/.test(password);
|
||||
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
|
||||
|
||||
if (!hasUpperCase || !hasLowerCase || !hasNumbers || !hasSpecialChar) {
|
||||
throw new Error('Пароль должен содержать заглавные и строчные буквы, цифры и специальные символы');
|
||||
}
|
||||
|
||||
return await bcrypt.hash(password, SALT_ROUNDS);
|
||||
}
|
||||
|
||||
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
|
||||
return await bcrypt.compare(password, hash);
|
||||
}
|
||||
|
||||
export function generateTempPassword(): string {
|
||||
const lower = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const digits = '0123456789';
|
||||
const special = '!@#$%^&*';
|
||||
const charset = lower + upper + digits + special;
|
||||
|
||||
const pick = (set: string) => set[crypto.randomInt(set.length)];
|
||||
|
||||
const chars: string[] = [
|
||||
pick(lower),
|
||||
pick(upper),
|
||||
pick(digits),
|
||||
pick(special),
|
||||
...Array.from({ length: 8 }, () => pick(charset)),
|
||||
];
|
||||
|
||||
// Shuffle using Fisher-Yates with crypto.randomInt
|
||||
for (let i = chars.length - 1; i > 0; i--) {
|
||||
const j = crypto.randomInt(i + 1);
|
||||
[chars[i], chars[j]] = [chars[j], chars[i]];
|
||||
}
|
||||
|
||||
return chars.join('');
|
||||
}
|
||||
Reference in New Issue
Block a user