optimized list protocol for space
Massimo Melina committed
Nov 11, 2023 at 11:49 UTC
2ea262e938b89456b778d969894dde4cdcb747d6
4 files changed
+25
-24
admin/src/api.ts
+7
-7
@@ -1,7 +1,7 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
import { createElement as h, useEffect, useMemo, useRef, useState } from 'react'
4
-import { Dict, err2msg, Falsy, IconBtn, spinner, useStateMounted, wantArray, xlate } from './misc'
4
+import { Dict, err2msg, Falsy, IconBtn, LIST, spinner, useStateMounted, wantArray, xlate } from './misc'
5
import { Alert } from '@mui/material'
6
import _ from 'lodash'
7
import { state } from './state'
@@ -75,34 +75,34 @@ export function useApiList<T=any, S=T>(cmd:string|Falsy, params: Dict={}, { map
75
if (!Array.isArray(msg))
76
return console.debug('illegal list packet', msg)
77
const [op, par] = msg
78
- if (op === 'ready') {
78
+ if (op === LIST.ready) {
79
apply.flush()
80
setInitializing(false)
81
return
82
}
83
- if (op === 'error') {
83
+ if (op === LIST.error) {
84
if (par === 401)
85
state.loginRequired = msg[2].possible !== false || 403
86
else
87
setError(err2msg(par))
88
return
89
}
90
- if (op === 'props')
90
+ if (op === LIST.props)
91
return setProps(par)
92
- if (op === 'add') {
92
+ if (op === LIST.add) {
93
const mappedPar = map?.(par) ?? par
94
mappedPar.id ??= idGenerator.current = Math.max(idGenerator.current, Date.now()) + .001
95
bufferAdd.push(mappedPar)
96
apply()
97
return
98
}
99
- if (op === 'remove') {
99
+ if (op === LIST.remove) {
100
const match = _.matches(par)
101
if (_.isEmpty(_.remove(bufferAdd, match))) // first remove from the buffer
102
removeOnList.push(match)
103
return
104
}
105
- if (op === 'update') {
105
+ if (op === LIST.update) {
106
const change = msg[2]
107
const found = _.find(bufferAdd, par)
108
if (found)
frontend/src/useFetchList.ts
+4
-4
@@ -7,7 +7,7 @@ import _ from 'lodash'
7
import { subscribeKey } from 'valtio/utils'
8
import { useIsMounted } from 'usehooks-ts'
9
import { alertDialog } from './dialog'
10
-import { HTTP_MESSAGES, xlate } from './misc'
10
+import { HTTP_MESSAGES, LIST, xlate } from './misc'
11
import { t } from './i18n'
12
import { useLocation, useNavigate } from 'react-router-dom'
13
@@ -76,7 +76,7 @@ export default function useFetchList() {
76
for (const entry of data) {
77
if (!Array.isArray(entry)) continue // unexpected
78
const [op, par] = entry
79
- const error = op === 'error' && par
79
+ const error = op === LIST.error && par
80
if (error === 405) { // "method not allowed" happens when we try to directly access an unauthorized file, and we get a login prompt, and then get_file_list the file (because we didn't know it was file or folder)
81
state.messageOnly = t('upload_starting', "Your download should now start")
82
window.location.reload() // reload will start the download, because now we got authenticated
@@ -93,11 +93,11 @@ export default function useFetchList() {
93
}
94
if (uri && !uri.endsWith('/')) // now we know it was a folder for sure
95
return navigate(uri + '/')
96
- if (op === 'props') {
96
+ if (op === LIST.props) {
97
state.props = par
98
continue
99
}
100
- if (op === 'add')
100
+ if (op === LIST.add)
101
buffer.push(new DirEntry(par.n, par))
102
}
103
if (src?.readyState === src?.CLOSED)
src/apiMiddleware.ts
+13
-13
@@ -3,7 +3,7 @@
3
import Koa from 'koa'
4
import createSSE from './sse'
5
import { Readable } from 'stream'
6
-import { asyncGeneratorToReadable, onOff, removeStarting } from './misc'
6
+import { asyncGeneratorToReadable, LIST, onOff, removeStarting } from './misc'
7
import events from './events'
8
import { HTTP_BAD_REQUEST, HTTP_FOOL, HTTP_NOT_FOUND } from './const'
9
import _ from 'lodash'
@@ -113,41 +113,41 @@ export class SendListReadable<T> extends Readable {
113
this.processBuffer()
114
}
115
add(rec: T | T[]) {
116
- this._push(['add', rec])
116
+ this._push([LIST.add, rec])
117
}
118
remove(search: Partial<T>) {
119
const match = _.matches(search)
120
const idx = _.findIndex(this.buffer, x => match(x[1]))
121
const found = this.buffer[idx]
122
const op = found?.[0]
123
- if (op === 'remove') return
123
+ if (op === LIST.remove) return
124
if (found) {
125
this.buffer.splice(idx, 1)
126
- if (op === 'add') return
126
+ if (op === LIST.add) return
127
}
128
- this._push(['remove', search])
128
+ this._push([LIST.remove, search])
129
}
130
update(search: Partial<T>, change: Partial<T>) {
131
if (_.isEmpty(change)) return
132
const match = _.matches(search)
133
const found = _.find(this.buffer, x => match(x[1]))
134
const op = found?.[0]
135
- if (op === 'remove') return
136
- if (op === 'add' || op === 'update')
137
- return Object.assign(found[op === 'add' ? 1 : 2], change)
138
- return this._push(['update', search, change])
135
+ if (op === LIST.remove) return
136
+ if (op === LIST.add || op === LIST.update)
137
+ return Object.assign(found[op === LIST.add ? 1 : 2], change)
138
+ return this._push([LIST.update, search, change])
139
}
140
ready() { // useful to indicate the end of an initial phase, but we leave open for updates
141
- this._push(['ready'])
141
+ this._push([LIST.ready])
142
}
143
custom(name: string, data: any) {
144
- this._push([name, data])
144
+ this._push(data === undefined ? [name] : [name, data])
145
}
146
props(props: object) {
147
- this._push(['props', props])
147
+ this._push([LIST.props, props])
148
}
149
error(msg: NonNullable<typeof this.lastError>, close=false, props?: object) {
150
- this._push(['error', msg, props])
150
+ this._push([LIST.error, msg, props])
151
this.lastError = msg
152
if (close)
153
this.close()
src/cross.ts
+1
@@ -22,6 +22,7 @@ export const FRONTEND_OPTIONS = {
22
export const SORT_BY_OPTIONS = ['name', 'extension', 'size', 'time']
23
export const THEME_OPTIONS = { auto: '', light: 'light', dark: 'dark' }
24
export const CFG = constMap(['geo_enable', 'geo_allow', 'geo_list', 'geo_allow_unknown'])
25
+export const LIST = { add: '+', remove: '-', update: '=', props: 'props', ready: 'ready', error: 'e' }
26
export type Dict<T=any> = Record<string, T>
27
export type Falsy = false | null | undefined | '' | 0
28
type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T