Skip to content

Commit 6c586c2

Browse files
authored
Merge pull request #1 from ut-code/auth
passwordとGoogleでの認証
2 parents 28c9686 + 307c451 commit 6c586c2

File tree

21 files changed

+730
-244
lines changed

21 files changed

+730
-244
lines changed

bun.lock

Lines changed: 239 additions & 231 deletions
Large diffs are not rendered by default.

packages/client/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@
3939
"vite": "^7.0.5"
4040
},
4141
"dependencies": {
42-
"@packages/convex": "workspace:convex",
42+
"@auth/core": "^0.40.0",
43+
"@convex-dev/auth": "^0.0.87",
44+
"@mmailaender/convex-auth-svelte": "^0.0.2",
45+
"@packages/convex": "workspace:*",
4346
"@inlang/paraglide-js": "^2.2.0",
4447
"@playwright/test": "^1.54.1",
4548
"@sveltejs/adapter-static": "^3.0.8",

packages/client/src/components/chat/MessageInput.svelte

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts">
22
import { api, type Id } from "@packages/convex";
3-
import { useConvexClient } from "convex-svelte";
3+
import { useConvexClient, useQuery } from "convex-svelte";
44
55
interface Props {
66
channelId: Id<"channels">;
@@ -9,8 +9,16 @@
99
const { channelId }: Props = $props();
1010
1111
const convex = useConvexClient();
12+
const identity = useQuery(api.users.me, {});
13+
1214
let messageContent = $state("");
13-
let authorName = $state("ユーザー");
15+
let authorName = $state("");
16+
17+
$effect(() => {
18+
if (identity?.data && !authorName) {
19+
authorName = identity.data.name ?? identity.data.email ?? "匿名";
20+
}
21+
});
1422
1523
async function sendMessage() {
1624
if (!messageContent.trim()) return;

packages/client/src/hooks.server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { createConvexAuthHooks } from "@mmailaender/convex-auth-svelte/sveltekit/server";
12
import type { Handle } from "@sveltejs/kit";
3+
import { sequence } from "@sveltejs/kit/hooks";
4+
import { PUBLIC_CONVEX_URL } from "$lib/env";
25
import { paraglideMiddleware } from "$lib/paraglide/server";
36

47
const handleParaglide: Handle = ({ event, resolve }) =>
@@ -11,4 +14,6 @@ const handleParaglide: Handle = ({ event, resolve }) =>
1114
});
1215
});
1316

14-
export const handle: Handle = handleParaglide;
17+
const { handleAuth } = createConvexAuthHooks({ convexUrl: PUBLIC_CONVEX_URL });
18+
19+
export const handle: Handle = sequence(handleParaglide, handleAuth);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createConvexAuthHandlers } from "@mmailaender/convex-auth-svelte/sveltekit/server";
2+
import { PUBLIC_CONVEX_URL } from "$lib/env.ts";
3+
import type { LayoutServerLoad } from "./$types";
4+
5+
const { getAuthState } = createConvexAuthHandlers({
6+
convexUrl: PUBLIC_CONVEX_URL,
7+
});
8+
9+
export const load: LayoutServerLoad = async (event) => {
10+
const authState = await getAuthState(event);
11+
12+
return { authState };
13+
};
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<script lang="ts">
22
import "@@/global.css";
33
4+
import { setupConvexAuth } from "@mmailaender/convex-auth-svelte/sveltekit";
45
import { setupConvex } from "convex-svelte";
5-
import type { Snippet } from "svelte";
66
import { PUBLIC_CONVEX_URL } from "$lib/env.ts";
77
8-
type Props = {
9-
children: Snippet;
10-
};
11-
12-
const { children }: Props = $props();
8+
const { children, data } = $props();
139
1410
setupConvex(PUBLIC_CONVEX_URL);
11+
12+
setupConvexAuth({
13+
getServerState: () => data.authState,
14+
convexUrl: PUBLIC_CONVEX_URL,
15+
});
1516
</script>
1617

1718
{@render children()}
Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
<script lang="ts">
2+
import { useAuth } from "@mmailaender/convex-auth-svelte/sveltekit";
23
import ChatApp from "$components/chat/ChatApp.svelte";
4+
5+
const isAuthenticated = $derived(useAuth().isAuthenticated);
6+
const isLoading = $derived(useAuth().isLoading);
37
</script>
48

