From 6192250c8bf862087e98037271aa7b9b374f9301 Mon Sep 17 00:00:00 2001 From: Timur Date: Wed, 20 Aug 2025 19:38:35 +0200 Subject: [PATCH 1/9] =?UTF-8?q?update:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=B1=D0=B0=D0=B7=D0=BE=D0=B2=D1=83=D1=8E=20?= =?UTF-8?q?=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=83=20=D0=B2=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-docker-compose.yml | 93 +++++++ www/.env.example | 2 + www/app/Modules/Admin/Admin.php | 39 +++ .../Modules/Admin/Controllers/Controller.php | 8 + .../Admin/Controllers/MainController.php | 10 + .../Admin/Middleware/PremisonalRole.php | 33 +++ www/app/Modules/Admin/Routes/Route.php | 40 +++ www/app/Modules/Master/Models/Role.php | 25 ++ www/app/Modules/Master/Models/User.php | 6 + .../Master/Repository/RoleRepository.php | 83 ++++++ www/database/factories/RoleFactory.php | 23 ++ ...04_12_000000_create_orchid_users_table.php | 29 -- ... 2015_10_19_214424_create_roles_table.php} | 5 +- ..._214425_create_orchid_role_users_table.php | 38 --- ... 2016_01_01_000000_create_users_table.php} | 5 +- www/database/seeders/DatabaseSeeder.php | 23 +- www/resources/css/style.css | 57 ++-- www/resources/views/admin/index.blade.php | 254 ++++++++++++++++++ www/resources/views/app/admin.blade.php | 107 ++++++++ www/routes/web.php | 4 + 20 files changed, 791 insertions(+), 93 deletions(-) create mode 100755 dev-docker-compose.yml create mode 100644 www/app/Modules/Admin/Admin.php create mode 100644 www/app/Modules/Admin/Controllers/Controller.php create mode 100644 www/app/Modules/Admin/Controllers/MainController.php create mode 100644 www/app/Modules/Admin/Middleware/PremisonalRole.php create mode 100644 www/app/Modules/Admin/Routes/Route.php create mode 100644 www/app/Modules/Master/Models/Role.php create mode 100644 www/app/Modules/Master/Repository/RoleRepository.php create mode 100644 www/database/factories/RoleFactory.php delete mode 100644 www/database/migrations/2015_04_12_000000_create_orchid_users_table.php rename www/database/migrations/{2015_10_19_214424_create_orchid_roles_table.php => 2015_10_19_214424_create_roles_table.php} (86%) delete mode 100644 www/database/migrations/2015_10_19_214425_create_orchid_role_users_table.php rename www/database/migrations/{0001_01_01_000000_create_users_table.php => 2016_01_01_000000_create_users_table.php} (93%) create mode 100644 www/resources/views/admin/index.blade.php create mode 100644 www/resources/views/app/admin.blade.php diff --git a/dev-docker-compose.yml b/dev-docker-compose.yml new file mode 100755 index 0000000..a46d50d --- /dev/null +++ b/dev-docker-compose.yml @@ -0,0 +1,93 @@ +version: "3" + +services: + web: + build: + context: . + dockerfile: ./etc/nginx/nginx.Dockerfile + container_name: beautiful-web + ports: + - '4444:80' + volumes: + - ./www:/var/www + - ./certbot/www/:/var/www/certbot + - ./certbot/conf/:/etc/letsencrypt/ + depends_on: + - php + networks: + - beautiful-net + php: + build: + context: . + dockerfile: ./etc/php/php.Dockerfile + ports: + - '9000:9000' + depends_on: + - cache + - db + volumes: + - ./www:/var/www + networks: + - beautiful-net + + cache: + build: + context: . + dockerfile: ./etc/redis/redis.Dockerfile + ports: + - '6379:6379' + networks: + - beautiful-net + volumes: + - volume-cache:/data + + db: + build: + context: . + dockerfile: ./etc/postgresql/postgres.Dockerfile + env_file: + - ./etc/postgresql/.env + ports: + - '5432:5432' + volumes: + - volume-db:/var/lib/postgresql/data + networks: + - beautiful-net + + s3: + image: adobe/s3mock + restart: unless-stopped + environment: + - debug=true + - COM_ADOBE_TESTING_S3MOCK_STORE_INITIAL_BUCKETS=laravel + - COM_ADOBE_TESTING_S3MOCK_STORE_RETAIN_FILES_ON_EXIT=true + - COM_ADOBE_TESTING_S3MOCK_STORE_ROOT=containers3root + ports: + - '9090:9090' + volumes: + - ./locals3root:/containers3root + networks: + - beautiful-net + + certbot: + image: certbot/certbot:latest + volumes: + - ./certbot/www/:/var/www/certbot/:rw + - ./certbot/conf/:/etc/letsencrypt/:rw + networks: + - beautiful-net + + adminer: + image: adminer + ports: + - '8080:8080' + networks: + - beautiful-net + +networks: + beautiful-net: + driver: bridge + +volumes: + volume-db: + volume-cache: diff --git a/www/.env.example b/www/.env.example index db5dd27..221b3aa 100644 --- a/www/.env.example +++ b/www/.env.example @@ -5,6 +5,8 @@ APP_DEBUG=true APP_TIMEZONE=UTC APP_URL=http://localhost +ADMIN_PASSWORD=password + APP_LOCALE=en APP_FALLBACK_LOCALE=en APP_FAKER_LOCALE=en_US diff --git a/www/app/Modules/Admin/Admin.php b/www/app/Modules/Admin/Admin.php new file mode 100644 index 0000000..c5bdac5 --- /dev/null +++ b/www/app/Modules/Admin/Admin.php @@ -0,0 +1,39 @@ +name; + } + + public function getVersion(): string + { + return $this->version; + } +} \ No newline at end of file diff --git a/www/app/Modules/Admin/Controllers/Controller.php b/www/app/Modules/Admin/Controllers/Controller.php new file mode 100644 index 0000000..e13a3e6 --- /dev/null +++ b/www/app/Modules/Admin/Controllers/Controller.php @@ -0,0 +1,8 @@ +check()) { + return redirect()->route('auth.login'); + } + + $role = auth()->user()->role()->get(); + $permissions = array_column($role->toArray(), 'permissions'); + + if(!in_array(1, $permissions)) { + return redirect()->route('master.home'); + } + + return $next($request); + } +} diff --git a/www/app/Modules/Admin/Routes/Route.php b/www/app/Modules/Admin/Routes/Route.php new file mode 100644 index 0000000..a953712 --- /dev/null +++ b/www/app/Modules/Admin/Routes/Route.php @@ -0,0 +1,40 @@ +middleware(['auth', PremisonalRole::class]) + ->prefix('admin') + ->group(fn() => self::routers()) + ->name('admin.routes'); + } + + private static function routers(): void + { + self::get('/', function(){ + return view('admin.index'); + }); + + self::get('/users', function(){ + return view('admin.index'); + }); + + self::get('/roles', function(){ + return view('admin.index'); + }); + + self::get('/post', function(){ + return view('admin.index'); + }); + + self::get('/comments', function(){ + return view('admin.index'); + }); + } +} diff --git a/www/app/Modules/Master/Models/Role.php b/www/app/Modules/Master/Models/Role.php new file mode 100644 index 0000000..84e78fa --- /dev/null +++ b/www/app/Modules/Master/Models/Role.php @@ -0,0 +1,25 @@ +BelongsTo(User::class, 'role_id', 'id'); + } +} diff --git a/www/app/Modules/Master/Models/User.php b/www/app/Modules/Master/Models/User.php index fea09d4..4c56ae7 100644 --- a/www/app/Modules/Master/Models/User.php +++ b/www/app/Modules/Master/Models/User.php @@ -8,6 +8,7 @@ use App\Modules\Profile\Models\Post; use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -69,6 +70,11 @@ function like(): HasMany return $this->hasMany(Like::class); } + function role(): BelongsTo + { + return $this->belongsTo(Role::class, 'role_id'); + } + /** * Get the attributes that should be cast. * diff --git a/www/app/Modules/Master/Repository/RoleRepository.php b/www/app/Modules/Master/Repository/RoleRepository.php new file mode 100644 index 0000000..6354f2c --- /dev/null +++ b/www/app/Modules/Master/Repository/RoleRepository.php @@ -0,0 +1,83 @@ +name = $data['name']; + $role->slug = $data['slug']; + $role->permissions = $data['permissions']; + + return $role->save(); + } + + /** + * @inheritDoc + */ + public static function remove(array $data): bool + { + if(!isset($data['id'])) { + throw new \InvalidArgumentException('Required (id) parameter is missing'); + } + + if(!is_int($data['id'])) { + throw new \InvalidArgumentException('Required (id) parameter must be integer'); + } + + + return Role::query()->where('id', $data['id'])->delete(); + } + + /** + * @inheritDoc + */ + public static function update(array $data): int + { + // TODO: Implement update() method. + } + + /** + * @inheritDoc + */ + public static function getAll(): Collection + { + return Role::all(); + } + + /** + * @inheritDoc + */ + public static function getById(int $id): Role|false + { + return Role::query()->find($id); + } + + /** + * @inheritDoc + */ + public static function getBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): Collection + { + return Collection::make([]); + } +} diff --git a/www/database/factories/RoleFactory.php b/www/database/factories/RoleFactory.php new file mode 100644 index 0000000..58fc72b --- /dev/null +++ b/www/database/factories/RoleFactory.php @@ -0,0 +1,23 @@ + $this->faker->randomNumber(), + 'name' => $this->faker->name(), + 'slug' => $this->faker->slug(), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/www/database/migrations/2015_04_12_000000_create_orchid_users_table.php b/www/database/migrations/2015_04_12_000000_create_orchid_users_table.php deleted file mode 100644 index be1eb0b..0000000 --- a/www/database/migrations/2015_04_12_000000_create_orchid_users_table.php +++ /dev/null @@ -1,29 +0,0 @@ -string('name')->nullable(); - $table->jsonb('permissions')->nullable(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::table('users', function (Blueprint $table) { - $table->dropColumn(['name', 'permissions']); - }); - } -}; diff --git a/www/database/migrations/2015_10_19_214424_create_orchid_roles_table.php b/www/database/migrations/2015_10_19_214424_create_roles_table.php similarity index 86% rename from www/database/migrations/2015_10_19_214424_create_orchid_roles_table.php rename to www/database/migrations/2015_10_19_214424_create_roles_table.php index 6aded4f..e4551b1 100644 --- a/www/database/migrations/2015_10_19_214424_create_orchid_roles_table.php +++ b/www/database/migrations/2015_10_19_214424_create_roles_table.php @@ -12,10 +12,11 @@ public function up(): void { Schema::create('roles', function (Blueprint $table): void { - $table->increments('id'); + $table->id(); $table->string('slug')->unique(); $table->string('name'); - $table->jsonb('permissions')->nullable(); + $table->integer('permissions')->nullable(); + $table->timestamps(); }); } diff --git a/www/database/migrations/2015_10_19_214425_create_orchid_role_users_table.php b/www/database/migrations/2015_10_19_214425_create_orchid_role_users_table.php deleted file mode 100644 index 4228819..0000000 --- a/www/database/migrations/2015_10_19_214425_create_orchid_role_users_table.php +++ /dev/null @@ -1,38 +0,0 @@ -unsignedBigInteger('user_id'); - $table->unsignedInteger('role_id'); - $table->primary(['user_id', 'role_id']); - $table->foreign('user_id') - ->references('id') - ->on('users') - ->onUpdate('cascade') - ->onDelete('cascade'); - $table->foreign('role_id') - ->references('id') - ->on('roles') - ->onUpdate('cascade') - ->onDelete('cascade'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('role_users'); - } -}; diff --git a/www/database/migrations/0001_01_01_000000_create_users_table.php b/www/database/migrations/2016_01_01_000000_create_users_table.php similarity index 93% rename from www/database/migrations/0001_01_01_000000_create_users_table.php rename to www/database/migrations/2016_01_01_000000_create_users_table.php index 80bc551..94c3e79 100644 --- a/www/database/migrations/0001_01_01_000000_create_users_table.php +++ b/www/database/migrations/2016_01_01_000000_create_users_table.php @@ -21,8 +21,11 @@ public function up(): void $table->string('picture')->nullable(); $table->string('description')->nullable(); $table->json('socialnetworks')->nullable(); + $table->integer('role_id'); + $table->foreign('role_id')->references('id')->on('roles'); + $table->rememberToken(); - + $table->timestamps(); }); diff --git a/www/database/seeders/DatabaseSeeder.php b/www/database/seeders/DatabaseSeeder.php index 89582ce..551e687 100644 --- a/www/database/seeders/DatabaseSeeder.php +++ b/www/database/seeders/DatabaseSeeder.php @@ -6,6 +6,7 @@ use App\Modules\Profile\Models\Comment; use App\Modules\Profile\Models\Like; use App\Modules\Profile\Models\Post; +use Couchbase\Role; use Illuminate\Database\Seeder; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; @@ -19,13 +20,29 @@ public function run(): void { // User::factory(10)->create(); + \App\Modules\Master\Models\Role::create([ + 'name' => 'admin', + 'slug' => 'admin', + 'permissions' => 1 + ]); + + \App\Modules\Master\Models\Role::create([ + 'name' => 'user', + 'slug' => 'user', + 'permissions' => 3 + ]); + User::factory()->create([ 'username' => 'admin', 'email' => 'admin@admin.com', + 'password' => env('ADMIN_PASSWORD'), + 'role_id' => 1 ]); - Post::factory(10)->create(); - Comment::factory(30)->create(); - Like::factory(10)->create(); + if(env('APP_ENV') !== 'production') { + Post::factory(10)->create(); + Comment::factory(30)->create(); + Like::factory(10)->create(); + } } } diff --git a/www/resources/css/style.css b/www/resources/css/style.css index a9a7114..f1c0910 100644 --- a/www/resources/css/style.css +++ b/www/resources/css/style.css @@ -186,26 +186,43 @@ a.posts { background-color: #d9dbde; } -/*main {*/ -/* padding-top: 29px; !* Высота навбара по умолчанию *!*/ -/* flex: 1 0 auto; !* Растягиваем контент *!*/ -/*}*/ - -/*html, body {*/ -/* height: 100%;*/ -/* margin: 0;*/ -/*}*/ - -/*body {*/ -/* display: flex;*/ -/* flex-direction: column;*/ -/* min-height: 150vh;*/ -/*}*/ - -/*footer {*/ -/* flex-shrink: 0; !* Фиксируем футер *!*/ -/* margin-top: auto; !* Прижимаем вниз *!*/ -/*}*/ +/* Admin panel */ +.sidebar { + min-height: calc(100vh - 56px); + background-color: #f8f9fa; + border-right: 1px solid #dee2e6; +} +.sidebar .nav-link { + color: #333; + border-radius: 0; +} +.sidebar .nav-link:hover { + background-color: #e9ecef; +} +.sidebar .nav-link.active { + background-color: #0d6efd; + color: white; +} +.content-header { + background-color: #f8f9fa; + padding: 15px 0; + margin-bottom: 20px; + border-bottom: 1px solid #dee2e6; +} +.stats-card { + transition: transform 0.2s; +} +.stats-card:hover { + transform: translateY(-5px); + box-shadow: 0 4px 8px rgba(0,0,0,0.1); +} +.navbar-brand { + font-weight: 600; +} +.table-actions { + white-space: nowrap; + width: 100px; +} @media (max-width: 1200px) { .responsive-element { diff --git a/www/resources/views/admin/index.blade.php b/www/resources/views/admin/index.blade.php new file mode 100644 index 0000000..2e8c3ff --- /dev/null +++ b/www/resources/views/admin/index.blade.php @@ -0,0 +1,254 @@ +@extends('app/admin') + +@section('content') +
+
+
+

