fix: inconsistent frontend after logout (both admin button and HFS.userBelongsTo plugins API)
Massimo Melina committed
Feb 28, 2026 at 17:21 UTC
60175f92d96277ec5642dfa1fc8529e5beea7425
4 files changed
+11
-13
frontend/src/login.ts
+1
-1
@@ -41,7 +41,7 @@ export function logout() {
41
return apiCall('logout', {}, { modal: working }).catch(res => {
42
if (res.code !== HTTP_UNAUTHORIZED) // we expect this error code
43
throw res
44
- state.username = ''
44
+ refreshSession()
45
if (fallbackToBasicAuth())
46
return location.reload() // reloading avoids nasty warnings with ff52
47
reloadList()
frontend/src/state.ts
+1
-2
@@ -38,9 +38,8 @@ export const state = proxy<typeof FRONTEND_OPTIONS & {
38
canChangePassword: boolean
39
uri: string
40
uploadOnExisting: 'skip' | 'overwrite' | 'rename'
41
- expandedUsername: string[]
41
+ expandedUsername?: string[]
42
}>({
43
- expandedUsername: [],
43
uploadOnExisting: getHFS().dontOverwriteUploading ? 'rename' : 'skip',
44
uri: '',
45
canChangePassword: false,
shared/index.ts
+8
-9
@@ -2,7 +2,7 @@
2
3
import _ from 'lodash'
4
import { apiCall } from './api'
5
-import { DAY, Dict, formatBytes, HOUR, MINUTE, objSameKeys, typedEntries, wantArray } from '../src/cross'
5
+import { DAY, Dict, formatBytes, HOUR, MINUTE, objFromKeys, objSameKeys, typedEntries, wantArray } from '../src/cross'
6
export * from './react'
7
export * from './dialogs'
8
export * from './md'
@@ -32,7 +32,7 @@ Object.assign(HFS, {
32
getPluginPublic: () => getScriptAttr('src')?.match(/^.*\//)?.[0],
33
getPluginConfig: () => HFS.plugins[HFS.getPluginKey()] || {},
34
loadScript: (uri: string) => loadScript(uri.includes('//') || uri.startsWith('/') ? uri : HFS.getPluginPublic() + uri),
35
- userBelongsTo: (username: string | string[]) => wantArray(username).some(x => HFS.state.expandedUsername.includes(x)),
35
+ userBelongsTo: (username: string | string[]) => wantArray(username).some(x => HFS.state.expandedUsername?.includes(x)),
36
cpuSpeedIndex,
37
copyTextToClipboard,
38
urlParams,
@@ -135,20 +135,19 @@ export function getPrefixUrl() {
135
136
export function makeSessionRefresher(state: any) {
137
let timeout: any
138
- const initial = getHFS().session
139
- refreshSession(initial)
138
+ refreshSession(getHFS().session)
139
return refreshSession
140
142
- function refreshSession(response: any) {
143
- if (!response) return
141
+ function refreshSession(response?: any) {
142
+ clearTimeout(timeout)
143
+ const keys = ['username', 'adminUrl', 'canChangePassword', 'accountExp', 'expandedUsername', 'requireChangePassword']
144
+ response ??= objFromKeys(keys, () => undefined)
145
const { exp } = response
145
- Object.assign(initial, response) // keep it updated, not necessary, just in case someone is looking at this instead of the state
146
- Object.assign(state, _.pick(response, ['username', 'adminUrl', 'canChangePassword', 'accountExp', 'expandedUsername']))
146
+ getHFS().session = Object.assign(state, _.pick(response, keys))
147
if (!response.username || !exp) return
148
const delta = new Date(exp).getTime() - Date.now()
149
const t = _.clamp(delta - 30_000, 4_000, 600_000)
150
console.debug('session refresh in', Math.round(t / 1000))
151
- clearTimeout(timeout)
151
timeout = setTimeout(() => apiCall('refresh_session').then(refreshSession), t)
152
}
153
}
src/api.auth.ts
+1
-1
@@ -115,7 +115,7 @@ export const refresh_session: ApiHandler = async ({}, ctx) => {
115
adminUrl: ctxAdminAccess(ctx) ? ctx.state.revProxyPath + ADMIN_URI : undefined,
116
canChangePassword: accountCanChangePassword(ctx.state.account),
117
requireChangePassword: ctx.state.account?.require_password_change,
118
- exp: keepSessionAlive.get() ? new Date(Date.now() + sessionDuration.compiled()) : undefined,
118
+ exp: username && keepSessionAlive.get() ? new Date(Date.now() + sessionDuration.compiled()) : undefined,
119
accountExp: ctx.state.account?.expire,
120
}
121
}