@torrent-tv/proxy 2.89.16 → 2.89.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 2.89.17
2
+
3
+ - **Fix**: Every browser log line the proxy keeps names the page's send it came in, as `batch=<n>`, and the server prints the same number. A send that reached neither side, or a batch the proxy took over a connection that could no longer carry its answer back, is now visible by comparing the two (torrent-tv/meta#77).
4
+
1
5
  ## 2.89.16
2
6
 
3
7
  - **Fix**: A proxy started without `--log-file` keeps the browser's log. The page sends its log to the proxy for most of a viewing, and the proxy wrote it only to per-session files beside its own log file; with no log file named, which is every run from npm or Docker other than the Home Assistant addon, the browser's half of every viewing was dropped. It now goes to the console, beside the proxy's lines, each prefixed `client <sessionId>`. `docs/logs.md` states where the log goes for each way of starting the proxy (torrent-tv/meta#96).
package/docs/logs.md CHANGED
@@ -98,6 +98,19 @@ The split is by PHASE, and neither half is the whole session:
98
98
  3. **At unload** it goes to the server again: `navigator.sendBeacon` is the only
99
99
  thing a page that is going away can use, and a data channel is not;
100
100
  4. **A batch that cannot reach the proxy** falls back to the server.
101
+ 5. **A batch nobody took** goes back into the page's queue and leaves with the
102
+ next send. What the page lost — lines pushed out by its 500-line queue — and
103
+ every refused send, by route and cause, are written into the log itself as
104
+ one line, `[client-logger] since <time>: …`, which travels with the next
105
+ batch that is delivered.
106
+
107
+ Every line carries the number of the page's send it came in, as `batch=<n>`,
108
+ on both sides: `[Windows/Chrome batch=12]` here, `[client Windows/Chrome
109
+ <sessionId> batch=12]` on the droplet. A number on neither side is a send no
110
+ route took: its lines went out again under a later number, and the report line
111
+ says why. A number on both is a batch the proxy took but whose answer never
112
+ reached the page — a connection wedged in the proxy-to-page direction still
113
+ carries requests the other way — which the page then sent to the server too.
101
114
 
102
115
  So a session that failed while connecting is on the droplet, a session that
103
116
  failed while playing is on the addon host, and a complete reading of a long
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.89.16",
3
+ "version": "2.89.17",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -53,7 +53,13 @@ function sanitizeLine(value) {
53
53
  * `POST /api/client-logs`
54
54
  *
55
55
  * Body: `{ sessionId, startedAt, torrentName, infoHash, tag, signalSessionId,
56
- * lines: [{ level, ts, msg }] }`.
56
+ * seq, lines: [{ level, ts, msg }] }`.
57
+ *
58
+ * `seq` numbers the page's sends and is printed as `batch=<seq>`. The server
59
+ * prints the same number, so a batch that reached neither side shows as a gap,
60
+ * and one taken here whose answer never reached the page — a wedged connection
61
+ * still carries the request — shows on both, because the page then sends it to
62
+ * the server too.
57
63
  *
58
64
  * @param {import("fastify").FastifyRequest} req
59
65
  * @param {import("fastify").FastifyReply} reply
@@ -69,6 +75,7 @@ export async function handleApiClientLogsPost(req, reply, { clientLogs }) {
69
75
  infoHash: safeString(body.infoHash, 40)
70
76
  };
71
77
  const tag = sanitizeLine(safeString(body.tag, MAX_TAG_LEN)) || "Unknown/Unknown";
78
+ const batch = Number.isSafeInteger(body.seq) && body.seq > 0 ? ` batch=${body.seq}` : "";
72
79
  const rows = Array.isArray(body.lines) ? body.lines.slice(0, MAX_LINES) : [];
73
80
 
74
81
  const lines = rows.map((row) => {
@@ -76,7 +83,7 @@ export async function handleApiClientLogsPost(req, reply, { clientLogs }) {
76
83
  const level = safeString(entry.level, 8) || "log";
77
84
  const at = sanitizeLine(safeString(entry.ts, 16));
78
85
  const message = sanitizeLine(safeString(entry.msg, MAX_MSG_LEN));
79
- return `[${tag}] ${at} ${level}: ${message}`;
86
+ return `[${tag}${batch}] ${at} ${level}: ${message}`;
80
87
  });
81
88
 
82
89
  if (lines.length > 0) {
@@ -0,0 +1,34 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { handleApiClientLogsPost } from "../routes/api/client-logs/post.js";
5
+
6
+ /**
7
+ * Post one body and return the lines the route wrote.
8
+ *
9
+ * @param {object} body
10
+ * @returns {Promise<string[]>}
11
+ */
12
+ async function linesWrittenFor(body) {
13
+ /** @type {string[]} */
14
+ const written = [];
15
+ const reply = { code: () => reply, send: () => reply };
16
+ await handleApiClientLogsPost({ body }, reply, {
17
+ clientLogs: { write: (_session, lines) => written.push(...lines) }
18
+ });
19
+ return written;
20
+ }
21
+
22
+ const line = { level: "info", ts: "12:00:00.000", msg: "hello" };
23
+
24
+ test("each line names the page's send it came in, so the two receivers can be compared", async () => {
25
+ assert.deepEqual(await linesWrittenFor({ tag: "Windows/Chrome", seq: 12, lines: [line] }), [
26
+ "[Windows/Chrome batch=12] 12:00:00.000 info: hello"
27
+ ]);
28
+ });
29
+
30
+ test("a page that numbers nothing is written as before", async () => {
31
+ assert.deepEqual(await linesWrittenFor({ tag: "Windows/Chrome", seq: "12", lines: [line] }), [
32
+ "[Windows/Chrome] 12:00:00.000 info: hello"
33
+ ]);
34
+ });