Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (h *Handler) RegisterEndpoint(w http.ResponseWriter, r *http.Request) {

type loginRequest struct {
Email string `json:"email" validate:"required,email,max=254"`
Password string `json:"password" validate:"min=8"`
Password string `json:"password" validate:"required"`
}

var dummyHash, _ = hashPassword("dummy-password-for-timing")
Expand Down
21 changes: 19 additions & 2 deletions web/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
MutationCache,
QueryCache,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { routeTree } from "./routeTree.gen";
import { meQuery } from "@/api/auth/me";
import "./index.css";

const queryClient = new QueryClient();
function handleUnauthorized(error: unknown) {
if ((error as { status?: number } | null)?.status !== 401) return;
if (!queryClient.getQueryData(meQuery.queryKey)) return;

queryClient.setQueryData(meQuery.queryKey, null);
router.invalidate();
}

const queryClient = new QueryClient({
queryCache: new QueryCache({ onError: handleUnauthorized }),
mutationCache: new MutationCache({ onError: handleUnauthorized }),
});
const router = createRouter({ routeTree, context: { queryClient } });

declare module "@tanstack/react-router" {
Expand Down
94 changes: 59 additions & 35 deletions web/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,41 @@

import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as GuestRouteImport } from './routes/_guest'
import { Route as ClientRouteRouteImport } from './routes/client/route'
import { Route as authLoginRouteImport } from './routes/(auth)/login'
import { Route as authLogoutRouteImport } from './routes/(auth)/logout'
import { Route as authRegisterRouteImport } from './routes/(auth)/register'
import { Route as GuestLoginRouteImport } from './routes/_guest/login'
import { Route as GuestRegisterRouteImport } from './routes/_guest/register'
import { Route as ClientIndexRouteImport } from './routes/client/index'

const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const GuestRoute = GuestRouteImport.update({
id: '/_guest',
getParentRoute: () => rootRouteImport,
} as any)
const ClientRouteRoute = ClientRouteRouteImport.update({
id: '/client',
path: '/client',
getParentRoute: () => rootRouteImport,
} as any)
const authLoginRoute = authLoginRouteImport.update({
id: '/(auth)/login',
path: '/login',
getParentRoute: () => rootRouteImport,
} as any)
const authLogoutRoute = authLogoutRouteImport.update({
id: '/(auth)/logout',
path: '/logout',
getParentRoute: () => rootRouteImport,
} as any)
const authRegisterRoute = authRegisterRouteImport.update({
id: '/(auth)/register',
const GuestLoginRoute = GuestLoginRouteImport.update({
id: '/login',
path: '/login',
getParentRoute: () => GuestRoute,
} as any)
const GuestRegisterRoute = GuestRegisterRouteImport.update({
id: '/register',
path: '/register',
getParentRoute: () => rootRouteImport,
getParentRoute: () => GuestRoute,
} as any)
const ClientIndexRoute = ClientIndexRouteImport.update({
id: '/',
Expand All @@ -50,48 +55,49 @@ const ClientIndexRoute = ClientIndexRouteImport.update({
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/client': typeof ClientRouteRouteWithChildren
'/login': typeof authLoginRoute
'/logout': typeof authLogoutRoute
'/register': typeof authRegisterRoute
'/login': typeof GuestLoginRoute
'/register': typeof GuestRegisterRoute
'/client/': typeof ClientIndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/login': typeof authLoginRoute
'/logout': typeof authLogoutRoute
'/register': typeof authRegisterRoute
'/login': typeof GuestLoginRoute
'/register': typeof GuestRegisterRoute
'/client': typeof ClientIndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/client': typeof ClientRouteRouteWithChildren
'/(auth)/login': typeof authLoginRoute
'/_guest': typeof GuestRouteWithChildren
'/(auth)/logout': typeof authLogoutRoute
'/(auth)/register': typeof authRegisterRoute
'/_guest/login': typeof GuestLoginRoute
'/_guest/register': typeof GuestRegisterRoute
'/client/': typeof ClientIndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/client' | '/login' | '/logout' | '/register' | '/client/'
fullPaths: '/' | '/client' | '/logout' | '/login' | '/register' | '/client/'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/login' | '/logout' | '/register' | '/client'
to: '/' | '/logout' | '/login' | '/register' | '/client'
id:
| '__root__'
| '/'
| '/client'
| '/(auth)/login'
| '/_guest'
| '/(auth)/logout'
| '/(auth)/register'
| '/_guest/login'
| '/_guest/register'
| '/client/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
ClientRouteRoute: typeof ClientRouteRouteWithChildren
authLoginRoute: typeof authLoginRoute
GuestRoute: typeof GuestRouteWithChildren
authLogoutRoute: typeof authLogoutRoute
authRegisterRoute: typeof authRegisterRoute
}

declare module '@tanstack/react-router' {
Expand All @@ -103,33 +109,40 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/_guest': {
id: '/_guest'
path: ''
fullPath: '/'
preLoaderRoute: typeof GuestRouteImport
parentRoute: typeof rootRouteImport
}
'/client': {
id: '/client'
path: '/client'
fullPath: '/client'
preLoaderRoute: typeof ClientRouteRouteImport
parentRoute: typeof rootRouteImport
}
'/(auth)/login': {
id: '/(auth)/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof authLoginRouteImport
parentRoute: typeof rootRouteImport
}
'/(auth)/logout': {
id: '/(auth)/logout'
path: '/logout'
fullPath: '/logout'
preLoaderRoute: typeof authLogoutRouteImport
parentRoute: typeof rootRouteImport
}
'/(auth)/register': {
id: '/(auth)/register'
'/_guest/login': {
id: '/_guest/login'
path: '/login'
fullPath: '/login'
preLoaderRoute: typeof GuestLoginRouteImport
parentRoute: typeof GuestRoute
}
'/_guest/register': {
id: '/_guest/register'
path: '/register'
fullPath: '/register'
preLoaderRoute: typeof authRegisterRouteImport
parentRoute: typeof rootRouteImport
preLoaderRoute: typeof GuestRegisterRouteImport
parentRoute: typeof GuestRoute
}
'/client/': {
id: '/client/'
Expand All @@ -153,12 +166,23 @@ const ClientRouteRouteWithChildren = ClientRouteRoute._addFileChildren(
ClientRouteRouteChildren,
)

interface GuestRouteChildren {
GuestLoginRoute: typeof GuestLoginRoute
GuestRegisterRoute: typeof GuestRegisterRoute
}

const GuestRouteChildren: GuestRouteChildren = {
GuestLoginRoute: GuestLoginRoute,
GuestRegisterRoute: GuestRegisterRoute,
}

const GuestRouteWithChildren = GuestRoute._addFileChildren(GuestRouteChildren)

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
ClientRouteRoute: ClientRouteRouteWithChildren,
authLoginRoute: authLoginRoute,
GuestRoute: GuestRouteWithChildren,
authLogoutRoute: authLogoutRoute,
authRegisterRoute: authRegisterRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
Expand Down
13 changes: 13 additions & 0 deletions web/src/routes/_guest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";

import { meQuery } from "@/api/auth/me";

export const Route = createFileRoute("/_guest")({
beforeLoad: async ({ context }) => {
const user = await context.queryClient.query(meQuery);
if (user) {
throw redirect({ to: "/client" });
}
},
component: () => <Outlet />,
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { AuthLayout } from "@/components/auth/auth-layout";
import { StatefulButton, type ButtonState } from "@/components/motion/button/stateful";
import { Input } from "@/components/motion/input";

export const Route = createFileRoute("/(auth)/login")({
export const Route = createFileRoute("/_guest/login")({
validateSearch: (search: Record<string, unknown>): { redirect?: string } => ({
redirect: typeof search.redirect === "string" ? search.redirect : undefined,
}),
Expand All @@ -21,7 +21,7 @@ export const Route = createFileRoute("/(auth)/login")({
const loginSchema = z
.object({
email: z.email("Enter a valid email").max(254),
password: z.string().min(8, "Password must be at least 8 characters"),
password: z.string().min(1, "Password is required"),
});

function Login() {
Expand Down Expand Up @@ -58,7 +58,7 @@ function Login() {
const { fieldErrors: serverFieldErrors, formError } = parseServerError(
login.error,
{
409: { email: "An account with this email already exists" },
401: "Invalid email or password",
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { Checkbox } from "@/components/motion/checkbox";
import { Input } from "@/components/motion/input";

export const Route = createFileRoute("/(auth)/register")({
export const Route = createFileRoute("/_guest/register")({
component: Register,
});

Expand Down
Loading