Create everything required to login and register
This commit is contained in:
parent
82d5625e60
commit
5be77eeba1
@ -13,14 +13,24 @@
|
||||
"symfony/console": "6.2.*",
|
||||
"symfony/dotenv": "6.2.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/form": "6.2.*",
|
||||
"symfony/framework-bundle": "6.2.*",
|
||||
"symfony/monolog-bundle": "^3.0",
|
||||
"symfony/runtime": "6.2.*",
|
||||
"symfony/security-bundle": "6.2.*",
|
||||
"symfony/twig-bundle": "6.2.*",
|
||||
"symfony/validator": "6.2.*",
|
||||
"symfony/yaml": "6.2.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"deployer/deployer": "^7.3",
|
||||
"symfony/debug-bundle": "6.2.*",
|
||||
"symfony/maker-bundle": "^1.48",
|
||||
"symfony/stopwatch": "6.2.*",
|
||||
"symfony/web-profiler-bundle": "6.2.*"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true,
|
||||
@ -68,8 +78,5 @@
|
||||
"allow-contrib": false,
|
||||
"require": "6.2.*"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/maker-bundle": "^1.48"
|
||||
}
|
||||
}
|
||||
|
887
composer.lock
generated
887
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,4 +8,7 @@ return [
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
|
||||
];
|
||||
|
5
config/packages/debug.yaml
Normal file
5
config/packages/debug.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
when@dev:
|
||||
debug:
|
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
|
||||
# See the "server:dump" command to start a new server.
|
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
|
61
config/packages/monolog.yaml
Normal file
61
config/packages/monolog.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
monolog:
|
||||
channels:
|
||||
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||
|
||||
when@dev:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
channels: ["!event"]
|
||||
# uncomment to get logging in your browser
|
||||
# you may have to allow bigger header sizes in your Web server configuration
|
||||
#firephp:
|
||||
# type: firephp
|
||||
# level: info
|
||||
#chromephp:
|
||||
# type: chromephp
|
||||
# level: info
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine", "!console"]
|
||||
|
||||
when@test:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
channels: ["!event"]
|
||||
nested:
|
||||
type: stream
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
level: debug
|
||||
|
||||
when@prod:
|
||||
monolog:
|
||||
handlers:
|
||||
main:
|
||||
type: fingers_crossed
|
||||
action_level: error
|
||||
handler: nested
|
||||
excluded_http_codes: [404, 405]
|
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||
nested:
|
||||
type: stream
|
||||
path: php://stderr
|
||||
level: debug
|
||||
formatter: monolog.formatter.json
|
||||
console:
|
||||
type: console
|
||||
process_psr_3_messages: false
|
||||
channels: ["!event", "!doctrine"]
|
||||
deprecation:
|
||||
type: stream
|
||||
channels: [deprecation]
|
||||
path: php://stderr
|
@ -5,7 +5,7 @@ security:
|
||||
# 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:
|
||||
user_provider:
|
||||
entity:
|
||||
class: App\Entity\User
|
||||
property: username
|
||||
@ -15,7 +15,19 @@ security:
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
provider: user_provider
|
||||
logout:
|
||||
path: logout
|
||||
form_login:
|
||||
login_path: login
|
||||
check_path: login
|
||||
remember_me:
|
||||
secret: '%kernel.secret%' # required
|
||||
lifetime: 2419200 # 4 weeks in seconds
|
||||
|
||||
secured_area:
|
||||
form_login:
|
||||
enable_csrf: true
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#the-firewall
|
||||
@ -26,8 +38,12 @@ security:
|
||||
# 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: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
- { path: ^/login$, role: PUBLIC_ACCESS }
|
||||
- { path: ^/logout$, role: ROLE_USER }
|
||||
- { path: ^/register, role: ROLE_ADMIN }
|
||||
- { path: ^/admin, role: ROLE_ADMIN }
|
||||
|
||||
- { path: ^/, role: ROLE_USER }
|
||||
|
||||
when@test:
|
||||
security:
|
||||
|
13
config/packages/validator.yaml
Normal file
13
config/packages/validator.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
framework:
|
||||
validation:
|
||||
email_validation_mode: html5
|
||||
|
||||
# Enables validator auto-mapping support.
|
||||
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||
#auto_mapping:
|
||||
# App\Entity\: []
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
17
config/packages/web_profiler.yaml
Normal file
17
config/packages/web_profiler.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
when@dev:
|
||||
web_profiler:
|
||||
toolbar: true
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler:
|
||||
only_exceptions: false
|
||||
collect_serializer_data: true
|
||||
|
||||
when@test:
|
||||
web_profiler:
|
||||
toolbar: false
|
||||
intercept_redirects: false
|
||||
|
||||
framework:
|
||||
profiler: { collect: false }
|
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
when@dev:
|
||||
web_profiler_wdt:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||
prefix: /_wdt
|
||||
|
||||
web_profiler_profiler:
|
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||
prefix: /_profiler
|
@ -20,5 +20,6 @@ services:
|
||||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
||||
App\Service\LastRelease:
|
||||
arguments:
|
||||
- '%kernel.project_dir%/release.json'
|
||||
|
108
deploy.php
Normal file
108
deploy.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Deployer;
|
||||
|
||||
require_once 'recipe/common.php';
|
||||
|
||||
// Project name
|
||||
set('application', 'snips');
|
||||
|
||||
// Project repository
|
||||
set('repository', 'git@git.loken.nl:ardent/Snips.git');
|
||||
|
||||
// [Optional] Allocate tty for git clone. Default value is false.
|
||||
set('git_tty', true);
|
||||
|
||||
// Shared files/dirs between deploys
|
||||
set('shared_dirs', ['var/log', 'var/sessions']);
|
||||
set('shared_files', ['.env.local']);
|
||||
//set('writable_dirs', ['var']);
|
||||
set('migrations_config', '');
|
||||
set('allow_anonymous_stats', false);
|
||||
|
||||
// Hosts
|
||||
host('snips.loken.nl')
|
||||
->setRemoteUser('www-data')
|
||||
->set('branch', function () {
|
||||
return input()->getOption('branch') ?: 'develop';
|
||||
})
|
||||
->set('deploy_path', '~/snips.loken.nl');
|
||||
|
||||
set('bin/console', function () {
|
||||
return parse('{{release_path}}/bin/console');
|
||||
});
|
||||
|
||||
set('console_options', function () {
|
||||
return '--no-interaction';
|
||||
});
|
||||
|
||||
desc('Clear cache');
|
||||
task('cache:clear', function () {
|
||||
run('{{bin/php}} {{bin/console}} cache:clear {{console_options}} --no-warmup');
|
||||
});
|
||||
|
||||
desc('Warm up cache');
|
||||
task('cache:warmup', function () {
|
||||
run('{{bin/php}} {{bin/console}} cache:warmup {{console_options}}');
|
||||
});
|
||||
|
||||
desc('Migrate database');
|
||||
task('database:migrate', function () {
|
||||
// $options = '--allow-no-migration';
|
||||
// if (get('migrations_config') !== '') {
|
||||
// $options = sprintf('%s --configuration={{release_path}}/{{migrations_config}}', $options);
|
||||
// }
|
||||
//
|
||||
// run(sprintf('{{bin/php}} {{bin/console}} doctrine:migrations:migrate %s {{console_options}}', $options));
|
||||
run('{{bin/php}} {{bin/console}} doctrine:schema:update --force');
|
||||
});
|
||||
|
||||
task('deployment:log', function () { //https://stackoverflow.com/questions/59686270/how-to-log-deployments-in-deployer
|
||||
$branch = parse('{{branch}}');
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$commitHashShort = runLocally('git rev-parse --short HEAD');
|
||||
// $commitHash = runLocally('git rev-parse HEAD');
|
||||
$commit = explode(PHP_EOL, runLocally('git log -1 --pretty="%H%n%ci"'));
|
||||
$commitHash = $commit[0];
|
||||
$commitDate = $commit[1];
|
||||
|
||||
// $line = sprintf('%s %s branch="%s" hash="%s"', $date, $commitHashShort, $branch, $commitHash);
|
||||
$projectUrlBase = 'https://git.loken.nl/ardent/AnimeRSS4';
|
||||
$array = [
|
||||
'branch' => $branch,
|
||||
'branchUrl' => sprintf('%s/src/branch/%s', $projectUrlBase, $branch),
|
||||
'date' => $date,
|
||||
'commitHashShort' => $commitHashShort,
|
||||
'commitHashLong' => $commitHash,
|
||||
'commitDate' => $commitDate,
|
||||
'commitUrl' => sprintf('%s/commit/%s', $projectUrlBase, $commitHash),
|
||||
'projectUrl' => $projectUrlBase,
|
||||
];
|
||||
$json = json_encode($array, JSON_PRETTY_PRINT);
|
||||
|
||||
runLocally("echo '$json' > release.json");
|
||||
upload('release.json', '{{release_path}}/release.json');
|
||||
});
|
||||
|
||||
//desc('Deploy project');
|
||||
//task('deploy', [
|
||||
// 'deployment:log',
|
||||
//]);
|
||||
|
||||
desc('Deploy project');
|
||||
task('deploy', [
|
||||
'deploy:prepare',
|
||||
'deploy:vendors',
|
||||
'cache:clear',
|
||||
'cache:warmup',
|
||||
'database:migrate',
|
||||
'deployment:log',
|
||||
'deploy:symlink',
|
||||
'deploy:unlock',
|
||||
'deploy:cleanup',
|
||||
]);
|
||||
|
||||
after('deploy', 'deploy:success');
|
||||
|
||||
// [Optional] if deploy fails automatically unlock.
|
||||
after('deploy:failed', 'deploy:unlock');
|
0
src/Controller/.gitignore
vendored
0
src/Controller/.gitignore
vendored
19
src/Controller/HomeController.php
Normal file
19
src/Controller/HomeController.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class HomeController extends AbstractController
|
||||
{
|
||||
#[Route('/', name: 'home')]
|
||||
public function home(): Response
|
||||
{
|
||||
return $this->redirectToRoute('task_view');
|
||||
// return $this->render('simple.html.twig', [
|
||||
// 'text' => 'Welcome!'
|
||||
// ]);
|
||||
}
|
||||
}
|
72
src/Controller/SecurityController.php
Normal file
72
src/Controller/SecurityController.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Form\RegistrationFormType;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
#[Route('/login', name: 'login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('security/login.html.twig', [
|
||||
'last_username' => $lastUsername,
|
||||
'error' => $error,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/logout', name: 'logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
// controller can be blank: it will never be called!
|
||||
throw new Exception('Don\'t forget to activate logout in security.yaml');
|
||||
}
|
||||
|
||||
#[Route('/register', name: 'register')]
|
||||
public function register(
|
||||
Request $request,
|
||||
UserPasswordHasherInterface $userPasswordHasher,
|
||||
EntityManagerInterface $entityManager,
|
||||
): Response
|
||||
{
|
||||
$user = new User();
|
||||
$form = $this->createForm(RegistrationFormType::class, $user);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if ($form->get('plainPassword')->getData() !== $form->get('plainPasswordRepeated')->getData()) {
|
||||
$this->addFlash('error', 'Password and password repeated must be the same');
|
||||
} else {
|
||||
$user->setPassword(
|
||||
$userPasswordHasher->hashPassword(
|
||||
$user,
|
||||
$form->get('plainPassword')->getData()
|
||||
)
|
||||
);
|
||||
|
||||
$entityManager->persist($user);
|
||||
$entityManager->flush();
|
||||
// do anything else you need here, like send an email
|
||||
$this->addFlash('success', sprintf('Successfully registered user %s', $user->getUsername()));
|
||||
|
||||
return $this->redirectToRoute('register');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('security/register.html.twig', [
|
||||
'registrationForm' => $form->createView(),
|
||||
]);
|
||||
}
|
||||
}
|
63
src/Controller/UserController.php
Normal file
63
src/Controller/UserController.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Form\ProfileType;
|
||||
use App\Form\UserSettingsType;
|
||||
use App\Service\LastRelease;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
#[Route('/user', name: 'user')]
|
||||
class UserController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private EntityManagerInterface $em,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/profile', name: '_profile')]
|
||||
public function profile(
|
||||
Request $request,
|
||||
UserPasswordHasherInterface $userPasswordHasher,
|
||||
LastRelease $lastRelease,
|
||||
): Response
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$form = $this->createForm(ProfileType::class, $user);
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
if (!empty($form->get('plainPassword')->getData())) {
|
||||
if ($form->get('plainPassword')->getData() !== $form->get('plainPasswordRepeated')->getData()) {
|
||||
$this->addFlash('error', 'Password and password repeated must be the same, password not changed');
|
||||
} else {
|
||||
$this->addFlash('success', 'Password updated successfully');
|
||||
$user
|
||||
->setPassword(
|
||||
$userPasswordHasher->hashPassword(
|
||||
$user,
|
||||
$form->get('plainPassword')->getData()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->addFlash('success', 'Profile updated successfully');
|
||||
|
||||
$this->em->persist($user);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
return $this->render('user/profile.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'release' => $lastRelease,
|
||||
]);
|
||||
}
|
||||
}
|
49
src/Form/ProfileType.php
Normal file
49
src/Form/ProfileType.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
|
||||
class ProfileType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('email')
|
||||
->add('plainPassword', PasswordType::class, [
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
'attr' => ['autocomplete' => 'new-password'],
|
||||
'label' => 'Password',
|
||||
'constraints' => [
|
||||
new Length([
|
||||
'min' => 8,
|
||||
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||
// max length allowed by Symfony for security reasons
|
||||
'max' => 4096,
|
||||
]),
|
||||
],
|
||||
])
|
||||
->add('plainPasswordRepeated', PasswordType::class, [
|
||||
'required' => false,
|
||||
'mapped' => false,
|
||||
'attr' => ['autocomplete' => 'new-password'],
|
||||
'label' => 'Password repeated',
|
||||
])
|
||||
->add('save', SubmitType::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => User::class,
|
||||
]);
|
||||
}
|
||||
}
|
64
src/Form/RegistrationFormType.php
Normal file
64
src/Form/RegistrationFormType.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||
use Symfony\Component\Validator\Constraints\Length;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
|
||||
class RegistrationFormType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('username')
|
||||
->add('name')
|
||||
->add('plainPassword', PasswordType::class, [
|
||||
// instead of being set onto the object directly,
|
||||
// this is read and encoded in the controller
|
||||
'mapped' => false,
|
||||
'attr' => ['autocomplete' => 'new-password'],
|
||||
'label' => 'Password',
|
||||
'constraints' => [
|
||||
new NotBlank([
|
||||
'message' => 'Please enter a password',
|
||||
]),
|
||||
new Length([
|
||||
'min' => 8,
|
||||
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||
// max length allowed by Symfony for security reasons
|
||||
'max' => 4096,
|
||||
]),
|
||||
],
|
||||
])
|
||||
->add('plainPasswordRepeated', PasswordType::class, [
|
||||
'mapped' => false,
|
||||
'label' => 'Password repeated',
|
||||
])
|
||||
->add('email', EmailType::class)
|
||||
// ->add('agreeTerms', CheckboxType::class, [
|
||||
// 'mapped' => false,
|
||||
// 'constraints' => [
|
||||
// new IsTrue([
|
||||
// 'message' => 'You should agree to our terms.',
|
||||
// ]),
|
||||
// ],
|
||||
// ])
|
||||
->add('register', SubmitType::class);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => User::class,
|
||||
]);
|
||||
}
|
||||
}
|
68
src/Service/LastRelease.php
Normal file
68
src/Service/LastRelease.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
|
||||
class LastRelease
|
||||
{
|
||||
#[ArrayShape([
|
||||
"branch" => "string",
|
||||
"date" => "string",
|
||||
"commitHashShort" => "string",
|
||||
"commitHashLong" => "string",
|
||||
"commitDate" => "string",
|
||||
"branchUrl" => "string",
|
||||
"projectUrl" => "string",
|
||||
"commitUrl" => "string",
|
||||
])]
|
||||
private array $lastRelease = [];
|
||||
|
||||
public function __construct(string $jsonFile)
|
||||
{
|
||||
if (file_exists($jsonFile)) {
|
||||
$this->lastRelease = json_decode(file_get_contents($jsonFile), true);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBranch(): string
|
||||
{
|
||||
return $this->lastRelease['branch'] ?? '-';
|
||||
}
|
||||
|
||||
public function getBranchUrl(): string
|
||||
{
|
||||
return $this->lastRelease['branchUrl'] ?? '#';
|
||||
}
|
||||
|
||||
public function getProjectUrl(): string
|
||||
{
|
||||
return $this->lastRelease['projectUrl'] ?? '#';
|
||||
}
|
||||
|
||||
public function getCommitUrl(): string
|
||||
{
|
||||
return $this->lastRelease['commitUrl'] ?? '#';
|
||||
}
|
||||
|
||||
public function getDate(): string
|
||||
{
|
||||
return $this->lastRelease['date'] ?? '-';
|
||||
}
|
||||
|
||||
public function getCommitHashShort(): string
|
||||
{
|
||||
return $this->lastRelease['commitHashShort'] ?? '-';
|
||||
}
|
||||
|
||||
public function getCommitHashLong(): string
|
||||
{
|
||||
return $this->lastRelease['commitHashLong'] ?? '-';
|
||||
}
|
||||
|
||||
public function getCommitDate(): string
|
||||
{
|
||||
return $this->lastRelease['commitDate'] ?? '-';
|
||||
}
|
||||
|
||||
}
|
49
symfony.lock
49
symfony.lock
@ -38,6 +38,18 @@
|
||||
"bin/console"
|
||||
]
|
||||
},
|
||||
"symfony/debug-bundle": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.3",
|
||||
"ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/debug.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/flex": {
|
||||
"version": "2.2",
|
||||
"recipe": {
|
||||
@ -78,6 +90,18 @@
|
||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||
}
|
||||
},
|
||||
"symfony/monolog-bundle": {
|
||||
"version": "3.8",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.7",
|
||||
"ref": "213676c4ec929f046dfde5ea8e97625b81bc0578"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/monolog.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
@ -116,6 +140,31 @@
|
||||
"templates/base.html.twig"
|
||||
]
|
||||
},
|
||||
"symfony/validator": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.3",
|
||||
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/validator.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/web-profiler-bundle": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "6.1",
|
||||
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/web_profiler.yaml",
|
||||
"config/routes/web_profiler.yaml"
|
||||
]
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.5.1"
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
<!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>
|
70
templates/base/base.html.twig
Normal file
70
templates/base/base.html.twig
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}BLES{% endblock %}</title>
|
||||
<link rel="shortcut icon" type="image/jpg" href="/favicon.png">
|
||||
<script src="https://kit.fontawesome.com/3471b6556e.js" crossorigin="anonymous"></script>
|
||||
{% if chartjs|default(false) %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||
{% endif %}
|
||||
|
||||
{% block css %}
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
|
||||
crossorigin="anonymous">
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{{ include('base/navbar.html.twig') }}
|
||||
|
||||
{# alert block #}
|
||||
<div class="container">
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-sm mx-auto">
|
||||
{% block flashes %}
|
||||
{% for msg in app.session.flashBag.get('error') %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<strong>Error: </strong> {{ msg|raw }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% for msg in app.session.flashBag.get('success') %}
|
||||
<div class="alert alert-success">
|
||||
<strong>Success: </strong> {{ msg|raw }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# body blocks #}
|
||||
{% block bodyraw %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm mx-auto">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
{% if block('body2') is defined %}
|
||||
<div class="col-sm mx-auto">
|
||||
{{ block('body2') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{# javascript block #}
|
||||
{% block js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"
|
||||
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"
|
||||
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
|
||||
crossorigin="anonymous"></script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
33
templates/base/navbar.html.twig
Normal file
33
templates/base/navbar.html.twig
Normal file
@ -0,0 +1,33 @@
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark" style="z-index: 1;">
|
||||
<div class="container-fluid">
|
||||
<a title="BlueLinked Eco System" class="navbar-brand" href="{{ path('home') }}">BLES</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar"
|
||||
aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ path('home') }}">Home</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav my-2 my-lg-0">
|
||||
{% if app.environment == 'dev' %}
|
||||
<li class="nav-item">
|
||||
<a class="btn navbar-text bg-danger" href="#">dev</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
||||
<span class="nav-item navbar-text mr-sm-2">Logged in as</span>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ path('user_profile') }}">{{ app.user.username }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ path('logout') }}">Logout</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
32
templates/security/login.html.twig
Normal file
32
templates/security/login.html.twig
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends 'base/base.html.twig' %}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form action="{{ path('login') }}" 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 already logged in as {{ app.user }}, <a href="{{ path('logout') }}">Logout</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1 class="h3 mb-3 font-weight-normal">Please login</h1>
|
||||
<label for="inputUsername">Username</label>
|
||||
<input type="text" value="{{ last_username }}" name="_username" id="inputUsername" class="form-control" required
|
||||
autofocus>
|
||||
<br/>
|
||||
<label for="inputPassword">Password</label>
|
||||
<input type="password" name="_password" id="inputPassword" class="form-control" required>
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||
<br/>
|
||||
|
||||
<div class="checkbox mb-3">
|
||||
<label>
|
||||
<input type="checkbox" name="_remember_me"> Remember me
|
||||
</label>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Login</button>
|
||||
</form>
|
||||
{% endblock %}
|
6
templates/security/register.html.twig
Normal file
6
templates/security/register.html.twig
Normal file
@ -0,0 +1,6 @@
|
||||
{% extends 'base/base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<h1 class="h3 mb-3 font-weight-normal">Register new user</h1>
|
||||
{{ form(registrationForm) }}
|
||||
{% endblock %}
|
5
templates/simple.html.twig
Normal file
5
templates/simple.html.twig
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends 'base/base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
{{ text | nl2br }}
|
||||
{% endblock %}
|
26
templates/user/profile.html.twig
Normal file
26
templates/user/profile.html.twig
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "base/base.html.twig" %}
|
||||
|
||||
{% block body %}
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
<h4>{{ app.user.name }}</h4>
|
||||
{% if is_granted('ROLE_API') %}
|
||||
Your api key: {{ app.user.apiKey }} <a class="btn btn-primary" href="{{ path('user_apikey_generate') }}">Regenerate</a> <br/>
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<br/><br/>
|
||||
<h4>Latest release stats</h4>
|
||||
Branch: {{ release.branch }} <br/>
|
||||
Date: {{ release.date }} <br/>
|
||||
Hash short: {{ release.commitHashShort }} <br/>
|
||||
Hash long: {{ release.commitHashLong }} <br/>
|
||||
Commit date: {{ release.commitDate }} <br/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<h4>Change profile</h4>
|
||||
{{ form(form) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user