hfs 0.26.8 → 0.27.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +15 -2
  2. package/admin/assets/index-509bb1d6.js +415 -0
  3. package/admin/assets/index-60a380a7.css +1 -0
  4. package/admin/assets/sha512-738f0943.js +8 -0
  5. package/admin/index.html +3 -1
  6. package/admin/{public/logo.svg → logo.svg} +0 -0
  7. package/frontend/assets/index-6e178dfd.css +1 -0
  8. package/frontend/assets/index-aea7654e.js +85 -0
  9. package/frontend/assets/sha512-bf915587.js +8 -0
  10. package/frontend/{public/fontello.css → fontello.css} +0 -0
  11. package/frontend/{public/fontello.woff2 → fontello.woff2} +0 -0
  12. package/frontend/index.html +4 -2
  13. package/package.json +2 -6
  14. package/plugins/vhosting/plugin.js +23 -20
  15. package/src/QuickZipStream.js +285 -0
  16. package/src/ThrottledStream.js +93 -0
  17. package/src/adminApis.js +169 -0
  18. package/src/api.accounts.js +59 -0
  19. package/src/api.auth.js +128 -0
  20. package/src/api.file_list.js +110 -0
  21. package/src/api.helpers.js +32 -0
  22. package/src/api.monitor.js +104 -0
  23. package/src/api.plugins.js +128 -0
  24. package/src/api.vfs.js +167 -0
  25. package/src/apiMiddleware.js +123 -0
  26. package/src/block.js +34 -0
  27. package/src/commands.js +125 -0
  28. package/src/config.js +168 -0
  29. package/src/connections.js +57 -0
  30. package/src/const.js +94 -0
  31. package/src/crypt.js +21 -0
  32. package/src/debounceAsync.js +49 -0
  33. package/src/events.js +9 -0
  34. package/src/frontEndApis.js +38 -0
  35. package/src/github.js +104 -0
  36. package/src/index.js +57 -0
  37. package/src/listen.js +235 -0
  38. package/src/log.js +137 -0
  39. package/src/middlewares.js +195 -0
  40. package/src/misc.js +160 -0
  41. package/src/pbkdf2.js +74 -0
  42. package/src/perm.js +183 -0
  43. package/src/plugins.js +343 -0
  44. package/src/serveFile.js +105 -0
  45. package/src/serveGuiFiles.js +113 -0
  46. package/src/sse.js +30 -0
  47. package/src/throttler.js +91 -0
  48. package/src/update.js +70 -0
  49. package/src/util-files.js +163 -0
  50. package/src/util-generators.js +31 -0
  51. package/src/util-http.js +32 -0
  52. package/src/vfs.js +232 -0
  53. package/src/watchLoad.js +73 -0
  54. package/src/zip.js +73 -0
  55. package/admin/.DS_Store +0 -0
  56. package/admin/.eslintrc +0 -8
  57. package/admin/.gitignore +0 -23
  58. package/admin/package.json +0 -67
  59. package/admin/src/AccountForm.ts +0 -92
  60. package/admin/src/AccountsPage.ts +0 -143
  61. package/admin/src/App.ts +0 -83
  62. package/admin/src/ArrayField.ts +0 -84
  63. package/admin/src/ConfigPage.ts +0 -279
  64. package/admin/src/FileField.ts +0 -52
  65. package/admin/src/FileForm.ts +0 -148
  66. package/admin/src/FilePicker.ts +0 -166
  67. package/admin/src/HomePage.ts +0 -96
  68. package/admin/src/InstalledPlugins.ts +0 -158
  69. package/admin/src/LoginRequired.ts +0 -75
  70. package/admin/src/LogoutPage.ts +0 -27
  71. package/admin/src/LogsPage.ts +0 -75
  72. package/admin/src/MainMenu.ts +0 -74
  73. package/admin/src/MenuButton.ts +0 -38
  74. package/admin/src/MonitorPage.ts +0 -200
  75. package/admin/src/OnlinePlugins.ts +0 -101
  76. package/admin/src/PermField.ts +0 -80
  77. package/admin/src/PluginsPage.ts +0 -27
  78. package/admin/src/VfsMenuBar.ts +0 -58
  79. package/admin/src/VfsPage.ts +0 -124
  80. package/admin/src/VfsTree.ts +0 -95
  81. package/admin/src/addFiles.ts +0 -59
  82. package/admin/src/api.ts +0 -246
  83. package/admin/src/dialog.ts +0 -203
  84. package/admin/src/index.css +0 -21
  85. package/admin/src/index.ts +0 -10
  86. package/admin/src/md.ts +0 -31
  87. package/admin/src/misc.ts +0 -141
  88. package/admin/src/react-app-env.d.ts +0 -1
  89. package/admin/src/reportWebVitals.ts +0 -15
  90. package/admin/src/setupTests.ts +0 -5
  91. package/admin/src/state.ts +0 -40
  92. package/admin/src/theme.ts +0 -37
  93. package/admin/tsconfig.json +0 -26
  94. package/admin/vite.config.ts +0 -32
  95. package/frontend/.DS_Store +0 -0
  96. package/frontend/.eslintrc +0 -8
  97. package/frontend/.gitignore +0 -23
  98. package/frontend/package.json +0 -51
  99. package/frontend/src/App.ts +0 -25
  100. package/frontend/src/Breadcrumbs.ts +0 -43
  101. package/frontend/src/BrowseFiles.ts +0 -141
  102. package/frontend/src/Head.ts +0 -45
  103. package/frontend/src/UserPanel.ts +0 -52
  104. package/frontend/src/api.ts +0 -78
  105. package/frontend/src/components.ts +0 -54
  106. package/frontend/src/dialog.css +0 -76
  107. package/frontend/src/dialog.ts +0 -105
  108. package/frontend/src/icons.ts +0 -46
  109. package/frontend/src/index.scss +0 -307
  110. package/frontend/src/index.ts +0 -10
  111. package/frontend/src/login.ts +0 -50
  112. package/frontend/src/menu.ts +0 -188
  113. package/frontend/src/misc.ts +0 -54
  114. package/frontend/src/options.ts +0 -52
  115. package/frontend/src/react-app-env.d.ts +0 -1
  116. package/frontend/src/reportWebVitals.ts +0 -15
  117. package/frontend/src/setupTests.ts +0 -5
  118. package/frontend/src/state.ts +0 -82
  119. package/frontend/src/useAuthorized.ts +0 -17
  120. package/frontend/src/useFetchList.ts +0 -144
  121. package/frontend/src/useTheme.ts +0 -23
  122. package/frontend/tsconfig.json +0 -26
  123. package/frontend/vite.config.ts +0 -21
  124. package/src/QuickZipStream.ts +0 -279
  125. package/src/ThrottledStream.ts +0 -98
  126. package/src/adminApis.ts +0 -161
  127. package/src/api.accounts.ts +0 -78
  128. package/src/api.auth.ts +0 -131
  129. package/src/api.file_list.ts +0 -102
  130. package/src/api.helpers.ts +0 -30
  131. package/src/api.monitor.ts +0 -106
  132. package/src/api.plugins.ts +0 -139
  133. package/src/api.vfs.ts +0 -182
  134. package/src/apiMiddleware.ts +0 -124
  135. package/src/block.ts +0 -35
  136. package/src/commands.ts +0 -122
  137. package/src/config.ts +0 -166
  138. package/src/connections.ts +0 -60
  139. package/src/const.ts +0 -57
  140. package/src/crypt.ts +0 -16
  141. package/src/debounceAsync.ts +0 -51
  142. package/src/events.ts +0 -6
  143. package/src/frontEndApis.ts +0 -17
  144. package/src/github.ts +0 -102
  145. package/src/index.ts +0 -53
  146. package/src/listen.ts +0 -220
  147. package/src/log.ts +0 -128
  148. package/src/middlewares.ts +0 -176
  149. package/src/misc.ts +0 -149
  150. package/src/pbkdf2.ts +0 -83
  151. package/src/perm.ts +0 -194
  152. package/src/plugins.ts +0 -342
  153. package/src/serveFile.ts +0 -104
  154. package/src/serveGuiFiles.ts +0 -95
  155. package/src/sse.ts +0 -29
  156. package/src/throttler.ts +0 -106
  157. package/src/update.ts +0 -67
  158. package/src/util-files.ts +0 -137
  159. package/src/util-generators.ts +0 -29
  160. package/src/util-http.ts +0 -29
  161. package/src/vfs.ts +0 -258
  162. package/src/watchLoad.ts +0 -75
  163. package/src/zip.ts +0 -69
