feat: add bio, return multiple results from search

This commit is contained in:
Steve Korshakov 2025-09-19 21:29:15 -07:00
parent 0d46e6fee3
commit 595e23967a
3 changed files with 29 additions and 55 deletions

View File

@ -2,11 +2,11 @@ import { z } from "zod";
import { Fastify } from "../types";
import { db } from "@/storage/db";
import { RelationshipStatus } from "@prisma/client";
import { getPublicUrl } from "@/storage/files";
import { friendAdd } from "@/app/social/friendAdd";
import { Context } from "@/context";
import { friendRemove } from "@/app/social/friendRemove";
import { friendList } from "@/app/social/friendList";
import { buildUserProfile } from "@/app/social/type";
export async function userRoutes(app: Fastify) {
@ -54,24 +54,11 @@ export async function userRoutes(app: Fastify) {
// Build user profile
return reply.send({
user: {
id: user.id,
firstName: user.firstName || '',
lastName: user.lastName,
avatar: user.avatar ? {
path: user.avatar.path,
url: getPublicUrl(user.avatar.path),
width: user.avatar.width,
height: user.avatar.height,
thumbhash: user.avatar.thumbhash
} : null,
username: user.username || (user.githubUser?.profile?.login || ''),
status: status
}
user: buildUserProfile(user, status)
});
});
// Search for user
// Search for users
app.get('/v1/user/search', {
schema: {
querystring: z.object({
@ -79,10 +66,7 @@ export async function userRoutes(app: Fastify) {
}),
response: {
200: z.object({
user: UserProfileSchema
}),
404: z.object({
error: z.literal('User not found')
users: z.array(UserProfileSchema)
})
}
},
@ -90,8 +74,8 @@ export async function userRoutes(app: Fastify) {
}, async (request, reply) => {
const { query } = request.query;
// Search for user by username or GitHub login
const user = await db.account.findFirst({
// Search for users by username, first 10 matches
const users = await db.account.findMany({
where: {
username: {
startsWith: query,
@ -100,37 +84,27 @@ export async function userRoutes(app: Fastify) {
},
include: {
githubUser: true
},
take: 10,
orderBy: {
username: 'asc'
}
});
if (!user) {
return reply.code(404).send({ error: 'User not found' });
}
// Resolve relationship status
const relationship = await db.userRelationship.findFirst({
where: {
fromUserId: request.userId,
toUserId: user.id
}
});
const status: RelationshipStatus = relationship?.status || RelationshipStatus.none;
// Resolve relationship status for each user
const userProfiles = await Promise.all(users.map(async (user) => {
const relationship = await db.userRelationship.findFirst({
where: {
fromUserId: request.userId,
toUserId: user.id
}
});
const status: RelationshipStatus = relationship?.status || RelationshipStatus.none;
return buildUserProfile(user, status);
}));
return reply.send({
user: {
id: user.id,
firstName: user.firstName || '',
lastName: user.lastName,
avatar: user.avatar ? {
path: user.avatar.path,
url: getPublicUrl(user.avatar.path),
width: user.avatar.width,
height: user.avatar.height,
thumbhash: user.avatar.thumbhash
} : null,
username: user.username || (user.githubUser?.profile?.login || ''),
status: status
}
users: userProfiles
});
});
@ -204,5 +178,6 @@ const UserProfileSchema = z.object({
thumbhash: z.string().optional()
}).nullable(),
username: z.string(),
bio: z.string().nullable(),
status: RelationshipStatusSchema
});

View File

@ -4,14 +4,13 @@ import { log } from "@/utils/log";
import { allocateUserSeq } from "@/storage/seq";
import { buildUpdateAccountUpdate, eventRouter } from "@/app/events/eventRouter";
import { randomKeyNaked } from "@/utils/randomKeyNaked";
import { Prisma } from "@prisma/client";
/**
* Disconnects a GitHub account from a user profile.
*
* Flow:
* 1. Check if user has GitHub connected - early exit if not
* 2. In transaction: clear GitHub link, username, avatar from account and delete GitHub user record
* 2. In transaction: clear GitHub link and username from account (keeps avatar) and delete GitHub user record
* 3. Send socket update after transaction completes
*
* @param ctx - Request context containing user ID
@ -36,13 +35,12 @@ export async function githubDisconnect(ctx: Context): Promise<void> {
// Step 2: Transaction for atomic database operations
await db.$transaction(async (tx) => {
// Clear GitHub connection, username, and avatar from account
// Clear GitHub connection and username from account (keep avatar)
await tx.account.update({
where: { id: userId },
data: {
githubUserId: null,
username: null,
avatar: Prisma.JsonNull
username: null
}
});
@ -56,8 +54,7 @@ export async function githubDisconnect(ctx: Context): Promise<void> {
const updSeq = await allocateUserSeq(userId);
const updatePayload = buildUpdateAccountUpdate(userId, {
github: null,
username: null,
avatar: null
username: null
}, updSeq, randomKeyNaked(12));
eventRouter.emitUpdate({

View File

@ -14,6 +14,7 @@ export type UserProfile = {
thumbhash?: string;
} | null;
username: string;
bio: string | null;
status: RelationshipStatus;
}
@ -49,6 +50,7 @@ export function buildUserProfile(
lastName: account.lastName,
avatar,
username: account.username || githubProfile?.login || '',
bio: githubProfile?.bio || null,
status
};
}