Version 1.0 #102
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\RegisterRequest;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use App\Models\User;
|
|
||||||
use App\Http\Requests\RegisterRequest;
|
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
@@ -24,6 +24,13 @@ class AuthController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
|
if ($user->blocked) {
|
||||||
|
Auth::logout();
|
||||||
|
|
||||||
|
return response()->json(['message' => 'Your account is blocked. Please contact support.'], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$token = $user->createToken('auth-token')->plainTextToken;
|
$token = $user->createToken('auth-token')->plainTextToken;
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@@ -14,8 +14,21 @@ class GameController extends Controller
|
|||||||
|
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
$this->authorize('viewAny', Game::class);
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
$query = Game::query()->with(["winner", "player1", "player2"]);
|
$query = Game::query()->with(["winner", "player1", "player2"]);
|
||||||
|
|
||||||
|
// Filter games based on user type
|
||||||
|
if ($user->type !== 'A') {
|
||||||
|
// Players can only see games they participate in
|
||||||
|
$query->where(function ($q) use ($user) {
|
||||||
|
$q->where('player1_user_id', $user->id)
|
||||||
|
->orWhere('player2_user_id', $user->id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Admins see all games (no filter needed)
|
||||||
|
|
||||||
if ($request->has("type") && in_array($request->type, ["3", "9"])) {
|
if ($request->has("type") && in_array($request->type, ["3", "9"])) {
|
||||||
$query->where("type", $request->type);
|
$query->where("type", $request->type);
|
||||||
}
|
}
|
||||||
@@ -43,6 +56,8 @@ class GameController extends Controller
|
|||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
|
$this->authorize('create', Game::class);
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
"type" => "required|in:3,9",
|
"type" => "required|in:3,9",
|
||||||
]);
|
]);
|
||||||
@@ -81,6 +96,8 @@ class GameController extends Controller
|
|||||||
|
|
||||||
public function join(Game $game)
|
public function join(Game $game)
|
||||||
{
|
{
|
||||||
|
$this->authorize('join', $game);
|
||||||
|
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
if ($game->player1_user_id == $user->id) {
|
if ($game->player1_user_id == $user->id) {
|
||||||
@@ -107,12 +124,16 @@ class GameController extends Controller
|
|||||||
|
|
||||||
public function show(Game $game)
|
public function show(Game $game)
|
||||||
{
|
{
|
||||||
|
$this->authorize('view', $game);
|
||||||
|
|
||||||
$game->load(["player1", "player2", "winner"]);
|
$game->load(["player1", "player2", "winner"]);
|
||||||
return response()->json($game);
|
return response()->json($game);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Request $request, Game $game)
|
public function update(Request $request, Game $game)
|
||||||
{
|
{
|
||||||
|
$this->authorize('update', $game);
|
||||||
|
|
||||||
if ($game->status == "Ended") {
|
if ($game->status == "Ended") {
|
||||||
return response()->json(["message" => "Game already ended"], 400);
|
return response()->json(["message" => "Game already ended"], 400);
|
||||||
}
|
}
|
||||||
@@ -135,4 +156,25 @@ class GameController extends Controller
|
|||||||
|
|
||||||
return response()->json($game);
|
return response()->json($game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function resign(Game $game)
|
||||||
|
{
|
||||||
|
$this->authorize('resign', $game);
|
||||||
|
|
||||||
|
$user = Auth::user();
|
||||||
|
|
||||||
|
$game->update([
|
||||||
|
'status' => 'Ended',
|
||||||
|
'winner_user_id' => $game->player1_user_id === $user->id
|
||||||
|
? $game->player2_user_id
|
||||||
|
: $game->player1_user_id,
|
||||||
|
'loser_user_id' => $user->id,
|
||||||
|
'ended_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'message' => 'You have resigned from the game',
|
||||||
|
'game' => $game->fresh(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request)
|
public function index(Request $request)
|
||||||
{
|
{
|
||||||
|
$this->authorize('viewAny', User::class);
|
||||||
|
|
||||||
$query = User::query();
|
$query = User::query();
|
||||||
|
|
||||||
// Filtros úteis para o Backoffice
|
// Filtros úteis para o Backoffice
|
||||||
@@ -41,6 +43,8 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
|
$this->authorize('create', User::class);
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'nickname' => 'required|string|max:20|unique:users,nickname',
|
'nickname' => 'required|string|max:20|unique:users,nickname',
|
||||||
@@ -73,7 +77,11 @@ class UserController extends Controller
|
|||||||
public function show(User $user)
|
public function show(User $user)
|
||||||
{
|
{
|
||||||
$currentUser = Auth::user();
|
$currentUser = Auth::user();
|
||||||
if ($currentUser->id !== $user->id && $currentUser->type !== 'A') {
|
|
||||||
|
// 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([
|
return response()->json([
|
||||||
'id' => $user->id,
|
'id' => $user->id,
|
||||||
'name' => $user->name,
|
'name' => $user->name,
|
||||||
@@ -82,8 +90,6 @@ class UserController extends Controller
|
|||||||
'type' => $user->type,
|
'type' => $user->type,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json($user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,11 +98,7 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, User $user)
|
public function update(Request $request, User $user)
|
||||||
{
|
{
|
||||||
$currentUser = Auth::user();
|
$this->authorize('update', $user);
|
||||||
|
|
||||||
if ($currentUser->id !== $user->id && $currentUser->type !== 'A') {
|
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'name' => 'sometimes|string|max:255',
|
'name' => 'sometimes|string|max:255',
|
||||||
@@ -112,16 +114,8 @@ class UserController extends Controller
|
|||||||
Rule::unique('users')->ignore($user->id),
|
Rule::unique('users')->ignore($user->id),
|
||||||
],
|
],
|
||||||
'password' => 'sometimes|string|min:3',
|
'password' => 'sometimes|string|min:3',
|
||||||
'blocked' => 'sometimes|boolean',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isset($validated['blocked']) && $currentUser->type !== 'A') {
|
|
||||||
return response()->json(
|
|
||||||
['message' => 'Only admins can block users'],
|
|
||||||
403,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lógica de Upload de Foto (Exemplo Básico)
|
// Lógica de Upload de Foto (Exemplo Básico)
|
||||||
// if ($request->hasFile('photo_avatar')) {
|
// if ($request->hasFile('photo_avatar')) {
|
||||||
// $path = $request->file('photo_avatar')->store('avatars', 'public');
|
// $path = $request->file('photo_avatar')->store('avatars', 'public');
|
||||||
@@ -139,24 +133,24 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function destroy(User $user)
|
public function destroy(User $user)
|
||||||
{
|
{
|
||||||
$currentUser = Auth::user();
|
$this->authorize('delete', $user);
|
||||||
|
|
||||||
if ($currentUser->id !== $user->id && $currentUser->type !== 'A') {
|
// Check if user has transactions or games - if so, soft delete
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
$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';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($currentUser->id === $user->id && $currentUser->type === 'A') {
|
return response()->json(['message' => $message]);
|
||||||
if ($user->type === 'A') {
|
|
||||||
return response()->json(
|
|
||||||
['message' => 'Admins cannot delete their own account'],
|
|
||||||
403,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$user->delete();
|
|
||||||
|
|
||||||
return response()->json(['message' => 'User deleted successfully']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,11 +159,7 @@ class UserController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function getMatches(Request $request, User $user)
|
public function getMatches(Request $request, User $user)
|
||||||
{
|
{
|
||||||
$currentUser = Auth::user();
|
$this->authorize('view', $user);
|
||||||
|
|
||||||
if ($currentUser->id !== $user->id && $currentUser->type !== 'A') {
|
|
||||||
return response()->json(['message' => 'Unauthorized'], 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$matches = Game::query()
|
$matches = Game::query()
|
||||||
->where(function ($q) use ($user) {
|
->where(function ($q) use ($user) {
|
||||||
@@ -184,4 +174,27 @@ class UserController extends Controller
|
|||||||
|
|
||||||
return response()->json($matches);
|
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']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Game;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class GamePolicy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view any models.
|
||||||
|
*/
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**2
|
||||||
|
* Determine whether the user can view the model.
|
||||||
|
*/
|
||||||
|
public function view(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
if ($user->type === 'A') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$game->player1_user_id === $user->id ||
|
||||||
|
$game->player2_user_id === $user->id
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can create models.
|
||||||
|
*/
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
if ($user->type === 'A') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function join(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
if ($user->type === 'A') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$game->status !== 'waiting' ||
|
||||||
|
$game->player1_user_id === $user->id
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resign(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
if ($user->type === 'A') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$game->status !== 'ongoing' ||
|
||||||
|
($game->player1_user_id !== $user->id &&
|
||||||
|
$game->player2_user_id !== $user->id)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can update the model.
|
||||||
|
*/
|
||||||
|
public function update(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
if ($user->type === 'A') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
$game->status !== 'ongoing' ||
|
||||||
|
($game->player1_user_id !== $user->id &&
|
||||||
|
$game->player2_user_id !== $user->id)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can delete the model.
|
||||||
|
*/
|
||||||
|
public function delete(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
// Only admins can delete games
|
||||||
|
return $user->type === 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can restore the model.
|
||||||
|
*/
|
||||||
|
public function restore(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can permanently delete the model.
|
||||||
|
*/
|
||||||
|
public function forceDelete(User $user, Game $game): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserPolicy
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view any models.
|
||||||
|
*/
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->type === 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view the model.
|
||||||
|
*/
|
||||||
|
public function view(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return $user->id === $model->id || $user->type === 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can create models.
|
||||||
|
*/
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->type === 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can update the model.
|
||||||
|
*/
|
||||||
|
public function update(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return $user->id === $model->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can delete the model.
|
||||||
|
*/
|
||||||
|
public function delete(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return ($user->type === 'A' && $user->id !== $model->id) || ($user->type === 'P' && $user->id === $model->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function block(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return $user->type === 'A' && $user->id !== $model->id && $model->type === 'P';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unblock(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return $user->type === 'A' && $user->id !== $model->id && $model->type === 'P';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can restore the model.
|
||||||
|
*/
|
||||||
|
public function restore(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can permanently delete the model.
|
||||||
|
*/
|
||||||
|
public function forceDelete(User $user, User $model): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
VITE_API_DOMAIN=localhost:8000
|
VITE_API_DOMAIN=localhost:8000
|
||||||
VITE_WS_CONNECTION=ws://localhost:3000
|
VITE_WS_CONNECTION=ws://localhost:3001
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import {
|
|||||||
} from '@/components/ui/navigation-menu'
|
} from '@/components/ui/navigation-menu'
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
|
|
||||||
|
|
||||||
const emits = defineEmits(['logout'])
|
const emits = defineEmits(['logout'])
|
||||||
const { userLoggedIn } = defineProps(['userLoggedIn'])
|
const { userLoggedIn } = defineProps(['userLoggedIn'])
|
||||||
|
|
||||||
@@ -68,6 +67,7 @@ const logoutClickHandler = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emits('logout')
|
emits('logout')
|
||||||
router.push('/login')
|
|
||||||
|
router.push({ name: 'home' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -2,14 +2,14 @@ import HomePage from '@/pages/home/HomePage.vue'
|
|||||||
import LoginPage from '@/pages/login/LoginPage.vue'
|
import LoginPage from '@/pages/login/LoginPage.vue'
|
||||||
import LaravelPage from '@/pages/testing/LaravelPage.vue'
|
import LaravelPage from '@/pages/testing/LaravelPage.vue'
|
||||||
import WebsocketsPage from '@/pages/testing/WebsocketsPage.vue'
|
import WebsocketsPage from '@/pages/testing/WebsocketsPage.vue'
|
||||||
import UserPage from '@/pages/user/UserPage.vue'
|
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
|
||||||
import { toast } from 'vue-sonner'
|
|
||||||
import TestAnimations from '@/pages/TestAnimations.vue'
|
import TestAnimations from '@/pages/TestAnimations.vue'
|
||||||
import TestDealing from '@/pages/TestDealing.vue'
|
import TestDealing from '@/pages/TestDealing.vue'
|
||||||
import TestAllAnimations from '@/pages/TestAllAnimations.vue'
|
import TestAllAnimations from '@/pages/TestAllAnimations.vue'
|
||||||
import TestGameBoard from '@/pages/TestGameBoard.vue'
|
import TestGameBoard from '@/pages/TestGameBoard.vue'
|
||||||
|
import UserPage from '@/pages/user/UserPage.vue'
|
||||||
import SinglePlayerGamePage from '@/pages/game/SinglePlayerGamePage.vue'
|
import SinglePlayerGamePage from '@/pages/game/SinglePlayerGamePage.vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@@ -24,7 +24,21 @@ const router = createRouter({
|
|||||||
name: 'login',
|
name: 'login',
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
path: '/game/3',
|
||||||
|
name: 'bisca3',
|
||||||
|
component: SinglePlayerGamePage,
|
||||||
|
props: { gameType: 3 },
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/game/9',
|
||||||
|
name: 'bisca9',
|
||||||
|
component: SinglePlayerGamePage,
|
||||||
|
props: { gameType: 9 },
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
|
{
|
||||||
path: '/game/3',
|
path: '/game/3',
|
||||||
name: 'bisca3',
|
name: 'bisca3',
|
||||||
component: SinglePlayerGamePage,
|
component: SinglePlayerGamePage,
|
||||||
|
|||||||
Reference in New Issue
Block a user