init
This commit is contained in:
391
src/app/routeTree.gen.ts
Normal file
391
src/app/routeTree.gen.ts
Normal file
@@ -0,0 +1,391 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { createServerRootRoute } from '@tanstack/react-start/server'
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as LogoutRouteImport } from './routes/logout'
|
||||
import { Route as LoginRouteImport } from './routes/login'
|
||||
import { Route as AuthedRouteImport } from './routes/_authed'
|
||||
import { Route as AuthedIndexRouteImport } from './routes/_authed/index'
|
||||
import { Route as AuthedSettingsRouteImport } from './routes/_authed/settings'
|
||||
import { Route as AuthedAdminRouteImport } from './routes/_authed/admin'
|
||||
import { Route as AuthedTournamentsIndexRouteImport } from './routes/_authed/tournaments/index'
|
||||
import { Route as AuthedAdminIndexRouteImport } from './routes/_authed/admin/index'
|
||||
import { Route as AuthedTournamentsTournamentIdRouteImport } from './routes/_authed/tournaments/$tournamentId'
|
||||
import { Route as AuthedTeamsTeamIdRouteImport } from './routes/_authed/teams.$teamId'
|
||||
import { Route as AuthedProfilePlayerIdRouteImport } from './routes/_authed/profile.$playerId'
|
||||
import { Route as AuthedAdminPreviewRouteImport } from './routes/_authed/admin/preview'
|
||||
import { ServerRoute as ApiTestServerRouteImport } from './routes/api/test'
|
||||
import { ServerRoute as ApiEventsSplatServerRouteImport } from './routes/api/events.$'
|
||||
import { ServerRoute as ApiAuthSplatServerRouteImport } from './routes/api/auth.$'
|
||||
|
||||
const rootServerRouteImport = createServerRootRoute()
|
||||
|
||||
const LogoutRoute = LogoutRouteImport.update({
|
||||
id: '/logout',
|
||||
path: '/logout',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const LoginRoute = LoginRouteImport.update({
|
||||
id: '/login',
|
||||
path: '/login',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuthedRoute = AuthedRouteImport.update({
|
||||
id: '/_authed',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const AuthedIndexRoute = AuthedIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedSettingsRoute = AuthedSettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedAdminRoute = AuthedAdminRouteImport.update({
|
||||
id: '/admin',
|
||||
path: '/admin',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedTournamentsIndexRoute = AuthedTournamentsIndexRouteImport.update({
|
||||
id: '/tournaments/',
|
||||
path: '/tournaments/',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedAdminIndexRoute = AuthedAdminIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => AuthedAdminRoute,
|
||||
} as any)
|
||||
const AuthedTournamentsTournamentIdRoute =
|
||||
AuthedTournamentsTournamentIdRouteImport.update({
|
||||
id: '/tournaments/$tournamentId',
|
||||
path: '/tournaments/$tournamentId',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedTeamsTeamIdRoute = AuthedTeamsTeamIdRouteImport.update({
|
||||
id: '/teams/$teamId',
|
||||
path: '/teams/$teamId',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedProfilePlayerIdRoute = AuthedProfilePlayerIdRouteImport.update({
|
||||
id: '/profile/$playerId',
|
||||
path: '/profile/$playerId',
|
||||
getParentRoute: () => AuthedRoute,
|
||||
} as any)
|
||||
const AuthedAdminPreviewRoute = AuthedAdminPreviewRouteImport.update({
|
||||
id: '/preview',
|
||||
path: '/preview',
|
||||
getParentRoute: () => AuthedAdminRoute,
|
||||
} as any)
|
||||
const ApiTestServerRoute = ApiTestServerRouteImport.update({
|
||||
id: '/api/test',
|
||||
path: '/api/test',
|
||||
getParentRoute: () => rootServerRouteImport,
|
||||
} as any)
|
||||
const ApiEventsSplatServerRoute = ApiEventsSplatServerRouteImport.update({
|
||||
id: '/api/events/$',
|
||||
path: '/api/events/$',
|
||||
getParentRoute: () => rootServerRouteImport,
|
||||
} as any)
|
||||
const ApiAuthSplatServerRoute = ApiAuthSplatServerRouteImport.update({
|
||||
id: '/api/auth/$',
|
||||
path: '/api/auth/$',
|
||||
getParentRoute: () => rootServerRouteImport,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/login': typeof LoginRoute
|
||||
'/logout': typeof LogoutRoute
|
||||
'/admin': typeof AuthedAdminRouteWithChildren
|
||||
'/settings': typeof AuthedSettingsRoute
|
||||
'/': typeof AuthedIndexRoute
|
||||
'/admin/preview': typeof AuthedAdminPreviewRoute
|
||||
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||
'/admin/': typeof AuthedAdminIndexRoute
|
||||
'/tournaments': typeof AuthedTournamentsIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/login': typeof LoginRoute
|
||||
'/logout': typeof LogoutRoute
|
||||
'/settings': typeof AuthedSettingsRoute
|
||||
'/': typeof AuthedIndexRoute
|
||||
'/admin/preview': typeof AuthedAdminPreviewRoute
|
||||
'/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||
'/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||
'/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||
'/admin': typeof AuthedAdminIndexRoute
|
||||
'/tournaments': typeof AuthedTournamentsIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/_authed': typeof AuthedRouteWithChildren
|
||||
'/login': typeof LoginRoute
|
||||
'/logout': typeof LogoutRoute
|
||||
'/_authed/admin': typeof AuthedAdminRouteWithChildren
|
||||
'/_authed/settings': typeof AuthedSettingsRoute
|
||||
'/_authed/': typeof AuthedIndexRoute
|
||||
'/_authed/admin/preview': typeof AuthedAdminPreviewRoute
|
||||
'/_authed/profile/$playerId': typeof AuthedProfilePlayerIdRoute
|
||||
'/_authed/teams/$teamId': typeof AuthedTeamsTeamIdRoute
|
||||
'/_authed/tournaments/$tournamentId': typeof AuthedTournamentsTournamentIdRoute
|
||||
'/_authed/admin/': typeof AuthedAdminIndexRoute
|
||||
'/_authed/tournaments/': typeof AuthedTournamentsIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/login'
|
||||
| '/logout'
|
||||
| '/admin'
|
||||
| '/settings'
|
||||
| '/'
|
||||
| '/admin/preview'
|
||||
| '/profile/$playerId'
|
||||
| '/teams/$teamId'
|
||||
| '/tournaments/$tournamentId'
|
||||
| '/admin/'
|
||||
| '/tournaments'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/login'
|
||||
| '/logout'
|
||||
| '/settings'
|
||||
| '/'
|
||||
| '/admin/preview'
|
||||
| '/profile/$playerId'
|
||||
| '/teams/$teamId'
|
||||
| '/tournaments/$tournamentId'
|
||||
| '/admin'
|
||||
| '/tournaments'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/_authed'
|
||||
| '/login'
|
||||
| '/logout'
|
||||
| '/_authed/admin'
|
||||
| '/_authed/settings'
|
||||
| '/_authed/'
|
||||
| '/_authed/admin/preview'
|
||||
| '/_authed/profile/$playerId'
|
||||
| '/_authed/teams/$teamId'
|
||||
| '/_authed/tournaments/$tournamentId'
|
||||
| '/_authed/admin/'
|
||||
| '/_authed/tournaments/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
AuthedRoute: typeof AuthedRouteWithChildren
|
||||
LoginRoute: typeof LoginRoute
|
||||
LogoutRoute: typeof LogoutRoute
|
||||
}
|
||||
export interface FileServerRoutesByFullPath {
|
||||
'/api/test': typeof ApiTestServerRoute
|
||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
||||
}
|
||||
export interface FileServerRoutesByTo {
|
||||
'/api/test': typeof ApiTestServerRoute
|
||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
||||
}
|
||||
export interface FileServerRoutesById {
|
||||
__root__: typeof rootServerRouteImport
|
||||
'/api/test': typeof ApiTestServerRoute
|
||||
'/api/auth/$': typeof ApiAuthSplatServerRoute
|
||||
'/api/events/$': typeof ApiEventsSplatServerRoute
|
||||
}
|
||||
export interface FileServerRouteTypes {
|
||||
fileServerRoutesByFullPath: FileServerRoutesByFullPath
|
||||
fullPaths: '/api/test' | '/api/auth/$' | '/api/events/$'
|
||||
fileServerRoutesByTo: FileServerRoutesByTo
|
||||
to: '/api/test' | '/api/auth/$' | '/api/events/$'
|
||||
id: '__root__' | '/api/test' | '/api/auth/$' | '/api/events/$'
|
||||
fileServerRoutesById: FileServerRoutesById
|
||||
}
|
||||
export interface RootServerRouteChildren {
|
||||
ApiTestServerRoute: typeof ApiTestServerRoute
|
||||
ApiAuthSplatServerRoute: typeof ApiAuthSplatServerRoute
|
||||
ApiEventsSplatServerRoute: typeof ApiEventsSplatServerRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/logout': {
|
||||
id: '/logout'
|
||||
path: '/logout'
|
||||
fullPath: '/logout'
|
||||
preLoaderRoute: typeof LogoutRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/login': {
|
||||
id: '/login'
|
||||
path: '/login'
|
||||
fullPath: '/login'
|
||||
preLoaderRoute: typeof LoginRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/_authed': {
|
||||
id: '/_authed'
|
||||
path: ''
|
||||
fullPath: ''
|
||||
preLoaderRoute: typeof AuthedRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/_authed/': {
|
||||
id: '/_authed/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof AuthedIndexRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/settings': {
|
||||
id: '/_authed/settings'
|
||||
path: '/settings'
|
||||
fullPath: '/settings'
|
||||
preLoaderRoute: typeof AuthedSettingsRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/admin': {
|
||||
id: '/_authed/admin'
|
||||
path: '/admin'
|
||||
fullPath: '/admin'
|
||||
preLoaderRoute: typeof AuthedAdminRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/tournaments/': {
|
||||
id: '/_authed/tournaments/'
|
||||
path: '/tournaments'
|
||||
fullPath: '/tournaments'
|
||||
preLoaderRoute: typeof AuthedTournamentsIndexRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/admin/': {
|
||||
id: '/_authed/admin/'
|
||||
path: '/'
|
||||
fullPath: '/admin/'
|
||||
preLoaderRoute: typeof AuthedAdminIndexRouteImport
|
||||
parentRoute: typeof AuthedAdminRoute
|
||||
}
|
||||
'/_authed/tournaments/$tournamentId': {
|
||||
id: '/_authed/tournaments/$tournamentId'
|
||||
path: '/tournaments/$tournamentId'
|
||||
fullPath: '/tournaments/$tournamentId'
|
||||
preLoaderRoute: typeof AuthedTournamentsTournamentIdRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/teams/$teamId': {
|
||||
id: '/_authed/teams/$teamId'
|
||||
path: '/teams/$teamId'
|
||||
fullPath: '/teams/$teamId'
|
||||
preLoaderRoute: typeof AuthedTeamsTeamIdRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/profile/$playerId': {
|
||||
id: '/_authed/profile/$playerId'
|
||||
path: '/profile/$playerId'
|
||||
fullPath: '/profile/$playerId'
|
||||
preLoaderRoute: typeof AuthedProfilePlayerIdRouteImport
|
||||
parentRoute: typeof AuthedRoute
|
||||
}
|
||||
'/_authed/admin/preview': {
|
||||
id: '/_authed/admin/preview'
|
||||
path: '/preview'
|
||||
fullPath: '/admin/preview'
|
||||
preLoaderRoute: typeof AuthedAdminPreviewRouteImport
|
||||
parentRoute: typeof AuthedAdminRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
declare module '@tanstack/react-start/server' {
|
||||
interface ServerFileRoutesByPath {
|
||||
'/api/test': {
|
||||
id: '/api/test'
|
||||
path: '/api/test'
|
||||
fullPath: '/api/test'
|
||||
preLoaderRoute: typeof ApiTestServerRouteImport
|
||||
parentRoute: typeof rootServerRouteImport
|
||||
}
|
||||
'/api/events/$': {
|
||||
id: '/api/events/$'
|
||||
path: '/api/events/$'
|
||||
fullPath: '/api/events/$'
|
||||
preLoaderRoute: typeof ApiEventsSplatServerRouteImport
|
||||
parentRoute: typeof rootServerRouteImport
|
||||
}
|
||||
'/api/auth/$': {
|
||||
id: '/api/auth/$'
|
||||
path: '/api/auth/$'
|
||||
fullPath: '/api/auth/$'
|
||||
preLoaderRoute: typeof ApiAuthSplatServerRouteImport
|
||||
parentRoute: typeof rootServerRouteImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AuthedAdminRouteChildren {
|
||||
AuthedAdminPreviewRoute: typeof AuthedAdminPreviewRoute
|
||||
AuthedAdminIndexRoute: typeof AuthedAdminIndexRoute
|
||||
}
|
||||
|
||||
const AuthedAdminRouteChildren: AuthedAdminRouteChildren = {
|
||||
AuthedAdminPreviewRoute: AuthedAdminPreviewRoute,
|
||||
AuthedAdminIndexRoute: AuthedAdminIndexRoute,
|
||||
}
|
||||
|
||||
const AuthedAdminRouteWithChildren = AuthedAdminRoute._addFileChildren(
|
||||
AuthedAdminRouteChildren,
|
||||
)
|
||||
|
||||
interface AuthedRouteChildren {
|
||||
AuthedAdminRoute: typeof AuthedAdminRouteWithChildren
|
||||
AuthedSettingsRoute: typeof AuthedSettingsRoute
|
||||
AuthedIndexRoute: typeof AuthedIndexRoute
|
||||
AuthedProfilePlayerIdRoute: typeof AuthedProfilePlayerIdRoute
|
||||
AuthedTeamsTeamIdRoute: typeof AuthedTeamsTeamIdRoute
|
||||
AuthedTournamentsTournamentIdRoute: typeof AuthedTournamentsTournamentIdRoute
|
||||
AuthedTournamentsIndexRoute: typeof AuthedTournamentsIndexRoute
|
||||
}
|
||||
|
||||
const AuthedRouteChildren: AuthedRouteChildren = {
|
||||
AuthedAdminRoute: AuthedAdminRouteWithChildren,
|
||||
AuthedSettingsRoute: AuthedSettingsRoute,
|
||||
AuthedIndexRoute: AuthedIndexRoute,
|
||||
AuthedProfilePlayerIdRoute: AuthedProfilePlayerIdRoute,
|
||||
AuthedTeamsTeamIdRoute: AuthedTeamsTeamIdRoute,
|
||||
AuthedTournamentsTournamentIdRoute: AuthedTournamentsTournamentIdRoute,
|
||||
AuthedTournamentsIndexRoute: AuthedTournamentsIndexRoute,
|
||||
}
|
||||
|
||||
const AuthedRouteWithChildren =
|
||||
AuthedRoute._addFileChildren(AuthedRouteChildren)
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
AuthedRoute: AuthedRouteWithChildren,
|
||||
LoginRoute: LoginRoute,
|
||||
LogoutRoute: LogoutRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
const rootServerRouteChildren: RootServerRouteChildren = {
|
||||
ApiTestServerRoute: ApiTestServerRoute,
|
||||
ApiAuthSplatServerRoute: ApiAuthSplatServerRoute,
|
||||
ApiEventsSplatServerRoute: ApiEventsSplatServerRoute,
|
||||
}
|
||||
export const serverRouteTree = rootServerRouteImport
|
||||
._addFileChildren(rootServerRouteChildren)
|
||||
._addFileTypes<FileServerRouteTypes>()
|
||||
37
src/app/router.tsx
Normal file
37
src/app/router.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
|
||||
import { routerWithQueryClient } from '@tanstack/react-router-with-query'
|
||||
import { routeTree } from './routeTree.gen'
|
||||
import { DefaultCatchBoundary } from '../components/DefaultCatchBoundary'
|
||||
import { defaultHeaderConfig } from '@/features/core/hooks/use-header-config'
|
||||
|
||||
export function createRouter() {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60 * 1000, // 60 seconds
|
||||
gcTime: 5 * 60 * 1000, // 5 minutes
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: 'always',
|
||||
retry: 3,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return routerWithQueryClient(
|
||||
createTanStackRouter({
|
||||
routeTree,
|
||||
context: { queryClient, auth: undefined!, header: defaultHeaderConfig, refresh: { toRefresh: [] } },
|
||||
defaultPreload: 'intent',
|
||||
defaultErrorComponent: DefaultCatchBoundary,
|
||||
scrollRestoration: true,
|
||||
}),
|
||||
queryClient,
|
||||
)
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: ReturnType<typeof createRouter>
|
||||
}
|
||||
}
|
||||
107
src/app/routes/__root.tsx
Normal file
107
src/app/routes/__root.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import '@mantine/core/styles.css';
|
||||
import '@mantine/dates/styles.css';
|
||||
import {
|
||||
HeadContent,
|
||||
Navigate,
|
||||
Outlet,
|
||||
Scripts,
|
||||
createRootRouteWithContext
|
||||
} from '@tanstack/react-router'
|
||||
import * as React from 'react'
|
||||
import { DefaultCatchBoundary } from '@/components/DefaultCatchBoundary'
|
||||
import { type QueryClient } from '@tanstack/react-query'
|
||||
import { ensureSuperTokensFrontend } from '@/lib/supertokens/client'
|
||||
import { AuthContextType, authQueryConfig } from '@/contexts/auth-context'
|
||||
import Providers from '@/components/providers'
|
||||
import { ColorSchemeScript, mantineHtmlProps } from '@mantine/core';
|
||||
import { HeaderConfig } from '@/features/core/types/header-config';
|
||||
|
||||
export const Route = createRootRouteWithContext<{
|
||||
queryClient: QueryClient,
|
||||
auth: AuthContextType,
|
||||
header: HeaderConfig,
|
||||
refresh: { toRefresh: string[] }
|
||||
}>()({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{
|
||||
charSet: 'utf-8'
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, interactive-widget=overlays-content',
|
||||
}
|
||||
],
|
||||
links: [
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '180x180',
|
||||
href: '/apple-touch-icon.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '32x32',
|
||||
href: '/favicon-32x32.png',
|
||||
},
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
sizes: '16x16',
|
||||
href: '/favicon-16x16.png',
|
||||
},
|
||||
{ rel: 'manifest', href: '/site.webmanifest' },
|
||||
{ rel: 'icon', href: '/favicon.ico' },
|
||||
],
|
||||
}),
|
||||
errorComponent: (props) => {
|
||||
return (
|
||||
<RootDocument>
|
||||
<DefaultCatchBoundary {...props} />
|
||||
</RootDocument>
|
||||
)
|
||||
},
|
||||
component: RootComponent,
|
||||
notFoundComponent: () => <Navigate to="/" />,
|
||||
beforeLoad: async ({ context }) => {
|
||||
// I don't really like this. I wish there was some way before the router is rendered to useAuth() and pass context there.
|
||||
// See: https://github.com/TanStack/router/discussions/3531
|
||||
const auth = await context.queryClient.ensureQueryData(authQueryConfig)
|
||||
return {
|
||||
auth
|
||||
};
|
||||
}
|
||||
})
|
||||
|
||||
function RootComponent() {
|
||||
|
||||
React.useEffect(() => {
|
||||
ensureSuperTokensFrontend()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<RootDocument>
|
||||
<Providers>
|
||||
<Outlet />
|
||||
</Providers>
|
||||
</RootDocument>
|
||||
)
|
||||
}
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html {...mantineHtmlProps} style={{ overflowX: 'hidden', overflowY: 'hidden', position: 'fixed', width: '100%' }}>
|
||||
<head>
|
||||
<HeadContent />
|
||||
<ColorSchemeScript />
|
||||
<link rel="stylesheet" href="/styles.css" />
|
||||
</head>
|
||||
<body style={{ overflowX: 'hidden', overflowY: 'hidden', position: 'fixed', width: '100%' }}>
|
||||
<div className='app'>
|
||||
{children}
|
||||
</div>
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
26
src/app/routes/_authed.tsx
Normal file
26
src/app/routes/_authed.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Outlet, redirect, createFileRoute } from "@tanstack/react-router";
|
||||
import Layout from "@/features/core/components/layout";
|
||||
import { useServerEvents } from "@/hooks/use-server-events";
|
||||
|
||||
export const Route = createFileRoute('/_authed')({
|
||||
beforeLoad: ({ context }) => {
|
||||
if (!context.auth?.user) {
|
||||
throw redirect({ to: '/login' })
|
||||
}
|
||||
|
||||
return {
|
||||
auth: {
|
||||
...context.auth,
|
||||
user: context.auth.user
|
||||
}
|
||||
};
|
||||
},
|
||||
component: () => {
|
||||
useServerEvents();
|
||||
return (
|
||||
<Layout>
|
||||
<Outlet />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
})
|
||||
18
src/app/routes/_authed/admin.tsx
Normal file
18
src/app/routes/_authed/admin.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Outlet, redirect, createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute('/_authed/admin')({
|
||||
component: Outlet,
|
||||
beforeLoad: ({ context }) => {
|
||||
if (!context.auth?.roles?.includes('Admin')) {
|
||||
throw redirect({ to: '/' })
|
||||
}
|
||||
|
||||
return {
|
||||
header: {
|
||||
...context.header,
|
||||
title: 'Admin',
|
||||
withBackButton: true
|
||||
},
|
||||
};
|
||||
}
|
||||
})
|
||||
22
src/app/routes/_authed/admin/index.tsx
Normal file
22
src/app/routes/_authed/admin/index.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { Title } from "@mantine/core";
|
||||
import Page from "@/components/page";
|
||||
import { playerQueries } from "@/features/players/queries";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import PlayerList from "@/features/players/components/player-list";
|
||||
|
||||
export const Route = createFileRoute("/_authed/admin/")({
|
||||
loader: async ({ context }) => {
|
||||
const { queryClient } = context;
|
||||
await queryClient.ensureQueryData(playerQueries.list())
|
||||
},
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { data: players, isLoading } = useQuery(playerQueries.list());
|
||||
return <Page>
|
||||
<Title order={2} mb='md'>Players</Title>
|
||||
<PlayerList players={players!} loading={isLoading} />
|
||||
</Page>
|
||||
}
|
||||
10
src/app/routes/_authed/admin/preview.tsx
Normal file
10
src/app/routes/_authed/admin/preview.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { PreviewBracketPage } from '@/features/bracket/components/bracket-page'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/_authed/admin/preview')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <PreviewBracketPage />
|
||||
}
|
||||
29
src/app/routes/_authed/index.tsx
Normal file
29
src/app/routes/_authed/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import Page from "@/components/page";
|
||||
import { TrophyIcon } from "@phosphor-icons/react";
|
||||
import ListLink from "@/components/list-link";
|
||||
import { tournamentQueries } from "@/features/tournaments/queries";
|
||||
import { Box, Divider, Text } from "@mantine/core";
|
||||
|
||||
export const Route = createFileRoute("/_authed/")({
|
||||
component: Home,
|
||||
beforeLoad: async ({ context }) => {
|
||||
await context.queryClient.ensureQueryData(tournamentQueries.list());
|
||||
},
|
||||
});
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<Page noPadding>
|
||||
<Box h='60vh' p="md">
|
||||
<Text m='16vh' fw={500}>Some Content Here</Text>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text pl='md'>Quick Links</Text>
|
||||
<Divider />
|
||||
<ListLink label="All Tournaments" to="/tournaments" Icon={TrophyIcon} />
|
||||
</Box>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
28
src/app/routes/_authed/profile.$playerId.tsx
Normal file
28
src/app/routes/_authed/profile.$playerId.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import Page from "@/components/page";
|
||||
import Profile from "@/features/players/components/profile";
|
||||
import { playerQueries } from "@/features/players/queries";
|
||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/_authed/profile/$playerId")({
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const { queryClient } = context;
|
||||
const player = await queryClient.ensureQueryData(playerQueries.details(params.playerId))
|
||||
if (!player) throw redirect({ to: '/' });
|
||||
return {
|
||||
player
|
||||
}
|
||||
},
|
||||
loader: ({ params }) => ({
|
||||
header: {
|
||||
collapsed: true,
|
||||
withBackButton: true
|
||||
},
|
||||
refresh: {
|
||||
toRefresh: [playerQueries.details(params.playerId).queryKey],
|
||||
}
|
||||
}),
|
||||
component: () => {
|
||||
const { player } = Route.useRouteContext();
|
||||
return <Page><Profile player={player} /></Page>
|
||||
},
|
||||
})
|
||||
34
src/app/routes/_authed/settings.tsx
Normal file
34
src/app/routes/_authed/settings.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { Box, Title, Stack } from "@mantine/core"
|
||||
import { ColorSchemePicker } from "@/features/settings/components/color-scheme-picker"
|
||||
import AccentColorPicker from "@/features/settings/components/accent-color-picker"
|
||||
import { SignOutIcon } from "@phosphor-icons/react"
|
||||
import ListLink from "@/components/list-link"
|
||||
import Page from "@/components/page"
|
||||
|
||||
export const Route = createFileRoute("/_authed/settings")({
|
||||
loader: () => ({
|
||||
header: {
|
||||
title: 'Settings',
|
||||
withBackButton: true,
|
||||
},
|
||||
}),
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <Page noPadding>
|
||||
<Box px='md' py='sm' style={{ borderBottom: '1px solid var(--mantine-color-default-border)' }}>
|
||||
<Title order={3}>Appearance</Title>
|
||||
<Stack>
|
||||
<AccentColorPicker />
|
||||
<ColorSchemePicker />
|
||||
</Stack>
|
||||
</Box>
|
||||
<ListLink
|
||||
label='Sign Out'
|
||||
to='/logout'
|
||||
Icon={SignOutIcon}
|
||||
/>
|
||||
</Page>
|
||||
}
|
||||
26
src/app/routes/_authed/teams.$teamId.tsx
Normal file
26
src/app/routes/_authed/teams.$teamId.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import Page from "@/components/page";
|
||||
import TeamProfile from "@/features/teams/components/team-profile";
|
||||
import { teamQueries } from "@/features/teams/queries";
|
||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/_authed/teams/$teamId")({
|
||||
beforeLoad: async ({ params, context }) => {
|
||||
const { queryClient } = context;
|
||||
const team = await queryClient.ensureQueryData(teamQueries.details(params.teamId))
|
||||
if (!team) throw redirect({ to: '/' });
|
||||
return { team }
|
||||
},
|
||||
loader: ({ params }) => ({
|
||||
header: {
|
||||
collapsed: true,
|
||||
withBackButton: true
|
||||
},
|
||||
refresh: {
|
||||
toRefresh: [teamQueries.details(params.teamId).queryKey],
|
||||
}
|
||||
}),
|
||||
component: () => {
|
||||
const { team } = Route.useRouteContext();
|
||||
return <Page><TeamProfile team={team} /></Page>
|
||||
},
|
||||
})
|
||||
67
src/app/routes/_authed/tournaments/$tournamentId.tsx
Normal file
67
src/app/routes/_authed/tournaments/$tournamentId.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { tournamentQueries } from '@/features/tournaments/queries';
|
||||
import Page from '@/components/page'
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Box, Button } from '@mantine/core';
|
||||
import { useSheet } from '@/hooks/use-sheet';
|
||||
import Sheet from '@/components/sheet/sheet';
|
||||
import { Tournament } from '@/features/tournaments/types';
|
||||
import { UsersIcon } from '@phosphor-icons/react';
|
||||
import ListButton from '@/components/list-button';
|
||||
import TeamList from '@/features/teams/components/team-list';
|
||||
|
||||
export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({
|
||||
beforeLoad: async ({ context, params }) => {
|
||||
const { queryClient } = context;
|
||||
await queryClient.ensureQueryData(tournamentQueries.details(params.tournamentId))
|
||||
},
|
||||
loader: ({ params }) => ({
|
||||
header: {
|
||||
collapsed: true,
|
||||
withBackButton: true
|
||||
},
|
||||
refresh: {
|
||||
toRefresh: tournamentQueries.details(params.tournamentId).queryKey,
|
||||
}
|
||||
}),
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { data: tournament } = useQuery(tournamentQueries.details(Route.useParams().tournamentId));
|
||||
|
||||
const sheet = useSheet()
|
||||
|
||||
return <Page noPadding>
|
||||
<Box mt='xl' p='md'>
|
||||
|
||||
<h3 style={{ marginTop: 0 }}>
|
||||
{tournament?.name}
|
||||
</h3>
|
||||
|
||||
<Button onClick={() => sheet.open()}>
|
||||
View Teams
|
||||
</Button>
|
||||
|
||||
</Box>
|
||||
|
||||
<ListButton
|
||||
label='Teams'
|
||||
onClick={() => sheet.open()}
|
||||
Icon={UsersIcon}
|
||||
/>
|
||||
|
||||
<Sheet
|
||||
{...sheet.props}
|
||||
title='Teams'
|
||||
>
|
||||
<TeamDrawer tournament={tournament!} />
|
||||
</Sheet>
|
||||
</Page>
|
||||
}
|
||||
|
||||
const TeamDrawer = ({ tournament }: { tournament: Tournament }) => {
|
||||
return (
|
||||
<TeamList teams={tournament?.teams!} />
|
||||
);
|
||||
}
|
||||
54
src/app/routes/_authed/tournaments/index.tsx
Normal file
54
src/app/routes/_authed/tournaments/index.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import Page from '@/components/page'
|
||||
import { Button, Stack } from '@mantine/core'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { TournamentCard } from '@/features/tournaments/components/tournament-card'
|
||||
import { tournamentQueries } from '@/features/tournaments/queries'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useAuth } from '@/contexts/auth-context'
|
||||
import { useSheet } from '@/hooks/use-sheet'
|
||||
import Sheet from '@/components/sheet/sheet'
|
||||
import CreateTournament from '@/features/admin/components/create-tournament'
|
||||
import { PlusIcon } from '@phosphor-icons/react'
|
||||
|
||||
export const Route = createFileRoute('/_authed/tournaments/')({
|
||||
beforeLoad: async ({ context }) => {
|
||||
const { queryClient } = context;
|
||||
await queryClient.ensureQueryData(tournamentQueries.list())
|
||||
},
|
||||
loader: () => ({
|
||||
header: {
|
||||
withBackButton: true,
|
||||
title: 'Tournaments',
|
||||
},
|
||||
refresh: {
|
||||
toRefresh: tournamentQueries.list().queryKey,
|
||||
}
|
||||
}),
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { data: tournaments } = useQuery(tournamentQueries.list());
|
||||
const { roles } = useAuth();
|
||||
const sheet = useSheet();
|
||||
|
||||
return <Page>
|
||||
<Stack>
|
||||
{
|
||||
roles?.includes("Admin") ? (
|
||||
<>
|
||||
<Button leftSection={<PlusIcon />} variant='subtle' onClick={sheet.open}>Create Tournament</Button>
|
||||
<Sheet {...sheet.props} title='Create Tournament'>
|
||||
<CreateTournament close={sheet.close} />
|
||||
</Sheet>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
tournaments?.map((tournament: any) => (
|
||||
<TournamentCard key={tournament.id} tournament={tournament} />
|
||||
))
|
||||
}
|
||||
</Stack>
|
||||
</Page>
|
||||
}
|
||||
19
src/app/routes/api/auth.$.ts
Normal file
19
src/app/routes/api/auth.$.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// API file that handles all supertokens auth routes
|
||||
import { createServerFileRoute } from '@tanstack/react-start/server';
|
||||
import { handleAuthAPIRequest } from 'supertokens-node/custom'
|
||||
import { ensureSuperTokensBackend } from '@/lib/supertokens/server'
|
||||
|
||||
ensureSuperTokensBackend();
|
||||
|
||||
// forwards all supertokens api methods to our API
|
||||
const superTokensHandler = handleAuthAPIRequest();
|
||||
const handleRequest = async ({ request }: {request: Request}) => superTokensHandler(request);
|
||||
export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
|
||||
GET: handleRequest,
|
||||
POST: handleRequest,
|
||||
PUT: handleRequest,
|
||||
DELETE: handleRequest,
|
||||
PATCH: handleRequest,
|
||||
OPTIONS: handleRequest,
|
||||
HEAD: handleRequest,
|
||||
})
|
||||
67
src/app/routes/api/events.$.ts
Normal file
67
src/app/routes/api/events.$.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { createServerFileRoute } from "@tanstack/react-start/server";
|
||||
import { serverEvents, type ServerEvent } from "@/lib/events/emitter";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { superTokensRequestMiddleware } from "@/utils/supertokens";
|
||||
|
||||
export const ServerRoute = createServerFileRoute("/api/events/$").middleware([superTokensRequestMiddleware]).methods({
|
||||
GET: ({ request, context }) => {
|
||||
logger.info('ServerEvents | New connection', (context as any)?.userAuthId);
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
// Send initial connection messages
|
||||
const connectMessage = `data: ${JSON.stringify({ type: "connected" })}\n\n`;
|
||||
controller.enqueue(new TextEncoder().encode(connectMessage));
|
||||
|
||||
// Listen for events and broadcast to all connections
|
||||
const handleEvent = (event: ServerEvent) => {
|
||||
logger.info('ServerEvents | Event received', event);
|
||||
const message = `data: ${JSON.stringify(event)}\n\n`;
|
||||
try {
|
||||
controller.enqueue(new TextEncoder().encode(message));
|
||||
} catch (error) {
|
||||
logger.error("ServerEvents | Error sending SSE message", error);
|
||||
}
|
||||
};
|
||||
|
||||
serverEvents.on("test", handleEvent);
|
||||
|
||||
// Keep alive ping every 30 seconds
|
||||
const pingInterval = setInterval(() => {
|
||||
try {
|
||||
const pingMessage = `data: ${JSON.stringify({ type: "ping" })}\n\n`;
|
||||
controller.enqueue(new TextEncoder().encode(pingMessage));
|
||||
} catch (e) {
|
||||
clearInterval(pingInterval);
|
||||
controller.close();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
const cleanup = () => {
|
||||
serverEvents.off("test", handleEvent);
|
||||
clearInterval(pingInterval);
|
||||
try {
|
||||
logger.info('ServerEvents | Closing connection', (context as any)?.userAuthId);
|
||||
controller.close();
|
||||
} catch (e) {
|
||||
logger.error('ServerEvents | Error closing controller', e);
|
||||
}
|
||||
};
|
||||
|
||||
request.signal?.addEventListener("abort", cleanup);
|
||||
|
||||
return cleanup;
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Headers": "Cache-Control",
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
9
src/app/routes/api/test.ts
Normal file
9
src/app/routes/api/test.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createServerFileRoute } from '@tanstack/react-start/server';
|
||||
import { superTokensRequestMiddleware } from '@/utils/supertokens';
|
||||
|
||||
// Simple test route for testing the auth middleware
|
||||
export const ServerRoute = createServerFileRoute('/api/test').middleware([superTokensRequestMiddleware]).methods({
|
||||
GET: () => {
|
||||
return new Response('Hello from the authenticated API!')
|
||||
},
|
||||
})
|
||||
27
src/app/routes/login.tsx
Normal file
27
src/app/routes/login.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import LoginLayout from "@/features/login/components/layout";
|
||||
import LoginFlow from "@/features/login/components/login-flow";
|
||||
import { redirect, createFileRoute } from "@tanstack/react-router";
|
||||
import z from "zod";
|
||||
|
||||
const loginSearchSchema = z.object({
|
||||
stage: z.enum(['code', 'name']).optional(),
|
||||
number: z.string().optional(),
|
||||
callback: z.string().optional()
|
||||
});
|
||||
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
validateSearch: loginSearchSchema,
|
||||
beforeLoad: async ({ context }) => {
|
||||
if (context.auth?.user) {
|
||||
throw redirect({ to: '/' })
|
||||
}
|
||||
},
|
||||
component: () => {
|
||||
return (
|
||||
<LoginLayout>
|
||||
<LoginFlow />
|
||||
</LoginLayout>
|
||||
)
|
||||
}
|
||||
})
|
||||
14
src/app/routes/logout.tsx
Normal file
14
src/app/routes/logout.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { LoadingOverlay } from '@mantine/core'
|
||||
import { signOut } from 'supertokens-web-js/recipe/passwordless'
|
||||
import { redirect, createFileRoute } from '@tanstack/react-router'
|
||||
import { authQueryConfig, defaultAuthData } from '@/contexts/auth-context'
|
||||
|
||||
export const Route = createFileRoute('/logout')({
|
||||
preload: false,
|
||||
loader: async ({ context }) => {
|
||||
await context.queryClient.setQueryData(authQueryConfig.queryKey, defaultAuthData);
|
||||
await signOut();
|
||||
throw redirect({ to: '/login' });
|
||||
},
|
||||
pendingComponent: () => <LoadingOverlay visible />
|
||||
})
|
||||
2
src/app/tanstack-start.d.ts
vendored
Normal file
2
src/app/tanstack-start.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference types="vite/client" />
|
||||
import '../../.tanstack-start/server-routes/routeTree.gen'
|
||||
Reference in New Issue
Block a user