Spaces:
Sleeping
Sleeping
| /** | |
| * User Queries - Drizzle ORM | |
| * | |
| * All user-related database operations. | |
| */ | |
| import { db } from "@/db"; | |
| import { users, profiles, profileSkills, profileMentoringTopics, profileConnectedRepos, userRepositories } from "@/db/schema"; | |
| import { eq, and, like, desc, sql } from "drizzle-orm"; | |
| import { v4 as uuidv4 } from "uuid"; | |
| // ============================================================================= | |
| // User CRUD | |
| // ============================================================================= | |
| export async function getUserById(id: string) { | |
| const result = await db.select().from(users).where(eq(users.id, id)).limit(1); | |
| return result[0] || null; | |
| } | |
| export async function getUserByGithubId(githubId: number) { | |
| const result = await db.select().from(users).where(eq(users.githubId, githubId)).limit(1); | |
| return result[0] || null; | |
| } | |
| export async function getUserByUsername(username: string) { | |
| // Case-insensitive lookup - GitHub usernames are case-insensitive | |
| const result = await db.select().from(users).where(sql`LOWER(${users.username}) = LOWER(${username})`).limit(1); | |
| return result[0] || null; | |
| } | |
| export async function createUser(data: { | |
| githubId: number; | |
| username: string; | |
| avatarUrl: string; | |
| role?: string; | |
| githubAccessToken?: string; | |
| }) { | |
| const now = new Date().toISOString(); | |
| const id = uuidv4(); | |
| await db.insert(users).values({ | |
| id, | |
| githubId: data.githubId, | |
| username: data.username, | |
| avatarUrl: data.avatarUrl, | |
| role: data.role || null, | |
| githubAccessToken: data.githubAccessToken || null, | |
| createdAt: now, | |
| updatedAt: now, | |
| }); | |
| return { id, ...data, createdAt: now, updatedAt: now }; | |
| } | |
| export async function updateUser(id: string, data: Partial<{ | |
| username: string; | |
| avatarUrl: string; | |
| role: string; | |
| githubAccessToken: string; | |
| }>) { | |
| await db.update(users) | |
| .set({ ...data, updatedAt: new Date().toISOString() }) | |
| .where(eq(users.id, id)); | |
| } | |
| export async function updateUserRole(id: string, role: string) { | |
| await db.update(users) | |
| .set({ role, updatedAt: new Date().toISOString() }) | |
| .where(eq(users.id, id)); | |
| } | |
| // ============================================================================= | |
| // Profile Operations | |
| // ============================================================================= | |
| export async function getProfile(userId: string) { | |
| const profile = await db.select().from(profiles).where(eq(profiles.userId, userId)).limit(1); | |
| if (!profile[0]) return null; | |
| const skills = await db.select().from(profileSkills).where(eq(profileSkills.profileId, userId)); | |
| const topics = await db.select().from(profileMentoringTopics).where(eq(profileMentoringTopics.profileId, userId)); | |
| const repos = await db.select().from(profileConnectedRepos).where(eq(profileConnectedRepos.profileId, userId)); | |
| return { | |
| ...profile[0], | |
| skills: skills.map(s => s.skill), | |
| mentoringTopics: topics.map(t => t.topic), | |
| connectedRepos: repos.map(r => r.repoName), | |
| }; | |
| } | |
| export async function getProfileByUsername(username: string) { | |
| // Case-insensitive lookup - GitHub usernames are case-insensitive | |
| const profile = await db.select().from(profiles).where(sql`LOWER(${profiles.username}) = LOWER(${username})`).limit(1); | |
| if (!profile[0]) return null; | |
| const userId = profile[0].userId; | |
| const skills = await db.select().from(profileSkills).where(eq(profileSkills.profileId, userId)); | |
| const topics = await db.select().from(profileMentoringTopics).where(eq(profileMentoringTopics.profileId, userId)); | |
| return { | |
| ...profile[0], | |
| // Include snake_case aliases for frontend compatibility | |
| available_for_mentoring: profile[0].availableForMentoring, | |
| avatar_url: profile[0].avatarUrl, | |
| profile_visibility: profile[0].profileVisibility, | |
| show_email: profile[0].showEmail, | |
| skills: skills.map(s => s.skill), | |
| mentoringTopics: topics.map(t => t.topic), | |
| }; | |
| } | |
| export async function createOrUpdateProfile(userId: string, data: { | |
| username: string; | |
| avatarUrl?: string; | |
| bio?: string; | |
| location?: string; | |
| website?: string; | |
| twitter?: string; | |
| availableForMentoring?: boolean; | |
| profileVisibility?: string; | |
| showEmail?: boolean; | |
| skills?: string[]; | |
| mentoringTopics?: string[]; | |
| }) { | |
| const now = new Date().toISOString(); | |
| // Upsert profile | |
| await db.insert(profiles).values({ | |
| userId, | |
| username: data.username, | |
| avatarUrl: data.avatarUrl || null, | |
| bio: data.bio || null, | |
| location: data.location || null, | |
| website: data.website || null, | |
| twitter: data.twitter || null, | |
| availableForMentoring: data.availableForMentoring || false, | |
| profileVisibility: data.profileVisibility || "public", | |
| showEmail: data.showEmail || false, | |
| createdAt: now, | |
| updatedAt: now, | |
| }).onConflictDoUpdate({ | |
| target: profiles.userId, | |
| set: { | |
| bio: data.bio, | |
| location: data.location, | |
| website: data.website, | |
| twitter: data.twitter, | |
| availableForMentoring: data.availableForMentoring, | |
| profileVisibility: data.profileVisibility, | |
| showEmail: data.showEmail, | |
| updatedAt: now, | |
| } | |
| }); | |
| // Update skills | |
| if (data.skills) { | |
| await db.delete(profileSkills).where(eq(profileSkills.profileId, userId)); | |
| for (const skill of data.skills) { | |
| await db.insert(profileSkills).values({ profileId: userId, skill }); | |
| } | |
| } | |
| // Update topics | |
| if (data.mentoringTopics) { | |
| await db.delete(profileMentoringTopics).where(eq(profileMentoringTopics.profileId, userId)); | |
| for (const topic of data.mentoringTopics) { | |
| await db.insert(profileMentoringTopics).values({ profileId: userId, topic }); | |
| } | |
| } | |
| return getProfile(userId); | |
| } | |
| // ============================================================================= | |
| // User Stats | |
| // ============================================================================= | |
| export async function getUserRepositories(userId: string) { | |
| return db.select().from(userRepositories).where(eq(userRepositories.userId, userId)); | |
| } | |
| export async function addUserRepository(userId: string, repoFullName: string) { | |
| await db.insert(userRepositories).values({ | |
| id: uuidv4(), | |
| userId, | |
| repoFullName, | |
| addedAt: new Date().toISOString(), | |
| }).onConflictDoNothing(); | |
| } | |
| export async function removeUserRepository(userId: string, repoFullName: string) { | |
| await db.delete(userRepositories).where( | |
| and( | |
| eq(userRepositories.userId, userId), | |
| eq(userRepositories.repoFullName, repoFullName) | |
| ) | |
| ); | |
| } | |