feat: implement togglable debug log streaming for server stdout/stderr and client console logs
- Created server/utils/logCapture.js to intercept and buffer server output, stripping ANSI escape codes. - Created server/middleware/logStreamAuth.js enforcing subnet IP filtering (LOG_ALLOW_SUBNETS), Emby session cookie, Basic Auth fallback, and X-Webhook-Secret header bypass. - Created server/routes/debug.js with SSE streams /api/debug/server-logs, /api/debug/client-logs and batched POST /api/debug/client-logs. Exposes public configuration status at /api/debug/status. - Integrated log capture and mounted debug routes in server/app.js and server/index.js. - Implemented client/src/utils/clientLogCapture.js in the frontend SPA to hook console log/warn/error and flush batched console events. - Documented all endpoints in OpenAPI server/openapi.yaml, ARCHITECTURE.md, and README.md. - Wrote route integration tests and frontend console capture tests, with full validation in swagger-coverage.
This commit is contained in:
@@ -1752,3 +1752,172 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/debug/status:
|
||||
get:
|
||||
tags: [Debug]
|
||||
summary: Check if log streaming is enabled
|
||||
description: Returns whether the log streaming feature is enabled at runtime. No authentication required.
|
||||
security: []
|
||||
responses:
|
||||
'200':
|
||||
description: Feature status returned successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
|
||||
/api/debug/server-logs:
|
||||
get:
|
||||
tags: [Debug]
|
||||
summary: Stream server logs in real-time
|
||||
description: |
|
||||
Streams server-side standard output (stdout/stderr) logs via Server-Sent Events (SSE).
|
||||
|
||||
**Security Policies:**
|
||||
- **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set).
|
||||
- **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`.
|
||||
- **Session Auth**: Emby session cookie `emby_user` where user is an administrator.
|
||||
- **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
parameters:
|
||||
- name: X-Webhook-Secret
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Fast-track webhook secret bypass token
|
||||
responses:
|
||||
'200':
|
||||
description: Event stream established
|
||||
content:
|
||||
text/event-stream:
|
||||
schema:
|
||||
type: string
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden (feature disabled or IP not in subnet allowlist)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
/api/debug/client-logs:
|
||||
get:
|
||||
tags: [Debug]
|
||||
summary: Stream client console logs in real-time
|
||||
description: |
|
||||
Streams client-side console logs via Server-Sent Events (SSE).
|
||||
|
||||
**Security Policies:**
|
||||
- **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set).
|
||||
- **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`.
|
||||
- **Session Auth**: Emby session cookie `emby_user` where user is an administrator.
|
||||
- **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
parameters:
|
||||
- name: X-Webhook-Secret
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Fast-track webhook secret bypass token
|
||||
responses:
|
||||
'200':
|
||||
description: Event stream established
|
||||
content:
|
||||
text/event-stream:
|
||||
schema:
|
||||
type: string
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden (feature disabled or IP not in subnet allowlist)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
post:
|
||||
tags: [Debug]
|
||||
summary: Ingest client console logs
|
||||
description: |
|
||||
Ingests a batch of client-side console logs into the server-side rolling clientLogBuffer.
|
||||
|
||||
**Security Policies:**
|
||||
- **Subnet IP Filtering**: IP must match subnet ranges configured in `LOG_ALLOW_SUBNETS` (if set).
|
||||
- **Bypass Header**: Direct bypass if `X-Webhook-Secret` header matches the configured `SOFARR_WEBHOOK_SECRET`.
|
||||
- **Session Auth**: Emby session cookie `emby_user` where user is an administrator.
|
||||
- **Basic Auth Fallback**: `Authorization` header containing credentials of a valid Emby administrator.
|
||||
security:
|
||||
- CookieAuth: []
|
||||
parameters:
|
||||
- name: X-Webhook-Secret
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Fast-track webhook secret bypass token
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [level, message]
|
||||
properties:
|
||||
timestamp:
|
||||
type: string
|
||||
format: date-time
|
||||
level:
|
||||
type: string
|
||||
enum: [info, warn, error]
|
||||
message:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Logs ingested successfully
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
count:
|
||||
type: integer
|
||||
'400':
|
||||
description: Invalid JSON body
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'401':
|
||||
description: Unauthorized
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
'403':
|
||||
description: Forbidden (feature disabled or IP not in subnet allowlist)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user