plugins: event checkVfsPermission
Massimo Melina committed
Aug 11, 2025 at 13:42 UTC
f69af56c94f5d27bad2165430b0614b0594def74
3 files changed
+39
-20
dev-plugins.md
+25
-18
@@ -1,31 +1,30 @@
1
-# For plug-in makers
1
+# For plugin makers
2
3
-If the information you are searching for is not in this document, [please ask](https://github.com/rejetto/hfs/discussions).
3
+If the information you are searching for is not in this document, [feel free to ask](https://github.com/rejetto/hfs/discussions).
4
5
-A plug-in is a folder with a `plugin.js` file in it. To install a plugin you just copy the folder into the `plugins` folder.
5
+A plugin for HFS is a folder that contains a `plugin.js` file. To install a plugin you just copy the folder into the `plugins` folder.
6
You will find `plugins` folder near `config.yaml`, and then in `USER_FOLDER/.hfs` for Linux and macOS, or near `hfs.exe` on Windows.
7
8
-Plug-ins can be hot-swapped, and to some extent can be edited without restarting the server.
8
+plugins can be hot-swapped, and to some extent can be edited without restarting the server.
9
10
-Each plug-in has access to the same set of features.
11
-Normally you'll have a plug-in that's a theme, and another that's a firewall,
12
-but nothing is preventing a single plug-in from doing both tasks.
10
+Normally you'll have a plugin that's a theme, and another that's a firewall,
11
+but nothing is preventing a single plugin from doing both tasks.
12
13
## Development environment
14
16
-The simplest way to develop is to create your folder inside the .hfs/plugins folder, and work there.
15
+The simplest way to develop is to create your folder inside the `.hfs/plugins` folder, and work there.
16
Each time you make a change, you'll see it reflected in the running server.
17
This is probably the easiest form to start with.
18
19
A neater way is to keep it both in the form of github repo and installed plugin.
20
If you want to do so, have a folder with your github repo in it, *outside* your `.hfs` folder.
22
-As you'll see in the [[Publish your plug-in]] section, you should keep your files inside the `dist` subfolder.
23
-Then you'll need to link it inside the plugins folder.
24
-If you go in your .hfs/plugins folder on linux and mac, and enter
21
+As you'll see in the [[Publish your plugin]] section, you should keep your files inside the `dist` subfolder.
22
+Then you'll need to link it inside the `plugins` folder.
23
+If you go in your `.hfs/plugins` folder on linux and mac, and enter
24
25
ln -s /PATH_TO_YOUR_REPO/dist MY_PLUGIN_NAME
26
28
-On Windows it may be something like
27
+On Windows, the command would be something like
28
29
mklink /d C:\path\to\hfs\plugins\my_plugin C:\my_code\my_plugin\dist
30
@@ -34,7 +33,8 @@ This allows you to continue editing your repo and be ready to commit changes.
33
34
## Backend / Frontend
35
37
-Plugins can run both in backend (the server) and frontend (the browser). Frontend files reside in the "public" folder, while all the rest is backend.
36
+plugins can have a part running in the backend (the server) and a part running in the frontend (the browser).
37
+Frontend files reside in the "public" folder, while all the rest is backend.
38
39
## System icons
40
@@ -280,9 +280,8 @@ The `api` object you get as parameter of the `init` contains the following:
280
- `storageDir: string` folder where a plugin is supposed to store run-time data. This folder is preserved during
281
an update of the plugin, while the rest could be deleted.
282
283
-- `events` this is the main events emitter used by HFS.
284
- These are backend side events, not to be confused with frontend ones. It's not the standard EventEmitter class,
285
- and the API is slightly different.
283
+- `events` this is the main events emitter used by HFS. These are backend-side events, not to be confused with frontend ones.
284
+ It's not the standard EventEmitter class, but the API is mostly the same.
285
286
- `events.on(name: string, listener: Callback): Callback`
287
@@ -732,6 +731,13 @@ This section is still partially documented, and you may need to have a look at t
731
to let the default behavior while getting the content of the list, return a function, and it will be called for each
732
entry, passed as first parameter (an object of standard class fs.Dirent), and when the list is over it will be called
733
with a boolean, true if the list is completed and false if it was aborted
734
+- `checkVfsPermission` called when a vfs permission is checked
735
+ - parameters: { node, perm, who, ctx }
736
+ - `perm: string` is the permission we are checking for
737
+ - `node: VfsNode` is the node on which the permission is checked
738
+ - `who: Who` is like `node[perm]`, that is the configured permission on the node,
739
+ but without references to other permissions or the object form, as they have already been translated
740
+ - return: an http error as number >= 400, or 0 or undefined
741
742
# Notifications (backend-to-frontend events)
743
@@ -818,7 +824,7 @@ If you use a library for the browser, you'll have to keep it in the "public" fol
824
If you want to use a module for node.js, just include "node_modules" folder (not in "public" folder).
825
You can decide if you want to use some building system/transpiler, but you'll have to set it up yourself.
826
821
-## Publish your plug-in
827
+## Publish your plugin
828
829
While you may just put a zip on any website, that would require manual installation.
830
If you want to appear in the Admin-panel, for easier finding and installation, please do as follows.
@@ -1045,4 +1051,5 @@ If you want to override a text regardless of the language, use the special langu
1051
- HFS.textSortCompare
1052
- 12.8 (v0.57.10)
1053
- api.onServer
1048
- - HFS.elementToEntry
\ No newline at end of file
1054
+ - HFS.elementToEntry
1055
+ - backend event: checkVfsPermission
\ No newline at end of file
src/listen.ts
+8
-2
@@ -41,6 +41,7 @@ export function getHttpsWorkingPort() {
41
}
42
43
const commonServerOptions: http.ServerOptions = { requestTimeout: 0 }
44
+// these are properties that can be assigned to the server object
45
const commonServerAssign = { headersTimeout: 30_000, timeout: MINUTE } // 'headersTimeout' is not recognized by type lib, and 'timeout' is not effective when passed in parameters
46
47
const readyToListen = Promise.all([ storedMap.isOpening(), events.once('app') ])
@@ -103,9 +104,14 @@ const considerHttps = debounceAsync(async () => {
104
defaultBaseUrl.port = getCurrentPort(httpSrv) ?? 0
105
let port = httpsPortCfg.get()
106
try {
106
- const moreOptions = Object.assign({}, ...await events.emitAsync('httpsServerOptions') || [])
107
+ const moreOptions = Object.assign({}, ...await events.emitAsync('httpsServerOptions') || []) // emitAsync returns an array of objects
108
httpsSrv = Object.assign(
108
- https.createServer(port === PORT_DISABLED ? {} : { ...commonServerOptions, key: httpsOptions.private_key, cert: httpsOptions.cert, ...moreOptions }, app.callback()),
109
+ https.createServer(port === PORT_DISABLED ? {} : {
110
+ ...commonServerOptions,
111
+ key: httpsOptions.private_key,
112
+ cert: httpsOptions.cert,
113
+ ...moreOptions,
114
+ }, app.callback()),
115
{ name: 'https' },
116
commonServerAssign
117
)
src/vfs.ts
+6
@@ -266,6 +266,12 @@ export function statusCodeForMissingPerm(node: VfsNode, perm: keyof VfsPerms, ct
266
}
267
cur = who
268
} while (1)
269
+ const eventName = 'checkVfsPermission'
270
+ if (events.anyListener(eventName)) {
271
+ const first = Number(events.emit(eventName, { who, node, perm, ctx })?.find(Boolean))
272
+ if (first >= 400)
273
+ return first
274
+ }
275
276
if (Array.isArray(who))
277
return ctxBelongsTo(ctx, who) ? 0 : HTTP_UNAUTHORIZED