├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── api ├── .dockerignore ├── .env.dist ├── .env.prod ├── .gitignore ├── LICENSE ├── bin │ └── console ├── composer.json ├── composer.lock ├── config │ ├── bundles.php │ ├── jwt │ │ └── .gitkeep │ ├── packages │ │ ├── api_platform.yaml │ │ ├── dev │ │ │ ├── jms_serializer.yaml │ │ │ ├── monolog.yaml │ │ │ ├── routing.yaml │ │ │ └── web_profiler.yaml │ │ ├── doctrine.yaml │ │ ├── framework.yaml │ │ ├── jms_serializer.yaml │ │ ├── lexik_jwt_authentication.yaml │ │ ├── mercure.yaml │ │ ├── messenger.yaml │ │ ├── nelmio_cors.yaml │ │ ├── prod │ │ │ ├── doctrine.yaml │ │ │ ├── jms_serializer.yaml │ │ │ └── monolog.yaml │ │ ├── routing.yaml │ │ ├── security.yaml │ │ └── test │ │ │ ├── framework.yaml │ │ │ ├── monolog.yaml │ │ │ └── web_profiler.yaml │ ├── routes.yaml │ ├── routes │ │ ├── annotations.yaml │ │ ├── api_platform.yaml │ │ └── dev │ │ │ └── web_profiler.yaml │ └── services.yaml ├── docker-compose.dist.yml ├── docker-compose.prod.yml ├── docker-compose.yml ├── public │ └── index.php ├── setup.sh ├── src │ ├── Controller │ │ ├── BookGetCollectionController.php │ │ ├── BookPostCollectionController.php │ │ ├── BookPutCollectionController.php │ │ └── UserRegisterPostCollectionController.php │ ├── Entity │ │ ├── Book.php │ │ ├── BookUpdateDownloadCount.php │ │ ├── Team.php │ │ └── User.php │ ├── EventListener │ │ ├── AuthenticationSuccessListener.php │ │ └── JWTCreatedListener.php │ ├── Handler │ │ └── BookUpdateDownloadCountHandler.php │ ├── Kernel.php │ └── Repository │ │ ├── BookRepository.php │ │ ├── TeamRepository.php │ │ └── UserRepository.php ├── symfony.lock ├── var │ ├── cache │ │ └── .gitkeep │ └── log │ │ └── .gitkeep └── vendor │ └── .gitkeep ├── app-release.apk ├── client ├── .env.dist ├── .env.prod ├── .gitignore ├── LICENSE ├── babel.config.js ├── docker-compose.dev.yml ├── docker-compose.prod.yml ├── package.json ├── public │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ └── loader-eclipse.gif │ ├── components │ │ ├── FormInput.vue │ │ ├── Loader.vue │ │ └── MainSlot.vue │ ├── interceptor.js │ ├── main.js │ ├── mixins │ │ └── Mercure.vue │ ├── pages │ │ ├── SignIn.vue │ │ ├── SignUp.vue │ │ └── book │ │ │ ├── BookForm.vue │ │ │ ├── Create.vue │ │ │ ├── List.vue │ │ │ ├── Show.vue │ │ │ └── Update.vue │ ├── router │ │ ├── book.js │ │ └── index.js │ └── store │ │ ├── index.js │ │ └── modules │ │ ├── auth │ │ ├── index.js │ │ └── mutation_types.js │ │ ├── book │ │ └── index.js │ │ ├── general │ │ ├── actions.js │ │ ├── index.js │ │ └── mutation_types.js │ │ └── user │ │ └── index.js ├── vue.config.js └── yarn.lock ├── mobile ├── .env.dist ├── .env.prod ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public │ └── index.html ├── src-cordova │ ├── config.xml │ ├── hooks │ │ └── README.md │ ├── package.json │ └── www │ │ └── .gitignore ├── src │ └── main.js └── vue.config.js ├── proxy ├── .env.sample ├── .gitignore ├── LICENSE ├── Readme.md ├── conf.d │ ├── realip.conf │ ├── servertokens.conf │ └── uploadsize.conf ├── data │ └── .gitkeep ├── docker-compose-multiple-networks.yml ├── docker-compose.yml ├── nginx.tmpl ├── scripts │ ├── base.sh │ └── update.sh ├── start.sh ├── test_start.sh ├── test_start_ssl.sh └── test_stop.sh └── setup.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .mercure 3 | mercure/ 4 | mercure/COPYRIGHT 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android APK 2 | We added a self signed app-release.apk to the repo. It points to the demo server. You can see Mercure auto updating in the app. (note, we only put Mercure ont he book show page, not the list) 3 | 4 | # Summary 5 | I was looking for a tutorial, or solution to learn from which had a few of the elements I think are key to most apps. 6 | * multi tenant/teams 7 | * scope of data by team 8 | * secured live data updates 9 | * app setup 10 | * messenger added for background data 11 | 12 | Since I couldn't find on I decided to post on upwork, and open source the result. I met Maksym: https://www.upwork.com/freelancers/~01f4204dd14c8f6973 a very bright coder, who pulled this together in record time, above and beyond expectation. I hope open sourcing this can help others who want to get onto the apiplatform (and Mercure) bandwagon 13 | 14 | # Basic Preparation 15 | 1) make sure no apache or nginx installed 16 | 2) make sure docker installed also docker-compose tool should be installed 17 | 3) clone the repo 18 | 4) run sh ./setup 19 | 5) docker ps to see all containers running 20 | 6) if after install any containers didn't start, cd api && docker-compose stop && rm -rf var/log/* && docker-compose up -d 21 | 22 | # how to prepare for real time communication 23 | look for and replace instances of the demo domain. Please be sure DNS is done first, as it will be needed by certbox for SSL 24 | 25 | # proxy 26 | set up your external IP in .env file 27 | 28 | # for api folder 29 | * api/docker-compose.yml: VIRTUAL_HOST: apidemo.yap.life 30 | * api/docker-compose.yml: LETSENCRYPT_HOST: apidemo.yap.life 31 | * api/docker-compose.yml: - VIRTUAL_HOST=mercuredemo.yap.life 32 | * api/docker-compose.yml: - LETSENCRYPT_HOST=mercuredemo.yap.life 33 | * api/docker-compose.prod.yml: VIRTUAL_HOST: apidemo.yap.life 34 | * api/docker-compose.prod.yml: LETSENCRYPT_HOST: apidemo.yap.life 35 | * api/docker-compose.prod.yml: - VIRTUAL_HOST=mercuredemo.yap.life 36 | * api/docker-compose.prod.yml: - LETSENCRYPT_HOST=mercuredemo.yap.life 37 | 38 | # client folder 39 | * client/docker-compose.prod.yml: VIRTUAL_HOST: clientdemo.yap.life 40 | * client/docker-compose.prod.yml: LETSENCRYPT_HOST: clientdemo.yap.life 41 | * client/docker-compose.yml: VIRTUAL_HOST: clientdemo.yap.life 42 | * client/docker-compose.yml: LETSENCRYPT_HOST: clientdemo.yap.life 43 | 44 | # mobile folder 45 | * .env.prod:VUE_APP_API_URL='https://apidemo.yap.life' 46 | * .env.prod:VUE_APP_MERCURE_URL='https://mercuredemo.yap.life/hub' 47 | 48 | # proxy 49 | nginx/data/conf.d/default.conf << setup all your domains 50 | 51 | # note 52 | passphrase !ChangeMe! << can be changed in env.prod 53 | 54 | # mobile app 55 | * clone same repo to your local machine 56 | * in client folder, yarn install && yarn build 57 | * install cordova 58 | * install needed emulators and SDKs 59 | * cp .env.prod .env 60 | * in mobile folder, yarn install && yarn build 61 | * cd src-cordova 62 | * cordova platform add ios (or other) 63 | * cordova run ios (or replace ios with other) 64 | 65 | # demo 66 | https://apidemo.yap.life/ 67 | https://clientdemo.yap.life/ 68 | -------------------------------------------------------------------------------- /api/.dockerignore: -------------------------------------------------------------------------------- 1 | /var/log/* 2 | /var/cache/* 3 | /.composer 4 | -------------------------------------------------------------------------------- /api/.env.dist: -------------------------------------------------------------------------------- 1 | # This file is a "template" of which env vars need to be defined for your application 2 | # Copy this file to .env file for development, create environment variables when deploying to production 3 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 4 | 5 | PORT_DATABASE=54321 6 | PORT_API=8888 7 | PORT_ELASTIC=9222 8 | PORT_MERCURE=1337 9 | 10 | ###> symfony/framework-bundle ### 11 | APP_ENV=dev 12 | APP_SECRET=!ChangeMe! 13 | TRUSTED_PROXIES=127.0.0.1,127.0.0.2 14 | TRUSTED_HOSTS=127.0.0.1,localhost,example.com 15 | ###< symfony/framework-bundle ### 16 | 17 | ###> nelmio/cors-bundle ### 18 | CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$ 19 | ###< nelmio/cors-bundle ### 20 | 21 | ###> doctrine/doctrine-bundle ### 22 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 23 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 24 | # Configure your db driver and server_version in config/packages/doctrine.yaml 25 | DATABASE_DRIVER=pdo_pgsql 26 | DATABASE_HOST=db 27 | DATABASE_PORT=5432 28 | DATABASE_NAME=api 29 | DATABASE_USER=api 30 | DATABASE_PASSWORD=!ChangeMe! 31 | ###< doctrine/doctrine-bundle ### 32 | 33 | ###> lexik/jwt-authentication-bundle ### 34 | JWT_PASSPHRASE=!ChangeMe! 35 | ###< lexik/jwt-authentication-bundle ### 36 | 37 | COMPOSE_PROJECT_NAME=mercure-api 38 | 39 | ###> symfony/mercure-bundle ### 40 | MERCURE_PUBLISH_URL=http://mercure/hub 41 | MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.obDjwCgqtPuIvwBlTxUEmibbBf0zypKCNzNKP7Op2UM 42 | MERCURE_JWT_SECRET_KEY=!ChangeMe! 43 | ###< symfony/mercure-bundle ### 44 | 45 | ###> symfony/messenger ### 46 | # Choose one of the transports below 47 | # MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages 48 | # MESSENGER_TRANSPORT_DSN=doctrine://default 49 | # MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages 50 | ###< symfony/messenger ### 51 | -------------------------------------------------------------------------------- /api/.env.prod: -------------------------------------------------------------------------------- 1 | # This file is a "template" of which env vars need to be defined for your application 2 | # Copy this file to .env file for development, create environment variables when deploying to production 3 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 4 | 5 | PORT_DATABASE=54321 6 | PORT_API=8888 7 | PORT_ELASTIC=9222 8 | PORT_MERCURE=1337 9 | 10 | ###> symfony/framework-bundle ### 11 | APP_ENV=prod 12 | APP_SECRET=!ChangeMe! 13 | #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 14 | #TRUSTED_HOSTS=127.0.0.1,localhost,example.com 15 | ###< symfony/framework-bundle ### 16 | 17 | ###> nelmio/cors-bundle ### 18 | #CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$ 19 | CORS_ALLOW_ORIGIN=. 20 | ###< nelmio/cors-bundle ### 21 | 22 | ###> doctrine/doctrine-bundle ### 23 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 24 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 25 | # Configure your db driver and server_version in config/packages/doctrine.yaml 26 | DATABASE_DRIVER=pdo_pgsql 27 | DATABASE_HOST=db 28 | DATABASE_PORT=5432 29 | DATABASE_NAME=api 30 | DATABASE_USER=api 31 | DATABASE_PASSWORD=!ChangeMe! 32 | ###< doctrine/doctrine-bundle ### 33 | 34 | ###> lexik/jwt-authentication-bundle ### 35 | JWT_PASSPHRASE=!ChangeMe! 36 | ###< lexik/jwt-authentication-bundle ### 37 | 38 | COMPOSE_PROJECT_NAME=mercure-api 39 | 40 | ###> symfony/mercure-bundle ### 41 | MERCURE_PUBLISH_URL=http://mercure/hub 42 | MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.obDjwCgqtPuIvwBlTxUEmibbBf0zypKCNzNKP7Op2UM 43 | MERCURE_JWT_SECRET_KEY=!ChangeMe! 44 | ###< symfony/mercure-bundle ### 45 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | ###> symfony/framework-bundle ### 2 | .env 3 | /public/bundles/ 4 | /var/log/* 5 | /var/cache/* 6 | !/var/cache/.gitkeep 7 | !/var/log/.gitkeep 8 | /vendor/* 9 | !/vendor/.gitkeep 10 | ###< symfony/framework-bundle ### 11 | 12 | ###> lexik/jwt-authentication-bundle ### 13 | /config/jwt/* 14 | !/config/jwt/.gitkeep 15 | ###< lexik/jwt-authentication-bundle ### 16 | 17 | .bash_history 18 | .composer 19 | .rnd 20 | 21 | supervisord.log 22 | supervisord.pid 23 | -------------------------------------------------------------------------------- /api/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Inshop Group 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 | -------------------------------------------------------------------------------- /api/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | load(__DIR__.'/../.env'); 23 | } 24 | 25 | $input = new ArgvInput(); 26 | $env = $input->getParameterOption(['--env', '-e'], $_SERVER['APP_ENV'] ?? 'dev'); 27 | $debug = ($_SERVER['APP_DEBUG'] ?? ('prod' !== $env)) && !$input->hasParameterOption(['--no-debug', '']); 28 | 29 | if ($debug) { 30 | umask(0000); 31 | 32 | if (class_exists(Debug::class)) { 33 | Debug::enable(); 34 | } 35 | } 36 | 37 | $kernel = new Kernel($env, $debug); 38 | $application = new Application($kernel); 39 | $application->run($input); 40 | -------------------------------------------------------------------------------- /api/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "license": "proprietary", 4 | "require": { 5 | "php": "^7.2", 6 | "ext-json": "^1.6", 7 | "api-platform/api-pack": "^1.2", 8 | "jms/serializer-bundle": "^2.4", 9 | "lcobucci/jwt": "^3.3", 10 | "lexik/jwt-authentication-bundle": "^2.6", 11 | "sensio/framework-extra-bundle": "^5.4", 12 | "symfony/console": "^4.3", 13 | "symfony/dotenv": "^4.3", 14 | "symfony/flex": "^1.4", 15 | "symfony/framework-bundle": "^4.3", 16 | "symfony/mercure-bundle": "^0.1.2", 17 | "symfony/messenger": "^4.2", 18 | "symfony/monolog-bundle": "^3.4", 19 | "symfony/security-bundle": "^4.3", 20 | "symfony/yaml": "^4.3" 21 | }, 22 | "require-dev": { 23 | "api-platform/schema-generator": "^2.1", 24 | "symfony/maker-bundle": "^1.9", 25 | "symfony/profiler-pack": "^1.0", 26 | "symfony/var-dumper": "^4.3" 27 | }, 28 | "config": { 29 | "preferred-install": { 30 | "*": "dist" 31 | }, 32 | "sort-packages": true 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "App\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "App\\Tests\\": "tests/" 42 | } 43 | }, 44 | "replace": { 45 | "symfony/polyfill-iconv": "*", 46 | "symfony/polyfill-php71": "*", 47 | "symfony/polyfill-php70": "*", 48 | "symfony/polyfill-php56": "*" 49 | }, 50 | "scripts": { 51 | "auto-scripts": { 52 | "cache:clear": "symfony-cmd", 53 | "assets:install": "symfony-cmd" 54 | }, 55 | "post-install-cmd": [ 56 | "@auto-scripts" 57 | ], 58 | "post-update-cmd": [ 59 | "@auto-scripts" 60 | ] 61 | }, 62 | "conflict": { 63 | "symfony/symfony": "*" 64 | }, 65 | "extra": { 66 | "symfony": { 67 | "allow-contrib": false 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /api/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 6 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 7 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], 8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 9 | ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 10 | Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], 11 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 12 | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], 13 | Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], 14 | JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], 15 | Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], 16 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 17 | Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], 18 | ]; 19 | -------------------------------------------------------------------------------- /api/config/jwt/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpharaoh/apiplatformtemplate/e795855b4db2117d775d9c225b83d9b250999492/api/config/jwt/.gitkeep -------------------------------------------------------------------------------- /api/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | #parameters: 2 | # # Adds a fallback VARNISH_URL if the env var is not set. 3 | # # This allows you to run cache:warmup even if your 4 | # # environment variables are not available yet. 5 | # # You should not need to change this value. 6 | # env(VARNISH_URL): '' 7 | 8 | api_platform: 9 | mapping: 10 | paths: ['%kernel.project_dir%/src/Entity'] 11 | title: Mercure POC 12 | version: 0.1 13 | collection: 14 | pagination: 15 | client_items_per_page: true 16 | client_enabled: true 17 | 18 | swagger: 19 | api_keys: 20 | apiKey: 21 | name: Authorization 22 | type: header 23 | -------------------------------------------------------------------------------- /api/config/packages/dev/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | json: 4 | options: 5 | - JSON_PRETTY_PRINT 6 | - JSON_UNESCAPED_SLASHES 7 | - JSON_PRESERVE_ZERO_FRACTION 8 | -------------------------------------------------------------------------------- /api/config/packages/dev/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 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 | -------------------------------------------------------------------------------- /api/config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /api/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 | -------------------------------------------------------------------------------- /api/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | # Adds a fallback DATABASE_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(DATABASE_DRIVER): '' 7 | env(DATABASE_HOST): '' 8 | env(DATABASE_PORT): '' 9 | env(DATABASE_NAME): '' 10 | env(DATABASE_USER): '' 11 | env(DATABASE_URL): '' 12 | env(DATABASE_PASSWORD): '' 13 | 14 | doctrine: 15 | dbal: 16 | # configure these for your database server 17 | driver: 'pdo_mysql' 18 | server_version: '5.7' 19 | charset: utf8mb4 20 | default_table_options: 21 | charset: utf8mb4 22 | collate: utf8mb4_unicode_ci 23 | 24 | url: '%env(resolve:DATABASE_URL)%' 25 | orm: 26 | default_entity_manager: default 27 | auto_generate_proxy_classes: '%kernel.debug%' 28 | entity_managers: 29 | default: 30 | connection: default 31 | naming_strategy: doctrine.orm.naming_strategy.underscore 32 | auto_mapping: true 33 | mappings: 34 | App: 35 | is_bundle: false 36 | type: annotation 37 | dir: '%kernel.project_dir%/src/Entity' 38 | prefix: 'App\Entity' 39 | alias: App 40 | -------------------------------------------------------------------------------- /api/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #default_locale: en 4 | #csrf_protection: ~ 5 | #http_method_override: true 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: ~ 11 | 12 | #esi: ~ 13 | #fragments: ~ 14 | php_errors: 15 | log: true 16 | 17 | cache: 18 | # The app cache caches to the filesystem by default. Other options include: 19 | 20 | # Redis 21 | #app: cache.adapter.redis 22 | #default_redis_provider: redis://localhost 23 | 24 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 25 | #app: cache.adapter.apcu 26 | -------------------------------------------------------------------------------- /api/config/packages/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | xml: 4 | format_output: '%kernel.debug%' 5 | # metadata: 6 | # auto_detection: false 7 | # directories: 8 | # any-name: 9 | # namespace_prefix: "My\\FooBundle" 10 | # path: "@MyFooBundle/Resources/config/serializer" 11 | # another-name: 12 | # namespace_prefix: "My\\BarBundle" 13 | # path: "@MyBarBundle/Resources/config/serializer" 14 | -------------------------------------------------------------------------------- /api/config/packages/lexik_jwt_authentication.yaml: -------------------------------------------------------------------------------- 1 | lexik_jwt_authentication: 2 | secret_key: '%kernel.project_dir%/config/jwt/private.pem' # required for token creation 3 | public_key: '%kernel.project_dir%/config/jwt/public.pem' # required for token verification 4 | pass_phrase: '%env(JWT_PASSPHRASE)%' 5 | token_ttl: 28800 6 | -------------------------------------------------------------------------------- /api/config/packages/mercure.yaml: -------------------------------------------------------------------------------- 1 | mercure: 2 | hubs: 3 | default: 4 | url: '%env(MERCURE_PUBLISH_URL)%' 5 | jwt: '%env(MERCURE_JWT_SECRET)%' 6 | -------------------------------------------------------------------------------- /api/config/packages/messenger.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | messenger: 3 | # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling. 4 | # failure_transport: failed 5 | 6 | transports: 7 | # https://symfony.com/doc/current/messenger.html#transport-configuration 8 | async: '%env(MESSENGER_TRANSPORT_DSN)%' 9 | # failed: 'doctrine://default?queue_name=failed' 10 | # sync: 'sync://' 11 | 12 | routing: 13 | # Route your messages to the transports 14 | # 'App\Message\YourMessage': async 15 | 'App\Entity\BookUpdateDownloadCount': async 16 | -------------------------------------------------------------------------------- /api/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'] 7 | allow_headers: ['*'] 8 | expose_headers: ['Link'] 9 | max_age: 3600 10 | paths: 11 | '^/': ~ 12 | -------------------------------------------------------------------------------- /api/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | metadata_cache_driver: 4 | type: service 5 | id: doctrine.system_cache_provider 6 | query_cache_driver: 7 | type: service 8 | id: doctrine.system_cache_provider 9 | result_cache_driver: 10 | type: service 11 | id: doctrine.result_cache_provider 12 | 13 | services: 14 | doctrine.result_cache_provider: 15 | class: Symfony\Component\Cache\DoctrineProvider 16 | public: false 17 | arguments: 18 | - '@doctrine.result_cache_pool' 19 | doctrine.system_cache_provider: 20 | class: Symfony\Component\Cache\DoctrineProvider 21 | public: false 22 | arguments: 23 | - '@doctrine.system_cache_pool' 24 | 25 | framework: 26 | cache: 27 | pools: 28 | doctrine.result_cache_pool: 29 | adapter: cache.app 30 | doctrine.system_cache_pool: 31 | adapter: cache.system 32 | -------------------------------------------------------------------------------- /api/config/packages/prod/jms_serializer.yaml: -------------------------------------------------------------------------------- 1 | jms_serializer: 2 | visitors: 3 | json: 4 | options: 5 | - JSON_UNESCAPED_SLASHES 6 | - JSON_PRESERVE_ZERO_FRACTION 7 | -------------------------------------------------------------------------------- /api/config/packages/prod/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: fingers_crossed 5 | action_level: error 6 | handler: nested 7 | excluded_404s: 8 | # regex: exclude all 404 errors from the logs 9 | - ^/ 10 | nested: 11 | type: stream 12 | path: "%kernel.logs_dir%/%kernel.environment%.log" 13 | level: debug 14 | console: 15 | type: console 16 | process_psr_3_messages: false 17 | channels: ["!event", "!doctrine"] 18 | -------------------------------------------------------------------------------- /api/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | -------------------------------------------------------------------------------- /api/config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers 3 | encoders: 4 | App\Entity\User: 5 | algorithm: bcrypt 6 | 7 | providers: 8 | db_provider: 9 | entity: 10 | class: App\Entity\User 11 | property: username 12 | 13 | firewalls: 14 | login: 15 | pattern: ^/login 16 | stateless: true 17 | anonymous: true 18 | provider: db_provider 19 | json_login: 20 | check_path: /login 21 | username_path: username 22 | password_path: password 23 | require_previous_session: false 24 | success_handler: lexik_jwt_authentication.handler.authentication_success 25 | failure_handler: lexik_jwt_authentication.handler.authentication_failure 26 | 27 | register: 28 | pattern: ^/register 29 | anonymous: true 30 | 31 | docs: 32 | pattern: ^/docs 33 | anonymous: true 34 | 35 | dev: 36 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 37 | security: false 38 | 39 | api: 40 | pattern: ^/ 41 | provider: db_provider 42 | stateless: true 43 | anonymous: false 44 | guard: 45 | authenticators: 46 | - lexik_jwt_authentication.jwt_token_authenticator 47 | 48 | access_control: 49 | - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY } 50 | - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } 51 | - { path: ^/docs, role: IS_AUTHENTICATED_ANONYMOUSLY } 52 | - { path: ^/, roles: [ ROLE_USER ] } 53 | -------------------------------------------------------------------------------- /api/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: ~ 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /api/config/packages/test/monolog.yaml: -------------------------------------------------------------------------------- 1 | monolog: 2 | handlers: 3 | main: 4 | type: stream 5 | path: "%kernel.logs_dir%/%kernel.environment%.log" 6 | level: debug 7 | channels: ["!event"] 8 | -------------------------------------------------------------------------------- /api/config/packages/test/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: false 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { collect: false } 7 | -------------------------------------------------------------------------------- /api/config/routes.yaml: -------------------------------------------------------------------------------- 1 | login: 2 | path: /login 3 | methods: [POST] 4 | -------------------------------------------------------------------------------- /api/config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /api/config/routes/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | resource: . 3 | type: api_platform 4 | -------------------------------------------------------------------------------- /api/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 | -------------------------------------------------------------------------------- /api/config/services.yaml: -------------------------------------------------------------------------------- 1 | # Put parameters here that don't need to change on each machine where the app is deployed 2 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 3 | parameters: 4 | locale: 'en' 5 | client_url: '%env(resolve:CLIENT_URL)%' 6 | mercure_secret_key: '%env(resolve:MERCURE_JWT_SECRET_KEY)%' 7 | env(MERCURE_PUBLISH_URL): '' 8 | env(MERCURE_JWT_SECRET): '' 9 | 10 | services: 11 | # default configuration for services in *this* file 12 | _defaults: 13 | autowire: true # Automatically injects dependencies in your services. 14 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 15 | public: false # Allows optimizing the container by removing unused services; this also means 16 | # fetching services directly from the container via $container->get() won't work. 17 | # The best practice is to be explicit about your dependencies anyway. 18 | 19 | # makes classes in src/ available to be used as services 20 | # this creates a service per class whose id is the fully-qualified class name 21 | App\: 22 | resource: '../src/*' 23 | exclude: '../src/{Entity,Migrations,Tests,DataFixtures}' 24 | 25 | # controllers are imported separately to make sure services can be injected 26 | # as action arguments even if you don't extend any base controller class 27 | App\Controller\: 28 | resource: '../src/Controller' 29 | tags: ['controller.service_arguments'] 30 | 31 | # add more service definitions when explicit configuration is needed 32 | # please note that last definitions always *replace* previous ones 33 | 34 | api.event.jwt_created_listener: 35 | class: App\EventListener\JWTCreatedListener 36 | tags: 37 | - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated } 38 | 39 | api.event.authentication_success_listener: 40 | class: App\EventListener\AuthenticationSuccessListener 41 | arguments: 42 | - '@lexik_jwt_authentication.jws_provider.lcobucci' 43 | tags: 44 | - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccessResponse } 45 | -------------------------------------------------------------------------------- /api/docker-compose.dist.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | php: 5 | image: inshopgroup/docker-inshop-crm-api-php-fpm-prod:latest 6 | depends_on: 7 | - db 8 | env_file: 9 | - ./.env 10 | volumes: 11 | - .:/var/www:rw,cached 12 | networks: 13 | - api 14 | 15 | nginx: 16 | image: inshopgroup/docker-inshop-crm-api-nginx-prod:latest 17 | depends_on: 18 | - php 19 | volumes: 20 | - ./public:/var/www/public:ro 21 | ports: 22 | - ${PORT_API}:80 23 | networks: 24 | - api 25 | 26 | db: 27 | image: postgres:11.4-alpine 28 | environment: 29 | - POSTGRES_DB=${DATABASE_NAME} 30 | - POSTGRES_USER=${DATABASE_USER} 31 | - POSTGRES_PASSWORD=${DATABASE_PASSWORD} 32 | volumes: 33 | - db-data:/var/lib/postgresql/data:rw 34 | ports: 35 | - ${PORT_DATABASE}:5432 36 | networks: 37 | - api 38 | 39 | mercure: 40 | image: dunglas/mercure 41 | environment: 42 | - JWT_KEY=${MERCURE_JWT_SECRET_KEY} 43 | - ALLOW_ANONYMOUS=0 44 | - CORS_ALLOWED_ORIGINS=* 45 | - PUBLISH_ALLOWED_ORIGINS=* 46 | ports: 47 | - ${PORT_MERCURE}:80 48 | networks: 49 | - api 50 | 51 | volumes: 52 | db-data: {} 53 | 54 | networks: 55 | api: 56 | -------------------------------------------------------------------------------- /api/docker-compose.prod.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | php: 5 | image: inshopgroup/docker-inshop-crm-api-php-fpm-prod:latest 6 | depends_on: 7 | - db 8 | env_file: 9 | - ./.env 10 | volumes: 11 | - .:/var/www:rw,cached 12 | networks: 13 | - api 14 | 15 | nginx: 16 | image: inshopgroup/docker-inshop-crm-api-nginx-prod:latest 17 | depends_on: 18 | - php 19 | volumes: 20 | - ./public:/var/www/public:ro 21 | environment: 22 | VIRTUAL_HOST: apidemo.yap.life 23 | LETSENCRYPT_HOST: apidemo.yap.life 24 | LETSENCRYPT_EMAIL: info@inshop.com.ua 25 | networks: 26 | - api 27 | - webproxy 28 | 29 | db: 30 | image: postgres:11.4-alpine 31 | environment: 32 | - POSTGRES_DB=${DATABASE_NAME} 33 | - POSTGRES_USER=${DATABASE_USER} 34 | - POSTGRES_PASSWORD=${DATABASE_PASSWORD} 35 | volumes: 36 | - db-data:/var/lib/postgresql/data:rw 37 | networks: 38 | - api 39 | 40 | mercure: 41 | image: dunglas/mercure 42 | environment: 43 | - JWT_KEY=${MERCURE_JWT_SECRET_KEY} 44 | - ALLOW_ANONYMOUS=0 45 | - CORS_ALLOWED_ORIGINS=* 46 | - PUBLISH_ALLOWED_ORIGINS=* 47 | - VIRTUAL_HOST=mercuredemo.yap.life 48 | - LETSENCRYPT_HOST=mercuredemo.yap.life 49 | - LETSENCRYPT_EMAIL=info@inshop.com.ua 50 | networks: 51 | - api 52 | - webproxy 53 | 54 | volumes: 55 | db-data: {} 56 | 57 | networks: 58 | api: 59 | webproxy: 60 | external: 61 | name: webproxy 62 | -------------------------------------------------------------------------------- /api/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.2' 2 | 3 | services: 4 | php: 5 | image: inshopgroup/docker-inshop-crm-api-php-fpm-prod:latest 6 | depends_on: 7 | - db 8 | env_file: 9 | - ./.env 10 | volumes: 11 | - .:/var/www:rw,cached 12 | networks: 13 | - api 14 | 15 | nginx: 16 | image: inshopgroup/docker-inshop-crm-api-nginx-prod:latest 17 | depends_on: 18 | - php 19 | volumes: 20 | - ./public:/var/www/public:ro 21 | ports: 22 | - ${PORT_API}:80 23 | networks: 24 | - api 25 | 26 | db: 27 | image: postgres:11.4-alpine 28 | environment: 29 | - POSTGRES_DB=${DATABASE_NAME} 30 | - POSTGRES_USER=${DATABASE_USER} 31 | - POSTGRES_PASSWORD=${DATABASE_PASSWORD} 32 | volumes: 33 | - db-data:/var/lib/postgresql/data:rw 34 | ports: 35 | - ${PORT_DATABASE}:5432 36 | networks: 37 | - api 38 | 39 | mercure: 40 | image: dunglas/mercure 41 | environment: 42 | - JWT_KEY=${MERCURE_JWT_SECRET_KEY} 43 | - ALLOW_ANONYMOUS=0 44 | - CORS_ALLOWED_ORIGINS=* 45 | - PUBLISH_ALLOWED_ORIGINS=* 46 | ports: 47 | - ${PORT_MERCURE}:80 48 | networks: 49 | - api 50 | 51 | volumes: 52 | db-data: {} 53 | 54 | networks: 55 | api: 56 | -------------------------------------------------------------------------------- /api/public/index.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/../.env'); 16 | } 17 | 18 | $env = $_SERVER['APP_ENV'] ?? 'dev'; 19 | $debug = $_SERVER['APP_DEBUG'] ?? ('prod' !== $env); 20 | 21 | if ($debug) { 22 | umask(0000); 23 | 24 | Debug::enable(); 25 | } 26 | 27 | if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? false) { 28 | Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL); 29 | } 30 | 31 | if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? false) { 32 | Request::setTrustedHosts(explode(',', $trustedHosts)); 33 | } 34 | 35 | $kernel = new Kernel($env, $debug); 36 | $request = Request::createFromGlobals(); 37 | $response = $kernel->handle($request); 38 | $response->send(); 39 | $kernel->terminate($request, $response); 40 | -------------------------------------------------------------------------------- /api/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | openssl genrsa -out config/jwt/private.pem -aes256 4096 4 | openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem 5 | openssl rsa -in config/jwt/private.pem -out config/jwt/private2.pem 6 | mv config/jwt/private.pem config/jwt/private.pem-back 7 | mv config/jwt/private2.pem config/jwt/private.pem 8 | 9 | composer install 10 | 11 | php bin/console make:entity App --regenerate -n 12 | php bin/console doctrine:database:drop --if-exists --force 13 | php bin/console doctrine:database:create 14 | php bin/console doctrine:schema:update --force 15 | php bin/console doctrine:schema:validate 16 | -------------------------------------------------------------------------------- /api/src/Controller/BookGetCollectionController.php: -------------------------------------------------------------------------------- 1 | getRepository(Book::class)->findBy(['team' => $user->getTeam()]); 25 | } 26 | 27 | return []; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /api/src/Controller/BookPostCollectionController.php: -------------------------------------------------------------------------------- 1 | setOwner($user); 24 | $data->setTeam($user->getTeam()); 25 | } 26 | $data->setDownloadCount(0); 27 | 28 | return $data; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/src/Controller/BookPutCollectionController.php: -------------------------------------------------------------------------------- 1 | setOwner($user); 26 | // $data->setTeam($user->getTeam()); 27 | 28 | // } 29 | 30 | $postData = json_decode($request->getContent(), true); 31 | $logger->info(count($postData)); 32 | if(count($postData)==1&&isset($postData['downloadCount'])){ 33 | $logger->info('send messenger !!!!!!'); 34 | }else{ 35 | $logger->info('NO **** messenger'); 36 | } 37 | // $data->setDownloadCount(3); 38 | // dump($data); 39 | return $data; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /api/src/Controller/UserRegisterPostCollectionController.php: -------------------------------------------------------------------------------- 1 | getContent(), true); 36 | 37 | $user = new User(); 38 | $user->setUsername($data['username']); 39 | $user->setName($data['name']); 40 | // $user->setPlainPassword($data['plainPassword']); 41 | $user->setPassword($encoder->encodePassword($user, $data['plainPassword'])); 42 | $em->persist($user); 43 | 44 | if (!empty($data['team'])) { 45 | $team = $em->getRepository(Team::class)->findOneBy(['hash' => $data['team']]); 46 | } else { 47 | $team = new Team(); 48 | $team->setHash(str_replace('.', '', uniqid('', true))); 49 | $team->setOwner($user); 50 | $em->persist($team); 51 | } 52 | 53 | $user->setTeam($team); 54 | $validator->validate($user); 55 | $em->flush(); 56 | 57 | return new Response(null, Response::HTTP_CREATED); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /api/src/Entity/Book.php: -------------------------------------------------------------------------------- 1 | id; 120 | } 121 | 122 | public function getTitle(): ?string 123 | { 124 | return $this->title; 125 | } 126 | 127 | public function setTitle(string $title): self 128 | { 129 | $this->title = $title; 130 | 131 | return $this; 132 | } 133 | 134 | public function getAuthor(): ?string 135 | { 136 | return $this->author; 137 | } 138 | 139 | public function setAuthor(string $author): self 140 | { 141 | $this->author = $author; 142 | 143 | return $this; 144 | } 145 | 146 | public function getTeam(): ?Team 147 | { 148 | return $this->team; 149 | } 150 | 151 | public function setTeam(?Team $team): self 152 | { 153 | $this->team = $team; 154 | 155 | return $this; 156 | } 157 | 158 | public function getOwner(): ?User 159 | { 160 | return $this->owner; 161 | } 162 | 163 | public function setOwner(?User $owner): self 164 | { 165 | $this->owner = $owner; 166 | 167 | return $this; 168 | } 169 | 170 | public function getDownloadCount(): ?int 171 | { 172 | return $this->downloadCount; 173 | } 174 | 175 | public function setDownloadCount(?int $downloadCount): self 176 | { 177 | $this->downloadCount = $downloadCount; 178 | 179 | return $this; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /api/src/Entity/BookUpdateDownloadCount.php: -------------------------------------------------------------------------------- 1 | members = new ArrayCollection(); 53 | } 54 | 55 | public function getId(): ?int 56 | { 57 | return $this->id; 58 | } 59 | 60 | public function getHash(): ?string 61 | { 62 | return $this->hash; 63 | } 64 | 65 | public function setHash(string $hash): self 66 | { 67 | $this->hash = $hash; 68 | 69 | return $this; 70 | } 71 | 72 | public function getOwner(): ?User 73 | { 74 | return $this->owner; 75 | } 76 | 77 | public function setOwner(?User $owner): self 78 | { 79 | $this->owner = $owner; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * @return Collection|User[] 86 | */ 87 | public function getMembers(): Collection 88 | { 89 | return $this->members; 90 | } 91 | 92 | public function addMember(User $member): self 93 | { 94 | if (!$this->members->contains($member)) { 95 | $this->members[] = $member; 96 | $member->setTeam($this); 97 | } 98 | 99 | return $this; 100 | } 101 | 102 | public function removeMember(User $member): self 103 | { 104 | if ($this->members->contains($member)) { 105 | $this->members->removeElement($member); 106 | // set the owning side to null (unless already changed) 107 | if ($member->getTeam() === $this) { 108 | $member->setTeam(null); 109 | } 110 | } 111 | 112 | return $this; 113 | } 114 | 115 | /** 116 | * @return array 117 | */ 118 | public function getMercureIri(): array 119 | { 120 | return ['/team/' . $this->getId()]; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /api/src/Entity/User.php: -------------------------------------------------------------------------------- 1 | username; 87 | } 88 | 89 | public function getSalt() 90 | { 91 | return null; 92 | } 93 | 94 | public function getPassword() 95 | { 96 | return $this->password; 97 | } 98 | 99 | /** 100 | * @return array 101 | */ 102 | public function getRoles() 103 | { 104 | return ['ROLE_USER']; 105 | } 106 | 107 | public function eraseCredentials() 108 | { 109 | } 110 | 111 | /** @see \Serializable::serialize() */ 112 | public function serialize() 113 | { 114 | return serialize(array( 115 | $this->id, 116 | $this->username, 117 | $this->password, 118 | )); 119 | } 120 | 121 | /** @see \Serializable::unserialize() */ 122 | public function unserialize($serialized) 123 | { 124 | list ( 125 | $this->id, 126 | $this->username, 127 | $this->password, 128 | ) = unserialize($serialized, ['allowed_classes' => false]); 129 | } 130 | 131 | /** 132 | * @return int 133 | */ 134 | public function getId(): int 135 | { 136 | return $this->id; 137 | } 138 | 139 | /** 140 | * @param int $id 141 | */ 142 | public function setId(int $id): void 143 | { 144 | $this->id = $id; 145 | } 146 | 147 | /** 148 | * @return string 149 | */ 150 | public function getName(): string 151 | { 152 | return $this->name; 153 | } 154 | 155 | /** 156 | * @param string $name 157 | */ 158 | public function setName(string $name): void 159 | { 160 | $this->name = $name; 161 | } 162 | 163 | public function setUsername(string $username): self 164 | { 165 | $this->username = $username; 166 | 167 | return $this; 168 | } 169 | 170 | public function setPassword(string $password): self 171 | { 172 | $this->password = $password; 173 | 174 | return $this; 175 | } 176 | 177 | public function getTeam(): Team 178 | { 179 | return $this->team; 180 | } 181 | 182 | public function setTeam(?Team $team): self 183 | { 184 | $this->team = $team; 185 | 186 | return $this; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /api/src/EventListener/AuthenticationSuccessListener.php: -------------------------------------------------------------------------------- 1 | jwt = $jwt; 36 | $this->params = $params; 37 | } 38 | 39 | /** 40 | * @param AuthenticationSuccessEvent $event 41 | * @throws \Exception 42 | */ 43 | public function onAuthenticationSuccessResponse(AuthenticationSuccessEvent $event): void 44 | { 45 | $data = $event->getData(); 46 | $user = $event->getUser(); 47 | 48 | if (!$user instanceof User) { 49 | return; 50 | } 51 | 52 | $token = (new Builder()) 53 | ->withClaim('mercure', ['subscribe' => $user->getTeam()->getMercureIri()]) 54 | ->sign(new Sha256(), $this->params->get('mercure_secret_key')) 55 | ->getToken(); 56 | 57 | $data['mercureToken'] = $token->__toString(); 58 | 59 | $event->setData($data); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /api/src/EventListener/JWTCreatedListener.php: -------------------------------------------------------------------------------- 1 | getUser(); 21 | $payload = $event->getData(); 22 | $payload['id'] = $user->getId(); 23 | $payload['name'] = $user->getName(); 24 | $payload['team'] = $user->getTeam()->getHash(); 25 | 26 | $event->setData($payload); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/Handler/BookUpdateDownloadCountHandler.php: -------------------------------------------------------------------------------- 1 | bookRepository = $bookRepository; 24 | $this->logger=$logger; 25 | $this->entityManager = $entityManager; 26 | } 27 | 28 | public function __invoke(BookUpdateDownloadCount $id) 29 | { 30 | 31 | sleep(2); 32 | 33 | $book = $this->bookRepository->find($id->book_id); 34 | $this->logger->info(serialize($book)); 35 | $book->setDownloadCount($book->getDownloadCount()+1); 36 | $this->entityManager->flush(); 37 | } 38 | } -------------------------------------------------------------------------------- /api/src/Kernel.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 | -------------------------------------------------------------------------------- /api/src/Repository/BookRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('b') 29 | ->andWhere('b.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('b.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Book 41 | { 42 | return $this->createQueryBuilder('b') 43 | ->andWhere('b.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/TeamRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('t') 29 | ->andWhere('t.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('t.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?Team 41 | { 42 | return $this->createQueryBuilder('t') 43 | ->andWhere('t.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/src/Repository/UserRepository.php: -------------------------------------------------------------------------------- 1 | 1%", 83 | "last 2 versions" 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mercure POC 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /client/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /client/src/assets/loader-eclipse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpharaoh/apiplatformtemplate/e795855b4db2117d775d9c225b83d9b250999492/client/src/assets/loader-eclipse.gif -------------------------------------------------------------------------------- /client/src/components/FormInput.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 47 | -------------------------------------------------------------------------------- /client/src/components/Loader.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 18 | 19 | 41 | -------------------------------------------------------------------------------- /client/src/components/MainSlot.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 51 | -------------------------------------------------------------------------------- /client/src/interceptor.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from './store' 3 | import * as types from './store/modules/general/mutation_types' 4 | 5 | axios.defaults.timeout = 30000 6 | 7 | axios.interceptors.request.use( 8 | config => { 9 | store.commit('general/' + types.LOADING_START) 10 | 11 | let token = store.getters['auth/jwtDecoded'] || null 12 | let authorized = token && token.exp > Date.now() / 1000 13 | 14 | if (authorized) { 15 | config.headers.common['Authorization'] = 16 | 'Bearer ' + store.state.auth.token 17 | } 18 | 19 | if (process.env.NODE_ENV !== 'production') { 20 | if (config.url.indexOf('?') > -1) { 21 | config.url = config.url + '&XDEBUG_SESSION_START=PHPSTORM' 22 | } else { 23 | config.url = config.url + '?XDEBUG_SESSION_START=PHPSTORM' 24 | } 25 | } 26 | 27 | return config 28 | }, 29 | error => { 30 | store.commit('general/' + types.LOADING_STOP) 31 | 32 | return Promise.reject(error) 33 | } 34 | ) 35 | 36 | axios.interceptors.response.use( 37 | data => { 38 | store.commit('general/' + types.LOADING_STOP) 39 | 40 | return data 41 | }, 42 | error => { 43 | store.commit('general/' + types.LOADING_STOP) 44 | 45 | if ( 46 | error.response && 47 | error.response.status && 48 | error.response.status === 401 49 | ) { 50 | window.location.href = '/signin' 51 | } 52 | 53 | return Promise.reject(error) 54 | } 55 | ) 56 | 57 | export default axios 58 | -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | import '../node_modules/bootstrap/dist/css/bootstrap.css' 2 | import store from './store' 3 | import router from './router' 4 | import Vue from 'vue' 5 | import App from './App' 6 | import Toastr from 'vue-toastr' 7 | 8 | Vue.use(router) 9 | Vue.use(Toastr) 10 | Vue.config.productionTip = false 11 | 12 | new Vue({ 13 | store, 14 | router, 15 | render: h => h(App) 16 | }).$mount('#app') 17 | -------------------------------------------------------------------------------- /client/src/mixins/Mercure.vue: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /client/src/pages/SignIn.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 72 | 73 | 81 | -------------------------------------------------------------------------------- /client/src/pages/SignUp.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 77 | 78 | 93 | -------------------------------------------------------------------------------- /client/src/pages/book/BookForm.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 56 | -------------------------------------------------------------------------------- /client/src/pages/book/Create.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | -------------------------------------------------------------------------------- /client/src/pages/book/List.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 144 | -------------------------------------------------------------------------------- /client/src/pages/book/Show.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 53 | -------------------------------------------------------------------------------- /client/src/pages/book/Update.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 50 | -------------------------------------------------------------------------------- /client/src/router/book.js: -------------------------------------------------------------------------------- 1 | import BookList from '../pages/book/List' 2 | import BookCreate from '../pages/book/Create' 3 | import BookUpdate from '../pages/book/Update' 4 | import BookShow from '../pages/book/Show' 5 | 6 | export default [ 7 | { 8 | name: 'BookList', 9 | path: '/books/', 10 | component: BookList, 11 | meta: { 12 | requiresAuth: true 13 | } 14 | }, 15 | { 16 | name: 'BookCreate', 17 | path: '/books/create', 18 | component: BookCreate, 19 | meta: { 20 | requiresAuth: true 21 | } 22 | }, 23 | { 24 | name: 'BookUpdate', 25 | path: '/books/edit/:id', 26 | component: BookUpdate, 27 | meta: { 28 | requiresAuth: true 29 | } 30 | }, 31 | { 32 | name: 'BookShow', 33 | path: '/books/:id', 34 | component: BookShow, 35 | meta: { 36 | requiresAuth: true 37 | } 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /client/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import store from '../store' 5 | import SignIn from '../pages/SignIn' 6 | import SignUp from '../pages/SignUp' 7 | import MainSlot from '../components/MainSlot' 8 | 9 | import bookRoutes from './book' 10 | 11 | Vue.use(VueRouter) 12 | 13 | const router = new VueRouter({ 14 | mode: 'hash', 15 | routes: [ 16 | { 17 | path: '/signin', 18 | name: 'SignIn', 19 | component: SignIn, 20 | meta: { 21 | requiresAuth: false 22 | } 23 | }, 24 | { 25 | path: '/signup', 26 | name: 'SignUp', 27 | component: SignUp, 28 | meta: { 29 | requiresAuth: false 30 | } 31 | }, 32 | { 33 | path: '/', 34 | component: MainSlot, 35 | children: [...bookRoutes], 36 | meta: { 37 | requiresAuth: true 38 | } 39 | } 40 | ] 41 | }) 42 | 43 | router.beforeEach((to, from, next) => { 44 | let token = store.getters['auth/jwtDecoded'] || null 45 | let authorized = token && token.exp > Date.now() / 1000 46 | 47 | if (authorized) { 48 | if (to.matched.some(record => !record.meta.requiresAuth)) { 49 | next({ name: 'BookList' }) 50 | } 51 | } else { 52 | if (to.matched.some(record => record.meta.requiresAuth)) { 53 | next({ name: 'SignIn' }) 54 | } 55 | } 56 | 57 | next() 58 | }) 59 | 60 | export default router 61 | -------------------------------------------------------------------------------- /client/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import general from './modules/general/' 5 | import auth from './modules/auth/' 6 | import book from './modules/book/' 7 | import user from './modules/user/' 8 | 9 | Vue.use(Vuex) 10 | 11 | export default new Vuex.Store({ 12 | strict: process.env.NODE_ENV !== 'production', 13 | modules: { 14 | general, 15 | auth, 16 | book, 17 | user 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /client/src/store/modules/auth/index.js: -------------------------------------------------------------------------------- 1 | import jwtDecode from 'jwt-decode' 2 | import * as types from './mutation_types' 3 | import router from '../../../router' 4 | import axios from '../../../interceptor' 5 | 6 | const actions = { 7 | login({ commit }, data) { 8 | commit(types.AUTH_ERROR_CHANGE, null) 9 | 10 | const url = process.env.VUE_APP_API_URL + '/login' 11 | 12 | return axios 13 | .post(url, data) 14 | .then(response => { 15 | commit(types.AUTH_UPDATE_TOKEN, response.data) 16 | }) 17 | .catch(() => { 18 | commit(types.AUTH_ERROR_CHANGE, 'Incorrect username or password') 19 | }) 20 | }, 21 | logout({ commit }) { 22 | commit(types.AUTH_RESET) 23 | router.push({ name: 'SignIn' }) 24 | } 25 | } 26 | 27 | function initialState() { 28 | return { 29 | token: localStorage.getItem('token'), 30 | mercureToken: localStorage.getItem('mercureToken'), 31 | error: null 32 | } 33 | } 34 | 35 | const state = initialState() 36 | 37 | const getters = { 38 | jwtDecoded: state => { 39 | if (state.token) { 40 | return jwtDecode(state.token) 41 | } 42 | }, 43 | error: state => state.error, 44 | mercureToken: state => state.mercureToken 45 | } 46 | 47 | const mutations = { 48 | [types.AUTH_UPDATE_TOKEN](state, data) { 49 | localStorage.setItem('token', data.token) 50 | localStorage.setItem('mercureToken', data.mercureToken) 51 | 52 | state.token = data.token 53 | state.mercureToken = data.mercureToken 54 | }, 55 | [types.AUTH_ERROR_CHANGE](state, error) { 56 | state.error = error 57 | }, 58 | [types.AUTH_RESET](state) { 59 | localStorage.removeItem('token') 60 | localStorage.removeItem('mercureToken') 61 | 62 | const s = initialState() 63 | Object.keys(s).forEach(key => { 64 | state[key] = s[key] 65 | }) 66 | } 67 | } 68 | 69 | export default { 70 | namespaced: true, 71 | state, 72 | getters, 73 | actions, 74 | mutations 75 | } 76 | -------------------------------------------------------------------------------- /client/src/store/modules/auth/mutation_types.js: -------------------------------------------------------------------------------- 1 | export const AUTH_UPDATE_TOKEN = 'AUTH_UPDATE_TOKEN' 2 | export const AUTH_ERROR_CHANGE = 'AUTH_ERROR_CHANGE' 3 | export const AUTH_RESET = 'AUTH_RESET' 4 | -------------------------------------------------------------------------------- /client/src/store/modules/book/index.js: -------------------------------------------------------------------------------- 1 | import axios from '../../../interceptor' 2 | 3 | export const state = () => ({ 4 | item: {}, 5 | items: [], 6 | errors: {} 7 | }) 8 | 9 | export const mutations = { 10 | SET_ITEM(state, item) { 11 | state.item = item 12 | }, 13 | SET_ITEMS(state, items) { 14 | state.items = items 15 | }, 16 | UPDATE_ITEM(state, item) { 17 | state.item = Object.assign({}, state.item, item) 18 | }, 19 | SET_ERRORS(state, errors) { 20 | state.errors = errors 21 | }, 22 | RESET_STATE(state) { 23 | Object.assign(state, { 24 | item: {}, 25 | items: [], 26 | errors: {} 27 | }) 28 | } 29 | } 30 | 31 | export const getters = { 32 | item: state => state.item, 33 | items: state => state.items, 34 | errors: state => state.errors 35 | } 36 | 37 | export const actions = { 38 | resetState({ commit }) { 39 | commit('RESET_STATE') 40 | }, 41 | processErrors({ commit }, data) { 42 | if (data.violations) { 43 | let errors = {} 44 | 45 | data.violations.map(violation => { 46 | Object.assign(errors, { [violation.propertyPath]: violation.message }) 47 | }) 48 | 49 | commit('SET_ERRORS', errors) 50 | } 51 | 52 | throw data 53 | }, 54 | getItems({ commit }) { 55 | const url = process.env.VUE_APP_API_URL + '/books' 56 | 57 | return axios 58 | .get(url) 59 | .then(response => response.data) 60 | .then(data => { 61 | commit('SET_ITEMS', data['hydra:member']) 62 | }) 63 | }, 64 | getItem({ commit }, id) { 65 | const url = process.env.VUE_APP_API_URL + '/books/' + id 66 | 67 | return axios 68 | .get(url) 69 | .then(response => response.data) 70 | .then(data => { 71 | commit('SET_ITEM', data) 72 | }) 73 | }, 74 | create({ state, dispatch }) { 75 | const url = process.env.VUE_APP_API_URL + '/books' 76 | 77 | return axios.post(url, state.item).catch(e => { 78 | dispatch('processErrors', e.response.data) 79 | }) 80 | }, 81 | update({ state, dispatch }) { 82 | const url = process.env.VUE_APP_API_URL + '/books/' + state.item.id 83 | 84 | return axios.put(url, state.item).catch(e => { 85 | dispatch('processErrors', e.response.data) 86 | }) 87 | }, 88 | remove({ state }, id) { 89 | const url = process.env.VUE_APP_API_URL + '/books/' + id 90 | 91 | return axios.delete(url, state.item) 92 | }, 93 | download({ state, dispatch }) { 94 | const url = process.env.VUE_APP_API_URL + '/book_update_download_counts' 95 | 96 | return axios.put(url, state.item).catch(e => { 97 | dispatch('processErrors', e.response.data) 98 | }) 99 | } 100 | } 101 | 102 | export default { 103 | namespaced: true, 104 | state, 105 | getters, 106 | actions, 107 | mutations 108 | } 109 | -------------------------------------------------------------------------------- /client/src/store/modules/general/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation_types' 2 | 3 | export const loadingStart = ({ commit }) => { 4 | commit(types.LOADING_START) 5 | } 6 | 7 | export const loadingStop = ({ commit }) => { 8 | commit(types.LOADING_STOP) 9 | } 10 | 11 | export const loadingAllow = ({ commit }, value) => { 12 | commit(types.LOADING_ALLOW, value) 13 | } 14 | -------------------------------------------------------------------------------- /client/src/store/modules/general/index.js: -------------------------------------------------------------------------------- 1 | import * as types from './mutation_types' 2 | import * as actions from './actions' 3 | 4 | function initialState() { 5 | return { 6 | isLoading: 0, 7 | loadingAllow: true 8 | } 9 | } 10 | 11 | const state = initialState() 12 | 13 | const getters = { 14 | isLoading: state => state.isLoading !== 0 15 | } 16 | 17 | export const mutations = { 18 | [types.LOADING_START](state) { 19 | if (state.loadingAllow) { 20 | state.isLoading += 1 21 | } 22 | }, 23 | [types.LOADING_STOP](state) { 24 | state.loadingAllow = true 25 | 26 | if (state.isLoading > 0) { 27 | state.isLoading -= 1 28 | } 29 | }, 30 | [types.LOADING_ALLOW](state, value) { 31 | state.loadingAllow = value 32 | } 33 | } 34 | 35 | export default { 36 | namespaced: true, 37 | state, 38 | getters, 39 | actions, 40 | mutations 41 | } 42 | -------------------------------------------------------------------------------- /client/src/store/modules/general/mutation_types.js: -------------------------------------------------------------------------------- 1 | export const LOADING_ALLOW = 'LOADING_ALLOW' 2 | export const LOADING_START = 'LOADING_START' 3 | export const LOADING_STOP = 'LOADING_STOP' 4 | -------------------------------------------------------------------------------- /client/src/store/modules/user/index.js: -------------------------------------------------------------------------------- 1 | import axios from '../../../interceptor' 2 | 3 | export const state = () => ({ 4 | item: {}, 5 | errors: {} 6 | }) 7 | 8 | export const mutations = { 9 | SET_ITEM(state, item) { 10 | state.item = item 11 | }, 12 | UPDATE_ITEM(state, item) { 13 | state.item = Object.assign({}, state.item, item) 14 | }, 15 | SET_ERRORS(state, errors) { 16 | state.errors = errors 17 | } 18 | } 19 | 20 | export const getters = { 21 | item: state => state.item, 22 | errors: state => state.errors 23 | } 24 | 25 | export const actions = { 26 | processErrors({ commit }, data) { 27 | if (data.violations) { 28 | let errors = {} 29 | 30 | data.violations.map(violation => { 31 | Object.assign(errors, { [violation.propertyPath]: violation.message }) 32 | }) 33 | 34 | commit('SET_ERRORS', errors) 35 | } 36 | 37 | throw data 38 | }, 39 | register({ state, commit, dispatch }) { 40 | commit('SET_ERRORS', {}) 41 | 42 | const url = process.env.VUE_APP_API_URL + '/register' 43 | 44 | return axios.post(url, state.item).catch(e => { 45 | dispatch('processErrors', e.response.data) 46 | }) 47 | } 48 | } 49 | 50 | export default { 51 | namespaced: true, 52 | state, 53 | getters, 54 | actions, 55 | mutations 56 | } 57 | -------------------------------------------------------------------------------- /client/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: true, 3 | runtimeCompiler: true 4 | } 5 | -------------------------------------------------------------------------------- /mobile/.env.dist: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL='http://localhost:8888' 2 | VUE_APP_MERCURE_URL='http://localhost:1337/hub' 3 | -------------------------------------------------------------------------------- /mobile/.env.prod: -------------------------------------------------------------------------------- 1 | VUE_APP_API_URL='https://apidemo.yap.life' 2 | VUE_APP_MERCURE_URL='https://mercuredemo.yap.life/hub' 3 | -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # Cordova 25 | /src-cordova/platforms 26 | /src-cordova/plugins 27 | /public/cordova.js 28 | -------------------------------------------------------------------------------- /mobile/README.md: -------------------------------------------------------------------------------- 1 | # mobile 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile", 3 | "description": "Mercure POC", 4 | "version": "0.1.0", 5 | "author": "info@inshop.com.ua", 6 | "license": "MIT", 7 | "private": false, 8 | "scripts": { 9 | "serve": "vue-cli-service serve", 10 | "build": "vue-cli-service build", 11 | "lint": "vue-cli-service lint", 12 | "cordova-build-android": "cross-env CORDOVA_PLATFORM=android vue-cli-service cordova-build-android", 13 | "cordova-build-browser": "cross-env CORDOVA_PLATFORM=browser vue-cli-service cordova-build-browser", 14 | "cordova-build-ios": "cross-env CORDOVA_PLATFORM=ios vue-cli-service cordova-build-ios", 15 | "cordova-build-only-www-android": "cross-env CORDOVA_PLATFORM=android vue-cli-service cordova-build-only-www-android", 16 | "cordova-build-only-www-browser": "cross-env CORDOVA_PLATFORM=browser vue-cli-service cordova-build-only-www-browser", 17 | "cordova-build-only-www-ios": "cross-env CORDOVA_PLATFORM=ios vue-cli-service cordova-build-only-www-ios", 18 | "cordova-build-only-www-osx": "cross-env CORDOVA_PLATFORM=osx vue-cli-service cordova-build-only-www-osx", 19 | "cordova-build-osx": "cross-env CORDOVA_PLATFORM=osx vue-cli-service cordova-build-osx", 20 | "cordova-prepare": "vue-cli-service cordova-prepare", 21 | "cordova-serve-android": "cross-env CORDOVA_PLATFORM=android vue-cli-service cordova-serve-android", 22 | "cordova-serve-browser": "cross-env CORDOVA_PLATFORM=browser vue-cli-service cordova-serve-browser", 23 | "cordova-serve-ios": "cross-env CORDOVA_PLATFORM=ios vue-cli-service cordova-serve-ios", 24 | "cordova-serve-osx": "cross-env CORDOVA_PLATFORM=osx vue-cli-service cordova-serve-osx" 25 | }, 26 | "dependencies": { 27 | "@inshopgroup/vue-inshop-crm-form-components": "^1.0.21", 28 | "axios": "^0.21.1", 29 | "bootstrap": "^4.3.1", 30 | "core-js": "^2.6.9", 31 | "eventsource": "^1.0.7", 32 | "jwt-decode": "^2.2.0", 33 | "pluralize": "^8.0.0", 34 | "vue": "^2.6.10", 35 | "vue-router": "^3.0.7", 36 | "vue-toastr": "^2.1.1", 37 | "vuex": "^3.1.1", 38 | "xregexp": "^4.2.4" 39 | }, 40 | "devDependencies": { 41 | "@vue/cli-plugin-babel": "^3.9.2", 42 | "@vue/cli-plugin-eslint": "^3.9.2", 43 | "@vue/cli-service": "^3.9.2", 44 | "babel-eslint": "^10.0.2", 45 | "eslint": "^5.16.0", 46 | "eslint-config-prettier": "^6.0.0", 47 | "eslint-plugin-prettier": "^3.1.0", 48 | "eslint-plugin-vue": "^5.2.3", 49 | "node-sass": "^7.0.0", 50 | "prettier": "^1.18.2", 51 | "sass-loader": "^7.1.0", 52 | "vue-cli-plugin-cordova": "^2.3.6", 53 | "vue-template-compiler": "^2.6.10" 54 | }, 55 | "eslintConfig": { 56 | "root": true, 57 | "env": { 58 | "node": true 59 | }, 60 | "extends": [ 61 | "plugin:vue/recommended", 62 | "eslint:recommended", 63 | "prettier/vue", 64 | "plugin:prettier/recommended" 65 | ], 66 | "parserOptions": { 67 | "parser": "babel-eslint" 68 | }, 69 | "rules": { 70 | "semi": [ 71 | "error", 72 | "never" 73 | ], 74 | "vue/script-indent": "error", 75 | "prettier/prettier": [ 76 | "warn", 77 | { 78 | "singleQuote": true, 79 | "semi": false, 80 | "trailingComma": "none" 81 | } 82 | ] 83 | } 84 | }, 85 | "postcss": { 86 | "plugins": { 87 | "autoprefixer": {} 88 | } 89 | }, 90 | "browserslist": [ 91 | "> 1%", 92 | "last 2 versions" 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /mobile/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mercure POC 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /mobile/src-cordova/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | mobile.yap.life 4 | 5 | A sample Apache Cordova application that responds to the deviceready event. 6 | 7 | 8 | Apache Cordova Team 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mobile/src-cordova/hooks/README.md: -------------------------------------------------------------------------------- 1 | 21 | # Cordova Hooks 22 | 23 | Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide. 24 | -------------------------------------------------------------------------------- /mobile/src-cordova/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.vue.example.app", 3 | "displayName": "VueExampleAppName", 4 | "version": "1.0.0", 5 | "description": "A sample Apache Cordova application that responds to the deviceready event.", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "ecosystem:cordova" 12 | ], 13 | "author": "Apache Cordova Team", 14 | "license": "Apache-2.0", 15 | "dependencies": { 16 | "cordova-android": "^8.0.0", 17 | "cordova-ios": "^5.0.1", 18 | "cordova-plugin-whitelist": "^1.3.4" 19 | }, 20 | "devDependencies": {}, 21 | "cordova": { 22 | "plugins": { 23 | "cordova-plugin-whitelist": {} 24 | }, 25 | "platforms": [ 26 | "android", 27 | "ios" 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /mobile/src-cordova/www/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | */ 3 | !.gitignore -------------------------------------------------------------------------------- /mobile/src/main.js: -------------------------------------------------------------------------------- 1 | import '../../client/src/main' 2 | -------------------------------------------------------------------------------- /mobile/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: '', 3 | pluginOptions: { 4 | cordovaPath: 'src-cordova' 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /proxy/.env.sample: -------------------------------------------------------------------------------- 1 | # 2 | # docker-compose-letsencrypt-nginx-proxy-companion 3 | # 4 | # A Web Proxy using docker with NGINX and Let's Encrypt 5 | # Using the great community docker-gen, nginx-proxy and docker-letsencrypt-nginx-proxy-companion 6 | # 7 | # This is the .env file to set up your webproxy enviornment 8 | 9 | # 10 | # Your local containers NAME 11 | # 12 | NGINX_WEB=nginx-web 13 | DOCKER_GEN=nginx-gen 14 | LETS_ENCRYPT=nginx-letsencrypt 15 | 16 | # 17 | # Your external IP address 18 | # 19 | IP=188.166.13.163 20 | 21 | # 22 | # Default Network 23 | # 24 | NETWORK=webproxy 25 | 26 | # 27 | # Service Network (Optional) 28 | # 29 | # In case you decide to add a new network to your services containers you can set this 30 | # network as a SERVICE_NETWORK 31 | # 32 | # [WARNING] This setting was built to use our `start.sh` script or in that special case 33 | # you could use the docker-composer with our multiple network option, as of: 34 | # `docker-compose -f docker-compose-multiple-networks.yml up -d` 35 | # 36 | #SERVICE_NETWORK=webservices 37 | 38 | # 39 | # NGINX file path 40 | # 41 | NGINX_FILES_PATH=/home/tim/apiplatformtemplate/proxy/nginx/data 42 | 43 | # 44 | # NGINX use special conf files 45 | # 46 | # In case you want to add some special configuration to your NGINX Web Proxy you could 47 | # add your files to ./conf.d/ folder as of sample file 'uploadsize.conf' 48 | # 49 | # [WARNING] This setting was built to use our `start.sh`. 50 | # 51 | # [WARNING] Once you set this options to true all your files will be copied to data 52 | # folder (./data/conf.d). If you decide to remove this special configuration 53 | # you must delete your files from data folder ./data/conf.d. 54 | # 55 | USE_NGINX_CONF_FILES=true 56 | 57 | # 58 | # Docker Logging Config 59 | # 60 | # This section offers two options max-size and max-file, which follow the docker documentation 61 | # as follow: 62 | # 63 | # logging: 64 | # driver: "json-file" 65 | # options: 66 | # max-size: "200k" 67 | # max-file: "10" 68 | # 69 | #NGINX_WEB_LOG_MAX_SIZE=4m 70 | #NGINX_WEB_LOG_MAX_FILE=10 71 | 72 | #NGINX_GEN_LOG_MAX_SIZE=2m 73 | #NGINX_GEN_LOG_MAX_FILE=10 74 | 75 | #NGINX_LETSENCRYPT_LOG_MAX_SIZE=2m 76 | #NGINX_LETSENCRYPT_LOG_MAX_FILE=10 77 | -------------------------------------------------------------------------------- /proxy/.gitignore: -------------------------------------------------------------------------------- 1 | !.env.sample 2 | -------------------------------------------------------------------------------- /proxy/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Evert Ramos 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 | -------------------------------------------------------------------------------- /proxy/Readme.md: -------------------------------------------------------------------------------- 1 | https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion 2 | 3 | # Web Proxy using Docker, NGINX and Let's Encrypt 4 | 5 | With this repo you will be able to set up your server with multiple sites using a single NGINX proxy to manage your connections, automating your apps container (port 80 and 443) to auto renew your ssl certificates with Let´s Encrypt. 6 | 7 | Something like: 8 | 9 | ![Web Proxy environment](https://github.com/evertramos/images/raw/master/webproxy.jpg) 10 | 11 | 12 | ## Why use it? 13 | 14 | Using this set up you will be able start a production environment in a few seconds. For each new web project simply start the containers with the option `-e VIRTUAL_HOST=your.domain.com` and you will be ready to go. If you want to use SSL (Let's Encrypt) just add the tag `-e LETSENCRYPT_HOST=your.domain.com`. Done! 15 | 16 | Easy and trustworthy! 17 | 18 | 19 | ## Prerequisites 20 | 21 | In order to use this compose file (docker-compose.yml) you must have: 22 | 23 | 1. docker (https://docs.docker.com/engine/installation/) 24 | 2. docker-compose (https://docs.docker.com/compose/install/) 25 | 26 | 27 | ## How to use it 28 | 29 | 1. Clone this repository: 30 | 31 | ```bash 32 | git clone https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion.git 33 | ``` 34 | 35 | 2. Make a copy of our `.env.sample` and rename it to `.env`: 36 | 37 | Update this file with your preferences. 38 | 39 | ``` 40 | # 41 | # docker-compose-letsencrypt-nginx-proxy-companion 42 | # 43 | # A Web Proxy using docker with NGINX and Let's Encrypt 44 | # Using the great community docker-gen, nginx-proxy and docker-letsencrypt-nginx-proxy-companion 45 | # 46 | # This is the .env file to set up your webproxy enviornment 47 | 48 | # 49 | # Your local containers NAME 50 | # 51 | NGINX_WEB=nginx-web 52 | DOCKER_GEN=nginx-gen 53 | LETS_ENCRYPT=nginx-letsencrypt 54 | 55 | # 56 | # Your external IP address 57 | # 58 | IP=0.0.0.0 59 | 60 | # 61 | # Default Network 62 | # 63 | NETWORK=webproxy 64 | 65 | # 66 | # Service Network 67 | # 68 | # This is optional in case you decide to add a new network to your services containers 69 | #SERVICE_NETWORK=webservices 70 | 71 | # 72 | # NGINX file path 73 | # 74 | NGINX_FILES_PATH=/path/to/your/nginx/data 75 | 76 | # 77 | # NGINX use special conf files 78 | # 79 | # In case you want to add some special configuration to your NGINX Web Proxy you could 80 | # add your files to ./conf.d/ folder as of sample file 'uploadsize.conf' 81 | # 82 | # [WARNING] This setting was built to use our `start.sh`. 83 | # 84 | # [WARNING] Once you set this options to true all your files will be copied to data 85 | # folder (./data/conf.d). If you decide to remove this special configuration 86 | # you must delete your files from data folder ./data/conf.d. 87 | # 88 | #USE_NGINX_CONF_FILES=true 89 | 90 | # 91 | # Docker Logging Config 92 | # 93 | # This section offers two options max-size and max-file, which follow the docker documentation 94 | # as follow: 95 | # 96 | # logging: 97 | # driver: "json-file" 98 | # options: 99 | # max-size: "200k" 100 | # max-file: "10" 101 | # 102 | #NGINX_WEB_LOG_MAX_SIZE=4m 103 | #NGINX_WEB_LOG_MAX_FILE=10 104 | 105 | #NGINX_GEN_LOG_MAX_SIZE=2m 106 | #NGINX_GEN_LOG_MAX_FILE=10 107 | 108 | #NGINX_LETSENCRYPT_LOG_MAX_SIZE=2m 109 | #NGINX_LETSENCRYPT_LOG_MAX_FILE=10 110 | ``` 111 | 112 | 3. Run our start script 113 | 114 | ```bash 115 | ./start.sh 116 | ``` 117 | 118 | Your proxy is ready to go! 119 | 120 | ## Starting your web containers 121 | 122 | After following the steps above you can start new web containers with port 80 open and add the option `-e VIRTUAL_HOST=your.domain.com` so proxy will automatically generate the reverse script in NGINX Proxy to forward new connections to your web/app container, as of: 123 | 124 | ```bash 125 | docker run -d -e VIRTUAL_HOST=your.domain.com \ 126 | --network=webproxy \ 127 | --name my_app \ 128 | httpd:alpine 129 | ``` 130 | 131 | To have SSL in your web/app you just add the option `-e LETSENCRYPT_HOST=your.domain.com`, as follow: 132 | 133 | ```bash 134 | docker run -d -e VIRTUAL_HOST=your.domain.com \ 135 | -e LETSENCRYPT_HOST=your.domain.com \ 136 | -e LETSENCRYPT_EMAIL=your.email@your.domain.com \ 137 | --network=webproxy \ 138 | --name my_app \ 139 | httpd:alpine 140 | ``` 141 | 142 | > You don´t need to open port *443* in your container, the certificate validation is managed by the web proxy. 143 | 144 | 145 | > Please note that when running a new container to generate certificates with LetsEncrypt (`-e LETSENCRYPT_HOST=your.domain.com`), it may take a few minutes, depending on multiples circumstances. 146 | 147 | ## Further Options 148 | 149 | 1. Basic Authentication Support 150 | 151 | In order to be able to secure your virtual host with basic authentication, you must create a htpasswd file within `${NGINX_FILES_PATH}/htpasswd/${VIRTUAL_HOST}` via: 152 | 153 | ```bash 154 | sudo sh -c "echo -n '[username]:' >> ${NGINX_FILES_PATH}/htpasswd/${VIRTUAL_HOST}" 155 | sudo sh -c "openssl passwd -apr1 >> ${NGINX_FILES_PATH}/htpasswd/${VIRTUAL_HOST}" 156 | ``` 157 | 158 | > Please substitute the `${NGINX_FILES_PATH}` with your path information, replace `[username]` with your username and `${VIRTUAL_HOST}` with your host's domain. You will be prompted for a password. 159 | 160 | 2. Using multiple networks 161 | 162 | If you want to use more than one network to better organize your environment you could set the option `SERVICE_NETWORK` in our `.env.sample` or you can just create your own network and attach all your containers as of: 163 | 164 | ```bash 165 | docker network create myownnetwork 166 | docker network connect myownnetwork nginx-web 167 | docker network connect myownnetwork nginx-gen 168 | docker network connect myownnetwork nginx-letsencrypt 169 | ``` 170 | 171 | 3. Using different ports to be proxied 172 | 173 | If your service container runs on port 8545 you probably will need to add the `VIRTUAL_PORT` environment variable to your container, in the `docker-compose.yml`, as of: 174 | 175 | ```bash 176 | parity 177 | image: parity/parity:v1.8.9 178 | [...] 179 | environment: 180 | [...] 181 | VIRTUAL_PORT: 8545 182 | ``` 183 | 184 | Or as of below: 185 | 186 | ```bash 187 | docker run [...] -e VIRTUAL_PORT=8545 [...] 188 | ``` 189 | 190 | ## Testing your proxy with scripts preconfigured 191 | 192 | 1. Run the script `test.sh` informing your domain already configured in your DNS to point out to your server as follow: 193 | 194 | ```bash 195 | ./test_start.sh your.domain.com 196 | ``` 197 | 198 | or simply run: 199 | 200 | ```bash 201 | docker run -dit -e VIRTUAL_HOST=your.domain.com --network=webproxy --name test-web httpd:alpine 202 | ``` 203 | 204 | Access your browser with your domain! 205 | 206 | To stop and remove your test container run our `stop_test.sh` script: 207 | 208 | ```bash 209 | ./test_stop.sh 210 | ``` 211 | 212 | Or simply run: 213 | 214 | ```bash 215 | docker stop test-web && docker rm test-web 216 | ``` 217 | 218 | ## Production Environment using Web Proxy and Wordpress 219 | 220 | 1. [docker-wordpress-letsencrypt](https://github.com/evertramos/docker-wordpress-letsencrypt) 221 | 2. [docker-portainer-letsencrypt](https://github.com/evertramos/docker-portainer-letsencrypt) 222 | 3. [docker-nextcloud-letsencrypt](https://github.com/evertramos/docker-nextcloud-letsencrypt) 223 | 224 | In this repo you will find a docker-compose file to start a production environment for a new wordpress site. 225 | 226 | ## Credits 227 | 228 | Without the repositories below this webproxy wouldn´t be possible. 229 | 230 | Credits goes to: 231 | - nginx-proxy [@jwilder](https://github.com/jwilder/nginx-proxy) 232 | - docker-gen [@jwilder](https://github.com/jwilder/docker-gen) 233 | - docker-letsencrypt-nginx-proxy-companion [@JrCs](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion) 234 | 235 | 236 | ### Special thanks to: 237 | 238 | - [@buchdag](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/pull/226#event-1145800062) 239 | - [@fracz](https://github.com/fracz) - Many contributions! 240 | -------------------------------------------------------------------------------- /proxy/conf.d/realip.conf: -------------------------------------------------------------------------------- 1 | # 2 | # Real IP Settings 3 | # 4 | # This option get user's real ip address 5 | # to be fowared to your service container 6 | 7 | # 8 | # Basic settings 9 | # 10 | # The option 'set_real_ip_from' 11 | # must correspont to your docker network address 12 | set_real_ip_from 172.18.0.0/32; 13 | real_ip_header X-Real-IP; 14 | real_ip_recursive on; 15 | 16 | # 17 | # CloudFlare settings 18 | # 19 | # If you CloudFlare and want to forward the 20 | # user's real IP to your app services you 21 | # must uncomment all lines below and be sure 22 | # to comment the lines of the "Basic settings" 23 | #set_real_ip_from 103.21.244.0/22; 24 | #set_real_ip_from 103.22.200.0/22; 25 | #set_real_ip_from 103.31.4.0/22; 26 | #set_real_ip_from 104.16.0.0/12; 27 | #set_real_ip_from 108.162.192.0/18; 28 | #set_real_ip_from 131.0.72.0/22; 29 | #set_real_ip_from 141.101.64.0/18; 30 | #set_real_ip_from 162.158.0.0/15; 31 | #set_real_ip_from 172.64.0.0/13; 32 | #set_real_ip_from 173.245.48.0/20; 33 | #set_real_ip_from 188.114.96.0/20; 34 | #set_real_ip_from 190.93.240.0/20; 35 | #set_real_ip_from 197.234.240.0/22; 36 | #set_real_ip_from 198.41.128.0/17; 37 | #set_real_ip_from 2400:cb00::/32; 38 | #set_real_ip_from 2606:4700::/32; 39 | #set_real_ip_from 2803:f800::/32; 40 | #set_real_ip_from 2405:b500::/32; 41 | #set_real_ip_from 2405:8100::/32; 42 | #set_real_ip_from 2c0f:f248::/32; 43 | #set_real_ip_from 2a06:98c0::/29; 44 | #real_ip_header X-Forwarded-For; 45 | -------------------------------------------------------------------------------- /proxy/conf.d/servertokens.conf: -------------------------------------------------------------------------------- 1 | server_tokens off; 2 | -------------------------------------------------------------------------------- /proxy/conf.d/uploadsize.conf: -------------------------------------------------------------------------------- 1 | client_max_body_size 100m; 2 | -------------------------------------------------------------------------------- /proxy/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpharaoh/apiplatformtemplate/e795855b4db2117d775d9c225b83d9b250999492/proxy/data/.gitkeep -------------------------------------------------------------------------------- /proxy/docker-compose-multiple-networks.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx-web: 4 | image: nginx 5 | labels: 6 | com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true" 7 | container_name: ${NGINX_WEB:-nginx-web} 8 | restart: always 9 | ports: 10 | - "${IP:-0.0.0.0}:80:80" 11 | - "${IP:-0.0.0.0}:443:443" 12 | volumes: 13 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 14 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 15 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 16 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro 17 | - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro 18 | networks: 19 | - default 20 | - outside 21 | logging: 22 | options: 23 | max-size: ${NGINX_WEB_LOG_MAX_SIZE:-4m} 24 | max-file: ${NGINX_WEB_LOG_MAX_FILE:-10} 25 | 26 | nginx-gen: 27 | image: jwilder/docker-gen 28 | command: -notify-sighup ${NGINX_WEB:-nginx-web} -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 29 | container_name: ${DOCKER_GEN:-nginx-gen} 30 | restart: always 31 | volumes: 32 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 33 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 34 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 35 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro 36 | - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro 37 | - /var/run/docker.sock:/tmp/docker.sock:ro 38 | - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro 39 | networks: 40 | - default 41 | - outside 42 | logging: 43 | options: 44 | max-size: ${NGINX_GEN_LOG_MAX_SIZE:-2m} 45 | max-file: ${NGINX_GEN_LOG_MAX_FILE:-10} 46 | 47 | nginx-letsencrypt: 48 | image: jrcs/letsencrypt-nginx-proxy-companion 49 | container_name: ${LETS_ENCRYPT:-nginx-letsencrypt} 50 | restart: always 51 | volumes: 52 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 53 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 54 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 55 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:rw 56 | - /var/run/docker.sock:/var/run/docker.sock:ro 57 | environment: 58 | NGINX_DOCKER_GEN_CONTAINER: ${DOCKER_GEN:-nginx-gen} 59 | NGINX_PROXY_CONTAINER: ${NGINX_WEB:-nginx-web} 60 | networks: 61 | - default 62 | - outside 63 | logging: 64 | options: 65 | max-size: ${NGINX_LETSENCRYPT_LOG_MAX_SIZE:-2m} 66 | max-file: ${NGINX_LETSENCRYPT_LOG_MAX_FILE:-10} 67 | 68 | networks: 69 | default: 70 | external: 71 | name: ${NETWORK:-webproxy} 72 | outside: 73 | external: 74 | name: ${SERVICE_NETWORK:-webservices} 75 | -------------------------------------------------------------------------------- /proxy/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | nginx-web: 4 | image: nginx 5 | labels: 6 | com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy: "true" 7 | container_name: ${NGINX_WEB:-nginx-web} 8 | restart: always 9 | ports: 10 | - "${IP:-0.0.0.0}:80:80" 11 | - "${IP:-0.0.0.0}:443:443" 12 | volumes: 13 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 14 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 15 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 16 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro 17 | - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro 18 | logging: 19 | options: 20 | max-size: ${NGINX_WEB_LOG_MAX_SIZE:-4m} 21 | max-file: ${NGINX_WEB_LOG_MAX_FILE:-10} 22 | 23 | nginx-gen: 24 | image: jwilder/docker-gen 25 | command: -notify-sighup ${NGINX_WEB:-nginx-web} -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 26 | container_name: ${DOCKER_GEN:-nginx-gen} 27 | restart: always 28 | volumes: 29 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 30 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 31 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 32 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:ro 33 | - ${NGINX_FILES_PATH:-./data}/htpasswd:/etc/nginx/htpasswd:ro 34 | - /var/run/docker.sock:/tmp/docker.sock:ro 35 | - ./nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro 36 | logging: 37 | options: 38 | max-size: ${NGINX_GEN_LOG_MAX_SIZE:-2m} 39 | max-file: ${NGINX_GEN_LOG_MAX_FILE:-10} 40 | 41 | nginx-letsencrypt: 42 | image: jrcs/letsencrypt-nginx-proxy-companion 43 | container_name: ${LETS_ENCRYPT:-nginx-letsencrypt} 44 | restart: always 45 | volumes: 46 | - ${NGINX_FILES_PATH:-./data}/conf.d:/etc/nginx/conf.d 47 | - ${NGINX_FILES_PATH:-./data}/vhost.d:/etc/nginx/vhost.d 48 | - ${NGINX_FILES_PATH:-./data}/html:/usr/share/nginx/html 49 | - ${NGINX_FILES_PATH:-./data}/certs:/etc/nginx/certs:rw 50 | - /var/run/docker.sock:/var/run/docker.sock:ro 51 | environment: 52 | NGINX_DOCKER_GEN_CONTAINER: ${DOCKER_GEN:-nginx-gen} 53 | NGINX_PROXY_CONTAINER: ${NGINX_WEB:-nginx-web} 54 | logging: 55 | options: 56 | max-size: ${NGINX_LETSENCRYPT_LOG_MAX_SIZE:-2m} 57 | max-file: ${NGINX_LETSENCRYPT_LOG_MAX_FILE:-10} 58 | 59 | networks: 60 | default: 61 | external: 62 | name: ${NETWORK:-webproxy} 63 | -------------------------------------------------------------------------------- /proxy/nginx.tmpl: -------------------------------------------------------------------------------- 1 | {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} 2 | 3 | {{ define "upstream" }} 4 | {{ if .Address }} 5 | {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} 6 | {{ if and .Container.Node.ID .Address.HostPort }} 7 | # {{ .Container.Node.Name }}/{{ .Container.Name }} 8 | server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; 9 | {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} 10 | {{ else if .Network }} 11 | # {{ .Container.Name }} 12 | server {{ .Network.IP }}:{{ .Address.Port }}; 13 | {{ end }} 14 | {{ else if .Network }} 15 | # {{ .Container.Name }} 16 | {{ if .Network.IP }} 17 | server {{ .Network.IP }} down; 18 | {{ else }} 19 | server 127.0.0.1 down; 20 | {{ end }} 21 | {{ end }} 22 | 23 | {{ end }} 24 | 25 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 26 | # scheme used to connect to this server 27 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 28 | default $http_x_forwarded_proto; 29 | '' $scheme; 30 | } 31 | 32 | # If we receive X-Forwarded-Port, pass it through; otherwise, pass along the 33 | # server port the client connected to 34 | map $http_x_forwarded_port $proxy_x_forwarded_port { 35 | default $http_x_forwarded_port; 36 | '' $server_port; 37 | } 38 | 39 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 40 | # Connection header that may have been passed to this server 41 | map $http_upgrade $proxy_connection { 42 | default upgrade; 43 | '' close; 44 | } 45 | 46 | # Apply fix for very long server names 47 | server_names_hash_bucket_size 128; 48 | 49 | # Default dhparam 50 | {{ if (exists "/etc/nginx/dhparam/dhparam.pem") }} 51 | ssl_dhparam /etc/nginx/dhparam/dhparam.pem; 52 | {{ end }} 53 | 54 | # Set appropriate X-Forwarded-Ssl header 55 | map $scheme $proxy_x_forwarded_ssl { 56 | default off; 57 | https on; 58 | } 59 | 60 | gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 61 | 62 | log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 63 | '"$request" $status $body_bytes_sent ' 64 | '"$http_referer" "$http_user_agent"'; 65 | 66 | access_log off; 67 | 68 | {{ if $.Env.RESOLVERS }} 69 | resolver {{ $.Env.RESOLVERS }}; 70 | {{ end }} 71 | 72 | {{ if (exists "/etc/nginx/proxy.conf") }} 73 | include /etc/nginx/proxy.conf; 74 | {{ else }} 75 | # HTTP 1.1 support 76 | proxy_http_version 1.1; 77 | proxy_buffering off; 78 | proxy_set_header Host $http_host; 79 | proxy_set_header Upgrade $http_upgrade; 80 | proxy_set_header Connection $proxy_connection; 81 | proxy_set_header X-Real-IP $remote_addr; 82 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 83 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 84 | proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; 85 | proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; 86 | 87 | # Mitigate httpoxy attack (see README for details) 88 | proxy_set_header Proxy ""; 89 | {{ end }} 90 | 91 | {{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }} 92 | server { 93 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 94 | listen 80; 95 | {{ if $enable_ipv6 }} 96 | listen [::]:80; 97 | {{ end }} 98 | access_log /var/log/nginx/access.log vhost; 99 | return 503; 100 | } 101 | 102 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 103 | server { 104 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 105 | listen 443 ssl http2; 106 | {{ if $enable_ipv6 }} 107 | listen [::]:443 ssl http2; 108 | {{ end }} 109 | access_log /var/log/nginx/access.log vhost; 110 | return 503; 111 | 112 | ssl_session_tickets off; 113 | ssl_certificate /etc/nginx/certs/default.crt; 114 | ssl_certificate_key /etc/nginx/certs/default.key; 115 | } 116 | {{ end }} 117 | 118 | {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} 119 | 120 | {{ $host := trim $host }} 121 | {{ $is_regexp := hasPrefix "~" $host }} 122 | {{ $upstream_name := when $is_regexp (sha1 $host) $host }} 123 | 124 | # {{ $host }} 125 | upstream {{ $upstream_name }} { 126 | 127 | {{ range $container := $containers }} 128 | {{ $addrLen := len $container.Addresses }} 129 | 130 | {{ range $knownNetwork := $CurrentContainer.Networks }} 131 | {{ range $containerNetwork := $container.Networks }} 132 | {{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }} 133 | ## Can be connected with "{{ $containerNetwork.Name }}" network 134 | 135 | {{/* If only 1 port exposed, use that */}} 136 | {{ if eq $addrLen 1 }} 137 | {{ $address := index $container.Addresses 0 }} 138 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 139 | {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} 140 | {{ else }} 141 | {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} 142 | {{ $address := where $container.Addresses "Port" $port | first }} 143 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 144 | {{ end }} 145 | {{ else }} 146 | # Cannot connect to network of this container 147 | server 127.0.0.1 down; 148 | {{ end }} 149 | {{ end }} 150 | {{ end }} 151 | {{ end }} 152 | } 153 | 154 | {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 155 | {{ $default_server := index (dict $host "" $default_host "default_server") $host }} 156 | 157 | {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} 158 | {{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }} 159 | 160 | {{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}} 161 | {{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }} 162 | 163 | {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} 164 | {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} 165 | 166 | {{/* Get the SSL_POLICY defined by containers w/ the same vhost, falling back to "Mozilla-Intermediate" */}} 167 | {{ $ssl_policy := or (first (groupByKeys $containers "Env.SSL_POLICY")) "Mozilla-Intermediate" }} 168 | 169 | {{/* Get the HSTS defined by containers w/ the same vhost, falling back to "max-age=31536000" */}} 170 | {{ $hsts := or (first (groupByKeys $containers "Env.HSTS")) "max-age=31536000" }} 171 | 172 | {{/* Get the VIRTUAL_ROOT By containers w/ use fastcgi root */}} 173 | {{ $vhost_root := or (first (groupByKeys $containers "Env.VIRTUAL_ROOT")) "/var/www/public" }} 174 | 175 | 176 | {{/* Get the first cert name defined by containers w/ the same vhost */}} 177 | {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} 178 | 179 | {{/* Get the best matching cert by name for the vhost. */}} 180 | {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} 181 | 182 | {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} 183 | {{ $vhostCert := trimSuffix ".crt" $vhostCert }} 184 | {{ $vhostCert := trimSuffix ".key" $vhostCert }} 185 | 186 | {{/* Use the cert specified on the container or fallback to the best vhost match */}} 187 | {{ $cert := (coalesce $certName $vhostCert) }} 188 | 189 | {{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} 190 | 191 | {{ if $is_https }} 192 | 193 | {{ if eq $https_method "redirect" }} 194 | server { 195 | server_name {{ $host }}; 196 | listen 80 {{ $default_server }}; 197 | {{ if $enable_ipv6 }} 198 | listen [::]:80 {{ $default_server }}; 199 | {{ end }} 200 | access_log /var/log/nginx/access.log vhost; 201 | return 301 https://$host$request_uri; 202 | } 203 | {{ end }} 204 | 205 | server { 206 | server_name {{ $host }}; 207 | listen 443 ssl http2 {{ $default_server }}; 208 | {{ if $enable_ipv6 }} 209 | listen [::]:443 ssl http2 {{ $default_server }}; 210 | {{ end }} 211 | access_log /var/log/nginx/access.log vhost; 212 | 213 | {{ if eq $network_tag "internal" }} 214 | # Only allow traffic from internal clients 215 | include /etc/nginx/network_internal.conf; 216 | {{ end }} 217 | 218 | {{ if eq $ssl_policy "Mozilla-Modern" }} 219 | ssl_protocols TLSv1.2 TLSv1.3; 220 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 221 | {{ else if eq $ssl_policy "Mozilla-Intermediate" }} 222 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 223 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:!DSS'; 224 | {{ else if eq $ssl_policy "Mozilla-Old" }} 225 | ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 226 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP'; 227 | {{ else if eq $ssl_policy "AWS-TLS-1-2-2017-01" }} 228 | ssl_protocols TLSv1.2 TLSv1.3; 229 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256'; 230 | {{ else if eq $ssl_policy "AWS-TLS-1-1-2017-01" }} 231 | ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; 232 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; 233 | {{ else if eq $ssl_policy "AWS-2016-08" }} 234 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 235 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA'; 236 | {{ else if eq $ssl_policy "AWS-2015-05" }} 237 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 238 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DES-CBC3-SHA'; 239 | {{ else if eq $ssl_policy "AWS-2015-03" }} 240 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 241 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA:DES-CBC3-SHA'; 242 | {{ else if eq $ssl_policy "AWS-2015-02" }} 243 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 244 | ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:DHE-DSS-AES128-SHA'; 245 | {{ end }} 246 | 247 | ssl_prefer_server_ciphers on; 248 | ssl_session_timeout 5m; 249 | ssl_session_cache shared:SSL:50m; 250 | ssl_session_tickets off; 251 | 252 | ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; 253 | ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; 254 | 255 | {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} 256 | ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; 257 | {{ end }} 258 | 259 | {{ if (exists (printf "/etc/nginx/certs/%s.chain.pem" $cert)) }} 260 | ssl_stapling on; 261 | ssl_stapling_verify on; 262 | ssl_trusted_certificate {{ printf "/etc/nginx/certs/%s.chain.pem" $cert }}; 263 | {{ end }} 264 | 265 | {{ if (and (ne $https_method "noredirect") (ne $hsts "off")) }} 266 | add_header Strict-Transport-Security "{{ trim $hsts }}" always; 267 | {{ end }} 268 | 269 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 270 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 271 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 272 | include /etc/nginx/vhost.d/default; 273 | {{ end }} 274 | 275 | location / { 276 | {{ if eq $proto "uwsgi" }} 277 | include uwsgi_params; 278 | uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; 279 | {{ else if eq $proto "fastcgi" }} 280 | root {{ trim $vhost_root }}; 281 | include fastcgi.conf; 282 | fastcgi_pass {{ trim $upstream_name }}; 283 | {{ else }} 284 | proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; 285 | {{ end }} 286 | 287 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 288 | auth_basic "Restricted {{ $host }}"; 289 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 290 | {{ end }} 291 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 292 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 293 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 294 | include /etc/nginx/vhost.d/default_location; 295 | {{ end }} 296 | } 297 | } 298 | 299 | {{ end }} 300 | 301 | {{ if or (not $is_https) (eq $https_method "noredirect") }} 302 | 303 | server { 304 | server_name {{ $host }}; 305 | listen 80 {{ $default_server }}; 306 | {{ if $enable_ipv6 }} 307 | listen [::]:80 {{ $default_server }}; 308 | {{ end }} 309 | access_log /var/log/nginx/access.log vhost; 310 | 311 | {{ if eq $network_tag "internal" }} 312 | # Only allow traffic from internal clients 313 | include /etc/nginx/network_internal.conf; 314 | {{ end }} 315 | 316 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 317 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 318 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 319 | include /etc/nginx/vhost.d/default; 320 | {{ end }} 321 | 322 | location / { 323 | {{ if eq $proto "uwsgi" }} 324 | include uwsgi_params; 325 | uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; 326 | {{ else if eq $proto "fastcgi" }} 327 | root {{ trim $vhost_root }}; 328 | include fastcgi.conf; 329 | fastcgi_pass {{ trim $upstream_name }}; 330 | {{ else }} 331 | proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; 332 | {{ end }} 333 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 334 | auth_basic "Restricted {{ $host }}"; 335 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 336 | {{ end }} 337 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 338 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 339 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 340 | include /etc/nginx/vhost.d/default_location; 341 | {{ end }} 342 | } 343 | } 344 | 345 | {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 346 | server { 347 | server_name {{ $host }}; 348 | listen 443 ssl http2 {{ $default_server }}; 349 | {{ if $enable_ipv6 }} 350 | listen [::]:443 ssl http2 {{ $default_server }}; 351 | {{ end }} 352 | access_log /var/log/nginx/access.log vhost; 353 | return 500; 354 | 355 | ssl_certificate /etc/nginx/certs/default.crt; 356 | ssl_certificate_key /etc/nginx/certs/default.key; 357 | } 358 | {{ end }} 359 | 360 | {{ end }} 361 | {{ end }} 362 | -------------------------------------------------------------------------------- /proxy/scripts/base.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Basic scripts 4 | # 5 | 6 | # 1. Check if .env file exists 7 | check_env_file() { 8 | if [ -e .env ]; then 9 | source .env 10 | else 11 | echo 12 | echo "Please set up your .env file before starting your enviornment." 13 | echo 14 | exit 1 15 | fi 16 | } 17 | 18 | 19 | -------------------------------------------------------------------------------- /proxy/scripts/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # This scrip update the web proxy without downtime 5 | # 6 | # Source: https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion 7 | # 8 | 9 | # 1. Check if .env file exists 10 | if [ -e .env ]; then 11 | source .env 12 | else 13 | echo 14 | echo "Please set up your .env file before starting your enviornment." 15 | echo 16 | exit 1 17 | fi 18 | 19 | # 2. Update your repo 20 | git pull 21 | git checkout master 22 | 23 | # 3. Check if your env files has the same line numbers 24 | if [ "$(wc -l .env | cut -f1 -d' ')" != "$(wc -l .env.sample | cut -f1 -d' ')" ]; then 25 | echo 26 | echo "The sample .env are different from the your current .env file." 27 | echo "Please update your .env file to continue." 28 | echo "It must has the same lines of the sample env file." 29 | echo 30 | echo "If you keep receiving this message please check the number of line of both files" 31 | echo 32 | fi 33 | 34 | # 3. Download the latest version of nginx.tmpl 35 | curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl > nginx.tmpl 36 | 37 | # 4. Update containers without downtime 38 | docker-compose up -d --no-deps --build nginx-web 39 | docker-compose up -d --no-deps --build nginx-gen 40 | docker-compose up -d --no-deps --build nginx-letsencrypt 41 | 42 | exit 0 43 | -------------------------------------------------------------------------------- /proxy/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # This file should be used to prepare and run your WebProxy after set up your .env file 5 | # Source: https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion 6 | # 7 | 8 | # 1. Check if .env file exists 9 | if [ -e .env ]; then 10 | source .env 11 | else 12 | echo "Please set up your .env file before starting your enviornment." 13 | exit 1 14 | fi 15 | 16 | # 2. Create docker network 17 | docker network create $NETWORK 18 | 19 | # 3. Verify if second network is configured 20 | if [ ! -z ${SERVICE_NETWORK+X} ]; then 21 | docker network create $SERVICE_NETWORK 22 | fi 23 | 24 | # 4. Download the latest version of nginx.tmpl 25 | curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl > nginx.tmpl 26 | 27 | # 5. Update local images 28 | docker-compose pull 29 | 30 | # 6. Add any special configuration if it's set in .env file 31 | 32 | # Check if user set to use Special Conf Files 33 | if [ ! -z ${USE_NGINX_CONF_FILES+X} ] && [ "$USE_NGINX_CONF_FILES" = true ]; then 34 | 35 | # Create the conf folder if it does not exists 36 | mkdir -p $NGINX_FILES_PATH/conf.d 37 | 38 | # Copy the special configurations to the nginx conf folder 39 | cp -R ./conf.d/* $NGINX_FILES_PATH/conf.d 40 | 41 | # Check if there was an error and try with sudo 42 | if [ $? -ne 0 ]; then 43 | sudo cp -R ./conf.d/* $NGINX_FILES_PATH/conf.d 44 | fi 45 | 46 | # If there was any errors inform the user 47 | if [ $? -ne 0 ]; then 48 | echo 49 | echo "#######################################################" 50 | echo 51 | echo "There was an error trying to copy the nginx conf files." 52 | echo "The webproxy will still work, your custom configuration" 53 | echo "will not be loaded." 54 | echo 55 | echo "#######################################################" 56 | fi 57 | fi 58 | 59 | # 7. Start proxy 60 | 61 | # Check if you have multiple network 62 | if [ -z ${SERVICE_NETWORK+X} ]; then 63 | docker-compose up -d 64 | else 65 | docker-compose -f docker-compose-multiple-networks.yml up -d 66 | fi 67 | 68 | exit 0 69 | -------------------------------------------------------------------------------- /proxy/test_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set up your DOMAIN 4 | if [ $# -eq 0 ]; then 5 | echo "Please inform your domain name to test your proxy." 6 | echo "./test_start.sh $1" 7 | exit 1 8 | else 9 | DOMAIN=$1 10 | fi 11 | 12 | # Read your .env file 13 | source .env 14 | 15 | # Testing your proxy 16 | if [ -z ${SERVICE_NETWORK+X} ]; then 17 | docker run -d -e VIRTUAL_HOST=$DOMAIN --network=$NETWORK --name test-web httpd:alpine 18 | else 19 | docker run -d -e VIRTUAL_HOST=$DOMAIN --network=$SERVICE_NETWORK --name test-web httpd:alpine 20 | fi 21 | 22 | exit 0 23 | -------------------------------------------------------------------------------- /proxy/test_start_ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME=test-web 4 | 5 | 6 | # Set up your DOMAIN 7 | if [ $# -eq 0 ]; then 8 | echo "Please inform your domain name to test your proxy." 9 | echo "./test_start.sh $1" 10 | exit 1 11 | else 12 | DOMAIN=$1 13 | fi 14 | 15 | # Read your .env file 16 | source .env 17 | 18 | # Testing your proxy 19 | if [ -z ${SERVICE_NETWORK+X} ]; then 20 | docker run -d -e VIRTUAL_HOST=$DOMAIN -e LETSENCRYPT_HOST=$DOMAIN --network=$NETWORK --name $NAME httpd:alpine 21 | else 22 | docker run -d -e VIRTUAL_HOST=$DOMAIN -e LETSENCRYPT_HOST=$DOMAIN --network=$SERVICE_NETWORK --name $NAME httpd:alpine 23 | fi 24 | 25 | exit 0 26 | -------------------------------------------------------------------------------- /proxy/test_stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Stop and remove test enviornment 4 | docker stop test-web && docker rm test-web 5 | 6 | exit 0 7 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd proxy 4 | cp .env.sample .env 5 | docker network rm webproxy 6 | docker network create webproxy 7 | sh ./start.sh 8 | cd .. 9 | 10 | 11 | cd client 12 | 13 | # for production environment 14 | # cp .env.prod .env 15 | 16 | # for dev environment 17 | cp .env.dist .env 18 | 19 | # build 20 | cp docker-compose.dev.yml docker-compose.yml 21 | docker-compose pull 22 | docker-compose up -d --force-recreate 23 | docker-compose exec nginx yarn install 24 | docker-compose exec nginx yarn run build 25 | docker-compose down 26 | 27 | # start 28 | # for production environment 29 | # cp docker-compose.prod.yml docker-compose.yml 30 | 31 | # for dev environment 32 | cp docker-compose.dist.yml docker-compose.yml 33 | 34 | docker-compose pull 35 | docker-compose up -d --force-recreate 36 | cd .. 37 | 38 | 39 | cd api 40 | 41 | # for production environment 42 | # cp .env.prod .env 43 | # cp docker-compose.prod.yml docker-compose.yml 44 | 45 | # for dev environment 46 | cp .env.dist .env 47 | cp docker-compose.dist.yml docker-compose.yml 48 | 49 | 50 | docker-compose pull 51 | docker-compose up -d --force-recreate 52 | sleep 15 53 | 54 | openssl genrsa -out config/jwt/private.pem -aes256 4096 55 | openssl rsa -pubout -in config/jwt/private.pem -out config/jwt/public.pem 56 | openssl rsa -in config/jwt/private.pem -out config/jwt/private2.pem 57 | mv config/jwt/private.pem config/jwt/private.pem-back 58 | mv config/jwt/private2.pem config/jwt/private.pem 59 | 60 | docker-compose exec php chown -R www-data:www-data /var/www/ 61 | docker-compose exec --user=www-data php composer install 62 | docker-compose exec --user=www-data php bin/console doctrine:schema:update --force 63 | docker-compose exec --user=www-data php bin/console doctrine:schema:validate 64 | docker-compose exec --user=www-data php composer install --no-dev -o 65 | docker-compose exec --user=www-data php composer dump-autoload --optimize --no-dev --classmap-authoritative 66 | docker-compose exec --user=www-data php php bin/console cache:clear 67 | 68 | cd .. 69 | --------------------------------------------------------------------------------