Migrate backend to Porto architecture and fix minor bugs

- Fully transitioned the backend to the Porto architectural pattern
- Improved code organization and maintainability
- Fixed minor bugs and inconsistencies
This commit is contained in:
F4ilji
2025-05-12 08:47:43 +05:00
parent 76deaf75f3
commit c01016a154
620 changed files with 16218 additions and 6073 deletions
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Broadcasting;
use Illuminate\Broadcasting\Channel as LaravelChannel;
abstract class Channel extends LaravelChannel
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Broadcasting;
use Illuminate\Broadcasting\PresenceChannel as LaravelPresenceChannel;
abstract class PresenceChannel extends LaravelPresenceChannel
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Broadcasting;
use Illuminate\Broadcasting\PrivateChannel as LaravelPrivateChannel;
abstract class PrivateChannel extends LaravelPrivateChannel
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Commands;
use Illuminate\Console\Command as LaravelCommand;
abstract class ConsoleCommand extends LaravelCommand
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Components;
use Illuminate\View\Component as LaravelComponent;
abstract class Component extends LaravelComponent
{
}
@@ -0,0 +1,13 @@
<?php
namespace App\Ship\Abstracts\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as LaravelBaseController;
abstract class Controller extends LaravelBaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Ship\Abstracts\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
abstract class Event
{
use Dispatchable, InteractsWithSockets, SerializesModels;
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as LaravelExceptionHandler;
abstract class Handler extends LaravelExceptionHandler
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Factories;
use Illuminate\Database\Eloquent\Factories\Factory as LaravelFactory;
abstract class Factory extends LaravelFactory
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Jobs;
use Illuminate\Foundation\Bus\Dispatchable;
abstract class Job
{
use Dispatchable;
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Ship\Abstracts\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
abstract class JobQueued implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
}
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace App\Ship\Abstracts\Mails;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable as LaravelMailable;
use Illuminate\Queue\SerializesModels;
abstract class Mailable extends LaravelMailable
{
use Queueable, SerializesModels;
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Mails\Mailables;
use Illuminate\Mail\Mailables\Content as LaravelContent;
abstract class Content extends LaravelContent
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Mails\Mailables;
use Illuminate\Mail\Mailables\Envelope as LaravelEnvelope;
abstract class Envelope extends LaravelEnvelope
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Auth\Middleware\Authenticate as LaravelMiddleware;
abstract class Authenticate extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as LaravelMiddleware;
abstract class EncryptCookies extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as LaravelMiddleware;
abstract class PreventRequestsDuringMaintenance extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as LaravelMiddleware;
abstract class TrimStrings extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Http\Middleware\TrustHosts as LaravelMiddleware;
abstract class TrustHosts extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Http\Middleware\TrustProxies as LaravelMiddleware;
abstract class TrustProxies extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as LaravelMiddleware;
abstract class ValidateSignature extends LaravelMiddleware
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as LaravelMiddleware;
abstract class VerifyCsrfToken extends LaravelMiddleware
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Models;
use Illuminate\Database\Eloquent\Model as LaravelModel;
abstract class BaseModel extends LaravelModel
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Models;
use Illuminate\Database\Eloquent\Builder as LaravelBuilder;
abstract class Builder extends LaravelBuilder
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Models;
use Illuminate\Database\Eloquent\Relations\MorphPivot as LaravelMorphPivot;
abstract class MorphPivot extends LaravelMorphPivot
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Models;
use Illuminate\Database\Eloquent\Relations\Pivot as LaravelPivot;
abstract class Pivot extends LaravelPivot
{
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace App\Ship\Abstracts\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as LaravelAuthenticate;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
abstract class UserModel extends LaravelAuthenticate
{
use HasApiTokens, HasFactory, Notifiable;
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Notifications\Messages;
use Illuminate\Notifications\Messages\MailMessage as LaravelMailMessage;
abstract class MailMessage extends LaravelMailMessage
{
}
@@ -0,0 +1,11 @@
<?php
namespace App\Ship\Abstracts\Notifications;
use Illuminate\Notifications\Notification as LaravelNotification;
use Illuminate\Bus\Queueable;
abstract class Notification extends LaravelNotification
{
use Queueable;
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Policies;
use Illuminate\Auth\Access\HandlesAuthorization;
abstract class Policy
{
use HandlesAuthorization;
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as LaravelAuthServiceProvider;
abstract class AuthServiceProvider extends LaravelAuthServiceProvider
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Providers;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as LaravelEventServiceProvider;
abstract class EventServiceProvider extends LaravelEventServiceProvider
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as LaravelRouteServiceProvider;
abstract class RouteServiceProvider extends LaravelRouteServiceProvider
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Providers;
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
abstract class ServiceProvider extends LaravelServiceProvider
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Requests;
use Illuminate\Foundation\Http\FormRequest as LaravelFormRequest;
abstract class FormRequest extends LaravelFormRequest
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Requests;
use Illuminate\Http\Request as LaravelRequest;
abstract class Request extends LaravelRequest
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Resources;
use Illuminate\Http\Resources\Json\JsonResource as LaravelJsonResource;
abstract class JsonResource extends LaravelJsonResource
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection as LaravelResourceCollection;
abstract class ResourceCollection extends LaravelResourceCollection
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Responses;
use Illuminate\Http\RedirectResponse as LaravelRedirectResponse;
abstract class RedirectResponse extends LaravelRedirectResponse
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Responses;
use Illuminate\Http\Response as LaravelResponse;
abstract class Response extends LaravelResponse
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Seeders;
use Illuminate\Database\Seeder as LaravelSeeder;
abstract class Seeder extends LaravelSeeder
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Tests\PhpUnit;
use Illuminate\Foundation\Testing\TestCase as LaravelTestCase;
abstract class TestCase extends LaravelTestCase
{
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Translations;
use Illuminate\Translation\PotentiallyTranslatedString as LaravelPotentiallyTranslatedString;
abstract class PotentiallyTranslatedString extends LaravelPotentiallyTranslatedString
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Abstracts\Views;
use Illuminate\View\Component as LaravelComponent;
abstract class Component extends LaravelComponent
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Broadcasting;
use App\Ship\Abstracts\Broadcasting\Channel as AbstractsChannel;
class Channel extends AbstractsChannel
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Broadcasting;
use App\Ship\Abstracts\Broadcasting\PresenceChannel as AbstractsPresenceChannel;
class PresenceChannel extends AbstractsPresenceChannel
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Broadcasting;
use App\Ship\Abstracts\Broadcasting\PrivateChannel as AbstractsPrivateChannel;
class PrivateChannel extends AbstractsPrivateChannel
{
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace App\Ship\Builders;
use RuntimeException;
class FilterBuilder
{
protected array $filters = [];
/**
* Добавляет фильтр
*
* @param string $key Уникальный ключ фильтра (например 'category_filter')
* @param string $type Тип фильтра ('category', 'tag', 'search', 'sort')
* @param mixed $value Значение фильтра
* @param string $param Параметр из URL (например 'category')
* @param array|object|null $content Контент (например данные о категории)
* @return $this
*/
public function add(string $key, string $type, mixed $value, string $param, mixed $content = null): self
{
if (isset($this->filters[$key])) {
throw new RuntimeException("Фильтр с ключом '$key' уже существует.");
}
$this->filters[$key] = [
'type' => $type,
'value' => $value,
'param' => $param,
'content' => $content,
];
return $this;
}
/**
* Возвращает собранные фильтры
*
* @return array
*/
public function get(): array
{
return $this->filters;
}
public function reset(): self
{
$this->filters = [];
return $this;
}
}
@@ -0,0 +1,8 @@
<?php
namespace App\Ship\Contracts;
interface SeoDescriptionInterface
{
public function getSeoDescription(): array;
}
@@ -0,0 +1,13 @@
<?php
namespace App\Ship\Contracts;
use Illuminate\Database\Eloquent\Model;
interface SeoServiceInterface
{
public function getSeoForModel(Model $model): ?array;
public function getSeoForCurrentPage(): array|null;
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Controllers;
use App\Ship\Abstracts\Controllers\Controller as AbstractBaseController;
class Controller extends AbstractBaseController
{
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace App\Ship\Enums;
enum CacheKeys: string {
case USER_PREFIX = 'person_';
case POST_PREFIX = 'post_';
case RECENT_POSTS_PREFIX = 'recent_posts_';
case POSTS_PREFIX = 'posts_';
case EDUCATION_PROGRAM_PREFIX = 'education_program_';
case ADMISSION_CAMPAIGNS_PREFIX = 'admission_campaigns_';
case DIRECTION_STUDIES = 'direction_studies_';
case ADMISSION_PLANS = 'admission_plans_';
case EDUCATION_PROGRAMS_PREFIX = 'education_programs_';
case ADDITIONAL_EDUCATIONAL_PROGRAM_PREFIX = 'additional_education_program_';
case ADDITIONAL_EDUCATIONAL_PROGRAMS_PREFIX = 'additional_education_programs_';
case ACADEMIC_JOURNAL_PREFIX = 'academic_journal_';
case ACADEMIC_JOURNALS_PREFIX = 'academic_journals_';
case CATEGORIES_PREFIX = 'categories_';
case CONTACT_WIDGET_PREFIX = 'contact_widget_';
case CONTACT_WIDGETS_PREFIX = 'contact_widgets_';
// Department
case DEPARTMENT_PREFIX = 'department_';
case DEPARTMENTS_PREFIX = 'departments_';
// Division
case DIVISION_PREFIX = 'division_';
case DIVISIONS_PREFIX = 'divisions_';
// Event
case EVENT_PREFIX = 'event_';
case EVENTS_PREFIX = 'events_';
// Faculty
case FACULTY_PREFIX = 'faculty_';
case FACULTIES_PREFIX = 'faculties_';
// Navigation
case NAVIGATION_PREFIX = 'navigation';
// Page
case PAGE_PREFIX = 'page_';
case PAGE_DATA_PREFIX = 'page_data_';
case PAGE_REFERENCE_LIST_PREFIX = 'page_reference_list_';
case PAGE_REFERENCE_LISTS_PREFIX = 'page_reference_lists_';
// Schedule
case SCHEDULE_PREFIX = 'schedule_';
case SCHEDULES_PREFIX = 'schedules_';
// Slider
case SLIDER_PREFIX = 'slider_';
// Tag
case TAG_IDS_PREFIX = 'tag_ids_';
case TAGS_PREFIX = 'tags_';
case TAG_CONTENT_PREFIX = 'tag_content_';
}
@@ -0,0 +1,31 @@
<?php
namespace App\Ship\Enums\Education;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;
enum AdmissionCampaignStatus: int implements HasLabel, HasColor
{
case ACTIVE = 1;
case ARCHIVED = 2;
case HIDDEN = 3;
public function getLabel(): ?string
{
return match ($this) {
self::ACTIVE => 'Активная',
self::ARCHIVED => 'Архивная',
self::HIDDEN => 'Скрытая',
};
}
public function getColor(): string|array|null
{
return match ($this) {
self::ACTIVE => 'success',
self::ARCHIVED => 'gray',
self::HIDDEN => 'danger',
};
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Ship\Enums\Education;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;
enum BudgetEducation: int implements HasLabel, HasColor
{
case MAIN_QUOTA = 1;
case TARGET_QUOTA = 2;
case SPECIAL_QUOTA = 3;
case PAID_EDUCATION = 4;
case OTHER_SOURCES = 5;
case SEPARATE_QUOTA = 6;
case COMBINED_QUOTA_ALL = 7;
case COMBINED_QUOTA_TARGET_SPECIAL = 8;
case COMBINED_QUOTA_TARGET_SEPARATE = 9;
case COMBINED_QUOTA_SPECIAL_SEPARATE = 10;
case DETAILED_TARGET_QUOTA = 11;
public static function fromName(string $name): ?self {
return match ($name) {
'MAIN_QUOTA' => self::MAIN_QUOTA,
'TARGET_QUOTA' => self::TARGET_QUOTA,
'SPECIAL_QUOTA' => self::SPECIAL_QUOTA,
'PAID_EDUCATION' => self::PAID_EDUCATION,
'OTHER_SOURCES' => self::OTHER_SOURCES,
'SEPARATE_QUOTA' => self::SEPARATE_QUOTA,
'COMBINED_QUOTA_ALL' => self::COMBINED_QUOTA_ALL,
'COMBINED_QUOTA_TARGET_SPECIAL' => self::COMBINED_QUOTA_TARGET_SPECIAL,
'COMBINED_QUOTA_TARGET_SEPARATE' => self::COMBINED_QUOTA_TARGET_SEPARATE,
'COMBINED_QUOTA_SPECIAL_SEPARATE' => self::COMBINED_QUOTA_SPECIAL_SEPARATE,
'DETAILED_TARGET_QUOTA' => self::DETAILED_TARGET_QUOTA,
default => null,
};
}
public function getLabel(): ?string
{
return match ($this) {
self::MAIN_QUOTA => 'Основные места',
self::TARGET_QUOTA => 'Целевая квота',
self::SPECIAL_QUOTA => 'Особая квота',
self::PAID_EDUCATION => 'С оплатой обучения',
self::OTHER_SOURCES => 'За счёт иных средств',
self::SEPARATE_QUOTA => 'Отдельная квота',
self::COMBINED_QUOTA_ALL => 'Совмещенная квота (целевая, особая и отдельная квоты)',
self::COMBINED_QUOTA_TARGET_SPECIAL => 'Совмещенная квота (целевая и особая квоты)',
self::COMBINED_QUOTA_TARGET_SEPARATE => 'Совмещенная квота (целевая и отдельная квоты)',
self::COMBINED_QUOTA_SPECIAL_SEPARATE => 'Совмещенная квота (особая и отдельная квоты)',
self::DETAILED_TARGET_QUOTA => 'Детализированная целевая квота',
};
}
public function getColor(): string|array|null
{
return match ($this) {
self::MAIN_QUOTA => 'primary',
self::TARGET_QUOTA => 'info',
self::SPECIAL_QUOTA => 'danger',
self::PAID_EDUCATION => 'warning',
self::OTHER_SOURCES => 'gray',
self::SEPARATE_QUOTA => 'success',
self::COMBINED_QUOTA_ALL => 'indigo',
self::COMBINED_QUOTA_TARGET_SPECIAL => 'violet',
self::COMBINED_QUOTA_TARGET_SEPARATE => 'fuchsia',
self::COMBINED_QUOTA_SPECIAL_SEPARATE => 'pink',
self::DETAILED_TARGET_QUOTA => 'amber',
};
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Ship\Enums\Education;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;
enum EducationalProgramStatus: int implements HasLabel, HasColor
{
case PUBLISHED = 1; // Опубликована. Отображается в публичной части сайта
case IN_PROGRESS = 2; // На стадии заполнения. Отображается только в редакторе
case ARCHIVED = 3; // Архивная. Отображается только в разделе Абитуриенту и в таблице сведений о трудоустройстве выпускников
case REFERENCE = 4; // Справочная. Отображается только в разделе "Образование" и других
case NOT_IMPLEMENTED = 5; // Не реализуется. Отображается только в справочнике образовательных программ
case LICENSE_PENDING = 6; // Представлена к лицензированию. Отображается только в разделе "Образование"
public function getLabel(): ?string
{
return match ($this) {
self::PUBLISHED => 'Опубликована. Отображается в публичной части сайта',
self::IN_PROGRESS => 'На стадии заполнения. Отображается только в редакторе',
self::ARCHIVED => 'Архивная. Отображается только в разделе Абитуриенту и в таблице сведений о трудоустройстве выпускников',
self::REFERENCE => 'Справочная. Отображается только в разделе "Образование" и других',
self::NOT_IMPLEMENTED => 'Не реализуется. Отображается только в справочнике образовательных программ',
self::LICENSE_PENDING => 'Представлена к лицензированию. Отображается только в разделе "Образование"',
};
}
public function getColor(): string|array|null
{
return match ($this) {
self::PUBLISHED => 'success',
self::IN_PROGRESS => 'warning',
self::ARCHIVED => 'secondary',
self::REFERENCE => 'info',
self::NOT_IMPLEMENTED => 'danger',
self::LICENSE_PENDING => 'primary',
};
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Ship\Enums\Education;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;
enum FormEducation: int implements HasLabel, HasColor
{
case FULL_TIME = 1; // Очная форма обучения
case FULL_PART_TIME = 2; // Заочная форма обучения
case PART_TIME = 3; // Заочная форма обучения
public static function fromName(string $name): ?self {
return match ($name) {
'FULL_TIME' => self::FULL_TIME,
'FULL_PART_TIME' => self::FULL_PART_TIME,
'PART_TIME' => self::PART_TIME,
default => null,
};
}
public function getLabel(): ?string
{
return match ($this) {
self::FULL_TIME => 'Очная форма обучения',
self::FULL_PART_TIME => 'Очно-заочная форма обучения',
self::PART_TIME => 'Заочная форма обучения',
};
}
public function getColor(): string|array|null
{
return match ($this) {
self::FULL_TIME => 'success', // Цвет для очной формы
self::PART_TIME => 'warning', // Цвет для заочной формы
self::FULL_PART_TIME => 'info',
};
}
}
@@ -0,0 +1,128 @@
<?php
namespace App\Ship\Enums\Education;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasColor;
enum LevelEducational: int implements HasLabel, HasColor
{
case PREPARATION_OF_QUALIFIED_WORKERS = 1; // Подготовка квалифицированных рабочих, служащих
case MIDDLE_LEVEL_SPECIALIST_TRAINING = 2; // Программа подготовки специалистов среднего звена
case BACHELOR = 3; // Бакалавриат
case MASTER = 4; // Магистратура
case SPECIALIST = 5; // Специалитет
case POSTGRADUATE = 6; // Аспирантура
case ADJUNCTURE = 7; // Адъюнктура
case RESIDENCY = 8; // Ординатура
case INTERNSHIP = 9; // Ассистентура - стажировка
case PROFESSIONAL_TRAINING = 10; // Профессиональная подготовка по профессиям рабочих, должностям служащих
case RETRAINING = 11; // Переподготовка рабочих, служащих
case ADVANCED_TRAINING = 12; // Повышение квалификации рабочих, служащих
case ADDITIONAL_GENERAL_DEVELOPMENT_PROGRAM = 13; // Дополнительная общеразвивающая программа
case ADDITIONAL_PREPROFESSIONAL_PROGRAM = 14; // Дополнительная предпрофессиональная программа
case ADDITIONAL_PREPROFESSIONAL_ART_PROGRAM = 15; // Дополнительная предпрофессиональная программа в сфере искусств
case PROFESSIONAL_ADVANCEMENT = 16; // Повышение квалификации
case PROFESSIONAL_RETRAINING = 17; // Профессиональная переподготовка
case PRESCHOOL_EDUCATION = 18; // Дошкольное образование
case PRIMARY_GENERAL_EDUCATION = 19; // Начальное общее образование
case BASIC_GENERAL_EDUCATION = 20; // Основное общее образование
case SECONDARY_GENERAL_EDUCATION = 21; // Среднее общее образование
case INTERNSHIP_PROGRAM = 22; // Интернатура
case ADDITIONAL_PREPROFESSIONAL_SPORT_PROGRAM = 23; // Дополнительная предпрофессиональная программа в сфере физической культуры и спорта
case BASIC_HIGHER_EDUCATION = 24; // Базовое высшее образование
case SPECIALIZED_HIGHER_EDUCATION = 25; // Специализированное высшее образование
public static function fromName(string $name): ?self {
return match ($name) {
'PREPARATION_OF_QUALIFIED_WORKERS' => self::PREPARATION_OF_QUALIFIED_WORKERS,
'MIDDLE_LEVEL_SPECIALIST_TRAINING' => self::MIDDLE_LEVEL_SPECIALIST_TRAINING,
'BACHELOR' => self::BACHELOR,
'MASTER' => self::MASTER,
'SPECIALIST' => self::SPECIALIST,
'POSTGRADUATE' => self::POSTGRADUATE,
'ADJUNCTURE' => self::ADJUNCTURE,
'RESIDENCY' => self::RESIDENCY,
'INTERNSHIP' => self::INTERNSHIP,
'PROFESSIONAL_TRAINING' => self::PROFESSIONAL_TRAINING,
'RETRAINING' => self::RETRAINING,
'ADVANCED_TRAINING' => self::ADVANCED_TRAINING,
'ADDITIONAL_GENERAL_DEVELOPMENT_PROGRAM' => self::ADDITIONAL_GENERAL_DEVELOPMENT_PROGRAM,
'ADDITIONAL_PREPROFESSIONAL_PROGRAM' => self::ADDITIONAL_PREPROFESSIONAL_PROGRAM,
'ADDITIONAL_PREPROFESSIONAL_ART_PROGRAM' => self::ADDITIONAL_PREPROFESSIONAL_ART_PROGRAM,
'PROFESSIONAL_ADVANCEMENT' => self::PROFESSIONAL_ADVANCEMENT,
'PROFESSIONAL_RETRAINING' => self::PROFESSIONAL_RETRAINING,
'PRESCHOOL_EDUCATION' => self::PRESCHOOL_EDUCATION,
'PRIMARY_GENERAL_EDUCATION' => self::PRIMARY_GENERAL_EDUCATION,
'BASIC_GENERAL_EDUCATION' => self::BASIC_GENERAL_EDUCATION,
'SECONDARY_GENERAL_EDUCATION' => self::SECONDARY_GENERAL_EDUCATION,
'INTERNSHIP_PROGRAM' => self::INTERNSHIP_PROGRAM,
'ADDITIONAL_PREPROFESSIONAL_SPORT_PROGRAM' => self::ADDITIONAL_PREPROFESSIONAL_SPORT_PROGRAM,
'BASIC_HIGHER_EDUCATION' => self::BASIC_HIGHER_EDUCATION,
'SPECIALIZED_HIGHER_EDUCATION' => self::SPECIALIZED_HIGHER_EDUCATION,
default => null,
};
}
public function getLabel(): ?string
{
return match ($this) {
self::PREPARATION_OF_QUALIFIED_WORKERS => 'Подготовка квалифицированных рабочих, служащих',
self::MIDDLE_LEVEL_SPECIALIST_TRAINING => 'Среднее профессиональное образование',
self::BACHELOR => 'Бакалавриат',
self::MASTER => 'Магистратура',
self::SPECIALIST => 'Специалитет',
self::POSTGRADUATE => 'Аспирантура',
self::ADJUNCTURE => 'Адъюнктура',
self::RESIDENCY => 'Ординатура',
self::INTERNSHIP => 'Ассистентура - стажировка',
self::PROFESSIONAL_TRAINING => 'Профессиональная подготовка по профессиям рабочих, должностям служащих',
self::RETRAINING => 'Переподготовка рабочих, служащих',
self::ADVANCED_TRAINING => 'Повышение квалификации рабочих, служащих',
self::ADDITIONAL_GENERAL_DEVELOPMENT_PROGRAM => 'Дополнительная общеразвивающая программа',
self::ADDITIONAL_PREPROFESSIONAL_PROGRAM => 'Дополнительная предпрофессиональная программа',
self::ADDITIONAL_PREPROFESSIONAL_ART_PROGRAM => 'Дополнительная предпрофессиональная программа в сфере искусств',
self::PROFESSIONAL_ADVANCEMENT => 'Повышение квалификации',
self::PROFESSIONAL_RETRAINING => 'Профессиональная переподготовка',
self::PRESCHOOL_EDUCATION => 'Дошкольное образование',
self::PRIMARY_GENERAL_EDUCATION => 'Начальное общее образование',
self::BASIC_GENERAL_EDUCATION => 'Основное общее образование',
self::SECONDARY_GENERAL_EDUCATION => 'Среднее общее образование',
self::INTERNSHIP_PROGRAM => 'Интернатура',
self::ADDITIONAL_PREPROFESSIONAL_SPORT_PROGRAM => 'Дополнительная предпрофессиональная программа в сфере физической культуры и спорта',
self::BASIC_HIGHER_EDUCATION => 'Базовое высшее образование',
self::SPECIALIZED_HIGHER_EDUCATION => 'Специализированное высшее образование',
};
}
public function getColor(): string|array|null
{
return match ($this) {
self::PREPARATION_OF_QUALIFIED_WORKERS => 'info',
self::MIDDLE_LEVEL_SPECIALIST_TRAINING => 'primary',
self::BACHELOR => 'success',
self::MASTER => 'success',
self::SPECIALIST => 'warning',
self::POSTGRADUATE => 'warning',
self::ADJUNCTURE => 'secondary',
self::RESIDENCY => 'secondary',
self::INTERNSHIP => 'light',
self::PROFESSIONAL_TRAINING => 'info',
self::RETRAINING => 'danger',
self::ADVANCED_TRAINING => 'success',
self::ADDITIONAL_GENERAL_DEVELOPMENT_PROGRAM => 'info',
self::ADDITIONAL_PREPROFESSIONAL_PROGRAM => 'info',
self::ADDITIONAL_PREPROFESSIONAL_ART_PROGRAM => 'info',
self::PROFESSIONAL_ADVANCEMENT => 'success',
self::PROFESSIONAL_RETRAINING => 'danger',
self::PRESCHOOL_EDUCATION => 'success',
self::PRIMARY_GENERAL_EDUCATION => 'success',
self::BASIC_GENERAL_EDUCATION => 'success',
self::SECONDARY_GENERAL_EDUCATION => 'success',
self::INTERNSHIP_PROGRAM => 'light',
self::ADDITIONAL_PREPROFESSIONAL_SPORT_PROGRAM => 'info',
self::BASIC_HIGHER_EDUCATION => 'success',
self::SPECIALIZED_HIGHER_EDUCATION => 'success',
};
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Events;
use App\Ship\Abstracts\Events\Event as AbstractEvent;
class Event extends AbstractEvent
{
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Ship\Exceptions;
use App\Ship\Abstracts\Exceptions\Handler as AbstractHandler;
use Throwable;
class Handler extends AbstractHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Ship\Helpers;
class ByteConverter
{
public static function bytesToHuman($bytes)
{
$units = ['Б', 'КиБ', 'МиБ', 'ГиБ', 'ТиБ', 'ПиБ'];
for ($i = 0; $bytes > 1024; $i++) {
$bytes /= 1024;
}
return round($bytes, 2) . ' ' . $units[$i];
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Ship\Kernels;
use AlxDorosenco\PortoForLaravel\Loaders\CommandsLoader;
use AlxDorosenco\PortoForLaravel\Loaders\RoutesLoader;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as LaravelConsoleKernel;
class ConsoleKernel extends LaravelConsoleKernel
{
use CommandsLoader;
use RoutesLoader;
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('sitemap:generate')->dailyAt('03:00');
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->loadCommandsForConsoleKernel();
$this->loadRoutesForConsoleKernel();
}
}
+90
View File
@@ -0,0 +1,90 @@
<?php
namespace App\Ship\Kernels;
use App\Ship\Middleware\AccessCheck;
use App\Ship\Middleware\EnsureUserIsSuperadmin;
use App\Ship\Middleware\FormTimePeriodMiddleware;
use App\Ship\Middleware\InternalRequestOnly;
use App\Ship\Middleware\LimitPost;
use App\Ship\Middleware\RateLimitCheckMiddleware;
use App\Ship\Middleware\RateLimitCounterMiddleware;
use App\Ship\Middleware\TransformRequestMiddleware;
use Illuminate\Foundation\Http\Kernel as LaravelHttpKernel;
use Spatie\Permission\Middleware\PermissionMiddleware;
use Spatie\Permission\Middleware\RoleMiddleware;
use Spatie\Permission\Middleware\RoleOrPermissionMiddleware;
class HttpKernel extends LaravelHttpKernel
{
/**
* 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 = [
TransformRequestMiddleware::class,
// \App\Ship\Middleware\TrustHosts::class,
\App\Ship\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Ship\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Ship\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\Ship\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Ship\Middleware\VerifyCsrfToken::class,
\App\Http\Middleware\HandleInertiaRequests::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Ship\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Ship\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Ship\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'role' => RoleMiddleware::class,
'permission' => PermissionMiddleware::class,
'role_or_permission' => RoleOrPermissionMiddleware::class,
'access-check' => AccessCheck::class,
'rate.limited.counter' => RateLimitCounterMiddleware::class,
'rate.limited.check' => RateLimitCheckMiddleware::class,
'ensure.browser' => InternalRequestOnly::class,
'superadmin' => EnsureUserIsSuperadmin::class,
'limit.post' => LimitPost::class,
'form.time.period' => FormTimePeriodMiddleware::class,
];
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Mails\Mailables;
use App\Ship\Abstracts\Mails\Mailables\Content as AbstractContent;
class Content extends AbstractContent
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Mails\Mailables;
use App\Ship\Abstracts\Mails\Mailables\Envelope as AbstractEnvelope;
class Envelope extends AbstractEnvelope
{
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace App\Ship\Middleware;
use App\Containers\AppStructure\Models\Page;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class AccessCheck
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Проверяем, существует ли запись для текущего маршрута
$registeredRoute = Page::where('path', '=', $request->route()->uri)
->where('is_registered', '=', true)
->first();
// Если запись не найдена, пропускаем запрос
if (!$registeredRoute) {
return $next($request);
}
// Если код не 200, возвращаем соответствующий код ошибки
if ($registeredRoute->code != 200) {
abort($registeredRoute->code);
}
$request->attributes->set('settings_page', $registeredRoute->settings);
// Если все проверки пройдены, продолжаем выполнение запроса
return $next($request);
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\Authenticate as AbstractMiddleware;
class Authenticate extends AbstractMiddleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return '/';
}
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\EncryptCookies as AbstractMiddleware;
class EncryptCookies extends AbstractMiddleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
@@ -0,0 +1,28 @@
<?php
namespace App\Ship\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Inertia;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsSuperadmin
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next)
{
if (!Auth::check() || !Auth::user()->hasRole('super_admin')) {
return Inertia::render('Error', ['status' => 403])
->toResponse($request)
->setStatusCode(403);
}
return $next($request);
}
}
@@ -0,0 +1,48 @@
<?php
namespace App\Ship\Middleware;
use App\Containers\Widget\Models\CustomForm;
use Carbon\Carbon;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class FormTimePeriodMiddleware
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$form = CustomForm::select('settings')->find($request->route('id'));
if (!$form) {
abort(Response::HTTP_NOT_FOUND, 'Form not found');
}
if (!isset($form->settings['period'])) {
return $next($request);
}
$period = $form->settings['period'];
try {
$start_time = Carbon::parse($period['start_time']);
$end_time = Carbon::parse($period['end_time']);
} catch (\Exception $e) {
abort(Response::HTTP_BAD_REQUEST, 'Invalid time format');
}
$now = Carbon::now();
if ($now >= $start_time && $now <= $end_time) {
return $next($request);
}
abort(Response::HTTP_FORBIDDEN, 'Form is not available at this time');
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Ship\Middleware;
use App\Containers\AppStructure\Models\Page;
use App\Ship\Resources\Breadcrumb\ClientBreadcrumbPage;
use App\Ship\Resources\Breadcrumb\ClientBreadcrumbSection;
use App\Ship\Resources\Breadcrumb\ClientBreadcrumbSubSection;
use Closure;
use Illuminate\Http\Request;
class GenerateBreadcrumbs
{
public function handle(Request $request, Closure $next)
{
$path = $request->path();
$page = Page::where('path', '=', $path)
->with('section.pages.section', 'section.mainSection')
->first();
if ($page && isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
} else {
$breadcrumbs = null;
}
$request->merge(['breadcrumbs' => $breadcrumbs]);
return $next($request);
}
}
@@ -0,0 +1,55 @@
<?php
namespace App\Ship\Middleware;
use App\Containers\AppStructure\Models\MainSection;
use App\Containers\AppStructure\UI\API\Transformers\NavigationResource;
use App\Services\App\Breadcrumb\BreadcrumbService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Inertia\Middleware;
use Tightenco\Ziggy\Ziggy;
class HandleInertiaRequests extends Middleware
{
protected $rootView = 'app';
public function version(Request $request): ?string
{
return parent::version($request);
}
public function share(Request $request): array
{
// Навигация (кешированная)
$navigation = Cache::remember('navigation', now()->addHours(1), function () {
return NavigationResource::collection(
MainSection::with('subSections.pages.section')
->orderBy('sort', 'asc')
->get()
);
});
// Хлебные крошки (автоматически по текущему URL)
$breadcrumbs = app(BreadcrumbService::class)->generateBreadcrumbs();
return [
...parent::share($request),
'auth' => [
'user' => $request->user() ? $request->user()->only('id', 'name', 'email', 'created_at') : null,
],
'ziggy' => fn () => [
...(new Ziggy)->toArray(),
'location' => $request->url(),
],
'navigation' => $navigation,
'breadcrumbs' => $breadcrumbs, // Добавляем хлебные крошки
'urlPrev' => function () {
if (url()->previous() !== url()->current()) {
return url()->previous();
}
return 'empty';
},
];
}
}
@@ -0,0 +1,27 @@
<?php
namespace App\Ship\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class InternalRequestOnly
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next)
{
$userAgent = $request->header('User-Agent');
// Проверяем, что User-Agent содержит ключевые слова, характерные для браузеров
if (!preg_match('/Mozilla|Chrome|Safari|Firefox|Edge/i', $userAgent)) {
return response('Access denied. This route is available only from a web browser.', 403);
}
return $next($request);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Ship\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
class LimitPost
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (auth()->user()->receivedInvitation === null) {
return $next($request);
}
if (auth()->user()->receivedInvitation->post_limit > 0) {
return $next($request);
} else {
throw new HttpException(403, 'Лимит постов исчерпан');
}
}
}
@@ -0,0 +1,17 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\PreventRequestsDuringMaintenance as AbstractMiddleware;
class PreventRequestsDuringMaintenance extends AbstractMiddleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
@@ -0,0 +1,27 @@
<?php
namespace App\Ship\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class RateLimitCheckMiddleware
{
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
$key = 'rate_limit:' . $ip;
// Получаем текущее количество попыток
$attempts = Cache::get($key, 0);
if ($attempts >= 5) {
return response()->json(['message' => 'Слишком много запросов, пожалуйста, попробуйте позже.'], 429);
}
// Увеличиваем количество попыток
return $next($request);
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Ship\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class RateLimitCounterMiddleware
{
public function handle(Request $request, Closure $next)
{
$ip = $request->ip();
$key = 'rate_limit:' . $ip;
$maxAttempts = 5; // Максимальное количество попыток
$decayMinutes = 1; // Время блокировки в минутах
// Получаем текущее количество попыток
$attempts = Cache::get($key, 0);
if ($attempts >= $maxAttempts) {
return response()->json(['message' => 'Слишком много запросов, пожалуйста, попробуйте позже.'], 429);
}
// Увеличиваем количество попыток
Cache::put($key, $attempts + 1, $decayMinutes * 60);
return $next($request);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
@@ -0,0 +1,19 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Requests\Request;
use Closure;
use Illuminate\Http\Request as LaravelRequest;
class TransformRequestMiddleware
{
public function handle(LaravelRequest $request, Closure $next)
{
// Преобразуем стандартный Request в ваш кастомный
$customRequest = Request::createFrom($request);
return $next($customRequest);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\TrimStrings as AbstractMiddleware;
class TrimStrings extends AbstractMiddleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\TrustHosts as AbstractMiddleware;
class TrustHosts extends AbstractMiddleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\TrustProxies as AbstractMiddleware;
use Illuminate\Http\Request;
class TrustProxies extends AbstractMiddleware
{
/**
* 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;
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\ValidateSignature as AbstractMiddleware;
class ValidateSignature extends AbstractMiddleware
{
/**
* 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',
];
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Ship\Middleware;
use App\Ship\Abstracts\Middleware\VerifyCsrfToken as AbstractMiddleware;
class VerifyCsrfToken extends AbstractMiddleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Models;
use App\Ship\Abstracts\Models\Builder as AbstractBuilder;
class Builder extends AbstractBuilder
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Models;
use App\Ship\Abstracts\Models\BaseModel as AbstractBaseModel;
class Model extends AbstractBaseModel
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Models;
use App\Ship\Abstracts\Models\MorphPivot as AbstractMorphPivot;
class MorphPivot extends AbstractMorphPivot
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Models;
use App\Ship\Abstracts\Models\Pivot as AbstractPivot;
class Pivot extends AbstractPivot
{
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace App\Ship\Models;
interface Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param Builder $builder
* @param Model $model
* @return void
*/
public function apply(Builder $builder, Model $model);
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace App\Ship\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Seo extends Model
{
use HasFactory;
protected $fillable = ['title', 'description'];
public function seoable()
{
return $this->morphTo();
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace App\Ship\Models;
use App\Containers\User\Models\User;
use App\Ship\Abstracts\Models\UserModel as AbstractUserModel;
use BezhanSalleh\FilamentShield\FilamentShield;
use BezhanSalleh\FilamentShield\Support\Utils;
use BezhanSalleh\FilamentShield\Traits\HasPanelShield;
use Filament\Tables\Columns\Layout\Panel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
class UserModel extends AbstractUserModel
{
use HasApiTokens, HasFactory, Notifiable, HasRoles, HasPanelShield;
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
protected static function booted(): void
{
if (config('filament-shield.dashboard_user.enabled', false)) {
FilamentShield::createRole(name: config('filament-shield.dashboard_user.name', 'dashboard_user'));
FilamentShield::createRole(name: config('', 'editor'));
static::created(function (User $user) {
$user->assignRole(config('filament-shield.dashboard_user.name', 'dashboard_user'));
});
static::deleting(function (User $user) {
$user->assignRole(config('filament-shield.dashboard_user.name', 'dashboard_user'));
});
}
}
public function canAccessPanel(Panel|\Filament\Panel $panel): bool
{
return match ($panel->getId()) {
"admin" => $this->hasRole(Utils::getSuperAdminName()),
"dashboard" => $this->hasRole(config('filament-shield.dashboard_user.name', 'dashboard_user'))
|| $this->hasRole(Utils::getSuperAdminName())
|| $this->hasRole(config('filament-shield.invited_user.name', 'invited_user')),
default => false,
};
}
}
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Notifications\Messages;
use App\Ship\Abstracts\Notifications\Messages\MailMessage as AbstractsMailMessage;
class MailMessage extends AbstractsMailMessage
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Notifications;
use App\Ship\Abstracts\Notifications\Notification as AbstractsNotification;
class Notification extends AbstractsNotification
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Policies;
use App\Ship\Abstracts\Policies\Policy as AbstractsPolicy;
class Policy extends AbstractsPolicy
{
}
@@ -0,0 +1,30 @@
<?php
namespace App\Ship\Providers;
// use Illuminate\Support\Facades\Gate;
use App\Ship\Abstracts\Providers\AuthServiceProvider as AbstractAuthServiceProvider;
class AuthServiceProvider extends AbstractAuthServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}
@@ -0,0 +1,21 @@
<?php
namespace App\Ship\Providers;
use Illuminate\Support\Facades\Broadcast;
use App\Ship\Abstracts\Providers\ServiceProvider as AbstractServiceProvider;
class BroadcastServiceProvider extends AbstractServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}
@@ -0,0 +1,42 @@
<?php
namespace App\Ship\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use App\Ship\Abstracts\Providers\EventServiceProvider as AbstractEventServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends AbstractEventServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*
* @return bool
*/
public function shouldDiscoverEvents()
{
return false;
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Ship\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use App\Ship\Abstracts\Providers\RouteServiceProvider as AbstractRouteServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
class RouteServiceProvider extends AbstractRouteServiceProvider
{
/**
* The path to the "home" route for your application.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*
* @return void
*/
public function boot()
{
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Requests;
use App\Ship\Abstracts\Requests\FormRequest as AbstractFormRequest;
class FormRequest extends AbstractFormRequest
{
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Ship\Requests;
use App\Ship\Abstracts\Requests\Request as AbstractRequest;
class Request extends AbstractRequest
{
}
@@ -0,0 +1,23 @@
<?php
namespace App\Ship\Resources\Breadcrumb;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ClientBreadcrumbPage extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'title' => $this->title,
'slug' => $this->slug,
'path' => $this->path
];
}
}

Some files were not shown because too many files have changed in this diff Show More