Rework flag

This commit is contained in:
Kalle
2026-08-07 12:45:56 +03:00
parent 6876ae9f65
commit 691c9a9ccf
7 changed files with 56 additions and 54 deletions

View File

@@ -48,6 +48,9 @@ SQL_LOG=none
VITE_SHOW_LUTI_NAV_ITEM=false
// If false the scanner page and its ingest endpoint are admin only
VITE_SCANNER_ENABLED=false
// Push notification. Generate values here https://vapidkeys.com/
VITE_VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=

View File

@@ -42,6 +42,7 @@ const values = {
VITE_PROD_MODE: stringBool("VITE_PROD_MODE"),
VITE_SHOW_LUTI_NAV_ITEM: stringBool("VITE_SHOW_LUTI_NAV_ITEM"),
VITE_FUSE_ENABLED: stringBool("VITE_FUSE_ENABLED"),
VITE_SCANNER_ENABLED: stringBool("VITE_SCANNER_ENABLED"),
VITE_LEAGUE_GOOGLE_FORM_URL: env.VITE_LEAGUE_GOOGLE_FORM_URL,
VITE_SHOW_BANNER_FOR_SEASON: env.VITE_SHOW_BANNER_FOR_SEASON,
VITE_SENTRY_DSN: env.VITE_SENTRY_DSN,
@@ -66,6 +67,8 @@ export const Config = {
/** Whether to show the LUTI navigation item. */
showLutiNavItem: values.VITE_SHOW_LUTI_NAV_ITEM,
fuseEnabled: values.VITE_FUSE_ENABLED,
/** Whether the scanner is available to everyone. While false only the admin can use the scanner page and its ingest endpoint. */
scannerEnabled: values.VITE_SCANNER_ENABLED,
/** Google Form URL for league registration, if configured. */
leagueGoogleFormUrl: values.VITE_LEAGUE_GOOGLE_FORM_URL,
/** Season identifier to show the registration banner for, if any. */

View File

@@ -1,9 +1,11 @@
import type { ActionFunction } from "react-router";
import { Config } from "~/config";
import { requireUser } from "~/features/auth/core/user.server";
import type { ScannerMatch } from "~/features/scanner/core/scanner-match";
import * as UserRepository from "~/features/user-page/UserRepository.server";
import { isAdmin } from "~/modules/permissions/utils";
import { logger } from "~/utils/logger";
import { badRequestIfFalsy, parseBody } from "~/utils/remix.server";
import { badRequestIfFalsy, forbidden, parseBody } from "~/utils/remix.server";
import * as Scoreboards from "../core/Scoreboards";
import * as ScannerIngestRepository from "../ScannerIngestRepository.server";
import { ingestBodySchema } from "../scanner-ingest-schemas";
@@ -15,6 +17,10 @@ import { ingestBodySchema } from "../scanner-ingest-schemas";
export const action: ActionFunction = async ({ request }) => {
const user = requireUser();
if (!Config.scannerEnabled && !isAdmin(user)) {
forbidden();
}
const data = await parseBody({ request, schema: ingestBodySchema });
const povUserId = data.povUserId ?? user?.id ?? null;

View File

@@ -42,7 +42,6 @@ import { EventCard } from "./EventCard";
import { EventsSummary } from "./EventsSummary";
import { downloadEventsCsv } from "./events-csv";
import { type FixtureData, saveFixture } from "./fixture-export";
import { SENDOU_UPLOAD_ENABLED } from "./flags";
import { MatchCard } from "./MatchCard";
import { MatchLobbyTabs } from "./MatchLobbyTabs";
import {
@@ -293,25 +292,21 @@ export function LivePage({
<div className="controls">
{!running ? (
<>
{SENDOU_UPLOAD_ENABLED ? (
<button
type="button"
disabled={!sendouUser}
title={sendouUser ? undefined : "log in on sendou.ink first"}
onClick={() => void start(true)}
>
<Send aria-hidden />
Start capture
</button>
) : null}
<button
type="button"
className={SENDOU_UPLOAD_ENABLED ? "outlined" : undefined}
disabled={!sendouUser}
title={sendouUser ? undefined : "log in on sendou.ink first"}
onClick={() => void start(true)}
>
<Send aria-hidden />
Start capture
</button>
<button
type="button"
className="outlined"
onClick={() => void start(false)}
>
{SENDOU_UPLOAD_ENABLED
? "Start capture (no sending)"
: "Start capture"}
Start capture (no sending)
</button>
</>
) : (
@@ -319,21 +314,19 @@ export function LivePage({
<button type="button" onClick={stop}>
Stop
</button>
{SENDOU_UPLOAD_ENABLED ? (
<button
type="button"
className="outlined"
disabled={!sendouUser}
title={sendouUser ? undefined : "log in on sendou.ink first"}
onClick={() => {
liveSendRef.current = !liveSend;
setLiveSend(!liveSend);
}}
>
<Send aria-hidden />
{liveSend ? "Stop sending" : "Start sending"}
</button>
) : null}
<button
type="button"
className="outlined"
disabled={!sendouUser}
title={sendouUser ? undefined : "log in on sendou.ink first"}
onClick={() => {
liveSendRef.current = !liveSend;
setLiveSend(!liveSend);
}}
>
<Send aria-hidden />
{liveSend ? "Stop sending" : "Start sending"}
</button>
</>
)}
<select value={deviceId} onChange={(e) => setDeviceId(e.target.value)}>
@@ -411,7 +404,7 @@ export function LivePage({
justFormed={justFormed}
send={aggregateSendStatus(built.sources)}
onSend={
SENDOU_UPLOAD_ENABLED && sendouUser && !skipReason
sendouUser && !skipReason
? () => void send(matchContaining(id), { manual: true })
: undefined
}
@@ -466,7 +459,6 @@ export function LivePage({
}
send={e.send}
onSend={
SENDOU_UPLOAD_ENABLED &&
sendouUser &&
e.id !== undefined &&
INGESTABLE_TYPES.includes(e.type)
@@ -525,15 +517,13 @@ function LiveMenu({
>
CSV
</SendouMenuItem>
{SENDOU_UPLOAD_ENABLED ? (
<SendouMenuItem
icon={<Send />}
isDisabled={!canSend}
onAction={onSendUnsent}
>
Send unsent to sendou.ink
</SendouMenuItem>
) : null}
<SendouMenuItem
icon={<Send />}
isDisabled={!canSend}
onAction={onSendUnsent}
>
Send unsent to sendou.ink
</SendouMenuItem>
<SendouMenuItem
icon={<Trash2 />}
isDestructive

View File

@@ -56,7 +56,6 @@ import { EventCard, type GetFrame } from "./EventCard";
import { EventsSummary } from "./EventsSummary";
import { downloadEventsCsv } from "./events-csv";
import type { FixtureData } from "./fixture-export";
import { SENDOU_UPLOAD_ENABLED } from "./flags";
import { formatTime } from "./format";
import { MatchCard } from "./MatchCard";
import { MatchLobbyTabs } from "./MatchLobbyTabs";
@@ -602,18 +601,18 @@ export function VodPage({
` · ${progress.rate.toFixed(0)}× realtime`}
</span>
)}
{SENDOU_UPLOAD_ENABLED && upload?.url && (
{upload?.url && (
<Link to={upload.url} className="link-button">
<Video aria-hidden />
Add VoD
</Link>
)}
{SENDOU_UPLOAD_ENABLED && upload?.problem && (
{upload?.problem && (
<span className="score">
upload unavailable: {upload.problem}
</span>
)}
{SENDOU_UPLOAD_ENABLED && resultsMatchCount > 0 && (
{resultsMatchCount > 0 && (
<button
type="button"
disabled={!sendouUser || resultsSend?.state === "sending"}

View File

@@ -1,7 +0,0 @@
/**
* WIP: sendou.ink uploading is not live yet — this hides every button that
* sends data to sendou.ink (Live tab live send / send unsent / per-card Send,
* VoD tab "Add VoD" and "Send results"). Flip to true to bring
* them back.
*/
export const SENDOU_UPLOAD_ENABLED = true;

View File

@@ -2,7 +2,10 @@ import { lazy } from "react";
import type { MetaFunction } from "react-router";
import { Main } from "~/components/Main";
import { Placeholder } from "~/components/Placeholder";
import { Redirect } from "~/components/Redirect";
import { Config } from "~/config";
import { useHydrated } from "~/hooks/useHydrated";
import { useHasRole } from "~/modules/permissions/hooks";
import { metaTags } from "~/utils/remix";
import type { SendouRouteHandle } from "~/utils/remix.server";
@@ -31,6 +34,11 @@ const ScannerApp = lazy(() =>
export default function ScannerPage() {
const isHydrated = useHydrated();
const isAdmin = useHasRole("ADMIN");
if (!Config.scannerEnabled && !isAdmin) {
return <Redirect to="/" />;
}
return <Main bigger>{isHydrated ? <ScannerApp /> : <Placeholder />}</Main>;
}