Merge pull request 'Development' (#25) from development into main
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Successful in 9s
CI/CD Pipeline / i18n Catalog Check (push) Successful in 40s
CI/CD Pipeline / Build and Push App Docker Image (push) Successful in 2m7s
CI/CD Pipeline / Deploy to Kubernetes (push) Successful in 8m29s
CI/CD Pipeline / Build and Push PocketBase Docker Image (push) Successful in 9s
CI/CD Pipeline / i18n Catalog Check (push) Successful in 40s
CI/CD Pipeline / Build and Push App Docker Image (push) Successful in 2m7s
CI/CD Pipeline / Deploy to Kubernetes (push) Successful in 8m29s
Reviewed-on: #25
This commit was merged in pull request #25.
This commit is contained in:
+17
-8
@@ -1,5 +1,5 @@
|
||||
// Grant or revoke the "Admin" role for a user by phone number (US +1 assumed).
|
||||
// Usage: bun run scripts/make-admin.ts <phone> [--revoke]
|
||||
// Grant or revoke a role for a user by phone number (US +1 assumed).
|
||||
// Usage: bun run scripts/make-admin.ts <phone> [--role Admin|Developer] [--revoke]
|
||||
import "dotenv/config";
|
||||
import SuperTokens from "supertokens-node";
|
||||
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 revoke = process.argv.includes("--revoke");
|
||||
const roleFlagIndex = process.argv.indexOf("--role");
|
||||
const role = roleFlagIndex === -1 ? "Admin" : process.argv[roleFlagIndex + 1];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -60,15 +69,15 @@ if (!user) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await UserRoles.createNewRoleOrAddPermissions("Admin", []);
|
||||
await UserRoles.createNewRoleOrAddPermissions(role, []);
|
||||
|
||||
if (revoke) {
|
||||
const res = await UserRoles.removeUserRole("public", user.id, "Admin");
|
||||
console.log(`Removed Admin from ${matched} (user ${user.id}):`, res.status);
|
||||
const res = await UserRoles.removeUserRole("public", user.id, role);
|
||||
console.log(`Removed ${role} from ${matched} (user ${user.id}):`, res.status);
|
||||
} else {
|
||||
const res = await UserRoles.addRoleToUser("public", user.id, "Admin");
|
||||
const res = await UserRoles.addRoleToUser("public", user.id, role);
|
||||
console.log(
|
||||
`Granted Admin to ${matched} (user ${user.id}):`,
|
||||
`Granted ${role} to ${matched} (user ${user.id}):`,
|
||||
res.status === "OK"
|
||||
? res.didUserAlreadyHaveRole
|
||||
? "already had it"
|
||||
|
||||
@@ -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 { Trans } from "@lingui/react/macro";
|
||||
import { msg } from "@lingui/core/macro";
|
||||
import { isDeveloperRole } from "@/features/core/utils/roles";
|
||||
|
||||
const TAB_ROUTES = {
|
||||
usage: "/admin/telemetry/usage",
|
||||
@@ -15,6 +16,11 @@ type TabValue = keyof typeof TAB_ROUTES;
|
||||
|
||||
export const Route = createFileRoute("/_authed/admin/telemetry")({
|
||||
component: TelemetryLayout,
|
||||
beforeLoad: ({ context }) => {
|
||||
if (!isDeveloperRole(context.auth?.roles)) {
|
||||
throw redirect({ to: "/admin" });
|
||||
}
|
||||
},
|
||||
loader: () => ({
|
||||
header: {
|
||||
title: msg`Telemetry`,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { msg } from "@lingui/core/macro";
|
||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||
import { isAdminRole } from "@/features/core/utils/roles";
|
||||
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { logger } from "@/lib/logger";
|
||||
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({
|
||||
const i18n = localizedFor(context.metadata);
|
||||
try {
|
||||
const userId = context.userAuthId;
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
|
||||
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { msg } from "@lingui/core/macro";
|
||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||
import { isAdminRole } from "@/features/core/utils/roles";
|
||||
import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { logger } from "@/lib/logger";
|
||||
@@ -19,7 +20,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({
|
||||
const i18n = localizedFor(context.metadata);
|
||||
try {
|
||||
const userId = context.userAuthId;
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
|
||||
if (!userId) return new Response("Unauthenticated", { status: 401 });
|
||||
if (!isAdmin) return new Response("Unauthorized", { status: 403 });
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Box } from "@mantine/core";
|
||||
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
||||
|
||||
const HeadToHeadIcon = ({ size = 14 }: { size?: number }) => (
|
||||
<Box
|
||||
component="span"
|
||||
style={{
|
||||
position: "relative",
|
||||
display: "inline-block",
|
||||
width: size * (27.5 / 14),
|
||||
height: size * (16 / 14),
|
||||
}}
|
||||
>
|
||||
<FootballHelmetIcon
|
||||
size={size}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
transform: "rotate(25deg)",
|
||||
}}
|
||||
/>
|
||||
<FootballHelmetIcon
|
||||
size={size}
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: "scaleX(-1) rotate(25deg)",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default HeadToHeadIcon;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { z } from "zod";
|
||||
@@ -17,7 +17,7 @@ const activitySearchParamsSchema = z.object({
|
||||
|
||||
export const searchActivities = createServerFn()
|
||||
.validator(activitySearchParamsSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<ActivityListResult>(async () => {
|
||||
const result = await pbAdmin.searchActivities(data);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { List, Text } from "@mantine/core";
|
||||
import { Plural, Trans, useLingui } from "@lingui/react/macro";
|
||||
import {
|
||||
DatabaseIcon,
|
||||
TreeStructureIcon,
|
||||
TrophyIcon,
|
||||
MedalIcon,
|
||||
@@ -10,12 +9,14 @@ import {
|
||||
} from "@phosphor-icons/react";
|
||||
import ListLink from "@/components/list-link";
|
||||
import ListButton from "@/components/list-button";
|
||||
import { useIsDeveloper } from "@/features/core/hooks/use-is-developer";
|
||||
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
|
||||
import { useServerQuery } from "@/lib/tanstack-query/hooks";
|
||||
import toast from "@/lib/sonner";
|
||||
|
||||
export const ToolsList = () => {
|
||||
const { t } = useLingui();
|
||||
const isDeveloper = useIsDeveloper();
|
||||
|
||||
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
|
||||
queryKey: ["admin", "badge-migration-status"],
|
||||
@@ -51,11 +52,13 @@ export const ToolsList = () => {
|
||||
Icon={TrophyIcon}
|
||||
to="/admin/tournaments"
|
||||
/>
|
||||
{isDeveloper && (
|
||||
<ListLink
|
||||
label={t`Telemetry`}
|
||||
Icon={PulseIcon}
|
||||
to="/admin/telemetry/usage"
|
||||
/>
|
||||
)}
|
||||
<ListLink
|
||||
label={t`Award Badges`}
|
||||
Icon={CrownIcon}
|
||||
@@ -91,13 +94,6 @@ export const ToolsList = () => {
|
||||
<Trans>Badge recalculation failed. Check the server logs.</Trans>
|
||||
</Text>
|
||||
)}
|
||||
<ListButton
|
||||
label={t`Open Pocketbase`}
|
||||
Icon={DatabaseIcon}
|
||||
onClick={() =>
|
||||
window.location.replace(process.env.POCKETBASE_URL! + "/_/")
|
||||
}
|
||||
/>
|
||||
<ListLink
|
||||
label={t`Bracket Preview`}
|
||||
Icon={TreeStructureIcon}
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
||||
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
||||
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { Match } from "@/features/matches/types";
|
||||
import TeamAvatar from "@/components/team-avatar";
|
||||
@@ -188,28 +188,7 @@ const MatchDock = ({ match, onClose }: MatchDockProps) => {
|
||||
w={40}
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
<Group
|
||||
style={{ position: "relative", width: 27.5, height: 16 }}
|
||||
>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
transform: "rotate(25deg)",
|
||||
}}
|
||||
/>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: "scaleX(-1) rotate(25deg)",
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<HeadToHeadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -1,4 +1,8 @@
|
||||
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 =>
|
||||
!!roles?.includes(ADMIN_ROLE);
|
||||
!!roles?.includes(ADMIN_ROLE) || isDeveloperRole(roles);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Text, Group, Stack, Paper, Indicator, Box, Tooltip, ActionIcon } from "@mantine/core";
|
||||
import { FootballHelmetIcon } from "@phosphor-icons/react";
|
||||
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { Match } from "../types";
|
||||
import TeamAvatar from "@/components/team-avatar";
|
||||
@@ -94,26 +94,7 @@ const MatchCard = ({ match, hideH2H = false }: MatchCardProps) => {
|
||||
aria-label={t`View head-to-head`}
|
||||
w={40}
|
||||
>
|
||||
<Group style={{ position: 'relative', width: 27.5, height: 16 }}>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
transform: 'rotate(25deg)',
|
||||
}}
|
||||
/>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: 'scaleX(-1) rotate(25deg)',
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
<HeadToHeadIcon />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import Sheet from "@/components/sheet/sheet";
|
||||
import { useAuth } from "@/contexts/auth-context";
|
||||
import { Flex, Title, ActionIcon, Stack, Button, Box } from "@mantine/core";
|
||||
import { PencilIcon, FootballHelmetIcon } from "@phosphor-icons/react";
|
||||
import { Flex, Title, ActionIcon, Stack, Button } from "@mantine/core";
|
||||
import { PencilIcon } from "@phosphor-icons/react";
|
||||
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||
import { useMemo } from "react";
|
||||
import { useLingui } from "@lingui/react/macro";
|
||||
import NameUpdateForm from "./name-form";
|
||||
@@ -64,26 +65,7 @@ const Header = ({ player }: HeaderProps) => {
|
||||
w={40}
|
||||
display={!owner ? 'block' : 'none'}
|
||||
>
|
||||
<Box style={{ position: 'relative', width: 27.5, height: 16 }}>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
transform: 'rotate(25deg)'
|
||||
}}
|
||||
/>
|
||||
<FootballHelmetIcon
|
||||
size={14}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
transform: 'scaleX(-1) rotate(25deg)'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<HeadToHeadIcon />
|
||||
</ActionIcon>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import { CaretLeftIcon, CaretRightIcon, InfoIcon } from "@phosphor-icons/react";
|
||||
import { CaretLeftIcon, CaretRightIcon } from "@phosphor-icons/react";
|
||||
import HeadToHeadIcon from "@/components/head-to-head-icon";
|
||||
import WizardOrbIcon from "@/components/wizard-orb-icon";
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
@@ -228,7 +229,7 @@ export const PredictionEditor: React.FC<PredictionEditorProps> = ({
|
||||
disabled={!activeResolved?.home && !activeResolved?.away}
|
||||
aria-label={t`Matchup details`}
|
||||
>
|
||||
<InfoIcon size={16} />
|
||||
<HeadToHeadIcon />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
<Group gap="sm" align="center" wrap="nowrap">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core";
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { useWebPush } from "@/hooks/use-web-push";
|
||||
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 toast from "@/lib/sonner";
|
||||
|
||||
@@ -40,7 +40,7 @@ const TelemetryAlertsSwitch = () => {
|
||||
export function NotificationsSection() {
|
||||
const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } =
|
||||
useWebPush();
|
||||
const isAdmin = useIsAdmin();
|
||||
const isDeveloper = useIsDeveloper();
|
||||
const { t } = useLingui();
|
||||
|
||||
if (!supported || !configured) {
|
||||
@@ -99,7 +99,7 @@ export function NotificationsSection() {
|
||||
disabled={busy}
|
||||
label={t`Enable notifications on this device`}
|
||||
/>
|
||||
{subscribed && isAdmin && <TelemetryAlertsSwitch />}
|
||||
{subscribed && isDeveloper && <TelemetryAlertsSwitch />}
|
||||
{subscribed && (
|
||||
<Group>
|
||||
<Button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import {
|
||||
superTokensAdminFunctionMiddleware,
|
||||
superTokensDeveloperFunctionMiddleware,
|
||||
superTokensFunctionMiddleware,
|
||||
} from "@/utils/supertokens";
|
||||
|
||||
@@ -145,7 +145,7 @@ export const updateUserAccentColor = createServerFn({ method: "POST" })
|
||||
|
||||
export const updateUserTelemetryAlerts = createServerFn({ method: "POST" })
|
||||
.validator((data: boolean) => data === true)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ context, data }) => {
|
||||
const { userAuthId, metadata } = context;
|
||||
if (!userAuthId) return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { superTokensFunctionMiddleware, superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { isAdminRole } from "@/features/core/utils/roles";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { z } from "zod";
|
||||
@@ -35,7 +36,7 @@ export const createTeam = createServerFn()
|
||||
.handler(async ({ data, context }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
|
||||
//if (!isAdmin && !data.players.includes(userId)) {
|
||||
// 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 }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
|
||||
const team = await pbAdmin.getTeam(id);
|
||||
if (!team) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { z } from "zod";
|
||||
import { superTokensAdminFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { superTokensDeveloperFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { toServerResult } from "@/lib/tanstack-query/utils/to-server-result";
|
||||
import {
|
||||
@@ -37,7 +37,7 @@ const clientEventSearchSchema = z.object({
|
||||
|
||||
export const searchClientEvents = createServerFn()
|
||||
.validator(clientEventSearchSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<TelemetryListResult<ClientEvent>>(async () => {
|
||||
const result = await pbAdmin.searchClientEvents(data);
|
||||
@@ -58,7 +58,7 @@ const clientErrorSearchSchema = z.object({
|
||||
|
||||
export const searchClientErrors = createServerFn()
|
||||
.validator(clientErrorSearchSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<TelemetryListResult<ClientError>>(async () => {
|
||||
const result = await pbAdmin.searchClientErrors(data);
|
||||
@@ -77,7 +77,7 @@ const errorGroupsSchema = z.object({
|
||||
// not on the next rollup tick.
|
||||
export const getClientErrorGroups = createServerFn()
|
||||
.validator(errorGroupsSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<ClientErrorGroup[]>(async () => {
|
||||
const { from, to, includeResolved = false, limit = 50 } = data;
|
||||
@@ -141,7 +141,7 @@ export const getClientErrorGroups = createServerFn()
|
||||
|
||||
export const resolveClientErrorGroup = createServerFn({ method: "POST" })
|
||||
.validator(z.object({ groupHash: z.string(), resolved: z.boolean().optional() }))
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<{ updated: number }>(async () => {
|
||||
const updated = await pbAdmin.resolveErrorsByGroup(data.groupHash, data.resolved ?? true);
|
||||
@@ -158,7 +158,7 @@ const rollupRangeSchema = z.object({
|
||||
|
||||
export const getRollupRange = createServerFn()
|
||||
.validator(rollupRangeSchema)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<RollupRow[]>(async () => pbAdmin.getRollupRange(data))
|
||||
);
|
||||
@@ -167,7 +167,7 @@ export const listTelemetryAlerts = createServerFn()
|
||||
.validator(
|
||||
z.object({ page: z.number().optional(), perPage: z.number().max(200).optional() })
|
||||
)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<TelemetryListResult<TelemetryAlert>>(async () =>
|
||||
pbAdmin.listTelemetryAlerts(data.page, data.perPage)
|
||||
@@ -197,7 +197,7 @@ export const listSessions = createServerFn()
|
||||
perPage: z.number().max(100).optional(),
|
||||
})
|
||||
)
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<TelemetryListResult<SessionSummary>>(async () => {
|
||||
const { days = 7, page = 1, perPage = 30 } = data;
|
||||
@@ -285,7 +285,7 @@ const MAX_TIMELINE_ENTRIES = 500;
|
||||
|
||||
export const getSessionTimeline = createServerFn()
|
||||
.validator(z.object({ sessionId: z.string().min(8).max(64) }))
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async ({ data }) =>
|
||||
toServerResult<SessionTimeline>(async () => {
|
||||
const events = await pbAdmin.searchClientEvents({
|
||||
@@ -426,7 +426,7 @@ export const getSessionTimeline = createServerFn()
|
||||
);
|
||||
|
||||
export const getTelemetryRuntimeStatus = createServerFn()
|
||||
.middleware([superTokensAdminFunctionMiddleware])
|
||||
.middleware([superTokensDeveloperFunctionMiddleware])
|
||||
.handler(async () =>
|
||||
toServerResult<TelemetryRuntimeStatus>(async () => {
|
||||
ensureTelemetryScheduler();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { superTokensAdminFunctionMiddleware, superTokensFunctionMiddleware } from "@/utils/supertokens";
|
||||
import { isAdminRole } from "@/features/core/utils/roles";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { tournamentInputSchema, GroupStanding } from "@/features/tournaments/types";
|
||||
@@ -46,7 +47,7 @@ export const getTournament = createServerFn()
|
||||
.validator(z.string())
|
||||
.middleware([superTokensFunctionMiddleware])
|
||||
.handler(async ({ data: tournamentId, context }) => {
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
return toServerResult(() => pbAdmin.getTournament(tournamentId, isAdmin));
|
||||
});
|
||||
|
||||
@@ -64,7 +65,7 @@ export const enrollTeam = createServerFn()
|
||||
.handler(async ({ data: { tournamentId, teamId }, context }) =>
|
||||
toServerResult(async () => {
|
||||
const userId = context.userAuthId;
|
||||
const isAdmin = context.roles.includes("Admin");
|
||||
const isAdmin = isAdminRole(context.roles);
|
||||
|
||||
const team = await pbAdmin.getTeam(teamId);
|
||||
if (!team) { throw new Error('Team not found'); }
|
||||
|
||||
@@ -2,7 +2,7 @@ import { msg } from "@lingui/core/macro";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { sendPushToPlayer } from "@/lib/push";
|
||||
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");
|
||||
|
||||
@@ -22,23 +22,23 @@ const fnFailureThreshold = (): number => {
|
||||
|
||||
const pbTimestamp = (date: Date): string => date.toISOString().replace("T", " ");
|
||||
|
||||
const ADMIN_CACHE_TTL_MS = 10 * 60 * 1000;
|
||||
let adminPlayersCache: { playerIds: string[]; expiresAt: number } | null = null;
|
||||
const DEVELOPER_CACHE_TTL_MS = 10 * 60 * 1000;
|
||||
let developerPlayersCache: { playerIds: string[]; expiresAt: number } | null = null;
|
||||
|
||||
const getAdminPlayerIds = async (): Promise<string[]> => {
|
||||
const getDeveloperPlayerIds = async (): Promise<string[]> => {
|
||||
const now = Date.now();
|
||||
if (adminPlayersCache && adminPlayersCache.expiresAt > now) {
|
||||
return adminPlayersCache.playerIds;
|
||||
if (developerPlayersCache && developerPlayersCache.expiresAt > now) {
|
||||
return developerPlayersCache.playerIds;
|
||||
}
|
||||
|
||||
const UserRoles = (await import("supertokens-node/recipe/userroles")).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 playerIds: string[] = [];
|
||||
for (const userId of users) {
|
||||
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);
|
||||
if (metadata?.telemetryAlerts !== true) continue;
|
||||
const player = await pbAdmin.getPlayerByAuthId(userId);
|
||||
@@ -46,7 +46,7 @@ const getAdminPlayerIds = async (): Promise<string[]> => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
adminPlayersCache = { playerIds, expiresAt: now + ADMIN_CACHE_TTL_MS };
|
||||
developerPlayersCache = { playerIds, expiresAt: now + DEVELOPER_CACHE_TTL_MS };
|
||||
return playerIds;
|
||||
};
|
||||
|
||||
@@ -71,8 +71,8 @@ const fireAlert = async (candidate: AlertCandidate) => {
|
||||
value: candidate.value,
|
||||
});
|
||||
|
||||
const admins = await getAdminPlayerIds();
|
||||
for (const playerId of admins) {
|
||||
const developers = await getDeveloperPlayerIds();
|
||||
for (const playerId of developers) {
|
||||
try {
|
||||
await sendPushToPlayer(playerId, {
|
||||
title: candidate.title,
|
||||
|
||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Ø Sieg-Marge"
|
||||
msgid "Award Badge"
|
||||
msgstr "Abzeichen verleihen"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||
msgid "Award Badges"
|
||||
msgstr "Abzeichen verleihen"
|
||||
|
||||
@@ -606,15 +606,15 @@ msgstr "Zurück"
|
||||
msgid "Badge awarded to {playerName}"
|
||||
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."
|
||||
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"
|
||||
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"
|
||||
msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet"
|
||||
|
||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
||||
msgstr "Turnierbaum"
|
||||
|
||||
#: 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"
|
||||
msgstr "Turnierbaum-Vorschau"
|
||||
|
||||
@@ -1118,7 +1118,7 @@ msgstr "Fehlerspitzen und fehlschlagende Funktionen, als Push an dieses Konto"
|
||||
msgid "Error: {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
|
||||
msgid "Errors"
|
||||
msgstr "Fehler"
|
||||
@@ -1186,7 +1186,7 @@ msgstr "Aktives Gerät konnte nicht festgelegt werden"
|
||||
msgid "Failed to set volume"
|
||||
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"
|
||||
msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden"
|
||||
|
||||
@@ -1198,8 +1198,8 @@ msgstr "Team konnte nicht aktualisiert werden"
|
||||
msgid "Failed to update tournament"
|
||||
msgstr "Turnier konnte nicht aktualisiert werden"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/features/teams/components/team-form/index.tsx:148
|
||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||
msgid "Failed to upload logo"
|
||||
@@ -1209,8 +1209,8 @@ msgstr "Logo konnte nicht hochgeladen werden"
|
||||
msgid "Failure"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
||||
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||
msgid "File too large. Maximum size is 10MB."
|
||||
msgstr "Datei zu groß. Maximale Größe ist 10 MB."
|
||||
|
||||
@@ -1264,7 +1264,7 @@ msgstr "Funktion"
|
||||
msgid "Function Name"
|
||||
msgstr "Funktionsname"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||
msgid "Functions"
|
||||
msgstr "Funktionen"
|
||||
|
||||
@@ -1335,11 +1335,11 @@ msgstr "Hast du schon mal mitgespielt?"
|
||||
|
||||
#: src/app/routes/_authed/stats.tsx:56
|
||||
#: src/features/bracket/components/match-dock.tsx:174
|
||||
#: src/features/bracket/components/match-dock.tsx:224
|
||||
#: src/features/bracket/components/match-dock.tsx:203
|
||||
#: src/features/matches/components/match-card.tsx:89
|
||||
#: src/features/matches/components/match-card.tsx:233
|
||||
#: src/features/matches/components/match-card.tsx:214
|
||||
#: src/features/players/components/league-head-to-head.tsx:198
|
||||
#: src/features/players/components/profile/header.tsx:97
|
||||
#: src/features/players/components/profile/header.tsx:79
|
||||
msgid "Head to Head"
|
||||
msgstr "Direktvergleich"
|
||||
|
||||
@@ -1401,8 +1401,8 @@ msgstr "Falscher Code"
|
||||
msgid "Invalid action"
|
||||
msgstr "Ungültige Aktion"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
||||
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||
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"
|
||||
msgstr "Ungültiges Hex-Farbformat"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
||||
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||
msgid "Invalid input"
|
||||
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"
|
||||
|
||||
#. 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}}"
|
||||
msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}"
|
||||
|
||||
@@ -1504,8 +1504,8 @@ msgstr "Ort ist erforderlich"
|
||||
msgid "Logo"
|
||||
msgstr "Logo"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
||||
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||
msgid "Logo file is required"
|
||||
msgstr "Logo-Datei ist erforderlich"
|
||||
|
||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
||||
msgstr "Team-Songs/Logos verwalten"
|
||||
|
||||
#: 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"
|
||||
msgstr "Turniere verwalten"
|
||||
|
||||
@@ -1630,11 +1630,11 @@ msgstr "Matches für Regionals sind ungeordnet"
|
||||
msgid "Matches Played"
|
||||
msgstr "Gespielte Matches"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
||||
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||
msgid "Matchup"
|
||||
msgstr "Matchup"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
||||
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||
msgid "Matchup details"
|
||||
msgstr "Matchup-Details"
|
||||
|
||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
||||
msgid "Me"
|
||||
msgstr "Ich"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||
msgid "Migrate Badge Progress"
|
||||
msgstr "Abzeichen-Fortschritt migrieren"
|
||||
|
||||
@@ -1712,7 +1712,7 @@ msgstr "Neuer Client-Fehler"
|
||||
msgid "New Player"
|
||||
msgstr "Neuer Spieler"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
||||
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||
msgid "Next match"
|
||||
msgstr "Nächstes Match"
|
||||
|
||||
@@ -1908,10 +1908,6 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon
|
||||
msgid "One time code"
|
||||
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
|
||||
msgid "Opponent"
|
||||
msgstr "Gegner"
|
||||
@@ -2047,7 +2043,7 @@ msgstr "Spieler erfolgreich entfernt"
|
||||
msgid "Player Stats"
|
||||
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
|
||||
msgid "Players"
|
||||
msgstr "Spieler"
|
||||
@@ -2083,7 +2079,7 @@ msgstr "Tipps"
|
||||
msgid "Predictions are open"
|
||||
msgstr "Tipps sind offen"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
||||
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||
msgid "Previous match"
|
||||
msgstr "Vorheriges Spiel"
|
||||
|
||||
@@ -2122,7 +2118,7 @@ msgstr "Reaktionen"
|
||||
|
||||
#. placeholder {0}: migrationStatus.playersProcessed
|
||||
#. 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})"
|
||||
msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})"
|
||||
|
||||
@@ -2366,7 +2362,7 @@ msgstr "Serverfunktionen"
|
||||
msgid "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
|
||||
msgid "Sessions"
|
||||
msgstr "Sitzungen"
|
||||
@@ -2520,7 +2516,7 @@ msgstr "Status"
|
||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||
#: src/features/login/components/player-prompt/index.tsx:115
|
||||
#: src/features/login/components/player-prompt/index.tsx:126
|
||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
||||
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||
msgid "Submit"
|
||||
msgstr "Absenden"
|
||||
@@ -2581,7 +2577,7 @@ msgstr "Team-Details"
|
||||
msgid "Team enrolled successfully!"
|
||||
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
|
||||
msgid "Team not found"
|
||||
msgstr "Team nicht gefunden"
|
||||
@@ -2615,8 +2611,8 @@ msgstr "Teams"
|
||||
msgid "Teams created successfully!"
|
||||
msgstr "Teams erfolgreich erstellt!"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||
msgid "Telemetry"
|
||||
msgstr "Telemetrie"
|
||||
|
||||
@@ -2730,7 +2726,7 @@ msgstr "Turnier erstellt, aber Logo-Upload fehlgeschlagen: {0}"
|
||||
msgid "Tournament created successfully!"
|
||||
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"
|
||||
msgstr "Turnier nicht gefunden"
|
||||
|
||||
@@ -2784,8 +2780,8 @@ msgstr "Team abmelden"
|
||||
msgid "Unknown error"
|
||||
msgstr "Unbekannter Fehler"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||
msgid "Unknown error occurred"
|
||||
msgstr "Unbekannter Fehler aufgetreten"
|
||||
|
||||
@@ -2810,7 +2806,7 @@ msgstr "Bevorstehend"
|
||||
msgid "Update Match"
|
||||
msgstr "Match aktualisieren"
|
||||
|
||||
#: src/features/players/components/profile/header.tsx:92
|
||||
#: src/features/players/components/profile/header.tsx:74
|
||||
msgid "Update Name"
|
||||
msgstr "Namen aktualisieren"
|
||||
|
||||
@@ -2824,7 +2820,7 @@ msgstr "Team aktualisieren"
|
||||
msgid "Update Tournament"
|
||||
msgstr "Turnier aktualisieren"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
msgid "Usage"
|
||||
msgstr "Nutzung"
|
||||
|
||||
|
||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Avg Win Margin"
|
||||
msgid "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"
|
||||
msgstr "Award Badges"
|
||||
|
||||
@@ -606,15 +606,15 @@ msgstr "Back"
|
||||
msgid "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."
|
||||
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"
|
||||
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"
|
||||
msgstr "Badge recalculation started in the background"
|
||||
|
||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
||||
msgstr "Bracket"
|
||||
|
||||
#: 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"
|
||||
msgstr "Bracket Preview"
|
||||
|
||||
@@ -1118,7 +1118,7 @@ msgstr "Error spikes and failing functions, pushed to this account"
|
||||
msgid "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
|
||||
msgid "Errors"
|
||||
msgstr "Errors"
|
||||
@@ -1186,7 +1186,7 @@ msgstr "Failed to set active device"
|
||||
msgid "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"
|
||||
msgstr "Failed to start badge recalculation"
|
||||
|
||||
@@ -1198,8 +1198,8 @@ msgstr "Failed to update team"
|
||||
msgid "Failed to update tournament"
|
||||
msgstr "Failed to update tournament"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/features/teams/components/team-form/index.tsx:148
|
||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||
msgid "Failed to upload logo"
|
||||
@@ -1209,8 +1209,8 @@ msgstr "Failed to upload logo"
|
||||
msgid "Failure"
|
||||
msgstr "Failure"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
||||
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||
msgid "File too large. Maximum size is 10MB."
|
||||
msgstr "File too large. Maximum size is 10MB."
|
||||
|
||||
@@ -1264,7 +1264,7 @@ msgstr "Function"
|
||||
msgid "Function Name"
|
||||
msgstr "Function Name"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||
msgid "Functions"
|
||||
msgstr "Functions"
|
||||
|
||||
@@ -1335,11 +1335,11 @@ msgstr "Have you played before?"
|
||||
|
||||
#: src/app/routes/_authed/stats.tsx:56
|
||||
#: src/features/bracket/components/match-dock.tsx:174
|
||||
#: src/features/bracket/components/match-dock.tsx:224
|
||||
#: src/features/bracket/components/match-dock.tsx:203
|
||||
#: src/features/matches/components/match-card.tsx:89
|
||||
#: src/features/matches/components/match-card.tsx:233
|
||||
#: src/features/matches/components/match-card.tsx:214
|
||||
#: src/features/players/components/league-head-to-head.tsx:198
|
||||
#: src/features/players/components/profile/header.tsx:97
|
||||
#: src/features/players/components/profile/header.tsx:79
|
||||
msgid "Head to Head"
|
||||
msgstr "Head to Head"
|
||||
|
||||
@@ -1401,8 +1401,8 @@ msgstr "Incorrect code"
|
||||
msgid "Invalid action"
|
||||
msgstr "Invalid action"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
||||
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||
msgid "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"
|
||||
msgstr "Invalid hex color format"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
||||
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||
msgid "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"
|
||||
|
||||
#. 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}}"
|
||||
msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}"
|
||||
|
||||
@@ -1504,8 +1504,8 @@ msgstr "Location is required"
|
||||
msgid "Logo"
|
||||
msgstr "Logo"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
||||
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||
msgid "Logo file is required"
|
||||
msgstr "Logo file is required"
|
||||
|
||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
||||
msgstr "Manage Team Songs/Logos"
|
||||
|
||||
#: 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"
|
||||
msgstr "Manage Tournaments"
|
||||
|
||||
@@ -1630,11 +1630,11 @@ msgstr "Matches for regionals are unordered"
|
||||
msgid "Matches Played"
|
||||
msgstr "Matches Played"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
||||
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||
msgid "Matchup"
|
||||
msgstr "Matchup"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
||||
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||
msgid "Matchup details"
|
||||
msgstr "Matchup details"
|
||||
|
||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
||||
msgid "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"
|
||||
msgstr "Migrate Badge Progress"
|
||||
|
||||
@@ -1712,7 +1712,7 @@ msgstr "New client error"
|
||||
msgid "New Player"
|
||||
msgstr "New Player"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
||||
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||
msgid "Next match"
|
||||
msgstr "Next match"
|
||||
|
||||
@@ -1908,10 +1908,6 @@ msgstr "Once enrolled, you can view other free agents and their phone number in
|
||||
msgid "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
|
||||
msgid "Opponent"
|
||||
msgstr "Opponent"
|
||||
@@ -2047,7 +2043,7 @@ msgstr "Player removed successfully"
|
||||
msgid "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
|
||||
msgid "Players"
|
||||
msgstr "Players"
|
||||
@@ -2083,7 +2079,7 @@ msgstr "Predictions"
|
||||
msgid "Predictions are open"
|
||||
msgstr "Predictions are open"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
||||
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||
msgid "Previous match"
|
||||
msgstr "Previous match"
|
||||
|
||||
@@ -2122,7 +2118,7 @@ msgstr "Reactions"
|
||||
|
||||
#. placeholder {0}: migrationStatus.playersProcessed
|
||||
#. 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})"
|
||||
msgstr "Recalculating badges in the background ({0}/{1})"
|
||||
|
||||
@@ -2366,7 +2362,7 @@ msgstr "Server functions"
|
||||
msgid "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
|
||||
msgid "Sessions"
|
||||
msgstr "Sessions"
|
||||
@@ -2520,7 +2516,7 @@ msgstr "Status"
|
||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||
#: src/features/login/components/player-prompt/index.tsx:115
|
||||
#: src/features/login/components/player-prompt/index.tsx:126
|
||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
||||
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||
msgid "Submit"
|
||||
msgstr "Submit"
|
||||
@@ -2581,7 +2577,7 @@ msgstr "Team Details"
|
||||
msgid "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
|
||||
msgid "Team not found"
|
||||
msgstr "Team not found"
|
||||
@@ -2615,8 +2611,8 @@ msgstr "Teams"
|
||||
msgid "Teams created successfully!"
|
||||
msgstr "Teams created successfully!"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||
msgid "Telemetry"
|
||||
msgstr "Telemetry"
|
||||
|
||||
@@ -2730,7 +2726,7 @@ msgstr "Tournament created but logo upload failed: {0}"
|
||||
msgid "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"
|
||||
msgstr "Tournament not found"
|
||||
|
||||
@@ -2784,8 +2780,8 @@ msgstr "Unenroll Team"
|
||||
msgid "Unknown error"
|
||||
msgstr "Unknown error"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||
msgid "Unknown error occurred"
|
||||
msgstr "Unknown error occurred"
|
||||
|
||||
@@ -2810,7 +2806,7 @@ msgstr "Upcoming"
|
||||
msgid "Update Match"
|
||||
msgstr "Update Match"
|
||||
|
||||
#: src/features/players/components/profile/header.tsx:92
|
||||
#: src/features/players/components/profile/header.tsx:74
|
||||
msgid "Update Name"
|
||||
msgstr "Update Name"
|
||||
|
||||
@@ -2824,7 +2820,7 @@ msgstr "Update Team"
|
||||
msgid "Update Tournament"
|
||||
msgstr "Update Tournament"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
msgid "Usage"
|
||||
msgstr "Usage"
|
||||
|
||||
|
||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "Prom. Margen de Victoria"
|
||||
msgid "Award Badge"
|
||||
msgstr "Otorgar insignia"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||
msgid "Award Badges"
|
||||
msgstr "Otorgar insignias"
|
||||
|
||||
@@ -606,15 +606,15 @@ msgstr "Atrás"
|
||||
msgid "Badge awarded to {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."
|
||||
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"
|
||||
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"
|
||||
msgstr "Recálculo de insignias iniciado en segundo plano"
|
||||
|
||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
||||
msgstr "Bracket"
|
||||
|
||||
#: 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"
|
||||
msgstr "Vista previa del bracket"
|
||||
|
||||
@@ -1118,7 +1118,7 @@ msgstr "Picos de errores y funciones fallando, enviados a esta cuenta"
|
||||
msgid "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
|
||||
msgid "Errors"
|
||||
msgstr "Errores"
|
||||
@@ -1186,7 +1186,7 @@ msgstr "No se pudo establecer el dispositivo activo"
|
||||
msgid "Failed to set volume"
|
||||
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"
|
||||
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"
|
||||
msgstr "No se pudo actualizar el torneo"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/features/teams/components/team-form/index.tsx:148
|
||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||
msgid "Failed to upload logo"
|
||||
@@ -1209,8 +1209,8 @@ msgstr "No se pudo subir el logo"
|
||||
msgid "Failure"
|
||||
msgstr "Fallo"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
||||
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||
msgid "File too large. Maximum size is 10MB."
|
||||
msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB."
|
||||
|
||||
@@ -1264,7 +1264,7 @@ msgstr "Función"
|
||||
msgid "Function Name"
|
||||
msgstr "Nombre de la función"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||
msgid "Functions"
|
||||
msgstr "Funciones"
|
||||
|
||||
@@ -1335,11 +1335,11 @@ msgstr "¿Ya has jugado antes?"
|
||||
|
||||
#: src/app/routes/_authed/stats.tsx:56
|
||||
#: src/features/bracket/components/match-dock.tsx:174
|
||||
#: src/features/bracket/components/match-dock.tsx:224
|
||||
#: src/features/bracket/components/match-dock.tsx:203
|
||||
#: src/features/matches/components/match-card.tsx:89
|
||||
#: src/features/matches/components/match-card.tsx:233
|
||||
#: src/features/matches/components/match-card.tsx:214
|
||||
#: src/features/players/components/league-head-to-head.tsx:198
|
||||
#: src/features/players/components/profile/header.tsx:97
|
||||
#: src/features/players/components/profile/header.tsx:79
|
||||
msgid "Head to Head"
|
||||
msgstr "Cara a cara"
|
||||
|
||||
@@ -1401,8 +1401,8 @@ msgstr "Código incorrecto"
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción no válida"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
||||
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||
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."
|
||||
|
||||
@@ -1410,8 +1410,8 @@ msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF."
|
||||
msgid "Invalid hex color format"
|
||||
msgstr "Formato de color hexadecimal no válido"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
||||
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||
msgid "Invalid input"
|
||||
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"
|
||||
|
||||
#. 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}}"
|
||||
msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}"
|
||||
|
||||
@@ -1504,8 +1504,8 @@ msgstr "La ubicación es obligatoria"
|
||||
msgid "Logo"
|
||||
msgstr "Logo"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
||||
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||
msgid "Logo file is required"
|
||||
msgstr "El archivo del logo es obligatorio"
|
||||
|
||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
||||
msgstr "Administrar canciones/logos del equipo"
|
||||
|
||||
#: 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"
|
||||
msgstr "Administrar torneos"
|
||||
|
||||
@@ -1630,11 +1630,11 @@ msgstr "Los partidos de regionales no tienen un orden específico"
|
||||
msgid "Matches Played"
|
||||
msgstr "Partidos jugados"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
||||
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||
msgid "Matchup"
|
||||
msgstr "Enfrentamiento"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
||||
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||
msgid "Matchup details"
|
||||
msgstr "Detalles del enfrentamiento"
|
||||
|
||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
||||
msgid "Me"
|
||||
msgstr "Yo"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||
msgid "Migrate Badge Progress"
|
||||
msgstr "Migrar progreso de insignias"
|
||||
|
||||
@@ -1712,7 +1712,7 @@ msgstr "Nuevo error de cliente"
|
||||
msgid "New Player"
|
||||
msgstr "Nuevo jugador"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
||||
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||
msgid "Next match"
|
||||
msgstr "Siguiente partido"
|
||||
|
||||
@@ -1908,10 +1908,6 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel
|
||||
msgid "One time code"
|
||||
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
|
||||
msgid "Opponent"
|
||||
msgstr "Rival"
|
||||
@@ -2047,7 +2043,7 @@ msgstr "Jugador eliminado con éxito"
|
||||
msgid "Player Stats"
|
||||
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
|
||||
msgid "Players"
|
||||
msgstr "Jugadores"
|
||||
@@ -2083,7 +2079,7 @@ msgstr "Predicciones"
|
||||
msgid "Predictions are open"
|
||||
msgstr "Las predicciones están abiertas"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
||||
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||
msgid "Previous match"
|
||||
msgstr "Partido anterior"
|
||||
|
||||
@@ -2122,7 +2118,7 @@ msgstr "Reacciones"
|
||||
|
||||
#. placeholder {0}: migrationStatus.playersProcessed
|
||||
#. 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})"
|
||||
msgstr "Recalculando insignias en segundo plano ({0}/{1})"
|
||||
|
||||
@@ -2366,7 +2362,7 @@ msgstr "Funciones del servidor"
|
||||
msgid "serverFn name"
|
||||
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
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiones"
|
||||
@@ -2520,7 +2516,7 @@ msgstr "Estado"
|
||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||
#: src/features/login/components/player-prompt/index.tsx:115
|
||||
#: src/features/login/components/player-prompt/index.tsx:126
|
||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
||||
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||
msgid "Submit"
|
||||
msgstr "Enviar"
|
||||
@@ -2581,7 +2577,7 @@ msgstr "Detalles del equipo"
|
||||
msgid "Team enrolled successfully!"
|
||||
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
|
||||
msgid "Team not found"
|
||||
msgstr "Equipo no encontrado"
|
||||
@@ -2615,8 +2611,8 @@ msgstr "Equipos"
|
||||
msgid "Teams created successfully!"
|
||||
msgstr "¡Equipos creados exitosamente!"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||
msgid "Telemetry"
|
||||
msgstr "Telemetría"
|
||||
|
||||
@@ -2730,7 +2726,7 @@ msgstr "Torneo creado, pero falló la carga del logo: {0}"
|
||||
msgid "Tournament created successfully!"
|
||||
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"
|
||||
msgstr "Torneo no encontrado"
|
||||
|
||||
@@ -2784,8 +2780,8 @@ msgstr "Cancelar inscripción del equipo"
|
||||
msgid "Unknown error"
|
||||
msgstr "Error desconocido"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||
msgid "Unknown error occurred"
|
||||
msgstr "Ocurrió un error desconocido"
|
||||
|
||||
@@ -2810,7 +2806,7 @@ msgstr "Próximos"
|
||||
msgid "Update Match"
|
||||
msgstr "Actualizar partido"
|
||||
|
||||
#: src/features/players/components/profile/header.tsx:92
|
||||
#: src/features/players/components/profile/header.tsx:74
|
||||
msgid "Update Name"
|
||||
msgstr "Actualizar nombre"
|
||||
|
||||
@@ -2824,7 +2820,7 @@ msgstr "Actualizar equipo"
|
||||
msgid "Update Tournament"
|
||||
msgstr "Actualizar torneo"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
|
||||
+40
-44
@@ -572,7 +572,7 @@ msgstr "平均勝差"
|
||||
msgid "Award Badge"
|
||||
msgstr "バッジを付与"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:60
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:63
|
||||
msgid "Award Badges"
|
||||
msgstr "バッジを付与"
|
||||
|
||||
@@ -606,15 +606,15 @@ msgstr "戻る"
|
||||
msgid "Badge awarded to {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."
|
||||
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"
|
||||
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"
|
||||
msgstr "バックグラウンドでバッジの再計算を開始しました"
|
||||
|
||||
@@ -645,7 +645,7 @@ msgid "Bracket"
|
||||
msgstr "ブラケット"
|
||||
|
||||
#: 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"
|
||||
msgstr "ブラケットプレビュー"
|
||||
|
||||
@@ -1118,7 +1118,7 @@ msgstr "エラーの急増や失敗している関数をこのアカウントに
|
||||
msgid "Error: {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
|
||||
msgid "Errors"
|
||||
msgstr "エラー"
|
||||
@@ -1186,7 +1186,7 @@ msgstr "アクティブなデバイスの設定に失敗しました"
|
||||
msgid "Failed to set volume"
|
||||
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"
|
||||
msgstr "バッジの再計算を開始できませんでした"
|
||||
|
||||
@@ -1198,8 +1198,8 @@ msgstr "チームの更新に失敗しました"
|
||||
msgid "Failed to update tournament"
|
||||
msgstr "トーナメントの更新に失敗しました"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:141
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:139
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/features/teams/components/team-form/index.tsx:148
|
||||
#: src/features/tournaments/components/tournament-form.tsx:88
|
||||
msgid "Failed to upload logo"
|
||||
@@ -1209,8 +1209,8 @@ msgstr "ロゴのアップロードに失敗しました"
|
||||
msgid "Failure"
|
||||
msgstr "失敗"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:80
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:81
|
||||
#: src/app/routes/api/teams/upload-logo.ts:81
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:82
|
||||
msgid "File too large. Maximum size is 10MB."
|
||||
msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。"
|
||||
|
||||
@@ -1264,7 +1264,7 @@ msgstr "関数"
|
||||
msgid "Function Name"
|
||||
msgstr "関数名"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:57
|
||||
msgid "Functions"
|
||||
msgstr "関数"
|
||||
|
||||
@@ -1335,11 +1335,11 @@ msgstr "参加したことはありますか?"
|
||||
|
||||
#: src/app/routes/_authed/stats.tsx:56
|
||||
#: src/features/bracket/components/match-dock.tsx:174
|
||||
#: src/features/bracket/components/match-dock.tsx:224
|
||||
#: src/features/bracket/components/match-dock.tsx:203
|
||||
#: src/features/matches/components/match-card.tsx:89
|
||||
#: src/features/matches/components/match-card.tsx:233
|
||||
#: src/features/matches/components/match-card.tsx:214
|
||||
#: src/features/players/components/league-head-to-head.tsx:198
|
||||
#: src/features/players/components/profile/header.tsx:97
|
||||
#: src/features/players/components/profile/header.tsx:79
|
||||
msgid "Head to Head"
|
||||
msgstr "対戦成績"
|
||||
|
||||
@@ -1401,8 +1401,8 @@ msgstr "コードが正しくありません"
|
||||
msgid "Invalid action"
|
||||
msgstr "無効な操作です"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:66
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:67
|
||||
#: src/app/routes/api/teams/upload-logo.ts:67
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:68
|
||||
msgid "Invalid file type. Only JPEG, PNG and GIF are allowed."
|
||||
msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。"
|
||||
|
||||
@@ -1410,8 +1410,8 @@ msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用でき
|
||||
msgid "Invalid hex color format"
|
||||
msgstr "無効なHEXカラー形式です"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:34
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:35
|
||||
#: src/app/routes/api/teams/upload-logo.ts:35
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:36
|
||||
msgid "Invalid input"
|
||||
msgstr "無効な入力です"
|
||||
|
||||
@@ -1470,7 +1470,7 @@ msgid "Last name must be 3-20 characters long and contain only letters"
|
||||
msgstr "姓は3〜20文字の英字のみで入力してください"
|
||||
|
||||
#. 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}}"
|
||||
msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}"
|
||||
|
||||
@@ -1504,8 +1504,8 @@ msgstr "場所は必須です"
|
||||
msgid "Logo"
|
||||
msgstr "ロゴ"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:47
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:48
|
||||
#: src/app/routes/api/teams/upload-logo.ts:48
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:49
|
||||
msgid "Logo file is required"
|
||||
msgstr "ロゴファイルは必須です"
|
||||
|
||||
@@ -1559,7 +1559,7 @@ msgid "Manage Team Songs/Logos"
|
||||
msgstr "チームの曲・ロゴを管理"
|
||||
|
||||
#: 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"
|
||||
msgstr "トーナメント管理"
|
||||
|
||||
@@ -1630,11 +1630,11 @@ msgstr "地区予選の試合は順不同です"
|
||||
msgid "Matches Played"
|
||||
msgstr "試合数"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:252
|
||||
#: src/features/predictions/components/prediction-editor.tsx:253
|
||||
msgid "Matchup"
|
||||
msgstr "対戦カード"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:229
|
||||
#: src/features/predictions/components/prediction-editor.tsx:230
|
||||
msgid "Matchup details"
|
||||
msgstr "対戦カードの詳細"
|
||||
|
||||
@@ -1646,7 +1646,7 @@ msgstr "MAU"
|
||||
msgid "Me"
|
||||
msgstr "自分"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:65
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:68
|
||||
msgid "Migrate Badge Progress"
|
||||
msgstr "バッジ進捗を移行"
|
||||
|
||||
@@ -1712,7 +1712,7 @@ msgstr "新しいクライアントエラー"
|
||||
msgid "New Player"
|
||||
msgstr "新しいプレイヤー"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:211
|
||||
#: src/features/predictions/components/prediction-editor.tsx:212
|
||||
msgid "Next match"
|
||||
msgstr "次の試合"
|
||||
|
||||
@@ -1908,10 +1908,6 @@ msgstr "登録が完了すると、他のフリーエージェントとその電
|
||||
msgid "One time code"
|
||||
msgstr "ワンタイムコード"
|
||||
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:95
|
||||
msgid "Open Pocketbase"
|
||||
msgstr "Pocketbaseを開く"
|
||||
|
||||
#: src/features/bracket/components/match-report.tsx:273
|
||||
msgid "Opponent"
|
||||
msgstr "対戦相手"
|
||||
@@ -2047,7 +2043,7 @@ msgstr "プレイヤーを削除しました"
|
||||
msgid "Player Stats"
|
||||
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
|
||||
msgid "Players"
|
||||
msgstr "プレイヤー"
|
||||
@@ -2083,7 +2079,7 @@ msgstr "予想"
|
||||
msgid "Predictions are open"
|
||||
msgstr "予想受付中"
|
||||
|
||||
#: src/features/predictions/components/prediction-editor.tsx:201
|
||||
#: src/features/predictions/components/prediction-editor.tsx:202
|
||||
msgid "Previous match"
|
||||
msgstr "前の試合"
|
||||
|
||||
@@ -2122,7 +2118,7 @@ msgstr "リアクション"
|
||||
|
||||
#. placeholder {0}: migrationStatus.playersProcessed
|
||||
#. 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})"
|
||||
msgstr "バックグラウンドでバッジを再計算中({0}/{1})"
|
||||
|
||||
@@ -2366,7 +2362,7 @@ msgstr "サーバー関数"
|
||||
msgid "serverFn name"
|
||||
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
|
||||
msgid "Sessions"
|
||||
msgstr "セッション数"
|
||||
@@ -2520,7 +2516,7 @@ msgstr "ステータス"
|
||||
#: src/components/sheet/slide-panel/slide-panel.tsx:40
|
||||
#: src/features/login/components/player-prompt/index.tsx:115
|
||||
#: src/features/login/components/player-prompt/index.tsx:126
|
||||
#: src/features/predictions/components/prediction-editor.tsx:243
|
||||
#: src/features/predictions/components/prediction-editor.tsx:244
|
||||
#: src/features/tournaments/components/edit-rules.tsx:39
|
||||
msgid "Submit"
|
||||
msgstr "送信"
|
||||
@@ -2581,7 +2577,7 @@ msgstr "チーム詳細"
|
||||
msgid "Team enrolled successfully!"
|
||||
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
|
||||
msgid "Team not found"
|
||||
msgstr "チームが見つかりません"
|
||||
@@ -2615,8 +2611,8 @@ msgstr "チーム"
|
||||
msgid "Teams created successfully!"
|
||||
msgstr "チームを作成しました!"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:20
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:55
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:26
|
||||
#: src/features/admin/components/dashboard/tools-list.tsx:57
|
||||
msgid "Telemetry"
|
||||
msgstr "テレメトリー"
|
||||
|
||||
@@ -2730,7 +2726,7 @@ msgstr "トーナメントを作成しましたが、ロゴのアップロード
|
||||
msgid "Tournament created successfully!"
|
||||
msgstr "トーナメントを作成しました!"
|
||||
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:94
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:95
|
||||
msgid "Tournament not found"
|
||||
msgstr "トーナメントが見つかりません"
|
||||
|
||||
@@ -2784,8 +2780,8 @@ msgstr "チームを登録解除"
|
||||
msgid "Unknown error"
|
||||
msgstr "不明なエラー"
|
||||
|
||||
#: src/app/routes/api/teams/upload-logo.ts:142
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:140
|
||||
#: src/app/routes/api/teams/upload-logo.ts:143
|
||||
#: src/app/routes/api/tournaments/upload-logo.ts:141
|
||||
msgid "Unknown error occurred"
|
||||
msgstr "不明なエラーが発生しました"
|
||||
|
||||
@@ -2810,7 +2806,7 @@ msgstr "開催予定"
|
||||
msgid "Update Match"
|
||||
msgstr "試合を更新"
|
||||
|
||||
#: src/features/players/components/profile/header.tsx:92
|
||||
#: src/features/players/components/profile/header.tsx:74
|
||||
msgid "Update Name"
|
||||
msgstr "名前を更新"
|
||||
|
||||
@@ -2824,7 +2820,7 @@ msgstr "チームを更新"
|
||||
msgid "Update Tournament"
|
||||
msgstr "トーナメントを更新"
|
||||
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:45
|
||||
#: src/app/routes/_authed/admin/telemetry.tsx:51
|
||||
msgid "Usage"
|
||||
msgstr "利用状況"
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
} from "@tanstack/react-start";
|
||||
import { getRequest } from "@tanstack/react-start/server";
|
||||
import { Logger } from "@/lib/logger";
|
||||
import { isAdminRole } from "@/features/core/utils/roles";
|
||||
import { isAdminRole, isDeveloperRole } from "@/features/core/utils/roles";
|
||||
import z from "zod";
|
||||
|
||||
const logger = new Logger("Middleware");
|
||||
@@ -70,19 +70,23 @@ export const superTokensFunctionMiddleware = createMiddleware({
|
||||
}
|
||||
});
|
||||
|
||||
export const superTokensAdminFunctionMiddleware = createMiddleware({
|
||||
const createRoleFunctionMiddleware = (
|
||||
hasRole: (roles?: string[] | null) => boolean,
|
||||
label: string
|
||||
) =>
|
||||
createMiddleware({
|
||||
type: "function",
|
||||
}).server(async ({ next, serverFnMeta }) => {
|
||||
}).server(async ({ next, serverFnMeta }) => {
|
||||
const request = getRequest();
|
||||
|
||||
try {
|
||||
const context = await getSessionContext(request, { isServerFunction: true });
|
||||
|
||||
if (isAdminRole(context.roles)) {
|
||||
if (hasRole(context.roles)) {
|
||||
return next({ context });
|
||||
}
|
||||
|
||||
logger.error("Unauthorized user in admin function.", {
|
||||
logger.error(`Unauthorized user in ${label} function.`, {
|
||||
userAuthId: context.userAuthId,
|
||||
roles: context.roles,
|
||||
});
|
||||
@@ -104,7 +108,17 @@ export const superTokensAdminFunctionMiddleware = createMiddleware({
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export const superTokensAdminFunctionMiddleware = createRoleFunctionMiddleware(
|
||||
isAdminRole,
|
||||
"admin"
|
||||
);
|
||||
|
||||
export const superTokensDeveloperFunctionMiddleware = createRoleFunctionMiddleware(
|
||||
isDeveloperRole,
|
||||
"developer"
|
||||
);
|
||||
|
||||
export const setUserMetadata = createServerFn({ method: "POST" })
|
||||
.validator(
|
||||
|
||||
Reference in New Issue
Block a user