@samitouri / QOSami-HFS / commits / b209be79

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)