Loopback Fastify auth-broker + esbuild SPA. Operator password login (constant-time, signed HttpOnly session cookie, per-forwarded-IP rate-limit) → session-gated proxy that mints a fresh 60s manage capability token per call to the control-plane admin API: list hosts, mint pairing codes (with QR + pair command), revoke hosts. Security headers + CSP, CP_URL pinned loopback (anti-SSRF), hostId dot-segment guard. 55 tests pass; security-reviewed. Deployed behind nginx panel.terminal.yaojia.wang.
38 lines
1.5 KiB
TypeScript
38 lines
1.5 KiB
TypeScript
/**
|
|
* Operator-facing pairing artifacts derived from a freshly-issued pairing code:
|
|
* - the ready-to-run `web-terminal-agent pair <code> --install --zone <zone>` command line, and
|
|
* - a QR PNG data: URL of the code (rendered with the `qrcode` dep) for phone scanning.
|
|
* Pure/deterministic given the code + zone (except the QR, which is a stable render of the code).
|
|
*/
|
|
import QRCode from 'qrcode'
|
|
|
|
/** Build the exact command an operator runs on the new host to enroll it into the tunnel. */
|
|
export function buildPairCommand(code: string, zone: string): string {
|
|
return `web-terminal-agent pair ${code} --install --zone ${zone}`
|
|
}
|
|
|
|
/** Render a QR PNG data: URL encoding the pairing code (for scanning on the phone client). */
|
|
export async function buildQrDataUrl(code: string): Promise<string> {
|
|
return QRCode.toDataURL(code, { errorCorrectionLevel: 'M', margin: 1, width: 256 })
|
|
}
|
|
|
|
export interface PairingArtifacts {
|
|
readonly code: string
|
|
readonly expiresAt: string
|
|
readonly pairCommand: string
|
|
readonly qrDataUrl: string
|
|
}
|
|
|
|
/** Combine an issued code with its operator-facing command + QR into the /api/pairing-codes payload. */
|
|
export async function buildPairingArtifacts(
|
|
issued: { readonly code: string; readonly expiresAt: string },
|
|
zone: string,
|
|
): Promise<PairingArtifacts> {
|
|
return {
|
|
code: issued.code,
|
|
expiresAt: issued.expiresAt,
|
|
pairCommand: buildPairCommand(issued.code, zone),
|
|
qrDataUrl: await buildQrDataUrl(issued.code),
|
|
}
|
|
}
|