a simple web player for subsonic tinysub.devins.page
subsonic navidrome javascript
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: clear db button

intergrav 50f02ac9 a49a3147

+58 -54
+1
src/index.html
··· 125 125 /> 126 126 </div> 127 127 <div class="modal-actions"> 128 + <button id="clear-db-settings-btn">clear dbs</button> 128 129 <button id="logout-settings-btn" class="danger">logout</button> 129 130 <button id="close-settings-btn">close</button> 130 131 </div>
+32 -48
src/js/auth.js
··· 1 1 // authorization 2 2 3 - const CredentialManager = { 4 - save: (server, username, token, salt) => { 5 - const creds = { server, username, token, salt }; 6 - localStorage.setItem("tinysub_credentials", JSON.stringify(creds)); 7 - }, 8 - load: () => { 9 - const saved = localStorage.getItem("tinysub_credentials"); 10 - return saved 11 - ? JSON.parse(saved) 12 - : { server: "", username: "", token: "", salt: "" }; 13 - }, 14 - clear: () => { 15 - localStorage.removeItem("tinysub_credentials"); 16 - }, 17 - }; 18 - 19 - // toggle auth modal 20 - const toggleAuthModal = (show) => { 21 - const authModal = document.getElementById(DOM_IDS.AUTH_MODAL); 22 - if (!authModal) return; 23 - 24 - if (show) { 25 - showModal(authModal, { 26 - focusSelector: "input", 27 - }); 28 - } else { 29 - hideModal(DOM_IDS.AUTH_MODAL); 30 - } 31 - }; 32 - 33 3 // load library, playlists, and favorites after successful login 34 4 async function initializeApp() { 35 - toggleAuthModal(false); 5 + const authModal = document.getElementById(DOM_IDS.AUTH_MODAL); 6 + if (authModal) hideModal(DOM_IDS.AUTH_MODAL); 36 7 await loadQueue().catch(() => {}); 37 8 updateQueueDisplay(); 38 9 await loadLibrary(); ··· 82 53 await state.api.ping(); 83 54 84 55 // Save credentials for auto-login 85 - CredentialManager.save(validServerUrl, validUsername, token, salt); 56 + localStorage.setItem( 57 + "tinysub_credentials", 58 + JSON.stringify({ 59 + server: validServerUrl, 60 + username: validUsername, 61 + token, 62 + salt, 63 + }), 64 + ); 86 65 87 66 await initializeApp(); 88 67 } catch (error) { ··· 103 82 indexedDB.deleteDatabase(db.name); 104 83 } 105 84 } 106 - CredentialManager.clear(); 85 + localStorage.clear(); 86 + alert("logging out, local databases and credentials cleared"); 107 87 } catch (error) { 108 88 console.error("[Auth] Logout cleanup failed:", error); 109 89 } ··· 112 92 113 93 // attempt auto-login on page load 114 94 async function attemptAutoLogin() { 115 - const creds = CredentialManager.load(); 95 + const saved = localStorage.getItem("tinysub_credentials"); 96 + const creds = saved 97 + ? JSON.parse(saved) 98 + : { server: "", username: "", token: "", salt: "" }; 116 99 117 - // security migration in case you were using version <1.8 which stored raw passwords 118 - const oldCredentials = localStorage.getItem("tinysub_credentials"); 119 - if (oldCredentials) { 100 + // migration from 1.7.x 101 + if (saved) { 120 102 try { 121 - const parsed = JSON.parse(oldCredentials); 103 + const parsed = JSON.parse(saved); 122 104 if (parsed.password) { 123 - console.warn( 124 - "[Auth] Old password format detected, clearing storage and logging out for security", 105 + console.warn("[Auth] Old password format detected, logging out"); 106 + alert( 107 + "tinysub 1.8+ now uses token-based auth and also has other incompatible changes from <1.8, logging out to clear old credentials", 125 108 ); 126 109 await handleLogout(); 127 110 return; 128 111 } 129 - } catch { 130 - // ignore 131 - } 112 + } catch {} 132 113 } 133 114 134 115 // no stored credentials 135 - if (!creds.server || !creds.username || !creds.token || !creds.salt) { 136 - toggleAuthModal(true); 116 + const hasCredentials = 117 + creds.server && creds.username && creds.token && creds.salt; 118 + if (!hasCredentials) { 119 + const authModal = document.getElementById(DOM_IDS.AUTH_MODAL); 120 + if (authModal) showModal(authModal, { focusSelector: "input" }); 137 121 return; 138 122 } 139 123 ··· 154 138 error.message, 155 139 ); 156 140 state.api = null; 157 - // pre-populate form with stored server and username for convenience 141 + // pre-populate form with stored server and username 158 142 ui.serverInput.value = creds.server; 159 143 ui.usernameInput.value = creds.username; 160 - ui.passwordInput.value = ""; // never prefill password 161 - toggleAuthModal(true); 144 + const authModal = document.getElementById(DOM_IDS.AUTH_MODAL); 145 + if (authModal) showModal(authModal, { focusSelector: "input" }); 162 146 } 163 147 } 164 148
-6
src/js/events.js
··· 52 52 // setup settings modal 53 53 setupSettings(); 54 54 55 - // logout button in settings modal 56 - const logoutBtn = document.getElementById("logout-settings-btn"); 57 - if (logoutBtn) { 58 - logoutBtn.addEventListener("click", handleLogout); 59 - } 60 - 61 55 // login/logout 62 56 ui.loginForm.addEventListener("submit", (e) => { 63 57 e.preventDefault();
+25
src/js/settings.js
··· 80 80 state.settings.scrobbling = scrobbleToggle.checked; 81 81 localStorage.setItem("tinysub_settings", JSON.stringify(state.settings)); 82 82 }); 83 + 84 + // clear dbs button 85 + const clearCacheBtn = document.getElementById("clear-db-settings-btn"); 86 + if (clearCacheBtn) { 87 + clearCacheBtn.addEventListener("click", async () => { 88 + try { 89 + if (indexedDB.databases) { 90 + const dbs = await indexedDB.databases(); 91 + for (const db of dbs) { 92 + indexedDB.deleteDatabase(db.name); 93 + } 94 + } 95 + alert("local databases cleared"); 96 + location.reload(); 97 + } catch (error) { 98 + console.error("[Settings] Failed to clear databases:", error); 99 + } 100 + }); 101 + } 102 + 103 + // logout button 104 + const logoutBtn = document.getElementById("logout-settings-btn"); 105 + if (logoutBtn) { 106 + logoutBtn.addEventListener("click", handleLogout); 107 + } 83 108 }