From e8e3ecc1ae569a44fcbc79e1945f0e55a172857b Mon Sep 17 00:00:00 2001 From: Ronald Gene Date: Fri, 12 Sep 2025 10:01:35 -0600 Subject: [PATCH 1/2] feat: Add wallet address to User model and implement user addition via wallet connection --- .../migration.sql | 11 ++++++ .../migration.sql | 2 + prisma/schema.prisma | 3 +- src/components/LoginForm.tsx | 3 +- src/components/walletconnectbutton.tsx | 24 ++++++++++++ src/lib/action.ts | 37 +++++++++++++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20250912152754_add_walletaddress_to_user_model/migration.sql create mode 100644 prisma/migrations/20250912153358_update_user_model_email/migration.sql create mode 100644 src/components/walletconnectbutton.tsx create mode 100644 src/lib/action.ts diff --git a/prisma/migrations/20250912152754_add_walletaddress_to_user_model/migration.sql b/prisma/migrations/20250912152754_add_walletaddress_to_user_model/migration.sql new file mode 100644 index 0000000..0c2ddb7 --- /dev/null +++ b/prisma/migrations/20250912152754_add_walletaddress_to_user_model/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[walletAddress]` on the table `User` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "public"."User" ADD COLUMN "walletAddress" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "User_walletAddress_key" ON "public"."User"("walletAddress"); diff --git a/prisma/migrations/20250912153358_update_user_model_email/migration.sql b/prisma/migrations/20250912153358_update_user_model_email/migration.sql new file mode 100644 index 0000000..9c40c31 --- /dev/null +++ b/prisma/migrations/20250912153358_update_user_model_email/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "public"."User" ALTER COLUMN "email" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 24d14a3..87539ef 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,7 +15,8 @@ datasource db { model User { id String @id @default(uuid()) - email String @unique + walletAddress String? @unique + email String? @unique name String? password String? role Role @default(USER) diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 74f863c..dea1baa 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -13,6 +13,7 @@ import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import GoogleSignInButton from "@/components/GoogleSignInButton"; import { ConnectButton } from "@rainbow-me/rainbowkit"; +import WalletConnectButton from "./walletconnectbutton"; export default function LoginForm() { const [email, setEmail] = useState(""); @@ -42,7 +43,7 @@ export default function LoginForm() {
- +
diff --git a/src/components/walletconnectbutton.tsx b/src/components/walletconnectbutton.tsx new file mode 100644 index 0000000..1b9e476 --- /dev/null +++ b/src/components/walletconnectbutton.tsx @@ -0,0 +1,24 @@ +"use client"; +import { addUser } from "@/lib/action"; +import { ConnectButton } from "@rainbow-me/rainbowkit"; +import { useEffect } from "react"; +import { useAccount } from "wagmi"; + +export default function WalletConnectButton() { + + const {address, isConnected} = useAccount(); + + useEffect(()=>{ + if(isConnected && address){ + addUser(address) + .then((user)=> + console.log("User added/login",user)) + .catch((err)=>{ + console.error("Error adding user", err); + }) + } + }, + [address, isConnected]); + + return ; +} diff --git a/src/lib/action.ts b/src/lib/action.ts new file mode 100644 index 0000000..d8b7de4 --- /dev/null +++ b/src/lib/action.ts @@ -0,0 +1,37 @@ +"use server"; +import { prisma } from "./prisma"; + + +export async function addUser(address: string, email?: string) { + + if (!address && !email) { + throw new Error("Must provide wallet address or email"); + } + + let user; + if(address){ + user = await prisma.user.findUnique({ + where: { + walletAddress: address + } + }) + }; + + if(!user && email) { + user = await prisma.user.findUnique({ + where:{ + email: email + } + }) + } + + if (!user) { + user = await prisma.user.create({ + data: { + walletAddress: address, + email:email + } + }) + } + return user; +} \ No newline at end of file From acaff5a6f5bd416e100757024f38f967236c92e4 Mon Sep 17 00:00:00 2001 From: Ronald Gene Date: Fri, 12 Sep 2025 10:19:16 -0600 Subject: [PATCH 2/2] feat: Refactor addUser function to normalize wallet address and streamline user creation logic --- src/lib/action.ts | 75 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/src/lib/action.ts b/src/lib/action.ts index d8b7de4..6266a12 100644 --- a/src/lib/action.ts +++ b/src/lib/action.ts @@ -4,34 +4,63 @@ import { prisma } from "./prisma"; export async function addUser(address: string, email?: string) { - if (!address && !email) { + const normalizedAddress = address ? address.trim().toLowerCase() : undefined; + if (!normalizedAddress && !email) { throw new Error("Must provide wallet address or email"); } - let user; - if(address){ - user = await prisma.user.findUnique({ - where: { - walletAddress: address - } - }) - }; + const selectSafe = { + id: true, + email: true, + walletAddress: true, + role: true, + name: true, + image: true, + subscriptionType: true, + createdAt: true, + updatedAt: true, + lastLoginAt: true, + isActive: true, + } as const; - if(!user && email) { - user = await prisma.user.findUnique({ - where:{ - email: email + // Prefer wallet path when available (idempotent via upsert) + if (normalizedAddress) { + try { + const user = await prisma.user.upsert({ + where: { walletAddress: normalizedAddress }, + update: { lastLoginAt: new Date() }, + create: { + walletAddress: normalizedAddress, + email: email ?? null, + lastLoginAt: new Date(), + }, + select: selectSafe, + }); + // Optionally link email if provided and not set yet + if (email && !user.email) { + return await prisma.user.update({ + where: { id: user.id }, + data: { email }, + select: selectSafe, + }); } - }) + return user; + } catch (e) { + // Handle race: if unique constraint hit, re-fetch + const existing = await prisma.user.findUnique({ + where: { walletAddress: normalizedAddress }, + select: selectSafe, + }); + if (existing) return existing; + throw e; + } } - if (!user) { - user = await prisma.user.create({ - data: { - walletAddress: address, - email:email - } - }) - } - return user; + // Email-only path (idempotent) + return prisma.user.upsert({ + where: { email: email as string }, + update: { lastLoginAt: new Date() }, + create: { email: email as string }, + select: selectSafe, + }); } \ No newline at end of file