Limit-Status im Upload-Flow anzeigen (Warnbanner + Sperrzustände).
Upload-Fehlercodes auswerten und freundliche Dialoge zeigen.
This commit is contained in:
@@ -6,32 +6,40 @@ use App\Models\Event;
|
||||
use App\Models\Photo;
|
||||
use App\Models\User;
|
||||
use App\Services\EventJoinTokenService;
|
||||
use App\Support\ApiError;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class TenantController extends BaseController
|
||||
{
|
||||
public function __construct(private readonly EventJoinTokenService $joinTokenService)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly EventJoinTokenService $joinTokenService) {}
|
||||
|
||||
public function login(Request $request)
|
||||
{
|
||||
$creds = $request->validate([
|
||||
'email' => ['required','email'],
|
||||
'password' => ['required','string'],
|
||||
'email' => ['required', 'email'],
|
||||
'password' => ['required', 'string'],
|
||||
]);
|
||||
if (! Auth::attempt($creds)) {
|
||||
return response()->json(['error' => ['code' => 'invalid_credentials']], 401);
|
||||
return ApiError::response(
|
||||
'invalid_credentials',
|
||||
'Invalid Credentials',
|
||||
'The provided credentials are incorrect.',
|
||||
Response::HTTP_UNAUTHORIZED,
|
||||
['email' => $creds['email']]
|
||||
);
|
||||
}
|
||||
/** @var User $user */
|
||||
$user = Auth::user();
|
||||
// naive token (cache-based), expires in 8 hours
|
||||
$token = Str::random(80);
|
||||
Cache::put('api_token:'.$token, $user->id, now()->addHours(8));
|
||||
|
||||
return response()->json([
|
||||
'token' => $token,
|
||||
'user' => [
|
||||
@@ -46,6 +54,7 @@ class TenantController extends BaseController
|
||||
public function me(Request $request)
|
||||
{
|
||||
$u = Auth::user();
|
||||
|
||||
return response()->json([
|
||||
'id' => $u->id,
|
||||
'name' => $u->name,
|
||||
@@ -62,7 +71,8 @@ class TenantController extends BaseController
|
||||
if ($tenantId) {
|
||||
$q->where('tenant_id', $tenantId);
|
||||
}
|
||||
return response()->json(['data' => $q->orderByDesc('created_at')->limit(100)->get(['id','name','slug','date','is_active'])]);
|
||||
|
||||
return response()->json(['data' => $q->orderByDesc('created_at')->limit(100)->get(['id', 'name', 'slug', 'date', 'is_active'])]);
|
||||
}
|
||||
|
||||
public function showEvent(int $id)
|
||||
@@ -71,9 +81,10 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.show', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
return response()->json($ev->only(['id','name','slug','date','is_active','default_locale']));
|
||||
|
||||
return response()->json($ev->only(['id', 'name', 'slug', 'date', 'is_active', 'default_locale']));
|
||||
}
|
||||
|
||||
public function storeEvent(Request $request)
|
||||
@@ -81,19 +92,20 @@ class TenantController extends BaseController
|
||||
$u = Auth::user();
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$data = $request->validate([
|
||||
'name' => ['required','string','max:255'],
|
||||
'slug' => ['required','string','max:255'],
|
||||
'date' => ['nullable','date'],
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'slug' => ['required', 'string', 'max:255'],
|
||||
'date' => ['nullable', 'date'],
|
||||
'is_active' => ['boolean'],
|
||||
]);
|
||||
$ev = new Event();
|
||||
$ev = new Event;
|
||||
$ev->tenant_id = $tenantId ?? $ev->tenant_id;
|
||||
$ev->name = ['de' => $data['name'], 'en' => $data['name']];
|
||||
$ev->slug = $data['slug'];
|
||||
$ev->date = $data['date'] ?? null;
|
||||
$ev->is_active = (bool)($data['is_active'] ?? true);
|
||||
$ev->is_active = (bool) ($data['is_active'] ?? true);
|
||||
$ev->default_locale = 'de';
|
||||
$ev->save();
|
||||
|
||||
return response()->json(['id' => $ev->id]);
|
||||
}
|
||||
|
||||
@@ -103,19 +115,28 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.update', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
$data = $request->validate([
|
||||
'name' => ['nullable','string','max:255'],
|
||||
'slug' => ['nullable','string','max:255'],
|
||||
'date' => ['nullable','date'],
|
||||
'is_active' => ['nullable','boolean'],
|
||||
'name' => ['nullable', 'string', 'max:255'],
|
||||
'slug' => ['nullable', 'string', 'max:255'],
|
||||
'date' => ['nullable', 'date'],
|
||||
'is_active' => ['nullable', 'boolean'],
|
||||
]);
|
||||
if (isset($data['name'])) $ev->name = ['de' => $data['name'], 'en' => $data['name']];
|
||||
if (isset($data['slug'])) $ev->slug = $data['slug'];
|
||||
if (array_key_exists('date', $data)) $ev->date = $data['date'];
|
||||
if (array_key_exists('is_active', $data)) $ev->is_active = (bool)$data['is_active'];
|
||||
if (isset($data['name'])) {
|
||||
$ev->name = ['de' => $data['name'], 'en' => $data['name']];
|
||||
}
|
||||
if (isset($data['slug'])) {
|
||||
$ev->slug = $data['slug'];
|
||||
}
|
||||
if (array_key_exists('date', $data)) {
|
||||
$ev->date = $data['date'];
|
||||
}
|
||||
if (array_key_exists('is_active', $data)) {
|
||||
$ev->is_active = (bool) $data['is_active'];
|
||||
}
|
||||
$ev->save();
|
||||
|
||||
return response()->json(['ok' => true]);
|
||||
}
|
||||
|
||||
@@ -125,11 +146,12 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.toggle', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
$ev->is_active = ! (bool) $ev->is_active;
|
||||
$ev->save();
|
||||
return response()->json(['is_active' => (bool)$ev->is_active]);
|
||||
|
||||
return response()->json(['is_active' => (bool) $ev->is_active]);
|
||||
}
|
||||
|
||||
public function eventStats(int $id)
|
||||
@@ -138,15 +160,16 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.stats', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
$total = Photo::where('event_id', $id)->count();
|
||||
$featured = Photo::where('event_id', $id)->where('is_featured', 1)->count();
|
||||
$likes = Photo::where('event_id', $id)->sum('likes_count');
|
||||
|
||||
return response()->json([
|
||||
'total' => (int)$total,
|
||||
'featured' => (int)$featured,
|
||||
'likes' => (int)$likes,
|
||||
'total' => (int) $total,
|
||||
'featured' => (int) $featured,
|
||||
'likes' => (int) $likes,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -156,7 +179,7 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.invite', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
|
||||
$joinToken = $this->joinTokenService->createToken($ev, [
|
||||
@@ -176,9 +199,10 @@ class TenantController extends BaseController
|
||||
$tenantId = $u->tenant_id ?? null;
|
||||
$ev = Event::findOrFail($id);
|
||||
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
||||
return response()->json(['error' => ['code' => 'forbidden']], 403);
|
||||
return $this->forbiddenResponse('events.photos', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
||||
}
|
||||
$rows = Photo::where('event_id', $id)->orderByDesc('created_at')->limit(100)->get(['id','thumbnail_path','file_path','likes_count','is_featured','created_at']);
|
||||
$rows = Photo::where('event_id', $id)->orderByDesc('created_at')->limit(100)->get(['id', 'thumbnail_path', 'file_path', 'likes_count', 'is_featured', 'created_at']);
|
||||
|
||||
return response()->json(['data' => $rows]);
|
||||
}
|
||||
|
||||
@@ -186,7 +210,9 @@ class TenantController extends BaseController
|
||||
{
|
||||
$p = Photo::findOrFail($photoId);
|
||||
$this->authorizePhoto($p);
|
||||
$p->is_featured = 1; $p->save();
|
||||
$p->is_featured = 1;
|
||||
$p->save();
|
||||
|
||||
return response()->json(['ok' => true]);
|
||||
}
|
||||
|
||||
@@ -194,7 +220,9 @@ class TenantController extends BaseController
|
||||
{
|
||||
$p = Photo::findOrFail($photoId);
|
||||
$this->authorizePhoto($p);
|
||||
$p->is_featured = 0; $p->save();
|
||||
$p->is_featured = 0;
|
||||
$p->save();
|
||||
|
||||
return response()->json(['ok' => true]);
|
||||
}
|
||||
|
||||
@@ -203,9 +231,21 @@ class TenantController extends BaseController
|
||||
$p = Photo::findOrFail($photoId);
|
||||
$this->authorizePhoto($p);
|
||||
$p->delete();
|
||||
|
||||
return response()->json(['ok' => true]);
|
||||
}
|
||||
|
||||
private function forbiddenResponse(string $action, array $meta = []): JsonResponse
|
||||
{
|
||||
return ApiError::response(
|
||||
'forbidden',
|
||||
'Forbidden',
|
||||
'You are not allowed to perform this action.',
|
||||
Response::HTTP_FORBIDDEN,
|
||||
array_merge(['action' => $action], $meta)
|
||||
);
|
||||
}
|
||||
|
||||
protected function authorizePhoto(Photo $p): void
|
||||
{
|
||||
$u = Auth::user();
|
||||
|
||||
Reference in New Issue
Block a user