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) {
case 'login':
handleLogin($input);
break;
case 'guest':
handleGuest();
break;
case 'refresh':
handleRefresh($input);
break;
......@@ -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 {
$refreshToken = $input['refresh_token'] ?? '';
if (!$refreshToken) {
......
......@@ -90,11 +90,23 @@ export function mountLogin(el) {
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');
const btn = el.querySelector('#guest-btn');
btn.disabled = true;
btn.textContent = '...';
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);
store.set('auth.guestId', crypto.randomUUID());
store.set('player', { username: 'ضيف', level: 1, coins: 0, gems: 0 });
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) {
`;
setTimeout(async () => {
if (store.get('auth.isGuest')) {
bus.emit('auth:guestSuccess');
return;
}
const token = store.get('auth.token');
if (token) {
try {
......@@ -34,7 +29,11 @@ export function mountSplash(el) {
if (profile && !profile.error) {
store.set('player', profile);
store.set('auth.userId', profile.id);
if (store.get('auth.isGuest')) {
bus.emit('auth:guestSuccess');
} else {
bus.emit('auth:success');
}
return;
}
} 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