305 lines
8.9 KiB
PHP
305 lines
8.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Game;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
class UserController extends Controller
|
|
{
|
|
/**
|
|
* GET /api/users
|
|
* Lista todos os utilizadores (Apenas para Admins)
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorize('viewAny', User::class);
|
|
|
|
$query = User::query();
|
|
|
|
// Filtros úteis para o Backoffice
|
|
if ($request->has('type')) {
|
|
$query->where('type', $request->type);
|
|
}
|
|
|
|
if ($request->has('search')) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('name', 'like', '%'.$request->search.'%')
|
|
->orWhere('email', 'like', '%'.$request->search.'%')
|
|
->orWhere('nickname', 'like', '%'.$request->search.'%');
|
|
});
|
|
}
|
|
|
|
return response()->json($query->paginate(15));
|
|
}
|
|
|
|
/**
|
|
* POST /api/users
|
|
* Registar um novo utilizador (Admin only)
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorize('create', User::class);
|
|
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'nickname' => 'required|string|max:20|unique:users,nickname',
|
|
'email' => 'required|email|unique:users,email',
|
|
'password' => 'required|string|min:3',
|
|
'type' => 'required|in:A,U',
|
|
]);
|
|
|
|
$validated['password'] = Hash::make($validated['password']);
|
|
|
|
$user = User::create($validated);
|
|
|
|
return response()->json($user, 201);
|
|
}
|
|
|
|
/**
|
|
* GET /api/users/me
|
|
* Retorna o perfil do utilizador autenticado atual.
|
|
* Usado pelo VueJS para saber quem está logado.
|
|
*/
|
|
public function me(Request $request)
|
|
{
|
|
return response()->json($request->user());
|
|
}
|
|
|
|
/**
|
|
* GET /api/users/{user}
|
|
* Mostra um perfil específico.
|
|
*/
|
|
public function show(User $user)
|
|
{
|
|
$currentUser = Auth::user();
|
|
|
|
// Admins and owners get full profile, others get limited view
|
|
if ($currentUser->type === 'A' || $currentUser->id === $user->id) {
|
|
return response()->json($user);
|
|
} else {
|
|
return response()->json([
|
|
'id' => $user->id,
|
|
'name' => $user->name,
|
|
'nickname' => $user->nickname,
|
|
'photo_avatar_filename' => $user->photo_avatar_filename,
|
|
'type' => $user->type,
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PUT/PATCH /api/users/{user}
|
|
* Atualizar perfil (Nome, Nickname, Password, Foto)
|
|
*/
|
|
public function update(Request $request, User $user)
|
|
{
|
|
$this->authorize('update', $user);
|
|
|
|
$validated = $request->validate([
|
|
'name' => 'sometimes|string|max:255',
|
|
'nickname' => [
|
|
'sometimes',
|
|
'string',
|
|
'max:20',
|
|
Rule::unique('users')->ignore($user->id),
|
|
],
|
|
'email' => [
|
|
'sometimes',
|
|
'email',
|
|
Rule::unique('users')->ignore($user->id),
|
|
],
|
|
'password' => 'sometimes|string|min:3',
|
|
]);
|
|
|
|
// Lógica de Upload de Foto (Exemplo Básico)
|
|
// if ($request->hasFile('photo_avatar')) {
|
|
// $path = $request->file('photo_avatar')->store('avatars', 'public');
|
|
// $validated['photo_avatar_filename'] = basename($path);
|
|
// }
|
|
|
|
$user->update($validated);
|
|
|
|
return response()->json($user);
|
|
}
|
|
|
|
|
|
public function updatePassword(Request $request, User $user)
|
|
{
|
|
$this->authorize('update', $user);
|
|
|
|
$validated = $request->validate([
|
|
'current_password' => 'required|string',
|
|
'new_password' => 'required|string|min:3|confirmed',
|
|
]);
|
|
|
|
// Verificar se a password atual está correta
|
|
if (!Hash::check($validated['current_password'], $user->password)) {
|
|
return response()->json(['message' => 'Current password is incorrect'], 403);
|
|
}
|
|
|
|
// Atualizar para a nova password
|
|
$user->password = Hash::make($validated['new_password']);
|
|
$user->save();
|
|
|
|
return response()->json(['message' => 'Password updated successfully']);
|
|
}
|
|
|
|
/**
|
|
* DELETE /api/users/{user}
|
|
* Apagar conta (Soft Delete)
|
|
*/
|
|
public function destroy(User $user)
|
|
{
|
|
$this->authorize('delete', $user);
|
|
|
|
// Check if user has transactions or games - if so, soft delete
|
|
$hasTransactions = $user->transactions()->exists();
|
|
$hasGames = Game::where(function ($q) use ($user) {
|
|
$q->where('player1_user_id', $user->id)
|
|
->orWhere('player2_user_id', $user->id);
|
|
})->exists();
|
|
|
|
if ($hasTransactions || $hasGames) {
|
|
$user->delete(); // Soft delete
|
|
$message = 'User account deactivated (soft-deleted due to transaction/game history)';
|
|
} else {
|
|
$user->forceDelete(); // Hard delete
|
|
$message = 'User permanently deleted';
|
|
}
|
|
|
|
return response()->json(['message' => $message]);
|
|
}
|
|
|
|
/**
|
|
* GET /api/users/{user}/matches
|
|
* Histórico de Jogos Multiplayer do Utilizador
|
|
*/
|
|
public function getMatches(Request $request, User $user)
|
|
{
|
|
$this->authorize('view', $user);
|
|
|
|
$outcomeSql = "CASE
|
|
WHEN is_draw = 1 THEN 'DRAW'
|
|
WHEN winner_user_id = {$user->id} THEN 'WIN'
|
|
ELSE 'LOSS'
|
|
END";
|
|
|
|
$query = Game::query()
|
|
->select('*')
|
|
->selectRaw("($outcomeSql) as outcome_text")
|
|
->where(function ($q) use ($user) {
|
|
$q->where('player1_user_id', $user->id)
|
|
->orWhere('player2_user_id', $user->id);
|
|
})
|
|
->with(['winner', 'player1', 'player2']);
|
|
|
|
if ($request->has('type')) {
|
|
$query->where('type', $request->type);
|
|
}
|
|
|
|
if ($request->has('status')) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
if ($request->filled('outcome')) {
|
|
switch ($request->outcome) {
|
|
case 'win':
|
|
$query->where('winner_user_id', $user->id)
|
|
->where('is_draw', 0);
|
|
break;
|
|
case 'loss':
|
|
case 'forfeit': // Forfeit treated as loss for now
|
|
$query->where('loser_user_id', $user->id)
|
|
->where('is_draw', 0);
|
|
break;
|
|
case 'draw':
|
|
$query->where('is_draw', 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($request->has('date_from')) {
|
|
$query->whereDate('began_at', '>=', $request->date_from);
|
|
}
|
|
|
|
if ($request->has('date_to')) {
|
|
$query->whereDate('ended_at', '<=', $request->date_to);
|
|
}
|
|
|
|
$order = $request->get('sort_by', 'began_at');
|
|
$direction = $request->get('sort_direction', 'desc');
|
|
|
|
if ($order === 'outcome') {
|
|
$query->orderBy('outcome_text', $direction);
|
|
} else {
|
|
$query->orderBy($order, $direction);
|
|
}
|
|
|
|
$matches = $query->orderBy($order, $direction)
|
|
->paginate(10);
|
|
|
|
return response()->json($matches);
|
|
}
|
|
|
|
public function block(User $user)
|
|
{
|
|
$this->authorize('block', $user);
|
|
|
|
$user->blocked = true;
|
|
$user->save();
|
|
|
|
// Revoke all active tokens for immediate effect
|
|
$user->tokens()->delete();
|
|
|
|
return response()->json(['message' => 'User blocked successfully']);
|
|
}
|
|
|
|
public function unblock(User $user)
|
|
{
|
|
$this->authorize('unblock', $user);
|
|
|
|
$user->blocked = false;
|
|
$user->save();
|
|
|
|
return response()->json(['message' => 'User unblocked successfully']);
|
|
}
|
|
|
|
public function getTransactions(Request $request, User $user)
|
|
{
|
|
if ($request->user()->id !== $user->id && $request->user()->type !== 'A') {
|
|
return response()->json(['message' => 'Forbidden'], 403);
|
|
}
|
|
|
|
$query = $user->transactions()->with('type');
|
|
|
|
if ($request->has('type')) {
|
|
$query->whereHas('type', function ($q) use ($request) {
|
|
$q->where('type', $request->type);
|
|
});
|
|
}
|
|
|
|
if ($request->has('date_from')) {
|
|
$query->whereDate('transaction_datetime', '>=', $request->date_from);
|
|
}
|
|
|
|
if ($request->has('date_to')) {
|
|
$query->whereDate('transaction_datetime', '<=', $request->date_to);
|
|
}
|
|
|
|
$transactions = $query->orderBy('transaction_datetime', 'desc')
|
|
->paginate(10);
|
|
|
|
return response()->json($transactions);
|
|
}
|
|
|
|
public function getMyTransactions(Request $request)
|
|
{
|
|
return $this->getTransactions($request, $request->user());
|
|
}
|
|
}
|