diff --git a/server/api/discover/users.get.ts b/server/api/discover/users.get.ts new file mode 100644 index 0000000..db887c7 --- /dev/null +++ b/server/api/discover/users.get.ts @@ -0,0 +1,8 @@ +import { listDiscoverUsersPage } from "#server/service/discover"; + +export default defineWrappedResponseHandler(async (event) => { + await event.context.auth.requireUser(); + const q = getQuery(event); + const payload = await listDiscoverUsersPage(q.page); + return R.success(payload); +}); diff --git a/server/service/discover/index.ts b/server/service/discover/index.ts new file mode 100644 index 0000000..52fed86 --- /dev/null +++ b/server/service/discover/index.ts @@ -0,0 +1,56 @@ +import { dbGlobal } from "drizzle-pkg/lib/db"; +import { users } from "drizzle-pkg/lib/schema/auth"; +import { and, asc, count, eq, isNotNull, ne } from "drizzle-orm"; +import { DISCOVER_LIST_PAGE_SIZE } from "#server/constants/discover-list"; +import { discoverCardAvatarUrl, discoverCardLocationLine } from "#server/utils/discover-card"; +import { normalizePublicListPage } from "#server/utils/public-pagination"; + +const listWhere = and( + eq(users.status, "active"), + eq(users.discoverVisible, true), + isNotNull(users.publicSlug), + ne(users.publicSlug, ""), +); + +export type DiscoverListItem = { + publicSlug: string; + displayName: string; + avatar: string | null; + location: string | null; +}; + +function mapRow(row: typeof users.$inferSelect): DiscoverListItem { + const displayName = row.nickname?.trim() || row.username; + const discoverVis = Boolean(row.discoverVisible); + const showLoc = Boolean(row.discoverShowLocation); + return { + publicSlug: row.publicSlug as string, + displayName, + avatar: discoverCardAvatarUrl(row.avatar, row.avatarVisibility), + location: discoverCardLocationLine(discoverVis, showLoc, row.discoverLocation), + }; +} + +export async function listDiscoverUsersPage(pageRaw: unknown) { + const page = normalizePublicListPage(pageRaw); + const pageSize = DISCOVER_LIST_PAGE_SIZE; + const offset = (page - 1) * pageSize; + + const [countRows, rows] = await Promise.all([ + dbGlobal.select({ total: count() }).from(users).where(listWhere), + dbGlobal + .select() + .from(users) + .where(listWhere) + .orderBy(asc(users.id)) + .limit(pageSize) + .offset(offset), + ]); + + return { + items: rows.map(mapRow), + total: countRows[0]?.total ?? 0, + page, + pageSize, + }; +}