├── .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 |
2 |
3 |
4 |
5 |
6 |
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 |
2 |
3 |
4 |
13 |
14 | {{ errors[property] }}
15 |
16 |
17 |
18 |
19 |
47 |
--------------------------------------------------------------------------------
/client/src/components/Loader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
41 |
--------------------------------------------------------------------------------
/client/src/components/MainSlot.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
24 |
25 |
26 |
27 |
28 |
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 |
2 |
40 |
41 |
42 |
72 |
73 |
81 |
--------------------------------------------------------------------------------
/client/src/pages/SignUp.vue:
--------------------------------------------------------------------------------
1 |
2 |
45 |
46 |
47 |
77 |
78 |
93 |
--------------------------------------------------------------------------------
/client/src/pages/book/BookForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
56 |
--------------------------------------------------------------------------------
/client/src/pages/book/Create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Add book
4 |
5 |
6 |
7 |
8 |
9 |
36 |
--------------------------------------------------------------------------------
/client/src/pages/book/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Books
4 |
5 |
8 |
9 |
10 |
11 |
12 | ID |
13 | Title |
14 | Author |
15 | Owner |
16 | Actions |
17 |
18 |
19 |
20 |
21 | {{index}}{{ item.id }} |
22 | {{ item.title }} {{item.downloadCount}} |
23 | {{ item.author }} |
24 | {{ item.owner.name }} |
25 |
26 |
33 |
34 |
35 |
40 |
47 |
54 |
55 | |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
144 |
--------------------------------------------------------------------------------
/client/src/pages/book/Show.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Book {{ item.title }}
4 |
5 |
6 |
7 | Title |
8 | {{ item.title }} ({{item.downloadCount}}) |
9 |
10 |
11 | Author |
12 | {{ item.author }} |
13 |
14 |
15 | Owner |
16 |
17 | {{ item.owner.name }}
18 | |
19 |
20 |
21 |
22 |
23 |
24 |
53 |
--------------------------------------------------------------------------------
/client/src/pages/book/Update.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Update book
4 |
5 |
6 |
7 |
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 | 
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 |
--------------------------------------------------------------------------------