5-
<ChatApp />
9+
{#if isLoading}
10+
<div class="flex h-screen w-full items-center justify-center">
11+
<span class="loading loading-dots loading-lg"></span>
12+
</div>
13+
{:else if isAuthenticated}
14+
<ChatApp />
15+
{:else}
16+
<div class="hero bg-base-100 min-h-screen">
17+
<div class="hero-content text-center">
18+
<div class="max-w-md">
19+
<h1 class="text-7xl font-bold">Prism</h1>
20+
<p class="py-6">The ultimate chat tool for engineers.</p>
21+
<a href="/signin" class="btn btn-primary">Get Started</a>
22+
</div>
23+
</div>
24+
</div>
25+
{/if}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<script lang="ts">
2+
import { useAuth } from "@mmailaender/convex-auth-svelte/sveltekit";
3+
4+
const { signIn, isLoading } = useAuth();
5+
6+
let step = $state<"forgot" | { email: string }>("forgot");
7+
</script>
8+
9+
<div class="hero bg-base-200 min-h-screen">
10+
<div class="card bg-base-100 w-full max-w-sm shrink-0 shadow-2xl">
11+
{#if step === "forgot"}
12+
<form
13+
class="card-body"
14+
onsubmit={(event) => {
15+
event.preventDefault();
16+
const formData = new FormData(event.currentTarget);
17+
void signIn("password", formData).then(
18+
() => (step = { email: formData.get("email") as string }),
19+
);
20+
}}
21+
>
22+
<h1 class="text-2xl font-bold">Reset Password</h1>
23+
<p class="text-base-content/70 text-sm">
24+
Enter your email address and we will send you a code to reset your
25+
password.
26+
</p>
27+
<div class="form-control">
28+
<label class="label" for="email">
29+
<span class="label-text">Email</span>
30+
</label>
31+
<input
32+
id="email"
33+
name="email"
34+
placeholder="email"
35+
class="input input-bordered"
36+
required
37+
/>
38+
<input name="flow" type="hidden" value="reset" />
39+
</div>
40+
<div class="form-control mt-6">
41+
<button type="submit" class="btn btn-primary" disabled={isLoading}>
42+
{#if isLoading}
43+
<span class="loading loading-spinner"></span>
44+
{/if}
45+
Send Code
46+
</button>
47+
</div>
48+
</form>
49+
{:else}
50+
<form
51+
class="card-body"
52+
onsubmit={(event) => {
53+
event.preventDefault();
54+
const formData = new FormData(event.currentTarget);
55+
void signIn("password", formData);
56+
}}
57+
>
58+
<h1 class="text-2xl font-bold">Enter Code</h1>
59+
<p class="text-base-content/70 text-sm">
60+
A code has been sent to <strong>{step.email}</strong>.
61+
</p>
62+
<div class="form-control">
63+
<label class="label" for="code">
64+
<span class="label-text">Code</span>
65+
</label>
66+
<input
67+
id="code"
68+
name="code"
69+
placeholder="123456"
70+
class="input input-bordered"
71+
required
72+
/>
73+
</div>
74+
<div class="form-control">
75+
<label class="label" for="newPassword">
76+
<span class="label-text">New Password</span>
77+
</label>
78+
<input
79+
id="newPassword"
80+
name="newPassword"
81+
type="password"
82+
placeholder="new password"
83+
class="input input-bordered"
84+
required
85+
/>
86+
<input name="email" value={step.email} type="hidden" />
87+
<input name="flow" value="reset-verification" type="hidden" />
88+
</div>
89+
<div class="form-control mt-6">
90+
<button type="submit" class="btn btn-primary" disabled={isLoading}>
91+
{#if isLoading}
92+
<span class="loading loading-spinner"></span>
93+
{/if}
94+
Continue
95+
</button>
96+
</div>
97+
<div class="mt-4 text-center">
98+
<button
99+
type="button"
100+
class="link-hover link text-sm"
101+
onclick={() => (step = "forgot")}
102+
>
103+
Cancel
104+
</button>
105+
</div>
106+
</form>
107+
{/if}
108+
</div>
109+
</div>

0 commit comments

Comments
 (0)