fix(cors): allow play from .to / staging / onion mirrors
Daemon CORS allowlist was hardcoded to torrentclaw.com + localhost. Browsers playing from any other official mirror (.to, onion, www., staging.) received 200 + body from the daemon's HLS server but no Access-Control-Allow-Origin header, so the response was dropped client-side. Probe loop treated every candidate as a failure and surfaced "No se puede conectar con tu agente — 404 todos los canales" even though the tunnel + ffmpeg were healthy. Static baseline now includes the full known mirror set (.com / www / app / staging / .to / www.to / built-in onion). At startup the daemon also fetches /api/mirrors with IPFS fallback and merges the live origins, so a future mirror addition does not require a CLI rebuild.
This commit is contained in:
parent
2e7cd7e8ed
commit
7b78d0b778
2 changed files with 69 additions and 1 deletions
|
|
@ -293,7 +293,15 @@ func runDaemonStart() error {
|
|||
// Create persistent stream server
|
||||
streamSrv := engine.NewStreamServer(cfg.Download.StreamPort)
|
||||
streamSrv.SetUPnPEnabled(cfg.Download.EnableUPnP)
|
||||
streamSrv.SetCORSAllowedOrigins(cfg.Download.CORSExtraOrigins)
|
||||
// CORS extras = operator config + dynamic mirror list from /api/mirrors.
|
||||
// Without the mirror merge, a user playing from `torrentclaw.to` (or any
|
||||
// future mirror) hits the daemon, gets 200 + body, but no
|
||||
// `Access-Control-Allow-Origin` → browser drops the response → player
|
||||
// reports "404 todos los canales". Fetching /api/mirrors at startup
|
||||
// future-proofs against mirror additions without a CLI rebuild.
|
||||
corsExtras := append([]string(nil), cfg.Download.CORSExtraOrigins...)
|
||||
corsExtras = append(corsExtras, mirrorCORSOrigins(ctx, cfg, userAgent)...)
|
||||
streamSrv.SetCORSAllowedOrigins(corsExtras)
|
||||
// Reap HLS tmpdirs left over from a previous daemon run before we start
|
||||
// accepting new sessions. The in-memory registry doesn't survive a
|
||||
// restart, so without this disk usage grows unbounded across restarts.
|
||||
|
|
@ -862,3 +870,48 @@ func superviseFunnel(ctx context.Context, d *agent.Daemon, port int) {
|
|||
backoff = min(backoff*2, maxBackoff)
|
||||
}
|
||||
}
|
||||
|
||||
// mirrorCORSOrigins fetches /api/mirrors from the configured primary (+ extra
|
||||
// mirror candidates + static IPFS fallback) and returns the discovered URLs as
|
||||
// Origin strings. Best-effort: any failure logs a warning and returns an empty
|
||||
// slice; the static defaultCORSAllowedOrigins in validate.go covers the known
|
||||
// mirrors (.com / .to / built-in onion) so the daemon still accepts the
|
||||
// official surfaces when this call fails.
|
||||
//
|
||||
// Bounded to a short timeout so a slow /api/mirrors response can't delay
|
||||
// daemon startup — every second here is a second the user can't play.
|
||||
func mirrorCORSOrigins(parent context.Context, cfg config.Config, userAgent string) []string {
|
||||
ctx, cancel := context.WithTimeout(parent, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
candidates := append([]string{cfg.Auth.APIURL}, cfg.Auth.Mirrors...)
|
||||
resp, err := agent.FetchMirrorsWithFallback(ctx, candidates, userAgent)
|
||||
if err != nil {
|
||||
log.Printf("[cors] mirror discovery failed (%v) — using static allowlist only", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{})
|
||||
out := make([]string, 0, len(resp.Mirrors))
|
||||
add := func(rawURL string) {
|
||||
if rawURL == "" {
|
||||
return
|
||||
}
|
||||
origin := strings.TrimRight(rawURL, "/")
|
||||
if _, dup := seen[origin]; dup {
|
||||
return
|
||||
}
|
||||
seen[origin] = struct{}{}
|
||||
out = append(out, origin)
|
||||
}
|
||||
for _, m := range resp.Mirrors {
|
||||
add(m.URL)
|
||||
}
|
||||
if resp.Tor != nil {
|
||||
add(resp.Tor.URL)
|
||||
}
|
||||
if len(out) > 0 {
|
||||
log.Printf("[cors] merged %d mirror origins from /api/mirrors", len(out))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,27 @@ var validSessionID = regexp.MustCompile(`^[a-zA-Z0-9_-]{1,128}$`)
|
|||
// 127.0.0.1 is listed in addition to localhost because some browsers treat
|
||||
// them as distinct origins for CORS.
|
||||
//
|
||||
// Mirrors (`.to`, `staging.torrentclaw.com`, `www.`) are listed so a user
|
||||
// playing from any official mirror succeeds the HEAD probe; without these
|
||||
// the browser drops the response for "missing ACAO" and the player reports
|
||||
// "404 todos los canales" even though the daemon returned 200.
|
||||
//
|
||||
// Note: media tags (<video src>, <audio src>) do not send the Origin
|
||||
// header so they are not gated by CORS at all; this allowlist only
|
||||
// affects fetch()/XHR.
|
||||
var defaultCORSAllowedOrigins = []string{
|
||||
"https://torrentclaw.com",
|
||||
"https://www.torrentclaw.com",
|
||||
"https://app.torrentclaw.com",
|
||||
"https://staging.torrentclaw.com",
|
||||
"https://torrentclaw.to",
|
||||
"https://www.torrentclaw.to",
|
||||
// Tor mirror — Tor Browser sends `Origin: http://<addr>.onion` (plain
|
||||
// http, no port). Mirror address is the BUILT_IN_ONION constant from
|
||||
// torrentclaw-web/src/lib/mirrors-config.ts; rotates rarely, kept in
|
||||
// sync by hand. Daemon also dynamically merges /api/mirrors at startup
|
||||
// (see daemon.go) so a new key doesn't need a CLI rebuild.
|
||||
"http://torrentf3aifidcsaaanmnmuhv2s53r6hqsl3zkmfidiaxainkeqk5id.onion",
|
||||
"http://localhost:3030",
|
||||
"http://127.0.0.1:3030",
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue