Новые изменения в компоненты
This commit is contained in:
parent
da261fcf9f
commit
f14125d198
2 changed files with 123 additions and 21 deletions
|
|
@ -1,11 +1,55 @@
|
||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro';
|
||||||
|
|
||||||
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090';
|
const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090';
|
||||||
|
const PB_ADMIN_EMAIL = import.meta.env.PB_ADMIN_EMAIL;
|
||||||
|
const PB_ADMIN_PASSWORD = import.meta.env.PB_ADMIN_PASSWORD;
|
||||||
|
|
||||||
const PASSWORD_MIN_LENGTH = 8;
|
const PASSWORD_MIN_LENGTH = 8;
|
||||||
const PASSWORD_MAX_LENGTH = 12;
|
const PASSWORD_MAX_LENGTH = 12;
|
||||||
const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d_!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+$/;
|
const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d_!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+$/;
|
||||||
|
|
||||||
|
async function getAdminToken(): Promise<string> {
|
||||||
|
const response = await fetch(`${PB_POCKETBASE_URL}/api/admins/auth-with-password`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
identity: PB_ADMIN_EMAIL,
|
||||||
|
password: PB_ADMIN_PASSWORD
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Не удалось авторизоваться как админ');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getUserIdByResetToken(token: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const adminToken = await getAdminToken();
|
||||||
|
|
||||||
|
const response = await fetch(`${PB_POCKETBASE_URL}/api/collections/users/records?filter=reset_token="${token}"&limit=1`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${adminToken}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) return null;
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.items && data.items.length > 0) {
|
||||||
|
return data.items[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[CONFIRM_RESET] Error getting user by token:', e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function validatePassword(password: string): { valid: boolean; error?: string } {
|
function validatePassword(password: string): { valid: boolean; error?: string } {
|
||||||
if (!password || password.length < PASSWORD_MIN_LENGTH) {
|
if (!password || password.length < PASSWORD_MIN_LENGTH) {
|
||||||
return { valid: false, error: 'Пароль должен быть не менее 8 символов' };
|
return { valid: false, error: 'Пароль должен быть не менее 8 символов' };
|
||||||
|
|
@ -62,39 +106,48 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmUrl = `${PB_POCKETBASE_URL}/api/collections/users/confirm-password-reset`;
|
// Ищем пользователя по токену через Admin API
|
||||||
|
const userId = await getUserIdByResetToken(token);
|
||||||
const response = await fetch(confirmUrl, {
|
|
||||||
method: 'POST',
|
if (!userId) {
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ token, password, passwordConfirm }),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 204) {
|
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({ error: 'Неверный или истёкший токен сброса пароля' }),
|
||||||
success: true,
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
||||||
message: 'Пароль успешно изменён'
|
|
||||||
}),
|
|
||||||
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data;
|
// Обновляем пароль через Admin API (обходит валидацию PB)
|
||||||
try {
|
const adminToken = await getAdminToken();
|
||||||
data = await response.json();
|
|
||||||
} catch {
|
const updateResponse = await fetch(`${PB_POCKETBASE_URL}/api/collections/users/records/${userId}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${adminToken}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
password: password,
|
||||||
|
passwordConfirm: passwordConfirm,
|
||||||
|
reset_token: '', // Очищаем токен
|
||||||
|
reset_token_expires: ''
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updateResponse.ok) {
|
||||||
|
const updateData = await updateResponse.json();
|
||||||
|
console.log('[CONFIRM_RESET] Update error:', updateData);
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: 'Ошибка обработки ответа' }),
|
JSON.stringify({ error: 'Не удалось обновить пароль' }),
|
||||||
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
{ status: 500, headers: { 'Content-Type': 'application/json' } }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
error: data.message || 'Неверный или истёкший токен сброса пароля'
|
success: true,
|
||||||
|
message: 'Пароль успешно изменён'
|
||||||
}),
|
}),
|
||||||
{ status: response.status, headers: { 'Content-Type': 'application/json' } }
|
{ status: 200, headers: { 'Content-Type': 'application/json' } }
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -162,6 +162,55 @@ export const POST: APIRoute = async ({ request }) => {
|
||||||
|
|
||||||
// Создаём свой токен сброса
|
// Создаём свой токен сброса
|
||||||
const resetToken = Buffer.from(`${user.id}:${Date.now()}`).toString('base64').replace(/=/g, '');
|
const resetToken = Buffer.from(`${user.id}:${Date.now()}`).toString('base64').replace(/=/g, '');
|
||||||
|
|
||||||
|
// Сохраняем токен в поле пользователя
|
||||||
|
try {
|
||||||
|
await pbAdmin.collection('users').update(user.id, {
|
||||||
|
reset_token: resetToken,
|
||||||
|
reset_token_expires: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
||||||
|
});
|
||||||
|
} catch (updateError) {
|
||||||
|
console.log('Fields reset_token may not exist, trying to add:', updateError);
|
||||||
|
// Поля могут не существовать - попробуем создать их
|
||||||
|
try {
|
||||||
|
const adminToken = await pbAdmin.authStore.token;
|
||||||
|
|
||||||
|
// Проверяем существуют ли поля
|
||||||
|
const collectionResponse = await fetch(`${PB_POCKETBASE_URL}/api/collections/users`, {
|
||||||
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
||||||
|
});
|
||||||
|
const collectionData = await collectionResponse.json();
|
||||||
|
|
||||||
|
const fieldExists = collectionData.fields?.some((f: any) => f.name === 'reset_token');
|
||||||
|
|
||||||
|
if (!fieldExists) {
|
||||||
|
// Добавляем поля в коллекцию
|
||||||
|
const newFields = [
|
||||||
|
...(collectionData.fields || []),
|
||||||
|
{ name: 'reset_token', type: 'text' },
|
||||||
|
{ name: 'reset_token_expires', type: 'text' }
|
||||||
|
];
|
||||||
|
|
||||||
|
await fetch(`${PB_POCKETBASE_URL}/api/collections/users`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${adminToken}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ fields: newFields })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пробуем снова обновить
|
||||||
|
await pbAdmin.collection('users').update(user.id, {
|
||||||
|
reset_token: resetToken,
|
||||||
|
reset_token_expires: new Date(Date.now() + 60 * 60 * 1000).toISOString()
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save reset token:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const resetLink = `${getSiteUrl()}/auth/reset-password?token=${resetToken}&userId=${user.id}`;
|
const resetLink = `${getSiteUrl()}/auth/reset-password?token=${resetToken}&userId=${user.id}`;
|
||||||
|
|
||||||
// Отправляем письмо через SMTP.BZ
|
// Отправляем письмо через SMTP.BZ
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue