feat(referrals): add referral attribution and daily login rewards
CI / check (push) Successful in 4m25s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m54s

- Track referral attribution at registration via ?ref code with
  same-IP and duplicate-pair guards
- Add daily login rewards with streak tracking, claim flow and
  sendCurrency payout backed by RCON with DB fallback
- Add admin pages for referral settings and the daily reward schedule
- Add migration 0033 with tables, seed schedule, settings and ACL grants
- Add admin.referrals.* and admin.dailyrewards.* permission slugs
- Localize new copy in en, nl and it
This commit is contained in:
openhands committed 2026-09-20 12:29:01 +02:00
1 parent 463bc2cb47
commit c3ff497050
30 files changed
+2028 -11

No files matched your search

+33
View File
@@ -714,6 +714,39 @@ export const ClaimedReferralLogs = mysqlTable("claimed_referral_logs", {
updatedAt: timestamp("updated_at"),
});
export const WebsiteDailyRewards = mysqlTable("website_daily_rewards", {
id: int("id", { unsigned: true }).autoincrement().primaryKey().notNull(),
day: int("day", { unsigned: true }).notNull().default(1),
currency: varchar("currency", { length: 20 }).notNull().default("credits"),
amount: int("amount").notNull().default(0),
createdAt: datetime("created_at", { fsp: 3 })
.notNull()
.default(sql`CURRENT_TIMESTAMP(3)`),
updatedAt: datetime("updated_at", { fsp: 3 })
.notNull()
.default(sql`CURRENT_TIMESTAMP(3)`),
});
export const WebsiteDailyRewardClaims = mysqlTable(
"website_daily_reward_claims",
{
id: bigint("id", { mode: "bigint", unsigned: true })
.autoincrement()
.primaryKey()
.notNull(),
userId: int("user_id").notNull(),
claimDate: dateAsDate("claim_date").notNull(),
streak: int("streak", { unsigned: true }).notNull().default(1),
rewardDay: int("reward_day", { unsigned: true }).notNull().default(1),
currency: varchar("currency", { length: 20 }).notNull().default("credits"),
amount: int("amount").notNull().default(0),
createdAt: datetime("created_at", { fsp: 3 })
.notNull()
.default(sql`CURRENT_TIMESTAMP(3)`),
},
(t) => [uniqueIndex("daily_claim_user_date_uk").on(t.userId, t.claimDate)],
);
export const WebsiteHousekeepingPermissions = mysqlTable(
"website_housekeeping_permissions",
{