259 lines
8.2 KiB
PHP
259 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
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 login(Request $request)
|
|
{
|
|
$creds = $request->validate([
|
|
'email' => ['required', 'email'],
|
|
'password' => ['required', 'string'],
|
|
]);
|
|
if (! Auth::attempt($creds)) {
|
|
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' => [
|
|
'id' => $user->id,
|
|
'name' => $user->name,
|
|
'email' => $user->email,
|
|
'tenant_id' => $user->tenant_id ?? null,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function me(Request $request)
|
|
{
|
|
$u = Auth::user();
|
|
|
|
return response()->json([
|
|
'id' => $u->id,
|
|
'name' => $u->name,
|
|
'email' => $u->email,
|
|
'tenant_id' => $u->tenant_id ?? null,
|
|
]);
|
|
}
|
|
|
|
public function events()
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$q = Event::query();
|
|
if ($tenantId) {
|
|
$q->where('tenant_id', $tenantId);
|
|
}
|
|
|
|
return response()->json(['data' => $q->orderByDesc('created_at')->limit(100)->get(['id', 'name', 'slug', 'date', 'is_active'])]);
|
|
}
|
|
|
|
public function showEvent(int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
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']));
|
|
}
|
|
|
|
public function storeEvent(Request $request)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$data = $request->validate([
|
|
'name' => ['required', 'string', 'max:255'],
|
|
'slug' => ['required', 'string', 'max:255'],
|
|
'date' => ['nullable', 'date'],
|
|
'is_active' => ['boolean'],
|
|
]);
|
|
$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->default_locale = 'de';
|
|
$ev->save();
|
|
|
|
return response()->json(['id' => $ev->id]);
|
|
}
|
|
|
|
public function updateEvent(Request $request, int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
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'],
|
|
]);
|
|
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]);
|
|
}
|
|
|
|
public function toggleEvent(int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
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]);
|
|
}
|
|
|
|
public function eventStats(int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
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,
|
|
]);
|
|
}
|
|
|
|
public function createInvite(Request $request, int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
return $this->forbiddenResponse('events.invite', ['event_id' => $ev->id, 'tenant_id' => $tenantId]);
|
|
}
|
|
|
|
$joinToken = $this->joinTokenService->createToken($ev, [
|
|
'created_by' => $u?->id,
|
|
]);
|
|
|
|
return response()->json([
|
|
'link' => url('/e/'.$joinToken->token),
|
|
'legacy_link' => url('/e/'.$ev->slug).'?invite='.$joinToken->token,
|
|
'token' => $joinToken->token,
|
|
]);
|
|
}
|
|
|
|
public function eventPhotos(int $id)
|
|
{
|
|
$u = Auth::user();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$ev = Event::findOrFail($id);
|
|
if ($tenantId && $ev->tenant_id !== $tenantId) {
|
|
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']);
|
|
|
|
return response()->json(['data' => $rows]);
|
|
}
|
|
|
|
public function featurePhoto(int $photoId)
|
|
{
|
|
$p = Photo::findOrFail($photoId);
|
|
$this->authorizePhoto($p);
|
|
$p->is_featured = 1;
|
|
$p->save();
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function unfeaturePhoto(int $photoId)
|
|
{
|
|
$p = Photo::findOrFail($photoId);
|
|
$this->authorizePhoto($p);
|
|
$p->is_featured = 0;
|
|
$p->save();
|
|
|
|
return response()->json(['ok' => true]);
|
|
}
|
|
|
|
public function deletePhoto(int $photoId)
|
|
{
|
|
$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();
|
|
$tenantId = $u->tenant_id ?? null;
|
|
$event = Event::find($p->event_id);
|
|
if ($tenantId && $event && $event->tenant_id !== $tenantId) {
|
|
abort(403);
|
|
}
|
|
}
|
|
}
|