-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Navbar, local tunnel and clerk webhook.
-Introduce navigation . - Create navbar and components that compose it (browse _components). - Add query-string and svix packages. - Create tunnel with ngrok for stability and security reasons. - Implement clerk webhook and it's route. - Add public routes to clerk middleware. - Add images remote patterns to next.config. - Update README. - Add assets. Navigation, full client life cycle authentication from login to logout.
- Loading branch information
1 parent
72ad71c
commit 64daf53
Showing
17 changed files
with
466 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export default function RootPage() { | ||
return ( | ||
<div className="flex flex-col gap-y-4"> | ||
<h1 className="text-3xl font-bold">Home Page</h1> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import Link from "next/link"; | ||
import { AppWindowIcon } from "lucide-react"; | ||
import { SignInButton, UserButton, currentUser } from "@clerk/nextjs"; | ||
|
||
import { Button } from "@/components/ui/button"; | ||
|
||
const Actions = async () => { | ||
const user = await currentUser(); | ||
|
||
return ( | ||
<div className="flex items-center justify-end gap-x-2 ml-4 lg:ml-0"> | ||
{!user && ( | ||
<SignInButton> | ||
<Button size="sm">Login</Button> | ||
</SignInButton> | ||
)} | ||
{!!user && ( | ||
<div className="flex items-center gap-x-4"> | ||
<Button | ||
size="sm" | ||
variant="ghost" | ||
className="text-muted-foreground" | ||
asChild | ||
> | ||
<Link href={`/u/${user.username}`}> | ||
<AppWindowIcon className="h-5 w-5 lg:mr-2" /> | ||
<span className="hidden lg:block">Dashboard</span> | ||
</Link> | ||
</Button> | ||
<UserButton afterSignOutUrl="/" /> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default Actions; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Actions from "./actions"; | ||
import Logo from "./logo"; | ||
import Search from "./search"; | ||
|
||
const Navbar = () => { | ||
return ( | ||
<nav className="fixed top-0 w-full h-20 z-[49] bg-background px-2 lg:px-4 flex justify-between items-center shadow-sm border-b"> | ||
<Logo /> | ||
<Search /> | ||
<Actions /> | ||
</nav> | ||
); | ||
}; | ||
|
||
export default Navbar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import Link from "next/link"; | ||
import Image from "next/image"; | ||
import { Poppins } from "next/font/google"; | ||
|
||
import { cn } from "@/lib/utils"; | ||
|
||
const font = Poppins({ | ||
subsets: ["latin"], | ||
weight: ["200", "300", "400", "500", "600", "700", "800"], | ||
}); | ||
|
||
const Logo = () => { | ||
return ( | ||
<Link href="/"> | ||
<div className="flex items-center gap-x-4 hover:opacity-75 transition"> | ||
<div className="rounded-full mr-12 shrink-0 lg:mr-0 lg:shrink"> | ||
<Image | ||
src="/s3mer.svg" | ||
alt="S3mer logo" | ||
height={50} | ||
width={50} | ||
/> | ||
</div> | ||
|
||
<div className={cn("hidden lg:block", font.className)}> | ||
<p className="text-lg font-semibold">S3MER</p> | ||
<p className="text-xs text-muted-foreground"> | ||
Stream, Share, Engage! | ||
</p> | ||
</div> | ||
</div> | ||
</Link> | ||
); | ||
}; | ||
|
||
export default Logo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
"use client"; | ||
|
||
import qs from "query-string"; | ||
import { useState } from "react"; | ||
import { useRouter } from "next/navigation"; | ||
import { SearchIcon, X } from "lucide-react"; | ||
|
||
import { Input } from "@/components/ui/input"; | ||
import { Button } from "@/components/ui/button"; | ||
|
||
const Search = () => { | ||
const router = useRouter(); | ||
const [value, setValue] = useState(""); | ||
|
||
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
|
||
if (!value) return; | ||
|
||
const url = qs.stringifyUrl( | ||
{ | ||
url: "/search", | ||
query: { term: value }, | ||
}, | ||
{ skipEmptyString: true } | ||
); | ||
|
||
router.push(url); | ||
}; | ||
|
||
const onClear = () => { | ||
setValue(""); | ||
}; | ||
|
||
return ( | ||
<form | ||
className="relative w-full lg:w-[400px] flex items-center" | ||
onSubmit={onSubmit} | ||
> | ||
<Input | ||
value={value} | ||
onChange={(e) => setValue(e.target.value)} | ||
placeholder="Search" | ||
className="rounded-r-none focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0" | ||
/> | ||
|
||
{value && ( | ||
<X | ||
className="absolute top-2.5 right-14 h-5 w-5 text-muted-foreground cursor-pointer hover:opacity-75 transition" | ||
onClick={onClear} | ||
/> | ||
)} | ||
<Button | ||
type="submit" | ||
variant="secondary" | ||
className="rounded-l-none" | ||
> | ||
<SearchIcon className="h-5 w-5 text-muted-foreground" /> | ||
</Button> | ||
</form> | ||
); | ||
}; | ||
|
||
export default Search; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Navbar from "./_components/navbar"; | ||
|
||
const BrowseLayout = ({ children }: { children: React.ReactNode }) => { | ||
return ( | ||
<> | ||
<Navbar /> | ||
<div className="flex h-full pt-20">{children}</div> | ||
</> | ||
); | ||
}; | ||
|
||
export default BrowseLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { Webhook } from "svix"; | ||
import { headers } from "next/headers"; | ||
import { WebhookEvent } from "@clerk/nextjs/server"; | ||
|
||
import { db } from "@/lib/db"; | ||
// import { resetIngresses } from "@/actions/ingress"; | ||
|
||
export async function POST(req: Request) { | ||
// You can find this in the Clerk Dashboard -> Webhooks -> choose the webhook | ||
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET; | ||
|
||
if (!WEBHOOK_SECRET) { | ||
throw new Error( | ||
"Please add CLERK_WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local" | ||
); | ||
} | ||
|
||
// Get the headers | ||
const headerPayload = headers(); | ||
const svix_id = headerPayload.get("svix-id"); | ||
const svix_timestamp = headerPayload.get("svix-timestamp"); | ||
const svix_signature = headerPayload.get("svix-signature"); | ||
|
||
// If there are no headers, error out | ||
if (!svix_id || !svix_timestamp || !svix_signature) { | ||
return new Response("Error occured -- no svix headers", { | ||
status: 400, | ||
}); | ||
} | ||
|
||
// Get the body | ||
const payload = await req.json(); | ||
const body = JSON.stringify(payload); | ||
|
||
// Create a new Svix instance with your secret. | ||
const wh = new Webhook(WEBHOOK_SECRET); | ||
|
||
let evt: WebhookEvent; | ||
|
||
// Verify the payload with the headers | ||
try { | ||
evt = wh.verify(body, { | ||
"svix-id": svix_id, | ||
"svix-timestamp": svix_timestamp, | ||
"svix-signature": svix_signature, | ||
}) as WebhookEvent; | ||
} catch (err) { | ||
console.error("Error verifying webhook:", err); | ||
return new Response("Error occured", { | ||
status: 400, | ||
}); | ||
} | ||
|
||
const eventType = evt.type; | ||
|
||
if (eventType === "user.created") { | ||
await db.user.create({ | ||
data: { | ||
externalUserId: payload.data.id, | ||
username: payload.data.username, | ||
imageUrl: payload.data.image_url, | ||
stream: { | ||
create: { | ||
name: `${payload.data.username}'s stream`, | ||
}, | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
if (eventType === "user.updated") { | ||
await db.user.update({ | ||
where: { | ||
externalUserId: payload.data.id, | ||
}, | ||
data: { | ||
username: payload.data.username, | ||
imageUrl: payload.data.image_url, | ||
}, | ||
}); | ||
} | ||
|
||
if (eventType === "user.deleted") { | ||
// await resetIngresses(payload.data.id); | ||
|
||
await db.user.delete({ | ||
where: { | ||
externalUserId: payload.data.id, | ||
}, | ||
}); | ||
} | ||
|
||
return new Response("", { status: 200 }); | ||
} |
Binary file not shown.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import * as React from "react" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
export interface InputProps | ||
extends React.InputHTMLAttributes<HTMLInputElement> {} | ||
|
||
const Input = React.forwardRef<HTMLInputElement, InputProps>( | ||
({ className, type, ...props }, ref) => { | ||
return ( | ||
<input | ||
type={type} | ||
className={cn( | ||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50", | ||
className | ||
)} | ||
ref={ref} | ||
{...props} | ||
/> | ||
) | ||
} | ||
) | ||
Input.displayName = "Input" | ||
|
||
export { Input } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,17 @@ | ||
/** @type {import('next').NextConfig} */ | ||
const nextConfig = {} | ||
const nextConfig = { | ||
images: { | ||
remotePatterns: [ | ||
{ | ||
protocol: "https", | ||
hostname: "utfs.io", | ||
}, | ||
{ | ||
protocol: "https", | ||
hostname: "uploadthing.com", | ||
}, | ||
], | ||
}, | ||
}; | ||
|
||
module.exports = nextConfig | ||
module.exports = nextConfig; |
Oops, something went wrong.