Skip to content

Commit 5568611

Browse files
authored
chore: Updated with-auth example (#1928)
Co-authored-by: thomasbuilds <thomas.giudici@proton.me>
1 parent af520c5 commit 5568611

File tree

18 files changed

+359
-216
lines changed

18 files changed

+359
-216
lines changed

examples/with-auth/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SESSION_SECRET = myverylonguniquesecretthatkeepsthingssafe
2+
DISCORD_ID =
3+
DISCORD_SECRET =

examples/with-auth/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,31 @@ npm run dev
2323
npm run dev -- --open
2424
```
2525

26+
## Env Vars
27+
28+
Rename the example file and add your Discord OAuth credentials:
29+
30+
```bash
31+
# rename example environment file
32+
cp .env.example .env
33+
```
34+
35+
Edit `.env` with your values:
36+
37+
```dotenv
38+
DISCORD_ID=your-discord-client-id
39+
DISCORD_SECRET=your-discord-client-secret
40+
```
41+
42+
1. Create an application at [https://discord.com/developers/applications](https://discord.com/developers/applications) to obtain your client ID and secret.
43+
2. In the app's **OAuth2 → Redirects** settings, add:
44+
45+
```text
46+
http://localhost:3000/api/oauth/discord
47+
```
48+
49+
For more details on the [start-oauth](https://github.com/thomasbuilds/start-oauth) integration, see the repository.
50+
2651
## Building
2752

2853
Solid apps are built with _presets_, which optimise your project for deployment to different environments.

examples/with-auth/app.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
import { defineConfig } from "@solidjs/start/config";
2+
import tailwindcss from "@tailwindcss/vite";
23

3-
export default defineConfig({});
4+
export default defineConfig({
5+
vite: { plugins: [tailwindcss()] }
6+
});

examples/with-auth/package.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
"build": "vinxi build",
77
"start": "vinxi start"
88
},
9-
"devDependencies": {
10-
"@types/node": "^20.12.7"
11-
},
129
"dependencies": {
13-
"@solidjs/router": "^0.15.0",
14-
"@solidjs/start": "^1.1.0",
15-
"solid-js": "^1.9.5",
16-
"unstorage": "1.10.2",
17-
"vinxi": "^0.5.7"
10+
"@solidjs/router": "^0.15.3",
11+
"@solidjs/start": "^1.1.7",
12+
"solid-js": "^1.9.7",
13+
"start-oauth": "^1.2.4",
14+
"unstorage": "1.16.1",
15+
"vinxi": "^0.5.8"
16+
},
17+
"devDependencies": {
18+
"@tailwindcss/vite": "^4.1.11",
19+
"tailwindcss": "^4.1.11"
1820
},
1921
"engines": {
2022
"node": ">=22"

examples/with-auth/src/app.css

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1 @@
1-
body {
2-
font-family: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
3-
}
4-
5-
a {
6-
margin-right: 1rem;
7-
}
8-
9-
main {
10-
text-align: center;
11-
padding: 1em;
12-
margin: 0 auto;
13-
}
14-
15-
h1 {
16-
color: #335d92;
17-
text-transform: uppercase;
18-
font-size: 4rem;
19-
font-weight: 100;
20-
line-height: 1.1;
21-
margin: 4rem auto;
22-
max-width: 14rem;
23-
}
24-
25-
p {
26-
max-width: 14rem;
27-
margin: 2rem auto;
28-
line-height: 1.35;
29-
}
30-
31-
@media (min-width: 480px) {
32-
h1 {
33-
max-width: none;
34-
}
35-
36-
p {
37-
max-width: none;
38-
}
39-
}
1+
@import "tailwindcss";

examples/with-auth/src/app.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
// @refresh reload
2-
import { Router } from "@solidjs/router";
2+
import { type RouteDefinition, Router } from "@solidjs/router";
33
import { FileRoutes } from "@solidjs/start/router";
44
import { Suspense } from "solid-js";
5+
import { querySession } from "./lib";
6+
import Session from "./lib/Context";
7+
import Nav from "./components/Nav";
58
import "./app.css";
69

10+
export const route: RouteDefinition = {
11+
preload: ({ location }) => querySession(location.pathname)
12+
};
13+
714
export default function App() {
815
return (
916
<Router
1017
root={props => (
11-
<>
12-
<a href="/">Index</a>
13-
<a href="/about">About</a>
14-
<Suspense>{props.children}</Suspense>
15-
</>
18+
<Session>
19+
<Suspense>
20+
<Nav />
21+
{props.children}
22+
</Suspense>
23+
</Session>
1624
)}
1725
>
1826
<FileRoutes />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { createSignal } from "solid-js";
2+
3+
export default function Counter() {
4+
const [count, setCount] = createSignal(0);
5+
6+
return (
7+
<button
8+
class="w-[200px] rounded-full bg-gray-100 border-2 border-gray-300 focus:border-gray-400 active:border-gray-400 px-[2rem] py-[1rem]"
9+
onclick={() => setCount(prev => prev + 1)}
10+
>
11+
Clicks: {count()}
12+
</button>
13+
);
14+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { useMatch } from "@solidjs/router";
2+
import { Show } from "solid-js";
3+
import { useSession } from "~/lib/Context";
4+
5+
export default function Nav() {
6+
const { signedIn, signOut } = useSession();
7+
const isHome = useMatch(() => "/");
8+
const isAbout = useMatch(() => "/about");
9+
10+
return (
11+
<nav class="fixed top-0 left-0 w-full bg-sky-800 shadow-md z-50">
12+
<ul class="container mx-auto flex items-center p-3">
13+
<li
14+
class={`mx-2 sm:mx-6 border-b-2 text-white ${
15+
isHome() ? "border-sky-400" : "border-transparent hover:border-sky-500"
16+
}`}
17+
>
18+
<a href="/">Home</a>
19+
</li>
20+
<li
21+
class={`mx-2 sm:mx-6 border-b-2 text-white ${
22+
isAbout() ? "border-sky-400 " : "border-transparent hover:border-sky-500"
23+
}`}
24+
>
25+
<a href="/about">About</a>
26+
</li>
27+
<li class="ml-auto px-2 sm:px-6 text-white">
28+
<Show when={signedIn()} fallback={<a href="/login">Login</a>}>
29+
<button onclick={signOut} class="cursor-pointer">
30+
Logout
31+
</button>
32+
</Show>
33+
</li>
34+
</ul>
35+
</nav>
36+
);
37+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createContext, useContext, type ParentProps } from "solid-js";
2+
import { type AccessorWithLatest, useLocation, createAsync, useAction } from "@solidjs/router";
3+
import { querySession, logout } from ".";
4+
import type { Session } from "./server";
5+
6+
const Context = createContext<{
7+
session: AccessorWithLatest<Session | null | undefined>;
8+
signedIn: () => boolean;
9+
signOut: () => Promise<never>;
10+
}>();
11+
12+
export default function Session(props: ParentProps) {
13+
const location = useLocation();
14+
const session = createAsync(() => querySession(location.pathname), {
15+
deferStream: true
16+
});
17+
const signOut = useAction(logout);
18+
const signedIn = () => Boolean(session()?.id);
19+
20+
return (
21+
<Context.Provider value={{ session, signedIn, signOut }}>{props.children}</Context.Provider>
22+
);
23+
}
24+
25+
export function useSession() {
26+
const context = useContext(Context);
27+
if (!context) throw new Error("useSession must be used within Session context");
28+
return context;
29+
}

examples/with-auth/src/lib/db.ts

Lines changed: 22 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,28 @@
11
import { createStorage } from "unstorage";
22
import fsLiteDriver from "unstorage/drivers/fs-lite";
33

4-
type User = {
4+
interface User {
55
id: number;
6-
username: string;
7-
password: string;
8-
};
6+
email: string;
7+
password?: string;
8+
}
99

10-
const storage = createStorage({
11-
driver: fsLiteDriver({
12-
base: "./.data"
13-
})
14-
});
15-
storage.setItem("users:data", [{ id: 0, username: "kody", password: "twixrox" }]);
16-
storage.setItem("users:counter", 1);
10+
const storage = createStorage({ driver: fsLiteDriver({ base: "./.data" }) });
1711

18-
export const db = {
19-
user: {
20-
async create({ data }: { data: { username: string; password: string } }) {
21-
const [{ value: users }, { value: index }] = await storage.getItems(["users:data", "users:counter"]);
22-
const user = { ...data, id: index as number };
23-
await Promise.all([
24-
storage.setItem("users:data", [...(users as User[]), user]),
25-
storage.setItem("users:counter", index as number + 1)
26-
]);
27-
return user;
28-
},
29-
async findUnique({ where: { username = undefined, id = undefined } }: { where: { username?: string; id?: number } }) {
30-
const users = await storage.getItem("users:data") as User[];
31-
if (id !== undefined) {
32-
return users.find(user => user.id === id);
33-
} else {
34-
return users.find(user => user.username === username);
35-
}
36-
}
37-
}
38-
};
12+
export async function createUser(data: Pick<User, "email" | "password">) {
13+
const users = (await storage.getItem<User[]>("users:data")) ?? [];
14+
const counter = (await storage.getItem<number>("users:counter")) ?? 1;
15+
const user: User = { id: counter, ...data };
16+
await Promise.all([
17+
storage.setItem("users:data", [...users, user]),
18+
storage.setItem("users:counter", counter + 1)
19+
]);
20+
return user;
21+
}
22+
23+
export async function findUser({ email, id }: { email?: string; id?: number }) {
24+
const users = (await storage.getItem<User[]>("users:data")) ?? [];
25+
if (id) return users.find(u => u.id === id);
26+
if (email) return users.find(u => u.email === email);
27+
return undefined;
28+
}

0 commit comments

Comments
 (0)