nixamp 0.7.17 → 0.7.19
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/dist/publish.d.ts +8 -1
- package/dist/publish.js +1 -1
- package/dist/server.js +14 -7
- package/dist/share.d.ts +13 -2
- package/dist/share.js +14 -3
- package/package.json +1 -1
- package/src/publish.ts +9 -2
- package/src/server.ts +14 -7
- package/src/share.ts +15 -4
- package/web/dist/sw.js +1 -1
package/dist/publish.d.ts
CHANGED
|
@@ -12,7 +12,14 @@ export interface PublishTarget {
|
|
|
12
12
|
* gets a 401 in JSON, which is a caller hearing nothing.
|
|
13
13
|
*/
|
|
14
14
|
audio?: string;
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* How many tracks there are, asked at every heartbeat rather than once.
|
|
17
|
+
*
|
|
18
|
+
* Taken as a number, it was read before the library had finished loading --
|
|
19
|
+
* the port opens first now -- so a server with five thousand tracks
|
|
20
|
+
* advertised nought of them for as long as it stayed up.
|
|
21
|
+
*/
|
|
22
|
+
tracks: () => number;
|
|
16
23
|
nowPlaying: () => string;
|
|
17
24
|
/**
|
|
18
25
|
* The account this stream belongs to, from `nixamp login`.
|
package/dist/publish.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -775,10 +775,17 @@ export function createHandler(engine, options) {
|
|
|
775
775
|
// cookie, so every later fetch, EventSource and <audio src> carries it
|
|
776
776
|
// without the page knowing anything about keys. Either key works here, and
|
|
777
777
|
// which one was used decides what the browser can then do.
|
|
778
|
-
const
|
|
779
|
-
if (
|
|
780
|
-
const offered =
|
|
781
|
-
|
|
778
|
+
const inPath = key !== null ? keyInPath(path) : null;
|
|
779
|
+
if (inPath !== null) {
|
|
780
|
+
const offered = inPath.key;
|
|
781
|
+
// The path has to be telling the truth about the key it carries. A `/v/`
|
|
782
|
+
// link holding the control key would read as view-only to whoever you
|
|
783
|
+
// sent it to and hand them the controls, which is the whole reason for
|
|
784
|
+
// naming the two shapes in the first place.
|
|
785
|
+
//
|
|
786
|
+
// A mismatch answers exactly as a wrong key does, so nothing is learned
|
|
787
|
+
// from the difference between the two.
|
|
788
|
+
if (scopeOf(offered, key, listenKey) !== inPath.wants) {
|
|
782
789
|
json(response, 404, { error: "not found" });
|
|
783
790
|
return;
|
|
784
791
|
}
|
|
@@ -2937,9 +2944,9 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
2937
2944
|
name: options.name || hostname(),
|
|
2938
2945
|
url: listen,
|
|
2939
2946
|
audio,
|
|
2940
|
-
// Asked
|
|
2941
|
-
//
|
|
2942
|
-
tracks: engine.snapshot(false).trackCount,
|
|
2947
|
+
// Asked at every heartbeat rather than once, because the library is
|
|
2948
|
+
// read after the port opens and is still arriving when this is made.
|
|
2949
|
+
tracks: () => engine.snapshot(false).trackCount,
|
|
2943
2950
|
// From `nixamp login`. The directory will not list a stream it cannot
|
|
2944
2951
|
// attribute to somebody, because a listing is now a phone code that
|
|
2945
2952
|
// costs money to answer.
|
package/dist/share.d.ts
CHANGED
|
@@ -94,8 +94,19 @@ export declare function reachableAddresses(host: string, port: number, publicUrl
|
|
|
94
94
|
* A link should say what it is before anybody clicks it.
|
|
95
95
|
*/
|
|
96
96
|
export declare const KEY_PATHS: readonly ["/a/", "/v/"];
|
|
97
|
-
/**
|
|
98
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The key in a share link, and what the link claimed to be.
|
|
99
|
+
*
|
|
100
|
+
* Both halves, because a label nobody checks is a label that can lie. `/a/`
|
|
101
|
+
* means this administers and `/v/` means this only views; handed the other
|
|
102
|
+
* key, a path would otherwise have said one thing and done the other -- and
|
|
103
|
+
* the dangerous direction is real: a `/v/` link built around the control key
|
|
104
|
+
* reads as view-only to the person you send it to and hands them the controls.
|
|
105
|
+
*/
|
|
106
|
+
export declare function keyInPath(path: string): {
|
|
107
|
+
key: string;
|
|
108
|
+
wants: Scope;
|
|
109
|
+
} | null;
|
|
99
110
|
/** The full link, key and all. `admin` picks the shape that says which it is. */
|
|
100
111
|
export declare function shareLink(base: string, key: string | null, admin?: boolean): string;
|
|
101
112
|
/**
|
package/dist/share.js
CHANGED
|
@@ -251,7 +251,15 @@ export function reachableAddresses(host, port, publicUrl = "", scheme = "http")
|
|
|
251
251
|
* A link should say what it is before anybody clicks it.
|
|
252
252
|
*/
|
|
253
253
|
export const KEY_PATHS = ["/a/", "/v/"];
|
|
254
|
-
/**
|
|
254
|
+
/**
|
|
255
|
+
* The key in a share link, and what the link claimed to be.
|
|
256
|
+
*
|
|
257
|
+
* Both halves, because a label nobody checks is a label that can lie. `/a/`
|
|
258
|
+
* means this administers and `/v/` means this only views; handed the other
|
|
259
|
+
* key, a path would otherwise have said one thing and done the other -- and
|
|
260
|
+
* the dangerous direction is real: a `/v/` link built around the control key
|
|
261
|
+
* reads as view-only to the person you send it to and hands them the controls.
|
|
262
|
+
*/
|
|
255
263
|
export function keyInPath(path) {
|
|
256
264
|
for (const prefix of KEY_PATHS) {
|
|
257
265
|
if (!path.startsWith(prefix))
|
|
@@ -259,12 +267,15 @@ export function keyInPath(path) {
|
|
|
259
267
|
const rest = path.slice(prefix.length).replace(/\/+$/, "");
|
|
260
268
|
if (rest === "" || rest.includes("/"))
|
|
261
269
|
return null;
|
|
270
|
+
let key = rest;
|
|
262
271
|
try {
|
|
263
|
-
|
|
272
|
+
key = decodeURIComponent(rest);
|
|
264
273
|
}
|
|
265
274
|
catch {
|
|
266
|
-
|
|
275
|
+
// A key that is not valid percent-encoding is taken as written; it will
|
|
276
|
+
// fail to match anything, which is the right answer either way.
|
|
267
277
|
}
|
|
278
|
+
return { key, wants: prefix === "/a/" ? "control" : "listen" };
|
|
268
279
|
}
|
|
269
280
|
return null;
|
|
270
281
|
}
|
package/package.json
CHANGED
package/src/publish.ts
CHANGED
|
@@ -21,7 +21,14 @@ export interface PublishTarget {
|
|
|
21
21
|
* gets a 401 in JSON, which is a caller hearing nothing.
|
|
22
22
|
*/
|
|
23
23
|
audio?: string;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* How many tracks there are, asked at every heartbeat rather than once.
|
|
26
|
+
*
|
|
27
|
+
* Taken as a number, it was read before the library had finished loading --
|
|
28
|
+
* the port opens first now -- so a server with five thousand tracks
|
|
29
|
+
* advertised nought of them for as long as it stayed up.
|
|
30
|
+
*/
|
|
31
|
+
tracks: () => number;
|
|
25
32
|
nowPlaying: () => string;
|
|
26
33
|
/**
|
|
27
34
|
* The account this stream belongs to, from `nixamp login`.
|
|
@@ -92,7 +99,7 @@ export class Publisher {
|
|
|
92
99
|
name: this.target.name,
|
|
93
100
|
url: this.target.url,
|
|
94
101
|
...(this.target.audio ? { audio: this.target.audio } : {}),
|
|
95
|
-
tracks: this.target.tracks,
|
|
102
|
+
tracks: this.target.tracks(),
|
|
96
103
|
nowPlaying: this.target.nowPlaying(),
|
|
97
104
|
}),
|
|
98
105
|
});
|
package/src/server.ts
CHANGED
|
@@ -1074,10 +1074,17 @@ export function createHandler(engine: Engine, options: HandlerOptions) {
|
|
|
1074
1074
|
// cookie, so every later fetch, EventSource and <audio src> carries it
|
|
1075
1075
|
// without the page knowing anything about keys. Either key works here, and
|
|
1076
1076
|
// which one was used decides what the browser can then do.
|
|
1077
|
-
const
|
|
1078
|
-
if (
|
|
1079
|
-
const offered =
|
|
1080
|
-
|
|
1077
|
+
const inPath = key !== null ? keyInPath(path) : null;
|
|
1078
|
+
if (inPath !== null) {
|
|
1079
|
+
const offered = inPath.key;
|
|
1080
|
+
// The path has to be telling the truth about the key it carries. A `/v/`
|
|
1081
|
+
// link holding the control key would read as view-only to whoever you
|
|
1082
|
+
// sent it to and hand them the controls, which is the whole reason for
|
|
1083
|
+
// naming the two shapes in the first place.
|
|
1084
|
+
//
|
|
1085
|
+
// A mismatch answers exactly as a wrong key does, so nothing is learned
|
|
1086
|
+
// from the difference between the two.
|
|
1087
|
+
if (scopeOf(offered, key, listenKey) !== inPath.wants) {
|
|
1081
1088
|
json(response, 404, { error: "not found" });
|
|
1082
1089
|
return;
|
|
1083
1090
|
}
|
|
@@ -3394,9 +3401,9 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
|
|
|
3394
3401
|
name: options.name || hostname(),
|
|
3395
3402
|
url: listen,
|
|
3396
3403
|
audio,
|
|
3397
|
-
// Asked
|
|
3398
|
-
//
|
|
3399
|
-
tracks: engine.snapshot(false).trackCount,
|
|
3404
|
+
// Asked at every heartbeat rather than once, because the library is
|
|
3405
|
+
// read after the port opens and is still arriving when this is made.
|
|
3406
|
+
tracks: () => engine.snapshot(false).trackCount,
|
|
3400
3407
|
// From `nixamp login`. The directory will not list a stream it cannot
|
|
3401
3408
|
// attribute to somebody, because a listing is now a phone code that
|
|
3402
3409
|
// costs money to answer.
|
package/src/share.ts
CHANGED
|
@@ -282,17 +282,28 @@ export function reachableAddresses(
|
|
|
282
282
|
*/
|
|
283
283
|
export const KEY_PATHS = ["/a/", "/v/"] as const;
|
|
284
284
|
|
|
285
|
-
/**
|
|
286
|
-
|
|
285
|
+
/**
|
|
286
|
+
* The key in a share link, and what the link claimed to be.
|
|
287
|
+
*
|
|
288
|
+
* Both halves, because a label nobody checks is a label that can lie. `/a/`
|
|
289
|
+
* means this administers and `/v/` means this only views; handed the other
|
|
290
|
+
* key, a path would otherwise have said one thing and done the other -- and
|
|
291
|
+
* the dangerous direction is real: a `/v/` link built around the control key
|
|
292
|
+
* reads as view-only to the person you send it to and hands them the controls.
|
|
293
|
+
*/
|
|
294
|
+
export function keyInPath(path: string): { key: string; wants: Scope } | null {
|
|
287
295
|
for (const prefix of KEY_PATHS) {
|
|
288
296
|
if (!path.startsWith(prefix)) continue;
|
|
289
297
|
const rest = path.slice(prefix.length).replace(/\/+$/, "");
|
|
290
298
|
if (rest === "" || rest.includes("/")) return null;
|
|
299
|
+
let key = rest;
|
|
291
300
|
try {
|
|
292
|
-
|
|
301
|
+
key = decodeURIComponent(rest);
|
|
293
302
|
} catch {
|
|
294
|
-
|
|
303
|
+
// A key that is not valid percent-encoding is taken as written; it will
|
|
304
|
+
// fail to match anything, which is the right answer either way.
|
|
295
305
|
}
|
|
306
|
+
return { key, wants: prefix === "/a/" ? "control" : "listen" };
|
|
296
307
|
}
|
|
297
308
|
return null;
|
|
298
309
|
}
|
package/web/dist/sw.js
CHANGED