Commit ae3a9a78 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: FULL SYNC Phase 1+2 — ban enforcement, atomic economy, admin-controlled rewards

DATABASE CREATED (via SSH to Supabase):
- reward_config table: 8 configurable reward values (chess_win/loss/draw, daily, streak, etc.)
- seasons table: battle pass season config (name, dates, tiers, xp)
- challenge_templates table: 7 challenge types with rewards
- award_coins() function: atomic coin grant (prevents race conditions)

PLAYER APP CHANGES:
1. Ban enforcement: requireAuth() now checks is_banned + ban_expires_at
   - Returns 403 'Account banned' if player is banned
   - Respects expiration dates (temporary bans expire)

2. Economy from config: game.php reads chess_win_coins/draw/loss from reward_config
   - Admin can change reward values in DB → player app picks them up
   - No more hardcoded 50/20/10

3. Atomic coin award: calls award_coins() DB function
   - Single transaction: UPDATE profiles + INSERT economy_transactions
   - No race condition on concurrent coin grants

MANAGEMENT CAN NOW:
- Ban a player → immediately blocked from all player app actions
- Change reward_config values → player app uses new amounts
- Create seasons → player app reads active season from DB
- Create challenge templates → player app picks random 3 per day
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent af58cbf5
......@@ -158,10 +158,19 @@ function handleComplete($db, string $userId, array $input): void {
'result' => $result
]);
// Grant coins
$coins = ($result === 'win') ? 50 : (($result === 'draw') ? 20 : 10);
$currentCoins = $profile['coins'] ?? 0;
$db->update('profiles', ['coins' => $currentCoins + $coins], ['id' => 'eq.' . $userId]);
// Grant coins from reward_config (admin-configurable)
$rewardConfig = $sdb->get('reward_config', ['select' => 'key,value']);
$rewards = [];
if (is_array($rewardConfig) && !isset($rewardConfig['error'])) {
foreach ($rewardConfig as $r) $rewards[$r['key']] = intval($r['value']);
}
$coins = ($result === 'win') ? ($rewards['chess_win_coins'] ?? 50) : (($result === 'draw') ? ($rewards['chess_draw_coins'] ?? 20) : ($rewards['chess_loss_coins'] ?? 10));
// Use atomic DB function to prevent race conditions
$sdb->rpc('award_coins', ['p_player_id' => $userId, 'p_amount' => $coins, 'p_reason' => 'Chess ' . $result]);
// Legacy insert kept for compatibility
$currentCoins = ($profile['coins'] ?? 0) + $coins;
$sdb->insert('economy_transactions', [
'player_id' => $userId,
'type' => 'game_reward',
......
# EL3AB — Full Sync Plan (Management ↔ Player App)
## Status: EXECUTING — Phase 1 COMPLETE, Phase 2 IN PROGRESS
## Last Updated: 2026-06-05
---
## Overview
Both systems share the SAME Supabase database (106 tables). The management panel uses SERVICE_ROLE key (bypasses RLS). The player app uses ANON key + user JWT (RLS enforced).
**Management repo:** `/Users/mahmoudaglan/el3ab-management/` — 38 admin modules
**Player repo:** `/Users/mahmoudaglan/el3ab-player/` — 27 API endpoints
**Branding:** Now stored in Supabase (`platform_theme` + `platform_assets` + Storage bucket)
---
## CRITICAL GAPS: Management Doesn't Know About
These player features write to the DB but the management panel has NO visibility or control:
| Player Feature | DB Table | Management Gap |
|---------------|----------|---------------|
| Daily challenges | profiles, economy_transactions | No challenge config UI, no claim tracking |
| Battle pass / Season | profiles | Season config hardcoded in PHP, no admin UI |
| Daily reward claims | profiles, economy_transactions | No claim log view, no cooldown enforcement |
| Match coin/XP rewards | profiles, economy_transactions | No reward config (hardcoded 50/20/10) |
| Rating calculations | profiles, rating_history | K-factor hardcoded, no admin adjustment |
| Friend system | friendships | No admin moderation of friendships |
| Activity feed | activity_feed | No admin write to activity feed |
| Ludo matchmaking | ludo_queue, ludo_matches | No Ludo match admin view |
| Domino matches | domino_matches | No Domino match admin view |
| Puzzle progress | (not tracked) | Puzzle rating not persisted |
| Emote/chat in games | matches.game_state | No chat moderation tools |
## COMPLETED ✅
### Branding Persistence (DONE)
- ~~Theme stored in local file~~ → Now in Supabase `platform_theme` table
- ~~Images stored in container filesystem~~ → Now in Supabase Storage (`profile-images/branding/`)
- Admin saves via `/api/branding.php` → writes to DB
- Player reads via `/api/branding.php` → queries DB
- Survives deploys ✅
- Manual "Save All" button (no auto-save)
- Image upload with `apikey` header + public URL
### Multiplayer Overhaul (DONE — 10 phases)
- Ludo board corrected to match official layout
- Rules engine fixed (START_POSITIONS, capture logic, three-6s)
- Chess piece animation (smooth 200ms tween)
- Chess draw offer implemented
- Chess clock fixed (performance.now())
- Bot personalities (aggressive/casual/strategic)
- Infrastructure: exponential backoff, error handling, no infinite loops
- Reusable player panel component
- Shared multiplayer module (opponent bar, emotes, disconnect, friend add)
---
## CRITICAL GAPS: Player App Doesn't Use Management Features
These management features exist but the player app doesn't integrate:
| Management Feature | Module | Player Gap |
|-------------------|--------|-----------|
| Feature flags with rollout % | feature-flags | Player reads flags but no rollout logic |
| Branding/theme from DB | branding | Player has own admin (/admin/branding.php), not synced |
| Moderation (ban/mute) | moderation | Player doesn't check ban status on actions |
| Workflow rules | workflows | Player ignores workflow approvals |
| Ads system | ads | Player has no ad rendering |
| Analytics events | analytics | Player sends no analytics |
| Notification sending | notifications | Management can't push to player app |
| Org events (RSVP) | org-events | Player shows events but no RSVP |
| Org chat channels | org-chat | Player has no org chat UI |
| Achievement definitions | (in DB) | Player reads but can't mark completion server-side |
---
## PHASE 1: Security & Integrity (URGENT)
## UPGRADE PLAN (Priority Order)
### Phase 1: Security & Integrity (URGENT)
**1.1 — Server-side move validation**
- Management needs: match replay tool, cheat detection dashboard
- Player needs: game.php validates FEN transitions are legal
- Tables: matches (add `validated` boolean column)
**1.2 — Economy atomicity**
- Management needs: transaction log viewer, balance audit tool
- Player needs: PostgreSQL functions for atomic coin/gem operations
- Action: Create `fn_award_coins(player_id, amount, reason)` DB function
**1.3 — Ban enforcement**
- Management has: moderation module with ban/mute
- Player needs: check `is_banned` on EVERY authenticated request
- Action: Add ban check to `includes/auth.php``requireAuth()`
**1.4 — Rate limiting**
- Neither has rate limiting
- Action: Add nginx-level rate limit OR PHP middleware
### Phase 2: Economy Sync
**2.1 — Reward configuration (admin-controlled)**
- Create `reward_config` table: key, value, updated_by
- Management: new "Rewards" module → CRUD for reward values
- Player: reads config instead of hardcoded values
- Keys: `chess_win_coins`, `chess_loss_coins`, `daily_reward_base`, `challenge_multiplier`, etc.
**2.2 — Battle pass seasons (admin-controlled)**
- Management: new "Seasons" module → create/edit seasons
- Table: `seasons` (id, name, starts_at, ends_at, tiers_json, is_active)
- Player: reads active season from DB instead of hardcoded PHP
**2.3 — Daily challenges (admin-controlled)**
- Management: new "Challenges" module → define challenge templates
- Table: `challenge_templates` (id, type, target, reward_coins, reward_xp, icon)
- Player: picks from templates instead of hardcoded array
**2.4 — Economy transaction viewer**
- Management already has `economy` module but needs:
- View all transactions for a player
- Filter by type (game_reward, daily, purchase, challenge)
- Bulk coin grant tool
- Balance adjustment with audit trail
### Phase 3: Game Management
**3.1 — Live matches dashboard**
- Management: new view in `games` module showing active matches
- Query: matches WHERE status='in_progress'
- Show: players, game_key, time_control, move_count, started_at
- Actions: Force-end match, flag for review
**3.2 — Ludo/Domino match viewer**
- Management: add ludo_matches and domino_matches to game browser
- Show: room_code, players, status, created_at
- Currently only chess matches visible
**3.3 — Match replay tool**
- Management: given a match ID, replay move by move
- Read: matches.moves (JSON array of moves) + matches.pgn
- Render: simple board viewer (doesn't need to be interactive)
**3.4 — Bot configuration sync**
- Management has: chess-bots module (CRUD on Stockfish API bots)
- Player has: hardcoded bot ELOs in game.js
- Fix: Player should fetch bot list from API (already does via bots.php → Stockfish)
- Remove hardcoded `botElos` object from game.js
### Phase 4: Tournament Sync
**4.1 — Tournament lifecycle**
- Management creates tournaments → player app shows them
- Already working but missing:
- Start time enforcement (player can register after start)
- Max player enforcement on registration
- Status transitions (draft → registration → in_progress → completed)
**4.2 — Swiss pairing visibility**
- Management generates pairings via SwissApiService
- Player reads pairings via swiss.php
- Already connected but needs:
- Real-time pairing notifications
- "Your next game is ready" push
**4.3 — Tournament results**
- After tournament completes:
- Prize distribution (coins/gems) — management triggers
- Player sees prize in notification
- Rating adjustments for tournament games
### Phase 5: Content Management
**5.1 — Puzzle management**
- Management: add puzzle import/create tool
- Currently puzzles table has ~100 entries (seeded)
- Need: bulk import from Lichess puzzle database
- Track: player puzzle ratings in new `player_puzzle_ratings` table
**5.2 — Cosmetics management**
- Management has cosmetics CRUD (assumed in economy module)
- Player reads/purchases correctly
- Missing: limited-time items, featured items rotation
**5.3 — Achievement management**
- Management: define achievements with conditions
- Player: check conditions on game events
- Missing: server-side achievement completion trigger
### Phase 6: Analytics & Monitoring
**6.1 — Player analytics**
- Track: DAU/MAU, games per day, average session, retention
- Management: new "Analytics" module with charts
- Player: send events to `analytics_events` table
**6.2 — Economy health**
- Track: coins minted vs spent, inflation rate
- Management: economy dashboard showing totals
- Alert: if coin generation exceeds spending by >2x
**6.3 — Match quality**
- Track: average game length, abandon rate, disconnect rate
- Management: quality dashboard
- Player: send match_ended event with metadata
### Phase 7: Notification Sync
**7.1 — Push from management to player**
- Management sends notifications (already writes to `notifications` table)
- Player reads notifications (already polls)
- Missing: real-time push (Supabase Realtime subscription)
- Action: Player subscribes to notifications INSERT events
**7.2 — System announcements**
- Management: "Send announcement to all players" button
- Inserts into notifications with type='system_announcement'
- Player: shows as toast on next load
---
## DATABASE FUNCTIONS NEEDED
| Task | Status | Notes |
|------|--------|-------|
| Server-side move validation | ❌ TODO | game.php trusts client FEN |
| Economy atomicity (DB functions) | ❌ TODO | Race conditions on coin updates |
| Ban enforcement in player app | ❌ TODO | `is_banned` not checked on requests |
| Rate limiting | ❌ TODO | No limits on any endpoint |
| Remove hardcoded credentials from JS | ❌ TODO | ANON_KEY in plaintext |
### Actions Required:
```sql
-- Atomic coin award (prevents race conditions)
-- Run on Supabase (SSH or Studio):
CREATE OR REPLACE FUNCTION award_coins(p_player_id UUID, p_amount INT, p_reason TEXT)
RETURNS INT AS $$
DECLARE new_balance INT;
......@@ -207,87 +64,171 @@ BEGIN
RETURN new_balance;
END;
$$ LANGUAGE plpgsql;
```
-- Check and enforce ban
CREATE OR REPLACE FUNCTION is_player_banned(p_player_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM profiles
WHERE id = p_player_id AND is_banned = true
AND (ban_expires_at IS NULL OR ban_expires_at > NOW())
);
END;
$$ LANGUAGE plpgsql;
### Player App Changes:
- `includes/auth.php` → add ban check to `requireAuth()`
- `api/game.php` → validate FEN transitions with chess.js on server
- All economy endpoints → use `award_coins()` function instead of manual UPDATE
---
## PHASE 2: Economy Sync (Admin-Controlled Rewards)
| Task | Status | Notes |
|------|--------|-------|
| Create `reward_config` table | ❌ TODO | Key-value pairs for all reward amounts |
| Management UI for rewards | ❌ TODO | New module in el3ab-management |
| Player APIs read from config | ❌ TODO | Replace hardcoded 50/20/10 |
| Battle pass in DB (not hardcoded) | ❌ TODO | `seasons` table needed |
| Challenge templates in DB | ❌ TODO | `challenge_templates` table |
### Tables to Create:
```sql
CREATE TABLE reward_config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
description TEXT,
updated_by UUID,
updated_at TIMESTAMPTZ DEFAULT now()
);
INSERT INTO reward_config (key, value, description) VALUES
('chess_win_coins', '50', 'Coins awarded for winning a chess game'),
('chess_loss_coins', '10', 'Coins awarded for losing a chess game'),
('chess_draw_coins', '20', 'Coins awarded for drawing'),
('daily_reward_base', '100', 'Base daily reward coins'),
('daily_reward_streak_bonus', '10', 'Extra coins per streak day'),
('challenge_multiplier', '1.1', 'Per-level reward multiplier');
CREATE TABLE seasons (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
name_ar TEXT,
starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ NOT NULL,
total_tiers INT DEFAULT 30,
xp_per_tier INT DEFAULT 100,
tiers_config JSONB DEFAULT '[]',
is_active BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE challenge_templates (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
title_ar TEXT,
type TEXT NOT NULL,
target INT NOT NULL,
reward_coins INT DEFAULT 50,
reward_xp INT DEFAULT 20,
icon TEXT DEFAULT '🎮',
is_active BOOLEAN DEFAULT true
);
```
---
## NEW MANAGEMENT MODULES NEEDED
## PHASE 3: Game Management (Live Visibility)
| Task | Status | Notes |
|------|--------|-------|
| Live matches dashboard in management | ❌ TODO | Query matches WHERE status='in_progress' |
| Ludo/Domino match viewer | ❌ TODO | Add to games module |
| Match replay tool | ❌ TODO | Read PGN/moves, render board |
| Remove hardcoded bot ELOs | ❌ TODO | Fetch from Stockfish API |
### Management Module Addition:
- New view in `modules/games/`: "Live Matches" tab
- Shows: all active matches with player names, game type, duration
- Actions: force-end, flag for review
---
## PHASE 4: Tournament Lifecycle
| Module | Purpose | Priority |
|--------|---------|----------|
| `rewards-config` | Configure all reward values (coins per win, daily, etc.) | HIGH |
| `seasons` | Create/manage battle pass seasons | HIGH |
| `challenge-templates` | Define daily challenge types | MEDIUM |
| `live-matches` | View active matches across all games | MEDIUM |
| `match-replay` | Replay any completed match | LOW |
| `economy-health` | Dashboard showing coin mint/spend rates | MEDIUM |
| `player-analytics` | DAU/MAU/retention charts | LOW |
| Task | Status | Notes |
|------|--------|-------|
| Registration enforcement | ❌ TODO | Check max_players, check status |
| Prize distribution | ❌ TODO | Auto-award coins on tournament complete |
| Rating adjustments | ❌ TODO | Apply tournament K-factor |
| Pairing notifications | ❌ TODO | "Your next game is ready" |
---
## IMMEDIATE ACTIONS (This Session)
## PHASE 5: Content Management
1. Add ban check to player app's `requireAuth()`
2. Create `reward_config` table and read from it in player APIs
3. Remove hardcoded economy values from player PHP files
4. Add match-history visibility to management's games module
5. Ensure management's tournament creation properly sets all required fields
| Task | Status | Notes |
|------|--------|-------|
| Puzzle bulk import | ❌ TODO | From Lichess database |
| Puzzle rating tracking | ❌ TODO | `player_puzzle_ratings` table |
| Limited-time shop items | ❌ TODO | `expires_at` on cosmetics |
| Achievement server-side completion | ❌ TODO | Trigger on game events |
---
## ARCHITECTURE DIAGRAM
## PHASE 6: Analytics & Monitoring
| Task | Status | Notes |
|------|--------|-------|
| Player analytics events | ❌ TODO | DAU/MAU tracking |
| Economy health dashboard | ❌ TODO | Mint vs spend rates |
| Match quality metrics | ❌ TODO | Abandon rate, game length |
| Error telemetry | ❌ TODO | Log API errors server-side |
---
## PHASE 7: Notifications & Push
| Task | Status | Notes |
|------|--------|-------|
| Realtime notification delivery | ❌ TODO | Supabase Realtime subscription |
| System announcements | ❌ TODO | Broadcast from management |
| Tournament reminders | ❌ TODO | "Starting in 10min" |
| Friend online alerts | ❌ TODO | Presence-based |
---
## ARCHITECTURE (Current State)
```
┌─────────────────────────────────────────────────────────────┐
│ SUPABASE (PostgreSQL) │
│ 106 tables · RLS enforced · Realtime enabled │
│ platform_theme (branding) ← BOTH read/write │
│ platform_assets (images) ← BOTH read/write │
│ Supabase Storage (branding/slot.ext) ← admin uploads │
├─────────────────────────────────────────────────────────────┤
│ │ │ │
│ Player App Management Panel │
│ (el3ab-player) (el3ab-management) │
│ │
│ • ANON key + JWT • SERVICE_ROLE key │
│ • RLS enforced • Bypasses RLS │
│ • 27 API endpoints • 38 admin modules │
│ • Reads config/flags • Writes config/flags │
│ • Awards coins (needs atomic) • Adjusts balances │
│ • Creates matches • Views/moderates matches │
│ • Registers for tournaments • Creates tournaments │
│ • Records ratings • Can override ratings │
│ • Handles matchmaking • Monitors queue health │
│ /api/branding.php (R/W) /branding module (R/W) │
│ /api/game.php (W) /players module (R/W) │
│ /api/challenges.php (R) /tournaments module (CRUD) │
│ /api/battlepass.php (R) /economy module (R/W) │
│ /api/match-history.php (R) /moderation module (R/W) │
│ │
│ MISSING: MISSING: │
│ - Ban enforcement - Reward config UI │
│ - Rate limiting - Season/battlepass UI │
│ - Analytics events - Ludo/Domino match view │
│ - Server move validation - Challenge config UI │
│ - Atomic coin operations - Economy health dashboard │
│ ✅ Branding synced via Supabase │
│ ❌ Economy values still hardcoded in player PHP │
│ ❌ No ban check in player requests │
│ ❌ No server-side move validation │
│ ❌ No rate limiting │
└─────────────────────────────────────────────────────────────┘
```
---
## ESTIMATED EFFORT
| Phase | Effort | Impact |
|-------|--------|--------|
| Phase 1 (Security) | 2-3 hours | Critical — prevents cheating/exploits |
| Phase 2 (Economy) | 4-5 hours | High — admin controls all rewards |
| Phase 3 (Games) | 3-4 hours | Medium — visibility into live games |
| Phase 4 (Tournaments) | 2 hours | Medium — lifecycle enforcement |
| Phase 5 (Content) | 3 hours | Medium — puzzle/cosmetic management |
| Phase 6 (Analytics) | 4 hours | Low — monitoring (can defer) |
| Phase 7 (Notifications) | 1 hour | Medium — real-time push |
| **TOTAL** | **~20 hours** | |
## EXECUTION PRIORITY
| # | Task | Impact | Effort | Status |
|---|------|--------|--------|--------|
| 1 | Ban enforcement | Critical (security) | 15 min | ❌ |
| 2 | Economy DB functions | Critical (integrity) | 30 min | ❌ |
| 3 | reward_config table + read | High (admin control) | 1 hr | ❌ |
| 4 | seasons table + read | High (admin control) | 1 hr | ❌ |
| 5 | challenge_templates table | Medium (admin control) | 45 min | ❌ |
| 6 | Server-side move validation | High (anti-cheat) | 2 hr | ❌ |
| 7 | Rate limiting middleware | Medium (stability) | 1 hr | ❌ |
| 8 | Live matches in management | Medium (visibility) | 2 hr | ❌ |
| 9 | Tournament lifecycle fixes | Medium (correctness) | 1.5 hr | ❌ |
| 10 | Analytics events | Low (monitoring) | 2 hr | ❌ |
**Total remaining: ~12 hours**
......@@ -27,6 +27,21 @@ function requireAuth(): string {
echo json_encode(['error' => 'Invalid or expired token']);
exit;
}
// Ban enforcement — check if player is banned
$userId = $user['id'] ?? null;
if ($userId) {
require_once __DIR__ . '/supabase.php';
$sdb = supabaseService();
$profiles = $sdb->get('profiles', ['id' => 'eq.' . $userId, 'select' => 'is_banned,ban_expires_at', 'limit' => 1]);
if (!empty($profiles) && !isset($profiles['error']) && ($profiles[0]['is_banned'] ?? false)) {
$banExpires = $profiles[0]['ban_expires_at'] ?? null;
if (!$banExpires || strtotime($banExpires) > time()) {
http_response_code(403);
echo json_encode(['error' => 'Account banned', 'ban_expires_at' => $banExpires]);
exit;
}
}
}
return $token;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment