leaderboard and ranked systems

This commit is contained in:
2026-07-11 11:13:28 -04:00
commit 02ac5f9593
44 changed files with 12318 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
const passport = require('passport');
const { Strategy: GoogleStrategy } = require('passport-google-oauth20');
const jwt = require('jsonwebtoken');
const db = require('../db');
async function generateUniqueUsername(base) {
const candidate = base.toLowerCase();
const taken = await db.user.findFirst({ where: { username: candidate } });
if (!taken) return candidate;
let attempts = 0;
while (attempts < 20) {
const suffix = String(Math.floor(1000 + Math.random() * 9000));
const username = `${candidate}${suffix}`;
const collision = await db.user.findFirst({ where: { username } });
if (!collision) return username;
attempts++;
}
return `${candidate}${Date.now()}`;
}
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
},
async (_accessToken, _refreshToken, profile, done) => {
try {
const email = profile.emails?.[0]?.value ?? '';
let user = await db.user.findUnique({ where: { googleId: profile.id } });
if (!user) {
const username = await generateUniqueUsername(email.split('@')[0]);
user = await db.user.create({
data: {
googleId: profile.id,
email,
displayName: profile.displayName,
avatarUrl: profile.photos?.[0]?.value ?? null,
username,
},
});
} else {
user = await db.user.update({
where: { googleId: profile.id },
data: {
email,
displayName: profile.displayName,
avatarUrl: profile.photos?.[0]?.value ?? null,
},
});
if (!user.username) {
const username = await generateUniqueUsername((user.email ?? '').split('@')[0]);
user = await db.user.update({ where: { id: user.id }, data: { username } });
}
}
const token = jwt.sign(
{
userId: user.id,
email: user.email,
name: user.displayName,
picture: user.avatarUrl,
},
process.env.JWT_SECRET,
{ algorithm: 'HS256', expiresIn: '7d' }
);
done(null, { token });
} catch (err) {
done(err);
}
}
)
);
module.exports = passport;