Unfinished

This commit is contained in:
Aron Malcher 2024-02-18 22:02:52 +01:00
parent 18c6535d1c
commit 6b388729d9
Signed by: aronmal
GPG key ID: 816B7707426FC612
25 changed files with 1598 additions and 2352 deletions

View file

@ -46,7 +46,7 @@ To get started with li'l Judd, follow the instructions below.
Create a `.env` file in the root directory and add the following variables:
```env
VITE_DISCORD_CLIENT=your_discord_client_id
VITE_DISCORD_CLIENT_ID=your_discord_client_id
VITE_DISCORD_CLIENT_SECRET=your_discord_client_secret
VITE_DISCORD_BOT_TOKEN=your_discord_bot_token
VITE_DISCORD_BOT_PERMISSIONS=18977581952080
@ -56,16 +56,14 @@ To get started with li'l Judd, follow the instructions below.
VITE_DATABASE_URL=your_database_url
```
Recieve your discord applications client id & secret, as well as the bot token from the [Applications dashboard](https://discord.com/developers/applications/).
Recieve your discord applications `CLIENT_ID` & `CLIENT_SECRET`, as well as the bot token from the [Applications dashboard](https://discord.com/developers/applications/).
How to generate your `VITE_AUTH_SECRET` with [`openssl rand -base64 32`](https://authjs.dev/reference/core#secret).
Your `VITE_AUTH_REDIRECT_URL` should look like this: `https://<hostname>/api/auth/callback/discord`.
Composite your `VITE_DATABASE_URL` like [`postgres://postgres:adminadmin@0.0.0.0:5432/db`](https://orm.drizzle.team/docs/get-started-postgresql#postgresjs).
#### Development
Specify `VITE_AUTH_REDIRECT_PROXY_URL` only if necessary, particularly when setting up a reverse proxy to test authentication with callbacks to your development box. [Auth.js Docs Reference](https://authjs.dev/reference/nextjs/#redirectproxyurl)
The duplicate `DATABASE_URL` is only needed for Drizzle Studio.
```

View file

@ -6,6 +6,6 @@ export default {
out: "./src/drizzle/migrations",
driver: "pg",
dbCredentials: {
connectionString: process.env.DATABASE_URL ?? "",
connectionString: process.env.DATABASE_URL!,
},
} satisfies Config;

View file

@ -13,9 +13,6 @@
"drizzle-studio": "drizzle-kit studio"
},
"dependencies": {
"@auth/core": "^0.19.0",
"@auth/drizzle-adapter": "^0.6.3",
"@auth/solid-start": "0.1.2",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
"@fortawesome/pro-light-svg-icons": "^6.5.1",
@ -23,29 +20,34 @@
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
"@fortawesome/pro-thin-svg-icons": "^6.5.1",
"@fortawesome/sharp-solid-svg-icons": "^6.5.1",
"@lucia-auth/adapter-drizzle": "^1.0.2",
"@paralleldrive/cuid2": "^2.2.2",
"@solidjs/meta": "^0.29.3",
"@solidjs/router": "^0.12.0",
"@solidjs/start": "^0.5.4",
"@solidjs/router": "^0.12.3",
"@solidjs/start": "^0.5.9",
"arctic": "^1.1.6",
"drizzle-orm": "^0.29.3",
"lucia": "^3.0.1",
"moment-timezone": "^0.5.45",
"openapi-fetch": "^0.8.2",
"openapi-fetch": "^0.9.1",
"postgres": "^3.4.3",
"solid-js": "^1.8.14",
"vinxi": "^0.2.1"
"solid-js": "^1.8.15",
"vinxi": "^0.3.3"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.21.0",
"dotenv": "^16.4.2",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"dotenv": "^16.4.4",
"drizzle-kit": "^0.20.14",
"drizzle-zod": "^0.5.1",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-solid": "^0.13.1",
"h3": "^1.10.1",
"openapi-typescript": "^6.7.4",
"pg": "^8.11.3",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"sass": "^1.70.0",
"sass": "^1.71.0",
"typescript": "^5.3.3",
"zod": "3.22.4"
},

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -79,7 +79,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
},
@ -113,7 +113,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -159,7 +159,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
},
@ -203,7 +203,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -252,7 +252,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -308,7 +308,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -374,7 +374,7 @@
},
"security": [
{
"api_key": []
"bot_token": []
}
]
}
@ -390,9 +390,6 @@
"items": {
"$ref": "#/components/schemas/guildConfig"
}
},
"accessToken": {
"type": "string"
}
}
},
@ -536,10 +533,10 @@
}
},
"securitySchemes": {
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "header"
"bot_token": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
}

View file

@ -6,17 +6,13 @@ import NavUser from "./NavUser";
export function Li(props: {
href: string;
action?: () => void;
rel?: string;
name?: string;
children?: JSX.Element;
}) {
return (
<li class="navElem flex-row thick">
<a
class="flex-row"
href={props.href}
onClick={() => props.action && props.action()}
>
<a class="flex-row" href={props.href} rel={props.rel}>
{props.children ?? <></>}
<Show when={props.name}>
<span>{props.name}</span>

View file

@ -1,50 +1,37 @@
import { getSession } from "@auth/solid-start";
import { signIn, signOut } from "@auth/solid-start/client";
import {
faArrowRightFromBracket,
faArrowRightToBracket,
faGear,
} from "@fortawesome/pro-regular-svg-icons";
import { eq } from "drizzle-orm";
import { User } from "lucia";
import { Show, createResource } from "solid-js";
import { getRequestEvent } from "solid-js/web";
import db from "~/drizzle";
import { users } from "~/drizzle/schema";
import { authOptions } from "~/server/auth";
import { FontAwesomeIcon } from "./FontAwesomeIcon";
import { Li } from "./NavBar";
const initialUser = {
id: "",
name: null as string | null,
email: "",
emailVerified: null as Date | null,
image: null as string | null,
};
async function getUser() {
async function getUser(): Promise<
| ({
success: false;
message: string;
// user?: undefined;
} & Partial<User>)
| ({
success: true;
message?: undefined;
} & User)
> {
"use server";
const event = getRequestEvent();
if (!event)
return { success: false, message: "No request event!", ...initialUser };
if (!event) return { success: false, message: "No request event!" };
const session = await getSession(event.request, authOptions);
if (!session?.user?.id)
return { success: false, message: "No user with id!", ...initialUser };
const pathname = new URL(event.request.url).pathname;
const { user } = event.nativeEvent.context;
if (!user) return { success: false, message: "User not logged in!" };
const user = (
await db
.selectDistinct()
.from(users)
.where(eq(users.id, session.user?.id))
.limit(1)
.execute()
)[0];
console.log("userInfo", pathname, "success");
console.log("userInfo", "success");
return { success: true, message: "", ...user };
return { success: true, ...user };
}
function NavUser() {
@ -55,16 +42,20 @@ function NavUser() {
return user;
});
const pfp = () => {
const thisUser = user();
if (!thisUser?.success) return "";
return thisUser.image
? `https://cdn.discordapp.com/avatars/${thisUser.discord_id}/${thisUser.image}.png`
: `https://cdn.discordapp.com/embed/avatars/${(parseInt(thisUser.discord_id) >> 22) % 6}.png`;
};
return (
<Show
when={user()?.id}
fallback={
<Li
href="#"
name="Login"
action={() => signIn("discord", { callbackUrl: "/config" })}
>
<Li href="/api/auth/login" name="Login" rel="external">
<FontAwesomeIcon
class="secondary"
icon={faArrowRightToBracket}
@ -75,11 +66,11 @@ function NavUser() {
>
<Li href="/config">
<div class="swap lower">
<img class="primary" src={user()?.image ?? ""} alt="User pfp" />
<img class="primary" src={pfp()} alt="User pfp" />
<FontAwesomeIcon class="secondary" icon={faGear} size="xl" />
</div>
</Li>
<Li href="#" action={() => signOut({ callbackUrl: "/" })}>
<Li href="/api/auth/logout" rel="external">
<FontAwesomeIcon icon={faArrowRightFromBracket} size="xl" />
</Li>
</Show>

View file

@ -1,10 +1,8 @@
import type { AdapterAccount } from "@auth/core/adapters";
import { relations } from "drizzle-orm";
import {
boolean,
integer,
pgTable,
primaryKey,
serial,
smallint,
text,
@ -13,56 +11,31 @@ import {
} from "drizzle-orm/pg-core";
export const users = pgTable("user", {
id: text("id").notNull().primaryKey(),
id: varchar("id", { length: 24 }).primaryKey(),
discord_id: text("discord_id").notNull(),
name: text("name"),
email: text("email").notNull(),
emailVerified: timestamp("emailVerified", { mode: "date" }),
image: text("image"),
});
export const accounts = pgTable(
"account",
{
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
type: text("type").$type<AdapterAccount["type"]>().notNull(),
provider: text("provider").notNull(),
providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
expires_at: integer("expires_at"),
token_type: text("token_type"),
scope: text("scope"),
id_token: text("id_token"),
session_state: text("session_state"),
},
(account) => ({
compoundKey: primaryKey({
columns: [account.provider, account.providerAccountId],
}),
}),
);
export const sessions = pgTable("session", {
sessionToken: text("sessionToken").notNull().primaryKey(),
userId: text("userId")
id: varchar("id", { length: 24 }).primaryKey(),
userId: varchar("user_id", { length: 24 })
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
expiresAt: timestamp("expires_at", {
withTimezone: true,
mode: "date",
}).notNull(),
});
export const verificationTokens = pgTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(vt) => ({
compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
}),
);
export const discordTokens = pgTable("tokens", {
userId: varchar("user_id", { length: 24 })
.primaryKey()
.references(() => users.id, { onDelete: "cascade" }),
refreshToken: text("refresh_token").notNull(),
accessToken: text("access_token").notNull(),
expiresAt: timestamp("expires_at", { mode: "date" }).notNull(),
});
export const matchPlannings = pgTable("match_planning", {
id: serial("id").primaryKey(),

View file

@ -1,3 +1,3 @@
import { mount, StartClient } from "@solidjs/start/client";
mount(() => <StartClient />, document.getElementById("app"));
mount(() => <StartClient />, document.getElementById("app")!);

46
src/lib/auth.ts Normal file
View file

@ -0,0 +1,46 @@
import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle";
import { Discord } from "arctic";
import { PgColumn, PgTableWithColumns } from "drizzle-orm/pg-core";
import { Lucia } from "lucia";
import db from "~/drizzle";
import { sessions, users } from "~/drizzle/schema";
const adapter = new DrizzlePostgreSQLAdapter(db, sessions, users);
export const lucia = new Lucia(adapter, {
sessionCookie: {
attributes: {
// set to `true` when using HTTPS
secure: import.meta.env.PROD,
},
},
getUserAttributes: (attributes) => attributes,
});
declare module "lucia" {
// eslint-disable-next-line no-unused-vars
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
}
}
type GetColumns<T> =
T extends PgTableWithColumns<infer First> ? First["columns"] : never;
type ExtractDataTypes<T> = {
[K in keyof T]: T[K] extends PgColumn<infer DataType, any, any>
? DataType["data"]
: never;
};
interface DatabaseUserAttributes
extends ExtractDataTypes<GetColumns<typeof users>> {
warst: string;
}
export const discord = new Discord(
import.meta.env.VITE_DISCORD_CLIENT_ID,
import.meta.env.VITE_DISCORD_CLIENT_SECRET,
import.meta.env.VITE_AUTH_REDIRECT_URL,
);

54
src/middleware.ts Normal file
View file

@ -0,0 +1,54 @@
import { createMiddleware } from "@solidjs/start/middleware";
import { Session, User, verifyRequestOrigin } from "lucia";
import { appendHeader, getCookie, getHeader } from "vinxi/http";
import { lucia } from "./lib/auth";
export default createMiddleware({
onRequest: async (event) => {
if (event.nativeEvent.node.req.method !== "GET") {
const originHeader = getHeader(event, "Origin") ?? null;
const hostHeader = getHeader(event, "Host") ?? null;
if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader])
) {
event.nativeEvent.node.res.writeHead(403).end();
return;
}
}
const sessionId = getCookie(event, lucia.sessionCookieName) ?? null;
if (!sessionId) {
event.nativeEvent.context.session = null;
event.nativeEvent.context.user = null;
return;
}
const { session, user } = await lucia.validateSession(sessionId);
if (session && session.fresh) {
appendHeader(
event,
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
}
if (!session) {
appendHeader(
event,
"Set-Cookie",
lucia.createBlankSessionCookie().serialize(),
);
}
event.nativeEvent.context.session = session;
event.nativeEvent.context.user = user;
},
});
declare module "h3" {
// eslint-disable-next-line no-unused-vars
interface H3EventContext {
user: User | null;
session: Session | null;
}
}

View file

@ -1,4 +0,0 @@
import { SolidAuth } from "@auth/solid-start"
import { authOptions } from "~/server/auth"
export const { GET, POST } = SolidAuth(authOptions)

View file

@ -0,0 +1,132 @@
import { createId } from "@paralleldrive/cuid2";
import { APIEvent } from "@solidjs/start/server/types";
import { OAuth2RequestError } from "arctic";
import { eq } from "drizzle-orm";
import createClient from "openapi-fetch";
import { getCookie, setCookie } from "vinxi/http";
import db from "~/drizzle";
import { discordTokens, users } from "~/drizzle/schema";
import { discord, lucia } from "~/lib/auth";
import { paths } from "~/types/discord";
export async function GET(event: APIEvent): Promise<Response> {
const code = new URL(event.request.url).searchParams.get("code");
const state = new URL(event.request.url).searchParams.get("state");
const error = new URL(event.request.url).searchParams.get("error");
const error_description = new URL(event.request.url).searchParams.get(
"error_description",
);
if (error)
switch (error) {
case "access_denied":
return new Response(null, {
status: 302,
headers: { Location: "/" },
});
default:
console.log("Discord oauth error:", error_description);
return new Response(decodeURI(error_description ?? ""), {
status: 400,
});
}
const storedState = getCookie("discord_oauth_state") ?? null;
if (!code || !state || !storedState || state !== storedState) {
return new Response(null, {
status: 400,
});
}
try {
const tokens = await discord.validateAuthorizationCode(code);
const { GET } = createClient<paths>({
baseUrl: "https://discord.com/api/v10",
});
const discordUserResponse = await GET("/users/@me", {
headers: { Authorization: `Bearer ${tokens.accessToken}` },
});
if (discordUserResponse.error) throw discordUserResponse.error;
const discordUser = discordUserResponse.data;
const existingUser = await db.query.users
.findFirst({
where: eq(users.discord_id, discordUser.id),
})
.execute();
if (existingUser) {
const session = await lucia.createSession(
existingUser.id,
{},
{ sessionId: createId() },
);
const sessionCookie = lucia.createSessionCookie(session.id);
console.log(sessionCookie);
setCookie(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
await db
.update(users)
.set({
name: discordUser.global_name,
image: discordUser.avatar,
})
.where(eq(users.discord_id, discordUser.id))
.returning()
.execute();
return new Response(null, {
status: 302,
headers: { Location: "/config" },
});
}
const userId = createId();
await db.insert(users).values({
id: userId,
discord_id: discordUser.id,
name: discordUser.global_name,
image: discordUser.avatar,
});
await db
.insert(discordTokens)
.values({
userId,
accessToken: tokens.accessToken,
expiresAt: tokens.accessTokenExpiresAt,
refreshToken: tokens.refreshToken,
})
.returning()
.execute();
console.log(createId(), createId(), { warst: createId() });
const session = await lucia.createSession(
userId,
{},
{ sessionId: createId() },
);
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(
sessionCookie.name,
sessionCookie.value,
sessionCookie.attributes,
);
return new Response(null, {
status: 302,
headers: { Location: "/config" },
});
} catch (e) {
// the specific error message depends on the provider
if (e instanceof OAuth2RequestError) {
// invalid code
return new Response(null, {
status: 400,
});
}
console.error("Unknown error on callback.");
console.error(e);
return new Response(null, {
status: 500,
});
}
}

View file

@ -0,0 +1,24 @@
import { APIEvent } from "@solidjs/start/server/types";
import { generateState } from "arctic";
import { setCookie } from "vinxi/http";
import { discord } from "~/lib/auth";
export async function GET(event: APIEvent) {
const state = generateState();
const url = await discord.createAuthorizationURL(state, {
scopes: ["identify", "guilds", "guilds.members.read"],
});
setCookie(event, "discord_oauth_state", state, {
path: "/",
secure: import.meta.env.PROD,
httpOnly: true,
maxAge: 60 * 10,
sameSite: "lax",
});
return new Response(null, {
status: 302,
headers: { Location: url.toString() },
});
}

View file

@ -0,0 +1,19 @@
import { APIEvent } from "@solidjs/start/server/types";
import { appendHeader } from "vinxi/http";
import { lucia } from "~/lib/auth";
export const GET = async (event: APIEvent) => {
if (!event.nativeEvent.context.session) {
return new Error("Unauthorized");
}
await lucia.invalidateSession(event.nativeEvent.context.session.id);
appendHeader(
event,
"Set-Cookie",
lucia.createBlankSessionCookie().serialize(),
);
return new Response(null, {
status: 302,
headers: { Location: "/" },
});
};

View file

@ -4,6 +4,19 @@ import db from "~/drizzle";
import { guilds } from "~/drizzle/schema";
export const GET = async ({ params }: APIEvent) => {
if (params.guildId === "boot") {
const guilds = await db.query.guilds
.findMany({
with: {
timePlanning: { with: { messages: true } },
matches: true,
},
})
.execute();
return { guilds };
}
const guild = await db.query.guilds
.findFirst({
where: eq(guilds.id, params.guildId),

View file

@ -1,4 +1,3 @@
import { getSession } from "@auth/solid-start";
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
import { useLocation, useNavigate, useParams } from "@solidjs/router";
import { eq } from "drizzle-orm";
@ -16,8 +15,7 @@ import { getRequestEvent } from "solid-js/web";
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
import Layout from "~/components/Layout";
import db from "~/drizzle";
import { accounts } from "~/drizzle/schema";
import { authOptions } from "~/server/auth";
import { discordTokens } from "~/drizzle/schema";
import { paths } from "~/types/discord";
import "../../styles/pages/config.scss";
@ -47,26 +45,21 @@ const getPayload = async (
if (!event) return { success: false, message: "No request event!" };
const pathname = new URL(event.request.url).pathname;
const session = await getSession(event.request, authOptions);
if (!session?.user?.id)
return { success: false, message: "No user with id!" };
const { user } = event.nativeEvent.context;
if (!user) return { success: false, message: "User not logged in!" };
const { DISCORD_ACCESS_TOKEN } = (
await db
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
.from(accounts)
.where(eq(accounts.userId, session.user?.id))
.limit(1)
.execute()
)[0];
if (!DISCORD_ACCESS_TOKEN)
return { success: false, message: "No discord access token!" };
const tokens = await db.query.discordTokens
.findFirst({
where: eq(discordTokens.userId, user.id),
})
.execute();
if (!tokens) return { success: false, message: "No discord access token!" };
const { GET } = createClient<paths>({
baseUrl: "https://discord.com/api/v10",
});
const guildsRequest = await GET("/users/@me/guilds", {
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
headers: { Authorization: `Bearer ${tokens.accessToken}` },
});
const channelsRequest = await GET("/guilds/{guild_id}/channels", {
params: {

View file

@ -1,4 +1,3 @@
import { getSession } from "@auth/solid-start";
import {
faBadgeCheck,
faCircleExclamation,
@ -12,8 +11,7 @@ import { getRequestEvent } from "solid-js/web";
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
import Layout from "~/components/Layout";
import db from "~/drizzle";
import { accounts } from "~/drizzle/schema";
import { authOptions } from "~/server/auth";
import { discordTokens } from "~/drizzle/schema";
import { paths } from "~/types/discord";
import "../../styles/pages/config.scss";
@ -36,26 +34,21 @@ const getPayload = async (): Promise<
if (!event) return { success: false, message: "No request event!" };
const pathname = new URL(event.request.url).pathname;
const session = await getSession(event.request, authOptions);
if (!session?.user?.id)
return { success: false, message: "No user with id!" };
const { user } = event.nativeEvent.context;
if (!user) return { success: false, message: "User not logged in!" };
const { DISCORD_ACCESS_TOKEN } = (
await db
.selectDistinct({ DISCORD_ACCESS_TOKEN: accounts.access_token })
.from(accounts)
.where(eq(accounts.userId, session.user?.id))
.limit(1)
.execute()
)[0];
if (!DISCORD_ACCESS_TOKEN)
return { success: false, message: "No discord access token!" };
const tokens = await db.query.discordTokens
.findFirst({
where: eq(discordTokens.userId, user.id),
})
.execute();
if (!tokens) return { success: false, message: "No discord access token!" };
const { GET } = createClient<paths>({
baseUrl: "https://discord.com/api/v10",
});
const { data: guilds, error } = await GET("/users/@me/guilds", {
headers: { Authorization: `Bearer ${DISCORD_ACCESS_TOKEN}` },
headers: { Authorization: `Bearer ${tokens.accessToken}` },
});
if (error) {

View file

@ -1,38 +0,0 @@
import Discord from "@auth/core/providers/discord";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { type SolidAuthConfig } from "@auth/solid-start";
import db from "~/drizzle";
export const authOptions: SolidAuthConfig = {
providers: [
{
...Discord({
clientId: import.meta.env.VITE_DISCORD_CLIENT_ID,
clientSecret: import.meta.env.VITE_DISCORD_CLIENT_SECRET,
}),
authorization:
"https://discord.com/api/oauth2/authorize?scope=identify+email+guilds+guilds.members.read",
},
],
adapter: DrizzleAdapter(db),
secret: import.meta.env.VITE_AUTH_SECRET,
callbacks: {
// @ts-ignore
session: ({ session, user }) => {
if (session?.user) {
session.user.id = user.id;
}
return session;
},
},
pages: {
// signIn: "/signin",
// signOut: "/signout",
// error: '/auth/error', // Error code passed in query string as ?error=
// verifyRequest: '/auth/verify-request', // (used for check email message)
// newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
},
redirectProxyUrl: import.meta.env.DEV
? import.meta.env.VITE_AUTH_REDIRECT_PROXY_URL
: undefined,
};

View file

@ -1,3 +1,7 @@
import { defineConfig } from "@solidjs/start/config";
export default defineConfig({});
export default defineConfig({
start: {
middleware: "./src/middleware.ts",
},
});