Fix: cache-bust + retry + graceful 401
- ?v=8 на всех JS (CF cache bypass) - api(): retry при 502/503/504 и network error - api(): защита от infinite loop при refresh (флаг _isRefresh) - App.init: при протухшем токене сразу showWelcome (без showError)
This commit is contained in:
parent
716f074ea7
commit
7feb693df9
@ -34,15 +34,15 @@
|
|||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<script src="/js/tg.js"></script>
|
<script src="/js/tg.js?v=8"></script>
|
||||||
<script src="/js/api.js"></script>
|
<script src="/js/api.js?v=8"></script>
|
||||||
<script src="/js/views/map.js"></script>
|
<script src="/js/views/map.js?v=8"></script>
|
||||||
<script src="/js/views/chats.js"></script>
|
<script src="/js/views/chats.js?v=8"></script>
|
||||||
<script src="/js/views/chat.js"></script>
|
<script src="/js/views/chat.js?v=8"></script>
|
||||||
<script src="/js/views/profile.js"></script>
|
<script src="/js/views/profile.js?v=8"></script>
|
||||||
<script src="/js/views/review.js"></script>
|
<script src="/js/views/review.js?v=8"></script>
|
||||||
<script src="/js/views/settings.js"></script>
|
<script src="/js/views/settings.js?v=8"></script>
|
||||||
<script src="/js/views/onboarding.js"></script>
|
<script src="/js/views/onboarding.js?v=8"></script>
|
||||||
<script src="/js/app.js"></script>
|
<script src="/js/app.js?v=8"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const Store = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
async function api(path, opts = {}) {
|
async function api(path, opts = {}, _retry = 0) {
|
||||||
opts.headers = opts.headers || {};
|
opts.headers = opts.headers || {};
|
||||||
if (Store.access && !opts.headers.Authorization) {
|
if (Store.access && !opts.headers.Authorization) {
|
||||||
opts.headers.Authorization = `Bearer ${Store.access}`;
|
opts.headers.Authorization = `Bearer ${Store.access}`;
|
||||||
@ -49,23 +49,41 @@ async function api(path, opts = {}) {
|
|||||||
opts.headers['Content-Type'] = 'application/json';
|
opts.headers['Content-Type'] = 'application/json';
|
||||||
opts.body = JSON.stringify(opts.body);
|
opts.body = JSON.stringify(opts.body);
|
||||||
}
|
}
|
||||||
let r = await fetch(API_BASE + path, opts);
|
let r;
|
||||||
if (r.status === 401 && Store.refresh) {
|
try {
|
||||||
|
r = await fetch(API_BASE + path, opts);
|
||||||
|
} catch (networkErr) {
|
||||||
|
// сеть упала — ретрай через 2с
|
||||||
|
if (_retry < 1) {
|
||||||
|
await new Promise((res) => setTimeout(res, 2000));
|
||||||
|
return api(path, opts, _retry + 1);
|
||||||
|
}
|
||||||
|
throw new Error('Нет связи с сервером');
|
||||||
|
}
|
||||||
|
if (r.status === 401 && Store.refresh && !opts._isRefresh) {
|
||||||
// refresh
|
// refresh
|
||||||
const r2 = await fetch(API_BASE + '/api/v1/auth/refresh', {
|
const r2 = await fetch(API_BASE + '/api/v1/auth/refresh', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ Refresh: Store.refresh }),
|
body: JSON.stringify({ Refresh: Store.refresh }),
|
||||||
|
_isRefresh: true,
|
||||||
});
|
});
|
||||||
if (r2.ok) {
|
if (r2.ok) {
|
||||||
const tokens = await r2.json();
|
const tokens = await r2.json();
|
||||||
Store.save(tokens);
|
Store.save(tokens);
|
||||||
opts.headers.Authorization = `Bearer ${Store.access}`;
|
opts.headers.Authorization = `Bearer ${Store.access}`;
|
||||||
r = await fetch(API_BASE + path, opts);
|
return api(path, opts, _retry + 1);
|
||||||
} else {
|
} else {
|
||||||
Store.clear();
|
Store.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (r.status === 502 || r.status === 503 || r.status === 504) {
|
||||||
|
if (_retry < 1) {
|
||||||
|
await new Promise((res) => setTimeout(res, 2000));
|
||||||
|
return api(path, opts, _retry + 1);
|
||||||
|
}
|
||||||
|
throw new Error('Сервер перезапускается, попробуй через минуту');
|
||||||
|
}
|
||||||
const text = await r.text();
|
const text = await r.text();
|
||||||
let data = null;
|
let data = null;
|
||||||
try { data = text ? JSON.parse(text) : null; } catch { data = text; }
|
try { data = text ? JSON.parse(text) : null; } catch { data = text; }
|
||||||
|
|||||||
@ -41,13 +41,16 @@ const App = {
|
|||||||
this.showError('Не удалось войти через Telegram: ' + e.message);
|
this.showError('Не удалось войти через Telegram: ' + e.message);
|
||||||
}
|
}
|
||||||
} else if (API.Store.access) {
|
} else if (API.Store.access) {
|
||||||
|
// есть токен — пробуем
|
||||||
try {
|
try {
|
||||||
await API.Auth.me();
|
await API.Auth.me();
|
||||||
await this.checkConsent();
|
await this.checkConsent();
|
||||||
this.showView('map');
|
this.showView('map');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
// токен протух или сеть — сразу на welcome, без показа ошибки
|
||||||
|
console.warn('init me() failed:', e);
|
||||||
API.Store.clear();
|
API.Store.clear();
|
||||||
this.showError('Сессия истекла: ' + e.message);
|
this.showWelcome();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Вне Telegram — показать email login (но сначала согласия)
|
// Вне Telegram — показать email login (но сначала согласия)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user