Compare commits
6 Commits
1e4d66cbe4
...
1f0c7bd099
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f0c7bd099 | |||
| 4cc0ff2bed | |||
| 9a19c0a861 | |||
| b9f94a6c98 | |||
| c42c73fe70 | |||
| c3ad78f1be |
|
|
@ -16,3 +16,4 @@ export {
|
||||||
unshareApp,
|
unshareApp,
|
||||||
} from './manage'
|
} from './manage'
|
||||||
export { metricsApp } from './metrics'
|
export { metricsApp } from './metrics'
|
||||||
|
export { perfToggle } from './perf'
|
||||||
|
|
|
||||||
17
src/cli/commands/perf.ts
Normal file
17
src/cli/commands/perf.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import color from 'ansis'
|
||||||
|
import { get, post } from '../http'
|
||||||
|
|
||||||
|
export async function perfToggle(onOff?: string) {
|
||||||
|
if (onOff && !['on', 'off', 'status'].includes(onOff)) {
|
||||||
|
console.error('Usage: toes perf [on|off|status]')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const body = onOff && onOff !== 'status' ? { on: onOff === 'on' } : {}
|
||||||
|
const res = onOff === 'status'
|
||||||
|
? await get<{ perfTiming: boolean }>('/api/system/perf')
|
||||||
|
: await post<{ perfTiming: boolean }>('/api/system/perf', body)
|
||||||
|
if (res) {
|
||||||
|
const label = res.perfTiming ? color.green('on') : color.red('off')
|
||||||
|
console.log(`Perf timing ${onOff === 'status' ? 'is ' : ''}${label}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,14 +16,15 @@ import {
|
||||||
infoApp,
|
infoApp,
|
||||||
listApps,
|
listApps,
|
||||||
logApp,
|
logApp,
|
||||||
|
metricsApp,
|
||||||
newApp,
|
newApp,
|
||||||
openApp,
|
openApp,
|
||||||
|
perfToggle,
|
||||||
renameApp,
|
renameApp,
|
||||||
restartApp,
|
restartApp,
|
||||||
rmApp,
|
rmApp,
|
||||||
shareApp,
|
shareApp,
|
||||||
startApp,
|
startApp,
|
||||||
metricsApp,
|
|
||||||
stopApp,
|
stopApp,
|
||||||
unshareApp,
|
unshareApp,
|
||||||
} from './commands'
|
} from './commands'
|
||||||
|
|
@ -232,6 +233,13 @@ env
|
||||||
.option('-g, --global', 'remove a global variable')
|
.option('-g, --global', 'remove a global variable')
|
||||||
.action(envRm)
|
.action(envRm)
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('perf')
|
||||||
|
.helpGroup('Config:')
|
||||||
|
.description('Toggle request timing for proxied app requests')
|
||||||
|
.argument('[on|off|status]', 'enable, disable, or check status (toggles if omitted)')
|
||||||
|
.action(perfToggle)
|
||||||
|
|
||||||
// Shell
|
// Shell
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { allApps, APPS_DIR, onChange } from '$apps'
|
import { allApps, APPS_DIR, onChange } from '$apps'
|
||||||
|
import { perf } from '../proxy'
|
||||||
import { onHostLog } from '../tui'
|
import { onHostLog } from '../tui'
|
||||||
import { Hype } from '@because/hype'
|
import { Hype } from '@because/hype'
|
||||||
import { cpus, freemem, platform, totalmem } from 'os'
|
import { cpus, freemem, platform, totalmem } from 'os'
|
||||||
|
|
@ -283,6 +284,16 @@ onChange(collectLogs)
|
||||||
// Subscribe to host-level log messages
|
// Subscribe to host-level log messages
|
||||||
onHostLog(pushHostLog)
|
onHostLog(pushHostLog)
|
||||||
|
|
||||||
|
// Perf timing toggle
|
||||||
|
router.get('/perf', c => c.json({ perfTiming: perf.timing }))
|
||||||
|
router.post('/perf', async c => {
|
||||||
|
const body = await c.req.json<{ on?: boolean }>().catch(() => ({}))
|
||||||
|
const on = body.on ?? !perf.timing
|
||||||
|
perf.timing = on
|
||||||
|
console.log(`[perf] timing ${on ? 'enabled' : 'disabled'}`)
|
||||||
|
return c.json({ perfTiming: on })
|
||||||
|
})
|
||||||
|
|
||||||
// Restart server (systemd brings it back)
|
// Restart server (systemd brings it back)
|
||||||
router.post('/restart', c => {
|
router.post('/restart', c => {
|
||||||
setTimeout(() => process.exit(0), 100)
|
setTimeout(() => process.exit(0), 100)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import type { Server, ServerWebSocket } from 'bun'
|
import type { Server, ServerWebSocket } from 'bun'
|
||||||
import { getAppBySubdomain } from '$apps'
|
import { getAppBySubdomain } from '$apps'
|
||||||
|
|
||||||
|
export const perf = { timing: false }
|
||||||
|
|
||||||
export type { WsData }
|
export type { WsData }
|
||||||
|
|
||||||
const pendingMessages = new Map<ServerWebSocket<WsData>, (string | ArrayBuffer | Uint8Array)[]>()
|
const pendingMessages = new Map<ServerWebSocket<WsData>, (string | ArrayBuffer | Uint8Array)[]>()
|
||||||
|
|
@ -53,12 +55,19 @@ export async function proxySubdomain(subdomain: string, req: Request): Promise<R
|
||||||
headers.delete('transfer-encoding')
|
headers.delete('transfer-encoding')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await fetch(target, {
|
const shouldTime = perf.timing
|
||||||
|
const start = shouldTime ? performance.now() : 0
|
||||||
|
const res = await fetch(target, {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
redirect: 'manual',
|
redirect: 'manual',
|
||||||
})
|
})
|
||||||
|
if (shouldTime) {
|
||||||
|
const ms = (performance.now() - start).toFixed(1)
|
||||||
|
console.log(`[perf] ${req.method} ${subdomain}${url.pathname} → ${res.status} in ${ms}ms`)
|
||||||
|
}
|
||||||
|
return res
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Proxy error for ${subdomain}:`, e)
|
console.error(`Proxy error for ${subdomain}:`, e)
|
||||||
return new Response(`App "${subdomain}" is not responding`, { status: 502 })
|
return new Response(`App "${subdomain}" is not responding`, { status: 502 })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user