This commit is contained in:
2025-09-18 15:31:27 +02:00
commit 2abba0c2b7
406 changed files with 31879 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Alert;
use Illuminate\Http\Request;
class AlertController extends Controller
{
/**
* Display alert index page
*/
public function index(): \Illuminate\View\View
{
return view('admin.alert.index');
}
/**
* Create Alert.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$validated = $request->validate([
'message' => 'required|string|max:255',
'type' => 'required|integer|min:0|digits_between:0,1',
]);
Alert::create([
'text' => $request->input('message'),
'type' => $request->input('type'),
]);
cache()->forget('alerts');
return redirect()->back();
}
/**
* Delete Alert.
*/
public function delete(int $alert_id): \Illuminate\Http\RedirectResponse
{
Alert::where('id', $alert_id)->forceDelete();
cache()->forget('alerts');
return redirect()->back();
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Contact;
class ContactController extends Controller
{
/**
* Display Contact Page.
*/
public function index(): \Illuminate\View\View
{
$contacts = Contact::orderBy('created_at', 'DESC')->get();
return view('admin.contact.index', [
'contacts' => $contacts
]);
}
/**
* Delete Contact.
*/
public function delete(int $contact_id): \Illuminate\Http\RedirectResponse
{
Contact::where('id', $contact_id)->delete();
return redirect()->back();
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Episode;
use App\Jobs\DiscordReleaseNotification;
use App\Services\DownloadService;
use App\Services\EpisodeService;
use App\Services\GalleryService;
use Illuminate\Http\Request;
class EpisodeController extends Controller
{
protected EpisodeService $episodeService;
protected GalleryService $galleryService;
protected DownloadService $downloadService;
public function __construct(
EpisodeService $episodeService,
GalleryService $galleryService,
DownloadService $downloadService
) {
$this->episodeService = $episodeService;
$this->galleryService = $galleryService;
$this->downloadService = $downloadService;
}
/**
* Add Episode to existing series
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$referenceEpisode = Episode::with('hentai')->where('id', $request->input('episode_id'))->firstOrFail();
$episodeNumber = $referenceEpisode->hentai->episodes()->count() + 1;
// Create Episode
$episode = $this->episodeService->createEpisode($request, $referenceEpisode->hentai, $episodeNumber, null, $referenceEpisode);
$this->episodeService->createOrUpdateCover($request, $episode, $referenceEpisode->hentai->slug, 1);
$this->downloadService->createOrUpdateDownloads($request, $episode, 1);
$this->galleryService->createOrUpdateGallery($request, $referenceEpisode->hentai, $episode, $episodeNumber, true);
// Discord Alert
DiscordReleaseNotification::dispatch($episode->slug, 'release');
cache()->flush();
return to_route('hentai.index', [
'title' => $episode->slug
]);
}
/**
* Edit Episode
*/
public function update(Request $request): \Illuminate\Http\RedirectResponse
{
$episode = Episode::with('hentai')->where('id', $request->input('episode_id'))->firstOrFail();
$studio = $this->episodeService->getOrCreateStudio(json_decode($request->input('studio'))[0]->value);
$oldinterpolated = $episode->interpolated;
$oldInterpolatedUHD = $episode->interpolated_uhd;
$episode = $this->episodeService->updateEpisode($request, $studio, $episode->id);
$this->episodeService->createOrUpdateCover($request, $episode, $episode->hentai->slug, 1);
$this->downloadService->createOrUpdateDownloads($request, $episode, 1);
$this->galleryService->createOrUpdateGallery($request, $episode->hentai, $episode, $episode->episode, true);
// Discord Alert
if ($oldinterpolated !== (int) $episode->interpolated) {
DiscordReleaseNotification::dispatch($episode->slug, 'update');
}
if ($oldInterpolatedUHD !== (int) $episode->interpolated_uhd) {
DiscordReleaseNotification::dispatch($episode->slug, 'updateUHD');
}
cache()->flush();
return to_route('hentai.index', [
'title' => $episode->slug
]);
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Hentai;
use App\Jobs\DiscordReleaseNotification;
use App\Services\DownloadService;
use App\Services\EpisodeService;
use App\Services\GalleryService;
use Illuminate\Http\Request;
class ReleaseController extends Controller
{
protected EpisodeService $episodeService;
protected GalleryService $galleryService;
protected DownloadService $downloadService;
public function __construct(
EpisodeService $episodeService,
GalleryService $galleryService,
DownloadService $downloadService
) {
$this->episodeService = $episodeService;
$this->galleryService = $galleryService;
$this->downloadService = $downloadService;
}
/**
* Display release page
*/
public function index(): \Illuminate\View\View
{
return view('admin.release.create');
}
/**
* Upload New Hentai with One or Multipe Episodes
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
// Create new Hentai or find existing one
$slug = $this->episodeService->generateSlug($request->input('title'));
$hentai = Hentai::where('slug', $slug)->first();
// If hentai exists and was created today, return to home
if ($hentai?->created_at->isToday()) {
return to_route('home.index');
}
// If hentai does not exist, create a new instance
$hentai = Hentai::firstOrCreate(
['slug' => $slug],
['description' => $request->input('description1')]
);
// Studio
$studio = $this->episodeService->getOrCreateStudio(json_decode($request->input('studio'))[0]->value);
// Create Episode(s)
$releasedEpisodes = [];
for ($i = 1; $i <= $request->input('episodes'); $i++) {
$episode = $this->episodeService->createEpisode($request, $hentai, $i, $studio);
$this->episodeService->createOrUpdateCover($request, $episode, $slug, $i);
$this->downloadService->createOrUpdateDownloads($request, $episode, $i);
$this->galleryService->createOrUpdateGallery($request, $hentai, $episode, $i);
$releasedEpisodes[] = $episode->slug;
}
foreach ($releasedEpisodes as $slug) {
// Dispatch Discord Alert
DiscordReleaseNotification::dispatch($slug, 'release');
}
cache()->flush();
return to_route('home.index');
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\SiteBackground;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Intervention\Image\Laravel\Facades\Image;
use Intervention\Image\Encoders\WebpEncoder;
class SiteBackgroundController extends Controller
{
/**
* Display admin index page
*/
public function index(): \Illuminate\View\View
{
return view('admin.background.index', [
'images' => SiteBackground::all(),
]);
}
/**
* Create new site backgrounds
*/
public function create(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validate([
'images' => 'required',
'date_start' => 'required',
'date_end' => 'required',
]);
foreach($request->file('images') as $file) {
// Initiating a database transaction in case something goes wrong.
DB::beginTransaction();
try {
$bg = SiteBackground::create(array_merge(
$request->only(['date_start', 'date_end']),
[
'default' => (bool) $request->input('default', false)
]
));
$resolutions = [1440, 1080, 720, 640];
foreach($resolutions as $resolution) {
// /images/background/1-2560p.webp
$targetPath = "/images/background/{$bg->id}-{$resolution}p.webp";
Image::read($file->getRealPath())
->scaleDown(height: $resolution)
->encode(new WebpEncoder())
->save(public_path($targetPath));
}
} catch (\Exception $e) {
DB::rollBack();
Log::error($e->getMessage());
return redirect()->back();
}
// Committing the database transaction.
DB::commit();
}
cache()->forget('background');
return redirect()->back();
}
public function update(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validate([
'id' => 'required|exists:site_backgrounds,id',
'date_start' => 'required',
'date_end' => 'required',
]);
SiteBackground::where('id', $request->input('id'))->update(array_merge(
$request->only(['date_start', 'date_end']),
[
'default' => (bool) $request->input('default', false)
]
));
cache()->forget('background');
return redirect()->back();
}
/**
* Delete backround
*/
public function delete(Request $request): \Illuminate\Http\RedirectResponse
{
$id = $request->input('id');
// Initiating a database transaction in case something goes wrong.
DB::beginTransaction();
$bg = SiteBackground::where('id', $id)->firstOrFail();
$bg->forceDelete();
$resolutions = [1440, 1080, 720, 640];
try {
foreach($resolutions as $resolution) {
$targetPath = "/images/background/{$id}-{$resolution}p.webp";
File::delete(public_path($targetPath));
}
} catch (\Exception $e) {
DB::rollBack();
Log::error($e->getMessage());
return redirect()->back();
}
// Committing the database transaction.
DB::commit();
cache()->forget('background');
return redirect()->back();
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\Episode;
use App\Models\EpisodeSubtitle;
use App\Models\Subtitle;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SubtitleController extends Controller
{
/**
* Add new Subtitle.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$subtitle = Subtitle::create([
'name' => $request->name,
'slug' => $request->slug,
]);
// Add to Episode
EpisodeSubtitle::create([
'episode_id' => $request->episode_id,
'subtitle_id' => $subtitle->id,
]);
return redirect()->back();
}
/**
* Update Episode Subtitles.
*/
public function update(Request $request): \Illuminate\Http\RedirectResponse
{
$episode = Episode::where('id', $request->input('episode_id'))->firstOrFail();
// Clear everything
foreach($episode->subtitles as $sub) {
$sub->forceDelete();
}
if (! $request->input('subtitles')) {
return redirect()->back();
}
// Re-Add
foreach (json_decode($request->input('subtitles')) as $sub) {
$subtitle = Subtitle::where('name', $sub->value)->firstOrFail();
// Add to Episode
EpisodeSubtitle::create([
'episode_id' => $episode->id,
'subtitle_id' => $subtitle->id,
]);
}
return redirect()->back();
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Torrents;
use Illuminate\Http\Request;
class TorrentController extends Controller
{
/**
* Display Add Torrent Page.
*/
public function index(int $hentai_id): \Illuminate\View\View
{
return view('admin.add-torrent', [
'hentai_id' => $hentai_id
]);
}
/**
* Add Torrent.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$validated = $request->validate([
'hentai_id' => 'required|exists:hentais,id',
'torrent_url' => 'required|string|max:256',
'torrent_episodes' => 'required|string|max:8',
]);
Torrents::create([
'hentai_id' => $request->hentai_id,
'torrent_url' => $request->torrent_url,
'episodes' => $request->torrent_episodes,
]);
return to_route('download.search');
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\User;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Display Users Page.
*/
public function index(): \Illuminate\View\View
{
return view('admin.users.index');
}
/**
* Update user (ban/unban).
*/
public function update(Request $request)
{
$validated = $request->validate([
'id' => 'required|exists:users,id',
'action' => 'required',
]);
$user = User::findOrFail($validated['id']);
switch ($validated['action']) {
case 'ban':
$user->update(['is_banned' => 1]);
alert()->success('Banned', 'User has been banned.');
break;
case 'unban':
$user->update(['is_banned' => 0]);
alert()->success('Unbanned', 'User has been unbanned.');
break;
default:
alert()->error('Error','Invalid action provided');
}
return redirect()->back();
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\CacheHelper;
use App\Models\Episode;
use App\Models\Studios;
use App\Models\Subtitle;
use App\Http\Controllers\Controller;
class AdminApiController extends Controller
{
/**
* Get Tags (API).
*/
public function getTags()
{
$tags = CacheHelper::getAllTags();
$tagWhiteList = [];
foreach ($tags as $tag) {
$tagWhiteList[] = $tag->name;
}
return response()->json(['message' => 'success', 'tags' => $tagWhiteList], 200);
}
/**
* Get Studios (API).
*/
public function getStudios()
{
$studios = Studios::orderBy('name', 'ASC')->get();
$studioList = [];
foreach ($studios as $studio) {
$studioList[] = $studio->name;
}
return response()->json(['message' => 'success', 'studios' => $studioList], 200);
}
/**
* Get Subtitles (API).
*/
public function getSubtitles(int $episode_id)
{
$subs = Subtitle::all();
$subsWhiteList = [];
foreach ($subs as $sub) {
$subsWhiteList[] = $sub->name;
}
$episode = Episode::where('id', $episode_id)->firstOrFail();
$episodetags = [];
foreach ($episode->subtitles as $tag) {
$episodetags[] = $tag->subtitle->name;
}
return response()->json(['message' => 'success', 'subs' => $subsWhiteList, 'episodesubs' => $episodetags], 200);
}
/**
* Get Episode Tags (API).
*/
public function getEpisodeTags(int $episode_id)
{
$tags = CacheHelper::getAllTags();
$tagWhiteList = [];
foreach ($tags as $tag) {
$tagWhiteList[] = $tag->name;
}
$episode = Episode::where('id', $episode_id)->firstOrFail();
$episodetags = [];
foreach ($episode->tags as $tag) {
$episodetags[] = $tag->name;
}
return response()->json(['message' => 'success', 'tags' => $tagWhiteList, 'episodetags' => $episodetags], 200);
}
/**
* Get Episode Studio (API).
*/
public function getEpisodeStudio(int $episode_id)
{
$studios = Studios::orderBy('name', 'ASC')->get();
$studioList = [];
foreach ($studios as $studio) {
$studioList[] = $studio->name;
}
$episode = Episode::where('id', $episode_id)->firstOrFail();
$episodestudio = [$episode->studio->name];
return response()->json(['message' => 'success', 'studios' => $studioList, 'episodestudios' => $episodestudio], 200);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Downloads;
use App\Models\Episode;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class DownloadApiController extends Controller
{
/**
* Get Download URL for users who are not logged in.
*/
public function getDownload(Request $request)
{
$validated = $request->validate([
'episode_id' => 'required',
'captcha' => 'required|captcha'
]);
$episode = Episode::where('id', $request->input('episode_id'))->firstOrFail();
// Increase download count, as we assume the user
// downloads after submitting the captcha
$download = Downloads::find($episode->getDownloadByType('FHD')->id);
$oldCount = $download->count;
$download->count++;
$download->save();
return response()->json([
'message' => 'success',
'download_url' => $download->url,
'download_count' => $oldCount,
], 200);
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Episode;
use Illuminate\Http\Request;
class StreamApiController extends Controller
{
/**
* Get Data used by the Video Player.
*/
public function getStream(Request $request)
{
$validated = $request->validate([
'episode_id' => 'required',
]);
$episode = Episode::where('id', $request->input('episode_id'))->firstOrFail();
$subtitles = $episode->subtitles
->mapWithKeys(fn($sub) => [$sub->subtitle->slug => $sub->subtitle->name])
->toArray();
return response()->json([
'title' => $episode->title.' - '.$episode->episode,
'poster' => $episode->gallery()->first()->image_url,
'interpolated' => $episode->interpolated,
'interpolated_uhd' => $episode->interpolated_uhd,
'stream_url' => $episode->url,
'stream_domains' => config('hstream.stream_domain'),
'asia_stream_domains' => config('hstream.asia_stream_domain'),
'extra_subtitles' => $subtitles
], 200);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Api;
use App\Helpers\CacheHelper;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Conner\Tagging\Model\Tag;
class UserApiController extends Controller
{
/**
* Get Tags (API).
*/
public function getBlacklist(Request $request)
{
$user = $request->user();
$tagWhiteList = [];
$tagBlackList = [];
// All Tags
foreach (CacheHelper::getAllTags() as $tag) {
$tagWhiteList[] = $tag->name;
}
// User Tags
if ($user->tag_blacklist) {
foreach ($user->tag_blacklist as $tag) {
$t = Tag::where('slug', $tag)->first();
$tagBlackList[] = $t->name;
}
}
return response()->json([
'message' => 'success',
'tags' => $tagWhiteList,
'usertags' => $tagBlackList
], 200);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*/
public function create(): View
{
return view('auth.login');
}
/**
* Handle an incoming authentication request.
*/
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::HOME);
}
/**
* Destroy an authenticated session.
*/
public function destroy(Request $request): RedirectResponse
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
use Illuminate\View\View;
class ConfirmablePasswordController extends Controller
{
/**
* Show the confirm password view.
*/
public function show(): View
{
return view('auth.confirm-password');
}
/**
* Confirm the user's password.
*/
public function store(Request $request): RedirectResponse
{
if (! Auth::guard('web')->validate([
'email' => $request->user()->email,
'password' => $request->password,
])) {
throw ValidationException::withMessages([
'password' => __('auth.password'),
]);
}
$request->session()->put('auth.password_confirmed_at', time());
return redirect()->intended(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class EmailVerificationNotificationController extends Controller
{
/**
* Send a new email verification notification.
*/
public function store(Request $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME);
}
$request->user()->sendEmailVerificationNotification();
return back()->with('status', 'verification-link-sent');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class EmailVerificationPromptController extends Controller
{
/**
* Display the email verification prompt.
*/
public function __invoke(Request $request): RedirectResponse|View
{
return $request->user()->hasVerifiedEmail()
? redirect()->intended(RouteServiceProvider::HOME)
: view('auth.verify-email');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
class NewPasswordController extends Controller
{
/**
* Display the password reset view.
*/
public function create(Request $request): View
{
return view('auth.reset-password', ['request' => $request]);
}
/**
* Handle an incoming new password request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'token' => ['required'],
'email' => ['required', 'email'],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user) use ($request) {
$user->forceFill([
'password' => Hash::make($request->password),
'remember_token' => Str::random(60),
])->save();
event(new PasswordReset($user));
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
class PasswordController extends Controller
{
/**
* Update the user's password.
*/
public function update(Request $request): RedirectResponse
{
$validated = $request->validateWithBag('updatePassword', [
'current_password' => ['required', 'current_password'],
'password' => ['required', Password::defaults(), 'confirmed'],
]);
$request->user()->update([
'password' => Hash::make($validated['password']),
]);
return back()->with('status', 'password-updated');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\View\View;
class PasswordResetLinkController extends Controller
{
/**
* Display the password reset link request view.
*/
public function create(): View
{
return view('auth.forgot-password');
}
/**
* Handle an incoming password reset link request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'email' => ['required', 'email'],
]);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$status = Password::sendResetLink(
$request->only('email')
);
return $status == Password::RESET_LINK_SENT
? back()->with('status', __($status))
: back()->withInput($request->only('email'))
->withErrors(['email' => __($status)]);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
class RegisteredUserController extends Controller
{
/**
* Display the registration view.
*/
public function create(): View
{
return view('auth.register');
}
/**
* Handle an incoming registration request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::HOME);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
class VerifyEmailController extends Controller
{
/**
* Mark the authenticated user's email address as verified.
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
if ($request->user()->hasVerifiedEmail()) {
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
return redirect()->intended(RouteServiceProvider::HOME.'?verified=1');
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use Illuminate\Http\Request;
class ContactController extends Controller
{
/**
* Display Contact Page.
*/
public function index(): \Illuminate\View\View
{
return view('contact.form');
}
/**
* Store Contact Submission.
*/
public function store(Request $request): \Illuminate\Http\RedirectResponse
{
$validated = $request->validate([
'name' => 'required|max:30',
'email' => 'required|max:50',
'message' => 'required|max:1000',
'subject' => 'required|max:50',
'captcha' => 'required|captcha',
]);
$contact = new Contact();
$contact->name = $request->input('name');
$contact->email = $request->input('email');
$contact->message = $request->input('message');
$contact->subject = $request->input('subject');
$contact->save();
return back()->with('status', 'contact-submitted');
}
public function reloadCaptcha(): \Illuminate\Http\JsonResponse
{
return response()->json(['captcha'=> captcha_img()]);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
}

View File

@@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers;
use App\Models\Episode;
use App\Helpers\CacheHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
class HomeController extends Controller
{
/**
* Display Home Page.
*/
public function index(): \Illuminate\View\View
{
$guest = Auth::guest();
$guestString = $guest ? 'guest' : 'authed';
$mostLikes = \cache()->remember('mostLikes'.$guestString, 300, fn () =>
Episode::with('gallery')
->when($guest, fn ($query) => $query->withoutTags(['loli', 'shota']))
->whereIn('id', function($query) {
$mostLikesIds = CacheHelper::getMostLikes()->pluck('markable_id')->toArray();
$query->selectRaw('id')
->from('episodes')
->whereIn('id', $mostLikesIds)
->orderByRaw("FIELD(id, " . implode(',', $mostLikesIds) . ")");
})
->get()
);
return view('home.index', [
'recentlyReleased' => CacheHelper::getRecentlyReleased($guest),
'recentlyUploaded' => CacheHelper::getRecentlyUploaded($guest),
'popularAllTime' => CacheHelper::getPopularAllTime($guest),
'popularMonthly' => CacheHelper::getPopularMonthly(),
'popularWeekly' => CacheHelper::getPopularWeekly(),
'popularDaily' => CacheHelper::getPopularDaily(),
'mostLikes' => $mostLikes,
'latestComments' => CacheHelper::getLatestComments(),
]);
}
/**
* Display Banned Page.
*/
public function banned(): \Illuminate\View\View
{
return view('auth.banned');
}
/**
* Display Search Page.
*/
public function search(): \Illuminate\View\View
{
return view('search.index');
}
/**
* Display Download Search Page.
*/
public function downloadSearch(): \Illuminate\View\View
{
return view('search.download');
}
/**
* Redirect POST Data to GET with Query String.
*/
public function searchRedirect(Request $request): \Illuminate\Http\RedirectResponse
{
return redirect()->route('hentai.search', [
'search' => $request->input('live-search'),
]);
}
/**
* Display Stats Page.
*/
public function stats(): \Illuminate\View\View
{
return view('home.stats', [
'viewCount' => CacheHelper::getTotalViewCount(),
'episodeCount' => CacheHelper::getTotalEpisodeCount(),
'hentaiCount' => CacheHelper::getTotalHentaiCount(),
'monthlyCount' => CacheHelper::getTotalMonthlyViews()
]);
}
/**
* Manually set website language
*/
public function updateLanguage(Request $request): \Illuminate\Http\RedirectResponse
{
if(! in_array($request->language, config('lang-detector.languages'))) {
return redirect()->back();
}
Cookie::queue(Cookie::forever('locale', $request->language));
return redirect()->back();
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class NotificationController extends Controller
{
/**
* Display the user's notification page.
*/
public function index(Request $request): \Illuminate\View\View
{
return view('profile.notifications', [
'user' => $request->user(),
'notifications' => $request->user()->unreadNotifications,
]);
}
/**
* Delete Notifcation
*/
public function delete(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validate([
'id' => 'required|exists:notifications,id',
]);
$notification = $request->user()
->notifications()
->where('id', $request->input('id'))
->firstOrFail();
$notification->forceDelete();
return redirect()->back();
}
}

View File

@@ -0,0 +1,198 @@
<?php
namespace App\Http\Controllers;
use App\Models\Episode;
use App\Models\Playlist;
use App\Models\PlaylistEpisode;
use App\Services\PlaylistService;
use Illuminate\Http\Request;
use RealRashid\SweetAlert\Facades\Alert;
class PlaylistController extends Controller
{
protected PlaylistService $playlistService;
public function __construct(PlaylistService $playlistService)
{
$this->playlistService = $playlistService;
}
/**
* Display the public playlists page.
*/
public function index(): \Illuminate\View\View
{
return view('playlist.index');
}
/**
* Display public playlist.
*/
public function show($playlist_id): \Illuminate\View\View
{
if (!is_numeric($playlist_id)) {
abort(404);
}
$playlist = Playlist::where('is_private', 0)->where('id', $playlist_id)->firstOrFail();
return view('playlist.list', [
'playlist' => $playlist,
]);
}
/**
* Display the user's playlists page.
*/
public function playlists(Request $request): \Illuminate\View\View
{
$title = 'Delete Playlist!';
$text = "Are you sure you want to delete?";
confirmDelete($title, $text);
return view('profile.playlists', [
'user' => $request->user(),
'playlists' => $request->user()->playlists,
]);
}
/**
* Display user's playlist.
*/
public function showPlaylist(Request $request, $playlist_id): \Illuminate\View\View
{
if (!is_numeric($playlist_id)) {
abort(404);
}
$user = $request->user();
$playlist = Playlist::where('user_id', $user->id)->where('id', $playlist_id)->firstOrFail();
return view('playlist.list', [
'playlist' => $playlist,
]);
}
/**
* Create user playlist (Form).
*/
public function createPlaylist(Request $request): \Illuminate\Http\RedirectResponse
{
$validated = $request->validate([
'name' => 'required|max:30',
]);
$playlist = new Playlist();
$playlist->user_id = $request->user()->id;
$playlist->name = $request->input('name');
$playlist->is_private = $request->input('visiblity') === 'private';
$playlist->save();
return to_route('profile.playlists');
}
/**
* Delete user playlist.
*/
public function deletePlaylist(Request $request, $playlist_id): \Illuminate\Http\RedirectResponse
{
if (!is_numeric($playlist_id)) {
abort(404);
}
$user = $request->user();
$playlist = Playlist::where('user_id', $user->id)->where('id', $playlist_id)->firstOrFail();
// Delete Playlist Episodes
PlaylistEpisode::where('playlist_id', $playlist->id)->forceDelete();
// Delete Playlist
$playlist->forceDelete();
return to_route('profile.playlists');
}
/**
* Delete episode from playlist.
*/
public function deleteEpisodeFromPlaylist(Request $request): \Illuminate\Http\JsonResponse
{
if (!is_numeric($request->input('playlist')) || !is_numeric($request->input('episode'))) {
return response()->json([
'message' => 'not-numeric',
'user' => $request->user(),
], 404);
}
$playlist = Playlist::where('user_id', $request->user()->id)->where('id', (int) $request->input('playlist'))->firstOrFail();
PlaylistEpisode::where('playlist_id', $playlist->id)->where('episode_id', (int) $request->input('episode'))->forceDelete();
$this->playlistService->reorderPositions($playlist);
return response()->json([
'message' => 'success',
'user' => $request->user(),
], 200);
}
/**
* Add to user playlist (API).
*/
public function addPlaylistApi(Request $request): \Illuminate\Http\JsonResponse
{
$user = $request->user();
$validated = $request->validate([
'playlist' => 'required|max:30',
'episode_id' => 'required'
]);
$playlist = Playlist::where('user_id', $user->id)->where('id', $request->input('playlist'))->firstOrFail();
$episode = Episode::where('id', $request->input('episode_id'))->firstOrFail();
// Check if already in playlist
$exists = PlaylistEpisode::where('playlist_id', $playlist->id)->where('episode_id', $episode->id)->exists();
if ($exists) {
return response()->json([
'message' => 'already-added'
], 200);
}
// Position of entry
$position = $playlist->episodes->count() + 1;
PlaylistEpisode::create([
'playlist_id' => $playlist->id,
'episode_id' => $episode->id,
'position' => $position,
]);
return response()->json([
'message' => 'success'
], 200);
}
/**
* Create user playlist (API).
*/
public function createPlaylistApi(Request $request): \Illuminate\Http\JsonResponse
{
$validated = $request->validate([
'name' => 'required|max:30',
]);
$playlist = new Playlist();
$playlist->user_id = $request->user()->id;
$playlist->name = $request->input('name');
$playlist->is_private = $request->input('visiblity') === 'private';
$playlist->save();
return response()->json([
'message' => 'success',
'playlist_id' => $playlist->id
], 200);
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace App\Http\Controllers;
use App\Models\Episode;
use App\Http\Requests\ProfileUpdateRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Conner\Tagging\Model\Tag;
class ProfileController extends Controller
{
/**
* Display the user page.
*/
public function index(Request $request): \Illuminate\View\View
{
return view('profile.index', [
'user' => $request->user(),
]);
}
/**
* Display the user's settings form.
*/
public function settings(Request $request): \Illuminate\View\View
{
$example = Episode::where('title', 'Succubus Yondara Gibo ga Kita!?')->first();
return view('profile.settings', [
'user' => $request->user(),
'example' => $example,
]);
}
/**
* Display the user's watched page.
*/
public function watched(Request $request): \Illuminate\View\View
{
return view('profile.watched', [
'user' => $request->user(),
]);
}
/**
* Display the user's comments page.
*/
public function comments(Request $request): \Illuminate\View\View
{
return view('profile.comments', [
'user' => $request->user(),
]);
}
/**
* Display the user's likes page.
*/
public function likes(Request $request): \Illuminate\View\View
{
return view('profile.likes', [
'user' => $request->user(),
]);
}
/**
* Update user settings.
*/
public function saveSettings(Request $request): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$user->search_design = $request->input('searchDesign') == 'thumbnail';
$user->home_top_design = $request->input('topDesign') == 'thumbnail';
$user->home_middle_design = $request->input('middleDesign') == 'thumbnail';
$user->save();
return Redirect::route('profile.settings')->with('status', 'design-updated');
}
/**
* Update user tag blacklist.
*/
public function saveBlacklist(Request $request): \Illuminate\Http\RedirectResponse
{
$user = $request->user();
$tags = json_decode($request->input('tags'));
if (!$tags) {
$user->tag_blacklist = null;
$user->save();
return Redirect::route('profile.settings')->with('status', 'blacklist-updated');
}
$blacklist = [];
foreach ($tags as $tag) {
$t = Tag::where('slug', Str::slug($tag->value))->firstOrFail();
$blacklist[] = $t->slug;
}
$user->tag_blacklist = $blacklist;
$user->save();
return Redirect::route('profile.settings')->with('status', 'blacklist-updated');
}
/**
* Delete the user's account.
*/
public function destroy(Request $request): \Illuminate\Http\RedirectResponse
{
$request->validateWithBag('userDeletion', [
'password' => ['required', 'current_password'],
]);
$user = $request->user();
Auth::logout();
$user->delete();
$request->session()->invalidate();
$request->session()->regenerateToken();
return Redirect::to('/');
}
}

View File

@@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers;
use App\Models\Episode;
use App\Models\Gallery;
use App\Models\Hentai;
use App\Models\Playlist;
use App\Models\PlaylistEpisode;
use App\Models\Watched;
use App\Helpers\CacheHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use hisorange\BrowserDetect\Facade as Browser;
class StreamController extends Controller
{
/**
* Display Stream Page.
*/
public function index(Request $request, string $title): \Illuminate\View\View
{
$titleParts = explode('-', $title);
if (! is_numeric($titleParts[array_key_last($titleParts)])) {
$hentai = Hentai::with('episodes')->where('slug', $title)->firstOrFail();
if (Auth::guest() && $hentai->isLoliOrShota()) {
return view('auth.please-login');
}
return view('series.index', [
'hentai' => $hentai,
'popularWeekly' => CacheHelper::getPopularWeekly(),
]);
}
$episode = Episode::where('slug', $title)->firstOrFail();
$gallery = Gallery::where('episode_id', $episode->id)->get();
$moreEpisodes = Episode::with(['gallery', 'studio'])->where('hentai_id', $episode->hentai_id)->whereNot('id', $episode->id)->get();
$studioEpisodes = Episode::with(['gallery', 'studio'])->inRandomOrder()->where('studios_id', $episode->studios_id)->whereNot('id', $episode->id)->limit(6)->get();
// Only allow access to problematic stuff when logged in
if (Auth::guest() && $episode->isLoliOrShota()) {
return view('auth.please-login');
}
// Increment View Count
$episode->incrementViewCount();
// Increment Popular Count
$episode->incrementPopularCount();
if (!Auth::guest()) {
$user = Auth::user();
// Add to user watched list
$time = Carbon::now()->subHour(1);
$alreadyWatched = Watched::where('user_id', $user->id)->where('episode_id', $episode->id)->where('created_at', '>=', $time)->exists();
if (!$alreadyWatched) {
Watched::create(['user_id' => $user->id, 'episode_id' => $episode->id]);
cache()->forget('user' . $user->id . 'watched' . $episode->id);
}
}
// Mobile Detection
$isMobile = Browser::isMobile();
// Playlist
if ($request->has('playlist')) {
// Get and check if playlist exists
$playlist = Playlist::where('id', $request->input('playlist'))->firstOrFail();
// Check if episode is in playlist
$inPlaylist = PlaylistEpisode::where('playlist_id', $playlist->id)->where('episode_id', $episode->id)->firstOrFail();
// Get Playlist Episodes and order them
$playlistEpisodes = $playlist->episodes()->orderBy('position')->get();
// Check if authorized
if ($playlist->is_private && (Auth::guest() || (!Auth::guest() && Auth::user()->id != $playlist->user_id))) {
abort(404);
}
return view('stream.index', [
'episode' => $episode,
'moreEpisodes' => $moreEpisodes,
'studioEpisodes' => $studioEpisodes,
'gallery' => $gallery,
'playlist' => $playlist,
'playlistEpisodes' => $playlistEpisodes,
'popularWeekly' => CacheHelper::getPopularWeekly(),
'isMobile' => $isMobile,
]);
}
return view('stream.index', [
'episode' => $episode,
'moreEpisodes' => $moreEpisodes,
'studioEpisodes' => $studioEpisodes,
'gallery' => $gallery,
'popularWeekly' => CacheHelper::getPopularWeekly(),
'isMobile' => $isMobile,
]);
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Playlist;
use App\Models\PlaylistEpisode;
use App\Models\Watched;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class UserController extends Controller
{
/**
* Display User Page.
*/
public function index(string $username): \Illuminate\View\View
{
$user = User::where('username', $username)
->select('id', 'username', 'global_name', 'avatar', 'created_at', 'is_patreon')
->firstOrFail();
return view('user.index', [
'user' => $user,
]);
}
/**
* Delete User.
*/
public function delete(Request $request): \Illuminate\Http\RedirectResponse
{
$user = User::where('id', $request->user()->id)->firstOrFail();
// Delete Playlist
$playlists = Playlist::where('user_id', $user->id)->get();
foreach($playlists as $playlist) {
PlaylistEpisode::where('playlist_id', $playlist->id)->forceDelete();
$playlist->forceDelete();
}
// Update comments to deleted user
DB::table('comments')->where('commenter_id', '=', $user->id)->update(['commenter_id' => 1]);
$user->forceDelete();
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
cache()->flush();
return redirect('/');
}
}

70
app/Http/Kernel.php Normal file
View File

@@ -0,0 +1,70 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\IsBanned::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's middleware aliases.
*
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'auth.admin' => \App\Http\Middleware\IsAdmin::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
return $request->expectsJson() ? null : route('login');
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,44 @@
<?php namespace app\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
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.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if( ! $this->auth->user()->is_admin)
{
session()->flash('error_msg','This resource is restricted to Administrators!');
return redirect()->route('home.index');
}
return $next($request);
}
}

View File

@@ -0,0 +1,29 @@
<?php namespace app\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Contracts\Auth\Guard;
class IsBanned {
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(auth()->check() && auth()->user()->is_banned == 1)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect()->route('home.banned');
}
return $next($request);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts(): array
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'email' => ['required', 'string', 'email'],
'password' => ['required', 'string'],
];
}
/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
/**
* Ensure the login request is not rate limited.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function ensureIsNotRateLimited(): void
{
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
return;
}
event(new Lockout($this));
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
]);
}
/**
* Get the rate limiting throttle key for the request.
*/
public function throttleKey(): string
{
return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Requests;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class ProfileUpdateRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'name' => ['string', 'max:255'],
'email' => ['email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)],
];
}
}