Skip to content

Commit 6c2ca8f

Browse files
authored
Merge pull request #23 from Developer-DAO/registration
Registration and Login
2 parents 00b62f3 + 46342dd commit 6c2ca8f

28 files changed

+596
-1049
lines changed

.github/workflows/playwright.yml

Lines changed: 0 additions & 27 deletions
This file was deleted.

app/_template/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React from "react";
44

55
export default function HomePage() {
66
return (
7-
<div className="relative flex min-h-screen flex-col bg-background">
7+
<div className="relative flex min-h-screen flex-col ">
88
<Navigation />
99
<main className="flex-1">
1010
template

app/about/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React from "react";
55

66
export default function HomePage() {
77
return (
8-
<div className="relative flex min-h-screen flex-col bg-background" data-testid="aboutpage">
8+
<div className="relative flex min-h-screen flex-col " data-testid="aboutpage">
99
<Navigation />
1010
<main className="flex-1">
1111

app/auth.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { SignJWT, jwtVerify } from "jose";
2+
import { cookies } from "next/headers";
3+
import { NextRequest, NextResponse } from "next/server";
4+
5+
const secretKey = process.env.JWT_SECRET;
6+
const key = new TextEncoder().encode(secretKey);
7+
8+
export async function encrypt(payload: any) {
9+
return await new SignJWT(payload)
10+
.setProtectedHeader({ alg: "HS256" })
11+
.setIssuedAt()
12+
.setExpirationTime(Date.now() + 5184000000)
13+
.sign(key);
14+
}
15+
16+
export async function decrypt(input: string): Promise<any> {
17+
const { payload } = await jwtVerify(input, key, {
18+
algorithms: ["HS256"],
19+
});
20+
return payload;
21+
}
22+
23+
export async function register(formData: FormData) {
24+
// Create the user
25+
const rawFormData = {
26+
email: formData.get('email'),
27+
password: formData.get('password'),
28+
}
29+
const response = await fetch('http://')
30+
}
31+
32+
export async function login(formData: FormData) {
33+
// Verify credentials && get the user
34+
const rawFormData = {
35+
email: formData.get('email'),
36+
password: formData.get('password'),
37+
}
38+
const response = await fetch('http://0.0.0.0:3000/login', {
39+
method: 'POST',
40+
headers: {
41+
'Content-Type': 'application/json',
42+
},
43+
body: JSON.stringify(rawFormData),
44+
})
45+
const result = await response;
46+
const responsetext = (await result.text());
47+
//console.log(responsetext, result.status);
48+
if (result.status != 200) {
49+
return { message: `Error: ${responsetext}`, success: false };
50+
}
51+
52+
53+
const user = { email: formData.get("email"), token: responsetext };
54+
55+
// Create the session
56+
const expires = new Date(Date.now() + 5184000000);
57+
const session = await encrypt({ user, expires });
58+
59+
// Save the session in a cookie
60+
cookies().set("session", session, { expires, httpOnly: true });
61+
}
62+
63+
export async function logout() {
64+
// Destroy the session
65+
cookies().set("session", "", { expires: new Date(0) });
66+
}
67+
68+
export async function getSession() {
69+
const session = cookies().get("session")?.value;
70+
if (!session) return null;
71+
return await decrypt(session);
72+
}
73+
74+
export async function updateSession(request: NextRequest) {
75+
const session = request.cookies.get("session")?.value;
76+
if (!session) return;
77+
78+
// Refresh the session so it doesn't expire
79+
const parsed = await decrypt(session);
80+
parsed.expires = new Date(Date.now() + 5184000000);
81+
const res = NextResponse.next();
82+
res.cookies.set({
83+
name: "session",
84+
value: await encrypt(parsed),
85+
httpOnly: true,
86+
expires: parsed.expires,
87+
});
88+
return res;
89+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Link from "next/link"
2+
import { getSession, login, logout } from "@/app/auth";
3+
import { Icons } from "@/components/ui/icons"
4+
5+
export async function Navigation() {
6+
const session = await getSession();
7+
return (
8+
<header className="sticky top-0 z-50 w-full bg-zinc-900/95 backdrop-blur supports-[backdrop-filter]:bg-zinc-900/60">
9+
<div className="container flex py-4 max-w-screen-2xl items-center">
10+
<div className="mr-4 hidden md:flex">
11+
<Link href="/" className="mr-6 flex items-center space-x-2" data-testid="nav-auth-home">
12+
<Icons.logo className="h-12 w-12" />
13+
<span className="hidden font-bold sm:inline-block text-white">
14+
RPC
15+
</span>
16+
</Link>
17+
<nav className="flex items-center gap-6 text-lg font-semibold">
18+
<Link
19+
href="/about"
20+
className=
21+
" tracking-wide text-neutral-300 p-0 hover:text-white transition-colors"
22+
data-testid="nav-about"
23+
>
24+
About
25+
</Link>
26+
<Link
27+
href="/contact"
28+
className=
29+
" tracking-wide text-neutral-300 p-0 hover:text-white transition-colors"
30+
data-testid="nav-contact"
31+
>
32+
Contact
33+
</Link>
34+
</nav>
35+
</div>
36+
<div className="flex flex-1 items-center justify-between space-x-2 md:justify-end">
37+
<nav className="flex items-center">
38+
{session ? (
39+
<Link href="/dashboard" className="flex w-fit items-center justify-center gap-1 rounded-full border-2 transition-all h-12 px-6 text-lg text-black border-white bg-white font-paragraph font-semibold tracking-wider hover:drop-shadow-[10px_0_20px_rgba(254,254,254,0.472)]" data-testid="nav-login">Application</Link>
40+
) : (
41+
<Link href="/authentication" className="flex w-fit items-center justify-center gap-1 rounded-full border-2 transition-all h-12 px-6 text-lg text-black border-white bg-white font-paragraph font-semibold tracking-wider hover:drop-shadow-[10px_0_20px_rgba(254,254,254,0.472)]" data-testid="nav-login">Registration</Link>
42+
)}
43+
</nav>
44+
</div>
45+
</div>
46+
</header>
47+
)
48+
}
Lines changed: 55 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,68 @@
1-
"use client"
2-
31
import * as React from "react"
4-
import { useAccount, useConnect, useDisconnect } from 'wagmi'
52
import { cn } from "@/lib/utils"
6-
import { Icons } from "@/components/ui/icons"
73
import { Button } from "@/components/ui/button"
84
import { Input } from "@/components/ui/input"
95
import { Label } from "@/components/ui/label"
10-
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
11-
import { WalletButton } from "@/components/wallet-buttons"
12-
13-
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> { }
14-
15-
export default function UserLoginForm({ className, ...props }: UserAuthFormProps) {
16-
const [isLoading, setIsLoading] = React.useState<boolean>(false)
17-
18-
const account = useAccount()
19-
const { connectors, connect, status, error } = useConnect()
20-
const { disconnect } = useDisconnect()
21-
22-
async function onSubmit(event: React.SyntheticEvent) {
23-
event.preventDefault()
24-
setIsLoading(true)
6+
import { getSession, login, logout } from "@/app/auth";
7+
import Link from "next/link"
8+
import { redirect } from "next/navigation"
259

26-
setTimeout(() => {
27-
setIsLoading(false)
28-
}, 3000)
29-
}
30-
31-
const loginButtons = connectors.map((connector) =>
32-
connector.name.toLowerCase() === "injected" ? null : (
33-
<WalletButton
34-
connector={connector}
35-
isLoading={isLoading}
36-
connect={connect}
37-
key={connector.uid}
38-
/>
39-
)
40-
);
4110

11+
export default function UserRegForm() {
4212
return (
43-
<div className={cn("grid gap-6", className)} {...props}>
44-
45-
{account.status === 'connected' ? (
46-
<HoverCard>
47-
<HoverCardTrigger asChild>
48-
<Button variant="destructive" onClick={() => disconnect()}>Disconnect</Button>
49-
</HoverCardTrigger>
50-
<HoverCardContent className="w-80">
51-
<div className="flex justify-between space-x-4">
52-
<div className="space-y-1">
53-
<h4 className="text-sm font-semibold">connected as</h4>
54-
<div className="text-xs">
55-
{JSON.stringify(account.addresses[0].replaceAll(account.addresses[0].slice(8, 36), '...'))}
56-
</div>
57-
</div>
58-
</div>
59-
</HoverCardContent>
60-
</HoverCard>
61-
) : (
62-
<div className="grid gap-1">
63-
{loginButtons}
64-
</div>
65-
)}
66-
<div className="relative">
67-
<div className="relative flex justify-center text-xs uppercase">
68-
<span className="bg-background px-2 text-neutral-500">
69-
Or continue with
70-
</span>
13+
<>
14+
<div className="grid gap-6">
15+
<div className="flex justify-start">
16+
<Link href="/authentication/walletlogin" className="flex w-fit items-center justify-center gap-1 rounded-full border-2 transition-all h-12 px-6 text-lg text-black border-white bg-white font-paragraph font-semibold tracking-wider hover:drop-shadow-[10px_0_20px_rgba(254,254,254,0.472)]">Wallet Login</Link>
7117
</div>
72-
</div>
73-
74-
<form onSubmit={onSubmit}>
75-
<div className="grid gap-2">
76-
<div className="grid gap-1">
77-
<Label className="sr-only" htmlFor="email">
78-
Email
79-
</Label>
80-
<Input
81-
id="email"
82-
placeholder="name@example.com"
83-
type="email"
84-
autoCapitalize="none"
85-
autoComplete="email"
86-
autoCorrect="off"
87-
disabled={isLoading}
88-
/>
89-
</div>
90-
<div className="grid gap-1">
91-
<Label className="sr-only" htmlFor="password">
92-
Password
93-
</Label>
94-
<Input
95-
id="password"
96-
placeholder="******"
97-
type="password"
98-
autoCapitalize="none"
99-
autoComplete="email"
100-
autoCorrect="off"
101-
disabled={isLoading}
102-
/>
18+
<div className="relative">
19+
<div className="relative flex justify-start text-xs uppercase">
20+
<span className=" px-2 text-neutral-500">
21+
Or continue with
22+
</span>
10323
</div>
104-
<Button disabled={isLoading} className="mt-2">
105-
{isLoading && (
106-
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
107-
)}
108-
Sign In with Email
109-
</Button>
11024
</div>
111-
</form>
11225

113-
114-
</div>
115-
)
26+
<form
27+
action={async (formData) => {
28+
"use server";
29+
await login(formData);
30+
redirect("/dashboard");
31+
}}
32+
>
33+
<div className="grid gap-2">
34+
<div className="grid gap-1">
35+
<Label className="sr-only" htmlFor="email">
36+
Email
37+
</Label>
38+
<Input
39+
id="email"
40+
name="email"
41+
placeholder="name@example.com"
42+
type="email"
43+
autoCapitalize="none"
44+
autoComplete="email"
45+
autoCorrect="off"
46+
/>
47+
</div>
48+
<div className="grid gap-1">
49+
<Label className="sr-only" htmlFor="password">
50+
Password
51+
</Label>
52+
<Input
53+
id="password"
54+
name="password"
55+
placeholder="******"
56+
type="password"
57+
autoCapitalize="none"
58+
autoComplete="email"
59+
autoCorrect="off"
60+
/>
61+
</div>
62+
<Button type="submit" >Log In</Button>
63+
</div>
64+
</form>
65+
</div>
66+
</>)
11667
}
68+

0 commit comments

Comments
 (0)