diff --git a/app/login/page.tsx b/app/login/page.tsx index 56d568d..4f5e24a 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,50 +1,83 @@ -import type { Metadata } from "next" +"use client" + +import { useState } from "react" +import { useRouter } from "next/navigation" import Link from "next/link" import { Button } from "@/components/ui/button" -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, +} from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" - -export const metadata: Metadata = { - title: "Log In", - description: "Log in to your Higgy's Android Boxes account to access video tutorials and support.", -} +import { checkPassword, setAuthenticated } from "@/lib/auth" export default function LoginPage() { + const [password, setPassword] = useState("") + const [error, setError] = useState("") + const [loading, setLoading] = useState(false) + const router = useRouter() + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + setError("") + setLoading(true) + + const valid = await checkPassword(password) + if (valid) { + setAuthenticated(true) + router.push("/videos") + } else { + setError("Incorrect password. Please try again.") + setLoading(false) + } + } + return (
- Welcome back + Client Access - Enter your credentials to access your video tutorials + Enter the password provided by Higgy to access video tutorials - -
- - -
-
- - -
- -

- Don't have an account?{" "} - +

+
+ + setPassword(e.target.value)} + placeholder="Enter your access password" + required + /> + {error && ( +

{error}

+ )} +
+ +

+ Don't have a password?{" "} + + Contact Higgy + +

+
diff --git a/app/page.tsx b/app/page.tsx index 9abc319..7ddf7b4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -56,7 +56,7 @@ export default function HomePage() { className="bg-[#8BC34A] hover:bg-[#7CB342] text-white" asChild > - Get Started + Get Started -

- Already have an account?{" "} - - Log in - -

- - - - ) -} diff --git a/app/sitemap.ts b/app/sitemap.ts index 83536a4..706a8a2 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -22,11 +22,5 @@ export default function sitemap(): MetadataRoute.Sitemap { changeFrequency: "yearly", priority: 0.3, }, - { - url: "https://higgysandroidboxes.com/signup", - lastModified: new Date(), - changeFrequency: "yearly", - priority: 0.5, - }, ] } diff --git a/app/videos/page.tsx b/app/videos/page.tsx index 736b9f4..e54cb5d 100644 --- a/app/videos/page.tsx +++ b/app/videos/page.tsx @@ -1,6 +1,5 @@ import type { Metadata } from "next" -import { VideoCard } from "@/components/video-card" -import { videos } from "@/lib/data" +import { VideosContent } from "./videos-content" export const metadata: Metadata = { title: "Video Tutorials", @@ -9,24 +8,5 @@ export const metadata: Metadata = { } export default function VideosPage() { - return ( -
-
-
-

- Video Tutorials -

-

- Learn how to get the most out of your Android box with our - step-by-step video guides -

-
-
- {videos.map((video) => ( - - ))} -
-
-
- ) + return } diff --git a/app/videos/videos-content.tsx b/app/videos/videos-content.tsx new file mode 100644 index 0000000..74d53d1 --- /dev/null +++ b/app/videos/videos-content.tsx @@ -0,0 +1,30 @@ +"use client" + +import { AuthGate } from "@/components/auth-gate" +import { VideoCard } from "@/components/video-card" +import { videos } from "@/lib/data" + +export function VideosContent() { + return ( + +
+
+
+

+ Video Tutorials +

+

+ Learn how to get the most out of your Android box with our + step-by-step video guides +

+
+
+ {videos.map((video) => ( + + ))} +
+
+
+
+ ) +} diff --git a/components/auth-gate.tsx b/components/auth-gate.tsx new file mode 100644 index 0000000..a2574f1 --- /dev/null +++ b/components/auth-gate.tsx @@ -0,0 +1,34 @@ +"use client" + +import { useState, useEffect } from "react" +import { useRouter } from "next/navigation" +import { isAuthenticated } from "@/lib/auth" + +export function AuthGate({ children }: { children: React.ReactNode }) { + const [checked, setChecked] = useState(false) + const [authed, setAuthed] = useState(false) + const router = useRouter() + + useEffect(() => { + if (isAuthenticated()) { + setAuthed(true) + } else { + router.replace("/login") + } + setChecked(true) + }, [router]) + + if (!checked) { + return ( +
+
Loading...
+
+ ) + } + + if (!authed) { + return null + } + + return <>{children} +} diff --git a/components/header.tsx b/components/header.tsx index e39b9da..68383fb 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -1,7 +1,25 @@ +"use client" + +import { useState, useEffect } from "react" import Link from "next/link" +import { useRouter } from "next/navigation" import { Button } from "@/components/ui/button" +import { isAuthenticated, setAuthenticated } from "@/lib/auth" export function Header() { + const [authed, setAuthed] = useState(false) + const router = useRouter() + + useEffect(() => { + setAuthed(isAuthenticated()) + }, []) + + function handleLogout() { + setAuthenticated(false) + setAuthed(false) + router.push("/") + } + return (
@@ -18,12 +36,21 @@ export function Header() { - - + {authed ? ( + + ) : ( + + )}
diff --git a/lib/auth.ts b/lib/auth.ts new file mode 100644 index 0000000..a74ab0c --- /dev/null +++ b/lib/auth.ts @@ -0,0 +1,37 @@ +// Shared password hash (SHA-256 of the password) +// To change the password: +// 1. Run: printf 'newpassword' | sha256sum +// 2. Replace VALID_HASH below with the output +// +// Current password: higgy2024 +const VALID_HASH = + "4e58a7a86830a9ae2aae7fc8253c290a4773b1dbbc4e71ffa3eb4afbec3acb25" + +const STORAGE_KEY = "higgys_auth" + +export async function hashPassword(password: string): Promise { + const encoder = new TextEncoder() + const data = encoder.encode(password) + const hashBuffer = await crypto.subtle.digest("SHA-256", data) + const hashArray = Array.from(new Uint8Array(hashBuffer)) + return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("") +} + +export async function checkPassword(password: string): Promise { + const hash = await hashPassword(password) + return hash === VALID_HASH +} + +export function isAuthenticated(): boolean { + if (typeof window === "undefined") return false + return localStorage.getItem(STORAGE_KEY) === "true" +} + +export function setAuthenticated(value: boolean) { + if (typeof window === "undefined") return + if (value) { + localStorage.setItem(STORAGE_KEY, "true") + } else { + localStorage.removeItem(STORAGE_KEY) + } +}