├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── back-end ├── .dockerignore ├── .env ├── .env.test ├── .gitignore ├── Dockerfile ├── bin │ ├── console │ └── phpunit ├── composer.json ├── config │ ├── bootstrap.php │ ├── bundles.php │ ├── jwt │ │ ├── private.pem │ │ └── public.pem │ ├── packages │ │ ├── cache.yaml │ │ ├── dev │ │ │ ├── debug.yaml │ │ │ ├── easy_log_handler.yaml │ │ │ ├── monolog.yaml │ │ │ ├── swiftmailer.yaml │ │ │ └── web_profiler.yaml │ │ ├── doctrine.yaml │ │ ├── doctrine_migrations.yaml │ │ ├── fos_rest.yaml │ │ ├── fos_user.yaml │ │ ├── framework.yaml │ │ ├── lexik_jwt_authentication.yaml │ │ ├── nelmio_cors.yaml │ │ ├── prod │ │ │ ├── doctrine.yaml │ │ │ ├── monolog.yaml │ │ │ └── routing.yaml │ │ ├── routing.yaml │ │ ├── security.yaml │ │ ├── sensio_framework_extra.yaml │ │ ├── swiftmailer.yaml │ │ ├── test │ │ │ ├── dama_doctrine_test_bundle.yaml │ │ │ ├── framework.yaml │ │ │ ├── monolog.yaml │ │ │ ├── swiftmailer.yaml │ │ │ ├── validator.yaml │ │ │ └── web_profiler.yaml │ │ ├── translation.yaml │ │ ├── twig.yaml │ │ └── validator.yaml │ ├── routes.yaml │ ├── routes │ │ ├── annotations.yaml │ │ ├── dev │ │ │ ├── twig.yaml │ │ │ └── web_profiler.yaml │ │ └── fos_user.yaml │ └── services.yaml ├── phpunit.xml.dist ├── public │ └── index.php ├── src │ ├── Controller │ │ ├── .gitignore │ │ ├── ApiController.php │ │ └── DefaultController.php │ ├── DataFixtures │ │ └── UserFixtures.php │ ├── Entity │ │ ├── .gitignore │ │ └── User.php │ ├── Kernel.php │ ├── Migrations │ │ ├── .gitignore │ │ └── Version20191115160910.php │ └── Repository │ │ └── .gitignore ├── symfony.lock ├── templates │ └── base.html.twig ├── tests │ ├── .gitignore │ └── Controller │ │ ├── ApiControllerTest.php │ │ └── DefaultControllerTest.php └── translations │ └── .gitignore ├── buy-me-a-coffee.png ├── config └── nginx │ ├── angular.conf │ ├── nginx.conf │ └── symfony.conf ├── docker-compose.yml └── front-end ├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── angular.json ├── browserslist ├── e2e ├── protractor.conf.js ├── src │ ├── app.e2e-spec.ts │ └── app.po.ts └── tsconfig.json ├── karma.conf.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── api.service.spec.ts │ ├── api.service.ts │ ├── app-routing.module.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.module.ts │ ├── hello │ │ ├── hello.component.css │ │ ├── hello.component.html │ │ ├── hello.component.spec.ts │ │ └── hello.component.ts │ ├── http-interceptor │ │ ├── index.ts │ │ └── jwt-interceptor.ts │ ├── login │ │ ├── login.component.css │ │ ├── login.component.html │ │ ├── login.component.spec.ts │ │ └── login.component.ts │ ├── token.service.spec.ts │ └── token.service.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts ├── polyfills.ts ├── styles.css └── test.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache and logs (Symfony2) 2 | /back-end/app/cache/* 3 | /back-end/app/logs/* 4 | !/back-end/app/cache/.gitkeep 5 | !/back-end/app/logs/.gitkeep 6 | 7 | 8 | # Assets and user uploads 9 | /back-end/vendor/ 10 | /back-end/web/bundles/ 11 | /back-end/web/uploads/ 12 | 13 | # PHPUnit 14 | /back-end/app/phpunit.xml 15 | /back-end/phpunit. 16 | 17 | # Dist 18 | /front-end/dist* 19 | 20 | # Composer PHAR 21 | *composer.phar 22 | *composer.lock 23 | 24 | node_modules 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | 7 | services: 8 | - postgresql 9 | 10 | before_script: 11 | - psql -c 'create database symfony_test;' -U postgres 12 | - phpenv config-rm xdebug.ini 13 | - cd back-end 14 | - composer self-update 15 | - composer install 16 | - APP_ENV=test bin/console doctrine:schema:create -q 17 | - APP_ENV=test bin/console doctrine:fixtures:load -q --purge-with-truncate 18 | 19 | script: 20 | - phpunit 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nassim Ben Ghmiss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angular-symfony [![Build Status](https://travis-ci.org/FlyersWeb/angular-symfony.svg?branch=master)](https://travis-ci.org/FlyersWeb/angular-symfony) 2 | =============== 3 | 4 | Project Bootstrap for an Angular 2+ and Symfony 4+ webservices project. 5 | 6 | Introduction 7 | ------------ 8 | 9 | This project is a template application with a secured RestFul API communication via JWT security scheme. 10 | 11 | Buy me a coffee 12 | --------------- 13 | 14 | [![Buy me a coffee](https://raw.githubusercontent.com/FlyersWeb/angular-symfony/master/buy-me-a-coffee.png)](https://paypal.me/nac1dbois) 15 | 16 | I'm working on this project in my free time and offering it free of charges. To help me work more on this you can send me tips to buy more coffee :) 17 | 18 | Installation 19 | ------------ 20 | 21 | Install docker and docker-compose. 22 | 23 | Clone the project : 24 | 25 | git clone git@github.com:FlyersWeb/angular-symfony.git 26 | 27 | Launch dockerized environment : 28 | 29 | docker-compose up -d 30 | 31 | Log in application docker image : 32 | 33 | docker-compose exec application bash 34 | 35 | Install dependencies : 36 | 37 | composer install 38 | 39 | Create database if necessary : 40 | 41 | php bin/console doctrine:database:create 42 | 43 | Create schemas (FOSUserBundle) : 44 | 45 | php bin/console doctrine:schema:create 46 | 47 | Create and activate user : 48 | 49 | php bin/console doctrine:fixtures:load 50 | 51 | Access the front end using port 4200 : 52 | 53 | firefox http://localhost:4200 & 54 | 55 | Launching tests 56 | --------------- 57 | 58 | If you want to contribute to project you'll need to have tests to pass. So in order to run them you'll need to : 59 | 60 | Log in application docker image : 61 | 62 | docker-compose exec application bash 63 | 64 | Update database connection information in `.env.test` 65 | 66 | Create database : 67 | 68 | php bin/console doctrine:database:create --env=test 69 | 70 | Create schemas (FOSUserBundle) : 71 | 72 | php bin/console doctrine:schema:create --env=test 73 | 74 | Create and activate user : 75 | 76 | php bin/console doctrine:fixtures:load --env=test 77 | 78 | Copy Phpunit config : 79 | 80 | cp phpunit.xml.dist phpunit.xml 81 | 82 | Launch tests using : 83 | 84 | bin/phpunit 85 | 86 | Authentication system 87 | --------------------- 88 | 89 | The Authentication system is based on the JWT token as implemented by [Lexik](https://github.com/lexik/LexikJWTAuthenticationBundle) 90 | 91 | User management is done through [FOSUserBundle](https://github.com/FriendsOfSymfony/FOSUserBundle), you can easily add / edit / delete users by using their API. 92 | 93 | The server provides a Rest API using [FOSRestBundle](https://github.com/FriendsOfSymfony/FOSRestBundle) allowing you to connect using the following query: 94 | 95 | `curl -X POST -H "Content-Type: application/json" http://localhost:8000/api/login_check -d '{"username":"bob","password":"Abc123"}'` 96 | 97 | Client Side specifics 98 | --------------------- 99 | 100 | On the client side, I've inspired my code from Angular official documentation about HttpInterceptor, allowing me to send the JWT Token on each HTTP request when token is available. 101 | 102 | The token is sent in *Authorization* headers: 103 | 104 | `Authorization: Bearer xxx` 105 | 106 | LICENSE 107 | ------- 108 | 109 | This program is free software. It comes without any warranty, to the extent permitted by applicable law. 110 | 111 | This software is LICENSED under the MIT License. Use it at your own risk. 112 | 113 | WARNING 114 | ------- 115 | 116 | Servers are configured for developments purposes. Do not deploy this project on production as is. You should have a look to [Symfony deployment documentation](https://symfony.com/doc/4.4/deployment.html) for the Back-end and the [Angular deployment documentation](https://angular.io/guide/deployment) for the Front-End part. 117 | 118 | You should also change the preconfigured keys for signatures by generating your own keys using : 119 | 120 | openssl genpkey -out config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096 121 | openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout 122 | 123 | And copy the passphrase into the field `JWT_PASSPHRASE` in `.env` file. 124 | 125 | You should also update the `APP_SECRET` in `.env` file. 126 | 127 | Conclusion 128 | ---------- 129 | 130 | You can use this template and adapt it to your needs. 131 | 132 | @FlyersWeb 133 | -------------------------------------------------------------------------------- /back-end/.dockerignore: -------------------------------------------------------------------------------- 1 | app/cache/* 2 | app/logs/* 3 | vendor/* -------------------------------------------------------------------------------- /back-end/.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the latter taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 15 | 16 | ###> symfony/framework-bundle ### 17 | APP_ENV=dev 18 | APP_SECRET=abb685fe97081c338ed39e8b1cb4bd69 19 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 20 | #TRUSTED_HOSTS='^localhost|example\.com$' 21 | ###< symfony/framework-bundle ### 22 | 23 | ###> doctrine/doctrine-bundle ### 24 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 25 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 26 | # For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11" 27 | # IMPORTANT: You MUST configure your db driver and server version, either here or in config/packages/doctrine.yaml 28 | DATABASE_URL=postgresql://postgres:postgres@database:5432/symfony_dev?serverVersion=10.10 29 | ###< doctrine/doctrine-bundle ### 30 | 31 | ###> symfony/swiftmailer-bundle ### 32 | # For Gmail as a transport, use: "gmail://username:password@localhost" 33 | # For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" 34 | # Delivery is disabled by default via "null://localhost" 35 | MAILER_URL=null://localhost 36 | ###< symfony/swiftmailer-bundle ### 37 | 38 | ###> nelmio/cors-bundle ### 39 | CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$ 40 | ###< nelmio/cors-bundle ### 41 | 42 | ###> lexik/jwt-authentication-bundle ### 43 | JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem 44 | JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem 45 | JWT_PASSPHRASE=7724eb8ec2bcd93bade2b15bcd946911 46 | ###< lexik/jwt-authentication-bundle ### 47 | -------------------------------------------------------------------------------- /back-end/.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_ENV=test 4 | APP_SECRET='$ecretf0rt3st' 5 | SYMFONY_DEPRECATIONS_HELPER=999999 6 | PANTHER_APP_ENV=panther 7 | DATABASE_URL=postgresql://postgres@localhost:5432/symfony_test?serverVersion=9.2 8 | -------------------------------------------------------------------------------- /back-end/.gitignore: -------------------------------------------------------------------------------- 1 | var 2 | -------------------------------------------------------------------------------- /back-end/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.4-fpm 2 | 3 | # Install Composer 4 | RUN apt-get update && apt-get install -y \ 5 | openssl \ 6 | git \ 7 | unzip 8 | 9 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ 10 | && composer --version 11 | 12 | ADD . /var/www 13 | WORKDIR /var/www 14 | 15 | RUN apt-get update && apt-get install -y \ 16 | libfreetype6-dev \ 17 | libjpeg62-turbo-dev \ 18 | libmcrypt-dev \ 19 | libpq-dev \ 20 | libpng-dev 21 | 22 | RUN pecl install mcrypt-1.0.3 23 | 24 | RUN docker-php-ext-enable mcrypt \ 25 | && docker-php-ext-configure gd --with-freetype --with-jpeg \ 26 | && docker-php-ext-install -j$(nproc) gd pdo_pgsql 27 | 28 | # Use the default production configuration 29 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 30 | 31 | RUN usermod -u 1000 www-data \ 32 | && mkdir -p /var/www/app/cache \ 33 | && mkdir -p /var/www/app/logs \ 34 | && chmod -R 777 /var/www/app/cache \ 35 | && chmod -R 777 /var/www/app/logs 36 | 37 | EXPOSE 9000 38 | CMD ["php-fpm"] 39 | -------------------------------------------------------------------------------- /back-end/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 23 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 24 | } 25 | 26 | if ($input->hasParameterOption('--no-debug', true)) { 27 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 28 | } 29 | 30 | require dirname(__DIR__).'/config/bootstrap.php'; 31 | 32 | if ($_SERVER['APP_DEBUG']) { 33 | umask(0000); 34 | 35 | if (class_exists(Debug::class)) { 36 | Debug::enable(); 37 | } 38 | } 39 | 40 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 41 | $application = new Application($kernel); 42 | $application->run($input); 43 | -------------------------------------------------------------------------------- /back-end/bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | =1.2) 9 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { 10 | foreach ($env as $k => $v) { 11 | $_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v); 12 | } 13 | } elseif (!class_exists(Dotenv::class)) { 14 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); 15 | } else { 16 | // load all the .env files 17 | (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); 18 | } 19 | 20 | $_SERVER += $_ENV; 21 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 22 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 23 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 24 | -------------------------------------------------------------------------------- /back-end/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 6 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], 7 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 8 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 9 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 10 | Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], 11 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 12 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 13 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 14 | Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true], 15 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 16 | Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], 17 | FOS\UserBundle\FOSUserBundle::class => ['all' => true], 18 | FOS\RestBundle\FOSRestBundle::class => ['all' => true], 19 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], 20 | Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], 21 | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], 22 | DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], 23 | ]; 24 | -------------------------------------------------------------------------------- /back-end/config/jwt/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIJnzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIu9HJIGBAV74CAggA 3 | MB0GCWCGSAFlAwQBKgQQ+l0f/ekDwk6LHbsS9uLKrASCCVAJxBdrl7eAdlOBfmNr 4 | WUMxGTdsn3QM8DJByfwD/AKMW3GYRgAT9kVz/5vwxQp0r1RsfwgEY5itFXOqzW9U 5 | 4x9EfTjgjstN42dqAx2Xni1YFOwUjK9t9/Oae4+gdzewuA4soSzmmQa6/HXzWlZW 6 | uaIGtgCQf2d76TwVkr1UTZBwlhcswv0v4pYA3jmW1m8pLSoiQvTRHoIOHXq//3Fp 7 | TxnLiGzxdg0AtZdrfO1OA+HUw+F6Yw3bmveWnxf41wsnl+Y1CSMVd1HjlnbINcRp 8 | inmJoZir5VJEjEwpO2R+ASwWgSwdIdLswFR83UkZ5TNwmWP4+v9AFNvM9ikCGibs 9 | zKu9CSoyXoQB9KjKSheKI++KeED7OJbsS2LbCBGm1B40CSaXu8NUi0kW2lxTjf0z 10 | gUxhps1JbqTy+WdOTxlP5AA4uZPTrNq04pPswqcHDgoWMCgUcBFyQ9fqOoAbWOj4 11 | d0yTW0tpuMqsizC6HOW227QKzWJd93dU9x7ajLzxp3F9wyCShm6NpDqdyGU4CiHk 12 | cjNBy+C8HOEaeWLyTk9ZHXkkVGgaT6o1YunECyz4IkpA0mxYfQtX65XMDSa7tXOq 13 | 7Lq1HWsIaUj50AXUfTGtOwtWyt7ldL5i3Ih0pOBLmsDKFv6yuaQcKdm9RZvybb3+ 14 | AWP7yz4iqZV5xtyHzPCFGDn2YyXAdTwc1ZtaH/QJfrVsdBvbNVLjCnVM7fbQHEH0 15 | Iske1K+1U9sZjQf804urwlK4+dbXKWYvmf0RR35U0AyEG9Scc5lch6NLlb5Gt57Z 16 | 1WYdyORDgNSmU+PPCrYpQaatWQA/MTUMRklGputdjKtX5qJrMVOJsIazY8qXK9rD 17 | 7aqNFdVsHV2pmdj1Y4h2SzwWlmc8SaHlZdo+44hS57SjN3QaQL4oWJPjgyT2IBQ0 18 | UDOd5BdxkUpaUWMzNZcLAD5pigOmQAHJ1GEWvPw5zRztLUYeYXA8R0axwuKDru1k 19 | Ncm4QffwuxuCLpxEeQtxn+U5DTTv8L5a6kqbsr/5vCRcLHJeeXbWQF6hi5YTahf+ 20 | 8zhDpedBQ/Cy+/04ZRyUgnFn1UGzg24PfG0f0AKOtbwx1xXII3cFafiUCU35eMPg 21 | NfyWzjz0Dz66rq+PYxMKNdqiiO/C+GnuxDcFvNmNv0Soda4eBvmtnZorh3BUgdNw 22 | MkbVp2xiHoJZTWfdxYsGuSi5Z5/jq2VSUNQ2c+Y6Dkq42k5wh0oe1M0u3D4j9JPK 23 | 2ybE8PHC2jRkvvQcxgEtDA843JD5ud344XmdZe4jOhMZxWGsKhz3YjyuKp/jbq1e 24 | IuXtwvVk5DIMTz0AzmpvNNS04+Fq1Eg0xIXIq9BCyMWOg14s6uNCK6eY8ux3vFTO 25 | SueZayqbcLQWLUoIgqiFUifq/hxbWP2J8eunSJwjhi0TLiU9qyiw38Rd9CJSaunP 26 | lC14d5ESGzJvgzY0HoSUfstG0KL9cP5s4P58SwKU2/qVKYgcb60aU+xRzW/esBmo 27 | 3kLJSAszkRKAe2vQn9pdNuSPBjoCirYYr2XqjYPjeBPeL6lDuf0bVmhal/VLaHls 28 | SjSn8iTKduJU9kyJjGF9Yqh7FFkbu/HasVPKNOZHPbcgffuq8laShZYncTBq557q 29 | OpNl113BLjaaOp3aB1OcTC5CqoLKBFPxq/oIdy4x75tgM74yekECLUZqCv5Nn8Nt 30 | R1fkLISCF2FApGE3/w1c6m9gSmnaelNJ9RFqylf2Q5qvbBBAe0nEagXfU3xsW/gs 31 | YtgffFIvBSowdT/yj7i8FMB3Uo8DUzCi1vAfOmR7Nhv9s/kfnw5+8qr4DvKoc7ng 32 | Jhh2lbI9drb9kuNgyczzzhOiNqNsQOMSvjqzgxkC2L2VLbuImq/x5KZQzSWbcgdh 33 | 88bplFfBSqWiR5hnr2DTFYZGQQXd8GCii89jXqhkXBG5QI0tPs1Qx68HCIla/obR 34 | VquulBzG524jE3F+/FG9rsf1fQ1bhS7g0yqdiEnCJX1SdbMtYjrgr5tVV82I/lsK 35 | ytlpWe1QhPmjBoQZTVt3K4TltjlGwuBxCjQw48bdpxlB3p3Exaso4tyU9w3TarHz 36 | moOQh5PKlcfhQl0Yzqpgdvfv9E72EcNR3aRKxbWIpvVIOKEZ8zK90ICsJ66I2K/X 37 | epRMyGGzFaTMMgIsjtt8+/l9O3pgW2ZvjwiSiDICJCbP7OD7uPZDhuxF1CSKAgOA 38 | T32R9g7mvW0y75ZjVZdD+7thcgGQsw8EKOavfOgISsxke4pzLPfWDonw5FWTOYNw 39 | 5tWubhUxYfMEuE1y4M+f2bnhwJYdTUoY5pPeeS1bpnO7lINHh52/zxrpvssH4Flm 40 | 5E4X9Jtbrh7aT8+Ao1hWva/spFa5VgaIrYdxZlj0x2Z/R0+d7spEtHPgLFiX15dz 41 | 7Sir+7cs7aL9qosjMdvLg8eiUQAxDh3OGw/NJ2MQU4MULYeq9S1OI3HLLctg9DgJ 42 | q26d0OQdQEZ9nmGIOvkKhy4yNkqiKTUXA0k0d3+aaKnmtBvxKXQyilum/nUvzU9S 43 | nR37aITffiyTmNYTtqgPneKQPX1FZQntQgSJUEw0scfzThuCgu0mWlkGoq8rvnX7 44 | 6trZM7yo4WepBW+S+CDHlgvvDOFFndRQatAynXE2knaiEyhFgCUWJRizeZwI3R23 45 | M7HIKZRFtynwAZoK/3eSFxSxLvSvAHzDH/r0euUHJsK/41sGB080M+DKYm5haKTM 46 | CTBa4hz6+2bKvLbo56jPDZHC5U3UyL6dbc6pKFJnAFMP/2Q90z5An321bUt1sHFz 47 | SN5+RozvwRB9M4lfFPzc9P2MXpyMJqJ+T8XJrLcd4iTTN5yqO1RcpjeO4/0Uk8/T 48 | tZ3tLrkYKfcTQCTjWsIN0XCndxN9ybkCGYw+eDr/KKzkK86FSXi6saB+oCaXLFcH 49 | cY4h7EVmjaCp069EIsZPjXiV+vgPFSK0DHoSakU61HTy0gXSdd10psFzBa4G+faj 50 | t5fPsGyb1/I1LTrhRmImNE+xyHTAkhv041H1aIYBpUzR03sCefq39S2BnoUUa9lP 51 | aQyZczmhfGCwuLs7BgNK86weWQqPs80qwGvTlNF1RtAuwEILxfyHWGXzQcoYDko8 52 | E05B9XPIZzSeIRjOQW9HB7H72zWGtp4jT12RrYngSAWpJ3zVlWNlALaeO4tt+Cdx 53 | VMOvT0HwSamtQdGoZTvjjZbKtA== 54 | -----END ENCRYPTED PRIVATE KEY----- 55 | -------------------------------------------------------------------------------- /back-end/config/jwt/public.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwEJ4isNm/VxgIYUCp/qs 3 | WMU6EEXpsiLhNHtyYC5ThoTLAIjmgC5nncQrpZ936PoBRaqXVC6U/+bS6cEyTGT/ 4 | nJkXRl+CznIJ+2j3X0N5pcbgfhWnTFsgI3CkKNLG57W4F+squAHVr3bxouRy4d6B 5 | goKrrdkaZifxA9MB5RB1XQuEEPiBRZC+x3XugxAY0AEius2g6i3XyKahUcptDi+Q 6 | GhjttJBQ58nmB4J9iOb5bTvFK7wkSv29qU9qpw8LwTYPkWgnYzUTIOEzZS172vB2 7 | +rkc3LirmKwrTtA70JLS6lBoG2zBIa97bYocYKYjGdOlb1Yk1pTZyKWY5pNPH9jY 8 | bTvJ4F/wO224W08J6LA19fy6ZMORMgRsCO4iUU0AWW7yQ08+x8qHDREOMERNgZLy 9 | A+kMILNLa9BTBhAG2tdwt2LagYdPdbgUUS4m6S9kYenBhrqehwDYlKuO66ZxCCCA 10 | CkqG8We5oyUKWiM4J923Ai9SlSCJK9G1wRa4ShhmI9K/RGPYKkgslJuHJ0zE0Z3n 11 | FoPjgJVYr+67c2vJoULa4xXJIGwODro5fD1/Qi1uMhgsn+sVqQbqmixSr/Mc6rpJ 12 | HRq0GsZ2aCL4wZ7KnjhUfYmivpGAHZb24bzf7r1gKu6j4B/hAg6EYCAK0okHJUDy 13 | OZjAiWdrPITL+wOKj0o0jS8CAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /back-end/config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | app: cache.adapter.filesystem 4 | system: cache.adapter.system 5 | # Put the unique name of your app here: the prefix seed 6 | # is used to compute stable namespaces for cache keys. 7 | #prefix_seed: your_vendor_name/app_name 8 | # The app cache caches to the filesystem by default. 9 | # Other options include: 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 14 | #app: cache.adapter.apcu 15 | # Namespaced pools use the above "app" backend by default 16 | #pools: 17 | #my.dedicated.cache: null 18 | -------------------------------------------------------------------------------- /back-end/config/packages/dev/debug.yaml: -------------------------------------------------------------------------------- 1 | debug: 2 | # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. 3 | # See the "server:dump" command to start a new server. 4 | dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" 5 | -------------------------------------------------------------------------------- /back-end/config/packages/dev/easy_log_handler.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | EasyCorp\EasyLog\EasyLogHandler: 3 | public: false 4 | arguments: ['%kernel.logs_dir%/%kernel.environment%.log'] 5 | 6 | #// FIXME: How to add this configuration automatically without messing up with the monolog configuration? 7 | #monolog: 8 | # handlers: 9 | # buffered: 10 | # type: buffer 11 | # handler: easylog 12 | # channels: ['!event'] 13 | # level: debug 14 | # easylog: 15 | # type: service 16 | # id: EasyCorp\EasyLog\EasyLogHandler 17 | -------------------------------------------------------------------------------- /back-end/config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "php://stderr" 6 | level: debug 7 | channels: ["!event"] 8 | # uncomment to get logging in your browser 9 | # you may have to allow bigger header sizes in your Web server configuration 10 | #firephp: 11 | # type: firephp 12 | # level: info 13 | #chromephp: 14 | # type: chromephp 15 | # level: info 16 | console: 17 | type: console 18 | process_psr_3_messages: false 19 | channels: ["!event", "!doctrine", "!console"] 20 | -------------------------------------------------------------------------------- /back-end/config/packages/dev/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | # See https://symfony.com/doc/current/email/dev_environment.html 2 | swiftmailer: 3 | # send all emails to a specific address 4 | #delivery_addresses: ['me@example.com'] 5 | -------------------------------------------------------------------------------- /back-end/config/packages/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { only_exceptions: false } 7 | -------------------------------------------------------------------------------- /back-end/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | url: "%env(resolve:DATABASE_URL)%" 4 | 5 | # IMPORTANT: You MUST configure your db driver and server version, 6 | # either here or in the DATABASE_URL env var (see .env file) 7 | driver: "pgsql" 8 | server_version: "10.10" 9 | 10 | # Only needed for MySQL (ignored otherwise) 11 | # charset: utf8mb4 12 | # default_table_options: 13 | # collate: utf8mb4_unicode_ci 14 | orm: 15 | auto_generate_proxy_classes: true 16 | naming_strategy: doctrine.orm.naming_strategy.underscore 17 | auto_mapping: true 18 | mappings: 19 | App: 20 | is_bundle: false 21 | type: annotation 22 | dir: "%kernel.project_dir%/src/Entity" 23 | prefix: 'App\Entity' 24 | alias: App 25 | -------------------------------------------------------------------------------- /back-end/config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | dir_name: "%kernel.project_dir%/src/Migrations" 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | namespace: DoctrineMigrations 6 | -------------------------------------------------------------------------------- /back-end/config/packages/fos_rest.yaml: -------------------------------------------------------------------------------- 1 | fos_rest: 2 | param_fetcher_listener: true 3 | allowed_methods_listener: true 4 | view: 5 | view_response_listener: true 6 | format_listener: 7 | rules: 8 | - { 9 | path: ^/, 10 | prefer_extension: true, 11 | fallback_format: json, 12 | priorities: [json], 13 | } 14 | -------------------------------------------------------------------------------- /back-end/config/packages/fos_user.yaml: -------------------------------------------------------------------------------- 1 | fos_user: 2 | db_driver: orm 3 | firewall_name: main 4 | user_class: App\Entity\User 5 | # error The service "security.authentication.provider.dao.main" has a dependency on a non-existent service "fos_user.user_checker". 6 | service: 7 | mailer: fos_user.mailer.twig_swift 8 | from_email: 9 | address: "contact@company.com" 10 | sender_name: "contact@company.com" 11 | -------------------------------------------------------------------------------- /back-end/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: "%env(APP_SECRET)%" 3 | #csrf_protection: true 4 | #http_method_override: true 5 | 6 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 7 | # Remove or comment this section to explicitly disable session support. 8 | session: 9 | handler_id: null 10 | cookie_secure: auto 11 | cookie_samesite: lax 12 | 13 | #esi: true 14 | #fragments: true 15 | php_errors: 16 | log: true 17 | -------------------------------------------------------------------------------- /back-end/config/packages/lexik_jwt_authentication.yaml: -------------------------------------------------------------------------------- 1 | lexik_jwt_authentication: 2 | secret_key: "%env(resolve:JWT_SECRET_KEY)%" 3 | public_key: "%env(resolve:JWT_PUBLIC_KEY)%" 4 | pass_phrase: "%env(JWT_PASSPHRASE)%" 5 | -------------------------------------------------------------------------------- /back-end/config/packages/nelmio_cors.yaml: -------------------------------------------------------------------------------- 1 | nelmio_cors: 2 | defaults: 3 | origin_regex: true 4 | allow_origin: ["%env(CORS_ALLOW_ORIGIN)%"] 5 | allow_methods: ["GET", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"] 6 | allow_headers: ["Content-Type", "Authorization", "X-WSSE"] 7 | expose_headers: ["Link"] 8 | max_age: 3600 9 | paths: 10 | "^/": null 11 | -------------------------------------------------------------------------------- /back-end/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: service 6 | id: doctrine.system_cache_provider 7 | query_cache_driver: 8 | type: service 9 | id: doctrine.system_cache_provider 10 | result_cache_driver: 11 | type: service 12 | id: doctrine.result_cache_provider 13 | 14 | services: 15 | doctrine.result_cache_provider: 16 | class: Symfony\Component\Cache\DoctrineProvider 17 | public: false 18 | arguments: 19 | - '@doctrine.result_cache_pool' 20 | doctrine.system_cache_provider: 21 | class: Symfony\Component\Cache\DoctrineProvider 22 | public: false 23 | arguments: 24 | - '@doctrine.system_cache_pool' 25 | 26 | framework: 27 | cache: 28 | pools: 29 | doctrine.result_cache_pool: 30 | adapter: cache.app 31 | doctrine.system_cache_pool: 32 | adapter: cache.system 33 | -------------------------------------------------------------------------------- /back-end/config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_http_codes: [404, 405] 8 | nested: 9 | type: stream 10 | path: "php://stderr" 11 | level: debug 12 | console: 13 | type: console 14 | process_psr_3_messages: false 15 | channels: ["!event", "!doctrine"] 16 | deprecation: 17 | type: stream 18 | path: "php://stderr" 19 | deprecation_filter: 20 | type: filter 21 | handler: deprecation 22 | max_level: info 23 | channels: ["php"] 24 | -------------------------------------------------------------------------------- /back-end/config/packages/prod/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: null 4 | -------------------------------------------------------------------------------- /back-end/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | -------------------------------------------------------------------------------- /back-end/config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | encoders: 3 | App\Entity\User: 4 | algorithm: sha512 5 | encode_as_base64: true 6 | iterations: 5000 7 | 8 | role_hierarchy: 9 | ROLE_ADMIN: ROLE_USER 10 | ROLE_SUPER_ADMIN: ROLE_ADMIN 11 | 12 | providers: 13 | fos_userbundle: 14 | id: fos_user.user_provider.username 15 | 16 | firewalls: 17 | login: 18 | pattern: ^/api/login 19 | stateless: true 20 | anonymous: true 21 | json_login: 22 | check_path: /api/login_check 23 | success_handler: lexik_jwt_authentication.handler.authentication_success 24 | failure_handler: lexik_jwt_authentication.handler.authentication_failure 25 | api: 26 | pattern: ^/api 27 | stateless: true 28 | guard: 29 | authenticators: 30 | - lexik_jwt_authentication.jwt_token_authenticator 31 | 32 | # Easy way to control access for large sections of your site 33 | # Note: Only the *first* access control that matches will be used 34 | access_control: 35 | - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 36 | - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } 37 | -------------------------------------------------------------------------------- /back-end/config/packages/sensio_framework_extra.yaml: -------------------------------------------------------------------------------- 1 | sensio_framework_extra: 2 | router: 3 | annotations: false 4 | -------------------------------------------------------------------------------- /back-end/config/packages/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | swiftmailer: 2 | url: "%env(MAILER_URL)%" 3 | spool: { type: "memory" } 4 | -------------------------------------------------------------------------------- /back-end/config/packages/test/dama_doctrine_test_bundle.yaml: -------------------------------------------------------------------------------- 1 | dama_doctrine_test: 2 | enable_static_connection: true 3 | enable_static_meta_data_cache: true 4 | enable_static_query_cache: true 5 | -------------------------------------------------------------------------------- /back-end/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /back-end/config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "php://stderr" 6 | level: debug 7 | channels: ["!event"] 8 | -------------------------------------------------------------------------------- /back-end/config/packages/test/swiftmailer.yaml: -------------------------------------------------------------------------------- 1 | swiftmailer: 2 | disable_delivery: true 3 | -------------------------------------------------------------------------------- /back-end/config/packages/test/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | not_compromised_password: false 4 | -------------------------------------------------------------------------------- /back-end/config/packages/test/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: false 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { collect: false } 7 | -------------------------------------------------------------------------------- /back-end/config/packages/translation.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | default_locale: en 3 | translator: 4 | default_path: "%kernel.project_dir%/translations" 5 | fallbacks: 6 | - en 7 | -------------------------------------------------------------------------------- /back-end/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: "%kernel.project_dir%/templates" 3 | debug: "%kernel.debug%" 4 | strict_variables: "%kernel.debug%" 5 | -------------------------------------------------------------------------------- /back-end/config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | # Enables validator auto-mapping support. 5 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 6 | #auto_mapping: 7 | # App\Entity\: [] 8 | -------------------------------------------------------------------------------- /back-end/config/routes.yaml: -------------------------------------------------------------------------------- 1 | api_login_check: 2 | path: /api/login_check 3 | -------------------------------------------------------------------------------- /back-end/config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /back-end/config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@TwigBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 4 | -------------------------------------------------------------------------------- /back-end/config/routes/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler_wdt: 2 | resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' 3 | prefix: /_wdt 4 | 5 | web_profiler_profiler: 6 | resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' 7 | prefix: /_profiler 8 | -------------------------------------------------------------------------------- /back-end/config/routes/fos_user.yaml: -------------------------------------------------------------------------------- 1 | fos_user: 2 | resource: "@FOSUserBundle/Resources/config/routing/all.xml" -------------------------------------------------------------------------------- /back-end/config/services.yaml: -------------------------------------------------------------------------------- 1 | # This file is the entry point to configure your own services. 2 | # Files in the packages/ subdirectory configure your dependencies. 3 | 4 | # Put parameters here that don't need to change on each machine where the app is deployed 5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 6 | parameters: 7 | 8 | services: 9 | # default configuration for services in *this* file 10 | _defaults: 11 | autowire: true # Automatically injects dependencies in your services. 12 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 13 | 14 | # makes classes in src/ available to be used as services 15 | # this creates a service per class whose id is the fully-qualified class name 16 | App\: 17 | resource: "../src/*" 18 | exclude: "../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}" 19 | 20 | # controllers are imported separately to make sure services can be injected 21 | # as action arguments even if you don't extend any base controller class 22 | App\Controller\: 23 | resource: "../src/Controller" 24 | tags: ["controller.service_arguments"] 25 | # add more service definitions when explicit configuration is needed 26 | # please note that last definitions always *replace* previous ones 27 | -------------------------------------------------------------------------------- /back-end/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | tests 21 | 22 | 23 | 24 | 25 | 26 | src 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /back-end/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /back-end/src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/src/Controller/.gitignore -------------------------------------------------------------------------------- /back-end/src/Controller/ApiController.php: -------------------------------------------------------------------------------- 1 | json(['hello' => 'world']); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /back-end/src/Controller/DefaultController.php: -------------------------------------------------------------------------------- 1 | passwordEncoder = $passwordEncoder; 17 | } 18 | 19 | public function load(ObjectManager $manager) 20 | { 21 | $user = new User(); 22 | 23 | $user->setUsername('bob'); 24 | $user->setUsernameCanonical('Bobby'); 25 | $user->setEmail('bob@doe.com'); 26 | $user->setEnabled(true); 27 | // Password is Abc123 28 | $user->setSalt('e4TNCCgLvPbDRh7ih+pK58pab0NToFzdZHuPmA0e'); 29 | $user->setPassword('sM1GM+zlChZH4xlLokuCueeWSDq+4I7XGtn+GErMF1ehMVrFRXWklCK0/LtTZ4gQEebbLW9lrSS0ocA/9/12Gw=='); 30 | 31 | $manager->persist($user); 32 | $manager->flush(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /back-end/src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/src/Entity/.gitignore -------------------------------------------------------------------------------- /back-end/src/Entity/User.php: -------------------------------------------------------------------------------- 1 | getProjectDir().'/var/cache/'.$this->environment; 20 | } 21 | 22 | public function getLogDir() 23 | { 24 | return $this->getProjectDir().'/var/log'; 25 | } 26 | 27 | public function registerBundles() 28 | { 29 | $contents = require $this->getProjectDir().'/config/bundles.php'; 30 | foreach ($contents as $class => $envs) { 31 | if (isset($envs['all']) || isset($envs[$this->environment])) { 32 | yield new $class(); 33 | } 34 | } 35 | } 36 | 37 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) 38 | { 39 | $container->setParameter('container.autowiring.strict_mode', true); 40 | $container->setParameter('container.dumper.inline_class_loader', true); 41 | $confDir = $this->getProjectDir().'/config'; 42 | $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob'); 43 | if (is_dir($confDir.'/packages/'.$this->environment)) { 44 | $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); 45 | } 46 | $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob'); 47 | $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob'); 48 | } 49 | 50 | protected function configureRoutes(RouteCollectionBuilder $routes) 51 | { 52 | $confDir = $this->getProjectDir().'/config'; 53 | if (is_dir($confDir.'/routes/')) { 54 | $routes->import($confDir.'/routes/*'.self::CONFIG_EXTS, '/', 'glob'); 55 | } 56 | if (is_dir($confDir.'/routes/'.$this->environment)) { 57 | $routes->import($confDir.'/routes/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); 58 | } 59 | $routes->import($confDir.'/routes'.self::CONFIG_EXTS, '/', 'glob'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /back-end/src/Migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/src/Migrations/.gitignore -------------------------------------------------------------------------------- /back-end/src/Migrations/Version20191115160910.php: -------------------------------------------------------------------------------- 1 | abortIf('postgresql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'postgresql\'.'); 24 | 25 | $this->addSql('CREATE SEQUENCE fos_user_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); 26 | $this->addSql('CREATE TABLE fos_user (id INT NOT NULL, username VARCHAR(180) NOT NULL, username_canonical VARCHAR(180) NOT NULL, email VARCHAR(180) NOT NULL, email_canonical VARCHAR(180) NOT NULL, enabled BOOLEAN NOT NULL, salt VARCHAR(255) DEFAULT NULL, password VARCHAR(255) NOT NULL, last_login TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL, password_requested_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, roles TEXT NOT NULL, PRIMARY KEY(id))'); 27 | $this->addSql('CREATE UNIQUE INDEX UNIQ_957A647992FC23A8 ON fos_user (username_canonical)'); 28 | $this->addSql('CREATE UNIQUE INDEX UNIQ_957A6479A0D96FBF ON fos_user (email_canonical)'); 29 | $this->addSql('CREATE UNIQUE INDEX UNIQ_957A6479C05FB297 ON fos_user (confirmation_token)'); 30 | $this->addSql('COMMENT ON COLUMN fos_user.roles IS \'(DC2Type:array)\''); 31 | } 32 | 33 | public function down(Schema $schema): void 34 | { 35 | // this down() migration is auto-generated, please modify it to your needs 36 | $this->abortIf('postgresql' !== $this->connection->getDatabasePlatform()->getName(), 'Migration can only be executed safely on \'postgresql\'.'); 37 | 38 | $this->addSql('CREATE SCHEMA public'); 39 | $this->addSql('DROP SEQUENCE fos_user_id_seq CASCADE'); 40 | $this->addSql('DROP TABLE fos_user'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /back-end/src/Repository/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/src/Repository/.gitignore -------------------------------------------------------------------------------- /back-end/symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "dama/doctrine-test-bundle": { 3 | "version": "4.0", 4 | "recipe": { 5 | "repo": "github.com/symfony/recipes-contrib", 6 | "branch": "master", 7 | "version": "4.0", 8 | "ref": "56eaa387b5e48ebcc7c95a893b47dfa1ad51449c" 9 | }, 10 | "files": [ 11 | "config/packages/test/dama_doctrine_test_bundle.yaml" 12 | ] 13 | }, 14 | "doctrine/annotations": { 15 | "version": "1.0", 16 | "recipe": { 17 | "repo": "github.com/symfony/recipes", 18 | "branch": "master", 19 | "version": "1.0", 20 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672" 21 | }, 22 | "files": [ 23 | "config/routes/annotations.yaml" 24 | ] 25 | }, 26 | "doctrine/cache": { 27 | "version": "1.9.0" 28 | }, 29 | "doctrine/collections": { 30 | "version": "1.6.4" 31 | }, 32 | "doctrine/common": { 33 | "version": "v2.11.0" 34 | }, 35 | "doctrine/data-fixtures": { 36 | "version": "1.4.0" 37 | }, 38 | "doctrine/dbal": { 39 | "version": "v2.10.0" 40 | }, 41 | "doctrine/doctrine-bundle": { 42 | "version": "1.6", 43 | "recipe": { 44 | "repo": "github.com/symfony/recipes", 45 | "branch": "master", 46 | "version": "1.6", 47 | "ref": "ae7f137d0cacaf8f259f4e05af59e326f065ba5e" 48 | }, 49 | "files": [ 50 | "config/packages/doctrine.yaml", 51 | "config/packages/prod/doctrine.yaml", 52 | "src/Entity/.gitignore", 53 | "src/Repository/.gitignore" 54 | ] 55 | }, 56 | "doctrine/doctrine-cache-bundle": { 57 | "version": "1.3.5" 58 | }, 59 | "doctrine/doctrine-fixtures-bundle": { 60 | "version": "3.0", 61 | "recipe": { 62 | "repo": "github.com/symfony/recipes", 63 | "branch": "master", 64 | "version": "3.0", 65 | "ref": "fc52d86631a6dfd9fdf3381d0b7e3df2069e51b3" 66 | }, 67 | "files": [ 68 | "src/DataFixtures/AppFixtures.php" 69 | ] 70 | }, 71 | "doctrine/doctrine-migrations-bundle": { 72 | "version": "1.2", 73 | "recipe": { 74 | "repo": "github.com/symfony/recipes", 75 | "branch": "master", 76 | "version": "1.2", 77 | "ref": "c1431086fec31f17fbcfe6d6d7e92059458facc1" 78 | }, 79 | "files": [ 80 | "config/packages/doctrine_migrations.yaml", 81 | "src/Migrations/.gitignore" 82 | ] 83 | }, 84 | "doctrine/event-manager": { 85 | "version": "1.1.0" 86 | }, 87 | "doctrine/inflector": { 88 | "version": "1.3.1" 89 | }, 90 | "doctrine/instantiator": { 91 | "version": "1.3.0" 92 | }, 93 | "doctrine/lexer": { 94 | "version": "1.2.0" 95 | }, 96 | "doctrine/migrations": { 97 | "version": "2.2.0" 98 | }, 99 | "doctrine/orm": { 100 | "version": "v2.6.4" 101 | }, 102 | "doctrine/persistence": { 103 | "version": "1.2.0" 104 | }, 105 | "doctrine/reflection": { 106 | "version": "v1.0.0" 107 | }, 108 | "easycorp/easy-log-handler": { 109 | "version": "1.0", 110 | "recipe": { 111 | "repo": "github.com/symfony/recipes", 112 | "branch": "master", 113 | "version": "1.0", 114 | "ref": "70062abc2cd58794d2a90274502f81b55cd9951b" 115 | }, 116 | "files": [ 117 | "config/packages/dev/easy_log_handler.yaml" 118 | ] 119 | }, 120 | "egulias/email-validator": { 121 | "version": "2.1.11" 122 | }, 123 | "fig/link-util": { 124 | "version": "1.0.0" 125 | }, 126 | "friendsofsymfony/rest-bundle": { 127 | "version": "2.2", 128 | "recipe": { 129 | "repo": "github.com/symfony/recipes-contrib", 130 | "branch": "master", 131 | "version": "2.2", 132 | "ref": "cad41ef93d6150067ae2bb3c7fd729492dff6f0a" 133 | }, 134 | "files": [ 135 | "config/packages/fos_rest.yaml" 136 | ] 137 | }, 138 | "friendsofsymfony/user-bundle": { 139 | "version": "v2.1.2" 140 | }, 141 | "jdorn/sql-formatter": { 142 | "version": "v1.2.17" 143 | }, 144 | "laminas/laminas-code": { 145 | "version": "3.4.1" 146 | }, 147 | "laminas/laminas-eventmanager": { 148 | "version": "3.2.1" 149 | }, 150 | "laminas/laminas-zendframework-bridge": { 151 | "version": "1.0.3" 152 | }, 153 | "lcobucci/jwt": { 154 | "version": "3.3.1" 155 | }, 156 | "lexik/jwt-authentication-bundle": { 157 | "version": "2.5", 158 | "recipe": { 159 | "repo": "github.com/symfony/recipes", 160 | "branch": "master", 161 | "version": "2.5", 162 | "ref": "5b2157bcd5778166a5696e42f552ad36529a07a6" 163 | }, 164 | "files": [ 165 | "config/packages/lexik_jwt_authentication.yaml" 166 | ] 167 | }, 168 | "monolog/monolog": { 169 | "version": "1.25.2" 170 | }, 171 | "namshi/jose": { 172 | "version": "7.2.3" 173 | }, 174 | "nelmio/cors-bundle": { 175 | "version": "1.5", 176 | "recipe": { 177 | "repo": "github.com/symfony/recipes", 178 | "branch": "master", 179 | "version": "1.5", 180 | "ref": "6388de23860284db9acce0a7a5d9d13153bcb571" 181 | }, 182 | "files": [ 183 | "config/packages/nelmio_cors.yaml" 184 | ] 185 | }, 186 | "nikic/php-parser": { 187 | "version": "v4.3.0" 188 | }, 189 | "ocramius/package-versions": { 190 | "version": "1.4.1" 191 | }, 192 | "ocramius/proxy-manager": { 193 | "version": "2.2.3" 194 | }, 195 | "php": { 196 | "version": "7.4" 197 | }, 198 | "phpdocumentor/reflection-common": { 199 | "version": "2.0.0" 200 | }, 201 | "phpdocumentor/reflection-docblock": { 202 | "version": "4.3.2" 203 | }, 204 | "phpdocumentor/type-resolver": { 205 | "version": "1.0.1" 206 | }, 207 | "psr/cache": { 208 | "version": "1.0.1" 209 | }, 210 | "psr/container": { 211 | "version": "1.0.0" 212 | }, 213 | "psr/link": { 214 | "version": "1.0.0" 215 | }, 216 | "psr/log": { 217 | "version": "1.1.2" 218 | }, 219 | "sensio/framework-extra-bundle": { 220 | "version": "5.2", 221 | "recipe": { 222 | "repo": "github.com/symfony/recipes", 223 | "branch": "master", 224 | "version": "5.2", 225 | "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" 226 | }, 227 | "files": [ 228 | "config/packages/sensio_framework_extra.yaml" 229 | ] 230 | }, 231 | "swiftmailer/swiftmailer": { 232 | "version": "v6.2.3" 233 | }, 234 | "symfony/asset": { 235 | "version": "v4.3.8" 236 | }, 237 | "symfony/browser-kit": { 238 | "version": "v4.3.8" 239 | }, 240 | "symfony/cache": { 241 | "version": "v4.3.8" 242 | }, 243 | "symfony/cache-contracts": { 244 | "version": "v1.1.7" 245 | }, 246 | "symfony/config": { 247 | "version": "v4.3.8" 248 | }, 249 | "symfony/console": { 250 | "version": "3.3", 251 | "recipe": { 252 | "repo": "github.com/symfony/recipes", 253 | "branch": "master", 254 | "version": "3.3", 255 | "ref": "482d233eb8de91ebd042992077bbd5838858890c" 256 | }, 257 | "files": [ 258 | "bin/console", 259 | "config/bootstrap.php" 260 | ] 261 | }, 262 | "symfony/css-selector": { 263 | "version": "v4.3.8" 264 | }, 265 | "symfony/debug": { 266 | "version": "v4.3.8" 267 | }, 268 | "symfony/debug-bundle": { 269 | "version": "4.1", 270 | "recipe": { 271 | "repo": "github.com/symfony/recipes", 272 | "branch": "master", 273 | "version": "4.1", 274 | "ref": "f8863cbad2f2e58c4b65fa1eac892ab189971bea" 275 | }, 276 | "files": [ 277 | "config/packages/dev/debug.yaml" 278 | ] 279 | }, 280 | "symfony/debug-pack": { 281 | "version": "v1.0.7" 282 | }, 283 | "symfony/dependency-injection": { 284 | "version": "v4.3.8" 285 | }, 286 | "symfony/doctrine-bridge": { 287 | "version": "v4.3.8" 288 | }, 289 | "symfony/dom-crawler": { 290 | "version": "v4.3.8" 291 | }, 292 | "symfony/dotenv": { 293 | "version": "v4.3.8" 294 | }, 295 | "symfony/event-dispatcher": { 296 | "version": "v4.3.8" 297 | }, 298 | "symfony/event-dispatcher-contracts": { 299 | "version": "v1.1.7" 300 | }, 301 | "symfony/expression-language": { 302 | "version": "v4.3.8" 303 | }, 304 | "symfony/filesystem": { 305 | "version": "v4.3.8" 306 | }, 307 | "symfony/finder": { 308 | "version": "v4.3.8" 309 | }, 310 | "symfony/flex": { 311 | "version": "1.0", 312 | "recipe": { 313 | "repo": "github.com/symfony/recipes", 314 | "branch": "master", 315 | "version": "1.0", 316 | "ref": "19fa03bacd9a6619583d1e4939da4388df22984d" 317 | }, 318 | "files": [ 319 | ".env" 320 | ] 321 | }, 322 | "symfony/form": { 323 | "version": "v4.3.8" 324 | }, 325 | "symfony/framework-bundle": { 326 | "version": "4.2", 327 | "recipe": { 328 | "repo": "github.com/symfony/recipes", 329 | "branch": "master", 330 | "version": "4.2", 331 | "ref": "61ad963f28c091b8bb9449507654b9c7d8bbb53c" 332 | }, 333 | "files": [ 334 | "config/bootstrap.php", 335 | "config/packages/cache.yaml", 336 | "config/packages/framework.yaml", 337 | "config/packages/test/framework.yaml", 338 | "config/services.yaml", 339 | "public/index.php", 340 | "src/Controller/.gitignore", 341 | "src/Kernel.php" 342 | ] 343 | }, 344 | "symfony/http-client": { 345 | "version": "v4.3.8" 346 | }, 347 | "symfony/http-client-contracts": { 348 | "version": "v1.1.8" 349 | }, 350 | "symfony/http-foundation": { 351 | "version": "v4.3.8" 352 | }, 353 | "symfony/http-kernel": { 354 | "version": "v4.3.8" 355 | }, 356 | "symfony/inflector": { 357 | "version": "v4.3.8" 358 | }, 359 | "symfony/intl": { 360 | "version": "v4.3.8" 361 | }, 362 | "symfony/maker-bundle": { 363 | "version": "1.0", 364 | "recipe": { 365 | "repo": "github.com/symfony/recipes", 366 | "branch": "master", 367 | "version": "1.0", 368 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 369 | } 370 | }, 371 | "symfony/mime": { 372 | "version": "v4.3.8" 373 | }, 374 | "symfony/monolog-bridge": { 375 | "version": "v4.3.8" 376 | }, 377 | "symfony/monolog-bundle": { 378 | "version": "3.3", 379 | "recipe": { 380 | "repo": "github.com/symfony/recipes", 381 | "branch": "master", 382 | "version": "3.3", 383 | "ref": "6240c6d43e8237a32452f057f81816820fd56ab6" 384 | }, 385 | "files": [ 386 | "config/packages/dev/monolog.yaml", 387 | "config/packages/prod/monolog.yaml", 388 | "config/packages/test/monolog.yaml" 389 | ] 390 | }, 391 | "symfony/options-resolver": { 392 | "version": "v4.3.8" 393 | }, 394 | "symfony/orm-pack": { 395 | "version": "v1.0.7" 396 | }, 397 | "symfony/phpunit-bridge": { 398 | "version": "4.3", 399 | "recipe": { 400 | "repo": "github.com/symfony/recipes", 401 | "branch": "master", 402 | "version": "4.3", 403 | "ref": "b0582341f1df39aaf3a9a866cdbe49937da35984" 404 | }, 405 | "files": [ 406 | ".env.test", 407 | "bin/phpunit", 408 | "config/bootstrap.php", 409 | "phpunit.xml.dist", 410 | "tests/.gitignore" 411 | ] 412 | }, 413 | "symfony/polyfill-intl-icu": { 414 | "version": "v1.12.0" 415 | }, 416 | "symfony/polyfill-intl-idn": { 417 | "version": "v1.12.0" 418 | }, 419 | "symfony/polyfill-mbstring": { 420 | "version": "v1.12.0" 421 | }, 422 | "symfony/polyfill-php72": { 423 | "version": "v1.12.0" 424 | }, 425 | "symfony/polyfill-php73": { 426 | "version": "v1.12.0" 427 | }, 428 | "symfony/process": { 429 | "version": "v4.3.8" 430 | }, 431 | "symfony/profiler-pack": { 432 | "version": "v1.0.4" 433 | }, 434 | "symfony/property-access": { 435 | "version": "v4.3.8" 436 | }, 437 | "symfony/property-info": { 438 | "version": "v4.3.8" 439 | }, 440 | "symfony/routing": { 441 | "version": "4.2", 442 | "recipe": { 443 | "repo": "github.com/symfony/recipes", 444 | "branch": "master", 445 | "version": "4.2", 446 | "ref": "683dcb08707ba8d41b7e34adb0344bfd68d248a7" 447 | }, 448 | "files": [ 449 | "config/packages/prod/routing.yaml", 450 | "config/packages/routing.yaml", 451 | "config/routes.yaml" 452 | ] 453 | }, 454 | "symfony/security-bundle": { 455 | "version": "3.3", 456 | "recipe": { 457 | "repo": "github.com/symfony/recipes", 458 | "branch": "master", 459 | "version": "3.3", 460 | "ref": "e5a0228251d1dd2bca4c8ef918e14423c06db625" 461 | }, 462 | "files": [ 463 | "config/packages/security.yaml" 464 | ] 465 | }, 466 | "symfony/security-core": { 467 | "version": "v4.3.8" 468 | }, 469 | "symfony/security-csrf": { 470 | "version": "v4.3.8" 471 | }, 472 | "symfony/security-guard": { 473 | "version": "v4.3.8" 474 | }, 475 | "symfony/security-http": { 476 | "version": "v4.3.8" 477 | }, 478 | "symfony/serializer": { 479 | "version": "v4.3.8" 480 | }, 481 | "symfony/serializer-pack": { 482 | "version": "v1.0.2" 483 | }, 484 | "symfony/service-contracts": { 485 | "version": "v1.1.8" 486 | }, 487 | "symfony/stopwatch": { 488 | "version": "v4.3.8" 489 | }, 490 | "symfony/swiftmailer-bundle": { 491 | "version": "2.5", 492 | "recipe": { 493 | "repo": "github.com/symfony/recipes", 494 | "branch": "master", 495 | "version": "2.5", 496 | "ref": "429afc6c6778a1fdddb52a4f708cd94ff2c9960f" 497 | }, 498 | "files": [ 499 | "config/packages/dev/swiftmailer.yaml", 500 | "config/packages/swiftmailer.yaml", 501 | "config/packages/test/swiftmailer.yaml" 502 | ] 503 | }, 504 | "symfony/templating": { 505 | "version": "v4.3.8" 506 | }, 507 | "symfony/test-pack": { 508 | "version": "v1.0.6" 509 | }, 510 | "symfony/translation": { 511 | "version": "3.3", 512 | "recipe": { 513 | "repo": "github.com/symfony/recipes", 514 | "branch": "master", 515 | "version": "3.3", 516 | "ref": "2ad9d2545bce8ca1a863e50e92141f0b9d87ffcd" 517 | }, 518 | "files": [ 519 | "config/packages/translation.yaml", 520 | "translations/.gitignore" 521 | ] 522 | }, 523 | "symfony/translation-contracts": { 524 | "version": "v1.1.7" 525 | }, 526 | "symfony/twig-bridge": { 527 | "version": "v4.3.8" 528 | }, 529 | "symfony/twig-bundle": { 530 | "version": "3.3", 531 | "recipe": { 532 | "repo": "github.com/symfony/recipes", 533 | "branch": "master", 534 | "version": "3.3", 535 | "ref": "7e5911186d596214c14dd28305485a3c143ee746" 536 | }, 537 | "files": [ 538 | "config/packages/twig.yaml", 539 | "config/routes/dev/twig.yaml", 540 | "templates/base.html.twig" 541 | ] 542 | }, 543 | "symfony/validator": { 544 | "version": "4.3", 545 | "recipe": { 546 | "repo": "github.com/symfony/recipes", 547 | "branch": "master", 548 | "version": "4.3", 549 | "ref": "d902da3e4952f18d3bf05aab29512eb61cabd869" 550 | }, 551 | "files": [ 552 | "config/packages/test/validator.yaml", 553 | "config/packages/validator.yaml" 554 | ] 555 | }, 556 | "symfony/var-dumper": { 557 | "version": "v4.3.8" 558 | }, 559 | "symfony/var-exporter": { 560 | "version": "v4.3.8" 561 | }, 562 | "symfony/web-link": { 563 | "version": "v4.3.8" 564 | }, 565 | "symfony/web-profiler-bundle": { 566 | "version": "3.3", 567 | "recipe": { 568 | "repo": "github.com/symfony/recipes", 569 | "branch": "master", 570 | "version": "3.3", 571 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6" 572 | }, 573 | "files": [ 574 | "config/packages/dev/web_profiler.yaml", 575 | "config/packages/test/web_profiler.yaml", 576 | "config/routes/dev/web_profiler.yaml" 577 | ] 578 | }, 579 | "symfony/web-server-bundle": { 580 | "version": "3.3", 581 | "recipe": { 582 | "repo": "github.com/symfony/recipes", 583 | "branch": "master", 584 | "version": "3.3", 585 | "ref": "dae9b39fd6717970be7601101ce5aa960bf53d9a" 586 | } 587 | }, 588 | "symfony/yaml": { 589 | "version": "v4.3.8" 590 | }, 591 | "twig/twig": { 592 | "version": "v2.12.2" 593 | }, 594 | "webimpress/safe-writer": { 595 | "version": "2.0.1" 596 | }, 597 | "webmozart/assert": { 598 | "version": "1.5.0" 599 | }, 600 | "willdurand/jsonp-callback-validator": { 601 | "version": "v1.1.0" 602 | }, 603 | "willdurand/negotiation": { 604 | "version": "v2.3.1" 605 | }, 606 | "zendframework/zend-code": { 607 | "version": "3.4.0" 608 | }, 609 | "zendframework/zend-eventmanager": { 610 | "version": "3.2.1" 611 | } 612 | } 613 | -------------------------------------------------------------------------------- /back-end/templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% block body %}{% endblock %} 10 | {% block javascripts %}{% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /back-end/tests/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/tests/.gitignore -------------------------------------------------------------------------------- /back-end/tests/Controller/ApiControllerTest.php: -------------------------------------------------------------------------------- 1 | request( 21 | 'POST', 22 | '/api/login_check', 23 | [], 24 | [], 25 | ['CONTENT_TYPE' => 'application/json'], 26 | json_encode([ 27 | 'username' => $username, 28 | 'password' => $password, 29 | ]) 30 | ); 31 | 32 | $data = json_decode($client->getResponse()->getContent(), true); 33 | 34 | $client = static::createClient(); 35 | $client->setServerParameter('HTTP_Authorization', sprintf('Bearer %s', $data['token'])); 36 | 37 | return $client; 38 | } 39 | 40 | public function testGetHelloWithoutToken() 41 | { 42 | $client = static::createClient(); 43 | 44 | $client->request('GET', '/api/hello'); 45 | 46 | $this->assertEquals(401, $client->getResponse()->getStatusCode()); 47 | } 48 | 49 | public function testGetHelloWithToken() 50 | { 51 | // User created by running doctrine fixtures 52 | $credentials = ['username' => 'bob', 'password' => 'Abc123']; 53 | 54 | $client = $this->createAuthenticatedClient($credentials['username'], $credentials['password']); 55 | $client->request('GET', '/api/hello'); 56 | 57 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 58 | $content = json_decode($client->getResponse()->getContent(), true); 59 | $this->assertEquals('world', $content['hello']); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /back-end/tests/Controller/DefaultControllerTest.php: -------------------------------------------------------------------------------- 1 | request('GET', '/'); 14 | 15 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 16 | } 17 | 18 | public function testPostLogin() 19 | { 20 | // User created by running doctrine fixtures 21 | $credentials = ['username' => 'bob', 'password' => 'Abc123']; 22 | $client = static::createClient(); 23 | $client->request( 24 | 'POST', 25 | '/api/login_check', 26 | [], 27 | [], 28 | ['CONTENT_TYPE' => 'application/json'], 29 | json_encode($credentials) 30 | ); 31 | 32 | // Get the user session token 33 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); 34 | $content = json_decode($client->getResponse()->getContent(), true); 35 | $this->assertArrayHasKey('token', $content); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /back-end/translations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/back-end/translations/.gitignore -------------------------------------------------------------------------------- /buy-me-a-coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/buy-me-a-coffee.png -------------------------------------------------------------------------------- /config/nginx/angular.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; ## listen for ipv4; this line is default and implied 3 | listen [::]:8080 default ipv6only=on; ## listen for ipv6 4 | 5 | root /app/dist; 6 | index index.php index.html index.htm; 7 | 8 | # Add stdout logging 9 | 10 | error_log /dev/stdout info; 11 | access_log /dev/stdout; 12 | 13 | location / { 14 | proxy_pass http://client:4200/; 15 | } 16 | 17 | # deny access to . files, for security 18 | # 19 | location ~ /\. { 20 | log_not_found off; 21 | deny all; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /config/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data www-data; 2 | worker_processes 1; 3 | 4 | error_log /var/log/nginx/error.log warn; 5 | pid /var/run/nginx.pid; 6 | 7 | 8 | events { 9 | worker_connections 1024; 10 | } 11 | 12 | 13 | http { 14 | include /etc/nginx/mime.types; 15 | default_type application/octet-stream; 16 | 17 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 18 | '$status $body_bytes_sent "$http_referer" ' 19 | '"$http_user_agent" "$http_x_forwarded_for"'; 20 | 21 | access_log /var/log/nginx/access.log main; 22 | 23 | sendfile off; 24 | #tcp_nopush on; 25 | 26 | keepalive_timeout 65; 27 | 28 | #gzip on; 29 | 30 | include /etc/nginx/conf.d/*.conf; 31 | } 32 | -------------------------------------------------------------------------------- /config/nginx/symfony.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8000; ## listen for ipv4; this line is default and implied 3 | listen [::]:8000 default ipv6only=on; ## listen for ipv6 4 | 5 | root /var/www/public; 6 | index index.php index.html index.htm; 7 | 8 | # Make site accessible from http://localhost/ 9 | #server_name localhost; 10 | 11 | # Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html 12 | #sendfile off; 13 | 14 | # Add stdout logging 15 | 16 | error_log /dev/stdout info; 17 | access_log /dev/stdout; 18 | 19 | location / { 20 | # try to serve file directly, fallback to index.php 21 | try_files $uri /index.php$is_args$args; 22 | } 23 | 24 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 25 | # 26 | location ~ ^/index\.php(/|$) { 27 | fastcgi_pass application:9000; 28 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 29 | include fastcgi_params; 30 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 31 | fastcgi_param DOCUMENT_ROOT $document_root; 32 | fastcgi_param HTTPS off; 33 | internal; 34 | } 35 | 36 | # return 404 for all other php files not matching the front controller 37 | # this prevents access to other php files you don't want to be accessible. 38 | location ~ \.php$ { 39 | return 404; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | application: 2 | build: back-end 3 | volumes: 4 | - "./back-end:/var/www" 5 | links: 6 | - database 7 | 8 | client: 9 | build: front-end 10 | volumes: 11 | - "./front-end:/app" 12 | links: 13 | - nginx-back 14 | 15 | database: 16 | image: postgres:10 17 | ports: 18 | - 5432:5432 19 | environment: 20 | POSTGRES_PASSWORD: postgres 21 | POSTGRES_USER: postgres 22 | POSTGRES_DB: symfony_dev 23 | 24 | nginx-front: 25 | image: nginx 26 | ports: 27 | - 8080:8080 28 | links: 29 | - client 30 | volumes: 31 | - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf 32 | - ./config/nginx/angular.conf:/etc/nginx/conf.d/default.conf 33 | 34 | nginx-back: 35 | image: nginx 36 | ports: 37 | - 8000:8000 38 | links: 39 | - application 40 | volumes: 41 | - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf 42 | - ./config/nginx/symfony.conf:/etc/nginx/conf.d/default.conf 43 | -------------------------------------------------------------------------------- /front-end/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .gitignore 4 | -------------------------------------------------------------------------------- /front-end/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tool-versions 3 | -------------------------------------------------------------------------------- /front-end/Dockerfile: -------------------------------------------------------------------------------- 1 | # base image 2 | FROM node:12.16 3 | 4 | # install chrome for protractor tests 5 | RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - 6 | RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' 7 | RUN apt-get update && apt-get install -yq google-chrome-stable 8 | 9 | # set working directory 10 | WORKDIR /app 11 | 12 | # install and cache app dependencies 13 | COPY package.json /app/package.json 14 | RUN npm install 15 | RUN npm install -g @angular/cli@7.3.9 16 | 17 | # add `/app/node_modules/.bin` to $PATH 18 | ENV PATH /app/node_modules/.bin:$PATH 19 | 20 | # add app 21 | COPY . /app 22 | 23 | EXPOSE 4200 24 | # start app 25 | CMD ng serve --host 0.0.0.0 --disable-host-check 26 | 27 | -------------------------------------------------------------------------------- /front-end/README.md: -------------------------------------------------------------------------------- 1 | # Angular Symfony 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.19. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /front-end/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angularSymfony": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angularSymfony", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": false, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | }, 54 | { 55 | "type": "anyComponentStyle", 56 | "maximumWarning": "6kb", 57 | "maximumError": "10kb" 58 | } 59 | ] 60 | } 61 | } 62 | }, 63 | "serve": { 64 | "builder": "@angular-devkit/build-angular:dev-server", 65 | "options": { 66 | "browserTarget": "angularSymfony:build" 67 | }, 68 | "configurations": { 69 | "production": { 70 | "browserTarget": "angularSymfony:build:production" 71 | } 72 | } 73 | }, 74 | "extract-i18n": { 75 | "builder": "@angular-devkit/build-angular:extract-i18n", 76 | "options": { 77 | "browserTarget": "angularSymfony:build" 78 | } 79 | }, 80 | "test": { 81 | "builder": "@angular-devkit/build-angular:karma", 82 | "options": { 83 | "main": "src/test.ts", 84 | "polyfills": "src/polyfills.ts", 85 | "tsConfig": "tsconfig.spec.json", 86 | "karmaConfig": "karma.conf.js", 87 | "assets": [ 88 | "src/favicon.ico", 89 | "src/assets" 90 | ], 91 | "styles": [ 92 | "src/styles.css" 93 | ], 94 | "scripts": [] 95 | } 96 | }, 97 | "lint": { 98 | "builder": "@angular-devkit/build-angular:tslint", 99 | "options": { 100 | "tsConfig": [ 101 | "tsconfig.app.json", 102 | "tsconfig.spec.json", 103 | "e2e/tsconfig.json" 104 | ], 105 | "exclude": [ 106 | "**/node_modules/**" 107 | ] 108 | } 109 | }, 110 | "e2e": { 111 | "builder": "@angular-devkit/build-angular:protractor", 112 | "options": { 113 | "protractorConfig": "e2e/protractor.conf.js", 114 | "devServerTarget": "angularSymfony:serve" 115 | }, 116 | "configurations": { 117 | "production": { 118 | "devServerTarget": "angularSymfony:serve:production" 119 | } 120 | } 121 | } 122 | } 123 | }}, 124 | "defaultProject": "angularSymfony" 125 | } -------------------------------------------------------------------------------- /front-end/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /front-end/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /front-end/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular symfony app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /front-end/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /front-end/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /front-end/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/project'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "full-front-project", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~8.2.14", 15 | "@angular/common": "~8.2.14", 16 | "@angular/compiler": "~8.2.14", 17 | "@angular/core": "~8.2.14", 18 | "@angular/forms": "~8.2.14", 19 | "@angular/platform-browser": "~8.2.14", 20 | "@angular/platform-browser-dynamic": "~8.2.14", 21 | "@angular/router": "~8.2.14", 22 | "rxjs": "~6.4.0", 23 | "zone.js": "~0.9.1" 24 | }, 25 | "devDependencies": { 26 | "@angular-devkit/build-angular": "^0.803.19", 27 | "@angular/cli": "~8.3.19", 28 | "@angular/compiler-cli": "~8.2.14", 29 | "@angular/language-service": "~8.2.14", 30 | "@types/jasmine": "~3.3.8", 31 | "@types/jasminewd2": "~2.0.3", 32 | "@types/node": "^8.10.59", 33 | "codelyzer": "^5.0.0", 34 | "jasmine-core": "~3.4.0", 35 | "jasmine-spec-reporter": "~4.2.1", 36 | "karma": "~4.1.0", 37 | "karma-chrome-launcher": "~2.2.0", 38 | "karma-coverage-istanbul-reporter": "~2.0.1", 39 | "karma-jasmine": "~2.0.1", 40 | "karma-jasmine-html-reporter": "^1.4.0", 41 | "protractor": "~5.4.0", 42 | "ts-node": "~7.0.0", 43 | "tslint": "^5.15.0", 44 | "typescript": "^3.5.3", 45 | "typescript-tslint-plugin": "^0.5.5" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /front-end/src/app/api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | 4 | import { APIService } from './api.service'; 5 | 6 | describe('APIService', () => { 7 | beforeEach(() => 8 | TestBed.configureTestingModule({ 9 | imports: [HttpClientTestingModule] 10 | }) 11 | ); 12 | 13 | it('should be created', () => { 14 | const service: APIService = TestBed.get(APIService); 15 | expect(service).toBeTruthy(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /front-end/src/app/api.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { HttpClient } from '@angular/common/http'; 4 | 5 | import { environment } from '../environments/environment'; 6 | import { Observable } from 'rxjs'; 7 | 8 | interface CredentialsType { 9 | username: string; 10 | password: string; 11 | } 12 | 13 | @Injectable({ 14 | providedIn: 'root' 15 | }) 16 | export class APIService { 17 | constructor(private httpClient: HttpClient) { } 18 | 19 | postCredentials( 20 | credentials: CredentialsType 21 | ): Observable<{ token?: string }> { 22 | return this.httpClient.post(environment.server + '/api/login_check', credentials); 23 | } 24 | 25 | getHello(): Observable<{ hello?: string }> { 26 | return this.httpClient.get(environment.server + '/api/hello'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /front-end/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { LoginComponent } from './login/login.component'; 4 | import { HelloComponent } from './hello/hello.component'; 5 | 6 | const routes: Routes = [ 7 | { path: '', component: LoginComponent }, 8 | { path: 'hello', component: HelloComponent } 9 | ]; 10 | 11 | @NgModule({ 12 | imports: [RouterModule.forRoot(routes)], 13 | exports: [RouterModule] 14 | }) 15 | export class AppRoutingModule {} 16 | -------------------------------------------------------------------------------- /front-end/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/front-end/src/app/app.component.css -------------------------------------------------------------------------------- /front-end/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front-end/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [RouterTestingModule], 9 | declarations: [AppComponent] 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /front-end/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'Angular Symfony'; 10 | } 11 | -------------------------------------------------------------------------------- /front-end/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { ReactiveFormsModule } from '@angular/forms'; 6 | import { HttpClientModule } from '@angular/common/http'; 7 | import { AppComponent } from './app.component'; 8 | import { LoginComponent } from './login/login.component'; 9 | import { HelloComponent } from './hello/hello.component'; 10 | import { httpInterceptorProviders } from './http-interceptor'; 11 | 12 | @NgModule({ 13 | declarations: [AppComponent, LoginComponent, HelloComponent], 14 | imports: [ 15 | BrowserModule, 16 | AppRoutingModule, 17 | ReactiveFormsModule, 18 | HttpClientModule 19 | ], 20 | providers: [httpInterceptorProviders], 21 | bootstrap: [AppComponent] 22 | }) 23 | export class AppModule { } 24 | -------------------------------------------------------------------------------- /front-end/src/app/hello/hello.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/front-end/src/app/hello/hello.component.css -------------------------------------------------------------------------------- /front-end/src/app/hello/hello.component.html: -------------------------------------------------------------------------------- 1 |

Hello Component

2 | 3 |

Server sended you hello message : {{ helloMessage }}

4 | 5 |

-------------------------------------------------------------------------------- /front-end/src/app/hello/hello.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 3 | import { RouterTestingModule } from '@angular/router/testing'; 4 | 5 | import { HelloComponent } from './hello.component'; 6 | 7 | describe('HelloComponent', () => { 8 | let component: HelloComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [HttpClientTestingModule, RouterTestingModule], 14 | declarations: [HelloComponent] 15 | }).compileComponents(); 16 | })); 17 | 18 | beforeEach(() => { 19 | fixture = TestBed.createComponent(HelloComponent); 20 | component = fixture.componentInstance; 21 | fixture.detectChanges(); 22 | }); 23 | 24 | it('should create', () => { 25 | expect(component).toBeTruthy(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /front-end/src/app/hello/hello.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | 4 | import { APIService } from '../api.service'; 5 | import { HttpErrorResponse } from '@angular/common/http'; 6 | import { Router } from '@angular/router'; 7 | import { TokenService } from '../token.service'; 8 | 9 | @Component({ 10 | selector: 'app-hello', 11 | templateUrl: './hello.component.html', 12 | styleUrls: ['./hello.component.css'] 13 | }) 14 | export class HelloComponent implements OnInit { 15 | $hello: Observable<{ hello?: string }>; 16 | helloMessage: string; 17 | 18 | constructor( 19 | private apiService: APIService, 20 | private tokenService: TokenService, 21 | private router: Router 22 | ) {} 23 | 24 | ngOnInit() { 25 | // Example API call showing an Hello World 26 | this.$hello = this.apiService.getHello(); 27 | 28 | this.$hello.subscribe( 29 | // Show API response 30 | ({ hello }) => { 31 | console.log(`Received from server ${hello}`); 32 | this.helloMessage = hello; 33 | }, 34 | // Log error message and redirect to login 35 | (error: HttpErrorResponse) => { 36 | console.error(error); 37 | if (error.status === 401) { 38 | return this.router.navigate(['']); 39 | } 40 | } 41 | ); 42 | } 43 | 44 | onGoBack() { 45 | return this.router.navigate(['']); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /front-end/src/app/http-interceptor/index.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | 3 | import { JWTInterceptor } from './jwt-interceptor'; 4 | 5 | /** Http interceptor providers in outside-in order */ 6 | export const httpInterceptorProviders = [ 7 | { provide: HTTP_INTERCEPTORS, useClass: JWTInterceptor, multi: true } 8 | ]; 9 | -------------------------------------------------------------------------------- /front-end/src/app/http-interceptor/jwt-interceptor.ts: -------------------------------------------------------------------------------- 1 | import { TokenService } from '../token.service'; 2 | import { Injectable } from '@angular/core'; 3 | import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http'; 4 | 5 | @Injectable() 6 | export class JWTInterceptor implements HttpInterceptor { 7 | constructor(private tokenService: TokenService) {} 8 | 9 | intercept(req: HttpRequest, next: HttpHandler) { 10 | const authToken = this.tokenService.getAuthorizationToken(); 11 | 12 | if (authToken) { 13 | // Clone the request and replace the original headers with 14 | // cloned headers, updated with the authorization. 15 | const authReq = req.clone({ 16 | headers: req.headers.set('Authorization', `Bearer ${authToken}`) 17 | }); 18 | // send cloned request with header to the next handler. 19 | return next.handle(authReq); 20 | } else { 21 | // otherwise send request without token 22 | return next.handle(req); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /front-end/src/app/login/login.component.css: -------------------------------------------------------------------------------- 1 | label { 2 | padding: 0.5em 1.5em; 3 | } -------------------------------------------------------------------------------- /front-end/src/app/login/login.component.html: -------------------------------------------------------------------------------- 1 |

