├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── Makefile ├── admin ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── docker │ └── nginx │ │ └── conf.d │ │ └── default.conf ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── App.test.js │ ├── index.js │ ├── serviceWorker.js │ └── setupTests.js └── yarn.lock ├── api ├── .dockerignore ├── .env ├── .gitignore ├── .php_cs.dist ├── Dockerfile ├── bin │ └── console ├── composer.json ├── composer.lock ├── config │ ├── bootstrap.php │ ├── bundles.php │ ├── packages │ │ ├── api_platform.yaml │ │ ├── cache.yaml │ │ ├── dev │ │ │ ├── routing.yaml │ │ │ └── web_profiler.yaml │ │ ├── doctrine.yaml │ │ ├── doctrine_migrations.yaml │ │ ├── framework.yaml │ │ ├── mercure.yaml │ │ ├── nelmio_cors.yaml │ │ ├── prod │ │ │ ├── api_platform.yaml │ │ │ ├── doctrine.yaml │ │ │ └── routing.yaml │ │ ├── routing.yaml │ │ ├── security.yaml │ │ ├── test │ │ │ ├── framework.yaml │ │ │ ├── routing.yaml │ │ │ ├── twig.yaml │ │ │ ├── validator.yaml │ │ │ └── web_profiler.yaml │ │ ├── twig.yaml │ │ └── validator.yaml │ ├── routes.yaml │ ├── routes │ │ ├── annotations.yaml │ │ ├── api_platform.yaml │ │ └── dev │ │ │ ├── framework.yaml │ │ │ ├── twig.yaml │ │ │ └── web_profiler.yaml │ └── services.yaml ├── docker │ ├── nginx │ │ └── conf.d │ │ │ └── default.conf │ ├── php │ │ ├── conf.d │ │ │ ├── api-platform.dev.ini │ │ │ └── api-platform.prod.ini │ │ ├── docker-entrypoint.sh │ │ └── docker-healthcheck.sh │ └── varnish │ │ └── conf │ │ └── default.vcl ├── helm │ └── api │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── ingress.yaml │ │ ├── nginx-deployment.yaml │ │ ├── nginx-service.yaml │ │ ├── php-deployment.yaml │ │ ├── php-service.yaml │ │ ├── secrets.yaml │ │ ├── serviceaccount.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── varnish-deployment.yaml │ │ └── varnish-service.yaml │ │ └── values.yaml ├── public │ ├── favicon.ico │ └── index.php ├── src │ ├── Controller │ │ └── .gitignore │ ├── DataFixtures │ │ └── AppFixtures.php │ ├── Entity │ │ ├── .gitignore │ │ ├── Greeting.php │ │ └── ProjectAuthor.php │ ├── Kernel.php │ ├── Migrations │ │ ├── .gitignore │ │ └── Version20200830163015.php │ └── Repository │ │ ├── .gitignore │ │ └── ProjectAuthorRepository.php ├── symfony.lock └── templates │ └── base.html.twig ├── client ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── docker │ └── nginx │ │ └── conf.d │ │ └── default.conf ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── Welcome.js │ ├── Welcome.test.js │ ├── config │ │ └── entrypoint.js │ ├── index.js │ ├── serviceWorker.js │ ├── setupTests.js │ └── welcome.css └── yarn.lock ├── docker-compose.yml ├── docker └── dev-tls │ └── Dockerfile └── github └── img ├── admin-page-1.png ├── admin-page-2.png ├── admin-page-3.png ├── api-page-1.png ├── api-page-2.png ├── client-page.png ├── make-dev.png ├── make-rebuild-2.png └── make-rebuild.png /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | # Change these settings to your own preference 9 | indent_style = space 10 | indent_size = 4 11 | 12 | # We recommend you to keep these unchanged 13 | end_of_line = lf 14 | charset = utf-8 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | 18 | [*.feature] 19 | indent_style = space 20 | indent_size = 2 21 | 22 | [*.js] 23 | indent_style = space 24 | indent_size = 2 25 | 26 | [*.json] 27 | indent_style = space 28 | indent_size = 2 29 | 30 | [*.md] 31 | trim_trailing_whitespace = false 32 | 33 | [*.php] 34 | indent_style = space 35 | indent_size = 4 36 | 37 | [*.sh] 38 | indent_style = tab 39 | indent_size = 4 40 | 41 | [*.vcl] 42 | indent_style = space 43 | indent_size = 2 44 | 45 | [*.xml] 46 | indent_style = space 47 | indent_size = 4 48 | 49 | [*.{yaml,yml}] 50 | indent_style = space 51 | indent_size = 4 52 | trim_trailing_whitespace = false 53 | 54 | [api/helm/api/**.yaml] 55 | indent_style = space 56 | indent_size = 2 57 | 58 | [.github/workflows/*.yml] 59 | indent_style = space 60 | indent_size = 2 61 | 62 | [.gitmodules] 63 | indent_style = tab 64 | indent_size = 4 65 | 66 | [.php_cs{,.dist}] 67 | indent_style = space 68 | indent_size = 4 69 | 70 | [.travis.yml] 71 | indent_style = space 72 | indent_size = 2 73 | 74 | [composer.json] 75 | indent_style = space 76 | indent_size = 4 77 | 78 | [docker-compose{,.*}.{yaml,yml}] 79 | indent_style = space 80 | indent_size = 2 81 | 82 | [Dockerfile] 83 | indent_style = tab 84 | indent_size = 4 85 | 86 | [package.json] 87 | indent_style = space 88 | indent_size = 2 89 | 90 | [phpunit.xml{,.dist}] 91 | indent_style = space 92 | indent_size = 4 93 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.conf text eol=lf 4 | *.html text eol=lf 5 | *.ini text eol=lf 6 | *.js text eol=lf 7 | *.json text eol=lf 8 | *.md text eol=lf 9 | *.php text eol=lf 10 | *.sh text eol=lf 11 | *.yaml text eol=lf 12 | *.yml text eol=lf 13 | bin/console text eol=lf 14 | composer.lock text eol=lf merge=ours 15 | 16 | *.ico binary 17 | *.png binary 18 | 19 | .github export-ignore 20 | .travis.yml export-ignore 21 | README.md export-ignore 22 | update-deps.sh export-ignore 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.env 2 | /docker-compose.override.yaml 3 | /docker-compose.override.yml 4 | docker/mysql/ 5 | .docker 6 | .idea/ 7 | .vscode 8 | vendor/ 9 | *.psd 10 | *.zip 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andy Ng (andy@pcinvent.com) at www.pcinvent.com 4 | http://linkedin.com/in/pcinvent 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # pls sort alphabetically 2 | 3 | default: dev 4 | 5 | dev: dockers 6 | @echo "\033[34mCopying Dev configuration files\033[0m" 7 | 8 | rebuild: 9 | docker ps -a -q | xargs -n 1 -P 8 -I {} docker stop {} 10 | docker builder prune --all --force 11 | docker-compose build 12 | 13 | dockers: 14 | # prevent timeout 15 | export DOCKER_CLIENT_TIMEOUT=120 16 | export COMPOSE_HTTP_TIMEOUT=12 17 | docker-compose up --remove-orphans 18 | # fixed known api platform php-fpm issue 19 | docker-compose exec php php-fpm -D 20 | 21 | -------------------------------------------------------------------------------- /admin/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/._* 4 | **/.dockerignore 5 | **/.DS_Store 6 | **/.git/ 7 | **/.gitattributes 8 | **/.gitignore 9 | **/.gitmodules 10 | **/docker-compose.*.yaml 11 | **/docker-compose.*.yml 12 | **/docker-compose.yaml 13 | **/docker-compose.yml 14 | **/Dockerfile 15 | **/Thumbs.db 16 | .editorconfig 17 | .env.*.local 18 | .env.local 19 | build/ 20 | node_modules/ 21 | -------------------------------------------------------------------------------- /admin/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_ENTRYPOINT=https://localhost:8443 2 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /admin/Dockerfile: -------------------------------------------------------------------------------- 1 | # https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage 2 | # https://docs.docker.com/compose/compose-file/#target 3 | 4 | 5 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 6 | ARG NODE_VERSION=13 7 | ARG NGINX_VERSION=1.17 8 | 9 | 10 | # "development" stage 11 | FROM node:${NODE_VERSION}-alpine AS api_platform_admin_development 12 | 13 | WORKDIR /usr/src/admin 14 | 15 | # prevent the reinstallation of node modules at every changes in the source code 16 | COPY package.json yarn.lock ./ 17 | RUN set -eux; \ 18 | apk add --no-cache --virtual .gyp \ 19 | g++ \ 20 | make \ 21 | python \ 22 | ; \ 23 | yarn install; \ 24 | apk del .gyp 25 | 26 | COPY . ./ 27 | 28 | VOLUME /usr/src/admin/node_modules 29 | 30 | ENV HTTPS true 31 | 32 | CMD ["yarn", "start"] 33 | 34 | 35 | # "build" stage 36 | # depends on the "development" stage above 37 | FROM api_platform_admin_development AS api_platform_admin_build 38 | 39 | ARG REACT_APP_API_ENTRYPOINT 40 | 41 | RUN set -eux; \ 42 | yarn build 43 | 44 | 45 | # "nginx" stage 46 | # depends on the "build" stage above 47 | FROM nginx:${NGINX_VERSION}-alpine AS api_platform_admin_nginx 48 | 49 | COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf 50 | 51 | WORKDIR /usr/src/admin/build 52 | 53 | COPY --from=api_platform_admin_build /usr/src/admin/build ./ 54 | -------------------------------------------------------------------------------- /admin/docker/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /usr/src/admin/build; 3 | 4 | location / { 5 | try_files $uri /index.html; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@api-platform/admin": "^2.0.0", 7 | "@babel/runtime": "^7.0.0", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.3.2", 10 | "@testing-library/user-event": "^7.1.2", 11 | "react": "^16.12.0", 12 | "react-dom": "^16.12.0", 13 | "react-scripts": "^3.4.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/admin/public/favicon.ico -------------------------------------------------------------------------------- /admin/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | API Platform Admin 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /admin/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/admin/public/logo192.png -------------------------------------------------------------------------------- /admin/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/admin/public/logo512.png -------------------------------------------------------------------------------- /admin/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Admin", 3 | "name": "API Platform Admin", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /admin/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /admin/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HydraAdmin } from '@api-platform/admin'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /admin/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders loader', () => { 6 | const { getByText } = render(); 7 | const divElement = getByText(/The page is loading, just a moment please/i); 8 | expect(divElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /admin/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import * as serviceWorker from './serviceWorker'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | 8 | // If you want your app to work offline and load faster, you can change 9 | // unregister() to register() below. Note this comes with some pitfalls. 10 | // Learn more about service workers: https://bit.ly/CRA-PWA 11 | serviceWorker.unregister(); 12 | -------------------------------------------------------------------------------- /admin/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' } 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /admin/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /api/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/*.php~ 4 | **/._* 5 | **/.dockerignore 6 | **/.DS_Store 7 | **/.git/ 8 | **/.gitattributes 9 | **/.gitignore 10 | **/.gitmodules 11 | **/docker-compose.*.yaml 12 | **/docker-compose.*.yml 13 | **/docker-compose.yaml 14 | **/docker-compose.yml 15 | **/Dockerfile 16 | **/Thumbs.db 17 | .editorconfig 18 | .env.*.local 19 | .env.local 20 | .env.local.php 21 | .php_cs.cache 22 | bin/* 23 | !bin/console 24 | build/ 25 | docker/db/data/ 26 | helm/ 27 | public/bundles/ 28 | var/ 29 | vendor/ 30 | -------------------------------------------------------------------------------- /api/.env: -------------------------------------------------------------------------------- 1 | # In all environments, the following files are loaded if they exist, 2 | # the latter taking precedence over the former: 3 | # 4 | # * .env contains default values for the environment variables needed by the app 5 | # * .env.local uncommitted file with local overrides 6 | # * .env.$APP_ENV committed environment-specific defaults 7 | # * .env.$APP_ENV.local uncommitted environment-specific overrides 8 | # 9 | # Real environment variables win over .env files. 10 | # 11 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. 12 | # 13 | # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). 14 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration 15 | 16 | # API Platform distribution 17 | MERCURE_SUBSCRIBE_URL=https://localhost:1337/.well-known/mercure 18 | VARNISH_URL=http://cache-proxy 19 | 20 | ###> symfony/framework-bundle ### 21 | APP_ENV=dev 22 | APP_SECRET=!ChangeMe! 23 | TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 24 | TRUSTED_HOSTS='^api.dev.provetrade.com|api.staging.provetrade.com|api.provetrade.com|localhost|api$' 25 | ###< symfony/framework-bundle ### 26 | 27 | ###> doctrine/doctrine-bundle ### 28 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 29 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 30 | # For a MySQL database, use: "mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7" 31 | # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml 32 | DATABASE_URL=mysql://api-platform:!ChangeMe!@db:3306/api?server_version=8 33 | ###< doctrine/doctrine-bundle ### 34 | 35 | ###> nelmio/cors-bundle ### 36 | CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$ 37 | ###< nelmio/cors-bundle ### 38 | 39 | ###> symfony/mercure-bundle ### 40 | # See https://symfony.com/doc/current/mercure.html#configuration 41 | MERCURE_PUBLISH_URL=https://mercure/.well-known/mercure 42 | # The default token is signed with the secret key: !ChangeMe! 43 | MERCURE_JWT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOltdfX0.Oo0yg7y4yMa1vr_bziltxuTCqb8JVHKxp-f_FwwOim0 44 | ###< symfony/mercure-bundle ### 45 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | /docker/db/data 2 | /helm/api/charts 3 | /helm/api/Chart.lock 4 | 5 | ###> symfony/framework-bundle ### 6 | /.env.local 7 | /.env.local.php 8 | /.env.*.local 9 | /config/secrets/prod/prod.decrypt.private.php 10 | /public/bundles/ 11 | /var/ 12 | /vendor/ 13 | ###< symfony/framework-bundle ### 14 | 15 | ###> friendsofphp/php-cs-fixer ### 16 | /.php_cs 17 | /.php_cs.cache 18 | ###< friendsofphp/php-cs-fixer ### 19 | -------------------------------------------------------------------------------- /api/.php_cs.dist: -------------------------------------------------------------------------------- 1 | in(__DIR__) 5 | ->exclude('var') 6 | ; 7 | 8 | return PhpCsFixer\Config::create() 9 | ->setRules([ 10 | '@Symfony' => true, 11 | 'array_syntax' => ['syntax' => 'short'], 12 | ]) 13 | ->setFinder($finder) 14 | ; 15 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | # the different stages of this Dockerfile are meant to be built into separate images 2 | # https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage 3 | # https://docs.docker.com/compose/compose-file/#target 4 | 5 | 6 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 7 | ARG PHP_VERSION=7.4 8 | ARG OPENRESTY_VERSION=1.15.8.3 9 | ARG VARNISH_VERSION=6.4 10 | 11 | 12 | # "php" stage 13 | FROM php:${PHP_VERSION}-fpm-alpine AS api_platform_php 14 | 15 | # persistent / runtime deps 16 | RUN apk add --no-cache \ 17 | acl \ 18 | fcgi \ 19 | file \ 20 | gettext \ 21 | git \ 22 | ; 23 | 24 | ARG APCU_VERSION=5.1.18 25 | RUN set -eux; \ 26 | apk add --no-cache --virtual .\build-deps \ 27 | $PHPIZE_DEPS \ 28 | icu-dev \ 29 | libzip-dev \ 30 | zip \ 31 | ; \ 32 | docker-php-ext-configure zip; \ 33 | docker-php-ext-install -j$(nproc) \ 34 | intl \ 35 | pdo_mysql \ 36 | zip \ 37 | ; \ 38 | runDeps="$( \ 39 | scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ 40 | | tr ',' '\n' \ 41 | | sort -u \ 42 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 43 | )"; \ 44 | apk add --no-cache --virtual .api-phpexts-rundeps $runDeps; \ 45 | \ 46 | apk del .build-deps 47 | 48 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 49 | 50 | RUN ln -s $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini 51 | COPY docker/php/conf.d/api-platform.prod.ini $PHP_INI_DIR/conf.d/api-platform.ini 52 | 53 | RUN set -eux; \ 54 | { \ 55 | echo '[www]'; \ 56 | echo 'ping.path = /ping'; \ 57 | } | tee /usr/local/etc/php-fpm.d/docker-healthcheck.conf 58 | 59 | # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser 60 | ENV COMPOSER_ALLOW_SUPERUSER=1 61 | # install Symfony Flex globally to speed up download of Composer packages (parallelized prefetching) 62 | RUN set -eux; \ 63 | composer global require "symfony/flex" --prefer-dist --no-progress --no-suggest --classmap-authoritative; \ 64 | composer clear-cache 65 | ENV PATH="${PATH}:/root/.composer/vendor/bin" 66 | 67 | WORKDIR /srv/api 68 | 69 | # build for production 70 | ARG APP_ENV=prod 71 | 72 | # prevent the reinstallation of vendors at every changes in the source code 73 | COPY composer.json composer.lock symfony.lock ./ 74 | RUN set -eux; \ 75 | composer install --prefer-dist --no-dev --no-scripts --no-progress --no-suggest; \ 76 | composer clear-cache 77 | 78 | # do not use .env files in production 79 | COPY .env ./ 80 | RUN composer dump-env prod; \ 81 | rm .env 82 | 83 | # copy only specifically what we need 84 | COPY bin bin/ 85 | COPY config config/ 86 | COPY public public/ 87 | COPY src src/ 88 | 89 | RUN set -eux; \ 90 | mkdir -p var/cache var/log; \ 91 | composer dump-autoload --classmap-authoritative --no-dev; \ 92 | composer run-script --no-dev post-install-cmd; \ 93 | chmod +x bin/console; sync 94 | VOLUME /srv/api/var 95 | 96 | COPY docker/php/docker-healthcheck.sh /usr/local/bin/docker-healthcheck 97 | RUN chmod +x /usr/local/bin/docker-healthcheck 98 | 99 | HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD ["docker-healthcheck"] 100 | 101 | COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint 102 | RUN chmod +x /usr/local/bin/docker-entrypoint 103 | 104 | ENTRYPOINT ["docker-entrypoint"] 105 | CMD ["php-fpm"] 106 | 107 | 108 | # "nginx" stage 109 | # depends on the "php" stage above 110 | # The OpenResty distribution of NGINX is only needed for Kubernetes compatiblity (dynamic upstream resolution) 111 | FROM openresty/openresty:${OPENRESTY_VERSION}-alpine AS api_platform_nginx 112 | 113 | RUN echo -e "env UPSTREAM;\n$(cat /usr/local/openresty/nginx/conf/nginx.conf)" > /usr/local/openresty/nginx/conf/nginx.conf 114 | COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf 115 | 116 | WORKDIR /srv/api/public 117 | 118 | COPY --from=api_platform_php /srv/api/public ./ 119 | 120 | 121 | # "varnish" stage 122 | # does not depend on any of the above stages, but placed here to keep everything in one Dockerfile 123 | FROM varnish:${VARNISH_VERSION} AS api_platform_varnish 124 | 125 | COPY docker/varnish/conf/default.vcl /etc/varnish/default.vcl 126 | 127 | CMD ["varnishd", "-F", "-f", "/etc/varnish/default.vcl", "-p", "http_resp_hdr_len=65536", "-p", "http_resp_size=98304"] 128 | -------------------------------------------------------------------------------- /api/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 23 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 24 | } 25 | 26 | if ($input->hasParameterOption('--no-debug', true)) { 27 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 28 | } 29 | 30 | require dirname(__DIR__).'/config/bootstrap.php'; 31 | 32 | if ($_SERVER['APP_DEBUG']) { 33 | umask(0000); 34 | 35 | if (class_exists(Debug::class)) { 36 | Debug::enable(); 37 | } 38 | } 39 | 40 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 41 | $application = new Application($kernel); 42 | $application->run($input); 43 | -------------------------------------------------------------------------------- /api/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "MIT", 3 | "require": { 4 | "php": "^7.2.5", 5 | "ext-ctype": "*", 6 | "ext-iconv": "*", 7 | "api-platform/api-pack": "^1.1", 8 | "doctrine/doctrine-migrations-bundle": "^2.0", 9 | "guzzlehttp/guzzle": "^6.3", 10 | "symfony/console": "5.0.*", 11 | "symfony/dotenv": "5.0.*", 12 | "symfony/flex": "^1.1", 13 | "symfony/framework-bundle": "5.0.*", 14 | "symfony/mercure-bundle": "^0.2", 15 | "symfony/yaml": "5.0.*" 16 | }, 17 | "require-dev": { 18 | "api-platform/schema-generator": "^2.1", 19 | "doctrine/doctrine-fixtures-bundle": "^3.3", 20 | "symfony/maker-bundle": "^1.11", 21 | "symfony/profiler-pack": "^1.0" 22 | }, 23 | "conflict": { 24 | "symfony/symfony": "*" 25 | }, 26 | "replace": { 27 | "paragonie/random_compat": "2.*", 28 | "symfony/polyfill-ctype": "*", 29 | "symfony/polyfill-iconv": "*", 30 | "symfony/polyfill-php56": "*", 31 | "symfony/polyfill-php70": "*", 32 | "symfony/polyfill-php71": "*" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "App\\": "src/" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "App\\Tests\\": "tests/" 42 | } 43 | }, 44 | "config": { 45 | "preferred-install": { 46 | "*": "dist" 47 | }, 48 | "sort-packages": true 49 | }, 50 | "scripts": { 51 | "auto-scripts": { 52 | "cache:clear": "symfony-cmd", 53 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 54 | }, 55 | "post-install-cmd": [ 56 | "@auto-scripts" 57 | ], 58 | "post-update-cmd": [ 59 | "@auto-scripts" 60 | ] 61 | }, 62 | "extra": { 63 | "symfony": { 64 | "allow-contrib": false, 65 | "require": "5.0.*" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /api/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 13 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) { 14 | (new Dotenv(false))->populate($env); 15 | } else { 16 | // load all the .env files 17 | (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env'); 18 | } 19 | 20 | $_SERVER += $_ENV; 21 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 22 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 23 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 24 | -------------------------------------------------------------------------------- /api/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 6 | Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true], 7 | Symfony\Bundle\TwigBundle\TwigBundle::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\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 12 | Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], 13 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 14 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], 15 | ]; 16 | -------------------------------------------------------------------------------- /api/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | title: Hello API Platform 3 | version: 1.0.0 4 | mapping: 5 | paths: ['%kernel.project_dir%/src/Entity'] 6 | patch_formats: 7 | json: ['application/merge-patch+json'] 8 | swagger: 9 | versions: [3] 10 | # Mercure integration, remove if unwanted 11 | mercure: 12 | hub_url: '%env(MERCURE_SUBSCRIBE_URL)%' 13 | -------------------------------------------------------------------------------- /api/config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | #pools: 19 | #my.dedicated.cache: null 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 | doctrine: 2 | dbal: 3 | url: '%env(resolve:DATABASE_URL)%' 4 | 5 | # IMPORTANT: You MUST configure your server version, 6 | # either here or in the DATABASE_URL env var (see .env file) 7 | server_version: '8' 8 | orm: 9 | auto_generate_proxy_classes: true 10 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 11 | auto_mapping: true 12 | mappings: 13 | App: 14 | is_bundle: false 15 | type: annotation 16 | dir: '%kernel.project_dir%/src/Entity' 17 | prefix: 'App\Entity' 18 | alias: App 19 | -------------------------------------------------------------------------------- /api/config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | dir_name: '%kernel.project_dir%/src/Migrations' 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | namespace: DoctrineMigrations 6 | -------------------------------------------------------------------------------- /api/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #csrf_protection: true 4 | #http_method_override: true 5 | 6 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 7 | # Remove or comment this section to explicitly disable session support. 8 | session: 9 | handler_id: null 10 | cookie_secure: auto 11 | cookie_samesite: lax 12 | 13 | #esi: true 14 | #fragments: true 15 | php_errors: 16 | log: true 17 | -------------------------------------------------------------------------------- /api/config/packages/mercure.yaml: -------------------------------------------------------------------------------- 1 | mercure: 2 | enable_profiler: '%kernel.debug%' 3 | hubs: 4 | default: 5 | url: '%env(MERCURE_PUBLISH_URL)%' 6 | jwt: '%env(MERCURE_JWT_TOKEN)%' 7 | -------------------------------------------------------------------------------- /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', 'Preload', 'Fields'] 7 | expose_headers: ['Link'] 8 | max_age: 3600 9 | paths: 10 | '^/': null 11 | -------------------------------------------------------------------------------- /api/config/packages/prod/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | # Varnish integration, remove if unwanted 3 | http_cache: 4 | invalidation: 5 | enabled: true 6 | varnish_urls: ['%env(VARNISH_URL)%'] 7 | max_age: 0 8 | shared_max_age: 3600 9 | vary: ['Content-Type', 'Authorization', 'Origin'] 10 | public: true 11 | -------------------------------------------------------------------------------- /api/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: pool 6 | pool: doctrine.system_cache_pool 7 | query_cache_driver: 8 | type: pool 9 | pool: doctrine.system_cache_pool 10 | result_cache_driver: 11 | type: pool 12 | pool: doctrine.result_cache_pool 13 | 14 | framework: 15 | cache: 16 | pools: 17 | doctrine.result_cache_pool: 18 | adapter: cache.app 19 | doctrine.system_cache_pool: 20 | adapter: cache.system 21 | -------------------------------------------------------------------------------- /api/config/packages/prod/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: null 4 | -------------------------------------------------------------------------------- /api/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | -------------------------------------------------------------------------------- /api/config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers 3 | providers: 4 | users_in_memory: { memory: null } 5 | firewalls: 6 | dev: 7 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 8 | security: false 9 | main: 10 | anonymous: lazy 11 | provider: users_in_memory 12 | 13 | # activate different ways to authenticate 14 | # https://symfony.com/doc/current/security.html#firewalls-authentication 15 | 16 | # https://symfony.com/doc/current/security/impersonating_user.html 17 | # switch_user: true 18 | 19 | # Easy way to control access for large sections of your site 20 | # Note: Only the *first* access control that matches will be used 21 | access_control: 22 | # - { path: ^/admin, roles: ROLE_ADMIN } 23 | # - { path: ^/profile, roles: ROLE_USER } 24 | -------------------------------------------------------------------------------- /api/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /api/config/packages/test/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /api/config/packages/test/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | strict_variables: true 3 | -------------------------------------------------------------------------------- /api/config/packages/test/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | not_compromised_password: false 4 | -------------------------------------------------------------------------------- /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/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | -------------------------------------------------------------------------------- /api/config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | 5 | # Enables validator auto-mapping support. 6 | # For instance, basic validation constraints will be inferred from Doctrine's metadata. 7 | #auto_mapping: 8 | # App\Entity\: [] 9 | -------------------------------------------------------------------------------- /api/config/routes.yaml: -------------------------------------------------------------------------------- 1 | #index: 2 | # path: / 3 | # controller: App\Controller\DefaultController::index 4 | -------------------------------------------------------------------------------- /api/config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | 5 | kernel: 6 | resource: ../../src/Kernel.php 7 | type: annotation 8 | -------------------------------------------------------------------------------- /api/config/routes/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | resource: . 3 | type: api_platform 4 | -------------------------------------------------------------------------------- /api/config/routes/dev/framework.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@FrameworkBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 4 | -------------------------------------------------------------------------------- /api/config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@FrameworkBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 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 | # This file is the entry point to configure your own services. 2 | # Files in the packages/ subdirectory configure your dependencies. 3 | 4 | # Put parameters here that don't need to change on each machine where the app is deployed 5 | # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration 6 | parameters: 7 | 8 | services: 9 | # default configuration for services in *this* file 10 | _defaults: 11 | autowire: true # Automatically injects dependencies in your services. 12 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 13 | 14 | # makes classes in src/ available to be used as services 15 | # this creates a service per class whose id is the fully-qualified class name 16 | App\: 17 | resource: '../src/*' 18 | exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' 19 | 20 | # controllers are imported separately to make sure services can be injected 21 | # as action arguments even if you don't extend any base controller class 22 | App\Controller\: 23 | resource: '../src/Controller' 24 | tags: ['controller.service_arguments'] 25 | 26 | # add more service definitions when explicit configuration is needed 27 | # please note that last definitions always *replace* previous ones 28 | -------------------------------------------------------------------------------- /api/docker/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /srv/api/public; 3 | 4 | location / { 5 | # try to serve file directly, fallback to index.php 6 | try_files $uri /index.php$is_args$args; 7 | } 8 | 9 | location ~ ^/index\.php(/|$) { 10 | set_by_lua $upstream_host 'return os.getenv("UPSTREAM") or "php:9000"'; 11 | fastcgi_pass $upstream_host; 12 | resolver local=on; 13 | 14 | # Increase the buffer size to handle large cache invalidation headers 15 | fastcgi_buffer_size 32k; 16 | fastcgi_buffers 32 4k; 17 | 18 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 19 | include fastcgi_params; 20 | 21 | # When you are using symlinks to link the document root to the 22 | # current version of your application, you should pass the real 23 | # application path instead of the path to the symlink to PHP 24 | # FPM. 25 | # Otherwise, PHP's OPcache may not properly detect changes to 26 | # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126 27 | # for more information). 28 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 29 | fastcgi_param DOCUMENT_ROOT $realpath_root; 30 | # Prevents URIs that include the front controller. This will 404: 31 | # http://domain.tld/index.php/some-path 32 | # Remove the internal directive to allow URIs like this 33 | internal; 34 | } 35 | 36 | # return 404 for all other php files not matching the front controller 37 | # this prevents access to other php files you don't want to be accessible. 38 | location ~ \.php$ { 39 | return 404; 40 | } 41 | 42 | # URL for health checks 43 | location /nginx-health { 44 | access_log off; 45 | default_type text/plain; 46 | return 200 "healthy\n"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api/docker/php/conf.d/api-platform.dev.ini: -------------------------------------------------------------------------------- 1 | apc.enable_cli = 1 2 | date.timezone = UTC 3 | session.auto_start = Off 4 | short_open_tag = Off 5 | 6 | # https://symfony.com/doc/current/performance.html 7 | opcache.interned_strings_buffer = 16 8 | opcache.max_accelerated_files = 20000 9 | opcache.memory_consumption = 256 10 | realpath_cache_size = 4096K 11 | realpath_cache_ttl = 600 12 | -------------------------------------------------------------------------------- /api/docker/php/conf.d/api-platform.prod.ini: -------------------------------------------------------------------------------- 1 | apc.enable_cli = 1 2 | date.timezone = UTC 3 | session.auto_start = Off 4 | short_open_tag = Off 5 | 6 | # https://symfony.com/doc/current/performance.html 7 | opcache.interned_strings_buffer = 16 8 | opcache.max_accelerated_files = 20000 9 | opcache.memory_consumption = 256 10 | opcache.validate_timestamps = 0 11 | realpath_cache_size = 4096K 12 | realpath_cache_ttl = 600 13 | -------------------------------------------------------------------------------- /api/docker/php/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | # first arg is `-f` or `--some-option` 5 | if [ "${1#-}" != "$1" ]; then 6 | set -- php-fpm "$@" 7 | fi 8 | 9 | if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then 10 | PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-production" 11 | if [ "$APP_ENV" != 'prod' ]; then 12 | PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-development" 13 | fi 14 | ln -sf "$PHP_INI_RECOMMENDED" "$PHP_INI_DIR/php.ini" 15 | 16 | mkdir -p var/cache var/log 17 | setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var 18 | setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var 19 | 20 | if [ "$APP_ENV" != 'prod' ] && [ -f /certs/localCA.crt ]; then 21 | ln -sf /certs/localCA.crt /usr/local/share/ca-certificates/localCA.crt 22 | update-ca-certificates 23 | fi 24 | 25 | if [ "$APP_ENV" != 'prod' ]; then 26 | composer install --prefer-dist --no-progress --no-suggest --no-interaction 27 | fi 28 | 29 | echo "Waiting for db to be ready..." 30 | until bin/console doctrine:query:sql "SELECT 1" > /dev/null 2>&1; do 31 | sleep 1 32 | done 33 | 34 | if ls -A src/Migrations/*.php > /dev/null 2>&1; then 35 | bin/console doctrine:migrations:migrate --no-interaction 36 | fi 37 | fi 38 | 39 | exec docker-php-entrypoint "$@" 40 | -------------------------------------------------------------------------------- /api/docker/php/docker-healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | export SCRIPT_NAME=/ping 5 | export SCRIPT_FILENAME=/ping 6 | export REQUEST_METHOD=GET 7 | 8 | if cgi-fcgi -bind -connect 127.0.0.1:9000; then 9 | exit 0 10 | fi 11 | 12 | exit 1 13 | -------------------------------------------------------------------------------- /api/docker/varnish/conf/default.vcl: -------------------------------------------------------------------------------- 1 | vcl 4.0; 2 | 3 | import std; 4 | 5 | backend default { 6 | .host = "api"; 7 | .port = "80"; 8 | # Health check 9 | #.probe = { 10 | # .url = "/"; 11 | # .timeout = 5s; 12 | # .interval = 10s; 13 | # .window = 5; 14 | # .threshold = 3; 15 | #} 16 | } 17 | 18 | # Hosts allowed to send BAN requests 19 | acl invalidators { 20 | "localhost"; 21 | "php"; 22 | # local Kubernetes network 23 | "10.0.0.0"/8; 24 | "172.16.0.0"/12; 25 | "192.168.0.0"/16; 26 | } 27 | 28 | sub vcl_recv { 29 | if (req.restarts > 0) { 30 | set req.hash_always_miss = true; 31 | } 32 | 33 | # Remove the "Forwarded" HTTP header if exists (security) 34 | unset req.http.forwarded; 35 | 36 | # To allow API Platform to ban by cache tags 37 | if (req.method == "BAN") { 38 | if (client.ip !~ invalidators) { 39 | return (synth(405, "Not allowed")); 40 | } 41 | 42 | if (req.http.ApiPlatform-Ban-Regex) { 43 | ban("obj.http.Cache-Tags ~ " + req.http.ApiPlatform-Ban-Regex); 44 | 45 | return (synth(200, "Ban added")); 46 | } 47 | 48 | return (synth(400, "ApiPlatform-Ban-Regex HTTP header must be set.")); 49 | } 50 | 51 | # For health checks 52 | if (req.method == "GET" && req.url == "/healthz") { 53 | return (synth(200, "OK")); 54 | } 55 | } 56 | 57 | sub vcl_hit { 58 | if (obj.ttl >= 0s) { 59 | # A pure unadulterated hit, deliver it 60 | return (deliver); 61 | } 62 | 63 | if (std.healthy(req.backend_hint)) { 64 | # The backend is healthy 65 | # Fetch the object from the backend 66 | return (restart); 67 | } 68 | 69 | # No fresh object and the backend is not healthy 70 | if (obj.ttl + obj.grace > 0s) { 71 | # Deliver graced object 72 | # Automatically triggers a background fetch 73 | return (deliver); 74 | } 75 | 76 | # No valid object to deliver 77 | # No healthy backend to handle request 78 | # Return error 79 | return (synth(503, "API is down")); 80 | } 81 | 82 | sub vcl_deliver { 83 | # Don't send cache tags related headers to the client 84 | unset resp.http.url; 85 | # Comment the following line to send the "Cache-Tags" header to the client (e.g. to use CloudFlare cache tags) 86 | unset resp.http.Cache-Tags; 87 | } 88 | 89 | sub vcl_backend_response { 90 | # Ban lurker friendly header 91 | set beresp.http.url = bereq.url; 92 | 93 | # Add a grace in case the backend is down 94 | set beresp.grace = 1h; 95 | } 96 | -------------------------------------------------------------------------------- /api/helm/api/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /api/helm/api/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | appVersion: 0.1.0 3 | description: A Helm chart for an API Platform API 4 | name: api 5 | version: 0.1.0 6 | home: https://api-platform.com 7 | icon: https://api-platform.com/logo-250x250.png 8 | dependencies: 9 | - name: postgresql 10 | version: ~8.6.0 11 | repository: https://charts.bitnami.com/bitnami 12 | condition: postgresql.enabled 13 | - name: mercure 14 | version: ~3.0.0 15 | repository: https://kubernetes-charts.storage.googleapis.com/ 16 | condition: mercure.enabled 17 | -------------------------------------------------------------------------------- /api/helm/api/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | {{- $serviceName := "varnish" -}} 2 | {{- $service := .Values.varnish.service -}} 3 | {{- if not .Values.varnish.enabled -}} 4 | {{- $serviceName = print (include "api.fullname" .) "-" "nginx" -}} 5 | {{- $service = .Values.nginx.service -}} 6 | {{- end -}} 7 | 1. Get the application URL by running these commands: 8 | {{- if .Values.ingress.enabled }} 9 | {{- range $host := .Values.ingress.hosts }} 10 | {{- range .paths }} 11 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 12 | {{- end }} 13 | {{- end }} 14 | {{- else if contains "NodePort" $service.type }} 15 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ $serviceName }}) 16 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 17 | echo http://$NODE_IP:$NODE_PORT 18 | {{- else if contains "LoadBalancer" $service.type }} 19 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 20 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "api.fullname" . }}' 21 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "api.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 22 | echo http://$SERVICE_IP:{{ $service.port }} 23 | {{- else if contains "ClusterIP" $service.type }} 24 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "api.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 25 | echo "Visit http://127.0.0.1:8080 to use your application" 26 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /api/helm/api/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "api.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "api.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "api.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "api.labels" -}} 38 | helm.sh/chart: {{ include "api.chart" . }} 39 | {{ include "api.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | app.kubernetes.io/part-of: {{ include "api.name" . }} 45 | {{- end -}} 46 | 47 | {{/* 48 | Selector labels 49 | */}} 50 | {{- define "api.selectorLabels" -}} 51 | app.kubernetes.io/name: {{ include "api.name" . }}{{- if .name -}}-{{- .name -}}{{- end }} 52 | app.kubernetes.io/instance: {{ .Release.Name }} 53 | {{- end -}} 54 | 55 | {{/* 56 | Create the name of the service account to use 57 | */}} 58 | {{- define "api.serviceAccountName" -}} 59 | {{- if .Values.serviceAccount.create -}} 60 | {{ default (include "api.fullname" .) .Values.serviceAccount.name }} 61 | {{- else -}} 62 | {{ default "default" .Values.serviceAccount.name }} 63 | {{- end -}} 64 | {{- end -}} 65 | -------------------------------------------------------------------------------- /api/helm/api/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "api.fullname" . }} 5 | labels: 6 | {{- include "api.labels" . | nindent 4 }} 7 | data: 8 | env: {{ .Values.php.env | quote }} 9 | debug: {{ .Values.php.debug | quote }} 10 | cors-allow-origin: {{ .Values.php.corsAllowOrigin | quote }} 11 | varnish-url: {{ if .Values.varnish.url }}{{ .Values.varnish.url | quote }}{{ else }}http://varnish{{ end }} 12 | trusted-hosts: {{ .Values.php.trustedHosts | quote }} 13 | trusted-proxies: {{ join "," .Values.php.trustedProxies }} 14 | mercure-publish-url: {{ .Values.mercure.publishUrl | quote }} 15 | mercure-subscribe-url: {{ .Values.mercure.subscribeUrl | quote }} 16 | -------------------------------------------------------------------------------- /api/helm/api/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "api.fullname" . -}} 3 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 4 | apiVersion: networking.k8s.io/v1beta1 5 | {{- else -}} 6 | apiVersion: extensions/v1beta1 7 | {{- end }} 8 | kind: Ingress 9 | metadata: 10 | name: {{ $fullName }} 11 | labels: 12 | {{- include "api.labels" . | nindent 4 }} 13 | {{- with .Values.ingress.annotations }} 14 | annotations: 15 | {{- toYaml . | nindent 4 }} 16 | {{- end }} 17 | spec: 18 | {{- if .Values.ingress.tls }} 19 | tls: 20 | {{- range .Values.ingress.tls }} 21 | - hosts: 22 | {{- range .hosts }} 23 | - {{ . | quote }} 24 | {{- end }} 25 | secretName: {{ .secretName }} 26 | {{- end }} 27 | {{- end }} 28 | rules: 29 | {{- range .Values.ingress.hosts }} 30 | - host: {{ .host | quote }} 31 | http: 32 | paths: 33 | {{- range .paths }} 34 | - path: {{ . }} 35 | backend: 36 | {{- if $.Values.varnish.enabled }} 37 | serviceName: {{ $fullName }}-varnish 38 | servicePort: {{ $.Values.varnish.service.port }} 39 | {{- else }} 40 | serviceName: {{ $fullName }}-nginx 41 | servicePort: {{ $.Values.nginx.service.port }} 42 | {{- end }} 43 | {{- end }} 44 | {{- end }} 45 | {{- end }} 46 | -------------------------------------------------------------------------------- /api/helm/api/templates/nginx-deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- $name := "nginx" -}} 2 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 3 | {{- $fullName := include "api.fullname" . -}} 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | name: {{ $fullName }}-{{ $name }} 8 | labels: 9 | {{- include "api.labels" $data | nindent 4 }} 10 | spec: 11 | replicas: {{ .Values.nginx.replicaCount }} 12 | selector: 13 | matchLabels: 14 | {{- include "api.selectorLabels" $data | nindent 6 }} 15 | template: 16 | metadata: 17 | labels: 18 | {{- include "api.selectorLabels" $data | nindent 8 }} 19 | spec: 20 | {{- with .Values.imagePullSecrets }} 21 | imagePullSecrets: 22 | {{- toYaml . | nindent 8 }} 23 | {{- end }} 24 | serviceAccountName: {{ include "api.serviceAccountName" . }} 25 | securityContext: 26 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 27 | containers: 28 | - name: {{ .Chart.Name }}-{{ $name }} 29 | securityContext: 30 | {{- toYaml .Values.securityContext | nindent 12 }} 31 | image: "{{ .Values.nginx.image.repository }}:{{ .Values.nginx.image.tag | default .Chart.AppVersion }}" 32 | imagePullPolicy: {{ .Values.nginx.image.pullPolicy }} 33 | env: 34 | - name: UPSTREAM 35 | value: "{{ $fullName }}-php.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.php.service.port }}" 36 | ports: 37 | - name: http 38 | containerPort: 80 39 | protocol: TCP 40 | livenessProbe: 41 | httpGet: 42 | path: /nginx-health 43 | port: http 44 | readinessProbe: 45 | httpGet: 46 | path: /nginx-health 47 | port: http 48 | resources: 49 | {{- toYaml .Values.resources | nindent 12 }} 50 | {{- with .Values.nodeSelector }} 51 | nodeSelector: 52 | {{- toYaml . | nindent 8 }} 53 | {{- end }} 54 | {{- with .Values.affinity }} 55 | affinity: 56 | {{- toYaml . | nindent 8 }} 57 | {{- end }} 58 | {{- with .Values.tolerations }} 59 | tolerations: 60 | {{- toYaml . | nindent 8 }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /api/helm/api/templates/nginx-service.yaml: -------------------------------------------------------------------------------- 1 | {{- $name := "nginx" -}} 2 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | # /!\ To be reachable by Varnish, the service name MUST be hardcoded. 7 | # /!\ To deploy several instances in the same namespace you MUST rename the service here and in api/docker/varnish/conf/default.vcl 8 | name: {{ if .Values.varnish.enabled }}api{{ else }}{{ include "api.fullname" . }}-{{ $name }}{{ end }} 9 | labels: 10 | {{- include "api.labels" $data | nindent 4 }} 11 | spec: 12 | type: {{ .Values.nginx.service.type }} 13 | ports: 14 | - port: {{ .Values.nginx.service.port }} 15 | targetPort: http 16 | protocol: TCP 17 | name: http 18 | selector: 19 | {{- include "api.selectorLabels" $data | nindent 4 }} 20 | -------------------------------------------------------------------------------- /api/helm/api/templates/php-deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "api.fullname" . -}} 2 | {{- $name := "php" -}} 3 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | name: {{ $fullName }}-{{ $name }} 8 | labels: 9 | {{- include "api.labels" $data | nindent 4 }} 10 | spec: 11 | replicas: {{ .Values.php.replicaCount }} 12 | selector: 13 | matchLabels: 14 | {{- include "api.selectorLabels" $data | nindent 6 }} 15 | template: 16 | metadata: 17 | labels: 18 | {{- include "api.selectorLabels" $data | nindent 8 }} 19 | spec: 20 | {{- with .Values.imagePullSecrets }} 21 | imagePullSecrets: 22 | {{- toYaml . | nindent 8 }} 23 | {{- end }} 24 | serviceAccountName: {{ include "api.serviceAccountName" . }} 25 | securityContext: 26 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 27 | initContainers: 28 | - name: init-php 29 | image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}" 30 | command: ['/bin/bash', '-c'] 31 | args: ['bin/console doctrine:migrations:migrate --no-interaction'] 32 | env: 33 | - name: APP_DEBUG 34 | valueFrom: 35 | configMapKeyRef: 36 | name: {{ $fullName }} 37 | key: debug 38 | - name: APP_ENV 39 | valueFrom: 40 | configMapKeyRef: 41 | name: {{ $fullName }} 42 | key: env 43 | - name: DATABASE_URL 44 | valueFrom: 45 | secretKeyRef: 46 | name: {{ $fullName }} 47 | key: database-url 48 | containers: 49 | - name: {{ .Chart.Name }}-{{ $name }} 50 | image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}" 51 | imagePullPolicy: {{ .Values.php.image.pullPolicy }} 52 | env: 53 | - name: TRUSTED_HOSTS 54 | valueFrom: 55 | configMapKeyRef: 56 | name: {{ $fullName }} 57 | key: trusted-hosts 58 | - name: TRUSTED_PROXIES 59 | valueFrom: 60 | configMapKeyRef: 61 | name: {{ $fullName }} 62 | key: trusted-proxies 63 | - name: APP_ENV 64 | valueFrom: 65 | configMapKeyRef: 66 | name: {{ $fullName }} 67 | key: env 68 | - name: APP_DEBUG 69 | valueFrom: 70 | configMapKeyRef: 71 | name: {{ $fullName }} 72 | key: debug 73 | - name: CORS_ALLOW_ORIGIN 74 | valueFrom: 75 | configMapKeyRef: 76 | name: {{ $fullName }} 77 | key: cors-allow-origin 78 | - name: VARNISH_URL 79 | valueFrom: 80 | configMapKeyRef: 81 | name: {{ $fullName }} 82 | key: varnish-url 83 | - name: APP_SECRET 84 | valueFrom: 85 | secretKeyRef: 86 | name: {{ $fullName }} 87 | key: secret 88 | - name: DATABASE_URL 89 | valueFrom: 90 | secretKeyRef: 91 | name: {{ $fullName }} 92 | key: database-url 93 | - name: MERCURE_PUBLISH_URL 94 | valueFrom: 95 | configMapKeyRef: 96 | name: {{ $fullName }} 97 | key: mercure-publish-url 98 | - name: MERCURE_SUBSCRIBE_URL 99 | valueFrom: 100 | configMapKeyRef: 101 | name: {{ $fullName }} 102 | key: mercure-subscribe-url 103 | - name: MERCURE_JWT_TOKEN 104 | valueFrom: 105 | secretKeyRef: 106 | name: {{ $fullName }} 107 | key: mercure-jwt-token 108 | ports: 109 | - containerPort: 9000 110 | readinessProbe: 111 | tcpSocket: 112 | port: 9000 113 | initialDelaySeconds: 120 114 | periodSeconds: 3 115 | livenessProbe: 116 | tcpSocket: 117 | port: 9000 118 | initialDelaySeconds: 120 119 | periodSeconds: 3 120 | resources: 121 | {{- toYaml .Values.resources | nindent 12 }} 122 | {{- with .Values.nodeSelector }} 123 | nodeSelector: 124 | {{- toYaml . | nindent 8 }} 125 | {{- end }} 126 | {{- with .Values.affinity }} 127 | affinity: 128 | {{- toYaml . | nindent 8 }} 129 | {{- end }} 130 | {{- with .Values.tolerations }} 131 | tolerations: 132 | {{- toYaml . | nindent 8 }} 133 | {{- end }} 134 | -------------------------------------------------------------------------------- /api/helm/api/templates/php-service.yaml: -------------------------------------------------------------------------------- 1 | {{- $name := "php" -}} 2 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: {{ include "api.fullname" . }}-{{ $name }} 7 | labels: 8 | {{- include "api.labels" $data | nindent 4 }} 9 | spec: 10 | type: {{ .Values.php.service.type }} 11 | ports: 12 | - port: {{ .Values.php.service.port }} 13 | selector: 14 | {{- include "api.selectorLabels" $data | nindent 4 }} 15 | -------------------------------------------------------------------------------- /api/helm/api/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ include "api.fullname" . }} 5 | labels: 6 | {{- include "api.labels" . | nindent 4 }} 7 | type: Opaque 8 | data: 9 | {{- if .Values.postgresql.enabled }} 10 | {{- $postgresqlFullName := include "postgresql.fullname" . }} 11 | database-url: {{ printf "pgsql://%s:%s@%s-postgresql/%s?serverVersion=12" .Values.postgresql.postgresqlUsername .Values.postgresql.postgresqlPassword $postgresqlFullName .Values.postgresql.postgresqlDatabase | b64enc | quote }} 12 | {{- else }} 13 | database-url: {{ .Values.postgresql.url | b64enc | quote }} 14 | {{- end }} 15 | secret: {{ .Values.php.secret | default (randAlphaNum 40) | b64enc | quote }} 16 | mercure-jwt-token: {{ .Values.php.mercure.jwtToken | b64enc | quote }} 17 | -------------------------------------------------------------------------------- /api/helm/api/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "api.serviceAccountName" . }} 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end -}} 13 | -------------------------------------------------------------------------------- /api/helm/api/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- $fullName := include "api.fullname" . -}} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: $fullName-test-connection 6 | labels: 7 | {{- include "api.labels" . | nindent 4 }} 8 | annotations: 9 | "helm.sh/hook": test-success 10 | spec: 11 | containers: 12 | - name: wget 13 | image: busybox 14 | command: ['wget'] 15 | args: ['{{ if .Values.varnish.enabled }}{{ $fullName }}-varnish:{{ .Values.varnish.service.port }}{{ else }}{{ $fullName }}-nginx:{{ .Values.nginx.service.port }}{{ end }}'] 16 | restartPolicy: Never 17 | -------------------------------------------------------------------------------- /api/helm/api/templates/varnish-deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.varnish.enabled -}} 2 | {{- $name := "varnish" -}} 3 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 4 | apiVersion: apps/v1 5 | kind: Deployment 6 | metadata: 7 | name: {{ include "api.fullname" . }}-{{ $name }} 8 | labels: 9 | {{- include "api.labels" $data | nindent 4 }} 10 | spec: 11 | replicas: {{ .Values.varnish.replicaCount }} 12 | selector: 13 | matchLabels: 14 | {{- include "api.selectorLabels" $data | nindent 6 }} 15 | template: 16 | metadata: 17 | labels: 18 | {{- include "api.selectorLabels" $data | nindent 8 }} 19 | spec: 20 | {{- with .Values.imagePullSecrets }} 21 | imagePullSecrets: 22 | {{- toYaml . | nindent 8 }} 23 | {{- end }} 24 | serviceAccountName: {{ include "api.serviceAccountName" . }} 25 | securityContext: 26 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 27 | containers: 28 | - name: {{ .Chart.Name }}-{{ $name }} 29 | image: "{{ .Values.varnish.image.repository }}:{{ .Values.varnish.image.tag }}" 30 | imagePullPolicy: {{ .Values.varnish.image.pullPolicy }} 31 | command: ["varnishd"] 32 | args: ["-F", "-f", "/etc/varnish/default.vcl", "-p", "http_resp_hdr_len=65536", "-p", "http_resp_size=98304"] 33 | ports: 34 | - containerPort: 80 35 | livenessProbe: 36 | httpGet: 37 | path: /healthz 38 | port: 80 39 | readinessProbe: 40 | httpGet: 41 | path: /healthz 42 | port: 80 43 | resources: 44 | {{ toYaml .Values.resources | indent 12 }} 45 | {{- if .Values.nodeSelector }} 46 | nodeSelector: 47 | {{ toYaml .Values.nodeSelector | indent 8 }} 48 | {{- end }} 49 | {{- end -}} 50 | -------------------------------------------------------------------------------- /api/helm/api/templates/varnish-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.varnish.enabled -}} 2 | {{- $name := "varnish" -}} 3 | {{- $data := dict "name" $name "Chart" .Chart "Release" .Release "Values" .Values -}} 4 | apiVersion: v1 5 | kind: Service 6 | metadata: 7 | name: {{ include "api.fullname" . }}-{{ $name }} 8 | labels: 9 | {{- include "api.labels" $data | nindent 4 }} 10 | spec: 11 | type: {{ .Values.varnish.service.type }} 12 | ports: 13 | - port: {{ .Values.varnish.service.port }} 14 | targetPort: http 15 | protocol: TCP 16 | name: http 17 | selector: 18 | {{- include "api.selectorLabels" $data | nindent 4 }} 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /api/helm/api/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for api. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | php: 6 | image: 7 | repository: quay.io/api-platform/php 8 | tag: latest 9 | pullPolicy: Always 10 | replicaCount: 1 11 | mercure: 12 | jwtToken: "" 13 | env: prod 14 | debug: '0' 15 | secret: "" 16 | corsAllowOrigin: "^https?://.*?\\.example\\.com$" 17 | trustedHosts: "^.*\\.example\\.com$" 18 | trustedProxies: 19 | - 10.0.0.0/8 20 | - 172.16.0.0/12 21 | - 192.168.0.0/16 22 | service: 23 | type: ClusterIP 24 | port: 9000 25 | 26 | nginx: 27 | image: 28 | repository: quay.io/api-platform/nginx 29 | tag: latest 30 | pullPolicy: Always 31 | replicaCount: 1 32 | service: 33 | type: ClusterIP 34 | port: 80 35 | 36 | varnish: 37 | enabled: false 38 | #url: https://example.com 39 | image: 40 | repository: quay.io/api-platform/varnish 41 | tag: latest 42 | pullPolicy: Always 43 | replicaCount: 1 44 | service: 45 | type: ClusterIP 46 | port: 80 47 | 48 | # Full configuration: https://github.com/bitnami/charts/tree/master/bitnami/postgresql 49 | postgresql: 50 | enabled: true 51 | imageTag: 12-alpine 52 | # If bringing your own PostgreSQL, the full uri to use 53 | url: pgsql://api-platform:!ChangeMe!@example.com/api?serverVersion=10.1 54 | postgresqlUsername: "example" 55 | postgresqlPassword: "!ChangeMe!" 56 | postgresqlDatabase: "api" 57 | # Persistent Volume Storage configuration. 58 | # ref: https://kubernetes.io/docs/user-guide/persistent-volumes 59 | persistence: 60 | enabled: false 61 | pullPolicy: Always 62 | 63 | # Full configuration: https://github.com/helm/charts/tree/master/stable/mercure 64 | mercure: 65 | enabled: true 66 | publishUrl: http://mercure/.well-known/mercure 67 | subscribeUrl: https://mercure.example.com/.well-known/mercure 68 | allowAnonymous: "1" 69 | corsAllowedOrigins: "^https?://.*?\\.example\\.com$" 70 | jwtKey: "" 71 | ingress: 72 | enabled: false 73 | hosts: 74 | - host: mercure.example.com 75 | paths: [] 76 | tls: [] 77 | # - secretName: mercure-example-tls 78 | # hosts: 79 | # - chart-example.local 80 | 81 | imagePullSecrets: [] 82 | nameOverride: "" 83 | fullnameOverride: "" 84 | 85 | serviceAccount: 86 | # Specifies whether a service account should be created 87 | create: true 88 | # Annotations to add to the service account 89 | annotations: {} 90 | # The name of the service account to use. 91 | # If not set and create is true, a name is generated using the fullname template 92 | name: 93 | 94 | podSecurityContext: {} 95 | # fsGroup: 2000 96 | 97 | securityContext: {} 98 | # capabilities: 99 | # drop: 100 | # - ALL 101 | # readOnlyRootFilesystem: true 102 | # runAsNonRoot: true 103 | # runAsUser: 1000 104 | 105 | ingress: 106 | enabled: false 107 | annotations: {} 108 | # kubernetes.io/ingress.class: nginx 109 | # kubernetes.io/tls-acme: "true" 110 | hosts: 111 | - host: api.example.com 112 | paths: [] 113 | tls: [] 114 | # - secretName: api-example-tls 115 | # hosts: 116 | # - api.example.com 117 | 118 | resources: {} 119 | # We usually recommend not to specify default resources and to leave this as a conscious 120 | # choice for the user. This also increases chances charts run on environments with little 121 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 122 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 123 | # limits: 124 | # cpu: 100m 125 | # memory: 128Mi 126 | # requests: 127 | # cpu: 100m 128 | # memory: 128Mi 129 | 130 | nodeSelector: {} 131 | 132 | tolerations: [] 133 | 134 | affinity: {} 135 | -------------------------------------------------------------------------------- /api/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/api/public/favicon.ico -------------------------------------------------------------------------------- /api/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /api/src/Controller/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/api/src/Controller/.gitignore -------------------------------------------------------------------------------- /api/src/DataFixtures/AppFixtures.php: -------------------------------------------------------------------------------- 1 | persist($product); 14 | 15 | $manager->flush(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /api/src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/api/src/Entity/.gitignore -------------------------------------------------------------------------------- /api/src/Entity/Greeting.php: -------------------------------------------------------------------------------- 1 | id; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /api/src/Entity/ProjectAuthor.php: -------------------------------------------------------------------------------- 1 | id; 35 | } 36 | 37 | public function getName(): ?string 38 | { 39 | return $this->name; 40 | } 41 | 42 | public function setName(string $name): self 43 | { 44 | $this->name = $name; 45 | 46 | return $this; 47 | } 48 | 49 | public function getEmail(): ?string 50 | { 51 | return $this->email; 52 | } 53 | 54 | public function setEmail(string $email): self 55 | { 56 | $this->email = $email; 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /api/src/Kernel.php: -------------------------------------------------------------------------------- 1 | getProjectDir().'/config/bundles.php'; 21 | foreach ($contents as $class => $envs) { 22 | if ($envs[$this->environment] ?? $envs['all'] ?? false) { 23 | yield new $class(); 24 | } 25 | } 26 | } 27 | 28 | public function getProjectDir(): string 29 | { 30 | return \dirname(__DIR__); 31 | } 32 | 33 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void 34 | { 35 | $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); 36 | $container->setParameter('container.dumper.inline_class_loader', \PHP_VERSION_ID < 70400 || $this->debug); 37 | $container->setParameter('container.dumper.inline_factories', true); 38 | $confDir = $this->getProjectDir().'/config'; 39 | 40 | $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); 41 | $loader->load($confDir.'/{packages}/'.$this->environment.'/*'.self::CONFIG_EXTS, 'glob'); 42 | $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); 43 | $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); 44 | } 45 | 46 | protected function configureRoutes(RouteCollectionBuilder $routes): void 47 | { 48 | $confDir = $this->getProjectDir().'/config'; 49 | 50 | $routes->import($confDir.'/{routes}/'.$this->environment.'/*'.self::CONFIG_EXTS, '/', 'glob'); 51 | $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); 52 | $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /api/src/Migrations/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/api/src/Migrations/.gitignore -------------------------------------------------------------------------------- /api/src/Migrations/Version20200830163015.php: -------------------------------------------------------------------------------- 1 | abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 24 | 25 | $this->addSql('CREATE TABLE project_author (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); 26 | $this->addSql('CREATE TABLE greeting (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); 27 | } 28 | 29 | public function down(Schema $schema) : void 30 | { 31 | // this down() migration is auto-generated, please modify it to your needs 32 | $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); 33 | 34 | $this->addSql('DROP TABLE project_author'); 35 | $this->addSql('DROP TABLE greeting'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /api/src/Repository/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/api/src/Repository/.gitignore -------------------------------------------------------------------------------- /api/src/Repository/ProjectAuthorRepository.php: -------------------------------------------------------------------------------- 1 | createQueryBuilder('p') 29 | ->andWhere('p.exampleField = :val') 30 | ->setParameter('val', $value) 31 | ->orderBy('p.id', 'ASC') 32 | ->setMaxResults(10) 33 | ->getQuery() 34 | ->getResult() 35 | ; 36 | } 37 | */ 38 | 39 | /* 40 | public function findOneBySomeField($value): ?ProjectAuthor 41 | { 42 | return $this->createQueryBuilder('p') 43 | ->andWhere('p.exampleField = :val') 44 | ->setParameter('val', $value) 45 | ->getQuery() 46 | ->getOneOrNullResult() 47 | ; 48 | } 49 | */ 50 | } 51 | -------------------------------------------------------------------------------- /api/symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "api-platform/api-pack": { 3 | "version": "v1.2.2" 4 | }, 5 | "api-platform/core": { 6 | "version": "2.5", 7 | "recipe": { 8 | "repo": "github.com/symfony/recipes", 9 | "branch": "master", 10 | "version": "2.5", 11 | "ref": "a93061567140e386f107be75340ac2aee3f86cbf" 12 | }, 13 | "files": [ 14 | "config/packages/api_platform.yaml", 15 | "config/routes/api_platform.yaml", 16 | "src/Entity/.gitignore" 17 | ] 18 | }, 19 | "api-platform/schema-generator": { 20 | "version": "v2.2.2" 21 | }, 22 | "composer/semver": { 23 | "version": "1.5.1" 24 | }, 25 | "composer/xdebug-handler": { 26 | "version": "1.4.1" 27 | }, 28 | "doctrine/annotations": { 29 | "version": "1.0", 30 | "recipe": { 31 | "repo": "github.com/symfony/recipes", 32 | "branch": "master", 33 | "version": "1.0", 34 | "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457" 35 | }, 36 | "files": [ 37 | "config/routes/annotations.yaml" 38 | ] 39 | }, 40 | "doctrine/cache": { 41 | "version": "1.10.1" 42 | }, 43 | "doctrine/collections": { 44 | "version": "1.6.5" 45 | }, 46 | "doctrine/common": { 47 | "version": "2.13.1" 48 | }, 49 | "doctrine/data-fixtures": { 50 | "version": "1.4.3" 51 | }, 52 | "doctrine/dbal": { 53 | "version": "2.10.2" 54 | }, 55 | "doctrine/doctrine-bundle": { 56 | "version": "2.0", 57 | "recipe": { 58 | "repo": "github.com/symfony/recipes", 59 | "branch": "master", 60 | "version": "2.0", 61 | "ref": "a9f2463b9f73efe74482f831f03a204a41328555" 62 | }, 63 | "files": [ 64 | "config/packages/doctrine.yaml", 65 | "config/packages/prod/doctrine.yaml", 66 | "src/Entity/.gitignore", 67 | "src/Repository/.gitignore" 68 | ] 69 | }, 70 | "doctrine/doctrine-cache-bundle": { 71 | "version": "1.3.5" 72 | }, 73 | "doctrine/doctrine-fixtures-bundle": { 74 | "version": "3.0", 75 | "recipe": { 76 | "repo": "github.com/symfony/recipes", 77 | "branch": "master", 78 | "version": "3.0", 79 | "ref": "e5b542d4ef47d8a003c91beb35650c76907f7e53" 80 | }, 81 | "files": [ 82 | "src/DataFixtures/AppFixtures.php" 83 | ] 84 | }, 85 | "doctrine/doctrine-migrations-bundle": { 86 | "version": "1.2", 87 | "recipe": { 88 | "repo": "github.com/symfony/recipes", 89 | "branch": "master", 90 | "version": "1.2", 91 | "ref": "c1431086fec31f17fbcfe6d6d7e92059458facc1" 92 | }, 93 | "files": [ 94 | "config/packages/doctrine_migrations.yaml", 95 | "src/Migrations/.gitignore" 96 | ] 97 | }, 98 | "doctrine/event-manager": { 99 | "version": "1.1.0" 100 | }, 101 | "doctrine/inflector": { 102 | "version": "1.4.2" 103 | }, 104 | "doctrine/instantiator": { 105 | "version": "1.3.0" 106 | }, 107 | "doctrine/lexer": { 108 | "version": "1.2.1" 109 | }, 110 | "doctrine/migrations": { 111 | "version": "2.2.1" 112 | }, 113 | "doctrine/orm": { 114 | "version": "v2.7.3" 115 | }, 116 | "doctrine/persistence": { 117 | "version": "1.3.7" 118 | }, 119 | "doctrine/reflection": { 120 | "version": "1.2.1" 121 | }, 122 | "doctrine/sql-formatter": { 123 | "version": "1.0.1" 124 | }, 125 | "easyrdf/easyrdf": { 126 | "version": "0.9.1" 127 | }, 128 | "fig/link-util": { 129 | "version": "1.1.1" 130 | }, 131 | "friendsofphp/php-cs-fixer": { 132 | "version": "2.2", 133 | "recipe": { 134 | "repo": "github.com/symfony/recipes", 135 | "branch": "master", 136 | "version": "2.2", 137 | "ref": "cc05ab6abf6894bddb9bbd6a252459010ebe040b" 138 | }, 139 | "files": [ 140 | ".php_cs.dist" 141 | ] 142 | }, 143 | "guzzlehttp/guzzle": { 144 | "version": "6.5.4" 145 | }, 146 | "guzzlehttp/promises": { 147 | "version": "v1.3.1" 148 | }, 149 | "guzzlehttp/psr7": { 150 | "version": "1.6.1" 151 | }, 152 | "laminas/laminas-code": { 153 | "version": "3.4.1" 154 | }, 155 | "laminas/laminas-eventmanager": { 156 | "version": "3.2.1" 157 | }, 158 | "laminas/laminas-zendframework-bridge": { 159 | "version": "1.0.4" 160 | }, 161 | "league/html-to-markdown": { 162 | "version": "4.9.1" 163 | }, 164 | "nelmio/cors-bundle": { 165 | "version": "1.5", 166 | "recipe": { 167 | "repo": "github.com/symfony/recipes", 168 | "branch": "master", 169 | "version": "1.5", 170 | "ref": "6388de23860284db9acce0a7a5d9d13153bcb571" 171 | }, 172 | "files": [ 173 | "config/packages/nelmio_cors.yaml" 174 | ] 175 | }, 176 | "nikic/php-parser": { 177 | "version": "v4.4.0" 178 | }, 179 | "ocramius/package-versions": { 180 | "version": "1.8.0" 181 | }, 182 | "ocramius/proxy-manager": { 183 | "version": "2.8.0" 184 | }, 185 | "php": { 186 | "version": "7.4" 187 | }, 188 | "php-cs-fixer/diff": { 189 | "version": "v1.3.0" 190 | }, 191 | "phpdocumentor/reflection-common": { 192 | "version": "2.1.0" 193 | }, 194 | "phpdocumentor/reflection-docblock": { 195 | "version": "5.1.0" 196 | }, 197 | "phpdocumentor/type-resolver": { 198 | "version": "1.1.0" 199 | }, 200 | "psr/cache": { 201 | "version": "1.0.1" 202 | }, 203 | "psr/container": { 204 | "version": "1.0.0" 205 | }, 206 | "psr/event-dispatcher": { 207 | "version": "1.0.0" 208 | }, 209 | "psr/http-message": { 210 | "version": "1.0.1" 211 | }, 212 | "psr/link": { 213 | "version": "1.0.0" 214 | }, 215 | "psr/log": { 216 | "version": "1.1.3" 217 | }, 218 | "ralouphie/getallheaders": { 219 | "version": "3.0.3" 220 | }, 221 | "symfony/asset": { 222 | "version": "v5.0.8" 223 | }, 224 | "symfony/cache": { 225 | "version": "v5.0.8" 226 | }, 227 | "symfony/cache-contracts": { 228 | "version": "v2.1.2" 229 | }, 230 | "symfony/config": { 231 | "version": "v5.0.8" 232 | }, 233 | "symfony/console": { 234 | "version": "4.4", 235 | "recipe": { 236 | "repo": "github.com/symfony/recipes", 237 | "branch": "master", 238 | "version": "4.4", 239 | "ref": "ea8c0eda34fda57e7d5cd8cbd889e2a387e3472c" 240 | }, 241 | "files": [ 242 | "bin/console", 243 | "config/bootstrap.php" 244 | ] 245 | }, 246 | "symfony/dependency-injection": { 247 | "version": "v5.0.8" 248 | }, 249 | "symfony/doctrine-bridge": { 250 | "version": "v5.0.8" 251 | }, 252 | "symfony/dotenv": { 253 | "version": "v5.0.8" 254 | }, 255 | "symfony/error-handler": { 256 | "version": "v5.0.8" 257 | }, 258 | "symfony/event-dispatcher": { 259 | "version": "v5.0.8" 260 | }, 261 | "symfony/event-dispatcher-contracts": { 262 | "version": "v2.1.2" 263 | }, 264 | "symfony/expression-language": { 265 | "version": "v5.0.8" 266 | }, 267 | "symfony/filesystem": { 268 | "version": "v5.0.8" 269 | }, 270 | "symfony/finder": { 271 | "version": "v5.0.8" 272 | }, 273 | "symfony/flex": { 274 | "version": "1.0", 275 | "recipe": { 276 | "repo": "github.com/symfony/recipes", 277 | "branch": "master", 278 | "version": "1.0", 279 | "ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e" 280 | }, 281 | "files": [ 282 | ".env" 283 | ] 284 | }, 285 | "symfony/framework-bundle": { 286 | "version": "4.4", 287 | "recipe": { 288 | "repo": "github.com/symfony/recipes", 289 | "branch": "master", 290 | "version": "4.4", 291 | "ref": "36d3075b2b8e0c4de0e82356a86e4c4a4eb6681b" 292 | }, 293 | "files": [ 294 | "config/bootstrap.php", 295 | "config/packages/cache.yaml", 296 | "config/packages/framework.yaml", 297 | "config/packages/test/framework.yaml", 298 | "config/routes/dev/framework.yaml", 299 | "config/services.yaml", 300 | "public/index.php", 301 | "src/Controller/.gitignore", 302 | "src/Kernel.php" 303 | ] 304 | }, 305 | "symfony/http-client": { 306 | "version": "v5.0.8" 307 | }, 308 | "symfony/http-client-contracts": { 309 | "version": "v2.1.2" 310 | }, 311 | "symfony/http-foundation": { 312 | "version": "v5.0.8" 313 | }, 314 | "symfony/http-kernel": { 315 | "version": "v5.0.8" 316 | }, 317 | "symfony/inflector": { 318 | "version": "v5.0.8" 319 | }, 320 | "symfony/maker-bundle": { 321 | "version": "1.0", 322 | "recipe": { 323 | "repo": "github.com/symfony/recipes", 324 | "branch": "master", 325 | "version": "1.0", 326 | "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" 327 | } 328 | }, 329 | "symfony/mercure": { 330 | "version": "v0.4.0" 331 | }, 332 | "symfony/mercure-bundle": { 333 | "version": "0.2", 334 | "recipe": { 335 | "repo": "github.com/symfony/recipes", 336 | "branch": "master", 337 | "version": "0.2", 338 | "ref": "a37d3438e4bd4f9f924c516996bc29e25f66b07e" 339 | }, 340 | "files": [ 341 | "config/packages/mercure.yaml" 342 | ] 343 | }, 344 | "symfony/mime": { 345 | "version": "v5.0.8" 346 | }, 347 | "symfony/options-resolver": { 348 | "version": "v5.0.8" 349 | }, 350 | "symfony/polyfill-intl-idn": { 351 | "version": "v1.17.0" 352 | }, 353 | "symfony/polyfill-mbstring": { 354 | "version": "v1.17.0" 355 | }, 356 | "symfony/polyfill-php72": { 357 | "version": "v1.17.0" 358 | }, 359 | "symfony/polyfill-php73": { 360 | "version": "v1.17.0" 361 | }, 362 | "symfony/process": { 363 | "version": "v5.0.8" 364 | }, 365 | "symfony/profiler-pack": { 366 | "version": "v1.0.4" 367 | }, 368 | "symfony/property-access": { 369 | "version": "v5.0.8" 370 | }, 371 | "symfony/property-info": { 372 | "version": "v5.0.8" 373 | }, 374 | "symfony/routing": { 375 | "version": "4.2", 376 | "recipe": { 377 | "repo": "github.com/symfony/recipes", 378 | "branch": "master", 379 | "version": "4.2", 380 | "ref": "683dcb08707ba8d41b7e34adb0344bfd68d248a7" 381 | }, 382 | "files": [ 383 | "config/packages/prod/routing.yaml", 384 | "config/packages/routing.yaml", 385 | "config/routes.yaml" 386 | ] 387 | }, 388 | "symfony/security-bundle": { 389 | "version": "4.4", 390 | "recipe": { 391 | "repo": "github.com/symfony/recipes", 392 | "branch": "master", 393 | "version": "4.4", 394 | "ref": "7b4408dc203049666fe23fabed23cbadc6d8440f" 395 | }, 396 | "files": [ 397 | "config/packages/security.yaml" 398 | ] 399 | }, 400 | "symfony/security-core": { 401 | "version": "v5.0.8" 402 | }, 403 | "symfony/security-csrf": { 404 | "version": "v5.0.8" 405 | }, 406 | "symfony/security-guard": { 407 | "version": "v5.0.8" 408 | }, 409 | "symfony/security-http": { 410 | "version": "v5.0.8" 411 | }, 412 | "symfony/serializer": { 413 | "version": "v5.0.8" 414 | }, 415 | "symfony/service-contracts": { 416 | "version": "v2.1.2" 417 | }, 418 | "symfony/stopwatch": { 419 | "version": "v5.0.8" 420 | }, 421 | "symfony/translation-contracts": { 422 | "version": "v2.1.2" 423 | }, 424 | "symfony/twig-bridge": { 425 | "version": "v5.0.8" 426 | }, 427 | "symfony/twig-bundle": { 428 | "version": "5.0", 429 | "recipe": { 430 | "repo": "github.com/symfony/recipes", 431 | "branch": "master", 432 | "version": "5.0", 433 | "ref": "fab9149bbaa4d5eca054ed93f9e1b66cc500895d" 434 | }, 435 | "files": [ 436 | "config/packages/test/twig.yaml", 437 | "config/packages/twig.yaml", 438 | "templates/base.html.twig" 439 | ] 440 | }, 441 | "symfony/validator": { 442 | "version": "4.3", 443 | "recipe": { 444 | "repo": "github.com/symfony/recipes", 445 | "branch": "master", 446 | "version": "4.3", 447 | "ref": "d902da3e4952f18d3bf05aab29512eb61cabd869" 448 | }, 449 | "files": [ 450 | "config/packages/test/validator.yaml", 451 | "config/packages/validator.yaml" 452 | ] 453 | }, 454 | "symfony/var-dumper": { 455 | "version": "v5.0.8" 456 | }, 457 | "symfony/var-exporter": { 458 | "version": "v5.0.8" 459 | }, 460 | "symfony/web-link": { 461 | "version": "v5.0.8" 462 | }, 463 | "symfony/web-profiler-bundle": { 464 | "version": "3.3", 465 | "recipe": { 466 | "repo": "github.com/symfony/recipes", 467 | "branch": "master", 468 | "version": "3.3", 469 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6" 470 | }, 471 | "files": [ 472 | "config/packages/dev/web_profiler.yaml", 473 | "config/packages/test/web_profiler.yaml", 474 | "config/routes/dev/web_profiler.yaml" 475 | ] 476 | }, 477 | "symfony/yaml": { 478 | "version": "v5.0.8" 479 | }, 480 | "twig/twig": { 481 | "version": "v3.0.3" 482 | }, 483 | "webimpress/safe-writer": { 484 | "version": "2.0.1" 485 | }, 486 | "webmozart/assert": { 487 | "version": "1.8.0" 488 | }, 489 | "willdurand/negotiation": { 490 | "version": "v2.3.1" 491 | }, 492 | "zendframework/zend-code": { 493 | "version": "3.4.1" 494 | }, 495 | "zendframework/zend-eventmanager": { 496 | "version": "3.2.1" 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /api/templates/base.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | {% block stylesheets %}{% endblock %} 7 | 8 | 9 | {% block body %}{% endblock %} 10 | {% block javascripts %}{% endblock %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/._* 4 | **/.dockerignore 5 | **/.DS_Store 6 | **/.git/ 7 | **/.gitattributes 8 | **/.gitignore 9 | **/.gitmodules 10 | **/docker-compose.*.yaml 11 | **/docker-compose.*.yml 12 | **/docker-compose.yaml 13 | **/docker-compose.yml 14 | **/Dockerfile 15 | **/Thumbs.db 16 | .editorconfig 17 | .env.*.local 18 | .env.local 19 | build/ 20 | node_modules/ 21 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_ENTRYPOINT=https://localhost:8443 2 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | # https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage 2 | # https://docs.docker.com/compose/compose-file/#target 3 | 4 | 5 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 6 | ARG NODE_VERSION=13 7 | ARG NGINX_VERSION=1.17 8 | 9 | 10 | # "development" stage 11 | FROM node:${NODE_VERSION}-alpine AS api_platform_client_development 12 | 13 | WORKDIR /usr/src/client 14 | 15 | RUN yarn global add @api-platform/client-generator 16 | 17 | # prevent the reinstallation of node modules at every changes in the source code 18 | COPY package.json yarn.lock ./ 19 | RUN set -eux; \ 20 | yarn install 21 | 22 | COPY . ./ 23 | 24 | VOLUME /usr/src/client/node_modules 25 | 26 | ENV HTTPS true 27 | 28 | CMD ["yarn", "start"] 29 | 30 | 31 | # "build" stage 32 | # depends on the "development" stage above 33 | FROM api_platform_client_development AS api_platform_client_build 34 | 35 | ARG REACT_APP_API_ENTRYPOINT 36 | 37 | RUN set -eux; \ 38 | yarn build 39 | 40 | 41 | # "nginx" stage 42 | # depends on the "build" stage above 43 | FROM nginx:${NGINX_VERSION}-alpine AS api_platform_client_nginx 44 | 45 | COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf 46 | 47 | WORKDIR /usr/src/client/build 48 | 49 | COPY --from=api_platform_client_build /usr/src/client/build ./ 50 | -------------------------------------------------------------------------------- /client/docker/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | root /usr/src/client/build; 3 | 4 | location / { 5 | try_files $uri /index.html; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-free": "^5.12.0", 7 | "@testing-library/jest-dom": "^4.2.4", 8 | "@testing-library/react": "^9.3.2", 9 | "@testing-library/user-event": "^7.1.2", 10 | "bootstrap": "^4.4.0", 11 | "connected-react-router": "^6.7.0", 12 | "lodash.get": "^4.4.2", 13 | "lodash.has": "^4.5.2", 14 | "lodash.mapvalues": "^4.6.0", 15 | "prop-types": "^15.6.2", 16 | "react": "^16.12.0", 17 | "react-dom": "^16.12.0", 18 | "react-redux": "^7.2.0", 19 | "react-router-dom": "^5.1.2", 20 | "react-scripts": "^3.4.0", 21 | "redux": "^4.0.1", 22 | "redux-form": "^8.3.0", 23 | "redux-thunk": "^2.3.0" 24 | }, 25 | "scripts": { 26 | "start": "react-scripts start", 27 | "build": "react-scripts build", 28 | "test": "react-scripts test", 29 | "eject": "react-scripts eject" 30 | }, 31 | "eslintConfig": { 32 | "extends": "react-app" 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | API Platform 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/client/public/logo512.png -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "API Platform App", 3 | "name": "API Platform App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/Welcome.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './Welcome'; 4 | 5 | test('renders API Platform title', () => { 6 | const { getByText } = render(); 7 | const strongElement = getByText(/API Platform/i); 8 | expect(strongElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /client/src/config/entrypoint.js: -------------------------------------------------------------------------------- 1 | export const ENTRYPOINT = process.env.REACT_APP_API_ENTRYPOINT; 2 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { createStore, combineReducers, applyMiddleware } from 'redux'; 4 | import { Provider } from 'react-redux'; 5 | import thunk from 'redux-thunk'; 6 | import { reducer as form } from 'redux-form'; 7 | import { Route, Switch } from 'react-router-dom'; 8 | import { createBrowserHistory } from 'history'; 9 | import { 10 | ConnectedRouter, 11 | connectRouter, 12 | routerMiddleware 13 | } from 'connected-react-router'; 14 | import 'bootstrap/dist/css/bootstrap.css'; 15 | import '@fortawesome/fontawesome-free/css/all.css'; 16 | import * as serviceWorker from './serviceWorker'; 17 | // Import your reducers and routes here 18 | import Welcome from './Welcome'; 19 | 20 | const history = createBrowserHistory(); 21 | const store = createStore( 22 | combineReducers({ 23 | router: connectRouter(history), 24 | form, 25 | /* Add your reducers here */ 26 | }), 27 | applyMiddleware(routerMiddleware(history), thunk) 28 | ); 29 | 30 | ReactDOM.render( 31 | 32 | 33 | 34 | 35 | {/* Add your routes here */} 36 |

