Compare commits
29 Commits
6ce0255764
...
laravel-12
| Author | SHA1 | Date | |
|---|---|---|---|
|
af739e3c88
|
|||
| 273ed65a8d | |||
| ccfd5b996b | |||
| e5ef197ed6 | |||
| c0be2e294a | |||
| 823a284fbc | |||
| 67e601d0c4 | |||
| 7e4ebd91ad | |||
| 4dc5dee2b9 | |||
| 5310908b0c | |||
| 4b05b3db6d | |||
| df47a926e4 | |||
| 1e9e95f35f | |||
| 2aa76baafd | |||
| aa50bb1f72 | |||
| dfedf4058e | |||
| 268e3eb4c2 | |||
| ab61574956 | |||
| 81038b6c26 | |||
| e949ba955a | |||
| 819e2fde27 | |||
| 3259e2197b | |||
| b133db0573 | |||
| 41c34e6d89 | |||
| db6da608aa | |||
| 13b70fdf23 | |||
| cfd6af59fb | |||
| 7810cd53fb | |||
| 871028930b |
11
app/Enums/UserRole.php
Normal file
11
app/Enums/UserRole.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum UserRole: string
|
||||||
|
{
|
||||||
|
case ADMINISTRATOR = 'admin';
|
||||||
|
case MODERATOR = 'moderator';
|
||||||
|
case SUPPORTER = 'supporter';
|
||||||
|
case BANNED = 'banned';
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use App\Models\Comment;
|
||||||
use App\Models\Episode;
|
use App\Models\Episode;
|
||||||
use App\Models\Hentai;
|
use App\Models\Hentai;
|
||||||
use App\Models\PopularMonthly;
|
use App\Models\PopularMonthly;
|
||||||
@@ -126,7 +127,7 @@ class CacheHelper
|
|||||||
public static function getLatestComments()
|
public static function getLatestComments()
|
||||||
{
|
{
|
||||||
return Cache::remember("latest_comments", now()->addMinutes(60), function () {
|
return Cache::remember("latest_comments", now()->addMinutes(60), function () {
|
||||||
return DB::table('comments')->latest()->take(10)->get();
|
return Comment::latest()->take(10)->get();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -31,11 +32,11 @@ class UserController extends Controller
|
|||||||
|
|
||||||
switch ($validated['action']) {
|
switch ($validated['action']) {
|
||||||
case 'ban':
|
case 'ban':
|
||||||
$user->update(['is_banned' => 1]);
|
$user->addRole(UserRole::BANNED);
|
||||||
alert()->success('Banned', 'User has been banned.');
|
alert()->success('Banned', 'User has been banned.');
|
||||||
break;
|
break;
|
||||||
case 'unban':
|
case 'unban':
|
||||||
$user->update(['is_banned' => 0]);
|
$user->removeRole(UserRole::BANNED);
|
||||||
alert()->success('Unbanned', 'User has been unbanned.');
|
alert()->success('Unbanned', 'User has been unbanned.');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use App\Models\Episode;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|
||||||
|
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||||
|
|
||||||
class DownloadApiController extends Controller
|
class DownloadApiController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -16,11 +18,12 @@ class DownloadApiController extends Controller
|
|||||||
public function getDownload(Request $request)
|
public function getDownload(Request $request)
|
||||||
{
|
{
|
||||||
$validated = $request->validate([
|
$validated = $request->validate([
|
||||||
'episode_id' => 'required',
|
'episode_id' => ['required'],
|
||||||
'captcha' => 'required|captcha'
|
'captcha' => ['required', new ValidAltcha],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$episode = Episode::where('id', $request->input('episode_id'))->firstOrFail();
|
$episode = Episode::where('id', $request->input('episode_id'))
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
// Increase download count, as we assume the user
|
// Increase download count, as we assume the user
|
||||||
// downloads after submitting the captcha
|
// downloads after submitting the captcha
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers\Auth;
|
namespace App\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
@@ -88,10 +89,7 @@ class DiscordAuthController extends Controller
|
|||||||
|
|
||||||
// User is not in the guild
|
// User is not in the guild
|
||||||
if ($response->status() === 404) {
|
if ($response->status() === 404) {
|
||||||
$user->update([
|
$user->removeRole(UserRole::SUPPORTER);
|
||||||
'is_patreon' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,21 +108,15 @@ class DiscordAuthController extends Controller
|
|||||||
$discordRoles = $response->json('roles', []);
|
$discordRoles = $response->json('roles', []);
|
||||||
$patreonRoles = config('discord.patreon_roles', []);
|
$patreonRoles = config('discord.patreon_roles', []);
|
||||||
|
|
||||||
$isPatreon = false;
|
// If intersect of array is empty, then the user doesn't have the role
|
||||||
foreach($patreonRoles as $patreonRole)
|
$hasSupporterRole = !empty(array_intersect($discordRoles, $patreonRoles));
|
||||||
{
|
|
||||||
if (in_array($patreonRole, $discordRoles, true)) {
|
if (!$hasSupporterRole) {
|
||||||
$isPatreon = true;
|
// Remove role if not found
|
||||||
break;
|
$user->removeRole(UserRole::SUPPORTER);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only update if something actually changed
|
$user->addRole(UserRole::SUPPORTER);
|
||||||
if ($user->is_patreon !== $isPatreon) {
|
|
||||||
$user->update([
|
|
||||||
'is_patreon' => $isPatreon,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use Illuminate\Support\Facades\Auth;
|
|||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Validation\Rules;
|
use Illuminate\Validation\Rules;
|
||||||
|
|
||||||
|
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||||
|
|
||||||
class RegisteredUserController extends Controller
|
class RegisteredUserController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -24,6 +26,7 @@ class RegisteredUserController extends Controller
|
|||||||
'name' => ['required', 'string', 'max:255'],
|
'name' => ['required', 'string', 'max:255'],
|
||||||
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
|
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
|
||||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||||
|
'altcha' => ['required', new ValidAltcha],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace App\Http\Controllers;
|
|||||||
use App\Models\Contact;
|
use App\Models\Contact;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||||
|
|
||||||
class ContactController extends Controller
|
class ContactController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -25,7 +27,7 @@ class ContactController extends Controller
|
|||||||
'email' => 'required|max:50',
|
'email' => 'required|max:50',
|
||||||
'message' => 'required|max:1000',
|
'message' => 'required|max:1000',
|
||||||
'subject' => 'required|max:50',
|
'subject' => 'required|max:50',
|
||||||
'captcha' => 'required|captcha',
|
'altcha' => ['required', new ValidAltcha],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$contact = new Contact();
|
$contact = new Contact();
|
||||||
@@ -37,9 +39,4 @@ class ContactController extends Controller
|
|||||||
|
|
||||||
return back()->with('status', 'contact-submitted');
|
return back()->with('status', 'contact-submitted');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reloadCaptcha(): \Illuminate\Http\JsonResponse
|
|
||||||
{
|
|
||||||
return response()->json(['captcha'=> captcha_img()]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,11 +111,13 @@ class HomeController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function updateLanguage(Request $request): \Illuminate\Http\RedirectResponse
|
public function updateLanguage(Request $request): \Illuminate\Http\RedirectResponse
|
||||||
{
|
{
|
||||||
if(! in_array($request->language, config('lang-detector.languages'))) {
|
abort_unless(in_array($request->language, config('app.supported_locales'), true), 404);
|
||||||
return redirect()->back();
|
|
||||||
}
|
|
||||||
|
|
||||||
Cookie::queue(Cookie::forever('locale', $request->language));
|
session(['locale' => $request->language]);
|
||||||
|
|
||||||
|
if (Auth::check()) {
|
||||||
|
Auth::user()->update(['locale' => $request->language]);
|
||||||
|
}
|
||||||
|
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Episode;
|
use App\Models\Episode;
|
||||||
use App\Models\Playlist;
|
|
||||||
use App\Models\PlaylistEpisode;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Http\Requests\ProfileUpdateRequest;
|
use App\Http\Requests\ProfileUpdateRequest;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
@@ -153,7 +151,7 @@ class ProfileController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update comments to deleted user
|
// Update comments to deleted user
|
||||||
DB::table('comments')->where('commenter_id', '=', $user->id)->update(['commenter_id' => 1]);
|
DB::table('comments')->where('user_id', '=', $user->id)->update(['user_id' => 1]);
|
||||||
|
|
||||||
// Delete Profile Picture
|
// Delete Profile Picture
|
||||||
if ($user->avatar) {
|
if ($user->avatar) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class Kernel extends HttpKernel
|
|||||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
\App\Http\Middleware\IsBanned::class,
|
\App\Http\Middleware\IsBanned::class,
|
||||||
|
\App\Http\Middleware\SetLocale::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
@@ -58,6 +59,7 @@ class Kernel extends HttpKernel
|
|||||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
'auth.admin' => \App\Http\Middleware\IsAdmin::class,
|
'auth.admin' => \App\Http\Middleware\IsAdmin::class,
|
||||||
|
'auth.moderator' => \App\Http\Middleware\IsModerator::class,
|
||||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
|||||||
@@ -1,28 +1,14 @@
|
|||||||
<?php namespace app\Http\Middleware;
|
<?php namespace app\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Contracts\Auth\Guard;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class IsAdmin {
|
class IsAdmin {
|
||||||
|
|
||||||
/**
|
|
||||||
* The Guard implementation.
|
|
||||||
*
|
|
||||||
* @var Guard
|
|
||||||
*/
|
|
||||||
protected $auth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new filter instance.
|
|
||||||
*
|
|
||||||
* @param Guard $auth
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(Guard $auth)
|
|
||||||
{
|
|
||||||
$this->auth = $auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an incoming request.
|
* Handle an incoming request.
|
||||||
*
|
*
|
||||||
@@ -30,15 +16,14 @@ class IsAdmin {
|
|||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
if( ! $this->auth->user()->is_admin)
|
if(Auth::check() && Auth::user()->hasRole(UserRole::ADMINISTRATOR))
|
||||||
{
|
{
|
||||||
session()->flash('error_msg','This resource is restricted to Administrators!');
|
|
||||||
return redirect()->route('home.index');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session()->flash('error_msg','This resource is restricted to Administrators!');
|
||||||
|
return redirect()->route('home.index');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<?php namespace app\Http\Middleware;
|
<?php namespace app\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Contracts\Auth\Guard;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class IsBanned {
|
class IsBanned {
|
||||||
|
|
||||||
@@ -13,9 +16,9 @@ class IsBanned {
|
|||||||
* @param \Closure $next
|
* @param \Closure $next
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle(Request $request, Closure $next): Response
|
||||||
{
|
{
|
||||||
if(auth()->check() && auth()->user()->is_banned == 1)
|
if(Auth::check() && Auth::user()->hasRole(UserRole::BANNED))
|
||||||
{
|
{
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
$request->session()->invalidate();
|
$request->session()->invalidate();
|
||||||
|
|||||||
29
app/Http/Middleware/IsModerator.php
Normal file
29
app/Http/Middleware/IsModerator.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class IsModerator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
if (Auth::check() && Auth::user()->hasRole(UserRole::MODERATOR))
|
||||||
|
{
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
session()->flash('error_msg','This resource is restricted to Administrators!');
|
||||||
|
return redirect()->route('home.index');
|
||||||
|
}
|
||||||
|
}
|
||||||
41
app/Http/Middleware/SetLocale.php
Normal file
41
app/Http/Middleware/SetLocale.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SetLocale
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
// 1. Logged-in user preference
|
||||||
|
if (Auth::check() && Auth::user()->locale) {
|
||||||
|
App::setLocale(Auth::user()->locale);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Session (guest or user override)
|
||||||
|
if (session()->has('locale') && in_array($request->language, config('app.supported_locales'), true)) {
|
||||||
|
App::setLocale(session('locale'));
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Browser language
|
||||||
|
$locale = $request->getPreferredLanguage(config('app.supported_locales'));
|
||||||
|
|
||||||
|
if ($locale) {
|
||||||
|
App::setLocale($locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,8 @@ use Illuminate\Support\Facades\RateLimiter;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
use GrantHolle\Altcha\Rules\ValidAltcha;
|
||||||
|
|
||||||
class LoginRequest extends FormRequest
|
class LoginRequest extends FormRequest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -29,6 +31,7 @@ class LoginRequest extends FormRequest
|
|||||||
return [
|
return [
|
||||||
'email' => ['required', 'string', 'email'],
|
'email' => ['required', 'string', 'email'],
|
||||||
'password' => ['required', 'string'],
|
'password' => ['required', 'string'],
|
||||||
|
'altcha' => ['required', new ValidAltcha],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\Comment;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@@ -24,13 +25,19 @@ class AdminCommentSearch extends Component
|
|||||||
$this->resetPage();
|
$this->resetPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteComment($commentId)
|
||||||
|
{
|
||||||
|
$comment = Comment::where('id', (int) $commentId)->firstOrFail();
|
||||||
|
$comment->delete();
|
||||||
|
|
||||||
|
cache()->flush();
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$comments = DB::table('comments')
|
$comments = Comment::when($this->search !== '', fn ($query) => $query->where('body', 'LIKE', "%$this->search%"))
|
||||||
->join('users', 'comments.commenter_id', '=', 'users.id')
|
->when($this->userSearch !== '', fn ($query) => $query->whereHas('user', fn ($query) => $query->where('name', 'LIKE', "%{$this->userSearch}%")))
|
||||||
->select('comments.*', 'users.name')
|
->orderBy('created_at', 'DESC')
|
||||||
->when($this->search !== '', fn ($query) => $query->where('comment', 'LIKE', "%$this->search%"))
|
|
||||||
->when($this->userSearch !== '', fn ($query) => $query->where('name', 'LIKE', "%$this->userSearch%"))
|
|
||||||
->paginate(12);
|
->paginate(12);
|
||||||
|
|
||||||
return view('livewire.admin-comment-search', [
|
return view('livewire.admin-comment-search', [
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
|
use App\Models\Comment;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
use Livewire\Attributes\Url;
|
use Livewire\Attributes\Url;
|
||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class AdminUserSearch extends Component
|
class AdminUserSearch extends Component
|
||||||
{
|
{
|
||||||
use WithPagination;
|
use WithPagination;
|
||||||
@@ -18,7 +18,7 @@ class AdminUserSearch extends Component
|
|||||||
public $search = '';
|
public $search = '';
|
||||||
|
|
||||||
#[Url(history: true)]
|
#[Url(history: true)]
|
||||||
public $filtered = ['true'];
|
public $discordId = '';
|
||||||
|
|
||||||
#[Url(history: true)]
|
#[Url(history: true)]
|
||||||
public $patreon = [];
|
public $patreon = [];
|
||||||
@@ -31,8 +31,7 @@ class AdminUserSearch extends Component
|
|||||||
$user = User::where('id', $userID)
|
$user = User::where('id', $userID)
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
DB::table('comments')
|
Comment::where('user_id', $user->id)
|
||||||
->where('commenter_id', '=', $user->id)
|
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
cache()->flush();
|
cache()->flush();
|
||||||
@@ -40,10 +39,10 @@ class AdminUserSearch extends Component
|
|||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$users = User::when($this->filtered !== [], fn ($query) => $query->where('id', '>=', 10000))
|
$users = User::when($this->patreon !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::SUPPORTER->value))
|
||||||
->when($this->patreon !== [], fn ($query) => $query->where('is_patreon', 1))
|
->when($this->banned !== [], fn ($query) => $query->whereJsonContains('roles', UserRole::BANNED->value))
|
||||||
->when($this->banned !== [], fn ($query) => $query->where('is_banned', 1))
|
|
||||||
->when($this->search !== '', fn ($query) => $query->where('name', 'like', '%'.$this->search.'%'))
|
->when($this->search !== '', fn ($query) => $query->where('name', 'like', '%'.$this->search.'%'))
|
||||||
|
->when($this->discordId !== '', fn ($query) => $query->where('discord_id', '=', $this->discordId))
|
||||||
->paginate(20);
|
->paginate(20);
|
||||||
|
|
||||||
return view('livewire.admin-user-search', [
|
return view('livewire.admin-user-search', [
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace App\Livewire;
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Episode;
|
||||||
|
use App\Notifications\CommentNotification;
|
||||||
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||||
|
|
||||||
|
use Maize\Markable\Models\Like;
|
||||||
|
|
||||||
class Comment extends Component
|
class Comment extends Component
|
||||||
{
|
{
|
||||||
use AuthorizesRequests;
|
use AuthorizesRequests;
|
||||||
@@ -13,6 +25,10 @@ class Comment extends Component
|
|||||||
|
|
||||||
public $isReplying = false;
|
public $isReplying = false;
|
||||||
|
|
||||||
|
public $likeCount = 0;
|
||||||
|
|
||||||
|
public $liked = false;
|
||||||
|
|
||||||
public $replyState = [
|
public $replyState = [
|
||||||
'body' => ''
|
'body' => ''
|
||||||
];
|
];
|
||||||
@@ -62,20 +78,50 @@ class Comment extends Component
|
|||||||
|
|
||||||
public function postReply()
|
public function postReply()
|
||||||
{
|
{
|
||||||
if (! $this->comment->depth() < 2) {
|
if (!($this->comment->depth() < 2)) {
|
||||||
|
$this->addError('replyState.body', "Too many sub comments.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
$rateLimitKey = "send-comment:{$user->id}";
|
||||||
|
$rateLimitMinutes = 60 * 5; // 5 minutes
|
||||||
|
|
||||||
|
if (RateLimiter::tooManyAttempts($rateLimitKey, 1)) {
|
||||||
|
$seconds = RateLimiter::availableIn($rateLimitKey);
|
||||||
|
|
||||||
|
$this->addError('replyState.body', "Too many comments. Try again in {$seconds} seconds.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RateLimiter::hit($rateLimitKey, $rateLimitMinutes);
|
||||||
|
|
||||||
$this->validate([
|
$this->validate([
|
||||||
'replyState.body' => 'required'
|
'replyState.body' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$reply = $this->comment->children()->make($this->replyState);
|
$reply = $this->comment->children()->make($this->replyState);
|
||||||
$reply->user()->associate(auth()->user());
|
$reply->user()->associate($user);
|
||||||
$reply->commentable()->associate($this->comment->commentable);
|
$reply->commentable()->associate($this->comment->commentable);
|
||||||
|
|
||||||
$reply->save();
|
$reply->save();
|
||||||
|
|
||||||
|
// Notify if Episode and if not the same user
|
||||||
|
if ($reply->commentable_type == Episode::class && $user->id !== $reply->parent->user->id) {
|
||||||
|
$episode = Episode::where('id', $reply->commentable_id)
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
$url = route('hentai.index', ['title' => $episode->slug]);
|
||||||
|
|
||||||
|
$reply->parent->user->notify(
|
||||||
|
new CommentNotification(
|
||||||
|
"{$user->name} replied to your comment.",
|
||||||
|
Str::limit($reply->body, 50),
|
||||||
|
"{$url}#comment-{$reply->id}"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$this->replyState = [
|
$this->replyState = [
|
||||||
'body' => ''
|
'body' => ''
|
||||||
];
|
];
|
||||||
@@ -85,6 +131,34 @@ class Comment extends Component
|
|||||||
$this->dispatch('refresh')->self();
|
$this->dispatch('refresh')->self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function like()
|
||||||
|
{
|
||||||
|
if (! Auth::check()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Like::toggle($this->comment, User::where('id', Auth::user()->id)->firstOrFail());
|
||||||
|
|
||||||
|
Cache::forget('commentLikes'.$this->comment->id);
|
||||||
|
|
||||||
|
if ($this->liked) {
|
||||||
|
$this->liked = false;
|
||||||
|
$this->likeCount--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->liked = true;
|
||||||
|
$this->likeCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
if (Auth::check()) {
|
||||||
|
$this->likeCount = $this->comment->likeCount();
|
||||||
|
$this->liked = Like::has($this->comment, User::where('id', Auth::user()->id)->firstOrFail());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.comment');
|
return view('livewire.comment');
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace App\Livewire;
|
|||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Livewire\WithPagination;
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
|
|
||||||
class Comments extends Component
|
class Comments extends Component
|
||||||
{
|
{
|
||||||
use WithPagination;
|
use WithPagination;
|
||||||
@@ -29,8 +31,21 @@ class Comments extends Component
|
|||||||
'newCommentState.body' => 'required'
|
'newCommentState.body' => 'required'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$user = auth()->user();
|
||||||
|
$rateLimitKey = "send-comment:{$user->id}";
|
||||||
|
$rateLimitMinutes = 60 * 5; // 5 minutes
|
||||||
|
|
||||||
|
if (RateLimiter::tooManyAttempts($rateLimitKey, 1)) {
|
||||||
|
$seconds = RateLimiter::availableIn($rateLimitKey);
|
||||||
|
|
||||||
|
$this->addError('newCommentState.body', "Too many comments. Try again in {$seconds} seconds.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RateLimiter::hit($rateLimitKey, $rateLimitMinutes);
|
||||||
|
|
||||||
$comment = $this->model->comments()->make($this->newCommentState);
|
$comment = $this->model->comments()->make($this->newCommentState);
|
||||||
$comment->user()->associate(auth()->user());
|
$comment->user()->associate($user);
|
||||||
$comment->save();
|
$comment->save();
|
||||||
|
|
||||||
$this->newCommentState = [
|
$this->newCommentState = [
|
||||||
@@ -47,7 +62,7 @@ class Comments extends Component
|
|||||||
->with('user', 'children.user', 'children.children')
|
->with('user', 'children.user', 'children.children')
|
||||||
->parent()
|
->parent()
|
||||||
->latest()
|
->latest()
|
||||||
->paginate(3);
|
->paginate(50);
|
||||||
|
|
||||||
return view('livewire.comments', [
|
return view('livewire.comments', [
|
||||||
'comments' => $comments
|
'comments' => $comments
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ class DownloadsSearch extends Component
|
|||||||
$types[] = 'FHD';
|
$types[] = 'FHD';
|
||||||
} elseif ($label === 'FHD 48fps') {
|
} elseif ($label === 'FHD 48fps') {
|
||||||
$types[] = 'FHDi';
|
$types[] = 'FHDi';
|
||||||
} elseif ($label === 'UHD' && auth()->user()->is_patreon) {
|
} elseif ($label === 'UHD' && auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) {
|
||||||
$types[] = 'UHD';
|
$types[] = 'UHD';
|
||||||
} elseif ($label === 'UHD 48fps' && auth()->user()->is_patreon) {
|
} elseif ($label === 'UHD 48fps' && auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) {
|
||||||
$types[] = 'UHDi';
|
$types[] = 'UHDi';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ class DownloadsSearch extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
if (!auth()->user()->is_patreon) {
|
if (!auth()->user()->hasRole(\App\Enums\UserRole::SUPPORTER)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
49
app/Livewire/UserComments.php
Normal file
49
app/Livewire/UserComments.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use App\Models\Comment;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\WithPagination;
|
||||||
|
|
||||||
|
class UserComments extends Component
|
||||||
|
{
|
||||||
|
use WithPagination;
|
||||||
|
|
||||||
|
public $model;
|
||||||
|
|
||||||
|
public $commentSearch;
|
||||||
|
|
||||||
|
public $order = 'created_at_desc';
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$orderby = 'created_at';
|
||||||
|
$orderdirection = 'desc';
|
||||||
|
|
||||||
|
switch ($this->order) {
|
||||||
|
case 'created_at_desc':
|
||||||
|
$orderby = 'created_at';
|
||||||
|
$orderdirection = 'desc';
|
||||||
|
break;
|
||||||
|
case 'created_at_asc':
|
||||||
|
$orderby = 'created_at';
|
||||||
|
$orderdirection = 'asc';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$orderby = 'created_at';
|
||||||
|
$orderdirection = 'desc';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$comments = Comment::where('user_id', $this->model->id)
|
||||||
|
->when($this->commentSearch != '', fn ($query) => $query->where('body', 'like', '%'.$this->commentSearch.'%'))
|
||||||
|
->orderBy($orderby, $orderdirection)
|
||||||
|
->paginate(10);
|
||||||
|
|
||||||
|
return view('livewire.user-comments', [
|
||||||
|
'comments' => $comments
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,16 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
||||||
|
use Maize\Markable\Markable;
|
||||||
|
use Maize\Markable\Models\Like;
|
||||||
|
|
||||||
class Comment extends Model
|
class Comment extends Model
|
||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes, Markable;
|
||||||
|
|
||||||
|
protected static $marks = [
|
||||||
|
Like::class
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
@@ -58,4 +65,12 @@ class Comment extends Model
|
|||||||
? $this->parent->depth() + 1
|
? $this->parent->depth() + 1
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached like count
|
||||||
|
*/
|
||||||
|
public function likeCount(): int
|
||||||
|
{
|
||||||
|
return cache()->remember('commentLikes' . $this->id, 300, fn() => $this->likes->count());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ class Hentai extends Model implements Sitemapable
|
|||||||
return $this->episodes->first()->title;
|
return $this->episodes->first()->title;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function comments()
|
||||||
|
{
|
||||||
|
return $this->morphMany(Comment::class, 'commentable');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Has a Gallery.
|
* Has a Gallery.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
//use Illuminate\Contracts\Auth\MustVerifyEmail;
|
//use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
|
|
||||||
|
use App\Enums\UserRole;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
@@ -25,7 +27,6 @@ class User extends Authenticatable
|
|||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
'locale',
|
'locale',
|
||||||
'is_banned',
|
|
||||||
// Discord
|
// Discord
|
||||||
'discord_id',
|
'discord_id',
|
||||||
'discord_avatar',
|
'discord_avatar',
|
||||||
@@ -54,7 +55,7 @@ class User extends Authenticatable
|
|||||||
'name' => 'string',
|
'name' => 'string',
|
||||||
'email' => 'string',
|
'email' => 'string',
|
||||||
'locale' => 'string',
|
'locale' => 'string',
|
||||||
'roles' => 'json',
|
'roles' => 'array',
|
||||||
'tag_blacklist' => 'array',
|
'tag_blacklist' => 'array',
|
||||||
// Discord
|
// Discord
|
||||||
'discord_id' => 'integer',
|
'discord_id' => 'integer',
|
||||||
@@ -89,9 +90,17 @@ class User extends Authenticatable
|
|||||||
/**
|
/**
|
||||||
* Has Many Comments.
|
* Has Many Comments.
|
||||||
*/
|
*/
|
||||||
|
public function comments()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Comment::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Comment Count.
|
||||||
|
*/
|
||||||
public function commentCount(): int
|
public function commentCount(): int
|
||||||
{
|
{
|
||||||
return DB::table('comments')->where('commenter_id', $this->id)->count();
|
return cache()->remember('userComments' . $this->id, 300, fn() => $this->comments->count());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,4 +120,44 @@ class User extends Authenticatable
|
|||||||
|
|
||||||
return asset('images/default-avatar.webp');
|
return asset('images/default-avatar.webp');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user has a specific role
|
||||||
|
*/
|
||||||
|
public function hasRole(UserRole $role): bool
|
||||||
|
{
|
||||||
|
return in_array($role->value, $this->roles ?? [], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Role to User
|
||||||
|
*/
|
||||||
|
public function addRole(UserRole $role): void
|
||||||
|
{
|
||||||
|
if ($this->hasRole($role)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all current roles
|
||||||
|
$roles = $this->roles ?? [];
|
||||||
|
|
||||||
|
// Add new role
|
||||||
|
$roles[] = $role->value;
|
||||||
|
|
||||||
|
$this->roles = $roles;
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove Role from User
|
||||||
|
*/
|
||||||
|
public function removeRole(UserRole $role): void
|
||||||
|
{
|
||||||
|
if (!$this->hasRole($role)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->roles = array_diff($this->roles, [$role->value]);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ class EpisodeService
|
|||||||
Request $request,
|
Request $request,
|
||||||
Hentai $hentai,
|
Hentai $hentai,
|
||||||
int $episodeNumber,
|
int $episodeNumber,
|
||||||
Studios $studio = null,
|
?Studios $studio = null,
|
||||||
Episode $referenceEpisode = null
|
?Episode $referenceEpisode = null
|
||||||
): Episode
|
): Episode
|
||||||
{
|
{
|
||||||
$episode = new Episode();
|
$episode = new Episode();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "laravel/laravel",
|
"name": "w33b/hstream",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"description": "The skeleton application for the Laravel framework.",
|
"description": "The website of hstream.moe",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"laravel",
|
"laravel",
|
||||||
"framework"
|
"framework"
|
||||||
@@ -9,45 +9,38 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
|
"grantholle/laravel-altcha": "^2.1",
|
||||||
"guzzlehttp/guzzle": "^7.8.1",
|
"guzzlehttp/guzzle": "^7.8.1",
|
||||||
"hisorange/browser-detect": "^5.0",
|
"hisorange/browser-detect": "^5.0",
|
||||||
"http-interop/http-factory-guzzle": "^1.2",
|
"http-interop/http-factory-guzzle": "^1.2",
|
||||||
"intervention/image": "^3.9",
|
"intervention/image": "^3.11",
|
||||||
"intervention/image-laravel": "^1.3",
|
"intervention/image-laravel": "^1.5",
|
||||||
"laravel/framework": "^11.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.2",
|
||||||
"laravel/scout": "^10.20",
|
"laravel/scout": "^10.20",
|
||||||
"laravel/socialite": "^5.24",
|
"laravel/socialite": "^5.24",
|
||||||
"laravel/tinker": "^2.10",
|
"laravel/tinker": "^2.10",
|
||||||
"livewire/livewire": "^3.6.4",
|
"livewire/livewire": "^3.7.0",
|
||||||
"maize-tech/laravel-markable": "^2.3.0",
|
"maize-tech/laravel-markable": "^2.3.0",
|
||||||
"meilisearch/meilisearch-php": "^1.16",
|
"meilisearch/meilisearch-php": "^1.16",
|
||||||
"mews/captcha": "3.4.4",
|
|
||||||
"predis/predis": "^2.2",
|
"predis/predis": "^2.2",
|
||||||
"realrashid/sweet-alert": "^7.2",
|
"realrashid/sweet-alert": "^7.2",
|
||||||
"rtconner/laravel-tagging": "^4.1",
|
"rtconner/laravel-tagging": "^5.0",
|
||||||
"socialiteproviders/discord": "^4.2",
|
"socialiteproviders/discord": "^4.2",
|
||||||
"spatie/laravel-discord-alerts": "^1.5",
|
"spatie/laravel-discord-alerts": "^1.8",
|
||||||
"spatie/laravel-sitemap": "^7.3",
|
"spatie/laravel-sitemap": "^7.3"
|
||||||
"vluzrmos/language-detector": "^2.3"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "^3.14.7",
|
"barryvdh/laravel-debugbar": "^3.16",
|
||||||
"fakerphp/faker": "^1.24.0",
|
"fakerphp/faker": "^1.24.0",
|
||||||
"laravel/breeze": "^2.3",
|
"laravel/breeze": "^2.3",
|
||||||
"laravel/pint": "^1.18",
|
"laravel/pint": "^1.18",
|
||||||
"laravel/sail": "^1.38",
|
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/collision": "^8.1",
|
"nunomaduro/collision": "^8.1",
|
||||||
"phpunit/phpunit": "^11.4",
|
"phpunit/phpunit": "^11.4",
|
||||||
"spatie/laravel-ignition": "^2.0"
|
"spatie/laravel-ignition": "^2.0"
|
||||||
},
|
},
|
||||||
"repositories": [
|
"repositories": [],
|
||||||
{
|
|
||||||
"type": "vcs",
|
|
||||||
"url": "https://github.com/renatokira/comments.git"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"exclude-from-classmap": [],
|
"exclude-from-classmap": [],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|||||||
2033
composer.lock
generated
2033
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -111,6 +111,18 @@ return [
|
|||||||
|
|
||||||
'faker_locale' => 'en_US',
|
'faker_locale' => 'en_US',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Supported Locales
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This is used to display the supported locales by this app, it also is
|
||||||
|
| used to verify session data and requests in the SetLocale Middleware
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'supported_locales' => ['en', 'de', 'fr'],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Encryption Key
|
| Encryption Key
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
'characters' => ['2', '3', '4', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'X', 'Y', 'Z'],
|
|
||||||
'default' => [
|
|
||||||
'length' => 5,
|
|
||||||
'width' => 120,
|
|
||||||
'height' => 36,
|
|
||||||
'quality' => 90,
|
|
||||||
'math' => false,
|
|
||||||
'expire' => 60,
|
|
||||||
'encrypt' => false,
|
|
||||||
],
|
|
||||||
'math' => [
|
|
||||||
'length' => 9,
|
|
||||||
'width' => 120,
|
|
||||||
'height' => 36,
|
|
||||||
'quality' => 90,
|
|
||||||
'math' => true,
|
|
||||||
],
|
|
||||||
|
|
||||||
'flat' => [
|
|
||||||
'length' => 6,
|
|
||||||
'width' => 160,
|
|
||||||
'height' => 46,
|
|
||||||
'quality' => 90,
|
|
||||||
'lines' => 6,
|
|
||||||
'bgImage' => false,
|
|
||||||
'bgColor' => '#ecf2f4',
|
|
||||||
'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],
|
|
||||||
'contrast' => -5,
|
|
||||||
],
|
|
||||||
'mini' => [
|
|
||||||
'length' => 3,
|
|
||||||
'width' => 60,
|
|
||||||
'height' => 32,
|
|
||||||
],
|
|
||||||
'inverse' => [
|
|
||||||
'length' => 5,
|
|
||||||
'width' => 120,
|
|
||||||
'height' => 36,
|
|
||||||
'quality' => 90,
|
|
||||||
'sensitive' => true,
|
|
||||||
'angle' => 12,
|
|
||||||
'sharpen' => 10,
|
|
||||||
'blur' => 2,
|
|
||||||
'invert' => true,
|
|
||||||
'contrast' => -5,
|
|
||||||
]
|
|
||||||
];
|
|
||||||
@@ -6,13 +6,13 @@ return [
|
|||||||
'guild_id' => 802233383710228550,
|
'guild_id' => 802233383710228550,
|
||||||
|
|
||||||
'patreon_roles' => [
|
'patreon_roles' => [
|
||||||
841798154999169054, // ????
|
'841798154999169054', // ????
|
||||||
803329707650187364, // Tier-5
|
'803329707650187364', // Tier-5
|
||||||
803327903659196416, // ????
|
'803327903659196416', // ????
|
||||||
803325441942356059, // Tier-3
|
'803325441942356059', // Tier-3
|
||||||
803322725576736858, // Tier-2
|
'803322725576736858', // Tier-2
|
||||||
802270568912519198, // Tier-1
|
'802270568912519198', // Tier-1
|
||||||
802234830384267315 // admin
|
'802234830384267315' // admin
|
||||||
],
|
],
|
||||||
|
|
||||||
'discord_bot_token' => env('DISCORD_BOT_TOKEN'),
|
'discord_bot_token' => env('DISCORD_BOT_TOKEN'),
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
/*
|
|
||||||
* Indicates whenever should autodetect and apply the language of the request.
|
|
||||||
*/
|
|
||||||
'autodetect' => env('LANG_DETECTOR_AUTODETECT', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Default driver to use to detect the request language.
|
|
||||||
*
|
|
||||||
* Available: browser, subdomain, uri.
|
|
||||||
*/
|
|
||||||
'driver' => env('LANG_DETECTOR_DRIVER', 'browser'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Used on subdomain and uri drivers. That indicates which segment should be used
|
|
||||||
* to verify the language.
|
|
||||||
*/
|
|
||||||
'segment' => env('LANG_DETECTOR_SEGMENT', 0),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Languages available on the application.
|
|
||||||
*
|
|
||||||
* You could use parse_langs_to_array to use the string syntax
|
|
||||||
* or just use the array of languages with its aliases.
|
|
||||||
*/
|
|
||||||
'languages' => parse_langs_to_array(
|
|
||||||
env('LANG_DETECTOR_LANGUAGES', ['en', 'de', 'fr'])
|
|
||||||
),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Indicates if should store detected locale on cookies
|
|
||||||
*/
|
|
||||||
'cookie' => (bool) env('LANG_DETECTOR_COOKIE', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Indicates if should encrypt cookie
|
|
||||||
*/
|
|
||||||
'cookie_encrypt' => (bool) env('LANG_DETECTOR_COOKIE_ENCRYPT', false),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cookie name
|
|
||||||
*/
|
|
||||||
'cookie_name' => env('LANG_DETECTOR_COOKIE', 'locale'),
|
|
||||||
];
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*/
|
|
||||||
public function up(): void
|
|
||||||
{
|
|
||||||
// Remame old table from laravelista/comments
|
|
||||||
Schema::rename('comments', 'comments_old');
|
|
||||||
|
|
||||||
Schema::create('comments', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
|
||||||
$table->foreignId('parent_id')->nullable()->constrained('comments')->cascadeOnDelete();
|
|
||||||
$table->morphs('commentable'); // What is being commented on
|
|
||||||
$table->text('body');
|
|
||||||
$table->softDeletes();
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('comments');
|
|
||||||
|
|
||||||
// Revert to old table from laravelista/comments
|
|
||||||
Schema::rename('comments_old', 'comments');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Drop Foreign Keys and Index
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['child_id']);
|
||||||
|
$table->dropIndex(['commenter_id', 'commenter_type']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rename and Drop columns
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->renameColumn('commenter_id', 'user_id');
|
||||||
|
$table->dropColumn('commenter_type');
|
||||||
|
$table->dropColumn('guest_name');
|
||||||
|
$table->dropColumn('guest_email');
|
||||||
|
$table->renameColumn('child_id', 'parent_id');
|
||||||
|
$table->renameColumn('comment', 'body');
|
||||||
|
$table->dropColumn('approved');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add Foreign Keys
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
// Ensure the column is unsigned
|
||||||
|
$table->bigInteger('user_id')->unsigned()->change();
|
||||||
|
|
||||||
|
// Add the foreign key constraint
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
$table->foreign('parent_id')->references('id')->on('comments')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// Drop Foreign Keys
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['parent_id']);
|
||||||
|
$table->dropForeign(['user_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rename and Re-Add Columns
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->renameColumn('user_id', 'commenter_id');
|
||||||
|
$table->string('commenter_type')->nullable()->after('commenter_id');
|
||||||
|
$table->string('guest_name')->nullable()->after('commenter_type');
|
||||||
|
$table->string('guest_email')->nullable()->after('guest_name');
|
||||||
|
$table->renameColumn('parent_id', 'child_id');
|
||||||
|
$table->renameColumn('body', 'comment');
|
||||||
|
$table->boolean('approved')->default(true)->after('comment');
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('comments')->update([
|
||||||
|
'commenter_type' => 'App\Models\User',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Re-Add foreign key constraint and index
|
||||||
|
Schema::table('comments', function (Blueprint $table) {
|
||||||
|
$table->foreign('child_id')->references('id')->on('comments')->onDelete('cascade');
|
||||||
|
$table->index(["commenter_id", "commenter_type"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->string('locale', 10)
|
||||||
|
->nullable()
|
||||||
|
->after('discord_avatar');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('locale');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
// Migrate supporters
|
||||||
|
DB::table('users')->where('is_patreon', 1)->update([
|
||||||
|
'roles' => DB::raw("JSON_ARRAY('supporter')")
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Migrate banned
|
||||||
|
DB::table('users')->where('is_banned', 1)->update([
|
||||||
|
'roles' => DB::raw("JSON_ARRAY('banned')")
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Migrate admins
|
||||||
|
DB::table('users')->where('is_admin', 1)->update([
|
||||||
|
'roles' => DB::raw("JSON_ARRAY('admin')")
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Drop columns
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_admin');
|
||||||
|
$table->dropColumn('is_patreon');
|
||||||
|
$table->dropColumn('is_banned');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
DB::table('users')->update(['roles' => null]);
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_admin')->default(0);
|
||||||
|
$table->boolean('is_patreon')->default(0);
|
||||||
|
$table->boolean('is_banned')->default(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
services:
|
|
||||||
laravel.test:
|
|
||||||
build:
|
|
||||||
context: './vendor/laravel/sail/runtimes/8.3'
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
args:
|
|
||||||
WWWGROUP: '${WWWGROUP}'
|
|
||||||
MYSQL_CLIENT: mariadb-client
|
|
||||||
image: 'sail-8.3/app'
|
|
||||||
extra_hosts:
|
|
||||||
- 'host.docker.internal:host-gateway'
|
|
||||||
ports:
|
|
||||||
- '${APP_PORT:-80}:80'
|
|
||||||
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
|
|
||||||
environment:
|
|
||||||
WWWUSER: '${WWWUSER}'
|
|
||||||
LARAVEL_SAIL: 1
|
|
||||||
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
|
|
||||||
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
|
|
||||||
IGNITION_LOCAL_SITES_PATH: '${PWD}'
|
|
||||||
volumes:
|
|
||||||
- '.:/var/www/html'
|
|
||||||
networks:
|
|
||||||
- sail
|
|
||||||
depends_on:
|
|
||||||
- mariadb
|
|
||||||
- redis
|
|
||||||
mariadb:
|
|
||||||
image: 'mariadb:11'
|
|
||||||
ports:
|
|
||||||
- '${FORWARD_DB_PORT:-3306}:3306'
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
|
|
||||||
MYSQL_ROOT_HOST: '%'
|
|
||||||
MYSQL_DATABASE: '${DB_DATABASE}'
|
|
||||||
MYSQL_USER: '${DB_USERNAME}'
|
|
||||||
MYSQL_PASSWORD: '${DB_PASSWORD}'
|
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
|
||||||
volumes:
|
|
||||||
- 'sail-mariadb:/var/lib/mysql'
|
|
||||||
- './vendor/laravel/sail/database/mariadb/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
|
|
||||||
networks:
|
|
||||||
- sail
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
- CMD
|
|
||||||
- healthcheck.sh
|
|
||||||
- '--connect'
|
|
||||||
- '--innodb_initialized'
|
|
||||||
retries: 3
|
|
||||||
timeout: 5s
|
|
||||||
redis:
|
|
||||||
image: 'redis:alpine'
|
|
||||||
ports:
|
|
||||||
- '${FORWARD_REDIS_PORT:-6379}:6379'
|
|
||||||
volumes:
|
|
||||||
- 'sail-redis:/data'
|
|
||||||
networks:
|
|
||||||
- sail
|
|
||||||
healthcheck:
|
|
||||||
test:
|
|
||||||
- CMD
|
|
||||||
- redis-cli
|
|
||||||
- ping
|
|
||||||
retries: 3
|
|
||||||
timeout: 5s
|
|
||||||
networks:
|
|
||||||
sail:
|
|
||||||
driver: bridge
|
|
||||||
volumes:
|
|
||||||
sail-mariadb:
|
|
||||||
driver: local
|
|
||||||
sail-redis:
|
|
||||||
driver: local
|
|
||||||
1313
package-lock.json
generated
1313
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
|||||||
"@fortawesome/fontawesome-free": "^6.5.1",
|
"@fortawesome/fontawesome-free": "^6.5.1",
|
||||||
"@jellyfin/libass-wasm": "^4.1.1",
|
"@jellyfin/libass-wasm": "^4.1.1",
|
||||||
"@yaireo/tagify": "^4.21.2",
|
"@yaireo/tagify": "^4.21.2",
|
||||||
|
"altcha": "^2.3.0",
|
||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
"dashjs": "^5.0.0",
|
"dashjs": "^5.0.0",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
|
|||||||
@@ -123,3 +123,32 @@ input:checked~.dot {
|
|||||||
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff) format('woff');
|
src: url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff2) format('woff2'), url(https://fonts.bunny.net/figtree/files/figtree-latin-ext-600-normal.woff) format('woff');
|
||||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Captcha */
|
||||||
|
:root {
|
||||||
|
--altcha-border-width: 1px;
|
||||||
|
--altcha-border-radius: 0.375rem;
|
||||||
|
--altcha-color-base: #333;
|
||||||
|
--altcha-color-border: #a0a0a0;
|
||||||
|
--altcha-color-text: #fff;
|
||||||
|
--altcha-color-border-focus: currentColor;
|
||||||
|
--altcha-color-error-text: #f23939;
|
||||||
|
--altcha-color-footer-bg: #141414;
|
||||||
|
--altcha-max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.altcha-footer {
|
||||||
|
border-bottom-left-radius: 0.375rem;
|
||||||
|
border-bottom-right-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-color: #a0a0a0;
|
||||||
|
color: rgb(225,29,72);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:checked {
|
||||||
|
background-color: rgb(225,29,72);
|
||||||
|
box-shadow: 0 0 0 0px #fff, 0 0 0 calc(2px + 0px) rgba(246, 59, 118, 0.5), 0 0 #0000;
|
||||||
|
}
|
||||||
@@ -12,6 +12,9 @@ import {
|
|||||||
initTE,
|
initTE,
|
||||||
} from "tw-elements";
|
} from "tw-elements";
|
||||||
|
|
||||||
|
// Captcha
|
||||||
|
import 'altcha';
|
||||||
|
|
||||||
// import Alpine from 'alpinejs';
|
// import Alpine from 'alpinejs';
|
||||||
|
|
||||||
// window.Alpine = Alpine;
|
// window.Alpine = Alpine;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="mb-4 rounded-lg bg-success-400 px-6 py-5 text-base text-success-800 mt-5" role="alert">
|
<div class="mb-4 rounded-lg bg-success-400 px-6 py-5 text-base text-success-800 mt-5" role="alert">
|
||||||
{{ $alert->text }}
|
{{ $alert->text }}
|
||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->is_admin)
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-success-900">
|
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-success-900">
|
||||||
@csrf
|
@csrf
|
||||||
@method('delete')
|
@method('delete')
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
<div class="mb-4 rounded-lg bg-danger-400 px-6 py-5 text-base text-danger-800 mt-5" role="alert">
|
<div class="mb-4 rounded-lg bg-danger-400 px-6 py-5 text-base text-danger-800 mt-5" role="alert">
|
||||||
{{ $alert->text }}
|
{{ $alert->text }}
|
||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->is_admin)
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-danger-900">
|
<form method="POST" action="{{ route('admin.alert.delete', $alert->id) }}" class="float-right hover:text-danger-900">
|
||||||
@csrf
|
@csrf
|
||||||
@method('delete')
|
@method('delete')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->is_admin)
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
|
<div class="relative p-5 bg-white dark:bg-neutral-700/40 rounded-lg overflow-hidden z-10">
|
||||||
<div class="float-left">
|
<div class="float-left">
|
||||||
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
<a data-te-toggle="modal" data-te-target="#modalUploadEpisode" class="text-xl text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap">
|
||||||
|
|||||||
@@ -69,6 +69,11 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<altcha-widget id="captcha" floating challengeurl="/altcha-challenge"></altcha-widget>
|
||||||
|
<x-input-error :messages="$errors->get('altcha')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
<div class="flex items-center justify-end mt-4">
|
||||||
@if (Route::has('password.request'))
|
@if (Route::has('password.request'))
|
||||||
<a class="underline text-sm text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-neutral-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 dark:focus:ring-offset-neutral-800" href="{{ route('password.request') }}">
|
<a class="underline text-sm text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-neutral-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-rose-500 dark:focus:ring-offset-neutral-800" href="{{ route('password.request') }}">
|
||||||
@@ -127,6 +132,11 @@
|
|||||||
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
<altcha-widget id="captcha" floating challengeurl="/altcha-challenge"></altcha-widget>
|
||||||
|
<x-input-error :messages="$errors->get('altcha')" class="mt-2" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
<div class="flex items-center justify-end mt-4">
|
||||||
<x-primary-button class="ms-4">
|
<x-primary-button class="ms-4">
|
||||||
{{ __('Register') }}
|
{{ __('Register') }}
|
||||||
|
|||||||
@@ -33,19 +33,7 @@
|
|||||||
<x-input-error class="mt-2" :messages="$errors->get('message')" />
|
<x-input-error class="mt-2" :messages="$errors->get('message')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<altcha-widget id="captcha" floating challengeurl="/altcha-challenge"></altcha-widget>
|
||||||
<x-input-label for="message" :value="__('Captcha')" />
|
|
||||||
<div class="flex pt-2">
|
|
||||||
<div id="captchaImg">
|
|
||||||
{!! captcha_img() !!}
|
|
||||||
</div>
|
|
||||||
<button type="button" class="inline-flex items-center ml-2 px-2 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150" id="reloadcaptcha">
|
|
||||||
<i class="fa-solid fa-rotate-right"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<x-text-input id="captcha" class="block " type="text" name="captcha" required />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
<x-primary-button>{{ __('Submit') }}</x-primary-button>
|
||||||
@@ -65,18 +53,4 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script>
|
|
||||||
function reloadCaptcha() {
|
|
||||||
window.axios.get('/reload-captcha').then(function(response) {
|
|
||||||
if (response.status == 200) {
|
|
||||||
document.querySelector("#captchaImg").innerHTML = response.data.captcha;
|
|
||||||
}
|
|
||||||
}).catch(function(error) {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelector("#reloadcaptcha").addEventListener("click", reloadCaptcha);
|
|
||||||
</script>
|
|
||||||
</section>
|
</section>
|
||||||
@@ -2,14 +2,14 @@
|
|||||||
{{ __('home.latest-comments') }}
|
{{ __('home.latest-comments') }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2 md:grid-cols-2">
|
<div class="grid gap-2 grid-cols-1 xl:grid-cols-2">
|
||||||
@foreach ($latestComments as $comment)
|
@foreach ($latestComments as $comment)
|
||||||
@if ($comment->commentable_type == 'App\Models\Episode')
|
@if ($comment->commentable_type == \App\Models\Episode::class)
|
||||||
@php $episode = cache()->rememberForever('commentEpisode'.$comment->commentable_id, fn () => App\Models\Episode::with('gallery')->where('id', $comment->commentable_id)->first()); @endphp
|
@php $episode = cache()->rememberForever('commentEpisode'.$comment->commentable_id, fn () => App\Models\Episode::with('gallery')->where('id', $comment->commentable_id)->first()); @endphp
|
||||||
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
|
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
|
||||||
<div
|
<div
|
||||||
class="w-[20vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
|
class="w-[15vw] mr-5 p-1 md:p-2 mb-4 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
|
||||||
<a class="hidden hover:text-blue-600 xl:block"
|
<a class="hidden 2xl:block"
|
||||||
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
||||||
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
||||||
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||||
@@ -18,28 +18,28 @@
|
|||||||
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
||||||
{{ $episode->getResolution() }}</p>
|
{{ $episode->getResolution() }}</p>
|
||||||
<div class="absolute w-[95%] grid grid-cols-1 text-center">
|
<div class="absolute w-[95%] grid grid-cols-1 text-center">
|
||||||
<p class="text-sm text-center text-black dark:text-white">{{ $episode->title }} -
|
<p class="text-sm text-center text-black dark:text-white truncate">{{ $episode->title }} -
|
||||||
{{ $episode->episode }}</p>
|
{{ $episode->episode }}</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="block hover:text-blue-600 xl:hidden"
|
<a class="block 2xl:hidden"
|
||||||
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
||||||
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
||||||
class="block object-cover object-center relative z-20 rounded-lg"
|
class="block object-cover object-center relative z-20 rounded-lg"
|
||||||
src="{{ $episode->cover_url }}"></img>
|
src="{{ $episode->cover_url }}"></img>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-[60vw]">
|
<div class="w-[60vw] pt-4 bg-neutral-100 dark:bg-neutral-800 rounded-lg pl-4">
|
||||||
{{--@include('partials.comment', ['comment' => $comment])--}}
|
@include('partials.comment', ['comment' => $comment])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@elseif($comment->commentable_type == 'App\Models\Hentai')
|
@elseif($comment->commentable_type == \App\Models\Hentai::class)
|
||||||
@php $hentai = cache()->rememberForever('commentHentai'.$comment->commentable_id, fn () => App\Models\Hentai::with('gallery', 'episodes')->where('id', $comment->commentable_id)->first()); @endphp
|
@php $hentai = cache()->rememberForever('commentHentai'.$comment->commentable_id, fn () => App\Models\Hentai::with('gallery', 'episodes')->where('id', $comment->commentable_id)->first()); @endphp
|
||||||
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
|
<div id="comments" class="flex p-4 bg-white rounded-lg dark:bg-neutral-950">
|
||||||
<div
|
<div
|
||||||
class="w-[20vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
|
class="w-[15vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
|
||||||
<a class="hover:text-blue-600" href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
|
<a class="hidden 2xl:block" href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
|
||||||
<img alt="{{ $hentai->episodes->first()->title }}" loading="lazy" width="1000"
|
<img alt="{{ $hentai->episodes->first()->title }}" loading="lazy" width="1000"
|
||||||
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||||
src="{{ $hentai->gallery->first()->thumbnail_url }}"></img>
|
src="{{ $hentai->gallery->first()->thumbnail_url }}"></img>
|
||||||
@@ -47,13 +47,20 @@
|
|||||||
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
||||||
{{ $hentai->episodes->first()->getResolution() }}</p>
|
{{ $hentai->episodes->first()->getResolution() }}</p>
|
||||||
<div class="absolute w-[95%] grid grid-cols-1 text-center">
|
<div class="absolute w-[95%] grid grid-cols-1 text-center">
|
||||||
<p class="text-sm text-center text-black dark:text-white">
|
<p class="text-sm text-center text-black dark:text-white truncate">
|
||||||
{{ $hentai->episodes->first()->title }}</p>
|
{{ $hentai->episodes->first()->title }}</p>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a class="block 2xl:hidden"
|
||||||
|
href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
|
||||||
|
<img alt="{{ $hentai->episodes->first()->title }}" loading="lazy" width="1000"
|
||||||
|
class="block object-cover object-center relative z-20 rounded-lg"
|
||||||
|
src="{{ $hentai->episodes->first()->cover_url }}"></img>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-[60vw]">
|
<div class="w-[60vw] pt-4 bg-neutral-100 dark:bg-neutral-800 rounded-lg pl-4">
|
||||||
{{--@include('partials.comment', ['comment' => $comment])--}}
|
@include('partials.comment', ['comment' => $comment])
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -163,7 +163,7 @@
|
|||||||
<i class="fa-solid fa-gear"></i> {{ __('nav.settings') }}
|
<i class="fa-solid fa-gear"></i> {{ __('nav.settings') }}
|
||||||
</x-dropdown-link>
|
</x-dropdown-link>
|
||||||
|
|
||||||
@if (Auth::user()->is_admin)
|
@if (Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<x-dropdown-link href="{{ route('admin.upload.index') }}">
|
<x-dropdown-link href="{{ route('admin.upload.index') }}">
|
||||||
<i class="fa-solid fa-user-tie"></i> Admin
|
<i class="fa-solid fa-user-tie"></i> Admin
|
||||||
</x-dropdown-link>
|
</x-dropdown-link>
|
||||||
@@ -296,8 +296,8 @@
|
|||||||
<div class="pb-1 text-center w-full">
|
<div class="pb-1 text-center w-full">
|
||||||
<x-responsive-nav-link :href="route('login')">
|
<x-responsive-nav-link :href="route('login')">
|
||||||
<div
|
<div
|
||||||
class="relative bg-blue-700 hover:bg-blue-600 text-white font-bold px-4 h-10 rounded text-center p-[10px]">
|
class="relative bg-rose-700 hover:bg-rose-600 text-white font-bold px-4 h-10 rounded text-center p-[10px]">
|
||||||
<i class="fa-brands fa-discord"></i> {{ __('nav.login') }}
|
<i class="fa-solid fa-arrow-right-to-bracket"></i> {{ __('nav.login') }}
|
||||||
</div>
|
</div>
|
||||||
</x-responsive-nav-link>
|
</x-responsive-nav-link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
>
|
>
|
||||||
</th>
|
</th>
|
||||||
|
<th scope="col" class="px-6 py-3">
|
||||||
|
</th>
|
||||||
<th scope="col" class="px-6 py-3">
|
<th scope="col" class="px-6 py-3">
|
||||||
Actions
|
Actions
|
||||||
</th>
|
</th>
|
||||||
@@ -34,17 +36,18 @@
|
|||||||
@foreach($comments as $comment)
|
@foreach($comments as $comment)
|
||||||
<tr wire:key="comment-{{ $comment->id }}" class="bg-white border-t dark:bg-neutral-800 dark:border-pink-700">
|
<tr wire:key="comment-{{ $comment->id }}" class="bg-white border-t dark:bg-neutral-800 dark:border-pink-700">
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{{ $comment->name }}
|
{{ $comment->user->name }}
|
||||||
</td>
|
</td>
|
||||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 dark:text-white max-w-lg">
|
<th scope="row" class="px-6 py-4 font-medium text-gray-900 dark:text-white max-w-lg">
|
||||||
{{ $comment->comment }}
|
{{ $comment->body }}
|
||||||
|
</th>
|
||||||
|
<th scope="row" class="px-6 py-4 font-medium text-gray-900 dark:text-white max-w-lg">
|
||||||
|
{{ $comment->created_at }}
|
||||||
</th>
|
</th>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
<a href="{{ route('comments.destroy', $comment->id) }}" onclick="event.preventDefault();document.getElementById('comment-delete-form-{{ $comment->id }}').submit();" class="inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 mt-2">@lang('comments::comments.delete')</a>
|
<button wire:click="deleteComment({{$comment->id}})" type="button" class="inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150 mt-2">
|
||||||
<form id="comment-delete-form-{{ $comment->id }}" action="{{ route('comments.destroy', $comment->id) }}" method="POST" style="display: none;">
|
Delete
|
||||||
@method('DELETE')
|
</button>
|
||||||
@csrf
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
@@ -6,12 +6,16 @@
|
|||||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-pink-700 dark:text-neutral-200 ">
|
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-pink-700 dark:text-neutral-200 ">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="px-6 py-3">
|
<th scope="col" class="px-6 py-3">
|
||||||
Discord-ID
|
ID
|
||||||
|
</th>
|
||||||
|
<th scope="col" class="px-6 py-3">
|
||||||
|
Discord ID
|
||||||
<input
|
<input
|
||||||
class="w-4 h-4 ml-2 text-rose-600 bg-gray-100 border-gray-300 rounded focus:ring-rose-500 dark:focus:ring-rose-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
|
wire:model.live.debounce.600ms="discordId"
|
||||||
type="checkbox"
|
type="search"
|
||||||
wire:model.live="filtered"
|
id="discord-search"
|
||||||
value="true"
|
class="ml-2 w-32 h-7 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
|
||||||
|
placeholder="Search..."
|
||||||
>
|
>
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" class="px-6 py-3">
|
<th scope="col" class="px-6 py-3">
|
||||||
@@ -59,14 +63,17 @@
|
|||||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||||
{{ $user->id }}
|
{{ $user->id }}
|
||||||
</th>
|
</th>
|
||||||
|
<td class="px-6 py-4">
|
||||||
|
{{ $user->discord_id ?? 'n/a' }}
|
||||||
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{{ $user->name }}
|
{{ $user->name }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{{ $user->is_patreon ? 'Yes' : 'No' }}
|
{{ $user->hasRole(\App\Enums\UserRole::SUPPORTER) ? 'Yes' : 'No' }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{{ $user->is_banned ? 'Yes' : 'No' }}
|
{{ $user->hasRole(\App\Enums\UserRole::BANNED) ? 'Yes' : 'No' }}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4">
|
<td class="px-6 py-4">
|
||||||
{{ $user->created_at->format('Y-m-d') }}
|
{{ $user->created_at->format('Y-m-d') }}
|
||||||
@@ -78,9 +85,9 @@
|
|||||||
<form method="POST" action="{{ route('admin.user.update') }}">
|
<form method="POST" action="{{ route('admin.user.update') }}">
|
||||||
@csrf
|
@csrf
|
||||||
<input type="hidden" value="{{ $user->id }}" name="id">
|
<input type="hidden" value="{{ $user->id }}" name="id">
|
||||||
<input type="hidden" value="{{ $user->is_banned ? 'unban' : 'ban' }}" name="action">
|
<input type="hidden" value="{{ $user->hasRole(\App\Enums\UserRole::BANNED) ? 'unban' : 'ban' }}" name="action">
|
||||||
<button type="submit" class="inline-block w-full rounded bg-rose-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
<button type="submit" class="inline-block w-full rounded bg-rose-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
||||||
{{ $user->is_banned ? 'Unban' : 'Ban' }}
|
{{ $user->hasRole(\App\Enums\UserRole::BANNED) ? 'Unban' : 'Ban' }}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<button wire:click="deleteUserComments('{{ $user->id }}')" class="inline-block w-full rounded bg-red-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
<button wire:click="deleteUserComments('{{ $user->id }}')" class="inline-block w-full rounded bg-red-600 pl-[4px] pr-[4px] p-[1px] text-xs font-medium uppercase leading-normal text-white transition duration-150 ease-in-out hover:bg-rose-700 focus:bg-rose-600">
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex">
|
<div class="flex" id="comment-{{ $comment->id }}">
|
||||||
<div class="flex-shrink-0 mr-4">
|
<div class="flex-shrink-0 mr-4">
|
||||||
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
||||||
@if($comment->user->is_admin)
|
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
||||||
@endif
|
@endif
|
||||||
@if($comment->user->is_patreon)
|
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mt-3 flex items-center justify-between">
|
<div class="mt-3 flex items-center justify-between">
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
class="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-rose-500">
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,10 +37,28 @@
|
|||||||
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 space-x-2">
|
<div class="mt-2 space-x-2 flex flex-row">
|
||||||
<span class="text-gray-500 dark:text-gray-300 font-medium">
|
<span class="text-gray-500 dark:text-gray-300">
|
||||||
{{ $comment->presenter()->relativeCreatedAt() }}
|
{{ $comment->presenter()->relativeCreatedAt() }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@guest
|
||||||
|
<span data-te-toggle="tooltip" title="Please login to like the episode" class="text-gray-800 cursor-pointer dark:text-gray-200">
|
||||||
|
<i class="fa-regular fa-heart"></i> {{ $comment->likeCount() }}
|
||||||
|
</span>
|
||||||
|
@endguest
|
||||||
|
|
||||||
|
@auth
|
||||||
|
<!-- Like Button -->
|
||||||
|
<button class="text-gray-800 dark:text-gray-200 leading-tight cursor-pointer whitespace-nowrap" wire:click="like">
|
||||||
|
@if ($liked)
|
||||||
|
<i class="fa-solid fa-heart text-rose-600"></i> {{ $likeCount }}
|
||||||
|
@else
|
||||||
|
<i class="fa-solid fa-heart"></i> {{ $likeCount }}
|
||||||
|
@endif
|
||||||
|
</button>
|
||||||
|
@endauth
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@if ($comment->depth() < 2)
|
@if ($comment->depth() < 2)
|
||||||
<button wire:click="$toggle('isReplying')" type="button" class="text-gray-900 dark:text-gray-100 font-medium">
|
<button wire:click="$toggle('isReplying')" type="button" class="text-gray-900 dark:text-gray-100 font-medium">
|
||||||
|
|||||||
@@ -4,19 +4,8 @@
|
|||||||
<div class="px-4 py-5 sm:px-6">
|
<div class="px-4 py-5 sm:px-6">
|
||||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-200">Comments</h2>
|
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-200">Comments</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-6 sm:px-6">
|
<div>
|
||||||
<div class="space-y-8">
|
<!-- Comment Input -->
|
||||||
@if ($comments->isNotEmpty())
|
|
||||||
@foreach($comments as $comment)
|
|
||||||
<livewire:comment :comment="$comment" :key="$comment->id"/>
|
|
||||||
@endforeach
|
|
||||||
{{ $comments->links() }}
|
|
||||||
@else
|
|
||||||
<p class="text-gray-900 dark:text-gray-200">No comments yet.</p>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="bg-gray-50 dark:bg-neutral-800 px-4 py-6 sm:px-6">
|
<div class="bg-gray-50 dark:bg-neutral-800 px-4 py-6 sm:px-6">
|
||||||
@auth
|
@auth
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
@@ -28,7 +17,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<label for="comment" class="sr-only">Comment body</label>
|
<label for="comment" class="sr-only">Comment body</label>
|
||||||
<textarea id="comment" name="comment" rows="3"
|
<textarea id="comment" name="comment" rows="3"
|
||||||
class="bg-white dark:bg-neutral-700 shadow-sm block w-full focus:ring-rose-500 focus:border-rose-500 border-gray-300 dark:border-gray-400/40 text-gray-900 dark:text-gray-200 placeholder:text-gray-400 rounded-md
|
class="peer block min-h-[auto] w-full border-1 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear dark:placeholder:text-neutral-200 border-gray-300 dark:border-neutral-950 dark:bg-neutral-900 dark:text-gray-300 focus:border-rose-500 dark:focus:border-rose-600 focus:ring-rose-500 dark:focus:ring-rose-600 rounded-md shadow-sm
|
||||||
@error('newCommentState.body') border-red-500 @enderror"
|
@error('newCommentState.body') border-red-500 @enderror"
|
||||||
placeholder="Write something" wire:model.defer="newCommentState.body"></textarea>
|
placeholder="Write something" wire:model.defer="newCommentState.body"></textarea>
|
||||||
@error('newCommentState.body')
|
@error('newCommentState.body')
|
||||||
@@ -50,5 +39,22 @@
|
|||||||
<p class="text-gray-900 dark:text-gray-200">Log in to comment.</p>
|
<p class="text-gray-900 dark:text-gray-200">Log in to comment.</p>
|
||||||
@endguest
|
@endguest
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Comments -->
|
||||||
|
<div class="px-4 py-6 sm:px-6">
|
||||||
|
<div class="space-y-8">
|
||||||
|
@if ($comments->isNotEmpty())
|
||||||
|
@foreach($comments as $comment)
|
||||||
|
<livewire:comment :comment="$comment" :key="$comment->id"/>
|
||||||
|
@endforeach
|
||||||
|
{{ $comments->links('pagination::tailwind') }}
|
||||||
|
@else
|
||||||
|
<p class="text-gray-900 dark:text-gray-200">No comments yet.</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
130
resources/views/livewire/user-comments.blade.php
Normal file
130
resources/views/livewire/user-comments.blade.php
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<div class="py-3 relative">
|
||||||
|
<div class="mx-auto sm:px-6 lg:px-8 space-y-6 max-w-[100%] lg:max-w-[90%] xl:max-w-[80%] 2xl:max-w-[90%] relative z-10">
|
||||||
|
<!-- Search Filter -->
|
||||||
|
<div class="p-4 sm:p-8 bg-white/30 dark:bg-neutral-950/40 shadow sm:rounded-lg backdrop-blur relative z-100">
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 ">
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<div>
|
||||||
|
<label for="live-search"
|
||||||
|
class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
|
||||||
|
<div class="relative right-2 left-0 sm:left-2 transition-all">
|
||||||
|
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
|
||||||
|
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input wire:model.live.debounce.600ms="commentSearch" type="search" id="live-search"
|
||||||
|
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900"
|
||||||
|
placeholder="Search comment..." required>
|
||||||
|
|
||||||
|
<div class="absolute right-0 top-[11px]" wire:loading>
|
||||||
|
<svg aria-hidden="true"
|
||||||
|
class="inline w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-pink-600"
|
||||||
|
viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||||
|
fill="currentColor" />
|
||||||
|
<path
|
||||||
|
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||||
|
fill="currentFill" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ordering -->
|
||||||
|
<div>
|
||||||
|
<div class="relative right-2 left-0 sm:left-2 transition-all">
|
||||||
|
<div class="absolute inset-y-0 left-2 flex items-center pl-3 pointer-events-none">
|
||||||
|
<i class="fa-solid fa-sort text-gray-500 dark:text-gray-400"></i>
|
||||||
|
</div>
|
||||||
|
<select wire:model.live="order"
|
||||||
|
class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-rose-800 focus:border-rose-900 dark:bg-neutral-900 dark:border-neutral-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-rose-800 dark:focus:border-rose-900">
|
||||||
|
<option value="created_at_desc">Created DESC</option>
|
||||||
|
<option value="created_at_asc">Created ASC</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="relative pt-5 mx-auto sm:px-6 lg:px-8 space-y-6 text-gray-900 dark:text-white max-w-[100%] lg:max-w-[90%] xl:max-w-[80%] 2xl:max-w-[90%] 2xl:w-[50vw]">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||||
|
<div class="inline-block min-w-full py-2 sm:px-6 lg:px-8">
|
||||||
|
<div class="overflow-hidden">
|
||||||
|
|
||||||
|
<!-- Desktop -->
|
||||||
|
<div class="w-full text-left text-sm font-light">
|
||||||
|
<!-- Header -->
|
||||||
|
<div
|
||||||
|
class="flex bg-white/30 dark:bg-neutral-950/40 backdrop-blur font-medium dark:border-neutral-500 border-b rounded-tl-lg rounded-tr-lg">
|
||||||
|
|
||||||
|
<div class="flex-1 px-6 py-4 text-center">Comment</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Rows -->
|
||||||
|
@foreach ($comments as $comment)
|
||||||
|
<div wire:key="comment-{{ $comment->id }}"
|
||||||
|
class="flex flex-col sm:flex-row items-center border-b bg-white dark:bg-neutral-950 dark:border-zinc-700 hover:bg-zinc-100 dark:hover:bg-neutral-800">
|
||||||
|
|
||||||
|
<!-- Image -->
|
||||||
|
<div class="flex w-fit sm:w-56">
|
||||||
|
@if($comment->commentable_type == \App\Models\Episode::class)
|
||||||
|
@php $episode = \App\Models\Episode::find($comment->commentable_id); @endphp
|
||||||
|
<div class="relative p-1 w-full transition duration-300 ease-in-out md:p-2 md:hover:-translate-y-1 md:hover:scale-110">
|
||||||
|
<a href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
||||||
|
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy" width="1000"
|
||||||
|
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||||
|
src="{{ $episode->gallery->first()->thumbnail_url }}" />
|
||||||
|
|
||||||
|
<p class="absolute left-1 md:left-2 bottom-1 md:bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
||||||
|
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }}
|
||||||
|
<i class="fa-regular fa-heart"></i> {{ $episode->likeCount() }}
|
||||||
|
<i class="fa-regular fa-comment"></i> {{ $episode->commentCount() }}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@elseif($comment->commentable_type == \App\Models\Hentai::class)
|
||||||
|
@php
|
||||||
|
$hentai = \App\Models\Hentai::find($comment->commentable_id);
|
||||||
|
$episode = $hentai->episodes->first();
|
||||||
|
@endphp
|
||||||
|
<div class="relative p-1 w-full transition duration-300 ease-in-out md:p-2 md:hover:-translate-y-1 md:hover:scale-110">
|
||||||
|
<a href="{{ route('hentai.index', ['title' => $hentai->slug]) }}">
|
||||||
|
<img alt="{{ $episode->title }}" loading="lazy" width="1000"
|
||||||
|
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
||||||
|
src="{{ $episode->gallery->first()->thumbnail_url }}" />
|
||||||
|
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body -->
|
||||||
|
<div class="flex text-lg flex-1 items-center space-x-2 px-3 py-2 bg-neutral-200 dark:bg-neutral-900 h-[115px] rounded-lg sm:mr-2">
|
||||||
|
{!! $comment->presenter()->markdownBody() !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-x-2 sm:mr-2 w-24">
|
||||||
|
<span class="text-gray-500 dark:text-gray-300 font-medium">
|
||||||
|
{{ $comment->presenter()->relativeCreatedAt() }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ $comments->links('pagination::tailwind') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<br>
|
<br>
|
||||||
@php $download = $episode->getDownloadByType('UHD'); @endphp
|
@php $download = $episode->getDownloadByType('UHD'); @endphp
|
||||||
@isset($download)
|
@isset($download)
|
||||||
@if (!Auth::user()->is_patreon)
|
@if (!Auth::user()->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
@if (config('hstream.free_downloads'))
|
@if (config('hstream.free_downloads'))
|
||||||
<p class="font-bold text-gray-800 dark:text-gray-200">
|
<p class="font-bold text-gray-800 dark:text-gray-200">
|
||||||
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k
|
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
@if ($episode->interpolated_uhd)
|
@if ($episode->interpolated_uhd)
|
||||||
@if (!Auth::user()->is_patreon)
|
@if (!Auth::user()->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
@if (config('hstream.free_downloads'))
|
@if (config('hstream.free_downloads'))
|
||||||
<p class="font-bold text-gray-800 dark:text-gray-200">
|
<p class="font-bold text-gray-800 dark:text-gray-200">
|
||||||
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k 48fps
|
<i class="fa-solid fa-lock-open pr-[4px] text-yellow-600"></i> 4k 48fps
|
||||||
|
|||||||
@@ -8,18 +8,7 @@
|
|||||||
<p id="message" class="text-red-600">
|
<p id="message" class="text-red-600">
|
||||||
</p>
|
</p>
|
||||||
<div class="flex pt-2">
|
<div class="flex pt-2">
|
||||||
<div id="captchaImg">
|
<altcha-widget id="altcha" challengeurl="/altcha-challenge"></altcha-widget>
|
||||||
{!! captcha_img() !!}
|
|
||||||
</div>
|
|
||||||
<button type="button" class="inline-flex items-center ml-2 px-2 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-500 rounded-md font-semibold text-xs text-gray-700 dark:text-gray-300 uppercase tracking-widest shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 disabled:opacity-25 transition ease-in-out duration-150" id="reloadcaptcha" >
|
|
||||||
<i class="fa-solid fa-rotate-right"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="flex pt-2 mt-1">
|
|
||||||
<x-text-input id="captcha_text" class="block " type="text" name="captcha_text"/>
|
|
||||||
<button type="button" class="inline-flex items-center ml-2 px-2 -pt-1 bg-rose-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-rose-700 active:bg-rose-900 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150" id="submitcaptcha" >
|
|
||||||
Submit
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<p class="text-gray-800 dark:text-gray-200 text-sm">
|
<p class="text-gray-800 dark:text-gray-200 text-sm">
|
||||||
@@ -51,21 +40,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
var downloadCounter = 0;
|
var downloadCounter = 0;
|
||||||
function reloadCaptcha() {
|
|
||||||
window.axios.get('/reload-captcha').then(function (response) {
|
|
||||||
if (response.status == 200) {
|
|
||||||
document.querySelector("#captchaImg").innerHTML = response.data.captcha;
|
|
||||||
}
|
|
||||||
}).catch(function (error) {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitCaptcha() {
|
function submitCaptcha(captchaToken) {
|
||||||
document.querySelector("#message").innerHTML = '';
|
document.querySelector("#message").innerHTML = '';
|
||||||
window.axios.post('/get-download', {
|
window.axios.post('/get-download', {
|
||||||
captcha: document.getElementById('captcha_text').value,
|
episode_id: document.getElementById('e_id').value,
|
||||||
episode_id: document.getElementById('e_id').value
|
captcha: captchaToken,
|
||||||
}).then(function (response) {
|
}).then(function (response) {
|
||||||
document.querySelector("#captcharequired").style.display = "none";
|
document.querySelector("#captcharequired").style.display = "none";
|
||||||
document.querySelector("#captchsolved").style.display = "block";
|
document.querySelector("#captchsolved").style.display = "block";
|
||||||
@@ -89,6 +69,16 @@
|
|||||||
|
|
||||||
document.querySelector("#downloadEpisode").addEventListener("click", increaseDownloadCounter);
|
document.querySelector("#downloadEpisode").addEventListener("click", increaseDownloadCounter);
|
||||||
|
|
||||||
document.querySelector("#reloadcaptcha").addEventListener("click", reloadCaptcha);
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.querySelector("#submitcaptcha").addEventListener("click", submitCaptcha);
|
const altcha = document.querySelector("#altcha");
|
||||||
|
|
||||||
|
altcha.addEventListener("statechange", (ev) => {
|
||||||
|
if (ev.detail.state === "verified") {
|
||||||
|
submitCaptcha(ev.detail.payload);
|
||||||
|
|
||||||
|
// Remove captcha from DOM
|
||||||
|
altcha.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
26
resources/views/partials/comment.blade.php
Normal file
26
resources/views/partials/comment.blade.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<div>
|
||||||
|
<div class="flex">
|
||||||
|
<div class="flex-shrink-0 mr-4">
|
||||||
|
<img class="h-10 w-10 rounded-full" src="{{ $comment->user->getAvatar() }}" alt="{{ $comment->user->name }}">
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow">
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<p class="font-medium text-gray-900 dark:text-gray-100">{{ $comment->user->name }}</p>
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
|
<a data-te-toggle="tooltip" title="Admin"><i class="fa-solid fa-crown text-yellow-600"></i></a>
|
||||||
|
@endif
|
||||||
|
@if($comment->user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
|
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i class="fa-solid fa-hand-holding-heart text-rose-600"></i></a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 flex-grow w-full">
|
||||||
|
<div class="text-gray-700 dark:text-gray-200">{!! $comment->presenter()->markdownBody() !!}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 space-x-2">
|
||||||
|
<span class="text-gray-500 dark:text-gray-300 font-medium">
|
||||||
|
{{ $comment->presenter()->relativeCreatedAt() }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -12,63 +12,10 @@
|
|||||||
class="relative max-w-[120rem] mx-auto sm:px-6 lg:px-8 space-y-6 pt-20 mt-[65px] flex flex-row justify-center xl:justify-normal">
|
class="relative max-w-[120rem] mx-auto sm:px-6 lg:px-8 space-y-6 pt-20 mt-[65px] flex flex-row justify-center xl:justify-normal">
|
||||||
<div class="flex flex-col xl:flex-row">
|
<div class="flex flex-col xl:flex-row">
|
||||||
@include('profile.partials.sidebar')
|
@include('profile.partials.sidebar')
|
||||||
<div class="pb-2 space-y-6 max-w-7xl px-0 md:px-6 lg:px-8 pt-8 xl:pt-0">
|
<div class="pb-2 space-y-6 max-w-7xl px-0 md:px-6 lg:px-8 pt-8 xl:pt-0 flex flex-row">
|
||||||
|
|
||||||
@php
|
<livewire:user-comments :model="$user"/>
|
||||||
$episode_ids = array_unique(
|
|
||||||
DB::table('comments')
|
|
||||||
->where('commenter_id', $user->id)
|
|
||||||
->get()
|
|
||||||
->pluck('commentable_id')
|
|
||||||
->toArray(),
|
|
||||||
);
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
@foreach ($episode_ids as $episode_id)
|
|
||||||
@php $episode = App\Models\Episode::where('id', $episode_id)->first(); @endphp
|
|
||||||
<div
|
|
||||||
class="flex flex-col 2xl:flex-row p-5 bg-white/40 dark:bg-neutral-950/40 backdrop-blur rounded-lg items-center">
|
|
||||||
<div
|
|
||||||
class="w-[20vw] mr-5 p-1 md:p-2 mb-8 relative transition ease-in-out hover:-translate-y-1 hover:scale-110 duration-300">
|
|
||||||
<a class="hidden hover:text-blue-600 md:block"
|
|
||||||
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
|
||||||
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy"
|
|
||||||
width="1000"
|
|
||||||
class="block object-cover object-center relative z-20 rounded-lg aspect-video"
|
|
||||||
src="{{ $episode->gallery->first()->thumbnail_url }}"></img>
|
|
||||||
<p
|
|
||||||
class="absolute right-2 top-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
|
||||||
{{ $episode->getResolution() }}</p>
|
|
||||||
<p
|
|
||||||
class="absolute left-2 bottom-2 bg-rose-700/70 !text-white rounded-bl-lg rounded-tr-lg p-1 pr-2 pl-2 font-semibold text-sm z-30">
|
|
||||||
<i class="fa-regular fa-eye"></i> {{ $episode->viewCountFormatted() }} <i
|
|
||||||
class="fa-regular fa-heart"></i> {{ $episode->likeCount() }} <i
|
|
||||||
class="fa-regular fa-comment"></i>
|
|
||||||
{{ $episode->commentCount() }}
|
|
||||||
</p>
|
|
||||||
<div class="absolute w-[95%] grid grid-cols-1 text-center">
|
|
||||||
<p class="text-sm text-center text-black dark:text-white">
|
|
||||||
{{ $episode->title }} - {{ $episode->episode }}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="block hover:text-blue-600 md:hidden"
|
|
||||||
href="{{ route('hentai.index', ['title' => $episode->slug]) }}">
|
|
||||||
<img alt="{{ $episode->title }} - {{ $episode->episode }}" loading="lazy"
|
|
||||||
width="1000"
|
|
||||||
class="block object-cover object-center relative z-20 rounded-lg"
|
|
||||||
src="{{ $episode->cover_url }}"></img>
|
|
||||||
<div class="relative w-[95%] grid grid-cols-1 text-center">
|
|
||||||
<p class="text-sm text-center text-black dark:text-white">
|
|
||||||
{{ $episode->title }} - {{ $episode->episode }}</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="md:w-[60vw]">
|
|
||||||
{{--@comments(['model' => $episode])--}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
@include('series.partials.episodes')
|
@include('series.partials.episodes')
|
||||||
|
|
||||||
@include('series.partials.comments')
|
<livewire:comments :model="$hentai"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('series.partials.popular')
|
@include('series.partials.popular')
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<div class="bg-transparent rounded-lg overflow-hidden bg-white dark:bg-neutral-800 p-5">
|
|
||||||
<div id="comments" class="grid grid-cols-1 bg-transparent rounded-lg">
|
|
||||||
<p class="leading-normal font-bold text-lg text-rose-600">
|
|
||||||
{{ __('home.latest-comments') }}
|
|
||||||
</p>
|
|
||||||
{{--@comments(['model' => $hentai])--}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
@include('modals.share')
|
@include('modals.share')
|
||||||
|
|
||||||
@auth
|
@auth
|
||||||
@if(Auth::user()->is_admin)
|
@if(Auth::user()->hasRole(\App\Enums\UserRole::ADMINISTRATOR))
|
||||||
@include('admin.modals.upload-episode')
|
@include('admin.modals.upload-episode')
|
||||||
@include('admin.modals.add-subtitles')
|
@include('admin.modals.add-subtitles')
|
||||||
@include('admin.modals.edit-episode')
|
@include('admin.modals.edit-episode')
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div class="flex flex-col py-5 pl-24">
|
<div class="flex flex-col py-5 pl-24">
|
||||||
<strong class="text-slate-900 text-xl font-bold dark:text-slate-200">
|
<strong class="text-slate-900 text-xl font-bold dark:text-slate-200">
|
||||||
{{ $user->name }}
|
{{ $user->name }}
|
||||||
@if ($user->is_patreon)
|
@if ($user->hasRole(\App\Enums\UserRole::SUPPORTER))
|
||||||
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i
|
<a data-te-toggle="tooltip" title="Badge of appreciation for the horny people supporting us! :3"><i
|
||||||
class="fa-solid fa-hand-holding-heart text-rose-600 animate-pulse"></i></a>
|
class="fa-solid fa-hand-holding-heart text-rose-600 animate-pulse"></i></a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
62
routes/admin.php
Normal file
62
routes/admin.php
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\Admin\AlertController;
|
||||||
|
use App\Http\Controllers\Admin\ContactController;
|
||||||
|
use App\Http\Controllers\Admin\CommentsController;
|
||||||
|
use App\Http\Controllers\Admin\EpisodeController;
|
||||||
|
use App\Http\Controllers\Admin\ReleaseController;
|
||||||
|
use App\Http\Controllers\Admin\UserController;
|
||||||
|
use App\Http\Controllers\Admin\SubtitleController;
|
||||||
|
use App\Http\Controllers\Admin\SiteBackgroundController;
|
||||||
|
use App\Http\Controllers\Api\AdminApiController;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
| Admin Routes
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Route::group(['middleware' => ['auth', 'auth.admin']], function () {
|
||||||
|
// Site alerts
|
||||||
|
Route::get('/admin/alert', [AlertController::class, 'index'])->name('admin.alert.index');
|
||||||
|
Route::post('/admin/alert', [AlertController::class, 'store'])->name('admin.alert.create');
|
||||||
|
Route::delete('/admin/alert/{alert_id}', [AlertController::class, 'delete'])->name('admin.alert.delete');
|
||||||
|
|
||||||
|
// Users
|
||||||
|
Route::get('/admin/users', [UserController::class, 'index'])->name('admin.user.index');
|
||||||
|
Route::post('/admin/users', [UserController::class, 'update'])->name('admin.user.update');
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
Route::get('/admin/comments', [CommentsController::class, 'index'])->name('admin.comments.index');
|
||||||
|
|
||||||
|
// Contact page overview
|
||||||
|
Route::get('/admin/contact', [ContactController::class, 'index'])->name('admin.contact.index');
|
||||||
|
Route::delete('/admin/contact/{contact_id}', [ContactController::class, 'delete'])->name('admin.contact.delete');
|
||||||
|
|
||||||
|
// Site background settings
|
||||||
|
Route::get('/admin/background', [SiteBackgroundController::class, 'index'])->name('admin.background.index');
|
||||||
|
Route::post('/admin/background', [SiteBackgroundController::class, 'create'])->name('admin.background.create');
|
||||||
|
Route::put('/admin/background', [SiteBackgroundController::class, 'update'])->name('admin.background.update');
|
||||||
|
Route::delete('/admin/background', [SiteBackgroundController::class, 'delete'])->name('admin.background.delete');
|
||||||
|
|
||||||
|
// Release
|
||||||
|
Route::get('/admin/release', [ReleaseController::class, 'index'])->name('admin.upload.index');
|
||||||
|
Route::post('/admin/release/upload', [ReleaseController::class, 'store'])->name('admin.upload');
|
||||||
|
|
||||||
|
// Episode
|
||||||
|
Route::post('/admin/episode/upload', [EpisodeController::class, 'store'])->name('admin.upload.episode');
|
||||||
|
Route::post('/admin/episode/edit', [EpisodeController::class, 'update'])->name('admin.edit');
|
||||||
|
|
||||||
|
// Get Tags used for Upload Form
|
||||||
|
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
||||||
|
Route::get('/admin/studios', [AdminApiController::class, 'getStudios'])->name('admin.studios');
|
||||||
|
|
||||||
|
// Get Tags for editing Episode
|
||||||
|
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
||||||
|
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
||||||
|
|
||||||
|
// Subtitles
|
||||||
|
Route::get('/admin/subtitles/{episode_id}', [AdminApiController::class, 'getSubtitles'])->name('admin.subtitles');
|
||||||
|
Route::post('/admin/add-new-subtitle', [SubtitleController::class, 'store'])->name('admin.add.new.subtitle');
|
||||||
|
Route::post('/admin/update-subtitles', [SubtitleController::class, 'update'])->name('admin.update.subtitles');
|
||||||
|
});
|
||||||
45
routes/user.php
Normal file
45
routes/user.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\HomeController;
|
||||||
|
use App\Http\Controllers\ProfileController;
|
||||||
|
use App\Http\Controllers\PlaylistController;
|
||||||
|
use App\Http\Controllers\Api\UserApiController;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
| User Routes
|
||||||
|
|---------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Route::middleware('auth')->group(function () {
|
||||||
|
Route::get('/user/profile', [ProfileController::class, 'index'])->name('profile.show');
|
||||||
|
Route::get('/user/comments', [ProfileController::class, 'comments'])->name('profile.comments');
|
||||||
|
Route::get('/user/likes', [ProfileController::class, 'likes'])->name('profile.likes');
|
||||||
|
Route::get('/user/watched', [ProfileController::class, 'watched'])->name('user.watched');
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
Route::get('/user/notifications', [App\Http\Controllers\NotificationController::class, 'index'])->name('profile.notifications');
|
||||||
|
Route::delete('/user/notifications', [App\Http\Controllers\NotificationController::class, 'delete'])->name('profile.notifications.delete');
|
||||||
|
|
||||||
|
// User Profile Actions
|
||||||
|
Route::get('/user/settings', [ProfileController::class, 'settings'])->name('profile.settings');
|
||||||
|
Route::patch('/user/settings', [ProfileController::class, 'update'])->name('profile.update');
|
||||||
|
Route::delete('/user/delete', [ProfileController::class, 'destroy'])->name('profile.delete');
|
||||||
|
Route::post('/user/settings', [ProfileController::class, 'saveSettings'])->name('profile.settings.save');
|
||||||
|
Route::get('/user/blacklist', [UserApiController::class, 'getBlacklist'])->name('profile.blacklist');
|
||||||
|
Route::post('/user/blacklist', [ProfileController::class, 'saveBlacklist'])->name('profile.blacklist.save');
|
||||||
|
|
||||||
|
// Playlist Routes for User Page
|
||||||
|
Route::get('/user/playlists', [PlaylistController::class, 'playlists'])->name('profile.playlists');
|
||||||
|
Route::get('/user/playlist/{playlist_id}', [PlaylistController::class, 'showPlaylist'])->name('profile.playlist.show');
|
||||||
|
Route::post('/create-playlist', [PlaylistController::class, 'createPlaylist'])->name('profile.playlists.create');
|
||||||
|
Route::delete('/user/playlist/{playlist_id}', [PlaylistController::class, 'deletePlaylist'])->name('profile.playlist.delete');
|
||||||
|
Route::post('/user/playlist-episode', [PlaylistController::class, 'deleteEpisodeFromPlaylist'])->name('playlist.delete.episode');
|
||||||
|
|
||||||
|
// Playlist Routes for Modals on Stream Page
|
||||||
|
Route::post('/hentai/add-to-playlist', [PlaylistController::class, 'addPlaylistApi'])->name('hentai.playlists.add');
|
||||||
|
Route::post('/hentai/create-playlist', [PlaylistController::class, 'createPlaylistApi'])->name('hentai.playlists.create');
|
||||||
|
|
||||||
|
// Download Page
|
||||||
|
Route::get('/download-search', [HomeController::class, 'downloadSearch'])->name('download.search');
|
||||||
|
});
|
||||||
@@ -3,14 +3,12 @@
|
|||||||
use App\Http\Controllers\ContactController;
|
use App\Http\Controllers\ContactController;
|
||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\HomeController;
|
||||||
use App\Http\Controllers\PlaylistController;
|
use App\Http\Controllers\PlaylistController;
|
||||||
use App\Http\Controllers\ProfileController;
|
|
||||||
use App\Http\Controllers\StreamController;
|
use App\Http\Controllers\StreamController;
|
||||||
use App\Http\Controllers\UserController;
|
|
||||||
use App\Http\Controllers\Api\AdminApiController;
|
|
||||||
use App\Http\Controllers\Api\DownloadApiController;
|
use App\Http\Controllers\Api\DownloadApiController;
|
||||||
use App\Http\Controllers\Api\HentaiApiController;
|
use App\Http\Controllers\Api\HentaiApiController;
|
||||||
use App\Http\Controllers\Api\StreamApiController;
|
use App\Http\Controllers\Api\StreamApiController;
|
||||||
use App\Http\Controllers\Api\UserApiController;
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -40,100 +38,15 @@ Route::post('/search', [HomeController::class, 'searchRedirect'])->name('hentai.
|
|||||||
Route::get('/contact', [ContactController::class, 'index'])->name('contact.index');
|
Route::get('/contact', [ContactController::class, 'index'])->name('contact.index');
|
||||||
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');
|
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');
|
||||||
|
|
||||||
// Public Playlistts
|
// Public Playlists
|
||||||
Route::get('/playlists', [PlaylistController::class, 'index'])->name('playlist.index');
|
Route::get('/playlists', [PlaylistController::class, 'index'])->name('playlist.index');
|
||||||
Route::get('/playlist/{playlist_id}', [PlaylistController::class, 'show'])->name('playlist.show');
|
Route::get('/playlist/{playlist_id}', [PlaylistController::class, 'show'])->name('playlist.show');
|
||||||
|
|
||||||
// Captcha Reload
|
|
||||||
Route::get('/reload-captcha', [ContactController::class, 'reloadCaptcha']);
|
|
||||||
|
|
||||||
// Download
|
// Download
|
||||||
Route::post('/get-download', [DownloadApiController::class, 'getDownload']);
|
Route::post('/get-download', [DownloadApiController::class, 'getDownload']);
|
||||||
|
|
||||||
Route::post('/update-language', [HomeController::class, 'updateLanguage'])->name('update.language');
|
Route::post('/update-language', [HomeController::class, 'updateLanguage'])->name('update.language');
|
||||||
|
|
||||||
// User Routes
|
require __DIR__.'/user.php';
|
||||||
Route::middleware('auth')->group(function () {
|
require __DIR__.'/admin.php';
|
||||||
Route::get('/user/profile', [ProfileController::class, 'index'])->name('profile.show');
|
|
||||||
Route::get('/user/comments', [ProfileController::class, 'comments'])->name('profile.comments');
|
|
||||||
Route::get('/user/likes', [ProfileController::class, 'likes'])->name('profile.likes');
|
|
||||||
Route::get('/user/watched', [ProfileController::class, 'watched'])->name('user.watched');
|
|
||||||
|
|
||||||
// Notifications
|
|
||||||
Route::get('/user/notifications', [App\Http\Controllers\NotificationController::class, 'index'])->name('profile.notifications');
|
|
||||||
Route::delete('/user/notifications', [App\Http\Controllers\NotificationController::class, 'delete'])->name('profile.notifications.delete');
|
|
||||||
|
|
||||||
// User Profile Actions
|
|
||||||
Route::get('/user/settings', [ProfileController::class, 'settings'])->name('profile.settings');
|
|
||||||
Route::patch('/user/settings', [ProfileController::class, 'update'])->name('profile.update');
|
|
||||||
Route::delete('/user/delete', [ProfileController::class, 'destroy'])->name('profile.delete');
|
|
||||||
Route::post('/user/settings', [ProfileController::class, 'saveSettings'])->name('profile.settings.save');
|
|
||||||
Route::get('/user/blacklist', [UserApiController::class, 'getBlacklist'])->name('profile.blacklist');
|
|
||||||
Route::post('/user/blacklist', [ProfileController::class, 'saveBlacklist'])->name('profile.blacklist.save');
|
|
||||||
|
|
||||||
// Playlist Routes for User Page
|
|
||||||
Route::get('/user/playlists', [PlaylistController::class, 'playlists'])->name('profile.playlists');
|
|
||||||
Route::get('/user/playlist/{playlist_id}', [PlaylistController::class, 'showPlaylist'])->name('profile.playlist.show');
|
|
||||||
Route::post('/create-playlist', [PlaylistController::class, 'createPlaylist'])->name('profile.playlists.create');
|
|
||||||
Route::delete('/user/playlist/{playlist_id}', [PlaylistController::class, 'deletePlaylist'])->name('profile.playlist.delete');
|
|
||||||
Route::post('/user/playlist-episode', [PlaylistController::class, 'deleteEpisodeFromPlaylist'])->name('playlist.delete.episode');
|
|
||||||
|
|
||||||
// Playlist Routes for Modals on Stream Page
|
|
||||||
Route::post('/hentai/add-to-playlist', [PlaylistController::class, 'addPlaylistApi'])->name('hentai.playlists.add');
|
|
||||||
Route::post('/hentai/create-playlist', [PlaylistController::class, 'createPlaylistApi'])->name('hentai.playlists.create');
|
|
||||||
|
|
||||||
// Download Page
|
|
||||||
Route::get('/download-search', [HomeController::class, 'downloadSearch'])->name('download.search');
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
|---------------------------------------------------------------------------------
|
|
||||||
| Admin Pages
|
|
||||||
|---------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
Route::group(['middleware' => ['auth', 'auth.admin']], function () {
|
|
||||||
// Site alerts
|
|
||||||
Route::get('/admin/alert', [App\Http\Controllers\Admin\AlertController::class, 'index'])->name('admin.alert.index');
|
|
||||||
Route::post('/admin/alert', [App\Http\Controllers\Admin\AlertController::class, 'store'])->name('admin.alert.create');
|
|
||||||
Route::delete('/admin/alert/{alert_id}', [App\Http\Controllers\Admin\AlertController::class, 'delete'])->name('admin.alert.delete');
|
|
||||||
|
|
||||||
// Users
|
|
||||||
Route::get('/admin/users', [App\Http\Controllers\Admin\UserController::class, 'index'])->name('admin.user.index');
|
|
||||||
Route::post('/admin/users', [App\Http\Controllers\Admin\UserController::class, 'update'])->name('admin.user.update');
|
|
||||||
|
|
||||||
// Comments
|
|
||||||
Route::get('/admin/comments', [App\Http\Controllers\Admin\CommentsController::class, 'index'])->name('admin.comments.index');
|
|
||||||
|
|
||||||
// Contact page overview
|
|
||||||
Route::get('/admin/contact', [App\Http\Controllers\Admin\ContactController::class, 'index'])->name('admin.contact.index');
|
|
||||||
Route::delete('/admin/contact/{contact_id}', [App\Http\Controllers\Admin\ContactController::class, 'delete'])->name('admin.contact.delete');
|
|
||||||
|
|
||||||
// Site background settings
|
|
||||||
Route::get('/admin/background', [App\Http\Controllers\Admin\SiteBackgroundController::class, 'index'])->name('admin.background.index');
|
|
||||||
Route::post('/admin/background', [App\Http\Controllers\Admin\SiteBackgroundController::class, 'create'])->name('admin.background.create');
|
|
||||||
Route::put('/admin/background', [App\Http\Controllers\Admin\SiteBackgroundController::class, 'update'])->name('admin.background.update');
|
|
||||||
Route::delete('/admin/background', [App\Http\Controllers\Admin\SiteBackgroundController::class, 'delete'])->name('admin.background.delete');
|
|
||||||
|
|
||||||
// Release
|
|
||||||
Route::get('/admin/release', [App\Http\Controllers\Admin\ReleaseController::class, 'index'])->name('admin.upload.index');
|
|
||||||
Route::post('/admin/release/upload', [App\Http\Controllers\Admin\ReleaseController::class, 'store'])->name('admin.upload');
|
|
||||||
|
|
||||||
// Episode
|
|
||||||
Route::post('/admin/episode/upload', [App\Http\Controllers\Admin\EpisodeController::class, 'store'])->name('admin.upload.episode');
|
|
||||||
Route::post('/admin/episode/edit', [App\Http\Controllers\Admin\EpisodeController::class, 'update'])->name('admin.edit');
|
|
||||||
|
|
||||||
// Get Tags used for Upload Form
|
|
||||||
Route::get('/admin/tags', [AdminApiController::class, 'getTags'])->name('admin.tags');
|
|
||||||
Route::get('/admin/studios', [AdminApiController::class, 'getStudios'])->name('admin.studios');
|
|
||||||
|
|
||||||
// Get Tags for editing Episode
|
|
||||||
Route::get('/admin/tags/{episode_id}', [AdminApiController::class, 'getEpisodeTags'])->name('admin.tags.episode');
|
|
||||||
Route::get('/admin/studio/{episode_id}', [AdminApiController::class, 'getEpisodeStudio'])->name('admin.studio.episode');
|
|
||||||
|
|
||||||
// Subtitles
|
|
||||||
Route::get('/admin/subtitles/{episode_id}', [AdminApiController::class, 'getSubtitles'])->name('admin.subtitles');
|
|
||||||
Route::post('/admin/add-new-subtitle', [App\Http\Controllers\Admin\SubtitleController::class, 'store'])->name('admin.add.new.subtitle');
|
|
||||||
Route::post('/admin/update-subtitles', [App\Http\Controllers\Admin\SubtitleController::class, 'update'])->name('admin.update.subtitles');
|
|
||||||
});
|
|
||||||
|
|
||||||
require __DIR__.'/auth.php';
|
require __DIR__.'/auth.php';
|
||||||
|
|||||||
Reference in New Issue
Block a user