Commit b3c62506 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: proper guest mode with Supabase anonymous auth

Guest login now creates a real anonymous Supabase user with token,
profile row, and refresh capability. Guest data persists across
sessions and page reloads properly.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 198833ff
...@@ -20,6 +20,9 @@ switch ($action) { ...@@ -20,6 +20,9 @@ switch ($action) {
case 'login': case 'login':
handleLogin($input); handleLogin($input);
break; break;
case 'guest':
handleGuest();
break;
case 'refresh': case 'refresh':
handleRefresh($input); handleRefresh($input);
break; break;
...@@ -123,6 +126,61 @@ function handleLogin(array $input): void { ...@@ -123,6 +126,61 @@ function handleLogin(array $input): void {
]); ]);
} }
function handleGuest(): void {
// Create an anonymous Supabase user — gives them a real token + profile
$ch = curl_init(SUPABASE_AUTH . '/signup');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'apikey: ' . SUPABASE_ANON_KEY,
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{}');
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
$res = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code >= 400 || !$res) {
jsonError('Guest login failed', 500);
}
$data = json_decode($res, true);
if (!isset($data['access_token']) || !isset($data['user']['id'])) {
jsonError('Guest login failed', 500);
}
$userId = $data['user']['id'];
// Create guest profile
$sdb = supabaseService();
$existing = $sdb->get('profiles', ['id' => 'eq.' . $userId, 'select' => 'id', 'limit' => 1]);
if (empty($existing) || isset($existing['error'])) {
$sdb->insert('profiles', [
'id' => $userId,
'display_name' => 'ضيف',
'username' => 'guest_' . substr($userId, 0, 8),
'coins' => 0,
'gems' => 0,
'xp' => 0,
'level' => 1,
'daily_streak' => 0,
]);
}
// Fetch profile to return
$db = supabase($data['access_token']);
$profiles = $db->get('profiles', ['id' => 'eq.' . $userId, 'select' => '*', 'limit' => 1]);
$profile = is_array($profiles) && !empty($profiles) && !isset($profiles['error']) ? $profiles[0] : null;
jsonResponse([
'access_token' => $data['access_token'],
'refresh_token' => $data['refresh_token'],
'user' => $data['user'],
'profile' => $profile ?? ['id' => $userId, 'display_name' => 'ضيف', 'level' => 1, 'coins' => 0, 'gems' => 0]
]);
}
function handleRefresh(array $input): void { function handleRefresh(array $input): void {
$refreshToken = $input['refresh_token'] ?? ''; $refreshToken = $input['refresh_token'] ?? '';
if (!$refreshToken) { if (!$refreshToken) {
......
...@@ -90,11 +90,23 @@ export function mountLogin(el) { ...@@ -90,11 +90,23 @@ export function mountLogin(el) {
window.location.href = `${SUPABASE_URL}/auth/v1/authorize?provider=google&redirect_to=${redirect}`; window.location.href = `${SUPABASE_URL}/auth/v1/authorize?provider=google&redirect_to=${redirect}`;
}); });
el.querySelector('#guest-btn').addEventListener('click', () => { el.querySelector('#guest-btn').addEventListener('click', async () => {
audio.play('click'); audio.play('click');
store.set('auth.isGuest', true); const btn = el.querySelector('#guest-btn');
store.set('auth.guestId', crypto.randomUUID()); btn.disabled = true;
store.set('player', { username: 'ضيف', level: 1, coins: 0, gems: 0 }); btn.textContent = '...';
bus.emit('auth:guestSuccess'); try {
const data = await net.post('auth.php', { action: 'guest' });
if (data.error) throw new Error(data.error);
store.set('auth.token', data.access_token);
store.set('auth.refreshToken', data.refresh_token);
store.set('auth.userId', data.user.id);
store.set('auth.isGuest', true);
if (data.profile) store.set('player', data.profile);
bus.emit('auth:guestSuccess');
} catch (e) {
btn.disabled = false;
btn.textContent = t('auth.guest_btn');
}
}); });
} }
...@@ -22,11 +22,6 @@ export function mountSplash(el) { ...@@ -22,11 +22,6 @@ export function mountSplash(el) {
`; `;
setTimeout(async () => { setTimeout(async () => {
if (store.get('auth.isGuest')) {
bus.emit('auth:guestSuccess');
return;
}
const token = store.get('auth.token'); const token = store.get('auth.token');
if (token) { if (token) {
try { try {
...@@ -34,7 +29,11 @@ export function mountSplash(el) { ...@@ -34,7 +29,11 @@ export function mountSplash(el) {
if (profile && !profile.error) { if (profile && !profile.error) {
store.set('player', profile); store.set('player', profile);
store.set('auth.userId', profile.id); store.set('auth.userId', profile.id);
bus.emit('auth:success'); if (store.get('auth.isGuest')) {
bus.emit('auth:guestSuccess');
} else {
bus.emit('auth:success');
}
return; return;
} }
} catch (e) {} } catch (e) {}
......
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