init
This commit is contained in:
parent
a00741861a
commit
6ed74c2284
8
.env
8
.env
|
@ -17,3 +17,11 @@
|
|||
APP_ENV=dev
|
||||
APP_SECRET=23cbc3b31c910f18a9822666f2bdb3ef
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
||||
#
|
||||
DATABASE_URL="mysql://app:password@mysql:3306/app"
|
||||
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
/var/
|
||||
/vendor/
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
/.idea/
|
||||
|
|
|
@ -7,14 +7,23 @@
|
|||
"php": ">=7.2.5",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"composer/package-versions-deprecated": "1.11.99.4",
|
||||
"doctrine/doctrine-bundle": "^2.5",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.2",
|
||||
"doctrine/orm": "^2.10",
|
||||
"symfony/console": "5.4.*",
|
||||
"symfony/dotenv": "5.4.*",
|
||||
"symfony/flex": "^1.17|^2",
|
||||
"symfony/framework-bundle": "5.4.*",
|
||||
"symfony/maker-bundle": "^1.36",
|
||||
"symfony/proxy-manager-bridge": "5.4.*",
|
||||
"symfony/runtime": "5.4.*",
|
||||
"symfony/yaml": "5.4.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/security-bundle": "5.4.*",
|
||||
"symfony/twig-bundle": "5.4.*",
|
||||
"symfony/web-profiler-bundle": "5.4.*",
|
||||
"symfony/yaml": "5.4.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,4 +2,11 @@
|
|||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
web_profiler:
|
||||
toolbar: true
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { only_exceptions: false }
|
|
@ -0,0 +1,17 @@
|
|||
doctrine:
|
||||
dbal:
|
||||
url: '%env(resolve:DATABASE_URL)%'
|
||||
|
||||
# IMPORTANT: You MUST configure your server version,
|
||||
# either here or in the DATABASE_URL env var (see .env file)
|
||||
#server_version: '13'
|
||||
orm:
|
||||
auto_generate_proxy_classes: true
|
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
|
||||
auto_mapping: true
|
||||
mappings:
|
||||
App:
|
||||
is_bundle: false
|
||||
dir: '%kernel.project_dir%/src/Entity'
|
||||
prefix: 'App\Entity'
|
||||
alias: App
|
|
@ -0,0 +1,6 @@
|
|||
doctrine_migrations:
|
||||
migrations_paths:
|
||||
# namespace is arbitrary but should be different from App\Migrations
|
||||
# as migrations classes should NOT be autoloaded
|
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations'
|
||||
enable_profiler: '%kernel.debug%'
|
|
@ -0,0 +1,17 @@
|
|||
doctrine:
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
query_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.system_cache_pool
|
||||
result_cache_driver:
|
||||
type: pool
|
||||
pool: doctrine.result_cache_pool
|
||||
|
||||
framework:
|
||||
cache:
|
||||
pools:
|
||||
doctrine.result_cache_pool:
|
||||
adapter: cache.app
|
||||
doctrine.system_cache_pool:
|
||||
adapter: cache.system
|
|
@ -0,0 +1,49 @@
|
|||
security:
|
||||
enable_authenticator_manager: true
|
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
||||
password_hashers:
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||
providers:
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
app_user_provider:
|
||||
entity:
|
||||
class: App\Entity\User
|
||||
property: username
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
custom_authenticator: App\Security\UserAuthenticator
|
||||
logout:
|
||||
path: app_logout
|
||||
# where to redirect after logout
|
||||
# target: app_any_route
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||
- { path: ^/, roles: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
password_hashers:
|
||||
# By default, password hashers are resource intensive and take time. This is
|
||||
# important to generate secure password hashes. In tests however, secure hashes
|
||||
# are not important, waste resources and increase test times. The following
|
||||
# reduces the work factor to the lowest possible values.
|
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
|
||||
algorithm: auto
|
||||
cost: 4 # Lowest possible value for bcrypt
|
||||
time_cost: 3 # Lowest possible value for argon
|
||||
memory_cost: 10 # Lowest possible value for argon
|
|
@ -0,0 +1,4 @@
|
|||
doctrine:
|
||||
dbal:
|
||||
# "TEST_TOKEN" is typically set by ParaTest
|
||||
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
|
|
@ -0,0 +1,6 @@
|
|||
web_profiler:
|
||||
toolbar: false
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { collect: false }
|
|
@ -0,0 +1,6 @@
|
|||
twig:
|
||||
default_path: '%kernel.project_dir%/templates'
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
strict_variables: true
|
|
@ -0,0 +1,7 @@
|
|||
controllers:
|
||||
resource: ../../src/Controller/
|
||||
type: annotation
|
||||
|
||||
kernel:
|
||||
resource: ../../src/Kernel.php
|
||||
type: annotation
|
|
@ -0,0 +1,7 @@
|
|||
web_profiler_wdt:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||
prefix: /_wdt
|
||||
|
||||
web_profiler_profiler:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||
prefix: /_profiler
|
|
@ -0,0 +1,43 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0.22
|
||||
container_name: mysql
|
||||
restart: always
|
||||
volumes:
|
||||
- ./var/mysql:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: 123456
|
||||
MYSQL_DATABASE: app
|
||||
MYSQL_USER: app
|
||||
MYSQL_PASSWORD: password
|
||||
ports:
|
||||
- "3306:3306"
|
||||
|
||||
nginx:
|
||||
image: nginx:stable
|
||||
container_name: nginx
|
||||
restart: always
|
||||
volumes:
|
||||
- ./:/app
|
||||
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
ports:
|
||||
- "80:80"
|
||||
links:
|
||||
- php-fpm
|
||||
|
||||
php-fpm:
|
||||
build: docker/php-fpm
|
||||
restart: always
|
||||
container_name: php-fpm
|
||||
volumes:
|
||||
- ./:/app
|
||||
- ./docker/php-fpm/php.ini:/usr/local/etc/php/php.ini
|
||||
links:
|
||||
- mysql
|
||||
|
||||
volumes:
|
||||
###> doctrine/doctrine-bundle ###
|
||||
db-data:
|
||||
###< doctrine/doctrine-bundle ###
|
|
@ -0,0 +1,40 @@
|
|||
worker_processes 1;
|
||||
events {
|
||||
worker_connections 2048;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
client_max_body_size 200M;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
chunked_transfer_encoding off;
|
||||
server_name platform.local;
|
||||
|
||||
root /app/public;
|
||||
index index.php;
|
||||
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
default_type text/html;
|
||||
access_log /app/var/log/nginx-access.log;
|
||||
error_log /app/var/log/nginx-error.log error;
|
||||
location ~ /upload/\. {
|
||||
deny all;
|
||||
}
|
||||
location ~* /upload/.*$ {
|
||||
add_header Content-Disposition "attachment";
|
||||
add_header Content-Type application/octet-stream;
|
||||
}
|
||||
location ~* \.php$ {
|
||||
fastcgi_pass php-fpm:9000;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
FROM php:8.1.0-fpm
|
||||
WORKDIR /app
|
||||
|
||||
RUN docker-php-ext-install pdo pdo_mysql
|
|
@ -0,0 +1,3 @@
|
|||
date.timezone=Europe/Moscow
|
||||
upload_max_filesize = 200M
|
||||
post_max_size = 200M
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20220108091848 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE `user` (id INT AUTO_INCREMENT NOT NULL, username VARCHAR(180) NOT NULL, score INT NOT NULL, log TINYINT(1) NOT NULL, roles JSON NOT NULL, UNIQUE INDEX UNIQ_8D93D649F85E0677 (username), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
||||
$this->addSql('CREATE TABLE user_log (id INT AUTO_INCREMENT NOT NULL, user_id INT NOT NULL, score INT NOT NULL, created_at DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE `user`');
|
||||
$this->addSql('DROP TABLE user_log');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20220108121138 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE INDEX IDX_8D93D649F85E0677 ON user (username)');
|
||||
$this->addSql('CREATE INDEX IDX_8D93D64932993751 ON user (score)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP INDEX IDX_8D93D649F85E0677 ON `user`');
|
||||
$this->addSql('DROP INDEX IDX_8D93D64932993751 ON `user`');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash
|
||||
|
||||
case "$1" in
|
||||
shortlist)
|
||||
echo consumer-restart consumer-restart-all
|
||||
exit 1
|
||||
;;
|
||||
up)
|
||||
docker-compose up -d --force-recreate --remove-orphans $2
|
||||
;;
|
||||
down)
|
||||
docker-compose down $2
|
||||
;;
|
||||
stop)
|
||||
docker-compose stop $2
|
||||
;;
|
||||
restart)
|
||||
docker-compose restart $2
|
||||
;;
|
||||
update)
|
||||
git pull
|
||||
composer install
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:migrate --no-interaction
|
||||
docker-compose exec php-fpm php /app/bin/console c:c
|
||||
;;
|
||||
migrate-run)
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:migrate --no-interaction
|
||||
;;
|
||||
migrate-redo)
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:execute $2 --down --no-interaction
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:execute $2 --up --no-interaction
|
||||
;;
|
||||
migrate-create)
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:migrations:generate
|
||||
;;
|
||||
build)
|
||||
docker-compose build
|
||||
;;
|
||||
cc)
|
||||
docker-compose exec php-fpm php /app/bin/console c:c
|
||||
;;
|
||||
command)
|
||||
docker-compose exec php-fpm php /app/bin/console $2 ${@:3}
|
||||
;;
|
||||
composer-install)
|
||||
docker-compose exec cron composer install
|
||||
;;
|
||||
composer-update)
|
||||
docker-compose exec cron composer update
|
||||
;;
|
||||
dump-sql-schema)
|
||||
docker-compose exec php-fpm php /app/bin/console doctrine:schema:create --dump-sql > config/base.sql
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {command}
|
||||
consumer-restart {consumer} - restart consumer
|
||||
consumer-restart-all - restart all consumer
|
||||
consumer-reread - reread supervisor config file (docker/supervisor/supervisord.conf -> /etc/supervisor/conf.d/supervisord.conf)
|
||||
up - up docker
|
||||
down - down docker
|
||||
stop {name] - stop single docker container
|
||||
restart {name} - restart all or single docker container
|
||||
update - update project (git puul, composer install, run migrate, clear cache)
|
||||
migrate-run - run migrate
|
||||
migrate-redo {id} - redo single migrate
|
||||
migrate-create - Generate a blank migration class
|
||||
build - build project
|
||||
cc - clear cache
|
||||
command {name} - execute command
|
||||
composer-install - composer install
|
||||
composer-update - composer update
|
||||
dump-sql-schema - create sql schema
|
||||
" >&2
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:create-user',
|
||||
description: 'Add a short description for your command',
|
||||
)]
|
||||
class CreateUserCommand extends Command
|
||||
{
|
||||
private EntityManager $entityManager;
|
||||
|
||||
/**
|
||||
* @param EntityManager $entityManager
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
|
||||
for ($i=0;$i<100000;$i++) {
|
||||
$user = new User();
|
||||
$user->setUsername('user' . random_int(10000000000, 99999999999))
|
||||
->setScore(random_int(0, 1000))
|
||||
->setLog(false);
|
||||
$this->entityManager->persist($user);
|
||||
}
|
||||
$this->entityManager->flush();
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:update-user-score',
|
||||
description: 'Add a short description for your command',
|
||||
)]
|
||||
class UpdateUserScoreCommand extends Command
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
/**
|
||||
* @param EntityManagerInterface $entityManager
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$this->entityManager->getConnection()->executeQuery('
|
||||
UPDATE `user` SET score = RAND() * 1000
|
||||
');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class MainController extends AbstractController
|
||||
{
|
||||
#[Route('/', name: 'main')]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('main.html.twig');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route("/login", name="app_login")
|
||||
*/
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
// if ($this->getUser()) {
|
||||
// return $this->redirectToRoute('target_path');
|
||||
// }
|
||||
|
||||
// get the login error if there is one
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
// last username entered by the user
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/logout", name="app_logout")
|
||||
*/
|
||||
public function logout(): void
|
||||
{
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
#[ORM\Index(columns: ['username'])]
|
||||
#[ORM\Index(columns: ['score'])]
|
||||
class User implements UserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 180, unique: true)]
|
||||
private string $username;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $score;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $log;
|
||||
|
||||
#[ORM\Column(type: 'json')]
|
||||
private array $roles = [];
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.3, use getUserIdentifier instead
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return (string) $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(string $username): self
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScore(): ?int
|
||||
{
|
||||
return $this->score;
|
||||
}
|
||||
|
||||
public function setScore(int $score): self
|
||||
{
|
||||
$this->score = $score;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLog(): ?bool
|
||||
{
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
public function setLog(bool $log): self
|
||||
{
|
||||
$this->log = $log;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A visual identifier that represents this user.
|
||||
*
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return (string) $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
// guarantee every user at least has ROLE_USER
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
public function setRoles(array $roles): self
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords.
|
||||
*
|
||||
* @see PasswordAuthenticatedUserInterface
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be removed in Symfony 6.0 - is not needed for apps that do not check user passwords.
|
||||
*
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getSalt(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function eraseCredentials()
|
||||
{
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserLogRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserLogRepository::class)]
|
||||
class UserLog
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $user_id;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $score;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
private \DateTimeImmutable $created_at;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->user_id;
|
||||
}
|
||||
|
||||
public function setUserId(int $user_id): self
|
||||
{
|
||||
$this->user_id = $user_id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScore(): ?int
|
||||
{
|
||||
return $this->score;
|
||||
}
|
||||
|
||||
public function setScore(int $score): self
|
||||
{
|
||||
$this->score = $score;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->created_at;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTimeImmutable $created_at): self
|
||||
{
|
||||
$this->created_at = $created_at;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\UserLog;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @method UserLog|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method UserLog|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method UserLog[] findAll()
|
||||
* @method UserLog[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class UserLogRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, UserLog::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @method User|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method User|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method User[] findAll()
|
||||
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class UserRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CredentialsInterface;
|
||||
|
||||
class EmptyPassword implements CredentialsInterface
|
||||
{
|
||||
public function isResolved(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
use Symfony\Component\Security\Http\Util\TargetPathTrait;
|
||||
|
||||
class UserAuthenticator extends AbstractLoginFormAuthenticator
|
||||
{
|
||||
use TargetPathTrait;
|
||||
|
||||
public const LOGIN_ROUTE = 'app_login';
|
||||
|
||||
private UrlGeneratorInterface $urlGenerator;
|
||||
|
||||
public function __construct(UrlGeneratorInterface $urlGenerator)
|
||||
{
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
public function authenticate(Request $request): Passport
|
||||
{
|
||||
$username = $request->request->get('username', '');
|
||||
|
||||
$request->getSession()->set(Security::LAST_USERNAME, $username);
|
||||
|
||||
return new Passport(
|
||||
new UserBadge($username),
|
||||
new EmptyPassword(),
|
||||
[
|
||||
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
|
||||
{
|
||||
return new RedirectResponse($this->urlGenerator->generate('main'));
|
||||
}
|
||||
|
||||
protected function getLoginUrl(Request $request): string
|
||||
{
|
||||
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
|
||||
}
|
||||
}
|
182
symfony.lock
182
symfony.lock
|
@ -1,4 +1,96 @@
|
|||
{
|
||||
"composer/package-versions-deprecated": {
|
||||
"version": "1.11.99.4"
|
||||
},
|
||||
"doctrine/annotations": {
|
||||
"version": "1.13",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
|
||||
},
|
||||
"files": [
|
||||
"config/routes/annotations.yaml"
|
||||
]
|
||||
},
|
||||
"doctrine/cache": {
|
||||
"version": "2.1.1"
|
||||
},
|
||||
"doctrine/collections": {
|
||||
"version": "1.6.8"
|
||||
},
|
||||
"doctrine/common": {
|
||||
"version": "3.2.1"
|
||||
},
|
||||
"doctrine/dbal": {
|
||||
"version": "3.2.1"
|
||||
},
|
||||
"doctrine/deprecations": {
|
||||
"version": "v0.5.3"
|
||||
},
|
||||
"doctrine/doctrine-bundle": {
|
||||
"version": "2.5",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "2.4",
|
||||
"ref": "f98f1affe028f8153a459d15f220ada3826b5aa2"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/doctrine.yaml",
|
||||
"config/packages/prod/doctrine.yaml",
|
||||
"config/packages/test/doctrine.yaml",
|
||||
"src/Entity/.gitignore",
|
||||
"src/Repository/.gitignore"
|
||||
]
|
||||
},
|
||||
"doctrine/doctrine-migrations-bundle": {
|
||||
"version": "3.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "3.1",
|
||||
"ref": "ee609429c9ee23e22d6fa5728211768f51ed2818"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/doctrine_migrations.yaml",
|
||||
"migrations/.gitignore"
|
||||
]
|
||||
},
|
||||
"doctrine/event-manager": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"doctrine/inflector": {
|
||||
"version": "2.0.4"
|
||||
},
|
||||
"doctrine/instantiator": {
|
||||
"version": "1.4.0"
|
||||
},
|
||||
"doctrine/lexer": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"doctrine/migrations": {
|
||||
"version": "3.3.2"
|
||||
},
|
||||
"doctrine/orm": {
|
||||
"version": "2.10.4"
|
||||
},
|
||||
"doctrine/persistence": {
|
||||
"version": "2.2.3"
|
||||
},
|
||||
"doctrine/sql-formatter": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"friendsofphp/proxy-manager-lts": {
|
||||
"version": "v1.0.5"
|
||||
},
|
||||
"laminas/laminas-code": {
|
||||
"version": "4.5.1"
|
||||
},
|
||||
"nikic/php-parser": {
|
||||
"version": "v4.13.2"
|
||||
},
|
||||
"psr/cache": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
|
@ -38,6 +130,9 @@
|
|||
"symfony/deprecation-contracts": {
|
||||
"version": "v3.0.0"
|
||||
},
|
||||
"symfony/doctrine-bridge": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/dotenv": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
|
@ -93,6 +188,18 @@
|
|||
"symfony/http-kernel": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/maker-bundle": {
|
||||
"version": "1.36",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||
}
|
||||
},
|
||||
"symfony/password-hasher": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/polyfill-intl-grapheme": {
|
||||
"version": "v1.24.0"
|
||||
},
|
||||
|
@ -111,6 +218,15 @@
|
|||
"symfony/polyfill-php81": {
|
||||
"version": "v1.24.0"
|
||||
},
|
||||
"symfony/property-access": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/property-info": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/proxy-manager-bridge": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "5.4",
|
||||
"recipe": {
|
||||
|
@ -127,19 +243,85 @@
|
|||
"symfony/runtime": {
|
||||
"version": "v5.4.1"
|
||||
},
|
||||
"symfony/security-bundle": {
|
||||
"version": "5.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.3",
|
||||
"ref": "09b5e809bd7a992061febd05b797c64a2d93b5cd"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/security.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/security-core": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/security-csrf": {
|
||||
"version": "v5.4.0"
|
||||
},
|
||||
"symfony/security-guard": {
|
||||
"version": "v5.4.0"
|
||||
},
|
||||
"symfony/security-http": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/service-contracts": {
|
||||
"version": "v2.4.1"
|
||||
},
|
||||
"symfony/stopwatch": {
|
||||
"version": "v5.4.0"
|
||||
},
|
||||
"symfony/string": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/translation-contracts": {
|
||||
"version": "v2.5.0"
|
||||
},
|
||||
"symfony/twig-bridge": {
|
||||
"version": "v5.4.0"
|
||||
},
|
||||
"symfony/twig-bundle": {
|
||||
"version": "5.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.4",
|
||||
"ref": "bffbb8f1a849736e64006735afae730cb428b6ff"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/twig.yaml",
|
||||
"templates/base.html.twig"
|
||||
]
|
||||
},
|
||||
"symfony/var-dumper": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/var-exporter": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"symfony/web-profiler-bundle": {
|
||||
"version": "5.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "3.3",
|
||||
"ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/dev/web_profiler.yaml",
|
||||
"config/packages/test/web_profiler.yaml",
|
||||
"config/routes/dev/web_profiler.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/yaml": {
|
||||
"version": "v5.4.2"
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.3.7"
|
||||
},
|
||||
"twig/twig": {
|
||||
"version": "v3.3.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
|
||||
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
|
||||
{% block stylesheets %}
|
||||
{{ encore_entry_link_tags('app') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{{ encore_entry_script_tags('app') }}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,7 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Main!{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
Main
|
||||
{% endblock %}
|
|
@ -0,0 +1,29 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}Log in!{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form method="post">
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if app.user %}
|
||||
<div class="mb-3">
|
||||
You are logged in as {{ app.user.username }}, <a href="{{ path('app_logout') }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
|
||||
<label for="inputUsername">Username</label>
|
||||
<input type="text" value="{{ last_username }}" name="username" id="inputUsername" class="form-control" autocomplete="username" required autofocus>
|
||||
|
||||
<input type="hidden" name="_csrf_token"
|
||||
value="{{ csrf_token('authenticate') }}"
|
||||
>
|
||||
|
||||
<button class="btn btn-lg btn-primary" type="submit">
|
||||
Sign in
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue