feat: ревизия до обновления цен + короткие ссылки /:code для опти
- При нажатии «обновить цены» создаётся ревизия текущего состояния («до обновления цен») через новый эндпоинт POST /api/configs/:uuid/snapshot, затем saveConfig создаёт ревизию с новыми ценами - Роут GET /:code → редирект на /projects/:uuid по коду опти (регистронезависимо) - Валидация кода опти: только URL-безопасные символы [A-Za-z0-9._-] (бэкенд + клиентская проверка + подсказка в форме) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2975,6 +2975,15 @@ async function refreshPrices() {
|
||||
}
|
||||
beforeTotal *= serverCount;
|
||||
|
||||
// Create a revision of the current state before prices are updated
|
||||
if (configUUID) {
|
||||
try {
|
||||
await fetch('/api/configs/' + configUUID + '/snapshot', { method: 'POST' });
|
||||
} catch (e) {
|
||||
console.warn('pre-refresh snapshot failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
await saveConfig(false);
|
||||
await refreshPriceLevels({ force: true, noCache: true });
|
||||
renderTab();
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
<div>
|
||||
<label for="create-project-code" class="block text-sm font-medium text-gray-700 mb-1">Код проекта</label>
|
||||
<input id="create-project-code" type="text" placeholder="Например: OPS-123"
|
||||
pattern="[A-Za-z0-9._-]+"
|
||||
title="Только буквы, цифры, дефис, точка, подчёркивание"
|
||||
class="w-full px-3 py-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
||||
<p class="text-xs text-gray-400 mt-1">Буквы, цифры, дефис, точка, подчёркивание. Код используется в URL.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label for="create-project-variant" class="block text-sm font-medium text-gray-700 mb-1">Вариант (необязательно)</label>
|
||||
@@ -396,6 +399,10 @@ async function createProject() {
|
||||
alert('Введите код проекта');
|
||||
return;
|
||||
}
|
||||
if (!/^[A-Za-z0-9._-]+$/.test(code)) {
|
||||
alert('Код проекта содержит недопустимые символы.\nРазрешены: буквы, цифры, дефис, точка, подчёркивание.');
|
||||
return;
|
||||
}
|
||||
const resp = await fetch('/api/projects', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
@@ -411,6 +418,11 @@ async function createProject() {
|
||||
alert('Проект с таким кодом и вариантом уже существует');
|
||||
return;
|
||||
}
|
||||
if (resp.status === 400) {
|
||||
const body = await resp.json().catch(() => ({}));
|
||||
alert(body.error || 'Некорректный запрос');
|
||||
return;
|
||||
}
|
||||
alert('Не удалось создать проект');
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user