Development #25

Merged
kyle merged 3 commits from development into main 2026-08-31 12:35:34 -05:00
19 changed files with 254 additions and 230 deletions
Showing only changes of commit f6faf97cdf - Show all commits
+17 -8
View File
@@ -1,5 +1,5 @@
// Grant or revoke the "Admin" role for a user by phone number (US +1 assumed). // Grant or revoke a role for a user by phone number (US +1 assumed).
// Usage: bun run scripts/make-admin.ts <phone> [--revoke] // Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]
import "dotenv/config"; import "dotenv/config";
import SuperTokens from "supertokens-node"; import SuperTokens from "supertokens-node";
import Session from "supertokens-node/recipe/session"; import Session from "supertokens-node/recipe/session";
@@ -26,10 +26,19 @@ SuperTokens.init({
], ],
}); });
const VALID_ROLES = ["Admin", "Developer"] as const;
const raw = process.argv[2]; const raw = process.argv[2];
const revoke = process.argv.includes("--revoke"); const revoke = process.argv.includes("--revoke");
const roleFlagIndex = process.argv.indexOf("--role");
const role = roleFlagIndex === -1 ? "Admin" : process.argv[roleFlagIndex + 1];
if (!raw) { if (!raw) {
console.error("Usage: bun run scripts/make-admin.ts <phone> [--revoke]"); console.error("Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]");
process.exit(1);
}
if (!VALID_ROLES.includes(role as (typeof VALID_ROLES)[number])) {
console.error(`Invalid role "${role}". Valid roles: ${VALID_ROLES.join(", ")}`);
process.exit(1); process.exit(1);
} }
@@ -60,15 +69,15 @@ if (!user) {
process.exit(1); process.exit(1);
} }
await UserRoles.createNewRoleOrAddPermissions("Admin", []); await UserRoles.createNewRoleOrAddPermissions(role, []);
if (revoke) { if (revoke) {
const res = await UserRoles.removeUserRole("public", user.id, "Admin"); const res = await UserRoles.removeUserRole("public", user.id, role);
console.log(`Removed Admin from ${matched} (user ${user.id}):`, res.status); console.log(`Removed ${role} from ${matched} (user ${user.id}):`, res.status);
} else { } else {
const res = await UserRoles.addRoleToUser("public", user.id, "Admin"); const res = await UserRoles.addRoleToUser("public", user.id, role);
console.log( console.log(
`Granted Admin to ${matched} (user ${user.id}):`, `Granted ${role} to ${matched} (user ${user.id}):`,
res.status === "OK" res.status === "OK"
? res.didUserAlreadyHaveRole ? res.didUserAlreadyHaveRole
? "already had it" ? "already had it"
+7 -1
View File
@@ -1,7 +1,8 @@
import { Outlet, createFileRoute, useNavigate, useRouterState } from "@tanstack/react-router"; import { Outlet, createFileRoute, redirect, useNavigate, useRouterState } from "@tanstack/react-router";
import { Tabs } from "@mantine/core"; import { Tabs } from "@mantine/core";
import { Trans } from "@lingui/react/macro"; import { Trans } from "@lingui/react/macro";
import { msg } from "@lingui/core/macro"; import { msg } from "@lingui/core/macro";
import { isDeveloperRole } from "@/features/core/utils/roles";
const TAB_ROUTES = { const TAB_ROUTES = {
usage: "/admin/telemetry/usage", usage: "/admin/telemetry/usage",
@@ -15,6 +16,11 @@ type TabValue = keyof typeof TAB_ROUTES;
export const Route = createFileRoute("/_authed/admin/telemetry")({ export const Route = createFileRoute("/_authed/admin/telemetry")({
component: TelemetryLayout, component: TelemetryLayout,
beforeLoad: ({ context }) => {
if (!isDeveloperRole(context.auth?.roles)) {
throw redirect({ to: "/admin" });
}
},
loader: () => ({ loader: () => ({
header: { header: {
title: msg`Telemetry`, title: msg`Telemetry`,
+2 -1
View File
@@ -1,6 +1,7 @@
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { msg } from "@lingui/core/macro"; import { msg } from "@lingui/core/macro";
import { superTokensRequestMiddleware } from "@/utils/supertokens"; import { superTokensRequestMiddleware } from "@/utils/supertokens";
import { isAdminRole } from "@/features/core/utils/roles";
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { logger } from "@/lib/logger"; import { logger } from "@/lib/logger";
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({
const i18n = localizedFor(context.metadata); const i18n = localizedFor(context.metadata);
try { try {
const userId = context.userAuthId; const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
if (!userId) return new Response("Unauthenticated", { status: 401 }); if (!userId) return new Response("Unauthenticated", { status: 401 });
@@ -1,6 +1,7 @@
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { msg } from "@lingui/core/macro"; import { msg } from "@lingui/core/macro";
import { superTokensRequestMiddleware } from "@/utils/supertokens"; import { superTokensRequestMiddleware } from "@/utils/supertokens";
import { isAdminRole } from "@/features/core/utils/roles";
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { logger } from "@/lib/logger"; import { logger } from "@/lib/logger";
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({
const i18n = localizedFor(context.metadata); const i18n = localizedFor(context.metadata);
try { try {
const userId = context.userAuthId; const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
if (!userId) return new Response("Unauthenticated", { status: 401 }); if (!userId) return new Response("Unauthenticated", { status: 401 });
if (!isAdmin) return new Response("Unauthorized", { status: 403 }); if (!isAdmin) return new Response("Unauthorized", { status: 403 });
+2 -2
View File
@@ -1,4 +1,4 @@
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens"; import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
import { createServerFn } from "@tanstack/react-start"; import { createServerFn } from "@tanstack/react-start";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { z } from "zod"; import { z } from "zod";
@@ -17,7 +17,7 @@ const activitySearchParamsSchema = z.object({
export const searchActivities = createServerFn() export const searchActivities = createServerFn()
.validator(activitySearchParamsSchema) .validator(activitySearchParamsSchema)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<ActivityListResult>(async () => { toServerResult<ActivityListResult>(async () => {
const result = await pbAdmin.searchActivities(data); const result = await pbAdmin.searchActivities(data);
@@ -1,7 +1,6 @@
import { List, Text } from "@mantine/core"; import { List, Text } from "@mantine/core";
import { Plural, Trans, useLingui } from "@lingui/react/macro"; import { Plural, Trans, useLingui } from "@lingui/react/macro";
import { import {
DatabaseIcon,
TreeStructureIcon, TreeStructureIcon,
TrophyIcon, TrophyIcon,
MedalIcon, MedalIcon,
@@ -10,12 +9,14 @@ import {
} from "@phosphor-icons/react"; } from "@phosphor-icons/react";
import ListLink from "@/components/list-link"; import ListLink from "@/components/list-link";
import ListButton from "@/components/list-button"; import ListButton from "@/components/list-button";
import { useIsDeveloper } from "@/features/core/hooks/use-is-developer";
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server"; import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
import { useServerQuery } from "@/lib/tanstack-query/hooks"; import { useServerQuery } from "@/lib/tanstack-query/hooks";
import toast from "@/lib/sonner"; import toast from "@/lib/sonner";
export const ToolsList = () => { export const ToolsList = () => {
const { t } = useLingui(); const { t } = useLingui();
const isDeveloper = useIsDeveloper();
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({ const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
queryKey: ["admin", "badge-migration-status"], queryKey: ["admin", "badge-migration-status"],
@@ -51,11 +52,13 @@ export const ToolsList = () => {
Icon={TrophyIcon} Icon={TrophyIcon}
to="/admin/tournaments" to="/admin/tournaments"
/> />
<ListLink {isDeveloper && (
label={t`Telemetry`} <ListLink
Icon={PulseIcon} label={t`Telemetry`}
to="/admin/telemetry/usage" Icon={PulseIcon}
/> to="/admin/telemetry/usage"
/>
)}
<ListLink <ListLink
label={t`Award Badges`} label={t`Award Badges`}
Icon={CrownIcon} Icon={CrownIcon}
@@ -91,13 +94,6 @@ export const ToolsList = () => {
<Trans>Badge recalculation failed. Check the server logs.</Trans> <Trans>Badge recalculation failed. Check the server logs.</Trans>
</Text> </Text>
)} )}
<ListButton
label={t`Open Pocketbase`}
Icon={DatabaseIcon}
onClick={() =>
window.location.replace(process.env.POCKETBASE_URL! + "/_/")
}
/>
<ListLink <ListLink
label={t`Bracket Preview`} label={t`Bracket Preview`}
Icon={TreeStructureIcon} Icon={TreeStructureIcon}
@@ -0,0 +1,7 @@
import { useAuth } from "@/contexts/auth-context";
import { isDeveloperRole } from "../utils/roles";
export const useIsDeveloper = (): boolean => {
const { roles } = useAuth();
return isDeveloperRole(roles);
};
+5 -1
View File
@@ -1,4 +1,8 @@
export const ADMIN_ROLE = "Admin"; export const ADMIN_ROLE = "Admin";
export const DEVELOPER_ROLE = "Developer";
export const isDeveloperRole = (roles?: string[] | null): boolean =>
!!roles?.includes(DEVELOPER_ROLE);
export const isAdminRole = (roles?: string[] | null): boolean => export const isAdminRole = (roles?: string[] | null): boolean =>
!!roles?.includes(ADMIN_ROLE); !!roles?.includes(ADMIN_ROLE) || isDeveloperRole(roles);
@@ -3,7 +3,7 @@ import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core";
import { Trans, useLingui } from "@lingui/react/macro"; import { Trans, useLingui } from "@lingui/react/macro";
import { useWebPush } from "@/hooks/use-web-push"; import { useWebPush } from "@/hooks/use-web-push";
import { useAuth } from "@/contexts/auth-context"; import { useAuth } from "@/contexts/auth-context";
import { useIsAdmin } from "@/features/core/hooks/use-is-admin"; import { useIsDeveloper } from "@/features/core/hooks/use-is-developer";
import { updateUserTelemetryAlerts } from "@/features/settings/server"; import { updateUserTelemetryAlerts } from "@/features/settings/server";
import toast from "@/lib/sonner"; import toast from "@/lib/sonner";
@@ -40,7 +40,7 @@ const TelemetryAlertsSwitch = () => {
export function NotificationsSection() { export function NotificationsSection() {
const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } = const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } =
useWebPush(); useWebPush();
const isAdmin = useIsAdmin(); const isDeveloper = useIsDeveloper();
const { t } = useLingui(); const { t } = useLingui();
if (!supported || !configured) { if (!supported || !configured) {
@@ -99,7 +99,7 @@ export function NotificationsSection() {
disabled={busy} disabled={busy}
label={t`Enable notifications on this device`} label={t`Enable notifications on this device`}
/> />
{subscribed && isAdmin && <TelemetryAlertsSwitch />} {subscribed && isDeveloper && <TelemetryAlertsSwitch />}
{subscribed && ( {subscribed && (
<Group> <Group>
<Button <Button
+2 -2
View File
@@ -1,6 +1,6 @@
import { createServerFn } from "@tanstack/react-start"; import { createServerFn } from "@tanstack/react-start";
import { import {
superTokensAdminFunctionMiddleware, superTokensDeveloperFunctionMiddleware,
superTokensFunctionMiddleware, superTokensFunctionMiddleware,
} from "@/utils/supertokens"; } from "@/utils/supertokens";
@@ -145,7 +145,7 @@ export const updateUserAccentColor = createServerFn({ method: "POST" })
export const updateUserTelemetryAlerts = createServerFn({ method: "POST" }) export const updateUserTelemetryAlerts = createServerFn({ method: "POST" })
.validator((data: boolean) => data === true) .validator((data: boolean) => data === true)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ context, data }) => { .handler(async ({ context, data }) => {
const { userAuthId, metadata } = context; const { userAuthId, metadata } = context;
if (!userAuthId) return; if (!userAuthId) return;
+4 -3
View File
@@ -1,4 +1,5 @@
import { superTokensFunctionMiddleware, superTokensAdminFunctionMiddleware } from "@/utils/supertokens"; import { superTokensFunctionMiddleware, superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
import { isAdminRole } from "@/features/core/utils/roles";
import { createServerFn } from "@tanstack/react-start"; import { createServerFn } from "@tanstack/react-start";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { z } from "zod"; import { z } from "zod";
@@ -35,8 +36,8 @@ export const createTeam = createServerFn()
.handler(async ({ data, context }) => .handler(async ({ data, context }) =>
toServerResult(async () => { toServerResult(async () => {
const userId = context.userAuthId; const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
//if (!isAdmin && !data.players.includes(userId)) { //if (!isAdmin && !data.players.includes(userId)) {
// throw new Error("You can only create teams that include yourself as a player"); // throw new Error("You can only create teams that include yourself as a player");
//} //}
@@ -57,7 +58,7 @@ export const updateTeam = createServerFn()
.handler(async ({ data: { id, updates }, context }) => .handler(async ({ data: { id, updates }, context }) =>
toServerResult(async () => { toServerResult(async () => {
const userId = context.userAuthId; const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
const team = await pbAdmin.getTeam(id); const team = await pbAdmin.getTeam(id);
if (!team) { if (!team) {
+10 -10
View File
@@ -1,6 +1,6 @@
import { createServerFn } from "@tanstack/react-start"; import { createServerFn } from "@tanstack/react-start";
import { z } from "zod"; import { z } from "zod";
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens"; import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result"; import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
import { import {
@@ -37,7 +37,7 @@ const clientEventSearchSchema = z.object({
export const searchClientEvents = createServerFn() export const searchClientEvents = createServerFn()
.validator(clientEventSearchSchema) .validator(clientEventSearchSchema)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<TelemetryListResult<ClientEvent>>(async () => { toServerResult<TelemetryListResult<ClientEvent>>(async () => {
const result = await pbAdmin.searchClientEvents(data); const result = await pbAdmin.searchClientEvents(data);
@@ -58,7 +58,7 @@ const clientErrorSearchSchema = z.object({
export const searchClientErrors = createServerFn() export const searchClientErrors = createServerFn()
.validator(clientErrorSearchSchema) .validator(clientErrorSearchSchema)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<TelemetryListResult<ClientError>>(async () => { toServerResult<TelemetryListResult<ClientError>>(async () => {
const result = await pbAdmin.searchClientErrors(data); const result = await pbAdmin.searchClientErrors(data);
@@ -77,7 +77,7 @@ const errorGroupsSchema = z.object({
// not on the next rollup tick. // not on the next rollup tick.
export const getClientErrorGroups = createServerFn() export const getClientErrorGroups = createServerFn()
.validator(errorGroupsSchema) .validator(errorGroupsSchema)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<ClientErrorGroup[]>(async () => { toServerResult<ClientErrorGroup[]>(async () => {
const { from, to, includeResolved = false, limit = 50 } = data; const { from, to, includeResolved = false, limit = 50 } = data;
@@ -141,7 +141,7 @@ export const getClientErrorGroups = createServerFn()
export const resolveClientErrorGroup = createServerFn({ method: "POST" }) export const resolveClientErrorGroup = createServerFn({ method: "POST" })
.validator(z.object({ groupHash: z.string(), resolved: z.boolean().optional() })) .validator(z.object({ groupHash: z.string(), resolved: z.boolean().optional() }))
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<{ updated: number }>(async () => { toServerResult<{ updated: number }>(async () => {
const updated = await pbAdmin.resolveErrorsByGroup(data.groupHash, data.resolved ?? true); const updated = await pbAdmin.resolveErrorsByGroup(data.groupHash, data.resolved ?? true);
@@ -158,7 +158,7 @@ const rollupRangeSchema = z.object({
export const getRollupRange = createServerFn() export const getRollupRange = createServerFn()
.validator(rollupRangeSchema) .validator(rollupRangeSchema)
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<RollupRow[]>(async () => pbAdmin.getRollupRange(data)) toServerResult<RollupRow[]>(async () => pbAdmin.getRollupRange(data))
); );
@@ -167,7 +167,7 @@ export const listTelemetryAlerts = createServerFn()
.validator( .validator(
z.object({ page: z.number().optional(), perPage: z.number().max(200).optional() }) z.object({ page: z.number().optional(), perPage: z.number().max(200).optional() })
) )
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<TelemetryListResult<TelemetryAlert>>(async () => toServerResult<TelemetryListResult<TelemetryAlert>>(async () =>
pbAdmin.listTelemetryAlerts(data.page, data.perPage) pbAdmin.listTelemetryAlerts(data.page, data.perPage)
@@ -197,7 +197,7 @@ export const listSessions = createServerFn()
perPage: z.number().max(100).optional(), perPage: z.number().max(100).optional(),
}) })
) )
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<TelemetryListResult<SessionSummary>>(async () => { toServerResult<TelemetryListResult<SessionSummary>>(async () => {
const { days = 7, page = 1, perPage = 30 } = data; const { days = 7, page = 1, perPage = 30 } = data;
@@ -285,7 +285,7 @@ const MAX_TIMELINE_ENTRIES = 500;
export const getSessionTimeline = createServerFn() export const getSessionTimeline = createServerFn()
.validator(z.object({ sessionId: z.string().min(8).max(64) })) .validator(z.object({ sessionId: z.string().min(8).max(64) }))
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async ({ data }) => .handler(async ({ data }) =>
toServerResult<SessionTimeline>(async () => { toServerResult<SessionTimeline>(async () => {
const events = await pbAdmin.searchClientEvents({ const events = await pbAdmin.searchClientEvents({
@@ -426,7 +426,7 @@ export const getSessionTimeline = createServerFn()
); );
export const getTelemetryRuntimeStatus = createServerFn() export const getTelemetryRuntimeStatus = createServerFn()
.middleware([superTokensAdminFunctionMiddleware]) .middleware([superTokensDeveloperFunctionMiddleware])
.handler(async () => .handler(async () =>
toServerResult<TelemetryRuntimeStatus>(async () => { toServerResult<TelemetryRuntimeStatus>(async () => {
ensureTelemetryScheduler(); ensureTelemetryScheduler();
+3 -2
View File
@@ -1,4 +1,5 @@
import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens"; import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens";
import { isAdminRole } from "@/features/core/utils/roles";
import { createServerFn } from "@tanstack/react-start"; import { createServerFn } from "@tanstack/react-start";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { tournamentInputSchema, GroupStanding } from "@/features/tournaments/types"; import { tournamentInputSchema, GroupStanding } from "@/features/tournaments/types";
@@ -46,7 +47,7 @@ export const getTournament = createServerFn()
.validator(z.string()) .validator(z.string())
.middleware([superTokensFunctionMiddleware]) .middleware([superTokensFunctionMiddleware])
.handler(async ({ data: tournamentId, context }) => { .handler(async ({ data: tournamentId, context }) => {
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
return toServerResult(() => pbAdmin.getTournament(tournamentId, isAdmin)); return toServerResult(() => pbAdmin.getTournament(tournamentId, isAdmin));
}); });
@@ -64,7 +65,7 @@ export const enrollTeam = createServerFn()
.handler(async ({ data: { tournamentId, teamId }, context }) => .handler(async ({ data: { tournamentId, teamId }, context }) =>
toServerResult(async () => { toServerResult(async () => {
const userId = context.userAuthId; const userId = context.userAuthId;
const isAdmin = context.roles.includes("Admin"); const isAdmin = isAdminRole(context.roles);
const team = await pbAdmin.getTeam(teamId); const team = await pbAdmin.getTeam(teamId);
if (!team) { throw new Error('Team not found'); } if (!team) { throw new Error('Team not found'); }
+11 -11
View File
@@ -2,7 +2,7 @@ import { msg } from "@lingui/core/macro";
import { pbAdmin } from "@/lib/pocketbase/client"; import { pbAdmin } from "@/lib/pocketbase/client";
import { sendPushToPlayer } from "@/lib/push"; import { sendPushToPlayer } from "@/lib/push";
import { Logger } from "@/lib/logger"; import { Logger } from "@/lib/logger";
import { ADMIN_ROLE } from "@/features/core/utils/roles"; import { DEVELOPER_ROLE } from "@/features/core/utils/roles";
const logger = new Logger("Telemetry > Alerts"); const logger = new Logger("Telemetry > Alerts");
@@ -22,23 +22,23 @@ const fnFailureThreshold = (): number => {
const pbTimestamp = (date: Date): string => date.toISOString().replace("T", " "); const pbTimestamp = (date: Date): string => date.toISOString().replace("T", " ");
const ADMIN_CACHE_TTL_MS = 10 * 60 * 1000; const DEVELOPER_CACHE_TTL_MS = 10 * 60 * 1000;
let adminPlayersCache: { playerIds: string[]; expiresAt: number } | null = null; let developerPlayersCache: { playerIds: string[]; expiresAt: number } | null = null;
const getAdminPlayerIds = async (): Promise<string[]> => { const getDeveloperPlayerIds = async (): Promise<string[]> => {
const now = Date.now(); const now = Date.now();
if (adminPlayersCache && adminPlayersCache.expiresAt > now) { if (developerPlayersCache && developerPlayersCache.expiresAt > now) {
return adminPlayersCache.playerIds; return developerPlayersCache.playerIds;
} }
const UserRoles = (await import("supertokens-node/recipe/userroles")).default; const UserRoles = (await import("supertokens-node/recipe/userroles")).default;
const UserMetadata = (await import("supertokens-node/recipe/usermetadata")).default; const UserMetadata = (await import("supertokens-node/recipe/usermetadata")).default;
const response = await UserRoles.getUsersThatHaveRole("public", ADMIN_ROLE); const response = await UserRoles.getUsersThatHaveRole("public", DEVELOPER_ROLE);
const users = response.status === "OK" ? response.users : []; const users = response.status === "OK" ? response.users : [];
const playerIds: string[] = []; const playerIds: string[] = [];
for (const userId of users) { for (const userId of users) {
try { try {
// Opt-in: only admins who enabled telemetry alerts in settings. // Opt-in: only developers who enabled telemetry alerts in settings.
const { metadata } = await UserMetadata.getUserMetadata(userId); const { metadata } = await UserMetadata.getUserMetadata(userId);
if (metadata?.telemetryAlerts !== true) continue; if (metadata?.telemetryAlerts !== true) continue;
const player = await pbAdmin.getPlayerByAuthId(userId); const player = await pbAdmin.getPlayerByAuthId(userId);
@@ -46,7 +46,7 @@ const getAdminPlayerIds = async (): Promise<string[]> => {
} catch {} } catch {}
} }
adminPlayersCache = { playerIds, expiresAt: now + ADMIN_CACHE_TTL_MS }; developerPlayersCache = { playerIds, expiresAt: now + DEVELOPER_CACHE_TTL_MS };
return playerIds; return playerIds;
}; };
@@ -71,8 +71,8 @@ const fireAlert = async (candidate: AlertCandidate) => {
value: candidate.value, value: candidate.value,
}); });
const admins = await getAdminPlayerIds(); const developers = await getDeveloperPlayerIds();
for (const playerId of admins) { for (const playerId of developers) {
try { try {
await sendPushToPlayer(playerId, { await sendPushToPlayer(playerId, {
title: candidate.title, title: candidate.title,
+31 -35
View File
@@ -572,7 +572,7 @@ msgstr "Ø Sieg-Marge"
msgid "Award Badge" msgid "Award Badge"
msgstr "Abzeichen verleihen" msgstr "Abzeichen verleihen"
#: src/features/admin/components/dashboard/tools-list.tsx:60 #: src/features/admin/components/dashboard/tools-list.tsx:63
msgid "Award Badges" msgid "Award Badges"
msgstr "Abzeichen verleihen" msgstr "Abzeichen verleihen"
@@ -606,15 +606,15 @@ msgstr "Zurück"
msgid "Badge awarded to {playerName}" msgid "Badge awarded to {playerName}"
msgstr "Abzeichen an {playerName} verliehen" msgstr "Abzeichen an {playerName} verliehen"
#: src/features/admin/components/dashboard/tools-list.tsx:91 #: src/features/admin/components/dashboard/tools-list.tsx:94
msgid "Badge recalculation failed. Check the server logs." msgid "Badge recalculation failed. Check the server logs."
msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs." msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs."
#: src/features/admin/components/dashboard/tools-list.tsx:39 #: src/features/admin/components/dashboard/tools-list.tsx:40
msgid "Badge recalculation is already running" msgid "Badge recalculation is already running"
msgstr "Abzeichen-Neuberechnung läuft bereits" msgstr "Abzeichen-Neuberechnung läuft bereits"
#: src/features/admin/components/dashboard/tools-list.tsx:38 #: src/features/admin/components/dashboard/tools-list.tsx:39
msgid "Badge recalculation started in the background" msgid "Badge recalculation started in the background"
msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet" msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet"
@@ -645,7 +645,7 @@ msgid "Bracket"
msgstr "Turnierbaum" msgstr "Turnierbaum"
#: src/app/routes/_authed/admin/preview.tsx:13 #: src/app/routes/_authed/admin/preview.tsx:13
#: src/features/admin/components/dashboard/tools-list.tsx:102 #: src/features/admin/components/dashboard/tools-list.tsx:98
msgid "Bracket Preview" msgid "Bracket Preview"
msgstr "Turnierbaum-Vorschau" msgstr "Turnierbaum-Vorschau"
@@ -1118,7 +1118,7 @@ msgstr "Fehlerspitzen und fehlschlagende Funktionen, als Push an dieses Konto"
msgid "Error: {errorMessage}" msgid "Error: {errorMessage}"
msgstr "Fehler: {errorMessage}" msgstr "Fehler: {errorMessage}"
#: src/app/routes/_authed/admin/telemetry.tsx:57 #: src/app/routes/_authed/admin/telemetry.tsx:63
#: src/features/telemetry/components/usage-screen.tsx:260 #: src/features/telemetry/components/usage-screen.tsx:260
msgid "Errors" msgid "Errors"
msgstr "Fehler" msgstr "Fehler"
@@ -1186,7 +1186,7 @@ msgstr "Aktives Gerät konnte nicht festgelegt werden"
msgid "Failed to set volume" msgid "Failed to set volume"
msgstr "Lautstärke konnte nicht festgelegt werden" msgstr "Lautstärke konnte nicht festgelegt werden"
#: src/features/admin/components/dashboard/tools-list.tsx:42 #: src/features/admin/components/dashboard/tools-list.tsx:43
msgid "Failed to start badge recalculation" msgid "Failed to start badge recalculation"
msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden" msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden"
@@ -1198,8 +1198,8 @@ msgstr "Team konnte nicht aktualisiert werden"
msgid "Failed to update tournament" msgid "Failed to update tournament"
msgstr "Turnier konnte nicht aktualisiert werden" msgstr "Turnier konnte nicht aktualisiert werden"
#: src/app/routes/api/teams/upload-logo.ts:141 #: src/app/routes/api/teams/upload-logo.ts:142
#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/app/routes/api/tournaments/upload-logo.ts:140
#: src/features/teams/components/team-form/index.tsx:148 #: src/features/teams/components/team-form/index.tsx:148
#: src/features/tournaments/components/tournament-form.tsx:88 #: src/features/tournaments/components/tournament-form.tsx:88
msgid "Failed to upload logo" msgid "Failed to upload logo"
@@ -1209,8 +1209,8 @@ msgstr "Logo konnte nicht hochgeladen werden"
msgid "Failure" msgid "Failure"
msgstr "Fehler" msgstr "Fehler"
#: src/app/routes/api/teams/upload-logo.ts:80 #: src/app/routes/api/teams/upload-logo.ts:81
#: src/app/routes/api/tournaments/upload-logo.ts:81 #: src/app/routes/api/tournaments/upload-logo.ts:82
msgid "File too large. Maximum size is 10MB." msgid "File too large. Maximum size is 10MB."
msgstr "Datei zu groß. Maximale Größe ist 10 MB." msgstr "Datei zu groß. Maximale Größe ist 10 MB."
@@ -1264,7 +1264,7 @@ msgstr "Funktion"
msgid "Function Name" msgid "Function Name"
msgstr "Funktionsname" msgstr "Funktionsname"
#: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/app/routes/_authed/admin/telemetry.tsx:57
msgid "Functions" msgid "Functions"
msgstr "Funktionen" msgstr "Funktionen"
@@ -1401,8 +1401,8 @@ msgstr "Falscher Code"
msgid "Invalid action" msgid "Invalid action"
msgstr "Ungültige Aktion" msgstr "Ungültige Aktion"
#: src/app/routes/api/teams/upload-logo.ts:66 #: src/app/routes/api/teams/upload-logo.ts:67
#: src/app/routes/api/tournaments/upload-logo.ts:67 #: src/app/routes/api/tournaments/upload-logo.ts:68
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt." msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt."
@@ -1410,8 +1410,8 @@ msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt."
msgid "Invalid hex color format" msgid "Invalid hex color format"
msgstr "Ungültiges Hex-Farbformat" msgstr "Ungültiges Hex-Farbformat"
#: src/app/routes/api/teams/upload-logo.ts:34 #: src/app/routes/api/teams/upload-logo.ts:35
#: src/app/routes/api/tournaments/upload-logo.ts:35 #: src/app/routes/api/tournaments/upload-logo.ts:36
msgid "Invalid input" msgid "Invalid input"
msgstr "Ungültige Eingabe" msgstr "Ungültige Eingabe"
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten" msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten"
#. placeholder {0}: migrationStatus.result.totalBadgesEarned #. placeholder {0}: migrationStatus.result.totalBadgesEarned
#: src/features/admin/components/dashboard/tools-list.tsx:79 #: src/features/admin/components/dashboard/tools-list.tsx:82
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}" msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}"
@@ -1504,8 +1504,8 @@ msgstr "Ort ist erforderlich"
msgid "Logo" msgid "Logo"
msgstr "Logo" msgstr "Logo"
#: src/app/routes/api/teams/upload-logo.ts:47 #: src/app/routes/api/teams/upload-logo.ts:48
#: src/app/routes/api/tournaments/upload-logo.ts:48 #: src/app/routes/api/tournaments/upload-logo.ts:49
msgid "Logo file is required" msgid "Logo file is required"
msgstr "Logo-Datei ist erforderlich" msgstr "Logo-Datei ist erforderlich"
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
msgstr "Team-Songs/Logos verwalten" msgstr "Team-Songs/Logos verwalten"
#: src/app/routes/_authed/admin/tournaments/index.tsx:17 #: src/app/routes/_authed/admin/tournaments/index.tsx:17
#: src/features/admin/components/dashboard/tools-list.tsx:50 #: src/features/admin/components/dashboard/tools-list.tsx:51
msgid "Manage Tournaments" msgid "Manage Tournaments"
msgstr "Turniere verwalten" msgstr "Turniere verwalten"
@@ -1646,7 +1646,7 @@ msgstr "MAU"
msgid "Me" msgid "Me"
msgstr "Ich" msgstr "Ich"
#: src/features/admin/components/dashboard/tools-list.tsx:65 #: src/features/admin/components/dashboard/tools-list.tsx:68
msgid "Migrate Badge Progress" msgid "Migrate Badge Progress"
msgstr "Abzeichen-Fortschritt migrieren" msgstr "Abzeichen-Fortschritt migrieren"
@@ -1908,10 +1908,6 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon
msgid "One time code" msgid "One time code"
msgstr "Einmalcode" msgstr "Einmalcode"
#: src/features/admin/components/dashboard/tools-list.tsx:95
msgid "Open Pocketbase"
msgstr "Pocketbase öffnen"
#: src/features/bracket/components/match-report.tsx:273 #: src/features/bracket/components/match-report.tsx:273
msgid "Opponent" msgid "Opponent"
msgstr "Gegner" msgstr "Gegner"
@@ -2047,7 +2043,7 @@ msgstr "Spieler erfolgreich entfernt"
msgid "Player Stats" msgid "Player Stats"
msgstr "Spielerstatistiken" msgstr "Spielerstatistiken"
#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/app/routes/_authed/admin/telemetry.tsx:54
#: src/features/teams/components/team-profile/index.tsx:29 #: src/features/teams/components/team-profile/index.tsx:29
msgid "Players" msgid "Players"
msgstr "Spieler" msgstr "Spieler"
@@ -2122,7 +2118,7 @@ msgstr "Reaktionen"
#. placeholder {0}: migrationStatus.playersProcessed #. placeholder {0}: migrationStatus.playersProcessed
#. placeholder {1}: migrationStatus.totalPlayers #. placeholder {1}: migrationStatus.totalPlayers
#: src/features/admin/components/dashboard/tools-list.tsx:72 #: src/features/admin/components/dashboard/tools-list.tsx:75
msgid "Recalculating badges in the background ({0}/{1})" msgid "Recalculating badges in the background ({0}/{1})"
msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})" msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})"
@@ -2366,7 +2362,7 @@ msgstr "Serverfunktionen"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn-Name" msgstr "serverFn-Name"
#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/app/routes/_authed/admin/telemetry.tsx:60
#: src/features/telemetry/components/usage-screen.tsx:212 #: src/features/telemetry/components/usage-screen.tsx:212
msgid "Sessions" msgid "Sessions"
msgstr "Sitzungen" msgstr "Sitzungen"
@@ -2581,7 +2577,7 @@ msgstr "Team-Details"
msgid "Team enrolled successfully!" msgid "Team enrolled successfully!"
msgstr "Team erfolgreich angemeldet!" msgstr "Team erfolgreich angemeldet!"
#: src/app/routes/api/teams/upload-logo.ts:93 #: src/app/routes/api/teams/upload-logo.ts:94
#: src/features/teams/components/team-profile/index.tsx:21 #: src/features/teams/components/team-profile/index.tsx:21
msgid "Team not found" msgid "Team not found"
msgstr "Team nicht gefunden" msgstr "Team nicht gefunden"
@@ -2615,8 +2611,8 @@ msgstr "Teams"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "Teams erfolgreich erstellt!" msgstr "Teams erfolgreich erstellt!"
#: src/app/routes/_authed/admin/telemetry.tsx:20 #: src/app/routes/_authed/admin/telemetry.tsx:26
#: src/features/admin/components/dashboard/tools-list.tsx:55 #: src/features/admin/components/dashboard/tools-list.tsx:57
msgid "Telemetry" msgid "Telemetry"
msgstr "Telemetrie" msgstr "Telemetrie"
@@ -2730,7 +2726,7 @@ msgstr "Turnier erstellt, aber Logo-Upload fehlgeschlagen: {0}"
msgid "Tournament created successfully!" msgid "Tournament created successfully!"
msgstr "Turnier erfolgreich erstellt!" msgstr "Turnier erfolgreich erstellt!"
#: src/app/routes/api/tournaments/upload-logo.ts:94 #: src/app/routes/api/tournaments/upload-logo.ts:95
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Turnier nicht gefunden" msgstr "Turnier nicht gefunden"
@@ -2784,8 +2780,8 @@ msgstr "Team abmelden"
msgid "Unknown error" msgid "Unknown error"
msgstr "Unbekannter Fehler" msgstr "Unbekannter Fehler"
#: src/app/routes/api/teams/upload-logo.ts:142 #: src/app/routes/api/teams/upload-logo.ts:143
#: src/app/routes/api/tournaments/upload-logo.ts:140 #: src/app/routes/api/tournaments/upload-logo.ts:141
msgid "Unknown error occurred" msgid "Unknown error occurred"
msgstr "Unbekannter Fehler aufgetreten" msgstr "Unbekannter Fehler aufgetreten"
@@ -2824,7 +2820,7 @@ msgstr "Team aktualisieren"
msgid "Update Tournament" msgid "Update Tournament"
msgstr "Turnier aktualisieren" msgstr "Turnier aktualisieren"
#: src/app/routes/_authed/admin/telemetry.tsx:45 #: src/app/routes/_authed/admin/telemetry.tsx:51
msgid "Usage" msgid "Usage"
msgstr "Nutzung" msgstr "Nutzung"
+31 -35
View File
@@ -572,7 +572,7 @@ msgstr "Avg Win Margin"
msgid "Award Badge" msgid "Award Badge"
msgstr "Award Badge" msgstr "Award Badge"
#: src/features/admin/components/dashboard/tools-list.tsx:60 #: src/features/admin/components/dashboard/tools-list.tsx:63
msgid "Award Badges" msgid "Award Badges"
msgstr "Award Badges" msgstr "Award Badges"
@@ -606,15 +606,15 @@ msgstr "Back"
msgid "Badge awarded to {playerName}" msgid "Badge awarded to {playerName}"
msgstr "Badge awarded to {playerName}" msgstr "Badge awarded to {playerName}"
#: src/features/admin/components/dashboard/tools-list.tsx:91 #: src/features/admin/components/dashboard/tools-list.tsx:94
msgid "Badge recalculation failed. Check the server logs." msgid "Badge recalculation failed. Check the server logs."
msgstr "Badge recalculation failed. Check the server logs." msgstr "Badge recalculation failed. Check the server logs."
#: src/features/admin/components/dashboard/tools-list.tsx:39 #: src/features/admin/components/dashboard/tools-list.tsx:40
msgid "Badge recalculation is already running" msgid "Badge recalculation is already running"
msgstr "Badge recalculation is already running" msgstr "Badge recalculation is already running"
#: src/features/admin/components/dashboard/tools-list.tsx:38 #: src/features/admin/components/dashboard/tools-list.tsx:39
msgid "Badge recalculation started in the background" msgid "Badge recalculation started in the background"
msgstr "Badge recalculation started in the background" msgstr "Badge recalculation started in the background"
@@ -645,7 +645,7 @@ msgid "Bracket"
msgstr "Bracket" msgstr "Bracket"
#: src/app/routes/_authed/admin/preview.tsx:13 #: src/app/routes/_authed/admin/preview.tsx:13
#: src/features/admin/components/dashboard/tools-list.tsx:102 #: src/features/admin/components/dashboard/tools-list.tsx:98
msgid "Bracket Preview" msgid "Bracket Preview"
msgstr "Bracket Preview" msgstr "Bracket Preview"
@@ -1118,7 +1118,7 @@ msgstr "Error spikes and failing functions, pushed to this account"
msgid "Error: {errorMessage}" msgid "Error: {errorMessage}"
msgstr "Error: {errorMessage}" msgstr "Error: {errorMessage}"
#: src/app/routes/_authed/admin/telemetry.tsx:57 #: src/app/routes/_authed/admin/telemetry.tsx:63
#: src/features/telemetry/components/usage-screen.tsx:260 #: src/features/telemetry/components/usage-screen.tsx:260
msgid "Errors" msgid "Errors"
msgstr "Errors" msgstr "Errors"
@@ -1186,7 +1186,7 @@ msgstr "Failed to set active device"
msgid "Failed to set volume" msgid "Failed to set volume"
msgstr "Failed to set volume" msgstr "Failed to set volume"
#: src/features/admin/components/dashboard/tools-list.tsx:42 #: src/features/admin/components/dashboard/tools-list.tsx:43
msgid "Failed to start badge recalculation" msgid "Failed to start badge recalculation"
msgstr "Failed to start badge recalculation" msgstr "Failed to start badge recalculation"
@@ -1198,8 +1198,8 @@ msgstr "Failed to update team"
msgid "Failed to update tournament" msgid "Failed to update tournament"
msgstr "Failed to update tournament" msgstr "Failed to update tournament"
#: src/app/routes/api/teams/upload-logo.ts:141 #: src/app/routes/api/teams/upload-logo.ts:142
#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/app/routes/api/tournaments/upload-logo.ts:140
#: src/features/teams/components/team-form/index.tsx:148 #: src/features/teams/components/team-form/index.tsx:148
#: src/features/tournaments/components/tournament-form.tsx:88 #: src/features/tournaments/components/tournament-form.tsx:88
msgid "Failed to upload logo" msgid "Failed to upload logo"
@@ -1209,8 +1209,8 @@ msgstr "Failed to upload logo"
msgid "Failure" msgid "Failure"
msgstr "Failure" msgstr "Failure"
#: src/app/routes/api/teams/upload-logo.ts:80 #: src/app/routes/api/teams/upload-logo.ts:81
#: src/app/routes/api/tournaments/upload-logo.ts:81 #: src/app/routes/api/tournaments/upload-logo.ts:82
msgid "File too large. Maximum size is 10MB." msgid "File too large. Maximum size is 10MB."
msgstr "File too large. Maximum size is 10MB." msgstr "File too large. Maximum size is 10MB."
@@ -1264,7 +1264,7 @@ msgstr "Function"
msgid "Function Name" msgid "Function Name"
msgstr "Function Name" msgstr "Function Name"
#: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/app/routes/_authed/admin/telemetry.tsx:57
msgid "Functions" msgid "Functions"
msgstr "Functions" msgstr "Functions"
@@ -1401,8 +1401,8 @@ msgstr "Incorrect code"
msgid "Invalid action" msgid "Invalid action"
msgstr "Invalid action" msgstr "Invalid action"
#: src/app/routes/api/teams/upload-logo.ts:66 #: src/app/routes/api/teams/upload-logo.ts:67
#: src/app/routes/api/tournaments/upload-logo.ts:67 #: src/app/routes/api/tournaments/upload-logo.ts:68
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed." msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed."
@@ -1410,8 +1410,8 @@ msgstr "Invalid file type. Only JPEG, PNG and GIF are allowed."
msgid "Invalid hex color format" msgid "Invalid hex color format"
msgstr "Invalid hex color format" msgstr "Invalid hex color format"
#: src/app/routes/api/teams/upload-logo.ts:34 #: src/app/routes/api/teams/upload-logo.ts:35
#: src/app/routes/api/tournaments/upload-logo.ts:35 #: src/app/routes/api/tournaments/upload-logo.ts:36
msgid "Invalid input" msgid "Invalid input"
msgstr "Invalid input" msgstr "Invalid input"
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
msgstr "Last name must be 3-20 characters long and contain only letters" msgstr "Last name must be 3-20 characters long and contain only letters"
#. placeholder {0}: migrationStatus.result.totalBadgesEarned #. placeholder {0}: migrationStatus.result.totalBadgesEarned
#: src/features/admin/components/dashboard/tools-list.tsx:79 #: src/features/admin/components/dashboard/tools-list.tsx:82
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
@@ -1504,8 +1504,8 @@ msgstr "Location is required"
msgid "Logo" msgid "Logo"
msgstr "Logo" msgstr "Logo"
#: src/app/routes/api/teams/upload-logo.ts:47 #: src/app/routes/api/teams/upload-logo.ts:48
#: src/app/routes/api/tournaments/upload-logo.ts:48 #: src/app/routes/api/tournaments/upload-logo.ts:49
msgid "Logo file is required" msgid "Logo file is required"
msgstr "Logo file is required" msgstr "Logo file is required"
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
msgstr "Manage Team Songs/Logos" msgstr "Manage Team Songs/Logos"
#: src/app/routes/_authed/admin/tournaments/index.tsx:17 #: src/app/routes/_authed/admin/tournaments/index.tsx:17
#: src/features/admin/components/dashboard/tools-list.tsx:50 #: src/features/admin/components/dashboard/tools-list.tsx:51
msgid "Manage Tournaments" msgid "Manage Tournaments"
msgstr "Manage Tournaments" msgstr "Manage Tournaments"
@@ -1646,7 +1646,7 @@ msgstr "MAU"
msgid "Me" msgid "Me"
msgstr "Me" msgstr "Me"
#: src/features/admin/components/dashboard/tools-list.tsx:65 #: src/features/admin/components/dashboard/tools-list.tsx:68
msgid "Migrate Badge Progress" msgid "Migrate Badge Progress"
msgstr "Migrate Badge Progress" msgstr "Migrate Badge Progress"
@@ -1908,10 +1908,6 @@ msgstr "Once enrolled, you can view other free agents and their phone number in
msgid "One time code" msgid "One time code"
msgstr "One time code" msgstr "One time code"
#: src/features/admin/components/dashboard/tools-list.tsx:95
msgid "Open Pocketbase"
msgstr "Open Pocketbase"
#: src/features/bracket/components/match-report.tsx:273 #: src/features/bracket/components/match-report.tsx:273
msgid "Opponent" msgid "Opponent"
msgstr "Opponent" msgstr "Opponent"
@@ -2047,7 +2043,7 @@ msgstr "Player removed successfully"
msgid "Player Stats" msgid "Player Stats"
msgstr "Player Stats" msgstr "Player Stats"
#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/app/routes/_authed/admin/telemetry.tsx:54
#: src/features/teams/components/team-profile/index.tsx:29 #: src/features/teams/components/team-profile/index.tsx:29
msgid "Players" msgid "Players"
msgstr "Players" msgstr "Players"
@@ -2122,7 +2118,7 @@ msgstr "Reactions"
#. placeholder {0}: migrationStatus.playersProcessed #. placeholder {0}: migrationStatus.playersProcessed
#. placeholder {1}: migrationStatus.totalPlayers #. placeholder {1}: migrationStatus.totalPlayers
#: src/features/admin/components/dashboard/tools-list.tsx:72 #: src/features/admin/components/dashboard/tools-list.tsx:75
msgid "Recalculating badges in the background ({0}/{1})" msgid "Recalculating badges in the background ({0}/{1})"
msgstr "Recalculating badges in the background ({0}/{1})" msgstr "Recalculating badges in the background ({0}/{1})"
@@ -2366,7 +2362,7 @@ msgstr "Server functions"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn name" msgstr "serverFn name"
#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/app/routes/_authed/admin/telemetry.tsx:60
#: src/features/telemetry/components/usage-screen.tsx:212 #: src/features/telemetry/components/usage-screen.tsx:212
msgid "Sessions" msgid "Sessions"
msgstr "Sessions" msgstr "Sessions"
@@ -2581,7 +2577,7 @@ msgstr "Team Details"
msgid "Team enrolled successfully!" msgid "Team enrolled successfully!"
msgstr "Team enrolled successfully!" msgstr "Team enrolled successfully!"
#: src/app/routes/api/teams/upload-logo.ts:93 #: src/app/routes/api/teams/upload-logo.ts:94
#: src/features/teams/components/team-profile/index.tsx:21 #: src/features/teams/components/team-profile/index.tsx:21
msgid "Team not found" msgid "Team not found"
msgstr "Team not found" msgstr "Team not found"
@@ -2615,8 +2611,8 @@ msgstr "Teams"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "Teams created successfully!" msgstr "Teams created successfully!"
#: src/app/routes/_authed/admin/telemetry.tsx:20 #: src/app/routes/_authed/admin/telemetry.tsx:26
#: src/features/admin/components/dashboard/tools-list.tsx:55 #: src/features/admin/components/dashboard/tools-list.tsx:57
msgid "Telemetry" msgid "Telemetry"
msgstr "Telemetry" msgstr "Telemetry"
@@ -2730,7 +2726,7 @@ msgstr "Tournament created but logo upload failed: {0}"
msgid "Tournament created successfully!" msgid "Tournament created successfully!"
msgstr "Tournament created successfully!" msgstr "Tournament created successfully!"
#: src/app/routes/api/tournaments/upload-logo.ts:94 #: src/app/routes/api/tournaments/upload-logo.ts:95
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Tournament not found" msgstr "Tournament not found"
@@ -2784,8 +2780,8 @@ msgstr "Unenroll Team"
msgid "Unknown error" msgid "Unknown error"
msgstr "Unknown error" msgstr "Unknown error"
#: src/app/routes/api/teams/upload-logo.ts:142 #: src/app/routes/api/teams/upload-logo.ts:143
#: src/app/routes/api/tournaments/upload-logo.ts:140 #: src/app/routes/api/tournaments/upload-logo.ts:141
msgid "Unknown error occurred" msgid "Unknown error occurred"
msgstr "Unknown error occurred" msgstr "Unknown error occurred"
@@ -2824,7 +2820,7 @@ msgstr "Update Team"
msgid "Update Tournament" msgid "Update Tournament"
msgstr "Update Tournament" msgstr "Update Tournament"
#: src/app/routes/_authed/admin/telemetry.tsx:45 #: src/app/routes/_authed/admin/telemetry.tsx:51
msgid "Usage" msgid "Usage"
msgstr "Usage" msgstr "Usage"
+31 -35
View File
@@ -572,7 +572,7 @@ msgstr "Prom. Margen de Victoria"
msgid "Award Badge" msgid "Award Badge"
msgstr "Otorgar insignia" msgstr "Otorgar insignia"
#: src/features/admin/components/dashboard/tools-list.tsx:60 #: src/features/admin/components/dashboard/tools-list.tsx:63
msgid "Award Badges" msgid "Award Badges"
msgstr "Otorgar insignias" msgstr "Otorgar insignias"
@@ -606,15 +606,15 @@ msgstr "Atrás"
msgid "Badge awarded to {playerName}" msgid "Badge awarded to {playerName}"
msgstr "Insignia otorgada a {playerName}" msgstr "Insignia otorgada a {playerName}"
#: src/features/admin/components/dashboard/tools-list.tsx:91 #: src/features/admin/components/dashboard/tools-list.tsx:94
msgid "Badge recalculation failed. Check the server logs." msgid "Badge recalculation failed. Check the server logs."
msgstr "Falló el recálculo de insignias. Revisa los registros del servidor." msgstr "Falló el recálculo de insignias. Revisa los registros del servidor."
#: src/features/admin/components/dashboard/tools-list.tsx:39 #: src/features/admin/components/dashboard/tools-list.tsx:40
msgid "Badge recalculation is already running" msgid "Badge recalculation is already running"
msgstr "El recálculo de insignias ya está en curso" msgstr "El recálculo de insignias ya está en curso"
#: src/features/admin/components/dashboard/tools-list.tsx:38 #: src/features/admin/components/dashboard/tools-list.tsx:39
msgid "Badge recalculation started in the background" msgid "Badge recalculation started in the background"
msgstr "Recálculo de insignias iniciado en segundo plano" msgstr "Recálculo de insignias iniciado en segundo plano"
@@ -645,7 +645,7 @@ msgid "Bracket"
msgstr "Bracket" msgstr "Bracket"
#: src/app/routes/_authed/admin/preview.tsx:13 #: src/app/routes/_authed/admin/preview.tsx:13
#: src/features/admin/components/dashboard/tools-list.tsx:102 #: src/features/admin/components/dashboard/tools-list.tsx:98
msgid "Bracket Preview" msgid "Bracket Preview"
msgstr "Vista previa del bracket" msgstr "Vista previa del bracket"
@@ -1118,7 +1118,7 @@ msgstr "Picos de errores y funciones fallando, enviados a esta cuenta"
msgid "Error: {errorMessage}" msgid "Error: {errorMessage}"
msgstr "Error: {errorMessage}" msgstr "Error: {errorMessage}"
#: src/app/routes/_authed/admin/telemetry.tsx:57 #: src/app/routes/_authed/admin/telemetry.tsx:63
#: src/features/telemetry/components/usage-screen.tsx:260 #: src/features/telemetry/components/usage-screen.tsx:260
msgid "Errors" msgid "Errors"
msgstr "Errores" msgstr "Errores"
@@ -1186,7 +1186,7 @@ msgstr "No se pudo establecer el dispositivo activo"
msgid "Failed to set volume" msgid "Failed to set volume"
msgstr "No se pudo ajustar el volumen" msgstr "No se pudo ajustar el volumen"
#: src/features/admin/components/dashboard/tools-list.tsx:42 #: src/features/admin/components/dashboard/tools-list.tsx:43
msgid "Failed to start badge recalculation" msgid "Failed to start badge recalculation"
msgstr "No se pudo iniciar el recálculo de insignias" msgstr "No se pudo iniciar el recálculo de insignias"
@@ -1198,8 +1198,8 @@ msgstr "No se pudo actualizar el equipo"
msgid "Failed to update tournament" msgid "Failed to update tournament"
msgstr "No se pudo actualizar el torneo" msgstr "No se pudo actualizar el torneo"
#: src/app/routes/api/teams/upload-logo.ts:141 #: src/app/routes/api/teams/upload-logo.ts:142
#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/app/routes/api/tournaments/upload-logo.ts:140
#: src/features/teams/components/team-form/index.tsx:148 #: src/features/teams/components/team-form/index.tsx:148
#: src/features/tournaments/components/tournament-form.tsx:88 #: src/features/tournaments/components/tournament-form.tsx:88
msgid "Failed to upload logo" msgid "Failed to upload logo"
@@ -1209,8 +1209,8 @@ msgstr "No se pudo subir el logo"
msgid "Failure" msgid "Failure"
msgstr "Fallo" msgstr "Fallo"
#: src/app/routes/api/teams/upload-logo.ts:80 #: src/app/routes/api/teams/upload-logo.ts:81
#: src/app/routes/api/tournaments/upload-logo.ts:81 #: src/app/routes/api/tournaments/upload-logo.ts:82
msgid "File too large. Maximum size is 10MB." msgid "File too large. Maximum size is 10MB."
msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB." msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB."
@@ -1264,7 +1264,7 @@ msgstr "Función"
msgid "Function Name" msgid "Function Name"
msgstr "Nombre de la función" msgstr "Nombre de la función"
#: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/app/routes/_authed/admin/telemetry.tsx:57
msgid "Functions" msgid "Functions"
msgstr "Funciones" msgstr "Funciones"
@@ -1401,8 +1401,8 @@ msgstr "Código incorrecto"
msgid "Invalid action" msgid "Invalid action"
msgstr "Acción no válida" msgstr "Acción no válida"
#: src/app/routes/api/teams/upload-logo.ts:66 #: src/app/routes/api/teams/upload-logo.ts:67
#: src/app/routes/api/tournaments/upload-logo.ts:67 #: src/app/routes/api/tournaments/upload-logo.ts:68
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF." msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
@@ -1410,8 +1410,8 @@ msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
msgid "Invalid hex color format" msgid "Invalid hex color format"
msgstr "Formato de color hexadecimal no válido" msgstr "Formato de color hexadecimal no válido"
#: src/app/routes/api/teams/upload-logo.ts:34 #: src/app/routes/api/teams/upload-logo.ts:35
#: src/app/routes/api/tournaments/upload-logo.ts:35 #: src/app/routes/api/tournaments/upload-logo.ts:36
msgid "Invalid input" msgid "Invalid input"
msgstr "Entrada no válida" msgstr "Entrada no válida"
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras" msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras"
#. placeholder {0}: migrationStatus.result.totalBadgesEarned #. placeholder {0}: migrationStatus.result.totalBadgesEarned
#: src/features/admin/components/dashboard/tools-list.tsx:79 #: src/features/admin/components/dashboard/tools-list.tsx:82
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}" msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}"
@@ -1504,8 +1504,8 @@ msgstr "La ubicación es obligatoria"
msgid "Logo" msgid "Logo"
msgstr "Logo" msgstr "Logo"
#: src/app/routes/api/teams/upload-logo.ts:47 #: src/app/routes/api/teams/upload-logo.ts:48
#: src/app/routes/api/tournaments/upload-logo.ts:48 #: src/app/routes/api/tournaments/upload-logo.ts:49
msgid "Logo file is required" msgid "Logo file is required"
msgstr "El archivo del logo es obligatorio" msgstr "El archivo del logo es obligatorio"
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
msgstr "Administrar canciones/logos del equipo" msgstr "Administrar canciones/logos del equipo"
#: src/app/routes/_authed/admin/tournaments/index.tsx:17 #: src/app/routes/_authed/admin/tournaments/index.tsx:17
#: src/features/admin/components/dashboard/tools-list.tsx:50 #: src/features/admin/components/dashboard/tools-list.tsx:51
msgid "Manage Tournaments" msgid "Manage Tournaments"
msgstr "Administrar torneos" msgstr "Administrar torneos"
@@ -1646,7 +1646,7 @@ msgstr "MAU"
msgid "Me" msgid "Me"
msgstr "Yo" msgstr "Yo"
#: src/features/admin/components/dashboard/tools-list.tsx:65 #: src/features/admin/components/dashboard/tools-list.tsx:68
msgid "Migrate Badge Progress" msgid "Migrate Badge Progress"
msgstr "Migrar progreso de insignias" msgstr "Migrar progreso de insignias"
@@ -1908,10 +1908,6 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel
msgid "One time code" msgid "One time code"
msgstr "Código de un solo uso" msgstr "Código de un solo uso"
#: src/features/admin/components/dashboard/tools-list.tsx:95
msgid "Open Pocketbase"
msgstr "Abrir Pocketbase"
#: src/features/bracket/components/match-report.tsx:273 #: src/features/bracket/components/match-report.tsx:273
msgid "Opponent" msgid "Opponent"
msgstr "Rival" msgstr "Rival"
@@ -2047,7 +2043,7 @@ msgstr "Jugador eliminado con éxito"
msgid "Player Stats" msgid "Player Stats"
msgstr "Estadísticas de jugador" msgstr "Estadísticas de jugador"
#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/app/routes/_authed/admin/telemetry.tsx:54
#: src/features/teams/components/team-profile/index.tsx:29 #: src/features/teams/components/team-profile/index.tsx:29
msgid "Players" msgid "Players"
msgstr "Jugadores" msgstr "Jugadores"
@@ -2122,7 +2118,7 @@ msgstr "Reacciones"
#. placeholder {0}: migrationStatus.playersProcessed #. placeholder {0}: migrationStatus.playersProcessed
#. placeholder {1}: migrationStatus.totalPlayers #. placeholder {1}: migrationStatus.totalPlayers
#: src/features/admin/components/dashboard/tools-list.tsx:72 #: src/features/admin/components/dashboard/tools-list.tsx:75
msgid "Recalculating badges in the background ({0}/{1})" msgid "Recalculating badges in the background ({0}/{1})"
msgstr "Recalculando insignias en segundo plano ({0}/{1})" msgstr "Recalculando insignias en segundo plano ({0}/{1})"
@@ -2366,7 +2362,7 @@ msgstr "Funciones del servidor"
msgid "serverFn name" msgid "serverFn name"
msgstr "nombre de serverFn" msgstr "nombre de serverFn"
#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/app/routes/_authed/admin/telemetry.tsx:60
#: src/features/telemetry/components/usage-screen.tsx:212 #: src/features/telemetry/components/usage-screen.tsx:212
msgid "Sessions" msgid "Sessions"
msgstr "Sesiones" msgstr "Sesiones"
@@ -2581,7 +2577,7 @@ msgstr "Detalles del equipo"
msgid "Team enrolled successfully!" msgid "Team enrolled successfully!"
msgstr "¡Equipo inscrito exitosamente!" msgstr "¡Equipo inscrito exitosamente!"
#: src/app/routes/api/teams/upload-logo.ts:93 #: src/app/routes/api/teams/upload-logo.ts:94
#: src/features/teams/components/team-profile/index.tsx:21 #: src/features/teams/components/team-profile/index.tsx:21
msgid "Team not found" msgid "Team not found"
msgstr "Equipo no encontrado" msgstr "Equipo no encontrado"
@@ -2615,8 +2611,8 @@ msgstr "Equipos"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "¡Equipos creados exitosamente!" msgstr "¡Equipos creados exitosamente!"
#: src/app/routes/_authed/admin/telemetry.tsx:20 #: src/app/routes/_authed/admin/telemetry.tsx:26
#: src/features/admin/components/dashboard/tools-list.tsx:55 #: src/features/admin/components/dashboard/tools-list.tsx:57
msgid "Telemetry" msgid "Telemetry"
msgstr "Telemetría" msgstr "Telemetría"
@@ -2730,7 +2726,7 @@ msgstr "Torneo creado, pero falló la carga del logo: {0}"
msgid "Tournament created successfully!" msgid "Tournament created successfully!"
msgstr "¡Torneo creado exitosamente!" msgstr "¡Torneo creado exitosamente!"
#: src/app/routes/api/tournaments/upload-logo.ts:94 #: src/app/routes/api/tournaments/upload-logo.ts:95
msgid "Tournament not found" msgid "Tournament not found"
msgstr "Torneo no encontrado" msgstr "Torneo no encontrado"
@@ -2784,8 +2780,8 @@ msgstr "Cancelar inscripción del equipo"
msgid "Unknown error" msgid "Unknown error"
msgstr "Error desconocido" msgstr "Error desconocido"
#: src/app/routes/api/teams/upload-logo.ts:142 #: src/app/routes/api/teams/upload-logo.ts:143
#: src/app/routes/api/tournaments/upload-logo.ts:140 #: src/app/routes/api/tournaments/upload-logo.ts:141
msgid "Unknown error occurred" msgid "Unknown error occurred"
msgstr "Ocurrió un error desconocido" msgstr "Ocurrió un error desconocido"
@@ -2824,7 +2820,7 @@ msgstr "Actualizar equipo"
msgid "Update Tournament" msgid "Update Tournament"
msgstr "Actualizar torneo" msgstr "Actualizar torneo"
#: src/app/routes/_authed/admin/telemetry.tsx:45 #: src/app/routes/_authed/admin/telemetry.tsx:51
msgid "Usage" msgid "Usage"
msgstr "Uso" msgstr "Uso"
+31 -35
View File
@@ -572,7 +572,7 @@ msgstr "平均勝差"
msgid "Award Badge" msgid "Award Badge"
msgstr "バッジを付与" msgstr "バッジを付与"
#: src/features/admin/components/dashboard/tools-list.tsx:60 #: src/features/admin/components/dashboard/tools-list.tsx:63
msgid "Award Badges" msgid "Award Badges"
msgstr "バッジを付与" msgstr "バッジを付与"
@@ -606,15 +606,15 @@ msgstr "戻る"
msgid "Badge awarded to {playerName}" msgid "Badge awarded to {playerName}"
msgstr "{playerName}にバッジを付与しました" msgstr "{playerName}にバッジを付与しました"
#: src/features/admin/components/dashboard/tools-list.tsx:91 #: src/features/admin/components/dashboard/tools-list.tsx:94
msgid "Badge recalculation failed. Check the server logs." msgid "Badge recalculation failed. Check the server logs."
msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。" msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。"
#: src/features/admin/components/dashboard/tools-list.tsx:39 #: src/features/admin/components/dashboard/tools-list.tsx:40
msgid "Badge recalculation is already running" msgid "Badge recalculation is already running"
msgstr "バッジの再計算はすでに実行中です" msgstr "バッジの再計算はすでに実行中です"
#: src/features/admin/components/dashboard/tools-list.tsx:38 #: src/features/admin/components/dashboard/tools-list.tsx:39
msgid "Badge recalculation started in the background" msgid "Badge recalculation started in the background"
msgstr "バックグラウンドでバッジの再計算を開始しました" msgstr "バックグラウンドでバッジの再計算を開始しました"
@@ -645,7 +645,7 @@ msgid "Bracket"
msgstr "ブラケット" msgstr "ブラケット"
#: src/app/routes/_authed/admin/preview.tsx:13 #: src/app/routes/_authed/admin/preview.tsx:13
#: src/features/admin/components/dashboard/tools-list.tsx:102 #: src/features/admin/components/dashboard/tools-list.tsx:98
msgid "Bracket Preview" msgid "Bracket Preview"
msgstr "ブラケットプレビュー" msgstr "ブラケットプレビュー"
@@ -1118,7 +1118,7 @@ msgstr "エラーの急増や失敗している関数をこのアカウントに
msgid "Error: {errorMessage}" msgid "Error: {errorMessage}"
msgstr "エラー: {errorMessage}" msgstr "エラー: {errorMessage}"
#: src/app/routes/_authed/admin/telemetry.tsx:57 #: src/app/routes/_authed/admin/telemetry.tsx:63
#: src/features/telemetry/components/usage-screen.tsx:260 #: src/features/telemetry/components/usage-screen.tsx:260
msgid "Errors" msgid "Errors"
msgstr "エラー" msgstr "エラー"
@@ -1186,7 +1186,7 @@ msgstr "アクティブなデバイスの設定に失敗しました"
msgid "Failed to set volume" msgid "Failed to set volume"
msgstr "音量の設定に失敗しました" msgstr "音量の設定に失敗しました"
#: src/features/admin/components/dashboard/tools-list.tsx:42 #: src/features/admin/components/dashboard/tools-list.tsx:43
msgid "Failed to start badge recalculation" msgid "Failed to start badge recalculation"
msgstr "バッジの再計算を開始できませんでした" msgstr "バッジの再計算を開始できませんでした"
@@ -1198,8 +1198,8 @@ msgstr "チームの更新に失敗しました"
msgid "Failed to update tournament" msgid "Failed to update tournament"
msgstr "トーナメントの更新に失敗しました" msgstr "トーナメントの更新に失敗しました"
#: src/app/routes/api/teams/upload-logo.ts:141 #: src/app/routes/api/teams/upload-logo.ts:142
#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/app/routes/api/tournaments/upload-logo.ts:140
#: src/features/teams/components/team-form/index.tsx:148 #: src/features/teams/components/team-form/index.tsx:148
#: src/features/tournaments/components/tournament-form.tsx:88 #: src/features/tournaments/components/tournament-form.tsx:88
msgid "Failed to upload logo" msgid "Failed to upload logo"
@@ -1209,8 +1209,8 @@ msgstr "ロゴのアップロードに失敗しました"
msgid "Failure" msgid "Failure"
msgstr "失敗" msgstr "失敗"
#: src/app/routes/api/teams/upload-logo.ts:80 #: src/app/routes/api/teams/upload-logo.ts:81
#: src/app/routes/api/tournaments/upload-logo.ts:81 #: src/app/routes/api/tournaments/upload-logo.ts:82
msgid "File too large. Maximum size is 10MB." msgid "File too large. Maximum size is 10MB."
msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。" msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。"
@@ -1264,7 +1264,7 @@ msgstr "関数"
msgid "Function Name" msgid "Function Name"
msgstr "関数名" msgstr "関数名"
#: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/app/routes/_authed/admin/telemetry.tsx:57
msgid "Functions" msgid "Functions"
msgstr "関数" msgstr "関数"
@@ -1401,8 +1401,8 @@ msgstr "コードが正しくありません"
msgid "Invalid action" msgid "Invalid action"
msgstr "無効な操作です" msgstr "無効な操作です"
#: src/app/routes/api/teams/upload-logo.ts:66 #: src/app/routes/api/teams/upload-logo.ts:67
#: src/app/routes/api/tournaments/upload-logo.ts:67 #: src/app/routes/api/tournaments/upload-logo.ts:68
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。" msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。"
@@ -1410,8 +1410,8 @@ msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用でき
msgid "Invalid hex color format" msgid "Invalid hex color format"
msgstr "無効なHEXカラー形式です" msgstr "無効なHEXカラー形式です"
#: src/app/routes/api/teams/upload-logo.ts:34 #: src/app/routes/api/teams/upload-logo.ts:35
#: src/app/routes/api/tournaments/upload-logo.ts:35 #: src/app/routes/api/tournaments/upload-logo.ts:36
msgid "Invalid input" msgid "Invalid input"
msgstr "無効な入力です" msgstr "無効な入力です"
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
msgstr "姓は3〜20文字の英字のみで入力してください" msgstr "姓は3〜20文字の英字のみで入力してください"
#. placeholder {0}: migrationStatus.result.totalBadgesEarned #. placeholder {0}: migrationStatus.result.totalBadgesEarned
#: src/features/admin/components/dashboard/tools-list.tsx:79 #: src/features/admin/components/dashboard/tools-list.tsx:82
msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}" msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}"
@@ -1504,8 +1504,8 @@ msgstr "場所は必須です"
msgid "Logo" msgid "Logo"
msgstr "ロゴ" msgstr "ロゴ"
#: src/app/routes/api/teams/upload-logo.ts:47 #: src/app/routes/api/teams/upload-logo.ts:48
#: src/app/routes/api/tournaments/upload-logo.ts:48 #: src/app/routes/api/tournaments/upload-logo.ts:49
msgid "Logo file is required" msgid "Logo file is required"
msgstr "ロゴファイルは必須です" msgstr "ロゴファイルは必須です"
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
msgstr "チームの曲・ロゴを管理" msgstr "チームの曲・ロゴを管理"
#: src/app/routes/_authed/admin/tournaments/index.tsx:17 #: src/app/routes/_authed/admin/tournaments/index.tsx:17
#: src/features/admin/components/dashboard/tools-list.tsx:50 #: src/features/admin/components/dashboard/tools-list.tsx:51
msgid "Manage Tournaments" msgid "Manage Tournaments"
msgstr "トーナメント管理" msgstr "トーナメント管理"
@@ -1646,7 +1646,7 @@ msgstr "MAU"
msgid "Me" msgid "Me"
msgstr "自分" msgstr "自分"
#: src/features/admin/components/dashboard/tools-list.tsx:65 #: src/features/admin/components/dashboard/tools-list.tsx:68
msgid "Migrate Badge Progress" msgid "Migrate Badge Progress"
msgstr "バッジ進捗を移行" msgstr "バッジ進捗を移行"
@@ -1908,10 +1908,6 @@ msgstr "登録が完了すると、他のフリーエージェントとその電
msgid "One time code" msgid "One time code"
msgstr "ワンタイムコード" msgstr "ワンタイムコード"
#: src/features/admin/components/dashboard/tools-list.tsx:95
msgid "Open Pocketbase"
msgstr "Pocketbaseを開く"
#: src/features/bracket/components/match-report.tsx:273 #: src/features/bracket/components/match-report.tsx:273
msgid "Opponent" msgid "Opponent"
msgstr "対戦相手" msgstr "対戦相手"
@@ -2047,7 +2043,7 @@ msgstr "プレイヤーを削除しました"
msgid "Player Stats" msgid "Player Stats"
msgstr "プレイヤースタッツ" msgstr "プレイヤースタッツ"
#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/app/routes/_authed/admin/telemetry.tsx:54
#: src/features/teams/components/team-profile/index.tsx:29 #: src/features/teams/components/team-profile/index.tsx:29
msgid "Players" msgid "Players"
msgstr "プレイヤー" msgstr "プレイヤー"
@@ -2122,7 +2118,7 @@ msgstr "リアクション"
#. placeholder {0}: migrationStatus.playersProcessed #. placeholder {0}: migrationStatus.playersProcessed
#. placeholder {1}: migrationStatus.totalPlayers #. placeholder {1}: migrationStatus.totalPlayers
#: src/features/admin/components/dashboard/tools-list.tsx:72 #: src/features/admin/components/dashboard/tools-list.tsx:75
msgid "Recalculating badges in the background ({0}/{1})" msgid "Recalculating badges in the background ({0}/{1})"
msgstr "バックグラウンドでバッジを再計算中({0}/{1})" msgstr "バックグラウンドでバッジを再計算中({0}/{1})"
@@ -2366,7 +2362,7 @@ msgstr "サーバー関数"
msgid "serverFn name" msgid "serverFn name"
msgstr "serverFn名" msgstr "serverFn名"
#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/app/routes/_authed/admin/telemetry.tsx:60
#: src/features/telemetry/components/usage-screen.tsx:212 #: src/features/telemetry/components/usage-screen.tsx:212
msgid "Sessions" msgid "Sessions"
msgstr "セッション数" msgstr "セッション数"
@@ -2581,7 +2577,7 @@ msgstr "チーム詳細"
msgid "Team enrolled successfully!" msgid "Team enrolled successfully!"
msgstr "チームを登録しました!" msgstr "チームを登録しました!"
#: src/app/routes/api/teams/upload-logo.ts:93 #: src/app/routes/api/teams/upload-logo.ts:94
#: src/features/teams/components/team-profile/index.tsx:21 #: src/features/teams/components/team-profile/index.tsx:21
msgid "Team not found" msgid "Team not found"
msgstr "チームが見つかりません" msgstr "チームが見つかりません"
@@ -2615,8 +2611,8 @@ msgstr "チーム"
msgid "Teams created successfully!" msgid "Teams created successfully!"
msgstr "チームを作成しました!" msgstr "チームを作成しました!"
#: src/app/routes/_authed/admin/telemetry.tsx:20 #: src/app/routes/_authed/admin/telemetry.tsx:26
#: src/features/admin/components/dashboard/tools-list.tsx:55 #: src/features/admin/components/dashboard/tools-list.tsx:57
msgid "Telemetry" msgid "Telemetry"
msgstr "テレメトリー" msgstr "テレメトリー"
@@ -2730,7 +2726,7 @@ msgstr "トーナメントを作成しましたが、ロゴのアップロード
msgid "Tournament created successfully!" msgid "Tournament created successfully!"
msgstr "トーナメントを作成しました!" msgstr "トーナメントを作成しました!"
#: src/app/routes/api/tournaments/upload-logo.ts:94 #: src/app/routes/api/tournaments/upload-logo.ts:95
msgid "Tournament not found" msgid "Tournament not found"
msgstr "トーナメントが見つかりません" msgstr "トーナメントが見つかりません"
@@ -2784,8 +2780,8 @@ msgstr "チームを登録解除"
msgid "Unknown error" msgid "Unknown error"
msgstr "不明なエラー" msgstr "不明なエラー"
#: src/app/routes/api/teams/upload-logo.ts:142 #: src/app/routes/api/teams/upload-logo.ts:143
#: src/app/routes/api/tournaments/upload-logo.ts:140 #: src/app/routes/api/tournaments/upload-logo.ts:141
msgid "Unknown error occurred" msgid "Unknown error occurred"
msgstr "不明なエラーが発生しました" msgstr "不明なエラーが発生しました"
@@ -2824,7 +2820,7 @@ msgstr "チームを更新"
msgid "Update Tournament" msgid "Update Tournament"
msgstr "トーナメントを更新" msgstr "トーナメントを更新"
#: src/app/routes/_authed/admin/telemetry.tsx:45 #: src/app/routes/_authed/admin/telemetry.tsx:51
msgid "Usage" msgid "Usage"
msgstr "利用状況" msgstr "利用状況"
+46 -32
View File
@@ -5,7 +5,7 @@ import {
} from "@tanstack/react-start"; } from "@tanstack/react-start";
import { getRequest } from "@tanstack/react-start/server"; import { getRequest } from "@tanstack/react-start/server";
import { Logger } from "@/lib/logger"; import { Logger } from "@/lib/logger";
import { isAdminRole } from "@/features/core/utils/roles"; import { isAdminRole, isDeveloperRole } from "@/features/core/utils/roles";
import z from "zod"; import z from "zod";
const logger = new Logger("Middleware"); const logger = new Logger("Middleware");
@@ -70,41 +70,55 @@ export const superTokensFunctionMiddleware = createMiddleware({
} }
}); });
export const superTokensAdminFunctionMiddleware = createMiddleware({ const createRoleFunctionMiddleware = (
type: "function", hasRole: (roles?: string[] | null) => boolean,
}).server(async ({ next, serverFnMeta }) => { label: string
const request = getRequest(); ) =>
createMiddleware({
type: "function",
}).server(async ({ next, serverFnMeta }) => {
const request = getRequest();
try { try {
const context = await getSessionContext(request, { isServerFunction: true }); const context = await getSessionContext(request, { isServerFunction: true });
if (isAdminRole(context.roles)) { if (hasRole(context.roles)) {
return next({ context }); return next({ context });
}
logger.error(`Unauthorized user in ${label} function.`, {
userAuthId: context.userAuthId,
roles: context.roles,
});
const { recordDeniedServerFn } = await import("./activities");
recordDeniedServerFn(request, context.metadata, serverFnMeta);
throw new Error("Unauthorized");
} catch (error: any) {
if (error.message === "SESSION_REFRESH_REQUIRED") {
throw new Response(
JSON.stringify({
error: "SESSION_REFRESH_REQUIRED",
message: "Session needs to be refreshed"
}),
{
status: 401,
headers: { "Content-Type": "application/json" }
}
);
}
throw error;
} }
});
logger.error("Unauthorized user in admin function.", { export const superTokensAdminFunctionMiddleware = createRoleFunctionMiddleware(
userAuthId: context.userAuthId, isAdminRole,
roles: context.roles, "admin"
}); );
const { recordDeniedServerFn } = await import("./activities");
recordDeniedServerFn(request, context.metadata, serverFnMeta); export const superTokensDeveloperFunctionMiddleware = createRoleFunctionMiddleware(
throw new Error("Unauthorized"); isDeveloperRole,
} catch (error: any) { "developer"
if (error.message === "SESSION_REFRESH_REQUIRED") { );
throw new Response(
JSON.stringify({
error: "SESSION_REFRESH_REQUIRED",
message: "Session needs to be refreshed"
}),
{
status: 401,
headers: { "Content-Type": "application/json" }
}
);
}
throw error;
}
});
export const setUserMetadata = createServerFn({ method: "POST" }) export const setUserMetadata = createServerFn({ method: "POST" })
.validator( .validator(