vip only spectator option

This commit is contained in:
legop3
2026-07-14 21:38:55 -04:00
parent 017b3c69d5
commit 3aa97baa4f
9 changed files with 18 additions and 7 deletions
+3 -2
View File
@@ -15,7 +15,8 @@
- external spectator access (new) - external spectator access (new)
- off (no one can access the spectate page externally) - off (no one can access the spectate page externally)
- on (everyone can access the spectate page externally) - on (everyone can access the spectate page externally)
- admin (external spectators get mode gate overlay, when logged in once that identity gets spectator access forever. use database.) - verifiedOnly (only verified identities can access the spectate page externally)
- admin (external spectators need a saved spectatorAccess.external identity grant)
- anything else related to bandwidth savings should also get config - anything else related to bandwidth savings should also get config
## implemented config shape ## implemented config shape
@@ -24,5 +25,5 @@ bandwidthSavings:
multiTabProtection: "verifiedOnly" # allowed | verifiedOnly | notAllowed multiTabProtection: "verifiedOnly" # allowed | verifiedOnly | notAllowed
nonTurnVideo: "snapshots" # snapshots | live nonTurnVideo: "snapshots" # snapshots | live
externalSpectatorVideo: "snapshots" # snapshots | live externalSpectatorVideo: "snapshots" # snapshots | live
externalSpectatorAccess: "on" # off | on | admin externalSpectatorAccess: "on" # off | on | verifiedOnly | admin
``` ```
+1
View File
@@ -70,6 +70,7 @@ bandwidthSavings:
# Whether non-local users may enter the spectator page. # Whether non-local users may enter the spectator page.
# off: block external spectators # off: block external spectators
# on: allow external spectators # on: allow external spectators
# verifiedOnly: require a verified identity, but no separate spectator grant
# admin: require an identity feature-state grant at spectatorAccess.external # admin: require an identity feature-state grant at spectatorAccess.external
externalSpectatorAccess: "on" externalSpectatorAccess: "on"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -78,7 +78,7 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script> <script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script> <script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title> <title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-DwzMEW-o.js"></script> <script type="module" crossorigin src="/assets/index-UX9YB3IV.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-yu4pxjK3.css"> <link rel="stylesheet" crossorigin href="/assets/index-yu4pxjK3.css">
</head> </head>
<body> <body>
+3 -1
View File
@@ -6,7 +6,7 @@ const { loadConfig } = require('./configLoader');
const MULTI_TAB_MODES = new Set(['allowed', 'verifiedOnly', 'notAllowed']); const MULTI_TAB_MODES = new Set(['allowed', 'verifiedOnly', 'notAllowed']);
const VIDEO_MODES = new Set(['snapshots', 'live']); const VIDEO_MODES = new Set(['snapshots', 'live']);
const EXTERNAL_SPECTATOR_ACCESS_MODES = new Set(['off', 'on', 'admin']); const EXTERNAL_SPECTATOR_ACCESS_MODES = new Set(['off', 'on', 'verifiedOnly', 'admin']);
const DEFAULT_BANDWIDTH_SAVINGS = Object.freeze({ const DEFAULT_BANDWIDTH_SAVINGS = Object.freeze({
multiTabProtection: 'verifiedOnly', multiTabProtection: 'verifiedOnly',
@@ -83,6 +83,7 @@ function shouldUseSnapshotsForExternalSpectatorVideo() {
function canUseExternalSpectatorAccess({ function canUseExternalSpectatorAccess({
isLocal = false, isLocal = false,
isAdmin = false, isAdmin = false,
isVerified = false,
hasGrant = false, hasGrant = false,
} = {}) { } = {}) {
/* /*
@@ -93,6 +94,7 @@ function canUseExternalSpectatorAccess({
if (isLocal || isAdmin) return true; if (isLocal || isAdmin) return true;
const { externalSpectatorAccess } = getBandwidthSavingsPolicy(); const { externalSpectatorAccess } = getBandwidthSavingsPolicy();
if (externalSpectatorAccess === 'off') return false; if (externalSpectatorAccess === 'off') return false;
if (externalSpectatorAccess === 'verifiedOnly') return Boolean(isVerified);
if (externalSpectatorAccess === 'admin') return Boolean(hasGrant); if (externalSpectatorAccess === 'admin') return Boolean(hasGrant);
return true; return true;
} }
+4
View File
@@ -65,12 +65,16 @@ function canBecomeSpectator(socket) {
return canUseExternalSpectatorAccess({ return canUseExternalSpectatorAccess({
isLocal: local, isLocal: local,
isAdmin: isAdmin(socket), isAdmin: isAdmin(socket),
isVerified: Boolean(socket?.data?.isVerified),
hasGrant: hasExternalSpectatorGrant(socket), hasGrant: hasExternalSpectatorGrant(socket),
}); });
} }
function externalSpectatorAccessError() { function externalSpectatorAccessError() {
const mode = getBandwidthSavingsPolicy().externalSpectatorAccess; const mode = getBandwidthSavingsPolicy().externalSpectatorAccess;
if (mode === 'verifiedOnly') {
return 'External spectator access requires a verified identity.';
}
if (mode === 'admin') { if (mode === 'admin') {
return 'External spectator access requires admin approval for this identity.'; return 'External spectator access requires admin approval for this identity.';
} }
@@ -94,6 +94,7 @@ function buildBandwidthSavingsSessionState(socket) {
canUseExternalSpectatorAccess: canUseExternalSpectatorAccess({ canUseExternalSpectatorAccess: canUseExternalSpectatorAccess({
isLocal: local, isLocal: local,
isAdmin: isAdmin(socket), isAdmin: isAdmin(socket),
isVerified: Boolean(socket?.data?.isVerified),
hasGrant: granted, hasGrant: granted,
}), }),
}; };
@@ -77,6 +77,8 @@ export default function SpectatorContent() {
*/ */
const message = spectatorAccessMode === 'admin' const message = spectatorAccessMode === 'admin'
? 'This external spectator identity needs admin approval before it can view the spectator page.' ? 'This external spectator identity needs admin approval before it can view the spectator page.'
: spectatorAccessMode === 'verifiedOnly'
? 'This external spectator identity must be verified before it can view the spectator page.'
: 'External spectator access is disabled on this server.'; : 'External spectator access is disabled on this server.';
return ( return (
<div className="flex min-h-screen items-center justify-center bg-black text-slate-200"> <div className="flex min-h-screen items-center justify-center bg-black text-slate-200">