Not Found

} /> 37 |
38 |
39 |
, 40 | document.getElementById('root') 41 | ); 42 | 43 | // If you want your app to work offline and load faster, you can change 44 | // unregister() to register() below. Note this comes with some pitfalls. 45 | // Learn more about service workers: https://bit.ly/CRA-PWA 46 | serviceWorker.unregister(); 47 | -------------------------------------------------------------------------------- /client/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' } 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /client/src/welcome.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700|Roboto+Slab:300,700'); 2 | 3 | body { 4 | margin: 0; 5 | } 6 | 7 | /***** GLOBAL *****/ 8 | 9 | .welcome { 10 | height: 100vh; 11 | width: 100vw; 12 | text-align: center; 13 | color: #1d1e1c; 14 | font-family: 'Open Sans', sans-serif; 15 | font-size: 14px; 16 | overflow: auto; 17 | background-color: #ececec; 18 | } 19 | 20 | .welcome a { 21 | text-decoration: none; 22 | color: #38a9b4; 23 | font-weight: bold; 24 | } 25 | 26 | .welcome h1 { 27 | font-family: 'Roboto Slab', serif; 28 | font-weight: 300; 29 | font-size: 36px; 30 | margin: 0 0 10px; 31 | line-height: 30px; 32 | } 33 | 34 | .welcome h1 strong { 35 | font-weight: 700; 36 | color: #38a9b4; 37 | } 38 | 39 | .welcome h2 { 40 | text-transform: uppercase; 41 | font-size: 18px; 42 | font-weight: bold; 43 | margin: 25px 0 5px; 44 | } 45 | 46 | .welcome h3 { 47 | text-transform: uppercase; 48 | font-weight: 500; 49 | color: #38a9b4; 50 | font-size: 16px; 51 | margin: 0 0 5px; 52 | display: block; 53 | } 54 | 55 | /***** TOP *****/ 56 | 57 | .welcome__top { 58 | background-color: #67cece; 59 | padding-bottom: 40px; 60 | } 61 | 62 | .welcome__flag { 63 | transform: rotate(30deg); 64 | position: fixed; 65 | right: -190px; 66 | top: 65px; 67 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2); 68 | z-index: 5; 69 | } 70 | 71 | /***** MAIN *****/ 72 | 73 | .welcome__main { 74 | box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 75 | 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.3); 76 | width: 80%; 77 | max-width: 1100px; 78 | margin-left: auto; 79 | margin-right: auto; 80 | transform: translateY(-50px); 81 | background-color: white; 82 | display: flex; 83 | } 84 | 85 | .main__aside { 86 | background-color: #afe5e5; 87 | width: 30%; 88 | position: relative; 89 | overflow: hidden; 90 | } 91 | 92 | .aside__circle, 93 | .main__aside svg { 94 | position: absolute; 95 | left: 50%; 96 | top: 50%; 97 | transform: translate(-50%, -50%); 98 | } 99 | 100 | .aside__circle { 101 | background-color: white; 102 | border-radius: 50%; 103 | width: 90%; 104 | height: 0; 105 | padding-bottom: 90%; 106 | } 107 | 108 | .aside__circle:after { 109 | content: ''; 110 | width: 4px; 111 | left: calc(50% - 5px); 112 | top: -50%; 113 | position: absolute; 114 | height: 100%; 115 | background-color: #1d1e1c; 116 | } 117 | 118 | .main__aside svg { 119 | width: 100%; 120 | } 121 | 122 | .main__content { 123 | padding: 30px; 124 | text-align: left; 125 | flex: auto; 126 | } 127 | .other__bloc { 128 | display: inline-flex; 129 | align-items: center; 130 | border: 4px solid #afe5e5; 131 | padding: 10px 20px; 132 | margin: 10px 0; 133 | height: 170px; 134 | box-sizing: border-box; 135 | text-align: left; 136 | width: 40%; 137 | } 138 | 139 | .other__bloc:not(:last-of-type) { 140 | margin-right: 10px; 141 | } 142 | 143 | .other__bloc h3:not(:first-child) { 144 | margin-top: 15px; 145 | padding-top: 5px; 146 | } 147 | 148 | .other__circle { 149 | width: 110px; 150 | height: 110px; 151 | background-color: #afe5e5; 152 | border-radius: 50%; 153 | margin-right:20px; 154 | } 155 | 156 | .other__circle svg{ 157 | width: 110px; 158 | } 159 | 160 | .buttons__group { 161 | display: inline-flex; 162 | vertical-align: center; 163 | } 164 | 165 | .buttons__group .buttons__or { 166 | width: 4px; 167 | position: relative; 168 | text-align:center; 169 | } 170 | 171 | .buttons__group .buttons__or:before { 172 | content: 'or'; 173 | font-size: 12px; 174 | color: #aaa; 175 | line-height: 18px; 176 | position: absolute; 177 | border-radius: 50%; 178 | top: 50%; 179 | left: 50%; 180 | transform: translate(-50%, -50%); 181 | background-color: white; 182 | width: 18px; 183 | height: 18px; 184 | } 185 | 186 | .buttons__group .other__button:first-child { 187 | border-radius: 5px 0 0 5px; 188 | padding-right: 15px; 189 | } 190 | 191 | .buttons__group .other__button:last-child { 192 | border-radius: 0 5px 5px 0; 193 | padding-left: 15px; 194 | } 195 | 196 | a.other__button { 197 | background-color: #e0e1e2; 198 | font-size: 11px; 199 | color: #686e63; 200 | cursor: pointer; 201 | padding: 5px 10px; 202 | display: inline-block; 203 | transition: all ease 0.2s; 204 | text-transform: uppercase; 205 | } 206 | 207 | .other__button:hover { 208 | background-color: #afe5e5; 209 | color: #339ba5; 210 | } 211 | 212 | .main__button { 213 | display: inline-block; 214 | padding: 10px 50px 10px 10px; 215 | border: 3px solid #339ba5; 216 | font-size: 22px; 217 | color: #339ba5; 218 | text-transform: uppercase; 219 | margin: 15px 0; 220 | overflow: hidden; 221 | transition: all ease 0.3s; 222 | cursor: pointer; 223 | position: relative; 224 | } 225 | 226 | .main__button svg { 227 | position: absolute; 228 | right: 10px; 229 | top: 50%; 230 | transform: translateY(-50%); 231 | transition: transform ease 0.2s; 232 | } 233 | 234 | .main__button:hover { 235 | background-color: #afe5e5; 236 | } 237 | 238 | .main__button:hover svg { 239 | transform: translateY(-50%) rotate(35deg); 240 | } 241 | 242 | /***** HELP *****/ 243 | 244 | .welcome__help { 245 | background-color: white; 246 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2); 247 | padding: 10px; 248 | position: fixed; 249 | right: -5px; 250 | top: 50%; 251 | transform: translateY(-50%); 252 | border-radius: 5px; 253 | text-align: center; 254 | } 255 | 256 | .welcome__help h2 { 257 | color: #aaa; 258 | font-size: 12px; 259 | margin: 10px 0; 260 | } 261 | 262 | .help__circle { 263 | width: 36px; 264 | height: 36px; 265 | border-radius: 50%; 266 | border: 2px solid #ccc; 267 | display: block; 268 | margin: 10px auto; 269 | transition: all ease 0.2s; 270 | position:relative; 271 | } 272 | 273 | .help__circle svg { 274 | position:absolute; 275 | left: 50%; 276 | top: 50%; 277 | transform:translate(-50%, -50%); 278 | } 279 | 280 | .help__circle:hover { 281 | border-color: #67cece; 282 | background-color: #afe5e5; 283 | } 284 | 285 | /***** MEDIAS *****/ 286 | 287 | @media (max-width: 1200px) { 288 | .main__aside, 289 | .welcome__help { 290 | display: none; 291 | } 292 | .main__content { 293 | width: 100%; 294 | text-align: center; 295 | padding: 20px; 296 | } 297 | } 298 | 299 | @media (max-width: 600px) { 300 | .welcome__main { 301 | width: calc(100% - 40px); 302 | } 303 | .welcome h1 { 304 | display: none; 305 | } 306 | .welcome__flag, 307 | .main__other { 308 | display: none; 309 | } 310 | .main__content { 311 | padding: 10px; 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | x-cache-from: 4 | - &api-cache-from 5 | cache_from: 6 | - ${NGINX_IMAGE:-quay.io/api-platform/nginx} 7 | - ${PHP_IMAGE:-quay.io/api-platform/php} 8 | 9 | services: 10 | php: 11 | build: 12 | context: ./api 13 | target: api_platform_php 14 | <<: *api-cache-from 15 | image: ${PHP_IMAGE:-quay.io/api-platform/php} 16 | healthcheck: 17 | interval: 10s 18 | timeout: 3s 19 | retries: 3 20 | start_period: 30s 21 | depends_on: 22 | - db 23 | - dev-tls 24 | volumes: 25 | - ./api:/srv/api:rw,cached 26 | - ./api/docker/php/conf.d/api-platform.dev.ini:/usr/local/etc/php/conf.d/api-platform.ini 27 | # if you develop on Linux, you may use a bind-mounted host directory instead 28 | # - ./api/var:/srv/api/var:rw 29 | - dev-certs:/certs:ro,nocopy 30 | 31 | api: 32 | build: 33 | context: ./api 34 | target: api_platform_nginx 35 | <<: *api-cache-from 36 | image: ${NGINX_IMAGE:-quay.io/api-platform/nginx} 37 | depends_on: 38 | - php 39 | volumes: 40 | - ./api/public:/srv/api/public:ro 41 | 42 | vulcain: 43 | image: dunglas/vulcain 44 | environment: 45 | - CERT_FILE=/certs/localhost.crt 46 | - KEY_FILE=/certs/localhost.key 47 | - UPSTREAM=http://api 48 | depends_on: 49 | - api 50 | - dev-tls 51 | volumes: 52 | - dev-certs:/certs:ro,nocopy 53 | ports: 54 | - target: 443 55 | published: 8443 56 | protocol: tcp 57 | 58 | db: 59 | image: mysql:8.0.19 60 | restart: always 61 | environment: 62 | - MYSQL_ROOT_PASSWORD=root 63 | - MYSQL_USER=api-platform 64 | - MYSQL_PASSWORD=!ChangeMe! 65 | - MYSQL_DATABASE=api 66 | volumes: 67 | - ./docker/mysql/data:/var/lib/mysql 68 | ports: 69 | - target: 3306 70 | published: 3306 71 | protocol: tcp 72 | 73 | mercure: 74 | image: dunglas/mercure 75 | environment: 76 | - ALLOW_ANONYMOUS=1 77 | - CERT_FILE=/certs/localhost.crt 78 | - CORS_ALLOWED_ORIGINS=* 79 | - DEMO=1 80 | - JWT_KEY=!ChangeMe! 81 | - KEY_FILE=/certs/localhost.key 82 | - PUBLISH_ALLOWED_ORIGINS=https://localhost:1337 # required for publishing from the demo page 83 | depends_on: 84 | - dev-tls 85 | volumes: 86 | - dev-certs:/certs:ro,nocopy 87 | ports: 88 | - target: 443 89 | published: 1337 90 | protocol: tcp 91 | 92 | client: 93 | build: 94 | context: ./client 95 | target: api_platform_client_development 96 | cache_from: 97 | - ${CLIENT_IMAGE:-quay.io/api-platform/client} 98 | image: ${CLIENT_IMAGE:-quay.io/api-platform/client} 99 | tty: true # https://github.com/facebook/create-react-app/issues/8688 100 | environment: 101 | - API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=http://api 102 | - API_PLATFORM_CLIENT_GENERATOR_OUTPUT=src 103 | depends_on: 104 | - dev-tls 105 | volumes: 106 | - ./client:/usr/src/client:rw,cached 107 | - dev-certs:/usr/src/client/node_modules/webpack-dev-server/ssl:rw,nocopy 108 | ports: 109 | - target: 3000 110 | published: 443 111 | protocol: tcp 112 | 113 | admin: 114 | build: 115 | context: ./admin 116 | target: api_platform_admin_development 117 | cache_from: 118 | - ${ADMIN_IMAGE:-quay.io/api-platform/admin} 119 | image: ${ADMIN_IMAGE:-quay.io/api-platform/admin} 120 | tty: true # https://github.com/facebook/create-react-app/issues/8688 121 | depends_on: 122 | - dev-tls 123 | volumes: 124 | - ./admin:/usr/src/admin:rw,cached 125 | - dev-certs:/usr/src/admin/node_modules/webpack-dev-server/ssl:rw,nocopy 126 | ports: 127 | - target: 3000 128 | published: 444 129 | protocol: tcp 130 | 131 | dev-tls: 132 | build: 133 | context: ./docker/dev-tls 134 | volumes: 135 | - dev-certs:/certs:rw 136 | ports: 137 | - target: 80 138 | published: 80 139 | protocol: tcp 140 | 141 | volumes: 142 | db-data: {} 143 | dev-certs: {} 144 | -------------------------------------------------------------------------------- /docker/dev-tls/Dockerfile: -------------------------------------------------------------------------------- 1 | # use this self-generated certificate only in dev, IT IS NOT SECURE! 2 | 3 | 4 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact 5 | ARG NGINX_VERSION=1.17 6 | 7 | 8 | FROM nginx:${NGINX_VERSION}-alpine 9 | 10 | # persistent / runtime deps 11 | RUN apk add --no-cache \ 12 | nss-tools \ 13 | ; 14 | 15 | WORKDIR /certs 16 | 17 | ARG MKCERT_VERSION=1.4.1 18 | RUN set -eux; \ 19 | wget -O /usr/local/bin/mkcert https://github.com/FiloSottile/mkcert/releases/download/v$MKCERT_VERSION/mkcert-v$MKCERT_VERSION-linux-amd64; \ 20 | chmod +x /usr/local/bin/mkcert; \ 21 | mkcert --cert-file localhost.crt --key-file localhost.key localhost 127.0.0.1 ::1 mercure; \ 22 | # the file must be named server.pem - the default certificate path in webpack-dev-server 23 | cat localhost.key localhost.crt > server.pem; \ 24 | # export the root CA cert, but not the root CA key 25 | cp "$(mkcert -CAROOT)/rootCA.pem" /certs/localCA.crt 26 | 27 | VOLUME /certs 28 | 29 | # add redirect from http://localhost to https://localhost 30 | RUN set -eux; \ 31 | { \ 32 | echo 'server {'; \ 33 | echo ' return 301 https://$host$request_uri;'; \ 34 | echo '}'; \ 35 | } | tee /etc/nginx/conf.d/default.conf 36 | -------------------------------------------------------------------------------- /github/img/admin-page-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/admin-page-1.png -------------------------------------------------------------------------------- /github/img/admin-page-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/admin-page-2.png -------------------------------------------------------------------------------- /github/img/admin-page-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/admin-page-3.png -------------------------------------------------------------------------------- /github/img/api-page-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/api-page-1.png -------------------------------------------------------------------------------- /github/img/api-page-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/api-page-2.png -------------------------------------------------------------------------------- /github/img/client-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/client-page.png -------------------------------------------------------------------------------- /github/img/make-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/make-dev.png -------------------------------------------------------------------------------- /github/img/make-rebuild-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/make-rebuild-2.png -------------------------------------------------------------------------------- /github/img/make-rebuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webcoderio/webdcoder-api-platform-mysql/843f3946913fc41c2d63ab654a9a9fd25e445d79/github/img/make-rebuild.png --------------------------------------------------------------------------------