Дашборд

+
+
+ +
+
+
+ + +
+
+
+
+
+
+
Пользователи
+

1,254

+

12% за месяц

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
Статьи
+

542

+

8% за месяц

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
Комментарии
+

2,841

+

3% за месяц

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
Посетители
+

12,587

+

24% за месяц

+
+
+
+ +
+
+
+
+
+
+
+ + +
+
+
Последние статьи
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDЗаголовокАвторКатегорияДатаСтатусДействия
125Новые возможности Laravel 11Иван ПетровРазработка12.05.2023Опубликовано + + +
124Оптимизация запросов к базе данныхМария СидороваБазы данных10.05.2023Опубликовано + + +
123Введение в Bootstrap 5Алексей ИвановВеб-дизайн08.05.2023Черновик + + +
122Создание API на LaravelИван ПетровРазработка05.05.2023Опубликовано + + +
121Основы работы с Vue.jsСергей КузнецовJavaScript02.05.2023На проверке + + +
+
+ + + +
+
+ + +
+
+
+
+
Активность
+
+
+
    +
  • +
    +
    + ... +
    +
    +

    Иван Петров добавил новую статью

    + 2 часа назад +
    +
    +
  • +
  • +
    +
    + ... +
    +
    +

    Мария Сидорова обновила свой профиль

    + 5 часов назад +
    +
    +
  • +
  • +
    +
    + ... +
    +
    +

    Алексей Иванов опубликовал комментарий

    + Вчера в 15:45 +
    +
    +
  • +
