start project
This commit is contained in:
58
app/Http/Controllers/AdminDashboardController.php
Normal file
58
app/Http/Controllers/AdminDashboardController.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Broadcast;
|
||||
use App\Models\Channel;
|
||||
use App\Models\User;
|
||||
use App\Models\Vod;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class AdminDashboardController extends Controller
|
||||
{
|
||||
public function __invoke(): Response
|
||||
{
|
||||
return Inertia::render('admin/dashboard', [
|
||||
'stats' => [
|
||||
'users' => User::query()->count(),
|
||||
'channels' => Channel::query()->count(),
|
||||
'live_channels' => Channel::query()->where('is_live', true)->count(),
|
||||
'vods' => Vod::query()->count(),
|
||||
],
|
||||
'liveBroadcasts' => Broadcast::query()
|
||||
->where('status', Broadcast::STATUS_LIVE)
|
||||
->with('channel.user')
|
||||
->latest('started_at')
|
||||
->limit(20)
|
||||
->get()
|
||||
->map(fn (Broadcast $broadcast) => [
|
||||
'id' => $broadcast->id,
|
||||
'title' => $broadcast->title,
|
||||
'started_at' => $broadcast->started_at?->toIso8601String(),
|
||||
'channel' => [
|
||||
'id' => $broadcast->channel->id,
|
||||
'slug' => $broadcast->channel->slug,
|
||||
'display_name' => $broadcast->channel->display_name,
|
||||
'owner' => $broadcast->channel->user->name,
|
||||
],
|
||||
]),
|
||||
'channels' => Channel::query()
|
||||
->with('user')
|
||||
->latest()
|
||||
->limit(25)
|
||||
->get()
|
||||
->map(fn (Channel $channel) => [
|
||||
'id' => $channel->id,
|
||||
'slug' => $channel->slug,
|
||||
'display_name' => $channel->display_name,
|
||||
'is_live' => $channel->is_live,
|
||||
'suspended_at' => $channel->suspended_at?->toIso8601String(),
|
||||
'owner' => [
|
||||
'id' => $channel->user->id,
|
||||
'name' => $channel->user->name,
|
||||
],
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
89
app/Http/Controllers/AdminModerationController.php
Normal file
89
app/Http/Controllers/AdminModerationController.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\AdminAuditLog;
|
||||
use App\Models\Broadcast;
|
||||
use App\Models\Channel;
|
||||
use App\Models\User;
|
||||
use App\Models\Vod;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AdminModerationController extends Controller
|
||||
{
|
||||
public function suspendChannel(Request $request, Channel $channel): RedirectResponse
|
||||
{
|
||||
$channel->forceFill([
|
||||
'suspended_at' => now(),
|
||||
'is_live' => false,
|
||||
'live_broadcast_id' => null,
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
|
||||
$this->audit($request, $channel, 'channel.suspended');
|
||||
|
||||
return back()->with('success', 'Channel suspended.');
|
||||
}
|
||||
|
||||
public function restoreChannel(Request $request, Channel $channel): RedirectResponse
|
||||
{
|
||||
$channel->forceFill(['suspended_at' => null])->save();
|
||||
|
||||
$this->audit($request, $channel, 'channel.restored');
|
||||
|
||||
return back()->with('success', 'Channel restored.');
|
||||
}
|
||||
|
||||
public function suspendUser(Request $request, User $user): RedirectResponse
|
||||
{
|
||||
$user->forceFill(['suspended_at' => now()])->save();
|
||||
$user->channel?->forceFill([
|
||||
'is_live' => false,
|
||||
'live_broadcast_id' => null,
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
|
||||
$this->audit($request, $user, 'user.suspended');
|
||||
|
||||
return back()->with('success', 'User suspended.');
|
||||
}
|
||||
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast): RedirectResponse
|
||||
{
|
||||
$broadcast->forceFill([
|
||||
'status' => Broadcast::STATUS_ENDED,
|
||||
'ended_at' => $broadcast->ended_at ?? now(),
|
||||
])->save();
|
||||
|
||||
$broadcast->channel->forceFill([
|
||||
'is_live' => false,
|
||||
'live_broadcast_id' => null,
|
||||
'viewer_count' => 0,
|
||||
])->save();
|
||||
|
||||
$this->audit($request, $broadcast, 'broadcast.stopped');
|
||||
|
||||
return back()->with('success', 'Broadcast stopped.');
|
||||
}
|
||||
|
||||
public function deleteVod(Request $request, Vod $vod): RedirectResponse
|
||||
{
|
||||
$vod->delete();
|
||||
|
||||
$this->audit($request, $vod, 'vod.deleted');
|
||||
|
||||
return back()->with('success', 'VOD deleted.');
|
||||
}
|
||||
|
||||
private function audit(Request $request, object $subject, string $action): void
|
||||
{
|
||||
AdminAuditLog::query()->create([
|
||||
'admin_user_id' => $request->user()?->id,
|
||||
'subject_type' => $subject::class,
|
||||
'subject_id' => $subject->id,
|
||||
'action' => $action,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
87
app/Http/Controllers/ChannelController.php
Normal file
87
app/Http/Controllers/ChannelController.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Channel;
|
||||
use App\Models\ChatMessage;
|
||||
use App\Models\Vod;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ChannelController extends Controller
|
||||
{
|
||||
public function show(Request $request, Channel $channel): Response
|
||||
{
|
||||
abort_if($channel->isSuspended(), 404);
|
||||
|
||||
$channel->load(['category', 'liveBroadcast', 'user']);
|
||||
$channel->loadCount('follows');
|
||||
|
||||
$currentBroadcast = $channel->liveBroadcast;
|
||||
|
||||
return Inertia::render('channels/show', [
|
||||
'channel' => [
|
||||
'id' => $channel->id,
|
||||
'slug' => $channel->slug,
|
||||
'display_name' => $channel->display_name,
|
||||
'description' => $channel->description,
|
||||
'is_live' => $channel->is_live,
|
||||
'viewer_count' => $channel->viewer_count,
|
||||
'followers_count' => $channel->follows_count,
|
||||
'category' => $channel->category ? [
|
||||
'id' => $channel->category->id,
|
||||
'name' => $channel->category->name,
|
||||
'slug' => $channel->category->slug,
|
||||
] : null,
|
||||
'owner' => [
|
||||
'id' => $channel->user->id,
|
||||
'name' => $channel->user->name,
|
||||
],
|
||||
],
|
||||
'currentBroadcast' => $currentBroadcast ? [
|
||||
'id' => $currentBroadcast->id,
|
||||
'title' => $currentBroadcast->title,
|
||||
'hls_url' => $currentBroadcast->hls_url,
|
||||
'recording_enabled' => $currentBroadcast->recording_enabled,
|
||||
'started_at' => $currentBroadcast->started_at?->toIso8601String(),
|
||||
] : null,
|
||||
'vods' => $channel->vods()
|
||||
->where(function ($query) {
|
||||
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
|
||||
})
|
||||
->latest('published_at')
|
||||
->limit(12)
|
||||
->get()
|
||||
->map(fn (Vod $vod) => [
|
||||
'id' => $vod->id,
|
||||
'title' => $vod->title,
|
||||
'playback_url' => $vod->playback_url,
|
||||
'duration_seconds' => $vod->duration_seconds,
|
||||
'published_at' => $vod->published_at?->toIso8601String(),
|
||||
'expires_at' => $vod->expires_at?->toIso8601String(),
|
||||
]),
|
||||
'chatMessages' => ChatMessage::query()
|
||||
->where('channel_id', $channel->id)
|
||||
->when($currentBroadcast, fn ($query) => $query->where('broadcast_id', $currentBroadcast->id))
|
||||
->with('user')
|
||||
->latest()
|
||||
->limit(50)
|
||||
->get()
|
||||
->reverse()
|
||||
->values()
|
||||
->map(fn (ChatMessage $message) => [
|
||||
'id' => $message->id,
|
||||
'body' => $message->body,
|
||||
'created_at' => $message->created_at?->toIso8601String(),
|
||||
'user' => [
|
||||
'id' => $message->user->id,
|
||||
'name' => $message->user->name,
|
||||
],
|
||||
]),
|
||||
'isFollowing' => $request->user()
|
||||
? $channel->follows()->where('user_id', $request->user()->id)->exists()
|
||||
: false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
28
app/Http/Controllers/ChatMessageController.php
Normal file
28
app/Http/Controllers/ChatMessageController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Channel;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ChatMessageController extends Controller
|
||||
{
|
||||
public function store(Request $request, Channel $channel): RedirectResponse
|
||||
{
|
||||
abort_if($request->user()->isSuspended() || $channel->isSuspended(), 403);
|
||||
abort_unless($channel->is_live && $channel->live_broadcast_id, 422, 'Chat is available only while the channel is live.');
|
||||
|
||||
$validated = $request->validate([
|
||||
'body' => ['required', 'string', 'max:500'],
|
||||
]);
|
||||
|
||||
$channel->chatMessages()->create([
|
||||
'broadcast_id' => $channel->live_broadcast_id,
|
||||
'user_id' => $request->user()->id,
|
||||
'body' => trim($validated['body']),
|
||||
]);
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
182
app/Http/Controllers/CreatorDashboardController.php
Normal file
182
app/Http/Controllers/CreatorDashboardController.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Broadcast;
|
||||
use App\Models\Category;
|
||||
use App\Models\Channel;
|
||||
use App\Services\Streaming\StreamKeyManager;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class CreatorDashboardController extends Controller
|
||||
{
|
||||
public function show(Request $request, StreamKeyManager $streamKeys): Response
|
||||
{
|
||||
$channel = $request->user()
|
||||
->channel()
|
||||
->with(['category', 'liveBroadcast', 'streamKey'])
|
||||
->first();
|
||||
|
||||
return Inertia::render('dashboard', [
|
||||
'channel' => $channel ? $this->channelPayload($channel) : null,
|
||||
'plainStreamKey' => session('plain_stream_key'),
|
||||
'streaming' => [
|
||||
'ingestServer' => $streamKeys->ingestServer(),
|
||||
'tokenizedPath' => $channel && session('plain_stream_key')
|
||||
? $streamKeys->tokenizedPath($channel, (string) session('plain_stream_key'))
|
||||
: null,
|
||||
],
|
||||
'categories' => Category::query()
|
||||
->orderBy('name')
|
||||
->get(['id', 'name', 'slug'])
|
||||
->map(fn (Category $category) => [
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'slug' => $category->slug,
|
||||
]),
|
||||
'recentBroadcasts' => $channel
|
||||
? $channel->broadcasts()->with('vod')->latest()->limit(10)->get()->map(fn (Broadcast $broadcast) => [
|
||||
'id' => $broadcast->id,
|
||||
'title' => $broadcast->title,
|
||||
'status' => $broadcast->status,
|
||||
'recording_enabled' => $broadcast->recording_enabled,
|
||||
'started_at' => $broadcast->started_at?->toIso8601String(),
|
||||
'ended_at' => $broadcast->ended_at?->toIso8601String(),
|
||||
'has_vod' => $broadcast->vod !== null,
|
||||
])
|
||||
: [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeChannel(Request $request, StreamKeyManager $streamKeys): RedirectResponse
|
||||
{
|
||||
abort_if($request->user()->channel()->exists(), 422, 'This account already owns a channel.');
|
||||
|
||||
$validated = $request->validate([
|
||||
'display_name' => ['required', 'string', 'max:80'],
|
||||
'slug' => ['required', 'string', 'min:3', 'max:40', 'alpha_dash:ascii', 'lowercase', 'unique:channels,slug'],
|
||||
'description' => ['nullable', 'string', 'max:1000'],
|
||||
'category_id' => ['nullable', 'exists:categories,id'],
|
||||
]);
|
||||
|
||||
$channel = $request->user()->channel()->create([
|
||||
...$validated,
|
||||
'display_name' => trim($validated['display_name']),
|
||||
'slug' => Str::slug($validated['slug']),
|
||||
]);
|
||||
|
||||
$plainStreamKey = $streamKeys->rotate($channel);
|
||||
|
||||
return redirect()
|
||||
->route('dashboard')
|
||||
->with('plain_stream_key', $plainStreamKey)
|
||||
->with('success', 'Channel created. Save the stream key now; it will not be shown again.');
|
||||
}
|
||||
|
||||
public function updateChannel(Request $request): RedirectResponse
|
||||
{
|
||||
$channel = $request->user()->channel;
|
||||
|
||||
abort_unless($channel, 404);
|
||||
|
||||
$validated = $request->validate([
|
||||
'display_name' => ['required', 'string', 'max:80'],
|
||||
'slug' => ['required', 'string', 'min:3', 'max:40', 'alpha_dash:ascii', 'lowercase', Rule::unique('channels', 'slug')->ignore($channel)],
|
||||
'description' => ['nullable', 'string', 'max:1000'],
|
||||
'category_id' => ['nullable', 'exists:categories,id'],
|
||||
]);
|
||||
|
||||
$channel->update([
|
||||
...$validated,
|
||||
'display_name' => trim($validated['display_name']),
|
||||
'slug' => Str::slug($validated['slug']),
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Channel updated.');
|
||||
}
|
||||
|
||||
public function rotateStreamKey(Request $request, StreamKeyManager $streamKeys): RedirectResponse
|
||||
{
|
||||
$channel = $request->user()->channel;
|
||||
|
||||
abort_unless($channel, 404);
|
||||
|
||||
$plainStreamKey = $streamKeys->rotate($channel);
|
||||
|
||||
return back()
|
||||
->with('plain_stream_key', $plainStreamKey)
|
||||
->with('success', 'Stream key rotated. Update OBS before going live again.');
|
||||
}
|
||||
|
||||
public function storeBroadcast(Request $request): RedirectResponse
|
||||
{
|
||||
$channel = $request->user()->channel;
|
||||
|
||||
abort_unless($channel, 404);
|
||||
abort_if($channel->isSuspended(), 403);
|
||||
abort_if(
|
||||
$channel->broadcasts()->whereIn('status', [Broadcast::STATUS_PENDING, Broadcast::STATUS_LIVE])->exists(),
|
||||
422,
|
||||
'Finish the current broadcast before creating another.',
|
||||
);
|
||||
|
||||
$validated = $request->validate([
|
||||
'title' => ['required', 'string', 'max:120'],
|
||||
'recording_enabled' => ['nullable', 'boolean'],
|
||||
]);
|
||||
|
||||
$channel->broadcasts()->create([
|
||||
'title' => $validated['title'],
|
||||
'status' => Broadcast::STATUS_PENDING,
|
||||
'recording_enabled' => (bool) ($validated['recording_enabled'] ?? false),
|
||||
'mediamtx_path' => $channel->slug,
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Broadcast is ready. Start streaming from OBS.');
|
||||
}
|
||||
|
||||
public function stopBroadcast(Request $request, Broadcast $broadcast): RedirectResponse
|
||||
{
|
||||
abort_unless($broadcast->channel->user_id === $request->user()->id, 403);
|
||||
|
||||
$broadcast->forceFill([
|
||||
'status' => Broadcast::STATUS_ENDED,
|
||||
'ended_at' => $broadcast->ended_at ?? now(),
|
||||
])->save();
|
||||
|
||||
$broadcast->channel->forceFill([
|
||||
'is_live' => false,
|
||||
'viewer_count' => 0,
|
||||
'live_broadcast_id' => null,
|
||||
])->save();
|
||||
|
||||
return back()->with('success', 'Broadcast ended.');
|
||||
}
|
||||
|
||||
private function channelPayload(Channel $channel): array
|
||||
{
|
||||
return [
|
||||
'id' => $channel->id,
|
||||
'slug' => $channel->slug,
|
||||
'display_name' => $channel->display_name,
|
||||
'description' => $channel->description,
|
||||
'is_live' => $channel->is_live,
|
||||
'viewer_count' => $channel->viewer_count,
|
||||
'stream_key_last_used_at' => $channel->streamKey?->last_used_at?->toIso8601String(),
|
||||
'category_id' => $channel->category_id,
|
||||
'live_broadcast' => $channel->liveBroadcast ? [
|
||||
'id' => $channel->liveBroadcast->id,
|
||||
'title' => $channel->liveBroadcast->title,
|
||||
'status' => $channel->liveBroadcast->status,
|
||||
'hls_url' => $channel->liveBroadcast->hls_url,
|
||||
'recording_enabled' => $channel->liveBroadcast->recording_enabled,
|
||||
'started_at' => $channel->liveBroadcast->started_at?->toIso8601String(),
|
||||
] : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
28
app/Http/Controllers/FollowController.php
Normal file
28
app/Http/Controllers/FollowController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Channel;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FollowController extends Controller
|
||||
{
|
||||
public function store(Request $request, Channel $channel): RedirectResponse
|
||||
{
|
||||
abort_if($channel->isSuspended(), 404);
|
||||
|
||||
$channel->follows()->firstOrCreate([
|
||||
'user_id' => $request->user()->id,
|
||||
]);
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
public function destroy(Request $request, Channel $channel): RedirectResponse
|
||||
{
|
||||
$channel->follows()->where('user_id', $request->user()->id)->delete();
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
67
app/Http/Controllers/HomeController.php
Normal file
67
app/Http/Controllers/HomeController.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Channel;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Laravel\Fortify\Features;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$liveChannels = Channel::query()
|
||||
->where('is_live', true)
|
||||
->whereNull('suspended_at')
|
||||
->with(['category', 'liveBroadcast', 'user'])
|
||||
->withCount('follows')
|
||||
->orderByDesc('viewer_count')
|
||||
->latest()
|
||||
->limit(24)
|
||||
->get()
|
||||
->map(fn (Channel $channel) => $this->channelCard($channel));
|
||||
|
||||
return Inertia::render('welcome', [
|
||||
'canRegister' => Features::enabled(Features::registration()),
|
||||
'liveChannels' => $liveChannels,
|
||||
'categories' => Category::query()
|
||||
->orderBy('name')
|
||||
->get(['id', 'name', 'slug'])
|
||||
->map(fn (Category $category) => [
|
||||
'id' => $category->id,
|
||||
'name' => $category->name,
|
||||
'slug' => $category->slug,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
private function channelCard(Channel $channel): array
|
||||
{
|
||||
return [
|
||||
'id' => $channel->id,
|
||||
'slug' => $channel->slug,
|
||||
'display_name' => $channel->display_name,
|
||||
'description' => $channel->description,
|
||||
'thumbnail_path' => $channel->thumbnail_path,
|
||||
'viewer_count' => $channel->viewer_count,
|
||||
'followers_count' => $channel->follows_count ?? 0,
|
||||
'category' => $channel->category ? [
|
||||
'id' => $channel->category->id,
|
||||
'name' => $channel->category->name,
|
||||
'slug' => $channel->category->slug,
|
||||
] : null,
|
||||
'broadcast' => $channel->liveBroadcast ? [
|
||||
'id' => $channel->liveBroadcast->id,
|
||||
'title' => $channel->liveBroadcast->title,
|
||||
'started_at' => $channel->liveBroadcast->started_at?->toIso8601String(),
|
||||
] : null,
|
||||
'owner' => [
|
||||
'id' => $channel->user->id,
|
||||
'name' => $channel->user->name,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
64
app/Http/Controllers/MediaMtxController.php
Normal file
64
app/Http/Controllers/MediaMtxController.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\Streaming\MediaMtxService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class MediaMtxController extends Controller
|
||||
{
|
||||
public function auth(Request $request, MediaMtxService $mediaMtx): Response
|
||||
{
|
||||
$this->ensureSecret($request);
|
||||
|
||||
return $mediaMtx->authorize($request->all())
|
||||
? response()->noContent()
|
||||
: response('Unauthorized', 403);
|
||||
}
|
||||
|
||||
public function ready(Request $request, MediaMtxService $mediaMtx): Response
|
||||
{
|
||||
$this->ensureSecret($request);
|
||||
|
||||
$mediaMtx->markReady(
|
||||
(string) $request->input('path', $request->query('path', '')),
|
||||
(string) $request->input('source_type', $request->query('source_type', '')),
|
||||
(string) $request->input('source_id', $request->query('source_id', '')),
|
||||
);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function notReady(Request $request, MediaMtxService $mediaMtx): Response
|
||||
{
|
||||
$this->ensureSecret($request);
|
||||
|
||||
$mediaMtx->markNotReady((string) $request->input('path', $request->query('path', '')));
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function recordingComplete(Request $request, MediaMtxService $mediaMtx): Response
|
||||
{
|
||||
$this->ensureSecret($request);
|
||||
|
||||
$mediaMtx->recordSegmentComplete(
|
||||
(string) $request->input('path', $request->query('path', '')),
|
||||
(string) $request->input('segment_path', $request->query('segment_path', '')),
|
||||
$request->input('duration', $request->query('duration')),
|
||||
);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
private function ensureSecret(Request $request): void
|
||||
{
|
||||
$expected = (string) config('streaming.mediamtx_shared_secret');
|
||||
$provided = (string) ($request->header('X-MediaMTX-Secret')
|
||||
?: $request->query('secret')
|
||||
?: $request->input('secret'));
|
||||
|
||||
abort_if($expected === '' || ! hash_equals($expected, $provided), 403);
|
||||
}
|
||||
}
|
||||
17
app/Http/Middleware/EnsureUserIsAdmin.php
Normal file
17
app/Http/Middleware/EnsureUserIsAdmin.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class EnsureUserIsAdmin
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
abort_unless($request->user()?->is_admin, 403);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user