@@ -1,52 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { useSnapState } from './state'
4
- import { createElement as h } from 'react'
5
- import { alertDialog, closeDialog, newDialog, promptDialog } from './dialog'
6
- import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
7
- import { apiCall } from './api'
8
- import { logout } from './login'
9
- import { MenuButton } from './menu'
10
-
11
- export default function showUserPanel() {
12
- newDialog({ Content })
13
- }
14
-
15
- function Content() {
16
- const snap = useSnapState()
17
- return h('div', { id: 'user-panel' },
18
- h('div', {}, "User: " + snap.username),
19
- h(MenuButton, {
20
- icon: 'password',
21
- label: "Change password",
22
- async onClick() {
23
- const pwd = await promptDialog("Enter new password", { type: 'password' })
24
- if (!pwd) return
25
- const check = await promptDialog("RE-enter new password", { type: 'password' })
26
- if (!check) return
27
- if (check !== pwd)
28
- return alertDialog("The second password you entered did not match the first. Procedure aborted.", 'warning')
29
- const srp6aNimbusRoutines = new SRPRoutines(new SRPParameters())
30
- const res = await createVerifierAndSalt(srp6aNimbusRoutines, snap.username, pwd)
31
- try {
32
- await apiCall('change_srp', { salt: String(res.s), verifier: String(res.v) }).catch(e => {
33
- if (e.code !== 406) // 406 = server was configured to support clear text authentication
34
- throw e
35
- return apiCall('change_password', { newPassword: pwd }) // unencrypted version
36
- })
37
- return alertDialog("Password changed")
38
- }
39
- catch(e) {
40
- return alertDialog(e as Error)
41
- }
42
- }
43
- }),
44
- h(MenuButton, {
45
- icon: 'logout',
46
- label: "Logout",
47
- onClick() {
48
- logout().then(closeDialog, alertDialog)
49
- }
50
- })
51
- )
52
- }
@@ -1,78 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { useEffect, useState } from 'react';
4
- import { Dict, Falsy, getCookie, working } from './misc'
5
-
6
- const PREFIX = '/~/api/'
7
-
8
- interface ApiCallOptions { noModal?:true }
9
- export function apiCall(cmd: string, params?: Dict, options: ApiCallOptions={}) : Promise<any> {
10
- const stop = options.noModal ? undefined : working()
11
- const csrf = getCsrf()
12
- if (csrf)
13
- params = { csrf, ...params }
14
- return fetch(PREFIX+cmd, {
15
- method: 'POST',
16
- headers: { 'content-type': 'application/json' },
17
- body: params && JSON.stringify(params),
18
- }).then(res => {
19
- stop?.()
20
- if (res.ok)
21
- return res.json()
22
- const msg = `Failed API ${cmd}: ${res.statusText}`
23
- console.warn(msg + (params ? ' ' + JSON.stringify(params) : ''))
24
- throw new ApiError(res.status, msg)
25
- }, err => {
26
- stop?.()
27
- if (err?.message?.includes('fetch'))
28
- throw Error("Network error")
29
- throw err
30
- })
31
- }
32
-
33
- export class ApiError extends Error {
34
- constructor(readonly code:number, message: string) {
35
- super(message);
36
- }
37
- }
38
-
39
- export function useApi(cmd: string | Falsy, params?: object) : any {
40
- const [x, setX] = useState()
41
- useEffect(()=>{
42
- setX(undefined)
43
- if (cmd)
44
- apiCall(cmd, params).then(setX, setX)
45
- }, [cmd, JSON.stringify(params)]) //eslint-disable-line
46
- return x
47
- }
48
-
49
- type EventHandler = (type:string, data?:any) => void
50
-
51
- export function apiEvents(cmd: string, params: Dict, cb:EventHandler) {
52
- const csrf = getCsrf()
53
- const processed: Record<string,string> = { csrf: csrf && JSON.stringify(csrf) }
54
- for (const k in params) {
55
- const v = params[k]
56
- if (v === undefined) continue
57
- processed[k] = JSON.stringify(v)
58
- }
59
- const source = new EventSource(PREFIX + cmd + '?' + new URLSearchParams(processed))
60
- source.onopen = () => cb('connected')
61
- source.onerror = err => cb('error', err)
62
- source.onmessage = ({ data }) => {
63
- if (!data) {
64
- cb('closed')
65
- return source.close()
66
- }
67
- try { data = JSON.parse(data) }
68
- catch {
69
- return cb('string', data)
70
- }
71
- cb('msg', data)
72
- }
73
- return source
74
- }
75
-
76
- function getCsrf() {
77
- return getCookie('csrf')
78
- }
@@ -1,54 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { hIcon } from './misc'
4
- import { createElement as h, HTMLAttributes, ReactNode, useMemo } from 'react'
5
-
6
- export function Spinner() {
7
- return hIcon('spinner', { className:'spinner' })
8
- }
9
-
10
- export function Flex({ gap='1em', vert=false, children=null }) {
11
- return h('div', {
12
- style: {
13
- display: 'flex',
14
- gap,
15
- flexDirection: vert ? 'column' : undefined,
16
- }
17
- }, children)
18
- }
19
-
20
- export function FlexV(props:any) {
21
- return h(Flex, { vert:true, ...props })
22
- }
23
-
24
- interface CheckboxProps { children?:ReactNode, value:any, onChange?:(v:boolean)=>void }
25
- export function Checkbox({ onChange, value, children, ...props }:CheckboxProps) {
26
- return h('label', {},
27
- h('input',{
28
- type:'checkbox',
29
- onChange: ev => onChange?.(Boolean(ev.target.checked)),
30
- checked: Boolean(value),
31
- value: 1,
32
- ...props
33
- }),
34
- children
35
- )
36
- }
37
-
38
- type Options = { label:string, value:string }[]
39
- interface SelectProps { value:any, onChange?:(v:string)=>void, options:Options }
40
- export function Select({ onChange, value, options, ...props }:SelectProps) {
41
- return h('select', {
42
- onChange: ev => // @ts-ignore
43
- onChange?.(ev.target.value),
44
- value,
45
- ...props,
46
- }, options.map(({ value, label }) => h('option', { key:value, value }, label)))
47
- }
48
-
49
- export function Html({ code, ...rest }:{ code:string } & HTMLAttributes<any>) {
50
- const o = useMemo(() => ({ __html: code }), [code])
51
- if (!code)
52
- return null
53
- return h('span', { ...rest, dangerouslySetInnerHTML: o })
54
- }
@@ -1,76 +0,0 @@
1
- .dialog-backdrop {
2
- position: fixed;
3
- top: 0;
4
- bottom: 0;
5
- left: 0;
6
- right: 0;
7
- background: #888a;
8
- display: flex;
9
- justify-content: center;
10
- align-items: center;
11
- z-index: 1000;
12
- }
13
- .dialog {
14
- background: #fff; /*fallback*/
15
- background: var(--bg);
16
- padding: max(0.5em, 1vw);
17
- border-radius: 1em;
18
- position: relative;
19
- margin: 0 3vw;
20
- overflow: hidden;
21
- max-height: calc(100vh - 2em)
22
- }
23
- .dialog-icon {
24
- color: #fff;
25
- background-color: var(--color);
26
- position: absolute;
27
- top: 0;
28
- width: 1.8em;
29
- height: 1.7em;
30
- text-align: center;
31
- border-radius: .8em 0;
32
- }
33
- .dialog-closer {
34
- border-radius: 0 0.8em;
35
- right: 0;
36
- padding: 0;
37
- background-color: #c99;
38
- }
39
- .dialog-icon ~ .dialog-content {
40
- margin-top: 1.3em;
41
- }
42
- .dialog-type {
43
- left: 0;
44
- top: 0;
45
- overflow: hidden;
46
- line-height: 1.7em;
47
- }
48
- .dialog-content {
49
- overflow: auto;
50
- max-height: calc(100vh - 4em);
51
- }
52
- .dialog-content p {
53
- white-space: pre-wrap;
54
- margin: .5em 0;
55
- }
56
- .dialog-confirm .dialog-content button {
57
- margin-top: 1em;
58
- }
59
- .dialog-alert-info {
60
- --color: #282
61
- }
62
- .dialog-alert-warning {
63
- --color: #c91
64
- }
65
- .dialog-alert-error {
66
- --color: #822;
67
- }
68
-
69
- @media (max-width: 50em) {
70
- .dialog-closer {
71
- font-size: 120%
72
- }
73
- .dialog-icon ~ .dialog-content {
74
- margin-top: 2em;
75
- }
76
- }
@@ -1,105 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { createElement as h, ReactElement, ReactNode, useEffect, useRef } from 'react'
4
- import './dialog.css'
5
- import { newDialog, closeDialog } from '@hfs/shared/lib/dialogs'
6
- export * from '@hfs/shared/lib/dialogs'
7
-
8
- interface PromptOptions { def?:string, type?:string }
9
- export async function promptDialog(msg: string, { def, type }:PromptOptions={}) : Promise<string | null> {
10
- return new Promise(resolve => newDialog({
11
- className: 'dialog-prompt',
12
- icon: '?',
13
- onClose: resolve,
14
- Content
15
- }) )
16
-
17
- function Content() {
18
- const ref = useRef<HTMLInputElement>()
19
- useEffect(()=>{
20
- const e = ref.current
21
- if (!e) return
22
- const inp = e as HTMLInputElement
23
- setTimeout(()=> inp.focus(),100)
24
- if (def)
25
- inp.value = def
26
- },[])
27
- return h('div', {},
28
- h('p', {}, msg),
29
- h('input', {
30
- ref,
31
- type,
32
- autoFocus: true,
33
- onKeyDown(ev: KeyboardEvent) {
34
- const { key } = ev
35
- if (key === 'Escape')
36
- return closeDialog(null)
37
- if (key === 'Enter')
38
- return go()
39
- }
40
- }),
41
- h('div', { style: { textAlign: 'right', marginTop: '.8em' } },
42
- h('button', { onClick: go }, "Continue")),
43
- )
44
-
45
- function go() {
46
- closeDialog(ref.current?.value)
47
- }
48
- }
49
- }
50
-
51
- type AlertType = 'error' | 'warning' | 'info'
52
-
53
- export async function alertDialog(msg: ReactElement | string | Error, type:AlertType='info') {
54
- if (msg instanceof Error) {
55
- msg = String(msg)
56
- type = 'error'
57
- }
58
- return new Promise(resolve => newDialog({
59
- className: 'dialog-alert-'+type,
60
- icon: '!',
61
- onClose: resolve,
62
- Content
63
- }))
64
-
65
- function Content(){
66
- if (typeof msg === 'string' || msg instanceof Error)
67
- msg = h('p', {}, String(msg))
68
- return msg
69
- }
70
- }
71
-
72
- export interface ConfirmOptions { href?: string, afterButtons?: ReactNode }
73
- export async function confirmDialog(msg: ReactElement | string, { href, afterButtons }: ConfirmOptions={}) : Promise<boolean> {
74
- if (typeof msg === 'string')
75
- msg = h('p', {}, msg)
76
- return new Promise(resolve => newDialog({
77
- className: 'dialog-confirm',
78
- icon: '?',
79
- onClose: resolve,
80
- Content
81
- }) )
82
-
83
- function Content() {
84
- return h('div', {},
85
- msg,
86
- h('div', {
87
- style: {
88
- display: 'flex',
89
- justifyContent: 'flex-end',
90
- gap: '1em'
91
- },
92
- },
93
- h('a', {
94
- href,
95
- onClick() { closeDialog(true) },
96
- }, h('button', {}, "Confirm")),
97
- h('button', {
98
- onClick() { closeDialog(false) },
99
- }, "Don't"),
100
- afterButtons,
101
- )
102
- )
103
- }
104
- }
105
-
@@ -1,46 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { state, useSnapState } from './state'
4
- import { createElement as h, memo } from 'react'
5
-
6
- const SYS_ICONS = {
7
- login: 'user:👤',
8
- user: 'user:👤',
9
- filter: ':✂',
10
- search: ':🔍',
11
- search_off: 'cancel:❌',
12
- stop: ':⏹️',
13
- settings: 'cog:⚙',
14
- archive: 'file-archive:📦',
15
- logout: ':🚪',
16
- home: ':🏠',
17
- parent: 'level-up mirror:⬆',
18
- folder: ':📂',
19
- file: 'doc:📄',
20
- spinner: 'spin6 spinner:🎲',
21
- password: 'key:🗝️',
22
- download: ':📥',
23
- invert: 'retweet:🙃',
24
- admin: 'crown:👑',
25
- check: ':✔️',
26
- }
27
-
28
- document.fonts.ready.then(async ()=> {
29
- const fontTester = '9px "fontello"'
30
- await document.fonts.load(fontTester) // force font to be loaded even if we didn't display anything with it yet
31
- if (document.fonts.check(fontTester))
32
- state.iconsClass = ' ' // with fontello we don't need an additional class (unlike google material icons), but the empty space will cause reload
33
- })
34
-
35
- export const Icon = memo(({ name, alt, className='', ...props }: { name:string, className?:string, alt?:string, style?:any }) => {
36
- // @ts-ignore
37
- const [clazz,emoji] = (SYS_ICONS[name] || name).split(':')
38
- const { iconsClass } = useSnapState()
39
- className += ' icon ' + (iconsClass ? 'fa-'+(clazz||name) : 'emoji')
40
- return h('span',{
41
- ...props,
42
- 'aria-label': alt,
43
- role: 'img',
44
- className,
45
- }, iconsClass ? null : (emoji||'#'))
46
- })