From d44eb2be8db6ef89aaf4c435bd742596e582ad0d Mon Sep 17 00:00:00 2001 From: root Date: Thu, 20 Aug 2026 22:04:27 +0000 Subject: [PATCH] =?UTF-8?q?Fix:=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20c?= =?UTF-8?q?ookie=20auth=20(=D0=B1=D0=B5=D0=B7=20Authorization)=20=E2=80=94?= =?UTF-8?q?=20=D0=B2=D1=81=D0=B5=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81?= =?UTF-8?q?=D1=8B=20=D0=B1=D0=B5=D0=B7=20preflight?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api.js: убрал Authorization header для всех методов (cookie достаточно) - убрал refresh block (cookie живёт дольше) - profile.save: явный feedback ("⏳ Сохраняю..." → "✓ Сохранено") + try/catch вокруг TG_APP.haptic --- public/js/api.js | 23 ++--------------------- public/js/views/profile.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/public/js/api.js b/public/js/api.js index 1cfce87..36cdbc4 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -42,32 +42,13 @@ const Store = { async function api(path, opts = {}) { opts.headers = opts.headers || {}; - // для GET используем cookie (без CORS preflight), для мутаций — Authorization - const isGet = !opts.method || opts.method === 'GET' || opts.method === 'HEAD'; - if (Store.access && !opts.headers.Authorization && !isGet) { - opts.headers.Authorization = `Bearer ${Store.access}`; - } + // НЕ шлём Authorization — используем HttpOnly cookie (без CORS preflight) + // Middleware читает cookie buhapp_at if (opts.body && typeof opts.body === 'object' && !(opts.body instanceof FormData)) { opts.headers['Content-Type'] = 'application/json'; opts.body = JSON.stringify(opts.body); } let r = await fetch(API_BASE + path, { ...opts, credentials: 'include' }); - if (r.status === 401 && Store.refresh) { - // refresh - const r2 = await fetch(API_BASE + '/api/v1/auth/refresh', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ Refresh: Store.refresh }), - }); - if (r2.ok) { - const tokens = await r2.json(); - Store.save(tokens); - opts.headers.Authorization = `Bearer ${Store.access}`; - r = await fetch(API_BASE + path, opts); - } else { - Store.clear(); - } - } const text = await r.text(); let data = null; try { data = text ? JSON.parse(text) : null; } catch { data = text; } diff --git a/public/js/views/profile.js b/public/js/views/profile.js index 64cb039..f320591 100644 --- a/public/js/views/profile.js +++ b/public/js/views/profile.js @@ -95,6 +95,9 @@ const ProfileView = { }, async save() { + const btn = document.getElementById('p-save'); + const orig = btn ? btn.textContent : null; + if (btn) { btn.disabled = true; btn.textContent = '⏳ Сохраняю…'; } try { await API.Auth.updateMe({ name: document.getElementById('p-name').value.trim(), @@ -107,10 +110,14 @@ const ProfileView = { activities: [...this.activities], purposes: [...this.purposes], }); - TG_APP.haptic('notification'); - App.toast('Сохранено ✓'); + try { TG_APP.haptic('notification'); } catch {} + App.toast('✓ Сохранено'); + if (btn) { btn.textContent = '✓ Сохранено'; setTimeout(() => { btn.textContent = orig || 'Сохранить'; btn.disabled = false; }, 1500); } } catch (e) { - TG_APP.showAlert('Ошибка: ' + e.message); + console.error('save profile:', e); + App.toast('⚠️ ' + e.message); + TG_APP.showAlert('Ошибка сохранения: ' + e.message); + if (btn) { btn.disabled = false; btn.textContent = orig || 'Сохранить'; } } }, };