mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
118 lines
4.0 KiB
JavaScript
118 lines
4.0 KiB
JavaScript
import { mutation, query } from "./_generated/server";
|
|
import { v } from "convex/values";
|
|
function hashCode(str) {
|
|
let hash = 0;
|
|
for (let i = 0; i < str.length; i++) {
|
|
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0;
|
|
}
|
|
return Math.abs(hash);
|
|
}
|
|
async function generateUniqueReferralCode(db, email) {
|
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
const input = attempt === 0 ? email : `${email}:${attempt}`;
|
|
const code = hashCode(input).toString(36).padStart(6, "0").slice(0, 8);
|
|
const existing = await db
|
|
.query("registrations")
|
|
.withIndex("by_referral_code", (q) => q.eq("referralCode", code))
|
|
.first();
|
|
if (!existing)
|
|
return code;
|
|
}
|
|
// Fallback: timestamp-based code (extremely unlikely path)
|
|
return Date.now().toString(36).slice(-8);
|
|
}
|
|
async function getCounter(db, name) {
|
|
const counter = await db
|
|
.query("counters")
|
|
.withIndex("by_name", (q) => q.eq("name", name))
|
|
.first();
|
|
return counter?.value ?? 0;
|
|
}
|
|
async function incrementCounter(db, name) {
|
|
const counter = await db
|
|
.query("counters")
|
|
.withIndex("by_name", (q) => q.eq("name", name))
|
|
.first();
|
|
const newVal = (counter?.value ?? 0) + 1;
|
|
if (counter) {
|
|
await db.patch(counter._id, { value: newVal });
|
|
}
|
|
else {
|
|
await db.insert("counters", { name, value: newVal });
|
|
}
|
|
return newVal;
|
|
}
|
|
export const register = mutation({
|
|
args: {
|
|
email: v.string(),
|
|
source: v.optional(v.string()),
|
|
appVersion: v.optional(v.string()),
|
|
referredBy: v.optional(v.string()),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const normalizedEmail = args.email.trim().toLowerCase();
|
|
const existing = await ctx.db
|
|
.query("registrations")
|
|
.withIndex("by_normalized_email", (q) => q.eq("normalizedEmail", normalizedEmail))
|
|
.first();
|
|
if (existing) {
|
|
let code = existing.referralCode;
|
|
if (!code) {
|
|
code = await generateUniqueReferralCode(ctx.db, normalizedEmail);
|
|
await ctx.db.patch(existing._id, { referralCode: code });
|
|
}
|
|
return {
|
|
status: "already_registered",
|
|
referralCode: code,
|
|
referralCount: existing.referralCount ?? 0,
|
|
};
|
|
}
|
|
const referralCode = await generateUniqueReferralCode(ctx.db, normalizedEmail);
|
|
// Credit the referrer
|
|
if (args.referredBy) {
|
|
const referrer = await ctx.db
|
|
.query("registrations")
|
|
.withIndex("by_referral_code", (q) => q.eq("referralCode", args.referredBy))
|
|
.first();
|
|
if (referrer) {
|
|
await ctx.db.patch(referrer._id, {
|
|
referralCount: (referrer.referralCount ?? 0) + 1,
|
|
});
|
|
}
|
|
}
|
|
const position = await incrementCounter(ctx.db, "registrations_total");
|
|
await ctx.db.insert("registrations", {
|
|
email: args.email.trim(),
|
|
normalizedEmail,
|
|
registeredAt: Date.now(),
|
|
source: args.source ?? "unknown",
|
|
appVersion: args.appVersion ?? "unknown",
|
|
referralCode,
|
|
referredBy: args.referredBy,
|
|
referralCount: 0,
|
|
});
|
|
return {
|
|
status: "registered",
|
|
referralCode,
|
|
referralCount: 0,
|
|
position,
|
|
};
|
|
},
|
|
});
|
|
export const getPosition = query({
|
|
args: { referralCode: v.string() },
|
|
handler: async (ctx, args) => {
|
|
const reg = await ctx.db
|
|
.query("registrations")
|
|
.withIndex("by_referral_code", (q) => q.eq("referralCode", args.referralCode))
|
|
.first();
|
|
if (!reg)
|
|
return null;
|
|
const total = await getCounter(ctx.db, "registrations_total");
|
|
return {
|
|
referralCount: reg.referralCount ?? 0,
|
|
total,
|
|
};
|
|
},
|
|
});
|