From 7c4707d677001c1d55360779981a20e4eced4c02 Mon Sep 17 00:00:00 2001 From: EdiFarcas Date: Mon, 28 Apr 2025 15:26:52 +0300 Subject: [PATCH] Youtube handle try, not working, user input for it. --- .../migration.sql | 16 +++++ .../migration.sql | 16 +++++ prisma/schema.prisma | 22 ++++--- src/app/api/auth/[...nextauth]/route.ts | 33 +++++++++- src/app/api/youtube-handle/route.ts | 25 ++++++++ src/app/profile/page.tsx | 64 +++++++++++++++++-- 6 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 prisma/migrations/20250428115623_youtube_id_fetch/migration.sql create mode 100644 prisma/migrations/20250428121000_youtube_id_fetch_2/migration.sql create mode 100644 src/app/api/youtube-handle/route.ts diff --git a/prisma/migrations/20250428115623_youtube_id_fetch/migration.sql b/prisma/migrations/20250428115623_youtube_id_fetch/migration.sql new file mode 100644 index 0000000..6c4b87e --- /dev/null +++ b/prisma/migrations/20250428115623_youtube_id_fetch/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - You are about to drop the column `youtubeId` on the `User` table. All the data in the column will be lost. + - A unique constraint covering the columns `[youtubeUsername]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "User_youtubeId_key"; + +-- AlterTable +ALTER TABLE "User" DROP COLUMN "youtubeId", +ADD COLUMN "youtubeUsername" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "User_youtubeUsername_key" ON "User"("youtubeUsername"); diff --git a/prisma/migrations/20250428121000_youtube_id_fetch_2/migration.sql b/prisma/migrations/20250428121000_youtube_id_fetch_2/migration.sql new file mode 100644 index 0000000..494b407 --- /dev/null +++ b/prisma/migrations/20250428121000_youtube_id_fetch_2/migration.sql @@ -0,0 +1,16 @@ +/* + Warnings: + + - A unique constraint covering the columns `[youtubeHandle]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[youtubeChannelId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "youtubeChannelId" TEXT, +ADD COLUMN "youtubeHandle" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "User_youtubeHandle_key" ON "User"("youtubeHandle"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_youtubeChannelId_key" ON "User"("youtubeChannelId"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6523268..b7fb3dc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,16 +8,18 @@ generator client { } model User { - id String @id @default(cuid()) - name String? - email String? @unique - emailVerified DateTime? - image String? - accounts Account[] - sessions Session[] - youtubeId String? @unique - coins Int @default(0) - entries Entry[] + id String @id @default(cuid()) + name String? + email String? @unique + emailVerified DateTime? + image String? + accounts Account[] + sessions Session[] + youtubeUsername String? @unique + youtubeHandle String? @unique + youtubeChannelId String? @unique + coins Int @default(0) + entries Entry[] } model Account { diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 1edf662..b7a25f6 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -31,10 +31,39 @@ export const authOptions = { console.error("Sign-in failed: Missing user, account, or profile data."); return false; } + + if (account.provider === "google" && account.access_token) { + try { + const { data } = await axios.get("https://www.googleapis.com/youtube/v3/channels", { + params: { + part: "brandingSettings", + mine: "true", + }, + headers: { + Authorization: `Bearer ${account.access_token}`, + }, + }); + + const customUrl = data.items?.[0]?.brandingSettings?.channel?.customUrl; + + // Create the handle + const youtubeHandle = customUrl ? `@${customUrl.replace(/^.*\//, "")}` : null; + + console.log("Fetched YouTube handle:", youtubeHandle); + + // Attach ONLY the handle to the user + user.youtubeHandle = youtubeHandle; + + } catch (error) { + console.error("Error fetching YouTube handle:", error); + } + } + return true; }, - async session({ session, user }: { session: any; user: { id: string } }) { - session.user.id = user.id; // Attach user ID to the session + async session({ session, user }: { session: any; user: any }) { + session.user.id = user.id; + session.user.youtubeHandle = user.youtubeHandle; // Only attach YouTube handle return session; }, }, diff --git a/src/app/api/youtube-handle/route.ts b/src/app/api/youtube-handle/route.ts new file mode 100644 index 0000000..71b8968 --- /dev/null +++ b/src/app/api/youtube-handle/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from "next/server"; +import { getServerSession } from "next-auth"; +import { authOptions } from "../auth/[...nextauth]/route"; +import { db } from "@/lib/db"; + +export async function POST(req: Request) { + const session = await getServerSession(authOptions); + + if (!session?.user?.email) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { youtubeHandle } = await req.json(); + + if (!youtubeHandle || typeof youtubeHandle !== "string") { + return NextResponse.json({ error: "Invalid handle" }, { status: 400 }); + } + + await db.user.update({ + where: { email: session.user.email }, + data: { youtubeHandle }, + }); + + return NextResponse.json({ success: true }); +} diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index 3f647c1..7fe4d3b 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -1,3 +1,6 @@ +"use client"; // <-- add this because we will use onChange, onSubmit! + +import { useState } from "react"; import { getServerSession } from "next-auth"; import { authOptions } from "../api/auth/[...nextauth]/route"; import { db } from "@/lib/db"; @@ -13,16 +16,43 @@ export default async function ProfilePage() { const user = await db.user.findUnique({ where: { email: session.user?.email! }, select: { - name: true, - email: true, - coins: true, - }, + name: true, + email: true, + coins: true, + youtubeHandle: true, + }, }); if (!user) { redirect("/api/auth/signin"); } + return ; +} + +// Create a separate component for the UI part +function ProfileContent({ user }: { user: any }) { + const [youtubeHandle, setYoutubeHandle] = useState(user.youtubeHandle || ""); + const [saving, setSaving] = useState(false); + + const handleSave = async () => { + setSaving(true); + try { + const res = await fetch("/api/youtube-handle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ youtubeHandle }), + }); + if (!res.ok) throw new Error("Failed to update"); + alert("YouTube handle updated successfully!"); + } catch (error) { + console.error(error); + alert("Error updating handle."); + } finally { + setSaving(false); + } + }; + return (
@@ -62,6 +92,27 @@ export default async function ProfilePage() {

{user.email}

+ + {/* YouTube Handle */} +
+ + setYoutubeHandle(e.target.value)} + /> + +
{/* Coins Section */} @@ -76,7 +127,7 @@ export default async function ProfilePage() { {/* Add functionality to add coins here */} + @@ -102,9 +153,10 @@ export default async function ProfilePage() { + ); -} \ No newline at end of file +}