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

+23
View File
@@ -1107,6 +1107,8 @@
"transactions": "Transazioni",
"vouchers": "Buoni",
"subscriptions": "Abbonamenti",
"referrals": "Inviti",
"dailyRewards": "Ricompense giornaliere",
"rareValues": "Valori rari",
"badges": "Distintivi",
"achievements": "Trofei",
@@ -4388,6 +4390,8 @@
"vouchers": "Voucher",
"subscriptions": "Abbonamenti",
"calendar": "Calendario",
"referrals": "Inviti",
"dailyRewards": "Ricompense giornaliere",
"history": "Cronologia",
"moderationTab": "Moderazione",
"monitoring": "Monitoraggio",
@@ -6282,6 +6286,7 @@
"login": "Accedi",
"never": "Mai",
"claimed": "Premio riscattato!",
"dailyClaimed": "Ricompensa giornaliera riscattata — torna domani!",
"welcome": "Il tuo spazio su {hotel}",
"noMotto": "Nessun motto",
"registered": "Iscritto dal {date}",
@@ -6334,6 +6339,13 @@
"ratelimit": "Troppi tentativi. Attendi un momento e riprova.",
"error": "Non è stato possibile riscattare il premio. Riprova."
},
"dailyErrors": {
"already_claimed": "Hai già riscattato la ricompensa giornaliera di oggi.",
"disabled": "Le ricompense giornaliere sono attualmente disattivate.",
"bad_config": "Il programma delle ricompense giornaliere non è configurato correttamente. Contatta lo staff.",
"ratelimit": "Troppi tentativi. Attendi un momento e riprova.",
"error": "Non è stato possibile riscattare la ricompensa giornaliera. Riprova."
},
"messagesCaughtUp": "Nessun messaggio da leggere",
"messagesUnavailable": "Apri la posta per controllare",
"eventTitle": "In programma",
@@ -6344,12 +6356,23 @@
"eventDetails": "Scopri l’evento",
"noUpcomingEvents": "Non sono ancora stati annunciati nuovi eventi.",
"roomsUnavailable": "Le stanze non sono al momento disponibili. Apri la community per riprovare.",
"dailyRewardTitle": "Ricompense giornaliere",
"dailyRewardHint": "Accedi ogni giorno e riscatta la ricompensa: la serie cresce fino ai giorni speciali.",
"dailyRewardDay": "Giorno {day}",
"dailyRewardStreak": "{count, plural, one {Serie: # giorno} other {Serie: # giorni}}",
"dailyRewardNextClaim": "Prossimo ritiro: {amount} {currency}",
"dailyRewardTomorrow": "Domani: {amount} {currency}",
"dailyRewardClaim": "Riscuoti la ricompensa giornaliera",
"dailyRewardClaimedToday": "Riscattata oggi — torna domani per la prossima ricompensa.",
"dailyRewardDisabled": "Le ricompense giornaliere sono attualmente disattivate. Puoi comunque esplorare l’hotel.",
"dailyRewardUnavailable": "Non è stato possibile caricare il programma delle ricompense giornaliere. Riprova.",
"tasks": {
"title": "Da fare",
"support": "{count, plural, one {# ticket con risposta del supporto} other {# ticket con risposte del supporto}}",
"friends": "{count, plural, one {# richiesta di amicizia da controllare} other {# richieste di amicizia da controllare}}",
"event": "Il prossimo evento a cui sei iscritto: {title} · {date}",
"reward": "Il premio per gli inviti è pronto da ritirare",
"dailyReward": "La tua ricompensa giornaliera è pronta da ritirare",
"empty": "Tutto a posto. Nessuna azione in attesa.",
"unavailable": "Non è stato possibile verificare alcune azioni. Quelle disponibili sono mostrate sopra.",
"retry": "Riprova",