better code: split files
Massimo Melina committed
Dec 27, 2023 at 16:43 UTC
b209be79d06208a399834b059e189b7b05749541
4 files changed
+78
-70
src/adminApis.ts
+3
-65
@@ -18,22 +18,18 @@ import pluginsApis from './api.plugins'
18
import monitorApis from './api.monitor'
19
import langApis from './api.lang'
20
import netApis from './api.net'
21
+import logApis from './api.log'
22
import { getConnections } from './connections'
22
-import { apiAssertTypes, debounceAsync, isLocalHost, makeNetMatcher, onOff, tryJson, wait, waitFor } from './misc'
23
-import events from './events'
23
+import { apiAssertTypes, debounceAsync, isLocalHost, makeNetMatcher, waitFor } from './misc'
24
import { accountCanLoginAdmin, accountsConfig } from './perm'
25
import Koa from 'koa'
26
import { getProxyDetected } from './middlewares'
27
import { writeFile } from 'fs/promises'
28
-import { createReadStream } from 'fs'
29
-import * as readline from 'readline'
30
-import { loggers } from './log'
28
import { execFile } from 'child_process'
29
import { promisify } from 'util'
30
import { customHtmlSections, customHtmlState, saveCustomHtml } from './customHtml'
31
import _ from 'lodash'
32
import { getUpdates, localUpdateAvailable, update, updateSupported } from './update'
36
-import { consoleLog } from './consoleLog'
33
import { resolve } from 'path'
34
import { getErrorSections } from './errorPages'
35
import { ip2country } from './geo'
@@ -49,6 +45,7 @@ export const adminApis: ApiHandlers = {
45
...monitorApis,
46
...langApis,
47
...netApis,
48
+ ...logApis,
49
get_dynamic_dns_error,
50
51
async set_config({ values }) {
@@ -136,65 +133,6 @@ export const adminApis: ApiHandlers = {
133
return files
134
},
135
139
- get_log({ file='log' }, ctx) {
140
- return new SendListReadable({
141
- bufferTime: 10,
142
- async doAtStart(list) {
143
- if (file === 'console') {
144
- for (const chunk of _.chunk(consoleLog, 1000)) { // avoid occupying the thread too long
145
- for (const x of chunk)
146
- list.add(x)
147
- await wait(0)
148
- }
149
- list.ready()
150
- events.on('console', x => list.add(x))
151
- return
152
- }
153
- const logger = loggers.find(l => l.name === file)
154
- if (!logger)
155
- return list.error(HTTP_NOT_FOUND, true)
156
- const input = createReadStream(logger.path)
157
- input.on('error', async (e: any) => {
158
- if (e.code === 'ENOENT') // ignore ENOENT, consider it an empty log
159
- return list.ready()
160
- list.error(e.code || e.message)
161
- })
162
- input.on('end', () =>
163
- list.ready())
164
- input.on('ready', () => {
165
- readline.createInterface({ input }).on('line', line => {
166
- if (ctx.aborted)
167
- return input.close()
168
- const obj = parse(line)
169
- if (obj)
170
- list.add(obj)
171
- }).on('close', () => { // file is automatically closed, so we continue by events
172
- ctx.res.once('close', onOff(events, { // unsubscribe when connection is interrupted
173
- [logger.name](entry) {
174
- list.add(entry)
175
- }
176
- }))
177
- })
178
- })
179
- }
180
- })
181
-
182
- function parse(line: string) {
183
- const m = /^(.+?) (.+?) (.+?) \[(.{11}):(.{14})] "(\w+) ([^"]+) HTTP\/\d.\d" (\d+) (-|\d+) ?(.*)/.exec(line)
184
- if (!m) return
185
- const [, ip, , user, date, time, method, uri, status, length, extra] = m
186
- return { // keep object format same as events emitted by the log module
187
- ip,
188
- user: user === '-' ? undefined : user,
189
- ts: new Date(date + ' ' + time),
190
- method,
191
- uri,
192
- status: Number(status),
193
- length: length === '-' ? undefined : Number(length),
194
- extra: tryJson(tryJson(extra)) || undefined,
195
- }
196
- }
197
- },
136
}
137
138
for (const [k, was] of Object.entries(adminApis))
src/api.accounts.ts
+2
-4
@@ -19,7 +19,7 @@ function prepareAccount(ac: Account | undefined) {
19
}
20
}
21
22
-const apis: ApiHandlers = {
22
+export default {
23
24
get_usernames() {
25
return { list: Object.keys(accountsConfig.get()) }
@@ -77,6 +77,4 @@ const apis: ApiHandlers = {
77
: new ApiError(HTTP_NOT_FOUND)
78
}
79
80
-}
81
-
82
-export default apis
80
+} satisfies ApiHandlers
\ No newline at end of file
src/api.log.ts
new
+72
@@ -0,0 +1,72 @@
1
+import { ApiHandlers } from './apiMiddleware'
2
+import _ from 'lodash'
3
+import { consoleLog } from './consoleLog'
4
+import { HTTP_NOT_FOUND, tryJson, wait } from './cross'
5
+import events from './events'
6
+import { loggers } from './log'
7
+import { createReadStream } from 'fs'
8
+import readline from 'readline'
9
+import { onOff } from './misc'
10
+import { SendListReadable } from './SendList'
11
+
12
+export default {
13
+ get_log({ file = 'log' }, ctx) {
14
+ return new SendListReadable({
15
+ bufferTime: 10,
16
+ async doAtStart(list) {
17
+ if (file === 'console') {
18
+ for (const chunk of _.chunk(consoleLog, 1000)) { // avoid occupying the thread too long
19
+ for (const x of chunk)
20
+ list.add(x)
21
+ await wait(0)
22
+ }
23
+ list.ready()
24
+ events.on('console', x => list.add(x))
25
+ return
26
+ }
27
+ const logger = loggers.find(l => l.name === file)
28
+ if (!logger)
29
+ return list.error(HTTP_NOT_FOUND, true)
30
+ const input = createReadStream(logger.path)
31
+ input.on('error', async (e: any) => {
32
+ if (e.code === 'ENOENT') // ignore ENOENT, consider it an empty log
33
+ return list.ready()
34
+ list.error(e.code || e.message)
35
+ })
36
+ input.on('end', () =>
37
+ list.ready())
38
+ input.on('ready', () => {
39
+ readline.createInterface({ input }).on('line', line => {
40
+ if (ctx.aborted)
41
+ return input.close()
42
+ const obj = parse(line)
43
+ if (obj)
44
+ list.add(obj)
45
+ }).on('close', () => { // file is automatically closed, so we continue by events
46
+ ctx.res.once('close', onOff(events, { // unsubscribe when connection is interrupted
47
+ [logger.name](entry) {
48
+ list.add(entry)
49
+ }
50
+ }))
51
+ })
52
+ })
53
+ }
54
+ })
55
+
56
+ function parse(line: string) {
57
+ const m = /^(.+?) (.+?) (.+?) \[(.{11}):(.{14})] "(\w+) ([^"]+) HTTP\/\d.\d" (\d+) (-|\d+) ?(.*)/.exec(line)
58
+ if (!m) return
59
+ const [, ip, , user, date, time, method, uri, status, length, extra] = m
60
+ return { // keep object format same as events emitted by the log module
61
+ ip,
62
+ user: user === '-' ? undefined : user,
63
+ ts: new Date(date + ' ' + time),
64
+ method,
65
+ uri,
66
+ status: Number(status),
67
+ length: length === '-' ? undefined : Number(length),
68
+ extra: tryJson(tryJson(extra)) || undefined,
69
+ }
70
+ }
71
+ },
72
+} satisfies ApiHandlers
\ No newline at end of file
tests/test.ts
+1
-1
@@ -195,7 +195,7 @@ const jar = {}
195
function req(url: string, test:Tester, { baseUrl, throttle, ...requestOptions }: XRequestOptions & { throttle?: number, baseUrl?: string }={}) {
196
// passing 'path' keeps it as it is, avoiding internal resolving
197
return () => httpStream((baseUrl || BASE_URL) + url, { path: url, jar, ...requestOptions }).catch(e => {
198
- if (e.code === "ECONNREFUSED")
198
+ if (e.code === 'ECONNREFUSED')
199
throw e
200
return e.cause
201
}).then(process)