plugins: api.onServer
Massimo Melina committed
Jul 29, 2025 at 12:04 UTC
bb1977ed31baaf72ba4997159372c811a9c01336
4 files changed
+35
-24
dev-plugins.md
+10
-3
@@ -356,6 +356,9 @@ The `api` object you get as parameter of the `init` contains the following:
356
357
- `setInterval`, `setTimeout` same as standard js functions, but will automatically cancel if the plugin is unloaded.
358
359
+- `onServer(cb: (Server) => any)` execute your callback on every instance of Server created by HFS.
360
+ It is the standard Node.js class, and it can be http or https. It can be instantiated multiple times.
361
+
362
## Frontend JS
363
364
The following information applies to the frontend bundled with HFS.
@@ -728,7 +731,6 @@ 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
731
-
734
735
# Notifications (backend-to-frontend events)
736
@@ -820,6 +822,9 @@ You can decide if you want to use some building system/transpiler, but you'll ha
822
While you may just put a zip on any website, that would require manual installation.
823
If you want to appear in the Admin-panel, for easier finding and installation, please do as follows.
824
825
+Be sure that you are exporting (not returning) the essential properties, like `apiRequired`.
826
+Find the full list in the [[Things a plugin can export]], marked with "JSON syntax".
827
+
828
Suggested method for publishing is to have a dedicated repository on GitHub, with topic `hfs-plugin`.
829
To set the topic go on the repo home and click on the gear icon near the "About" box.
830
Be sure to also fill the "exports.description" field, especially with words that people may search for.
@@ -841,7 +846,7 @@ You can refer to these published plugins for reference, like
846
- https://github.com/rejetto/simple-player/
847
- https://github.com/rejetto/theme-example/
848
844
-Published plugins are required to specify the `apiRequired` property.
849
+Published plugins to have `exports.apiRequired`.
850
851
### Multiple versions
852
@@ -1036,4 +1041,6 @@ If you want to override a text regardless of the language, use the special langu
1041
- HFS.fileShow return value
1042
- HFS.isShowSupported
1043
- 12.6 (v0.57.6)
1039
- - HFS.textSortCompare
\ No newline at end of file
1044
+ - HFS.textSortCompare
1045
+- 12.7 (v0.57.10)
1046
+ - api.onServer
\ No newline at end of file
src/const.ts
+1
-1
@@ -9,7 +9,7 @@ import { formatTimestamp } from './cross'
9
import { argv } from './argv'
10
export * from './cross-const'
11
12
-export const API_VERSION = 12.6
12
+export const API_VERSION = 12.7
13
export const COMPATIBLE_API_VERSION = 1 // while changes in the api are not breaking, this number stays the same, otherwise it is made equal to API_VERSION
14
15
// you can add arguments with this file, currently used for the update process on mac/linux
src/listen.ts
+3
-2
@@ -145,8 +145,6 @@ const considerHttps = debounceAsync(async () => {
145
console.error("failed to create https server: check your private key and certificate", e.message)
146
return
147
}
148
- port = await startServer(httpsSrv, { port, host: listenInterface.get() })
149
- if (!port) return
148
httpsSrv.on('connection', newConnection) // this event is emitted as soon as the tcp layer is connected
149
httpsSrv.on('secureConnection', (socket: TLSSocket) => { // emitted when the TLS layer is connected
150
for (const c of getConnections()) // TLSSocket shares same ip:port, so we can find its matching Connection
@@ -154,6 +152,8 @@ const considerHttps = debounceAsync(async () => {
152
&& socket.remotePort === c.socket.remotePort)
153
return c.socket.emit('secure', socket) // let know Connection about the secure socket
154
})
155
+ port = await startServer(httpsSrv, { port, host: listenInterface.get() })
156
+ if (!port) return
157
printUrls(httpsSrv.name)
158
events.emit('httpsReady')
159
defaultBaseUrl.proto = 'https'
@@ -237,6 +237,7 @@ export function startServer(srv: typeof httpSrv, { port, host }: StartServer) {
237
return reject('type of socket not supported')
238
}
239
srv.removeListener('error', onError) // necessary in case someone calls stop/start many times
240
+ events.emit('listening', { server: srv, port: ad.port })
241
resolve(ad.port)
242
})
243
src/plugins.ts
+21
-18
@@ -33,6 +33,7 @@ import { i18nFromTranslations } from './i18n'
33
import { addAccount, ctxBelongsTo, delAccount, getAccount, getUsernames, renameAccount, updateAccount } from './perm'
34
import { getCurrentUsername } from './auth'
35
import { CustomizedIcons, watchIconsFolder } from './icons'
36
+import { getServerStatus } from './listen'
37
38
export const PATH = 'plugins'
39
export const DISABLING_SUFFIX = '-disabled'
@@ -123,18 +124,17 @@ export function getPluginConfigFields(id: string) {
124
async function initPlugin(pl: any, morePassedToInit?: { id: string } & Dict<any>) {
125
const undoEvents: any[] = []
126
const timeouts: NodeJS.Timeout[] = []
127
+ const controlledEvents = Object.create(events, objFromKeys(['on', 'once', 'multi'], k => ({
128
+ value() {
129
+ const ret = (events[k] as any)(...arguments)
130
+ undoEvents.push(ret)
131
+ return ret
132
+ }
133
+ })))
134
const res = await pl.init?.({
127
- Const,
128
- require,
129
- getConnections,
135
+ Const, require,
136
// intercept all subscriptions, so to be able to undo them on unload
131
- events: Object.create(events, objFromKeys(['on', 'once', 'multi'], k => ({
132
- value() {
133
- const ret = (events[k] as any)(...arguments)
134
- undoEvents.push(ret)
135
- return ret
136
- }
137
- }))),
137
+ events: controlledEvents,
138
log: console.log,
139
setError(msg: string) { setError(morePassedToInit?.id || 'server_code', msg) },
140
getHfsConfig: getConfig,
@@ -148,14 +148,17 @@ async function initPlugin(pl: any, morePassedToInit?: { id: string } & Dict<any>
148
timeouts.push(ret)
149
return ret
150
},
151
- customApiCall,
152
- notifyClient,
153
- addBlock,
154
- misc,
155
- _,
156
- ctxBelongsTo,
157
- getCurrentUsername,
158
- getAccount, getUsernames, addAccount, delAccount, updateAccount, renameAccount,
151
+ async onServer(cb: Callback<object>) {
152
+ const res = await getServerStatus()
153
+ if (res.http.srv)
154
+ cb(res.http.srv)
155
+ if (res.https.srv)
156
+ cb(res.https.srv)
157
+ controlledEvents.on('listening', ({ server }: any) => cb(server))
158
+ },
159
+ misc, _,
160
+ customApiCall, notifyClient, addBlock, ctxBelongsTo, getConnections,
161
+ getCurrentUsername, getAccount, getUsernames, addAccount, delAccount, updateAccount, renameAccount,
162
...morePassedToInit
163
})
164
Object.assign(pl, typeof res === 'function' ? { unload: res } : res)