+
+
+
+
+
+
+
Статистика посещений
+
+
+
+

Здесь будет график статистики посещений

+
+ +
+
+
+
+
+
+@endsection diff --git a/www/resources/views/app/admin.blade.php b/www/resources/views/app/admin.blade.php new file mode 100644 index 0000000..4954096 --- /dev/null +++ b/www/resources/views/app/admin.blade.php @@ -0,0 +1,107 @@ + + + + + + Admin panel + @vite([ + 'resources/css/app.css', + 'resources/js/app.js', + ]) + + + + + +
+
+ + + + +
+ @yield('content') + + +
+
+
+
+
+ + + + diff --git a/www/routes/web.php b/www/routes/web.php index a017997..0362dc0 100644 --- a/www/routes/web.php +++ b/www/routes/web.php @@ -4,6 +4,7 @@ $auth = new \App\Modules\Auth\Auth; $profile = new \App\Modules\Profile\Profile; $search = new \App\Modules\Search\Search; +$admin = new \App\Modules\Admin\Admin; $master->registerRoutes() ->enable(); @@ -16,3 +17,6 @@ $search->registerRoutes() ->enable(); + +$admin->registerRoutes() + ->enable(); From 2fc852e42e6f6175805a549e5bc093f52ab485d3 Mon Sep 17 00:00:00 2001 From: Timur Date: Wed, 20 Aug 2025 23:54:21 +0200 Subject: [PATCH 2/9] =?UTF-8?q?update:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20=D1=81=D0=BE=D0=B7=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F,=20=D0=B0=20=D1=82=D0=B0=D0=BA=D0=B6?= =?UTF-8?q?=D0=B5=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B0?= =?UTF-8?q?=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81?= =?UTF-8?q?=D0=BA=D1=83=D1=8E=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8E=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/github-actions.yml | 2 +- .../Admin/Controllers/AdminController.php | 14 + .../Contents/UsersAdminController.php | 52 ++++ .../Modules/Admin/Controllers/Controller.php | 103 ++++++- .../Admin/Controllers/MainController.php | 10 - www/app/Modules/Admin/Routes/Route.php | 17 +- .../Controllers/RegistrationController.php | 1 + .../Master/Lib/TableValidatorService.php | 155 +++++++++++ www/app/Modules/Master/Models/User.php | 2 - .../Master/Repository/UserRepository.php | 15 +- .../2016_01_01_000000_create_users_table.php | 2 +- .../2024_08_12_134940_create_posts_table.php | 8 +- www/resources/js/script.js | 158 +++++++++++ .../views/admin/content/detail.blade.php | 105 +++++++ .../views/admin/content/index.blade.php | 113 ++++++++ www/resources/views/admin/index.blade.php | 263 +----------------- .../views/admin/login/index.blade.php | 72 +++++ www/resources/views/app/admin.blade.php | 126 ++++----- 18 files changed, 862 insertions(+), 356 deletions(-) create mode 100644 www/app/Modules/Admin/Controllers/AdminController.php create mode 100644 www/app/Modules/Admin/Controllers/Contents/UsersAdminController.php delete mode 100644 www/app/Modules/Admin/Controllers/MainController.php create mode 100644 www/app/Modules/Master/Lib/TableValidatorService.php create mode 100755 www/resources/views/admin/content/detail.blade.php create mode 100755 www/resources/views/admin/content/index.blade.php mode change 100644 => 100755 www/resources/views/admin/index.blade.php create mode 100755 www/resources/views/admin/login/index.blade.php diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 9d2b9a4..ed35c96 100755 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -25,7 +25,7 @@ jobs: - name: Build app run: - sudo docker-compose up -d --build && + sudo docker-compose -f dev-docker-compose.yml up -d --build && sudo chmod -R 777 $(pwd)/www && sudo docker-compose run php composer update && sudo docker-compose run php composer upgrade && diff --git a/www/app/Modules/Admin/Controllers/AdminController.php b/www/app/Modules/Admin/Controllers/AdminController.php new file mode 100644 index 0000000..6eafed3 --- /dev/null +++ b/www/app/Modules/Admin/Controllers/AdminController.php @@ -0,0 +1,14 @@ +getData($request, 'users', (new UserRepository)); + + $result = parent::index($request, $data); + if($result instanceof View) { + return $result; + } + + $user = new User; + $contents = $this->getContents($user); + + return view('admin.content.index', [ + 'fillables' => $this->getFillables($user), + 'contents' => $contents, + 'currentPage' => $contents->currentPage(), + 'lastPage' => $contents->lastPage(), + 'route' => 'admin.users' + ]); + } + + private function getFillables(User $user): array + { + $fillables = $user->getFillable(); + $fillables = array_merge(['id',], $fillables); + $fillables = array_merge($fillables, ['created_at', 'updated_at']); + + return $fillables; + } + + private function getContents(User $user): LengthAwarePaginator + { + return $user + ->limit(20) + ->paginate(); + } +} diff --git a/www/app/Modules/Admin/Controllers/Controller.php b/www/app/Modules/Admin/Controllers/Controller.php index e13a3e6..8bc9e52 100644 --- a/www/app/Modules/Admin/Controllers/Controller.php +++ b/www/app/Modules/Admin/Controllers/Controller.php @@ -2,7 +2,108 @@ namespace App\Modules\Admin\Controllers; +use App\Interfaces\Repository\RepositoryInterface; +use App\Modules\Master\Lib\TableValidatorService; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Schema; +use Illuminate\View\View; + abstract class Controller { + private array $types = [ + 'int' => 'number', + 'varchar' => 'text', + 'jsonb' => 'object', + 'string' => 'text', + 'text' => 'textarea', + 'integer' => 'number', + 'bigint' => 'number', + 'float' => 'number', + 'double' => 'number', + 'decimal' => 'number', + 'boolean' => 'checkbox', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'json' => 'object', + ]; + + + + public function index(Request $request, array $data = []): View|RedirectResponse|null + { + if($request->isMethod('GET')) { + if( + ($request->has('create') && $request->get('create') === 'Y') || + ($request->has('update') && $request->get('update') === 'Y') + ) { + return $this->show($data); + } + } + + if($request->isMethod('POST')) { + return $this->create($request, $data['validator']); + } + + return null; + } + + public function show(array $data): View + { + if(!isset($data['items']) || count($data['items']) === 0) { + throw new \LogicException('Not found item'); + } + + return view('admin.content.detail', data: $data); + } + + public function create(Request $request, array $validation): RedirectResponse + { + dd($request->all(), $validation); + } + + public function getData(Request $request, string $table, RepositoryInterface $repository): array + { + $model = null; + if($request->has('id') && $request->get('id') > 0) { + $model = $repository::getAll($request->get('id')); + } + + $data = [ + 'method' => 'POST', + 'items' => [], + 'validator' => (new TableValidatorService)->generateValidationRulesTable($table), + ]; + + $columns = Schema::getColumnListing($table); + foreach($columns as $column) { + $type = Schema::getColumnType($table, $column); + + if ($column === 'created_at' || $column === 'updated_at') { + continue; + } + + if(str_contains($type, 'int')) { + $type = 'number'; + } else { + $type = $this->types[$type]; + } + + $data['items'][$column]['column'] = $column; + $data['items'][$column]['type'] = $type; + } + + if($model) { + $data['method'] = 'PATCH'; + foreach($model->first()->toArray() as $fillable => $item) { + $data['items'][$fillable] = array_merge( + $data['items'][$fillable], + ['value' => $item,] + ); + } + } -} \ No newline at end of file + return $data; + } +} diff --git a/www/app/Modules/Admin/Controllers/MainController.php b/www/app/Modules/Admin/Controllers/MainController.php deleted file mode 100644 index 86c8bab..0000000 --- a/www/app/Modules/Admin/Controllers/MainController.php +++ /dev/null @@ -1,10 +0,0 @@ -name('admin.home'); - self::get('/users', function(){ - return view('admin.index'); - }); + self::match( + ['GET', 'POST', 'PATCH', 'DELETE',], + '/users', 'Contents\UsersAdminController@index') + ->name('admin.users'); self::get('/roles', function(){ return view('admin.index'); - }); + })->name('admin.roles'); self::get('/post', function(){ return view('admin.index'); - }); + })->name('admin.posts'); self::get('/comments', function(){ return view('admin.index'); - }); + })->name('admin.comments'); } } diff --git a/www/app/Modules/Auth/Controllers/RegistrationController.php b/www/app/Modules/Auth/Controllers/RegistrationController.php index 342ba5a..88c8587 100644 --- a/www/app/Modules/Auth/Controllers/RegistrationController.php +++ b/www/app/Modules/Auth/Controllers/RegistrationController.php @@ -38,6 +38,7 @@ function store(Request $request): RedirectResponse 'username' => $data['username'], 'email' => $data['email'], 'password' => bcrypt($data['password']), + 'role_id' => 2, ]); if(!$isSaved) { return back()->withErrors('Failed to registration user'); diff --git a/www/app/Modules/Master/Lib/TableValidatorService.php b/www/app/Modules/Master/Lib/TableValidatorService.php new file mode 100644 index 0000000..1ab11e8 --- /dev/null +++ b/www/app/Modules/Master/Lib/TableValidatorService.php @@ -0,0 +1,155 @@ + 'text', + 'text' => 'textarea', + 'integer' => 'number', + 'bigint' => 'number', + 'float' => 'number', + 'double' => 'number', + 'decimal' => 'number', + 'boolean' => 'checkbox', + 'date' => 'date', + 'datetime' => 'datetime', + 'timestamp' => 'datetime', + 'json' => 'object', + ]; + + public function generateValidationRulesTable(string $table, $ignoreId = null): array + { + $rules = []; + $columns = Schema::getColumnListing($table); + + foreach ($columns as $column) { + if ($column === 'id' || $column === 'created_at' || $column === 'updated_at') { + continue; + } + + $rules[$column] = $this->generateValidationRules($table, $column); + + foreach ($rules[$column] as $key => $rule) { + if (is_string($rule) && strpos($rule, 'unique:') === 0 && $ignoreId) { + $rules[$column][$key] = Rule::unique($table, $column)->ignore($ignoreId); + } + } + } + + return $rules; + } + + protected function generateValidationRules(string $table, string $column): array + { + $rules = []; + $type = Schema::getColumnType($table, $column); + + switch ($type) { + case 'integer': + case 'bigint': + $rules[] = 'integer'; + break; + case 'float': + case 'double': + case 'decimal': + $rules[] = 'numeric'; + break; + case 'boolean': + $rules[] = 'boolean'; + break; + case 'date': + $rules[] = 'date'; + break; + case 'datetime': + case 'timestamp': + $rules[] = 'date_format:Y-m-d H:i:s'; + break; + case 'json': + $rules[] = 'json'; + break; + default: + $rules[] = 'string'; + } + + $nullable = $this->isColumnNullable($table, $column); + if (!$nullable) { + array_unshift($rules, 'required'); + } else { + $rules[] = 'nullable'; + } + + if (in_array($type, ['string', 'text'])) { + $maxLength = $this->getColumnMaxLength($table, $column); + if ($maxLength) { + $rules[] = "max:{$maxLength}"; + } + } + + if ($column !== 'id' && $this->isColumnUnique($table, $column)) { + $rules[] = 'unique:' . $table . ',' . $column; + } + + return $rules; + } + + protected function isColumnNullable(string $table, string $column): bool + { + if(!Schema::hasColumn($table, $column)) { + return false; + } + + $columnsSchema = Schema::getColumns($table); + foreach($columnsSchema as $columnSchema) { + if($columnSchema['name'] === $column) { + return $columnSchema['nullable']; + } + } + + return false; + } + + protected function getColumnMaxLength(string $table, string $column): ?int + { + if(!Schema::hasColumn($table, $column)) { + return null; + } + + $columnsSchema = Schema::getColumns($table); + foreach($columnsSchema as $columnSchema) { + if($columnSchema['name'] === $column) { + if($columnSchema['type_name'] == 'varchar') { + $number = str_replace('varying(', $columnSchema['type']); + $number = str_replace(')', '', $number); + + return (int)$number; + } + + return 255; + } + } + + return null; + } + + protected function isColumnUnique(string $table, string $column): bool + { + if(!Schema::hasColumn($table, $column)) { + return false; + } + + $columnsSchema = Schema::getColumns($table); + foreach($columnsSchema as $columnSchema) { + if($columnSchema['name'] === $column) { + return str_contains('unique', $columnSchema['type']) || (isset($columnSchema['unique']) && $columnSchema['unique']); + } + } + + return false; + } +} diff --git a/www/app/Modules/Master/Models/User.php b/www/app/Modules/Master/Models/User.php index 4c56ae7..629cb46 100644 --- a/www/app/Modules/Master/Models/User.php +++ b/www/app/Modules/Master/Models/User.php @@ -23,14 +23,12 @@ class User extends Authenticatable * @var array */ protected $fillable = [ - 'name', 'username', 'email', 'password', 'remember_token', 'picture', 'description', - 'permissions', 'email_verified_at', 'socialnetworks', ]; diff --git a/www/app/Modules/Master/Repository/UserRepository.php b/www/app/Modules/Master/Repository/UserRepository.php index d45340e..e787e97 100644 --- a/www/app/Modules/Master/Repository/UserRepository.php +++ b/www/app/Modules/Master/Repository/UserRepository.php @@ -13,15 +13,20 @@ final class UserRepository implements UserRepositoryInterface { public static function save(array $data): bool { - if(!isset($data['password']) || !isset($data['username']) || !isset($data['email'])) { - throw new InvalidArgumentException('Required (password, name, email) parameter is missing'); + if( + !isset($data['password']) || + !isset($data['username']) || + !isset($data['email']) || + !isset($data['role_id']) + ) { + throw new InvalidArgumentException('Required (password, name, email, role_id) parameter is missing'); } $user = new User; - $user->username = $data['username']; - $user->email = $data['email']; - $user->password = $data['password']; + foreach($data as $key => $value) { + $user->{$key} = $value; + } return $user->save(); } diff --git a/www/database/migrations/2016_01_01_000000_create_users_table.php b/www/database/migrations/2016_01_01_000000_create_users_table.php index 94c3e79..5e2bc49 100644 --- a/www/database/migrations/2016_01_01_000000_create_users_table.php +++ b/www/database/migrations/2016_01_01_000000_create_users_table.php @@ -19,7 +19,7 @@ public function up(): void $table->string('password'); $table->date('email_verified_at')->nullable(); $table->string('picture')->nullable(); - $table->string('description')->nullable(); + $table->text('description')->nullable(); $table->json('socialnetworks')->nullable(); $table->integer('role_id'); $table->foreign('role_id')->references('id')->on('roles'); diff --git a/www/database/migrations/2024_08_12_134940_create_posts_table.php b/www/database/migrations/2024_08_12_134940_create_posts_table.php index 23eeeca..69960ce 100644 --- a/www/database/migrations/2024_08_12_134940_create_posts_table.php +++ b/www/database/migrations/2024_08_12_134940_create_posts_table.php @@ -13,12 +13,12 @@ public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); - + $table->string('name'); - $table->string('description'); + $table->text('description'); $table->string('file'); - - + + $table->unsignedBigInteger('user_id'); $table->foreign('user_id') ->references('id') diff --git a/www/resources/js/script.js b/www/resources/js/script.js index 39e03da..ec5fbc7 100644 --- a/www/resources/js/script.js +++ b/www/resources/js/script.js @@ -15,4 +15,162 @@ document.addEventListener('DOMContentLoaded', function(){ }); }); + if(window.location.href.includes('/admin')) { + const uploadFile = document.getElementById('uploadFile'); + const previewImage = document.getElementById('previewImage'); + const removeImage = document.getElementById('removeImage'); + const uploadContainer = document.getElementById('uploadContainer'); + + if (uploadFile && previewImage && removeImage) { + uploadFile.addEventListener('change', function(e) { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = function(e) { + previewImage.src = e.target.result; + previewImage.style.display = 'block'; + removeImage.style.display = 'block'; + }; + reader.readAsDataURL(file); + } + }); + + removeImage.addEventListener('click', function() { + uploadFile.value = ''; + previewImage.src = ''; + previewImage.style.display = 'none'; + removeImage.style.display = 'none'; + }); + + // Обработка перетаскивания файла + uploadContainer.addEventListener('dragover', function(e) { + e.preventDefault(); + this.classList.add('border-primary'); + this.classList.remove('border-dashed'); + }); + + uploadContainer.addEventListener('dragleave', function(e) { + e.preventDefault(); + this.classList.remove('border-primary'); + this.classList.add('border-dashed'); + }); + + uploadContainer.addEventListener('drop', function(e) { + e.preventDefault(); + this.classList.remove('border-primary'); + this.classList.add('border-dashed'); + + if (e.dataTransfer.files.length) { + uploadFile.files = e.dataTransfer.files; + const event = new Event('change'); + uploadFile.dispatchEvent(event); + } + }); + } + + + const keyValueContainer = document.getElementById('keyValueContainer'); + const addKeyValueBtn = document.getElementById('addKeyValueBtn'); + const objectField = document.getElementById('objectField'); + + function addKeyValuePair(key = '', value = '') { + const pairId = Date.now() + Math.random().toString(36).substr(2, 5); + const pairElement = document.createElement('div'); + pairElement.className = 'card mb-2 key-value-pair'; + pairElement.id = `pair-${pairId}`; + pairElement.innerHTML = ` +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ `; + keyValueContainer.appendChild(pairElement); + + const removeBtn = pairElement.querySelector('.remove-pair-btn'); + removeBtn.addEventListener('click', function() { + pairElement.remove(); + updateObjectField(); + }); + + const keyInput = pairElement.querySelector('.key-input'); + const valueInput = pairElement.querySelector('.value-input'); + + keyInput.addEventListener('input', updateObjectField); + valueInput.addEventListener('input', updateObjectField); + } + + function updateObjectField() { + const pairs = document.querySelectorAll('.key-value-pair'); + const data = {}; + + pairs.forEach(pair => { + const keyInput = pair.querySelector('.key-input'); + const valueInput = pair.querySelector('.value-input'); + + if (keyInput.value.trim() !== '') { + data[keyInput.value] = valueInput.value; + } + }); + + objectField.value = JSON.stringify(data); + } + + addKeyValueBtn.addEventListener('click', function() { + addKeyValuePair(); + }); + + // Загрузка существующих данных + try { + const existingData = JSON.parse(objectField.value); + if (existingData && typeof existingData === 'object') { + Object.keys(existingData).forEach(key => { + addKeyValuePair(key, existingData[key]); + }); + } + + if (Object.keys(existingData).length === 0) { + addKeyValuePair(); + } + } catch (e) { + console.error('error unserialze JSON:', e); + addKeyValuePair(); + } + + document.getElementById('objectForm').addEventListener('submit', function(e) { + updateObjectField(); + + const pairs = document.querySelectorAll('.key-value-pair'); + let hasErrors = false; + + pairs.forEach(pair => { + const keyInput = pair.querySelector('.key-input'); + const valueInput = pair.querySelector('.value-input'); + + if (keyInput.value.trim() === '' && valueInput.value.trim() !== '') { + keyInput.classList.add('is-invalid'); + hasErrors = true; + } else { + keyInput.classList.remove('is-invalid'); + } + }); + + if (hasErrors) { + e.preventDefault(); + } + }); + } + }); diff --git a/www/resources/views/admin/content/detail.blade.php b/www/resources/views/admin/content/detail.blade.php new file mode 100755 index 0000000..758de01 --- /dev/null +++ b/www/resources/views/admin/content/detail.blade.php @@ -0,0 +1,105 @@ +@extends('app.admin') + +@section('content') +
+
+ @csrf + @method($method) +
+
+ @foreach($items as $item) + @switch($item['type']) + @case('textarea') +
+ + +
+ @break + @case('text') + @if(!in_array($item['column'], ['picture', 'file',])) +
+ + +
+ @else +
+

{{ $item['column'] }}

+ + +
+ + + +
+ + +
+ + + +
+ +

Upload file

+

Перетащите или кликните для выбора

+
+
+
+ @endif + @break + @case('datetime') +
+ + +
+ @break + @case('object') +
+ + +
+
+ + + + +
+ @break + @endswitch + @endforeach +
+
+ +
+
+ + Cancel + + +
+
+
+
+@endsection diff --git a/www/resources/views/admin/content/index.blade.php b/www/resources/views/admin/content/index.blade.php new file mode 100755 index 0000000..61ac8c0 --- /dev/null +++ b/www/resources/views/admin/content/index.blade.php @@ -0,0 +1,113 @@ +@extends('app.admin') + +@section('content') + + + + +
+
+
+ + + + @foreach($fillables as $fillable) + + @endforeach + + + + @if(isset($contents) && $contents->count() > 0) + + @foreach($contents as $content) + + @foreach($content->toArray() as $fillable => $column) + + @endforeach + + + @endforeach + + @endif +
+ {{ $fillable }} + + Действия +
+
+ @switch($fillable) + @case('description') + + {{ Str::limit($column, 100) }} + + @break + + @case('active') + + {{ $column ? 'Да' : 'Нет' }} + + @break + + @case('user') + {{ $column['username'] ?? '' }} + @break + + @default + @if(is_array($column) || is_object($column)) +
+ Данные по обратной связи +
+                                                                {{ json_encode($column, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) }}
+                                                            
+
+ @break + @endif + + @if(in_array($fillable, $fillables)) + {{ $column ?? '' }} + @endif + @endswitch +
+
+ +
+
+
+
+ + @if(isset($content) && $contents->count() > 0) +
+ +
+ @endif +@endsection diff --git a/www/resources/views/admin/index.blade.php b/www/resources/views/admin/index.blade.php old mode 100644 new mode 100755 index 2e8c3ff..832fdb6 --- a/www/resources/views/admin/index.blade.php +++ b/www/resources/views/admin/index.blade.php @@ -1,254 +1,19 @@ -@extends('app/admin') +@extends('app.admin') @section('content') -
-
-
-

Дашборд

-
-
- -
-
-
- - -
-
-
-
-
-
-
Пользователи
-

1,254

-

12% за месяц

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
Статьи
-

542

-

8% за месяц

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
Комментарии
-

2,841

-

3% за месяц

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
Посетители
-

12,587

-

24% за месяц

-
-
-
- -
-
-
-
-
-
-
- - -
-
-
Последние статьи
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IDЗаголовокАвторКатегорияДатаСтатусДействия
125Новые возможности Laravel 11Иван ПетровРазработка12.05.2023Опубликовано - - -
124Оптимизация запросов к базе данныхМария СидороваБазы данных10.05.2023Опубликовано - - -
123Введение в Bootstrap 5Алексей ИвановВеб-дизайн08.05.2023Черновик - - -
122Создание API на LaravelИван ПетровРазработка05.05.2023Опубликовано - - -
121Основы работы с Vue.jsСергей КузнецовJavaScript02.05.2023На проверке - - -
-
- - - -
-
- - -
-
-
-
-
Активность
-
-
-
    -
  • -
    -
    - ... -
    -
    -

    Иван Петров добавил новую статью

    - 2 часа назад -
    -
    -
  • -
  • -
    -
    - ... -
    -
    -

    Мария Сидорова обновила свой профиль

    - 5 часов назад -
    -
    -
  • -
  • -
    -
    - ... -
    -
    -

    Алексей Иванов опубликовал комментарий

    - Вчера в 15:45 -
    -
    -
  • -
-
-
-
-
-
-
-
Статистика посещений
-
-
-
-

Здесь будет график статистики посещений

-
- -
-
-
-
+ +
+
+
+
Количество пользователей
+
+
+
+
Количество активных услуг
+
+
+
+
Количество активных новостей
@endsection diff --git a/www/resources/views/admin/login/index.blade.php b/www/resources/views/admin/login/index.blade.php new file mode 100755 index 0000000..630d269 --- /dev/null +++ b/www/resources/views/admin/login/index.blade.php @@ -0,0 +1,72 @@ +@extends('app.admin') + +@section('content') +
+
+
+
+ + + +
+

+ Авторизация в панели управления +

+
+ + @if($errors->any()) +
+
+
+ + + +
+
+

+ Ошибка авторизации +

+
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+
+
+
+ @endif + +
+ @csrf +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+@endsection diff --git a/www/resources/views/app/admin.blade.php b/www/resources/views/app/admin.blade.php index 4954096..409ed81 100644 --- a/www/resources/views/app/admin.blade.php +++ b/www/resources/views/app/admin.blade.php @@ -1,107 +1,85 @@ - + - Admin panel - @vite([ - 'resources/css/app.css', - 'resources/js/app.js', - ]) + + Admin Panel + @vite(['resources/css/app.css', 'resources/js/app.js']) - - - - + +
- - +
+ @if($errors->any()) + @foreach($errors->all() as $error) + + @endforeach + @endif + + @if(Session::has('success')) + + @endif - -
- @yield('content') + + @if(\Illuminate\Support\Facades\Auth::check() && (isset($exception) && $exception->getStatusCode() < 400)) +
+

@yield('title')

+ + Выйти + +
+ @endif - -
-
+ @yield('content')
- + + From 3f3bafa2e7420c2adb79e277593e6b2d2592e26b Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 21 Aug 2025 00:43:38 +0200 Subject: [PATCH 3/9] =?UTF-8?q?update:=20=D0=B4=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B0=D0=BB=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contents/UsersAdminController.php | 2 +- .../Modules/Admin/Controllers/Controller.php | 51 +++++++++++++++++-- .../Master/Lib/TableValidatorService.php | 7 ++- .../Profile/Controllers/ProfileController.php | 2 +- .../views/admin/content/detail.blade.php | 17 +++++-- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/www/app/Modules/Admin/Controllers/Contents/UsersAdminController.php b/www/app/Modules/Admin/Controllers/Contents/UsersAdminController.php index 0baecd2..cb4d8dd 100644 --- a/www/app/Modules/Admin/Controllers/Contents/UsersAdminController.php +++ b/www/app/Modules/Admin/Controllers/Contents/UsersAdminController.php @@ -18,7 +18,7 @@ public function index(Request $request, array $data = []): View|RedirectResponse $data = $this->getData($request, 'users', (new UserRepository)); $result = parent::index($request, $data); - if($result instanceof View) { + if($result) { return $result; } diff --git a/www/app/Modules/Admin/Controllers/Controller.php b/www/app/Modules/Admin/Controllers/Controller.php index 8bc9e52..df49359 100644 --- a/www/app/Modules/Admin/Controllers/Controller.php +++ b/www/app/Modules/Admin/Controllers/Controller.php @@ -4,6 +4,7 @@ use App\Interfaces\Repository\RepositoryInterface; use App\Modules\Master\Lib\TableValidatorService; +use App\Modules\S3Storage\Lib\S3Storage; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Schema; @@ -43,7 +44,7 @@ public function index(Request $request, array $data = []): View|RedirectResponse } if($request->isMethod('POST')) { - return $this->create($request, $data['validator']); + return $this->create($request, $data['validator'], $data['repository']); } return null; @@ -58,9 +59,35 @@ public function show(array $data): View return view('admin.content.detail', data: $data); } - public function create(Request $request, array $validation): RedirectResponse - { - dd($request->all(), $validation); + public function create( + Request $request, + array $validation, + RepositoryInterface $repository, + ): RedirectResponse { + $data = $request->validate($validation); + + if($request->has('picture')) { + $data['picture'] = $this->saveFile($data['picture']); + } + + if($request->has('file')) { + $data['file'] = $this->saveFile($data['file']); + } + + $isSave = false; + try { + $isSave = $repository::save($data); + } catch (\Exception $e) { + return back()->withErrors($e->getMessage()); + } + + if(!$isSave) { + return back()->withErrors('Can`t be created element'); + } + + $url = explode('?', url()->current()); + return redirect($url[0]) + ->with('success', 'Created element is complete!'); } public function getData(Request $request, string $table, RepositoryInterface $repository): array @@ -74,13 +101,14 @@ public function getData(Request $request, string $table, RepositoryInterface $re 'method' => 'POST', 'items' => [], 'validator' => (new TableValidatorService)->generateValidationRulesTable($table), + 'repository' => $repository, ]; $columns = Schema::getColumnListing($table); foreach($columns as $column) { $type = Schema::getColumnType($table, $column); - if ($column === 'created_at' || $column === 'updated_at') { + if($column == 'id' || $column === 'created_at' || $column === 'updated_at') { continue; } @@ -106,4 +134,17 @@ public function getData(Request $request, string $table, RepositoryInterface $re return $data; } + + private function saveFile($file) + { + if($file) { + if(!S3Storage::putFile('/', $file)) { + return back()->withErrors("Update profile data failed"); + } + + $file = S3Storage::getFile($file->hashName()); + } + + return $file; + } } diff --git a/www/app/Modules/Master/Lib/TableValidatorService.php b/www/app/Modules/Master/Lib/TableValidatorService.php index 1ab11e8..9f49034 100644 --- a/www/app/Modules/Master/Lib/TableValidatorService.php +++ b/www/app/Modules/Master/Lib/TableValidatorService.php @@ -74,7 +74,12 @@ protected function generateValidationRules(string $table, string $column): array $rules[] = 'json'; break; default: - $rules[] = 'string'; + if(in_array($column, ['picture', 'file'])) { + $rules[] = 'image'; + $rules[] = 'max:30004'; + } else { + $rules[] = 'string'; + } } $nullable = $this->isColumnNullable($table, $column); diff --git a/www/app/Modules/Profile/Controllers/ProfileController.php b/www/app/Modules/Profile/Controllers/ProfileController.php index 2167980..0289a60 100644 --- a/www/app/Modules/Profile/Controllers/ProfileController.php +++ b/www/app/Modules/Profile/Controllers/ProfileController.php @@ -97,7 +97,7 @@ function unflower(User $user): RedirectResponse function update(Request $request): RedirectResponse { $data = $request->validate([ - 'picture' => 'image|max:1004|nullable', + 'picture' => 'image|max:30004|nullable', 'description' => 'string|max:255|nullable', 'patreon' => 'string|url|nullable', 'github' => 'string|url|nullable', diff --git a/www/resources/views/admin/content/detail.blade.php b/www/resources/views/admin/content/detail.blade.php index 758de01..aacc261 100755 --- a/www/resources/views/admin/content/detail.blade.php +++ b/www/resources/views/admin/content/detail.blade.php @@ -9,11 +9,20 @@
@foreach($items as $item) @switch($item['type']) + @case('number') +
+ + +
+ @break @case('textarea')
@break @@ -22,7 +31,7 @@ class="form-control">{{ $item['value'] ?? '' }}
@@ -48,7 +57,7 @@ class="btn btn-sm btn-outline-danger mt-2" style="display: none;"> id="uploadContainer" style="cursor: pointer;"> @case('datetime')
- +
@break @case('object') From c9f4c9465c97a2e56646dab75fe3839c418cf69f Mon Sep 17 00:00:00 2001 From: Timur Date: Thu, 21 Aug 2025 01:01:12 +0200 Subject: [PATCH 4/9] =?UTF-8?q?update:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BE=D1=81=D1=82=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D1=8D=D0=BB=D0=B5=D0=BC=D0=B5=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contents/CommentsAdminController.php | 51 ++++++++++++++++++ .../Contents/PostsAdminController.php | 54 +++++++++++++++++++ .../Contents/RolesAdminController.php | 51 ++++++++++++++++++ www/app/Modules/Admin/Routes/Route.php | 21 ++++---- .../Profile/Repository/CommentRepository.php | 8 +-- .../Profile/Repository/PostRepository.php | 7 ++- www/resources/views/app/admin.blade.php | 5 ++ 7 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 www/app/Modules/Admin/Controllers/Contents/CommentsAdminController.php create mode 100644 www/app/Modules/Admin/Controllers/Contents/PostsAdminController.php create mode 100644 www/app/Modules/Admin/Controllers/Contents/RolesAdminController.php diff --git a/www/app/Modules/Admin/Controllers/Contents/CommentsAdminController.php b/www/app/Modules/Admin/Controllers/Contents/CommentsAdminController.php new file mode 100644 index 0000000..09ffcbb --- /dev/null +++ b/www/app/Modules/Admin/Controllers/Contents/CommentsAdminController.php @@ -0,0 +1,51 @@ +getData($request, 'comments', (new CommentRepository)); + + $result = parent::index($request, $data); + if($result) { + return $result; + } + + $comment = new Comment; + $contents = $this->getContents($comment); + + return view('admin.content.index', [ + 'fillables' => $this->getFillables($comment), + 'contents' => $contents, + 'currentPage' => $contents->currentPage(), + 'lastPage' => $contents->lastPage(), + 'route' => 'admin.comments', + ]); + } + + private function getFillables(Comment $comment): array + { + $fillables = $comment->getFillable(); + $fillables = array_merge(['id',], $fillables); + $fillables = array_merge($fillables, ['created_at', 'updated_at']); + + return $fillables; + } + + private function getContents(Comment $comment): LengthAwarePaginator + { + return $comment->limit(20) + ->paginate(); + } +} diff --git a/www/app/Modules/Admin/Controllers/Contents/PostsAdminController.php b/www/app/Modules/Admin/Controllers/Contents/PostsAdminController.php new file mode 100644 index 0000000..7c97718 --- /dev/null +++ b/www/app/Modules/Admin/Controllers/Contents/PostsAdminController.php @@ -0,0 +1,54 @@ +getData($request, 'posts', (new PostRepository)); + + $result = parent::index($request, $data); + if($result) { + return $result; + } + + $post = new Post; + $contents = $this->getContents($post); + + return view('admin.content.index', [ + 'fillables' => $this->getFillables($post), + 'contents' => $contents, + 'currentPage' => $contents->currentPage(), + 'lastPage' => $contents->lastPage(), + 'route' => 'admin.posts' + ]); + } + + private function getFillables(Post $post): array + { + $fillables = $post->getFillable(); + $fillables = array_merge(['id',], $fillables); + $fillables = array_merge($fillables, ['created_at', 'updated_at']); + + return $fillables; + } + + private function getContents(Post $post): LengthAwarePaginator + { + return $post + ->limit(20) + ->paginate(); + } +} diff --git a/www/app/Modules/Admin/Controllers/Contents/RolesAdminController.php b/www/app/Modules/Admin/Controllers/Contents/RolesAdminController.php new file mode 100644 index 0000000..264f906 --- /dev/null +++ b/www/app/Modules/Admin/Controllers/Contents/RolesAdminController.php @@ -0,0 +1,51 @@ +getData($request, 'roles', (new RoleRepository)); + + $result = parent::index($request, $data); + if($result) { + return $result; + } + + $role = new Role; + $contents = $this->getContents($role); + + return view('admin.content.index', [ + 'fillables' => $this->getFillables($role), + 'contents' => $contents, + 'currentPage' => $contents->currentPage(), + 'lastPage' => $contents->lastPage(), + 'route' => 'admin.roles', + ]); + } + + private function getFillables(Role $role): array + { + $fillables = $role->getFillable(); + $fillables = array_merge(['id',], $fillables); + $fillables = array_merge($fillables, ['created_at', 'updated_at']); + + return $fillables; + } + + private function getContents(Role $role): LengthAwarePaginator + { + return $role->limit(20) + ->paginate(); + } +} diff --git a/www/app/Modules/Admin/Routes/Route.php b/www/app/Modules/Admin/Routes/Route.php index 0cc4c44..4220582 100644 --- a/www/app/Modules/Admin/Routes/Route.php +++ b/www/app/Modules/Admin/Routes/Route.php @@ -24,16 +24,19 @@ private static function routers(): void '/users', 'Contents\UsersAdminController@index') ->name('admin.users'); - self::get('/roles', function(){ - return view('admin.index'); - })->name('admin.roles'); + self::match( + ['GET', 'POST', 'PATCH', 'DELETE',], + '/posts', 'Contents\PostsAdminController@index') + ->name('admin.posts'); - self::get('/post', function(){ - return view('admin.index'); - })->name('admin.posts'); + self::match( + ['GET', 'POST', 'PATCH', 'DELETE',], + '/roles', 'Contents\RolesAdminController@index') + ->name('admin.roles'); - self::get('/comments', function(){ - return view('admin.index'); - })->name('admin.comments'); + self::match( + ['GET', 'POST', 'PATCH', 'DELETE',], + '/comments', 'Contents\CommentsAdminController@index') + ->name('admin.comments'); } } diff --git a/www/app/Modules/Profile/Repository/CommentRepository.php b/www/app/Modules/Profile/Repository/CommentRepository.php index f24c997..e9e8f70 100644 --- a/www/app/Modules/Profile/Repository/CommentRepository.php +++ b/www/app/Modules/Profile/Repository/CommentRepository.php @@ -23,15 +23,15 @@ public static function save(array $data): bool throw new \InvalidArgumentException('Required (user_id, post_id, description) parameters is missing'); } - if(!is_int($data['user_id']) || !is_int($data['post_id'])) { + if(!filter_var($data['user_id'], FILTER_VALIDATE_INT) || !filter_var($data['post_id'], FILTER_VALIDATE_INT)) { throw new \InvalidArgumentException('Required (user_id, post_id) is must be integer'); } $comment = new Comment; - $comment->user_id = $data['user_id']; - $comment->post_id = $data['post_id']; - $comment->description = $data['description']; + foreach($data as $key => $value) { + $comment->{$key} = $value; + } return $comment->save(); } diff --git a/www/app/Modules/Profile/Repository/PostRepository.php b/www/app/Modules/Profile/Repository/PostRepository.php index 5496ba4..b7d5a68 100644 --- a/www/app/Modules/Profile/Repository/PostRepository.php +++ b/www/app/Modules/Profile/Repository/PostRepository.php @@ -28,10 +28,9 @@ public static function save(array $data): bool $post = new Post; - $post->file = $data['file']; - $post->name = $data['name']; - $post->description = $data['description']; - $post->user_id = $data['user_id']; + foreach($data as $key => $value) { + $post->{$key} = $value; + } return $post->save(); } diff --git a/www/resources/views/app/admin.blade.php b/www/resources/views/app/admin.blade.php index 409ed81..2225daa 100644 --- a/www/resources/views/app/admin.blade.php +++ b/www/resources/views/app/admin.blade.php @@ -40,6 +40,11 @@ Users +