Оформление
Установка
В этом руководстве описана установка административной панели bia на чистую Symfony 6.4.
Установка Symfony и зависимостей
Устанавливаем чистую Symfony версии 6.4, если еще не установлена:
bashcomposer create-project symfony/skeleton:"6.4.*" .Добавляем репозиторий с исходным кодом bia/admin-bundle:
json"repositories": [ { "type": "git", "url": "https://gitlab.srvdev.ru/bia-cms/bundles/admin-bundle.git" } ],Создаём файл конфигурации config/packages/bia_admin.yaml
yamlbia_admin: exporter: default_handlers: ['json', 'csv', 'xlsx'] only_delayed_strictly: false default_only_delayed: false handlers: csv: delimiter: ',' enclosure: '"' escape: "\\" show_headers: trueВ config/services.yaml добавляем сервис загрузки маршрутов и параметр для переводов:
yamlparameters: defaults.admin.translation_domain: 'AdminTranslationDomain' services: admin.routing.loader: class: Bia\AdminBundle\Routing\AdminLoader public: true arguments: $adminServiceIds: [] $container: '@service_container' tags: - { name: 'routing.loader' }Устанавливаем необходимые зависимости (TODO добавить зависимости в admin-bundle/composer.json):
bashcomposer require symfony/orm-pack composer require symfony/validator composer require symfony/form composer require lexik/jwt-authentication-bundle composer require lcobucci/jwt composer require phpdocumentor/reflection-docblock composer require symfony/var-exporter:6.4.4В
.envили.env.distдобавляем:###> lexik/jwt-authentication-bundle ### JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PASSPHRASE=85d8de1a30ac46474f2f91c4ff97a477 JWT_NAME=jwt JWT_TTL=130000 AUTH_PROTECTED_LEVEL=IS_AUTHENTICATED_FULLY ###< lexik/jwt-authentication-bundle ### ###> admin front api service ### VITE_ADMIN_API_SERVICE='http://127.0.0.1:8001/' ###< admin front api service ###В
config/services.yamlдобавляем:yamlform.factory: class: Symfony\Component\Form\FormFactory public: true arguments: ['@form.registry'] Bia\AdminBundle\Repository\AdminUserRepository: autowire: true tags: ['doctrine.repository_service'] Bia\AdminBundle\DataFixtures\: resource: '../vendor/bia/admin-bundle/DataFixtures'В
config/routes.yamlдобавляем:yamladmin_login_token: path: /admin/login_token admin_logout: path: /admin/logout methods: ['POST'] bia_admin_bundle: resource: "@BiaAdminBundle/Resources/config/routing.php" admin_routes: resource: '.' type: 'admin_routes'Устанавливаем пакет:
bashcomposer require bia/admin-bundle:0.9.0Создаем файл
config/packages/roles.yamlс содержимым:
yaml
parameters:
roles: []- В
config/packages/security.yamlдобавляем:
yaml
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
app_admin_provider:
entity:
class: Bia\AdminBundle\Entity\AdminUser
property: email
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
provider: app_admin_provider
pattern: ^/admin/login
stateless: true
json_login:
check_path: /admin/login_token
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
logout:
pattern: ^/admin/logout
logout:
path: /admin/logout
invalidate_session: true
delete_cookies:
jwt: true
target: admin_user_logout
admin:
provider: app_admin_provider
pattern: ^/admin
stateless: true
jwt: ~
access_control:
- { path: ^/admin/login_token, roles: PUBLIC_ACCESS }
- { path: ^/admin/user_logout, roles: PUBLIC_ACCESS }
- { path: ^/admin, roles: '%env(AUTH_PROTECTED_LEVEL)%' }
when@test:
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
algorithm: auto
cost: 4
time_cost: 3
memory_cost: 10- В
config/packages/lexik_jwt_authentication.yamlдобавляем:
yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: '%env(JWT_TTL)%'
user_identity_field: email
# token extraction settings
token_extractors:
authorization_header:
enabled: true
cookie:
enabled: true
name: '%env(JWT_NAME)%'
set_cookies:
'%env(JWT_NAME)%':
httpOnly: true
secure: false
remove_token_from_body_when_cookies_used: false- Генерируем приватный и публичный ключ для JWT:
bash
mkdir config/jwt
openssl genrsa -out config/jwt/private.pem 4096
openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem- Выполняем миграции:
bash
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate- Генерируем администратора:
bash
php bin/console bia:admin:create-super-admin- Генерируем меню:
bash
php bin/console bia:admin:generate-menu- Генерируем роли и права:
bash
php bin/console bia:admin:generate-roles-configУстановка SPA-фронта
Конфигурация nginx:
server { listen 80; listen 443 ssl http2; server_name "~^(?<domain>.+).pers.bia.terranova.srvdev.ru$"; root /www/bia.terranova.srvdev.ru/personal/$host/public; location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ ^/(index)\.php(/|$) { fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location /bia { try_files $uri /bia/$uri /bia/index.html; } charset utf-8; client_max_body_size 100m; error_log /var/log/nginx/personal.bia.srvdev.ru_error.log; access_log /var/log/nginx/personal.bia.srvdev.ru_access.log; fastcgi_read_timeout 960; }Создаем
package.json:json{ "name": "development", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "devDependencies": { "typescript": "^5.2.2", "vite": "^5.1.4" }, "dependencies": { "bia-cms": "file:vendor/bia/admin-bundle/frontend" } }Создаем
vite.config.ts:tsimport { defineConfig, loadEnv } from 'vite' export default ({mode}: { mode: string }) => { process.env = Object.assign(process.env, loadEnv(mode, process.cwd())) return defineConfig({ server: { proxy: { '^/(upload|admin)': { target: process.env.VITE_ADMIN_API_SERVICE, changeOrigin: true, secure: false, }, }, }, build: { assetsDir: 'bia', outDir: 'public/bia' }, publicDir: false }) }Создаем файл
index.htmlв корне проекта:html<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Dev project</title> </head> <body> <div id="app"></div> <script type="module" src="/src/frontend/main.js"></script> </body> </html>Создаем файл src/frontend/main.js:
jsimport {mountApp} from "bia-cms" import 'bia-cms/dist/style.css' mountApp('app', { apiService: import.meta.env.MODE === 'production' ? import.meta.env.VITE_ADMIN_API_SERVICE : undefined, components: import.meta.glob('./fields/GroupUserList.vue'), })Создаем файл src/frontend/fields/GroupUserList.vue:
vue<template> <h2>test</h2> </template> <script setup lang="ts"> import type { IFormField } from "bia-cms/src/views/dynamicPage/types/form"; import { IApiWrapper } from "bia-cms/src/assets/js/helpers/useApi"; const props = defineProps<{ modelValue: any | undefined, field: IFormField, formValues: Record<string, any>, api: IApiWrapper, route: any, }>() const emit = defineEmits(['update:modelValue']) </script> <style scoped> </style>Затем запускаем
bashphp bin/console bia:admin:build-frontend