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
{user.email}