feat: add leaderboard category

This commit is contained in:
2024-08-09 20:33:07 +07:00
parent e8ed08d49b
commit a5cd6383f3
33 changed files with 744 additions and 277 deletions
+12 -14
View File
@@ -2,7 +2,7 @@ import db from "@server/db";
import { JWT_SECRET } from "@server/lib/consts";
import github from "@server/lib/github";
import queue from "@server/lib/queue";
import { repositories, users } from "@server/models";
import { users } from "@server/models";
import { CreateUser } from "@server/models/users";
import { eq } from "drizzle-orm";
import { Hono } from "hono";
@@ -50,7 +50,7 @@ export const auth = new Hono()
const userData: CreateUser = {
username: ghUser.login,
name: ghUser.name,
name: ghUser.name || ghUser.login,
avatar: ghUser.avatar_url,
location: ghUser.location,
accessToken,
@@ -73,18 +73,16 @@ export const auth = new Hono()
}
// Fetch latest user profile
await queue.add("fetchUserProfile", { userId: user.id });
// Fetch user repositories
const [hasRepo] = await db
.select({ id: repositories.id })
.from(repositories)
.where(eq(repositories.userId, user.id))
.limit(1);
if (!hasRepo) {
await queue.add("fetchUserRepos", { userId: user.id });
}
await queue.add(
"fetchUserProfile",
{ userId: user.id },
{ jobId: `fetchUserProfile:${user.id}` }
);
await queue.add(
"fetchUserRepos",
{ userId: user.id },
{ jobId: `fetchUserRepos:${user.id}` }
);
const authToken = await jwt.sign({ id: user.id }, JWT_SECRET);
setCookie(c, "token", authToken, { httpOnly: true });
+26 -61
View File
@@ -1,8 +1,8 @@
import db from "@server/db";
import { repositories, users } from "@server/models";
import { desc, eq, getTableColumns, sql } from "drizzle-orm";
import {
GetLeaderboardRes,
LeaderboardRepository,
} from "@server/repository/leaderboard";
import { Hono } from "hono";
import { HTTPException } from "hono/http-exception";
export const leaderboard = new Hono()
@@ -10,12 +10,25 @@ export const leaderboard = new Hono()
* Get users leaderboard
*/
.get("/", async (c) => {
const rows = await db
.select()
.from(users)
.orderBy(desc(users.points))
.limit(100);
const result = rows.map((data, idx) => ({ ...data, rank: idx + 1 }));
const query = c.req.query();
const repo = new LeaderboardRepository();
let result: GetLeaderboardRes;
switch (query.type) {
case "lang":
result = await repo.getTopLanguages();
break;
case "lang-users":
result = await repo.getTopUsersByLang(query.lang);
break;
case "user":
case "":
result = await repo.getTopUsers();
break;
default:
throw new Error("Invalid query type");
}
return c.json(result);
})
@@ -25,56 +38,8 @@ export const leaderboard = new Hono()
*/
.get("/:username", async (c) => {
const { username } = c.req.param();
const repo = new LeaderboardRepository();
const result = await repo.getUserRank(username);
const rankSubquery = db
.select({
userId: users.id,
value: sql`rank() over (order by points desc)`.as("rank"),
})
.from(users)
.orderBy(desc(users.points))
.as("rank");
const [user] = await db
.select({
...getTableColumns(users),
accessToken: sql`null`,
rank: rankSubquery.value,
})
.from(users)
.leftJoin(rankSubquery, eq(users.id, rankSubquery.userId))
.where(eq(users.username, username));
if (!user) {
throw new HTTPException(404, { message: "User not found!" });
}
const repos = await db
.select()
.from(repositories)
.where(eq(repositories.userId, user.id))
.orderBy(desc(repositories.stars), desc(repositories.forks));
const languageMap: Record<string, number> = {};
repos
.flatMap((i) => i.languages || [])
.forEach((i) => {
if (!languageMap[i.lang]) languageMap[i.lang] = 0;
languageMap[i.lang] += i.amount;
});
const totalLangWeight = Object.values(languageMap).reduce(
(a, b) => a + b,
0
);
let languages = [] as { name: string; percent: number }[];
for (const [lang, amount] of Object.entries(languageMap)) {
languages.push({
name: lang,
percent: (amount / totalLangWeight) * 100,
});
}
languages = languages.sort((a, b) => b.percent - a.percent);
return c.json({ user, repositories: repos, languages });
return c.json(result);
});