fix(auth): implement DB-first auth, persistent bearer token, secure cookies, and CLI reset_password script
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>Notes — Персональный тренажер и словарь английского языка</title>
|
||||
<meta name="description" content="Защищённый персональный тренажёр и словарь английского языка с интервальными повторениями SRS SM-2, контекстным поиском и озвучкой">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=2.8">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=2.9">
|
||||
|
||||
<!-- PWA meta and icons -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
@@ -928,6 +928,7 @@
|
||||
<!-- Audio Element for native streaming -->
|
||||
<audio id="global-audio-player" preload="none"></audio>
|
||||
|
||||
<script src="/static/js/app.js?v=2.8"></script>
|
||||
<script src="/static/js/app.js?v=2.9"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
+29
-1
@@ -1,5 +1,27 @@
|
||||
// Notes Application Frontend Logic - Complete SRS, Heatmap, Quiz, and Analytics
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Persistent session: auto-inject Authorization header if token exists in localStorage
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function(url, options = {}) {
|
||||
const token = localStorage.getItem('notes_token');
|
||||
if (token && typeof url === 'string' && url.startsWith('/api/')) {
|
||||
options.headers = options.headers || {};
|
||||
if (options.headers instanceof Headers) {
|
||||
if (!options.headers.has('Authorization')) {
|
||||
options.headers.set('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
} else if (Array.isArray(options.headers)) {
|
||||
if (!options.headers.some(([k]) => k.toLowerCase() === 'authorization')) {
|
||||
options.headers.push(['Authorization', `Bearer ${token}`]);
|
||||
}
|
||||
} else {
|
||||
if (!options.headers['Authorization']) {
|
||||
options.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return originalFetch.call(this, url, options);
|
||||
};
|
||||
|
||||
// Register PWA Service Worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js', { scope: '/' })
|
||||
@@ -7,6 +29,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
.catch(err => console.log('Notes ServiceWorker failed:', err));
|
||||
}
|
||||
|
||||
|
||||
// App State
|
||||
let currentUser = null;
|
||||
let currentLookupData = null;
|
||||
@@ -385,6 +408,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (resp.ok && data.status === 'ok') {
|
||||
if (data.token) {
|
||||
localStorage.setItem('notes_token', data.token);
|
||||
}
|
||||
currentUser = data.user;
|
||||
showApp();
|
||||
loadInitialData();
|
||||
@@ -399,11 +425,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
logoutBtn.addEventListener('click', async () => {
|
||||
localStorage.removeItem('notes_token');
|
||||
await fetch('/api/logout', { method: 'POST' });
|
||||
currentUser = null;
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Data Loading: Cards, Collections, Tags, Stats
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user