mirror of
https://github.com/SrIzan10/next-auth.git
synced 2026-05-01 10:55:20 +00:00
feat(solid-start): introduce @auth/solid-start package/example (#6158)
* feat: add solid framework package and example * solidstart docs * Update 02-oauth-tutorial.mdx * minor docs fixes * Fix sidebar typo * Update sync.yml * Update sidebars.js * minor fixes * fix deps * upgrade auth example * Update root.tsx * Update NavBar.tsx * Update Protected.tsx * protected * move example * Update sidebars.js Co-authored-by: Balázs Orbán <info@balazsorban.com> Co-authored-by: Nico Domino <yo@ndo.dev>
This commit is contained in:
72
apps/examples/solid-start/src/components/NavBar/NavBar.tsx
Normal file
72
apps/examples/solid-start/src/components/NavBar/NavBar.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Match, Show, Switch, type Component } from "solid-js";
|
||||
import { createServerData$ } from "solid-start/server";
|
||||
import { authOpts } from "~/routes/api/auth/[...solidauth]";
|
||||
import { signIn, signOut } from "@solid-auth/next/client";
|
||||
import { getSession } from "@solid-auth/next";
|
||||
import { A } from "solid-start";
|
||||
|
||||
interface INavBarProps {}
|
||||
|
||||
const NavBar: Component<INavBarProps> = () => {
|
||||
const session = useSession();
|
||||
return (
|
||||
<header class="flex flex-col w-full gap-2 fixed left-2/4 right-2/4 -translate-x-2/4 items-center">
|
||||
<nav class="w-[70vw] sm:w-2/4 lg:w-[40%] p-5 bg-[#0000000d] flex items-center justify-between rounded-lg">
|
||||
<Show
|
||||
when={session()?.user}
|
||||
keyed
|
||||
fallback={
|
||||
<>
|
||||
<p class="text-lg font-semibold">You are not signed in</p>
|
||||
<button
|
||||
class="p-2.5 rounded-lg bg-[#346df1] text-white text-lg font-bold flex items-center justify-center"
|
||||
onClick={() => signIn("github")}
|
||||
>
|
||||
Sign in
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
{(us) => (
|
||||
<>
|
||||
<div class="flex gap-2 items-center">
|
||||
<Show when={us.image} keyed>
|
||||
{(im) => <img src={im} class="w-12 h-12 rounded-full" />}
|
||||
</Show>
|
||||
<div class="flex flex-col">
|
||||
<h3 class="font-bold text-lg">Signed in as</h3>
|
||||
<p class="text-lg font-semibold">{us.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => signOut()}
|
||||
class="text-[#555] font-semibold underline"
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
</nav>
|
||||
<div class="flex gap-2 items-center">
|
||||
<A class="text-blue-500 font-bold underline" href="/">
|
||||
Home
|
||||
</A>
|
||||
<A class="text-blue-500 font-bold underline" href="/protected">
|
||||
Protected
|
||||
</A>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavBar;
|
||||
|
||||
export const useSession = () => {
|
||||
return createServerData$(
|
||||
async (_, { request }) => {
|
||||
return await getSession(request, authOpts);
|
||||
},
|
||||
{ key: () => ["auth_user"] }
|
||||
);
|
||||
};
|
||||
1
apps/examples/solid-start/src/components/NavBar/index.ts
Normal file
1
apps/examples/solid-start/src/components/NavBar/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./NavBar";
|
||||
@@ -0,0 +1,37 @@
|
||||
import { type Session } from "@auth/core";
|
||||
import { getSession } from "@solid-auth/next";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useRouteData } from "solid-start";
|
||||
import { createServerData$, redirect } from "solid-start/server";
|
||||
import { authOpts } from "~/routes/api/auth/[...solidauth]";
|
||||
|
||||
const Protected = (Comp: IProtectedComponent) => {
|
||||
const routeData = () => {
|
||||
return createServerData$(
|
||||
async (_, event) => {
|
||||
const session = await getSession(event.request, authOpts);
|
||||
if (!session || !session.user) {
|
||||
throw redirect("/");
|
||||
}
|
||||
return session;
|
||||
},
|
||||
{ key: () => ["auth_user"] }
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
routeData,
|
||||
Page: () => {
|
||||
const session = useRouteData<typeof routeData>();
|
||||
return (
|
||||
<Show when={session()} keyed>
|
||||
{(sess) => <Comp {...sess} />}
|
||||
</Show>
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type IProtectedComponent = Component<Session>;
|
||||
|
||||
export default Protected;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from "./Protected";
|
||||
2
apps/examples/solid-start/src/components/index.ts
Normal file
2
apps/examples/solid-start/src/components/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as NavBar } from "./NavBar";
|
||||
export { default as Protected } from "./Protected";
|
||||
3
apps/examples/solid-start/src/entry-client.tsx
Normal file
3
apps/examples/solid-start/src/entry-client.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
import { mount, StartClient } from "solid-start/entry-client";
|
||||
|
||||
mount(() => <StartClient />, document);
|
||||
9
apps/examples/solid-start/src/entry-server.tsx
Normal file
9
apps/examples/solid-start/src/entry-server.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import {
|
||||
StartServer,
|
||||
createHandler,
|
||||
renderAsync,
|
||||
} from "solid-start/entry-server";
|
||||
|
||||
export default createHandler(
|
||||
renderAsync((event) => <StartServer event={event} />)
|
||||
);
|
||||
24
apps/examples/solid-start/src/env/client.ts
vendored
Normal file
24
apps/examples/solid-start/src/env/client.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { ZodFormattedError } from "zod";
|
||||
import { clientScheme } from "./schema";
|
||||
|
||||
export const formatErrors = (
|
||||
errors: ZodFormattedError<Map<string, string>, string>
|
||||
) =>
|
||||
Object.entries(errors)
|
||||
.map(([name, value]) => {
|
||||
if (value && "_errors" in value)
|
||||
return `${name}: ${value._errors.join(", ")}\n`;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const env = clientScheme.safeParse(import.meta.env);
|
||||
|
||||
if (env.success === false) {
|
||||
console.error(
|
||||
"❌ Invalid environment variables:\n",
|
||||
...formatErrors(env.error.format())
|
||||
);
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
|
||||
export const clientEnv = env.data;
|
||||
15
apps/examples/solid-start/src/env/schema.ts
vendored
Normal file
15
apps/examples/solid-start/src/env/schema.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const serverScheme = z.object({
|
||||
NODE_ENV: z
|
||||
.enum(["development", "production", "test"])
|
||||
.default("development"),
|
||||
GITHUB_ID: z.string(),
|
||||
GITHUB_SECRET: z.string(),
|
||||
AUTH_SECRET: z.string(),
|
||||
NEXTAUTH_URL: z.string().optional(),
|
||||
});
|
||||
|
||||
export const clientScheme = z.object({
|
||||
MODE: z.enum(["development", "production", "test"]).default("development"),
|
||||
});
|
||||
24
apps/examples/solid-start/src/env/server.ts
vendored
Normal file
24
apps/examples/solid-start/src/env/server.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { serverScheme } from "./schema";
|
||||
import type { ZodFormattedError } from "zod";
|
||||
|
||||
export const formatErrors = (
|
||||
errors: ZodFormattedError<Map<string, string>, string>
|
||||
) =>
|
||||
Object.entries(errors)
|
||||
.map(([name, value]) => {
|
||||
if (value && "_errors" in value)
|
||||
return `${name}: ${value._errors.join(", ")}\n`;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const env = serverScheme.safeParse(process.env);
|
||||
|
||||
if (env.success === false) {
|
||||
console.error(
|
||||
"❌ Invalid environment variables:\n",
|
||||
...formatErrors(env.error.format())
|
||||
);
|
||||
throw new Error("Invalid environment variables");
|
||||
}
|
||||
|
||||
export const serverEnv = env.data;
|
||||
3
apps/examples/solid-start/src/root.css
Normal file
3
apps/examples/solid-start/src/root.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
40
apps/examples/solid-start/src/root.tsx
Normal file
40
apps/examples/solid-start/src/root.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
// @refresh reload
|
||||
import "./root.css";
|
||||
import { Suspense } from "solid-js";
|
||||
import {
|
||||
Body,
|
||||
ErrorBoundary,
|
||||
FileRoutes,
|
||||
Head,
|
||||
Html,
|
||||
Meta,
|
||||
Routes,
|
||||
Scripts,
|
||||
Title,
|
||||
} from "solid-start";
|
||||
import { NavBar } from "./components";
|
||||
|
||||
export default function Root() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<Title>Create JD App</Title>
|
||||
<Meta charset="utf-8" />
|
||||
<Meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</Head>
|
||||
<Body>
|
||||
<Suspense>
|
||||
<NavBar />
|
||||
<div class="py-44 px-8">
|
||||
<ErrorBoundary>
|
||||
<Routes>
|
||||
<FileRoutes />
|
||||
</Routes>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</Suspense>
|
||||
<Scripts />
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { SolidAuth, type SolidAuthConfig } from "@solid-auth/next";
|
||||
import GitHub from "@auth/core/providers/github";
|
||||
import { serverEnv } from "~/env/server";
|
||||
import { type APIEvent } from "solid-start";
|
||||
|
||||
export const authOpts: SolidAuthConfig = {
|
||||
providers: [
|
||||
GitHub({
|
||||
clientId: serverEnv.GITHUB_ID,
|
||||
clientSecret: serverEnv.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
debug: false,
|
||||
};
|
||||
|
||||
export const { GET, POST } = SolidAuth(authOpts);
|
||||
44
apps/examples/solid-start/src/routes/index.tsx
Normal file
44
apps/examples/solid-start/src/routes/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { type ParentComponent } from "solid-js";
|
||||
import { A, Title, useRouteData } from "solid-start";
|
||||
import { createServerData$ } from "solid-start/server";
|
||||
import { authOpts } from "./api/auth/[...solidauth]";
|
||||
import { getSession } from "@solid-auth/next";
|
||||
|
||||
export const routeData = () => {
|
||||
return createServerData$(
|
||||
async (_, { request }) => {
|
||||
return await getSession(request, authOpts);
|
||||
},
|
||||
{ key: () => ["auth_user"] }
|
||||
);
|
||||
};
|
||||
const Home: ParentComponent = () => {
|
||||
const user = useRouteData<typeof routeData>();
|
||||
return (
|
||||
<>
|
||||
<Title>Create JD App</Title>
|
||||
<div class="flex flex-col gap-2 items-center">
|
||||
<h1 class="text-4xl font-bold">SolidStart Auth Example</h1>
|
||||
<p class="font-semibold text-md max-w-[40rem]">
|
||||
This is an example site to demonstrate how to use{" "}
|
||||
<A
|
||||
href="https://start.solidjs.com/getting-started/what-is-solidstart"
|
||||
class="text-blue-500 underline font-bold"
|
||||
>
|
||||
SolidStart
|
||||
</A>{" "}
|
||||
with{" "}
|
||||
<A
|
||||
href="https://authjs.dev/reference/solid-start/modules/main"
|
||||
class="text-blue-500 underline font-bold"
|
||||
>
|
||||
SolidStart Auth
|
||||
</A>{" "}
|
||||
for authentication.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
11
apps/examples/solid-start/src/routes/protected.tsx
Normal file
11
apps/examples/solid-start/src/routes/protected.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Protected } from "~/components";
|
||||
|
||||
export const { routeData, Page } = Protected((session) => {
|
||||
return (
|
||||
<main class="flex flex-col gap-2 items-center">
|
||||
<h1>This is a proteced route</h1>
|
||||
</main>
|
||||
);
|
||||
});
|
||||
|
||||
export default Page;
|
||||
Reference in New Issue
Block a user