Skip to content

Commit b61ad12

Browse files
committed
feat: emailとpasswordを使用したsigninとsignup機能を追加
1 parent 5a62fa5 commit b61ad12

11 files changed

+1133
-9
lines changed

package-lock.json

+429-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@radix-ui/react-icons": "^1.3.0",
1717
"@radix-ui/react-label": "^2.0.2",
1818
"@radix-ui/react-slot": "^1.0.2",
19+
"@supabase/ssr": "^0.0.10",
1920
"@tanstack/react-table": "^8.11.6",
2021
"class-variance-authority": "^0.7.0",
2122
"clsx": "^2.1.0",
@@ -24,6 +25,7 @@
2425
"react": "^18",
2526
"react-dom": "^18",
2627
"react-hook-form": "^7.49.3",
28+
"supabase": "^1.136.3",
2729
"tailwind-merge": "^2.2.0",
2830
"tailwindcss-animate": "^1.0.7",
2931
"zod": "^3.22.4"
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"use server";
2+
3+
import { revalidatePath } from "next/cache";
4+
import { cookies } from "next/headers";
5+
import { redirect } from "next/navigation";
6+
import { createClient } from "@/clients/supabase/cookie-writable-server-client";
7+
8+
export const signinAction = async (formData: FormData) => {
9+
const cookieStore = cookies();
10+
const supabase = createClient(cookieStore);
11+
12+
const data = {
13+
email: formData.get("email") as string,
14+
password: formData.get("password") as string,
15+
};
16+
17+
const { error } = await supabase.auth.signInWithPassword(data);
18+
19+
if (error) {
20+
console.error(error);
21+
redirect("/error");
22+
}
23+
24+
redirect("/");
25+
};
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use server";
2+
3+
import { revalidatePath } from "next/cache";
4+
import { cookies } from "next/headers";
5+
import { redirect } from "next/navigation";
6+
import { createClient } from "@/clients/supabase/cookie-writable-server-client";
7+
8+
export const signupAction = async (formData: FormData) => {
9+
const cookieStore = cookies();
10+
const supabase = createClient(cookieStore);
11+
12+
const data = {
13+
email: formData.get("email") as string,
14+
password: formData.get("password") as string,
15+
};
16+
17+
const { error } = await supabase.auth.signUp(data);
18+
19+
if (error) {
20+
redirect("/error");
21+
}
22+
23+
redirect("/");
24+
};

src/app/sign-in/page.tsx

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"use client";
2+
3+
import { z } from "zod";
4+
import { useForm } from "react-hook-form";
5+
import { zodResolver } from "@hookform/resolvers/zod";
6+
import {
7+
Form,
8+
FormControl,
9+
FormDescription,
10+
FormField,
11+
FormItem,
12+
FormLabel,
13+
FormMessage,
14+
} from "@/components/ui/form";
15+
import { Input } from "@/components/ui/input";
16+
import { Button } from "@/components/ui/button";
17+
import { signinAction } from "@/app/services/auth/sign-in.action";
18+
import { signupAction } from "@/app/services/auth/sign-up.action";
19+
20+
const formSchema = z.object({
21+
email: z.string().email(),
22+
password: z.string().min(6),
23+
});
24+
25+
export default function SignInOrSignUp() {
26+
const form = useForm<z.infer<typeof formSchema>>({
27+
resolver: zodResolver(formSchema),
28+
defaultValues: {
29+
email: "",
30+
password: "",
31+
},
32+
});
33+
34+
return (
35+
<div className="max-w-sm mx-auto mt-32">
36+
<Form {...form}>
37+
<form className="space-y-12">
38+
<div className="space-y-4">
39+
<FormField
40+
control={form.control}
41+
name="email"
42+
render={({ field }) => (
43+
<FormItem>
44+
<FormLabel>email</FormLabel>
45+
<FormControl>
46+
<Input placeholder="Enter your em@1l" {...field} />
47+
</FormControl>
48+
<FormMessage />
49+
</FormItem>
50+
)}
51+
/>
52+
<FormField
53+
control={form.control}
54+
name="password"
55+
render={({ field }) => (
56+
<FormItem>
57+
<FormLabel>password</FormLabel>
58+
<FormControl>
59+
<Input
60+
type="password"
61+
placeholder="Enter p@ssw0rd"
62+
{...field}
63+
/>
64+
</FormControl>
65+
<FormMessage />
66+
</FormItem>
67+
)}
68+
/>
69+
</div>
70+
<div className="space-y-4">
71+
<Button formAction={signinAction} type="submit" className="w-full">
72+
Sign In
73+
</Button>
74+
<Button formAction={signupAction} type="submit" className="w-full">
75+
Sign Up
76+
</Button>
77+
</div>
78+
</form>
79+
</Form>
80+
</div>
81+
);
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { type CookieOptions, createServerClient } from "@supabase/ssr";
2+
import { cookies } from "next/headers";
3+
import { Database } from "../../../supabase/database.types";
4+
5+
export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
6+
return createServerClient<Database>(
7+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
8+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
9+
{
10+
cookies: {
11+
get(name: string) {
12+
return cookieStore.get(name)?.value;
13+
},
14+
set(name: string, value: string, options: CookieOptions) {
15+
cookieStore.set({ name, value, ...options });
16+
},
17+
remove(name: string, options: CookieOptions) {
18+
cookieStore.set({ name, value: "", ...options });
19+
},
20+
},
21+
},
22+
);
23+
};

src/middleware.ts

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { createServerClient, type CookieOptions } from "@supabase/ssr";
2+
import { NextResponse, type NextRequest } from "next/server";
3+
import { Database } from "../supabase/database.types";
4+
5+
export async function middleware(request: NextRequest) {
6+
let response = NextResponse.next({
7+
request: {
8+
headers: request.headers,
9+
},
10+
});
11+
12+
const supabase = createServerClient<Database>(
13+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
14+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
15+
{
16+
cookies: {
17+
get(name: string) {
18+
return request.cookies.get(name)?.value;
19+
},
20+
set(name: string, value: string, options: CookieOptions) {
21+
request.cookies.set({
22+
name,
23+
value,
24+
...options,
25+
});
26+
response = NextResponse.next({
27+
request: {
28+
headers: request.headers,
29+
},
30+
});
31+
response.cookies.set({
32+
name,
33+
value,
34+
...options,
35+
});
36+
},
37+
remove(name: string, options: CookieOptions) {
38+
request.cookies.set({
39+
name,
40+
value: "",
41+
...options,
42+
});
43+
response = NextResponse.next({
44+
request: {
45+
headers: request.headers,
46+
},
47+
});
48+
response.cookies.set({
49+
name,
50+
value: "",
51+
...options,
52+
});
53+
},
54+
},
55+
},
56+
);
57+
58+
const {
59+
data: { user },
60+
} = await supabase.auth.getUser();
61+
62+
const signInPath = request.nextUrl.pathname === "/sign-in";
63+
64+
if (!user && !signInPath) {
65+
const baseUrl = request.nextUrl.origin;
66+
return NextResponse.redirect(`${baseUrl}/sign-in`);
67+
}
68+
69+
return response;
70+
}
71+
72+
export const config = {
73+
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
74+
};

supabase/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Supabase
2+
.branches
3+
.temp
4+
.env

0 commit comments

Comments
 (0)