Login Component

2 | 3 |

Error from server : {{ error }}

4 | 5 |

Generated token is
{{ token }}

6 | 7 |

Please enter a symfony valid user

8 | 9 |
10 | 11 | 15 | 16 | 20 | 21 | 24 | 25 |
26 | 27 |

Try to access to API after your're logged in.

28 | 29 |

-------------------------------------------------------------------------------- /front-end/src/app/login/login.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LoginComponent } from './login.component'; 4 | import { ReactiveFormsModule } from '@angular/forms'; 5 | import { HttpClientTestingModule } from '@angular/common/http/testing'; 6 | import { RouterTestingModule } from '@angular/router/testing'; 7 | 8 | describe('LoginComponent', () => { 9 | let component: LoginComponent; 10 | let fixture: ComponentFixture; 11 | 12 | beforeEach(async(() => { 13 | TestBed.configureTestingModule({ 14 | imports: [ 15 | ReactiveFormsModule, 16 | HttpClientTestingModule, 17 | RouterTestingModule 18 | ], 19 | declarations: [LoginComponent] 20 | }).compileComponents(); 21 | })); 22 | 23 | beforeEach(() => { 24 | fixture = TestBed.createComponent(LoginComponent); 25 | component = fixture.componentInstance; 26 | fixture.detectChanges(); 27 | }); 28 | 29 | it('should create', () => { 30 | expect(component).toBeTruthy(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /front-end/src/app/login/login.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { FormGroup, FormControl } from '@angular/forms'; 3 | import { Router } from '@angular/router'; 4 | 5 | import { APIService } from '../api.service'; 6 | import { TokenService } from '../token.service'; 7 | import { Observable } from 'rxjs'; 8 | import { HttpErrorResponse } from '@angular/common/http'; 9 | 10 | @Component({ 11 | selector: 'app-login', 12 | templateUrl: './login.component.html', 13 | styleUrls: ['./login.component.css'] 14 | }) 15 | export class LoginComponent implements OnInit { 16 | $login: Observable<{ token?: string }>; 17 | token: string; 18 | error: string; 19 | 20 | credentialsForm = new FormGroup({ 21 | username: new FormControl(''), 22 | password: new FormControl('') 23 | }); 24 | 25 | constructor( 26 | private apiService: APIService, 27 | private tokenService: TokenService, 28 | private router: Router 29 | ) { } 30 | 31 | ngOnInit() { 32 | this.token = this.tokenService.getAuthorizationToken(); 33 | } 34 | 35 | onSubmit() { 36 | const credentials: { username: string; password: string } = this 37 | .credentialsForm.value; 38 | 39 | // Login should return jwt token 40 | this.$login = this.apiService.postCredentials(credentials); 41 | 42 | this.$login.subscribe( 43 | // Show generated token 44 | ({ token }) => { 45 | console.log('Received JWT token', token); 46 | this.tokenService.setAuthorizationToken(token); 47 | this.token = token; 48 | }, 49 | // Show server error 50 | (error: HttpErrorResponse) => { 51 | console.error(error); 52 | this.error = error.message; 53 | } 54 | ); 55 | } 56 | 57 | onLogout() { 58 | this.tokenService.cleanAuthorizationToken(); 59 | this.token = null; 60 | return this.router.navigate(['']); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /front-end/src/app/token.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { TokenService } from './token.service'; 4 | 5 | describe('TokenService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: TokenService = TestBed.get(TokenService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /front-end/src/app/token.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | @Injectable({ 4 | providedIn: 'root' 5 | }) 6 | export class TokenService { 7 | token: string; 8 | username: string; 9 | created: string; 10 | secret: string; 11 | wsseToken: string; 12 | 13 | constructor() { } 14 | 15 | hasAuthorizationToken(): boolean { 16 | const hasToken = !!this.token; 17 | 18 | return hasToken; 19 | } 20 | 21 | getAuthorizationToken(): string { 22 | // Generate a new token with new nonce each time otherwise it's a replay attack 23 | const token = localStorage.getItem('access_token'); 24 | this.token = token; 25 | return this.token; 26 | } 27 | 28 | setAuthorizationToken(token: string) { 29 | // Save static parts of the token 30 | this.token = token; 31 | localStorage.setItem('access_token', this.token); 32 | } 33 | 34 | cleanAuthorizationToken() { 35 | // Clean token informations 36 | this.token = null; 37 | localStorage.removeItem('access_token'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /front-end/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/front-end/src/assets/.gitkeep -------------------------------------------------------------------------------- /front-end/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | server: 'http://0.0.0.0:8000', 3 | production: true 4 | }; 5 | -------------------------------------------------------------------------------- /front-end/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | server: 'http://0.0.0.0:8000', 7 | production: false 8 | }; 9 | 10 | /* 11 | * For easier debugging in development mode, you can import the following file 12 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 13 | * 14 | * This import should be commented out in production mode because it will have a negative impact 15 | * on performance if an error is thrown. 16 | */ 17 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 18 | -------------------------------------------------------------------------------- /front-end/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Acrecio/angular-symfony/df1f6f0785f32ee9037f63fb0e6cc7559b5760f8/front-end/src/favicon.ico -------------------------------------------------------------------------------- /front-end/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Symfony 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /front-end/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /front-end/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /front-end/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /front-end/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /front-end/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "files": [ 10 | "src/main.ts", 11 | "src/polyfills.ts" 12 | ], 13 | "include": [ 14 | "src/**/*.ts" 15 | ], 16 | "exclude": [ 17 | "src/test.ts", 18 | "src/**/*.spec.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /front-end/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "plugins": [ 15 | { 16 | "name": "typescript-tslint-plugin" 17 | } 18 | ], 19 | "typeRoots": [ 20 | "node_modules/@types" 21 | ], 22 | "lib": [ 23 | "es2018", 24 | "dom" 25 | ] 26 | }, 27 | "exclude": [ 28 | "node_modules" 29 | ], 30 | "angularCompilerOptions": { 31 | "fullTemplateTypeCheck": true, 32 | "strictInjectionParameters": true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /front-end/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /front-end/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-var-requires": false, 64 | "object-literal-key-quotes": [ 65 | true, 66 | "as-needed" 67 | ], 68 | "object-literal-sort-keys": false, 69 | "ordered-imports": false, 70 | "quotemark": [ 71 | true, 72 | "single" 73 | ], 74 | "trailing-comma": false, 75 | "no-conflicting-lifecycle": true, 76 | "no-host-metadata-property": true, 77 | "no-input-rename": true, 78 | "no-inputs-metadata-property": true, 79 | "no-output-native": true, 80 | "no-output-on-prefix": true, 81 | "no-output-rename": true, 82 | "no-outputs-metadata-property": true, 83 | "template-banana-in-box": true, 84 | "template-no-negated-async": true, 85 | "use-lifecycle-interface": true, 86 | "use-pipe-transform-interface": true 87 | }, 88 | "rulesDirectory": [ 89 | "codelyzer" 90 | ] 91 | } --------------------------------------------------------------------------------