├── api ├── tests │ └── .gitignore ├── src │ ├── Entity │ │ ├── .gitignore │ │ └── User.php │ ├── Controller │ │ └── .gitignore │ ├── Repository │ │ ├── .gitignore │ │ └── UserRepository.php │ ├── Kernel.php │ └── Command │ │ └── CreateUserCommand.php ├── config │ ├── routes │ │ ├── api_platform.yaml │ │ ├── annotations.yaml │ │ └── dev │ │ │ ├── twig.yaml │ │ │ └── web_profiler.yaml │ ├── packages │ │ ├── dev │ │ │ ├── routing.yaml │ │ │ └── web_profiler.yaml │ │ ├── validator.yaml │ │ ├── routing.yaml │ │ ├── test │ │ │ ├── framework.yaml │ │ │ └── web_profiler.yaml │ │ ├── ramsey_uuid_doctrine.yaml │ │ ├── twig.yaml │ │ ├── lexik_jwt_authentication.yaml │ │ ├── nelmio_cors.yaml │ │ ├── framework.yaml │ │ ├── cache.yaml │ │ ├── api_platform.yaml │ │ ├── doctrine.yaml │ │ ├── prod │ │ │ └── doctrine.yaml │ │ └── security.yaml │ ├── routes.yaml │ ├── bundles.php │ ├── bootstrap.php │ └── services.yaml ├── public │ ├── favicon.ico │ └── index.php ├── .env.test ├── helm │ └── api │ │ ├── Chart.yaml │ │ ├── requirements.yaml │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── php-service.yaml │ │ ├── nginx-service.yaml │ │ ├── configmap.yaml │ │ ├── varnish-service.yaml │ │ ├── ingress.yaml │ │ ├── _helpers.tpl │ │ ├── secrets.yaml │ │ ├── nginx-deployment.yaml │ │ ├── varnish-deployment.yaml │ │ └── php-deployment.yaml │ │ ├── requirements.lock │ │ ├── .helmignore │ │ └── values.yaml ├── .php_cs.dist ├── .dockerignore ├── docker │ ├── php │ │ ├── conf.d │ │ │ └── api-platform.ini │ │ └── docker-entrypoint.sh │ ├── nginx │ │ └── conf.d │ │ │ └── default.conf │ └── varnish │ │ └── conf │ │ └── default.vcl ├── templates │ └── base.html.twig ├── .gitignore ├── bin │ ├── phpunit │ └── console ├── phpunit.xml.dist ├── .env ├── composer.json ├── Dockerfile └── symfony.lock ├── .env ├── admin ├── .env ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── src │ ├── App.js │ ├── App.test.js │ ├── index.js │ └── serviceWorker.js ├── .dockerignore ├── .gitignore ├── Dockerfile └── package.json ├── .gitignore ├── client ├── src │ ├── config │ │ └── entrypoint.js │ ├── index.js │ ├── serviceWorker.js │ ├── welcome.css │ └── Welcome.js ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── .env ├── .dockerignore ├── Dockerfile ├── .gitignore └── package.json ├── .gitattributes ├── h2-proxy ├── Dockerfile └── conf.d │ └── default.conf ├── .editorconfig └── docker-compose.yml /api/tests/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/src/Entity/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/src/Controller/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/src/Repository/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | CONTAINER_REGISTRY_BASE=quay.io/api-platform 2 | -------------------------------------------------------------------------------- /admin/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_ENTRYPOINT=https://localhost:8443 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docker-compose.override.yaml 2 | /docker-compose.override.yml 3 | -------------------------------------------------------------------------------- /api/config/routes/api_platform.yaml: -------------------------------------------------------------------------------- 1 | api_platform: 2 | resource: . 3 | type: api_platform 4 | -------------------------------------------------------------------------------- /client/src/config/entrypoint.js: -------------------------------------------------------------------------------- 1 | export const ENTRYPOINT = process.env.REACT_APP_API_ENTRYPOINT; 2 | -------------------------------------------------------------------------------- /api/config/packages/dev/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: true 4 | -------------------------------------------------------------------------------- /api/config/packages/validator.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | validation: 3 | email_validation_mode: html5 4 | -------------------------------------------------------------------------------- /api/config/routes/annotations.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../../src/Controller/ 3 | type: annotation 4 | -------------------------------------------------------------------------------- /api/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | strict_requirements: ~ 4 | utf8: true 5 | -------------------------------------------------------------------------------- /api/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabbl/api-platform-cqrs-eventsourcing-tutorial/HEAD/api/public/favicon.ico -------------------------------------------------------------------------------- /admin/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabbl/api-platform-cqrs-eventsourcing-tutorial/HEAD/admin/public/favicon.ico -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabbl/api-platform-cqrs-eventsourcing-tutorial/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /api/config/routes/dev/twig.yaml: -------------------------------------------------------------------------------- 1 | _errors: 2 | resource: '@TwigBundle/Resources/config/routing/errors.xml' 3 | prefix: /_error 4 | -------------------------------------------------------------------------------- /api/config/packages/test/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | test: true 3 | session: 4 | storage_id: session.storage.mock_file 5 | -------------------------------------------------------------------------------- /api/config/packages/ramsey_uuid_doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | types: 4 | uuid: 'Ramsey\Uuid\Doctrine\UuidType' 5 | -------------------------------------------------------------------------------- /api/.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='s$cretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_ENTRYPOINT=https://localhost:8443 2 | API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=http://api 3 | API_PLATFORM_CLIENT_GENERATOR_OUTPUT=src 4 | -------------------------------------------------------------------------------- /api/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | default_path: '%kernel.project_dir%/templates' 3 | debug: '%kernel.debug%' 4 | strict_variables: '%kernel.debug%' 5 | -------------------------------------------------------------------------------- /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/dev/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | web_profiler: 2 | toolbar: true 3 | intercept_redirects: false 4 | 5 | framework: 6 | profiler: { only_exceptions: false } 7 | -------------------------------------------------------------------------------- /api/helm/api/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for an API Platform API 3 | name: api 4 | version: 0.2.0 5 | icon: https://api-platform.com/logo-250x250.png 6 | -------------------------------------------------------------------------------- /admin/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HydraAdmin } from '@api-platform/admin'; 3 | 4 | export default () => ; 5 | -------------------------------------------------------------------------------- /api/helm/api/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: postgresql 3 | version: 0.18.1 4 | repository: https://kubernetes-charts.storage.googleapis.com/ 5 | condition: postgresql.enabled 6 | -------------------------------------------------------------------------------- /api/helm/api/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | API deployed. 2 | 3 | To get the ingress's IP: 4 | 5 | kubectl --namespace {{ .Release.Namespace }} get ingress -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}" 6 | -------------------------------------------------------------------------------- /admin/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/._* 4 | **/.dockerignore 5 | **/.DS_Store 6 | **/.git/ 7 | **/.gitattributes 8 | **/.gitignore 9 | **/.gitmodules 10 | **/Dockerfile* 11 | **/Thumbs.db 12 | .env* 13 | build/ 14 | node_modules/ 15 | -------------------------------------------------------------------------------- /api/config/packages/lexik_jwt_authentication.yaml: -------------------------------------------------------------------------------- 1 | lexik_jwt_authentication: 2 | secret_key: '%env(resolve:JWT_SECRET_KEY)%' 3 | public_key: '%env(resolve:JWT_PUBLIC_KEY)%' 4 | pass_phrase: '%env(JWT_PASSPHRASE)%' 5 | token_ttl: 3600 6 | -------------------------------------------------------------------------------- /client/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/._* 4 | **/.dockerignore 5 | **/.DS_Store 6 | **/.git/ 7 | **/.gitattributes 8 | **/.gitignore 9 | **/.gitmodules 10 | **/Dockerfile* 11 | **/Thumbs.db 12 | .env* 13 | build/ 14 | node_modules/ 15 | -------------------------------------------------------------------------------- /api/config/routes.yaml: -------------------------------------------------------------------------------- 1 | #index: 2 | # path: / 3 | # controller: App\Controller\DefaultController::index 4 | 5 | api: 6 | resource: '.' 7 | type: 'api_platform' 8 | prefix: 'api' 9 | 10 | api_login_check: 11 | path: /api/login_check 12 | 13 | -------------------------------------------------------------------------------- /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/helm/api/requirements.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: postgresql 3 | repository: https://kubernetes-charts.storage.googleapis.com/ 4 | version: 0.18.1 5 | digest: sha256:d3a1c2738398c0657d885ca0451349f83086936b7a853aeca3d117f93260516d 6 | generated: 2018-10-01T22:29:12.850677078+07:00 7 | -------------------------------------------------------------------------------- /admin/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /api/.php_cs.dist: -------------------------------------------------------------------------------- 1 | exclude('var') 5 | ->in(__DIR__) 6 | ; 7 | 8 | return PhpCsFixer\Config::create() 9 | ->setRules([ 10 | '@Symfony' => true, 11 | 'array_syntax' => ['syntax' => 'short'], 12 | ]) 13 | ->setFinder($finder) 14 | ; 15 | -------------------------------------------------------------------------------- /api/.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/*.md 3 | **/*.php~ 4 | **/._* 5 | **/.dockerignore 6 | **/.DS_Store 7 | **/.git/ 8 | **/.gitattributes 9 | **/.gitignore 10 | **/.gitmodules 11 | **/Dockerfile 12 | **/Thumbs.db 13 | .editorconfig 14 | .env* 15 | .php_cs.cache 16 | bin/* 17 | !bin/console 18 | docker/db/data/ 19 | helm/ 20 | public/bundles/ 21 | var/ 22 | vendor/ 23 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:11.5-alpine 2 | 3 | RUN mkdir -p /usr/src/client 4 | 5 | WORKDIR /usr/src/client 6 | 7 | RUN yarn global add @api-platform/client-generator 8 | 9 | # Prevent the reinstallation of node modules at every changes in the source code 10 | COPY package.json yarn.lock ./ 11 | RUN yarn install 12 | 13 | COPY . ./ 14 | 15 | CMD yarn start 16 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "API Platform", 3 | "name": "Client", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /admin/.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 | -------------------------------------------------------------------------------- /admin/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "API Platform Admin", 3 | "name": "Admin", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /api/docker/php/conf.d/api-platform.ini: -------------------------------------------------------------------------------- 1 | apc.enable_cli = 1 2 | date.timezone = UTC 3 | session.auto_start = Off 4 | short_open_tag = Off 5 | 6 | # http://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/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/.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 | -------------------------------------------------------------------------------- /api/config/packages/nelmio_cors.yaml: -------------------------------------------------------------------------------- 1 | nelmio_cors: 2 | defaults: 3 | origin_regex: true 4 | allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] 5 | allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE'] 6 | allow_headers: ['Content-Type', 'Authorization'] 7 | expose_headers: ['Link'] 8 | max_age: 3600 9 | paths: 10 | '^/': ~ 11 | -------------------------------------------------------------------------------- /admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:11.5-alpine 2 | 3 | RUN mkdir -p /usr/src/admin 4 | 5 | WORKDIR /usr/src/admin 6 | 7 | # Prevent the reinstallation of node modules at every changes in the source code 8 | COPY package.json yarn.lock ./ 9 | 10 | RUN apk add --no-cache --virtual .gyp \ 11 | python \ 12 | make \ 13 | g++ \ 14 | && yarn install \ 15 | && apk del .gyp 16 | 17 | COPY . ./ 18 | 19 | CMD yarn start 20 | -------------------------------------------------------------------------------- /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 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /api/helm/api/templates/php-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: php 5 | labels: 6 | app: {{ template "name" . }}-php 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | type: ClusterIP 12 | ports: 13 | - port: 9000 14 | selector: 15 | app: {{ template "name" . }}-php 16 | release: {{ .Release.Name }} 17 | -------------------------------------------------------------------------------- /.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 | 15 | *.ico binary 16 | *.png binary 17 | 18 | .github export-ignore 19 | .travis.yml export-ignore 20 | LICENSE export-ignore 21 | README.md export-ignore 22 | update-deps.sh export-ignore 23 | -------------------------------------------------------------------------------- /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: http://bit.ly/CRA-PWA 11 | serviceWorker.unregister(); 12 | -------------------------------------------------------------------------------- /api/helm/api/templates/nginx-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: api 5 | labels: 6 | app: {{ template "name" . }}-nginx 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | type: NodePort 12 | ports: 13 | - port: 80 14 | targetPort: 80 15 | protocol: TCP 16 | selector: 17 | app: {{ template "name" . }}-nginx 18 | release: {{ .Release.Name }} 19 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | /helm/api/charts 2 | 3 | ###> symfony/framework-bundle ### 4 | /.env.local 5 | /.env.*.local 6 | /public/bundles/ 7 | /var/ 8 | /vendor/ 9 | ###< symfony/framework-bundle ### 10 | 11 | ###> friendsofphp/php-cs-fixer ### 12 | .php_cs 13 | .php_cs.cache 14 | ###< friendsofphp/php-cs-fixer ### 15 | 16 | ###> symfony/phpunit-bridge ### 17 | .phpunit 18 | /phpunit.xml 19 | ###< symfony/phpunit-bridge ### 20 | 21 | ###> lexik/jwt-authentication-bundle ### 22 | /config/jwt/*.pem 23 | ###< lexik/jwt-authentication-bundle ### 24 | -------------------------------------------------------------------------------- /api/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: '%env(APP_SECRET)%' 3 | #default_locale: en 4 | #csrf_protection: true 5 | #http_method_override: true 6 | 7 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 8 | # Remove or comment this section to explicitly disable session support. 9 | session: 10 | handler_id: ~ 11 | cookie_secure: auto 12 | cookie_samesite: lax 13 | 14 | #esi: true 15 | #fragments: true 16 | php_errors: 17 | log: true 18 | -------------------------------------------------------------------------------- /api/helm/api/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ template "fullname" . }} 5 | labels: 6 | app: {{ template "fullname" . }} 7 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 8 | release: "{{ .Release.Name }}" 9 | heritage: "{{ .Release.Service }}" 10 | data: 11 | env: {{ .Values.env | quote }} 12 | debug: {{ .Values.debug | quote }} 13 | cors-allow-origin: {{ .Values.corsAllowOrigin | quote }} 14 | varnish-url: {{ if .Values.varnish.enabled }}http://varnish{{ else }}{{ .Values.varnish.url | quote }}{{ end }} 15 | -------------------------------------------------------------------------------- /api/helm/api/templates/varnish-service.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.varnish.enabled -}} 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: varnish 6 | labels: 7 | app: {{ template "name" . }}-varnish 8 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: NodePort 13 | ports: 14 | - port: 80 15 | targetPort: 80 16 | protocol: TCP 17 | selector: 18 | app: {{ template "name" . }}-varnish 19 | release: {{ .Release.Name }} 20 | {{- end -}} 21 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@api-platform/admin": "^0.6.1", 7 | "@babel/runtime": "7.0.0-beta.55", 8 | "react": "^16.3.0", 9 | "react-dom": "^16.3.0", 10 | "react-scripts": "^2.1.2" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test", 16 | "eject": "react-scripts eject" 17 | }, 18 | "browserslist": [ 19 | ">0.2%", 20 | "not dead", 21 | "not ie <= 11", 22 | "not op_mini all" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /api/bin/phpunit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | _em; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /h2-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add --no-cache openssl 4 | 5 | # Use this self-generated certificate only in dev, IT IS NOT SECURE! 6 | RUN openssl genrsa -des3 -passout pass:NotSecure -out cert.pass.key 2048 7 | RUN openssl rsa -passin pass:NotSecure -in cert.pass.key -out cert.key 8 | RUN rm cert.pass.key 9 | RUN openssl req -new -passout pass:NotSecure -key cert.key -out cert.csr \ 10 | -subj '/C=SS/ST=SS/L=Gotham City/O=API Platform Dev/CN=localhost' 11 | RUN openssl x509 -req -sha256 -days 365 -in cert.csr -signkey cert.key -out cert.crt 12 | 13 | FROM nginx:1.15-alpine 14 | 15 | RUN mkdir -p /etc/nginx/ssl/ 16 | COPY --from=0 cert.key cert.crt /etc/nginx/ssl/ 17 | COPY conf.d /etc/nginx/conf.d/ 18 | -------------------------------------------------------------------------------- /api/helm/api/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: {{ template "name" . }}-ingress 5 | labels: 6 | app: {{ template "name" . }}-ingress 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | annotations: 11 | {{- range $key, $value := .Values.ingress.annotations }} 12 | {{ $key }}: {{ $value | quote }} 13 | {{- end }} 14 | spec: 15 | backend: 16 | serviceName: {{ if .Values.varnish.enabled }}varnish{{ else }}api{{ end }} 17 | servicePort: 80 18 | {{- if .Values.ingress.tls }} 19 | tls: 20 | {{ toYaml .Values.ingress.tls | indent 4 }} 21 | {{- end -}} 22 | -------------------------------------------------------------------------------- /api/helm/api/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "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 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | 18 | {{- define "postgresql.fullname" -}} 19 | {{- printf "%s-%s" .Release.Name "postgresql" | trunc 63 | trimSuffix "-" -}} 20 | {{- end -}} 21 | -------------------------------------------------------------------------------- /api/config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Put the unique name of your app here: the prefix seed 4 | # is used to compute stable namespaces for cache keys. 5 | #prefix_seed: your_vendor_name/app_name 6 | 7 | # The app cache caches to the filesystem by default. 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: ~ 20 | -------------------------------------------------------------------------------- /api/config/packages/api_platform.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | # Adds a fallback VARNISH_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(VARNISH_URL): '' 7 | 8 | api_platform: 9 | mapping: 10 | paths: ['%kernel.project_dir%/src/Entity'] 11 | title: Hello API Platform 12 | version: 1.0.0 13 | # Enable the Varnish integration 14 | http_cache: 15 | invalidation: 16 | enabled: true 17 | varnish_urls: ['%env(VARNISH_URL)%'] 18 | max_age: 0 19 | shared_max_age: 3600 20 | vary: ['Content-Type', 'Authorization'] 21 | public: true 22 | -------------------------------------------------------------------------------- /api/helm/api/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | {{- $postgresqlServiceName := include "postgresql.fullname" . -}} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ template "fullname" . }} 6 | labels: 7 | app: {{ template "fullname" . }} 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" 9 | release: "{{ .Release.Name }}" 10 | heritage: "{{ .Release.Service }}" 11 | type: Opaque 12 | data: 13 | {{ if .Values.postgresql.enabled }} 14 | database-url: {{ printf "postgres://%s:%s@%s/%s" .Values.postgresql.postgresUser .Values.postgresql.postgresPassword $postgresqlServiceName .Values.postgresql.postgresDatabase | b64enc | quote }} 15 | {{ else }} 16 | database-url: {{ .Values.postgresql.url | b64enc | quote }} 17 | {{ end }} 18 | secret: {{ .Values.secret | b64enc | quote }} 19 | -------------------------------------------------------------------------------- /api/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 6 | Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], 7 | Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], 8 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 9 | ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], 10 | Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], 11 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 12 | Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true], 13 | ]; 14 | -------------------------------------------------------------------------------- /api/public/index.php: -------------------------------------------------------------------------------- 1 | handle($request); 26 | $response->send(); 27 | $kernel->terminate($request, $response); 28 | -------------------------------------------------------------------------------- /api/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | parameters: 2 | # Adds a fallback DATABASE_URL if the env var is not set. 3 | # This allows you to run cache:warmup even if your 4 | # environment variables are not available yet. 5 | # You should not need to change this value. 6 | env(DATABASE_URL): '' 7 | 8 | doctrine: 9 | dbal: 10 | # configure these for your database server 11 | driver: 'pdo_pgsql' 12 | server_version: '10' 13 | 14 | # With Symfony 3.3, remove the `resolve:` prefix 15 | url: '%env(resolve:DATABASE_URL)%' 16 | orm: 17 | auto_generate_proxy_classes: true 18 | naming_strategy: doctrine.orm.naming_strategy.underscore 19 | auto_mapping: true 20 | mappings: 21 | App: 22 | is_bundle: false 23 | type: annotation 24 | dir: '%kernel.project_dir%/src/Entity' 25 | prefix: 'App\Entity' 26 | alias: App 27 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "bootstrap": "^4.1.3", 7 | "connected-react-router": "^5.0.1", 8 | "font-awesome": "^4.7.0", 9 | "jest-junit": "^5.2.0", 10 | "lodash.get": "^4.4.2", 11 | "lodash.has": "^4.5.2", 12 | "lodash.mapvalues": "^4.6.0", 13 | "prettier": "^1.14.3", 14 | "prop-types": "^15.6.2", 15 | "react": "^16.6.0", 16 | "react-dom": "^16.6.0", 17 | "react-redux": "^5.1.0", 18 | "react-router-dom": "^4.3.1", 19 | "react-scripts": "^2.1.1", 20 | "redux": "^4.0.1", 21 | "redux-form": "^7.4.2", 22 | "redux-thunk": "^2.3.0" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test": "react-scripts test", 28 | "eject": "react-scripts eject" 29 | }, 30 | "browserslist": [ 31 | ">0.2%", 32 | "not dead", 33 | "not ie <= 11", 34 | "not op_mini all" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /api/helm/api/templates/nginx-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "name" . }}-nginx 5 | labels: 6 | app: {{ template "name" . }}-nginx 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.nginx.replicaCount }} 12 | template: 13 | metadata: 14 | labels: 15 | app: {{ template "name" . }}-nginx 16 | release: {{ .Release.Name }} 17 | spec: 18 | containers: 19 | - name: {{ .Chart.Name }}-nginx 20 | image: "{{ .Values.nginx.repository }}:{{ .Values.nginx.tag }}" 21 | imagePullPolicy: {{ .Values.nginx.pullPolicy }} 22 | ports: 23 | - containerPort: 80 24 | resources: 25 | {{ toYaml .Values.resources | indent 12 }} 26 | {{- if .Values.nodeSelector }} 27 | nodeSelector: 28 | {{ toYaml .Values.nodeSelector | indent 8 }} 29 | {{- end }} 30 | -------------------------------------------------------------------------------- /api/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | 23 | 24 | src 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /api/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2) 9 | if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { 10 | $_SERVER += $env; 11 | $_ENV += $env; 12 | } elseif (!class_exists(Dotenv::class)) { 13 | throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); 14 | } else { 15 | // load all the .env files 16 | (new Dotenv())->loadEnv(dirname(__DIR__).'/.env'); 17 | } 18 | 19 | $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; 20 | $_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; 21 | $_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; 22 | -------------------------------------------------------------------------------- /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' ]; then 21 | composer install --prefer-dist --no-progress --no-suggest --no-interaction 22 | fi 23 | 24 | echo "Waiting for db to be ready..." 25 | until bin/console doctrine:query:sql "SELECT 1" > /dev/null 2>&1; do 26 | sleep 1 27 | done 28 | 29 | if [ "$APP_ENV" != 'prod' ]; then 30 | bin/console doctrine:schema:update --force --no-interaction 31 | fi 32 | fi 33 | 34 | exec docker-php-entrypoint "$@" 35 | -------------------------------------------------------------------------------- /api/config/packages/prod/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | orm: 3 | auto_generate_proxy_classes: false 4 | metadata_cache_driver: 5 | type: service 6 | id: doctrine.system_cache_provider 7 | query_cache_driver: 8 | type: service 9 | id: doctrine.system_cache_provider 10 | result_cache_driver: 11 | type: service 12 | id: doctrine.result_cache_provider 13 | 14 | services: 15 | doctrine.result_cache_provider: 16 | class: Symfony\Component\Cache\DoctrineProvider 17 | public: false 18 | arguments: 19 | - '@doctrine.result_cache_pool' 20 | doctrine.system_cache_provider: 21 | class: Symfony\Component\Cache\DoctrineProvider 22 | public: false 23 | arguments: 24 | - '@doctrine.system_cache_pool' 25 | 26 | framework: 27 | cache: 28 | pools: 29 | doctrine.result_cache_pool: 30 | adapter: cache.app 31 | doctrine.system_cache_pool: 32 | adapter: cache.system 33 | -------------------------------------------------------------------------------- /api/helm/api/templates/varnish-deployment.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.varnish.enabled -}} 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: {{ template "name" . }}-varnish 6 | labels: 7 | app: {{ template "name" . }}-varnish 8 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | replicas: {{ .Values.varnish.replicaCount }} 13 | template: 14 | metadata: 15 | labels: 16 | app: {{ template "name" . }}-varnish 17 | release: {{ .Release.Name }} 18 | spec: 19 | containers: 20 | - name: {{ .Chart.Name }}-varnish 21 | image: "{{ .Values.varnish.repository }}:{{ .Values.varnish.tag }}" 22 | imagePullPolicy: {{ .Values.varnish.pullPolicy }} 23 | ports: 24 | - containerPort: 80 25 | resources: 26 | {{ toYaml .Values.resources | indent 12 }} 27 | {{- if .Values.nodeSelector }} 28 | nodeSelector: 29 | {{ toYaml .Values.nodeSelector | indent 8 }} 30 | {{- end }} 31 | {{- end -}} 32 | -------------------------------------------------------------------------------- /api/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getParameterOption(['--env', '-e'], null, true)) { 19 | putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); 20 | } 21 | 22 | if ($input->hasParameterOption('--no-debug', true)) { 23 | putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); 24 | } 25 | 26 | require dirname(__DIR__).'/config/bootstrap.php'; 27 | 28 | if ($_SERVER['APP_DEBUG']) { 29 | umask(0000); 30 | 31 | if (class_exists(Debug::class)) { 32 | Debug::enable(); 33 | } 34 | } 35 | 36 | $kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); 37 | $application = new Application($kernel); 38 | $application->run($input); 39 | -------------------------------------------------------------------------------- /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/.env: -------------------------------------------------------------------------------- 1 | # This file defines all environment variables that the application needs. 2 | # DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE. 3 | # Use ".env.local" for local overrides during development. 4 | # Use real environment variables when deploying to production. 5 | # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration 6 | 7 | ###> symfony/framework-bundle ### 8 | APP_ENV=dev 9 | APP_SECRET=!ChangeMe! 10 | TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 11 | TRUSTED_HOSTS=^localhost|api$ 12 | ###< symfony/framework-bundle ### 13 | 14 | ###> doctrine/doctrine-bundle ### 15 | # Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 16 | # For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" 17 | # Configure your db driver and server_version in config/packages/doctrine.yaml 18 | DATABASE_URL=postgres://api-platform:!ChangeMe!@db/api 19 | ###< doctrine/doctrine-bundle ### 20 | 21 | ###> nelmio/cors-bundle ### 22 | CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$ 23 | ###< nelmio/cors-bundle ### 24 | 25 | VARNISH_URL=http://cache-proxy 26 | 27 | ###> lexik/jwt-authentication-bundle ### 28 | JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem 29 | JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem 30 | JWT_PASSPHRASE=test123 31 | ###< lexik/jwt-authentication-bundle ### 32 | -------------------------------------------------------------------------------- /.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 | [*.xml] 42 | indent_style = space 43 | indent_size = 4 44 | 45 | [*.{yaml,yml}] 46 | indent_style = space 47 | indent_size = 4 48 | trim_trailing_whitespace = false 49 | 50 | [.gitmodules] 51 | indent_style = tab 52 | indent_size = 4 53 | 54 | [.php_cs{,.dist}] 55 | indent_style = space 56 | indent_size = 4 57 | 58 | [.travis.yml] 59 | indent_style = space 60 | indent_size = 2 61 | 62 | [composer.json] 63 | indent_style = space 64 | indent_size = 4 65 | 66 | [docker-compose{,.override}.{yaml,yml}] 67 | indent_style = space 68 | indent_size = 2 69 | 70 | [Dockerfile] 71 | indent_style = tab 72 | indent_size = 4 73 | 74 | [package.json] 75 | indent_style = space 76 | indent_size = 2 77 | 78 | [phpunit.xml{,.dist}] 79 | indent_style = space 80 | indent_size = 4 81 | -------------------------------------------------------------------------------- /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 | # Comment the next line and uncomment the next to enable dynamic resolution (incompatible with Kubernetes) 11 | fastcgi_pass php:9000; 12 | #resolver 127.0.0.11; 13 | #set $upstream_host php; 14 | #fastcgi_pass $upstream_host:9000; 15 | 16 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 17 | include fastcgi_params; 18 | # When you are using symlinks to link the document root to the 19 | # current version of your application, you should pass the real 20 | # application path instead of the path to the symlink to PHP 21 | # FPM. 22 | # Otherwise, PHP's OPcache may not properly detect changes to 23 | # your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126 24 | # for more information). 25 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 26 | fastcgi_param DOCUMENT_ROOT $realpath_root; 27 | # Prevents URIs that include the front controller. This will 404: 28 | # http://domain.tld/index.php/some-path 29 | # Remove the internal directive to allow URIs like this 30 | internal; 31 | } 32 | 33 | # return 404 for all other php files not matching the front controller 34 | # this prevents access to other php files you don't want to be accessible. 35 | location ~ \.php$ { 36 | return 404; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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/createBrowserHistory'; 9 | import { 10 | ConnectedRouter, 11 | connectRouter, 12 | routerMiddleware 13 | } from 'connected-react-router'; 14 | import 'bootstrap/dist/css/bootstrap.css'; 15 | import 'font-awesome/css/font-awesome.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: http://bit.ly/CRA-PWA 46 | serviceWorker.unregister(); 47 | -------------------------------------------------------------------------------- /admin/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | API Platform Admin 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | Welcome to API Platform 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /h2-proxy/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl http2; 3 | listen [::]:443 ssl http2; 4 | 5 | ssl_certificate /etc/nginx/ssl/cert.crt; 6 | ssl_certificate_key /etc/nginx/ssl/cert.key; 7 | 8 | location / { 9 | proxy_pass http://client:3000; 10 | proxy_set_header Upgrade $http_upgrade; 11 | proxy_set_header Connection "upgrade"; 12 | } 13 | } 14 | 15 | server { 16 | listen 444 ssl http2; 17 | listen [::]:444 ssl http2; 18 | 19 | ssl_certificate /etc/nginx/ssl/cert.crt; 20 | ssl_certificate_key /etc/nginx/ssl/cert.key; 21 | 22 | location / { 23 | proxy_pass http://admin:3000; 24 | proxy_set_header Upgrade $http_upgrade; 25 | proxy_set_header Connection "upgrade"; 26 | } 27 | } 28 | 29 | server { 30 | listen 8443 ssl http2; 31 | listen [::]:8443 ssl http2; 32 | 33 | ssl_certificate /etc/nginx/ssl/cert.crt; 34 | ssl_certificate_key /etc/nginx/ssl/cert.key; 35 | 36 | location / { 37 | proxy_pass http://api; 38 | proxy_http_version 1.1; 39 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 40 | proxy_set_header X-Forwarded-Host $host; 41 | proxy_set_header X-Forwarded-Proto $scheme; 42 | proxy_set_header X-Forwarded-Port 8443; 43 | } 44 | } 45 | 46 | server { 47 | listen 8444 ssl http2; 48 | listen [::]:8444 ssl http2; 49 | 50 | ssl_certificate /etc/nginx/ssl/cert.crt; 51 | ssl_certificate_key /etc/nginx/ssl/cert.key; 52 | 53 | location / { 54 | proxy_pass http://cache-proxy; 55 | proxy_http_version 1.1; 56 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 57 | proxy_set_header X-Forwarded-Host $host; 58 | proxy_set_header X-Forwarded-Proto $scheme; 59 | proxy_set_header X-Forwarded-Port 8444; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /api/config/packages/security.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | encoders: 3 | App\Entity\User: 4 | algorithm: bcrypt 5 | cost: 12 6 | 7 | # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers 8 | providers: 9 | db_provider: 10 | entity: 11 | class: App\Entity\User 12 | property: username 13 | 14 | firewalls: 15 | dev: 16 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 17 | security: false 18 | 19 | login: 20 | pattern: ^/api/login 21 | stateless: true 22 | anonymous: true 23 | json_login: 24 | check_path: /api/login_check 25 | success_handler: lexik_jwt_authentication.handler.authentication_success 26 | failure_handler: lexik_jwt_authentication.handler.authentication_failure 27 | 28 | api: 29 | pattern: ^/api 30 | stateless: true 31 | guard: 32 | authenticators: 33 | - lexik_jwt_authentication.jwt_token_authenticator 34 | 35 | main: 36 | anonymous: true 37 | 38 | # activate different ways to authenticate 39 | 40 | # http_basic: true 41 | # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate 42 | 43 | # form_login: true 44 | # https://symfony.com/doc/current/security/form_login_setup.html 45 | 46 | # Easy way to control access for large sections of your site 47 | # Note: Only the *first* access control that matches will be used 48 | access_control: 49 | - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 50 | - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } 51 | -------------------------------------------------------------------------------- /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 | env: prod 6 | debug: '0' 7 | secret: ChangeMe 8 | corsAllowOrigin: http://example.com 9 | 10 | php: 11 | repository: quay.io/api-platform/php 12 | tag: latest 13 | pullPolicy: Always 14 | replicaCount: 1 15 | 16 | nginx: 17 | repository: quay.io/api-platform/nginx 18 | tag: latest 19 | pullPolicy: Always 20 | replicaCount: 1 21 | 22 | varnish: 23 | enabled: true 24 | #url: https://example.com 25 | repository: quay.io/api-platform/varnish 26 | tag: latest 27 | pullPolicy: Always 28 | replicaCount: 1 29 | 30 | ingress: 31 | annotations: 32 | # kubernetes.io/ingress.class: gce 33 | # kubernetes.io/tls-acme: "true" 34 | tls: 35 | # Secrets must be manually created in the namespace. 36 | # - secretName: chart-example-tls 37 | # hosts: 38 | # - chart-example.local 39 | 40 | postgresql: 41 | enabled: true 42 | imageTag: 10-alpine 43 | # If bringing your own PostgreSQL, the full uri to use 44 | #url: postgres://api-platform:!ChangeMe!@example.com/api 45 | postgresUser: api-platform 46 | postgresPassword: ChangeMe 47 | postgresDatabase: api 48 | # Persistent Volume Storage configuration. 49 | # ref: https://kubernetes.io/docs/user-guide/persistent-volumes 50 | persistence: 51 | enabled: true 52 | 53 | resources: {} 54 | # We usually recommend not to specify default resources and to leave this as a conscious 55 | # choice for the user. This also increases chances charts run on environments with little 56 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 57 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 58 | # limits: 59 | # cpu: 100m 60 | # memory: 128Mi 61 | # requests: 62 | # cpu: 100m 63 | # memory: 128Mi 64 | -------------------------------------------------------------------------------- /api/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "license": "proprietary", 4 | "require": { 5 | "php": "^7.1.3", 6 | "ext-ctype": "*", 7 | "ext-iconv": "*", 8 | "api-platform/api-pack": "^1.1", 9 | "guzzlehttp/guzzle": "^6.3", 10 | "lexik/jwt-authentication-bundle": "^2.6", 11 | "ramsey/uuid-doctrine": "^1.5", 12 | "symfony/console": "4.2.*", 13 | "symfony/dotenv": "4.2.*", 14 | "symfony/flex": "^1.1", 15 | "symfony/framework-bundle": "4.2.*", 16 | "symfony/yaml": "4.2.*" 17 | }, 18 | "require-dev": { 19 | "api-platform/schema-generator": "^2.1", 20 | "symfony/phpunit-bridge": "4.2.*", 21 | "symfony/profiler-pack": "^1.0" 22 | }, 23 | "config": { 24 | "preferred-install": { 25 | "*": "dist" 26 | }, 27 | "sort-packages": true 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "App\\": "src/" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "App\\Tests\\": "tests/" 37 | } 38 | }, 39 | "replace": { 40 | "paragonie/random_compat": "2.*", 41 | "symfony/polyfill-ctype": "*", 42 | "symfony/polyfill-iconv": "*", 43 | "symfony/polyfill-php71": "*", 44 | "symfony/polyfill-php70": "*", 45 | "symfony/polyfill-php56": "*" 46 | }, 47 | "scripts": { 48 | "auto-scripts": { 49 | "cache:clear": "symfony-cmd", 50 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 51 | }, 52 | "post-install-cmd": [ 53 | "@auto-scripts" 54 | ], 55 | "post-update-cmd": [ 56 | "@auto-scripts" 57 | ] 58 | }, 59 | "conflict": { 60 | "symfony/symfony": "*" 61 | }, 62 | "extra": { 63 | "symfony": { 64 | "allow-contrib": false, 65 | "require": "4.2.*" 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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 | protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader) 29 | { 30 | $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php')); 31 | $container->setParameter('container.dumper.inline_class_loader', true); 32 | $confDir = $this->getProjectDir().'/config'; 33 | 34 | $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob'); 35 | $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob'); 36 | $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob'); 37 | $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob'); 38 | } 39 | 40 | protected function configureRoutes(RouteCollectionBuilder $routes) 41 | { 42 | $confDir = $this->getProjectDir().'/config'; 43 | 44 | $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob'); 45 | $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob'); 46 | $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 | } 23 | 24 | sub vcl_backend_response { 25 | # Ban lurker friendly header 26 | set beresp.http.url = bereq.url; 27 | 28 | # Add a grace in case the backend is down 29 | set beresp.grace = 1h; 30 | } 31 | 32 | sub vcl_deliver { 33 | # Don't send cache tags related headers to the client 34 | unset resp.http.url; 35 | # Uncomment the following line to NOT send the "Cache-Tags" header to the client (prevent using CloudFlare cache tags) 36 | #unset resp.http.Cache-Tags; 37 | } 38 | 39 | sub vcl_recv { 40 | # Remove the "Forwarded" HTTP header if exists (security) 41 | unset req.http.forwarded; 42 | 43 | # To allow API Platform to ban by cache tags 44 | if (req.method == "BAN") { 45 | if (client.ip !~ invalidators) { 46 | return(synth(405, "Not allowed")); 47 | } 48 | 49 | if (req.http.ApiPlatform-Ban-Regex) { 50 | ban("obj.http.Cache-Tags ~ " + req.http.ApiPlatform-Ban-Regex); 51 | 52 | return(synth(200, "Ban added")); 53 | } 54 | 55 | return(synth(400, "ApiPlatform-Ban-Regex HTTP header must be set.")); 56 | } 57 | } 58 | 59 | sub vcl_hit { 60 | if (obj.ttl >= 0s) { 61 | # A pure unadulterated hit, deliver it 62 | return (deliver); 63 | } 64 | if (std.healthy(req.backend_hint)) { 65 | # The backend is healthy 66 | # Fetch the object from the backend 67 | return (miss); 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 | # No valid object to deliver 76 | # No healthy backend to handle request 77 | # Return error 78 | return (synth(503, "API is down")); 79 | } 80 | -------------------------------------------------------------------------------- /api/helm/api/templates/php-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: {{ template "name" . }}-php 5 | labels: 6 | app: {{ template "name" . }}-php 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.php.replicaCount }} 12 | template: 13 | metadata: 14 | labels: 15 | app: {{ template "name" . }}-php 16 | release: {{ .Release.Name }} 17 | spec: 18 | containers: 19 | - name: {{ .Chart.Name }}-php 20 | image: "{{ .Values.php.repository }}:{{ .Values.php.tag }}" 21 | imagePullPolicy: {{ .Values.php.pullPolicy }} 22 | ports: 23 | - containerPort: 9000 24 | env: 25 | - name: APP_ENV 26 | valueFrom: 27 | configMapKeyRef: 28 | name: {{ template "fullname" . }} 29 | key: env 30 | - name: APP_DEBUG 31 | valueFrom: 32 | configMapKeyRef: 33 | name: {{ template "fullname" . }} 34 | key: debug 35 | - name: CORS_ALLOW_ORIGIN 36 | valueFrom: 37 | configMapKeyRef: 38 | name: {{ template "fullname" . }} 39 | key: cors-allow-origin 40 | - name: VARNISH_URL 41 | valueFrom: 42 | configMapKeyRef: 43 | name: {{ template "fullname" . }} 44 | key: varnish-url 45 | - name: APP_SECRET 46 | valueFrom: 47 | secretKeyRef: 48 | name: {{ template "fullname" . }} 49 | key: secret 50 | - name: DATABASE_URL 51 | valueFrom: 52 | secretKeyRef: 53 | name: {{ template "fullname" . }} 54 | key: database-url 55 | resources: 56 | {{ toYaml .Values.resources | indent 12 }} 57 | {{- if .Values.nodeSelector }} 58 | nodeSelector: 59 | {{ toYaml .Values.nodeSelector | indent 8 }} 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /api/src/Command/CreateUserCommand.php: -------------------------------------------------------------------------------- 1 | passwordEncoder = $passwordEncoder; 30 | $this->userRepository = $userRepository; 31 | parent::__construct(); 32 | } 33 | 34 | protected function configure(): void 35 | { 36 | $this 37 | ->setDescription('Creates a new user.') 38 | ->addArgument('username', InputArgument::REQUIRED, 'Username') 39 | ->addArgument('password', InputArgument::REQUIRED, 'User password') 40 | ->addArgument('role', InputArgument::OPTIONAL, 'Users role') 41 | ->setHelp('This command allows you to create a user...') 42 | ; 43 | } 44 | 45 | /** 46 | * @param InputInterface $input 47 | * @param OutputInterface $output 48 | * @return int|void|null 49 | * @throws \Exception 50 | */ 51 | protected function execute(InputInterface $input, OutputInterface $output) 52 | { 53 | 54 | $username = $input->getArgument('username'); 55 | $password = $input->getArgument('password'); 56 | $role = $input->getArgument('role') ?? 'ROLE_USER'; 57 | 58 | if (strlen($username) < 5) { 59 | $output->writeln('Username too short, length must be >= 5 !'); 60 | return; 61 | } 62 | 63 | $user = $this->userRepository->findOneBy(['username' => $username]); 64 | if ($user instanceof User) { 65 | $output->writeln('Username already taken!'); 66 | return; 67 | } 68 | 69 | /** @var User $user */ 70 | $user = new User($username, $password, [$role]); 71 | $encryptedPassword = $this->passwordEncoder->encodePassword($user, $password); 72 | $user->setPassword($encryptedPassword); 73 | 74 | $entityManager = $this->userRepository->getEm(); 75 | $entityManager->persist($user); 76 | $entityManager->flush(); 77 | 78 | $output->writeln('User successfully generated!'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /api/src/Entity/User.php: -------------------------------------------------------------------------------- 1 | id = Uuid::uuid4(); 69 | $this->username = $username; 70 | $this->password = $password; 71 | $this->enabled = $enabled; 72 | $this->roles = $roles; 73 | } 74 | 75 | public function getId(): Uuid 76 | { 77 | return $this->id; 78 | } 79 | 80 | public function getUsername(): string 81 | { 82 | return $this->username; 83 | } 84 | 85 | public function setUsername(string $username): void 86 | { 87 | $this->username = $username; 88 | } 89 | 90 | public function getPassword(): string 91 | { 92 | return $this->password; 93 | } 94 | 95 | public function setPassword(string $password): void 96 | { 97 | $this->password = $password; 98 | } 99 | 100 | public function isEnabled(): bool 101 | { 102 | return $this->enabled; 103 | } 104 | 105 | public function setEnabled(bool $enabled): void 106 | { 107 | $this->enabled = $enabled; 108 | } 109 | 110 | public function getRoles(): array 111 | { 112 | return $this->roles; 113 | } 114 | 115 | public function setRoles(array $roles): void 116 | { 117 | $this->roles = $roles; 118 | } 119 | 120 | public function getSalt(): string 121 | { 122 | return ''; 123 | } 124 | 125 | public function eraseCredentials() 126 | { 127 | return null; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | # the different stages of this Dockerfile are meant to be built into separate images 2 | # https://docs.docker.com/compose/compose-file/#target 3 | 4 | ARG PHP_VERSION=7.2 5 | ARG NGINX_VERSION=1.15 6 | ARG VARNISH_VERSION=6.0 7 | 8 | FROM php:${PHP_VERSION}-fpm-alpine AS api_platform_php 9 | 10 | # persistent / runtime deps 11 | RUN apk add --no-cache \ 12 | acl \ 13 | file \ 14 | gettext \ 15 | git \ 16 | ; 17 | 18 | ARG APCU_VERSION=5.1.16 19 | RUN set -eux; \ 20 | apk add --no-cache --virtual .build-deps \ 21 | $PHPIZE_DEPS \ 22 | icu-dev \ 23 | libzip-dev \ 24 | postgresql-dev \ 25 | zlib-dev \ 26 | ; \ 27 | \ 28 | docker-php-ext-configure zip --with-libzip; \ 29 | docker-php-ext-install -j$(nproc) \ 30 | intl \ 31 | pdo_pgsql \ 32 | zip \ 33 | ; \ 34 | pecl install \ 35 | apcu-${APCU_VERSION} \ 36 | ; \ 37 | pecl clear-cache; \ 38 | docker-php-ext-enable \ 39 | apcu \ 40 | opcache \ 41 | ; \ 42 | \ 43 | runDeps="$( \ 44 | scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ 45 | | tr ',' '\n' \ 46 | | sort -u \ 47 | | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ 48 | )"; \ 49 | apk add --no-cache --virtual .api-phpexts-rundeps $runDeps; \ 50 | \ 51 | apk del .build-deps 52 | 53 | COPY --from=composer:latest /usr/bin/composer /usr/bin/composer 54 | RUN ln -s $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini 55 | COPY docker/php/conf.d/api-platform.ini $PHP_INI_DIR/conf.d/api-platform.ini 56 | 57 | # https://getcomposer.org/doc/03-cli.md#composer-allow-superuser 58 | ENV COMPOSER_ALLOW_SUPERUSER=1 59 | RUN set -eux; \ 60 | composer global require "hirak/prestissimo:^0.3" --prefer-dist --no-progress --no-suggest --classmap-authoritative; \ 61 | composer clear-cache 62 | ENV PATH="${PATH}:/root/.composer/vendor/bin" 63 | 64 | WORKDIR /srv/api 65 | 66 | # build for production 67 | ARG APP_ENV=prod 68 | 69 | # prevent the reinstallation of vendors at every changes in the source code 70 | COPY composer.json composer.lock symfony.lock ./ 71 | # do not use .env files in production 72 | RUN echo ' .env.local.php 73 | RUN set -eux; \ 74 | composer install --prefer-dist --no-dev --no-autoloader --no-scripts --no-progress --no-suggest; \ 75 | composer clear-cache 76 | 77 | # copy only specifically what we need 78 | COPY bin bin/ 79 | COPY config config/ 80 | COPY public public/ 81 | COPY src src/ 82 | 83 | RUN set -eux; \ 84 | mkdir -p var/cache var/log; \ 85 | composer dump-autoload --classmap-authoritative --no-dev; \ 86 | composer run-script --no-dev post-install-cmd; \ 87 | chmod +x bin/console; sync 88 | VOLUME /srv/api/var 89 | 90 | COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint 91 | RUN chmod +x /usr/local/bin/docker-entrypoint 92 | 93 | ENTRYPOINT ["docker-entrypoint"] 94 | CMD ["php-fpm"] 95 | 96 | FROM nginx:${NGINX_VERSION}-alpine AS api_platform_nginx 97 | 98 | COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf 99 | 100 | WORKDIR /srv/api 101 | 102 | COPY --from=api_platform_php /srv/api/public public/ 103 | 104 | FROM cooptilleuls/varnish:${VARNISH_VERSION}-alpine AS api_platform_varnish 105 | 106 | COPY docker/varnish/conf/default.vcl /usr/local/etc/varnish/default.vcl 107 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | php: 5 | image: ${CONTAINER_REGISTRY_BASE}/php 6 | build: 7 | context: ./api 8 | target: api_platform_php 9 | cache_from: 10 | - ${CONTAINER_REGISTRY_BASE}/php 11 | - ${CONTAINER_REGISTRY_BASE}/nginx 12 | - ${CONTAINER_REGISTRY_BASE}/varnish 13 | depends_on: 14 | - db 15 | # Comment out these volumes in production 16 | volumes: 17 | - ./api:/srv/api:rw,cached 18 | # If you develop on Linux, uncomment the following line to use a bind-mounted host directory instead 19 | # - ./api/var:/srv/api/var:rw 20 | 21 | api: 22 | image: ${CONTAINER_REGISTRY_BASE}/nginx 23 | build: 24 | context: ./api 25 | target: api_platform_nginx 26 | cache_from: 27 | - ${CONTAINER_REGISTRY_BASE}/php 28 | - ${CONTAINER_REGISTRY_BASE}/nginx 29 | - ${CONTAINER_REGISTRY_BASE}/varnish 30 | depends_on: 31 | - php 32 | # Comment out this volume in production 33 | volumes: 34 | - ./api/public:/srv/api/public:ro 35 | ports: 36 | - "8080:80" 37 | 38 | cache-proxy: 39 | image: ${CONTAINER_REGISTRY_BASE}/varnish 40 | build: 41 | context: ./api 42 | target: api_platform_varnish 43 | cache_from: 44 | - ${CONTAINER_REGISTRY_BASE}/php 45 | - ${CONTAINER_REGISTRY_BASE}/nginx 46 | - ${CONTAINER_REGISTRY_BASE}/varnish 47 | depends_on: 48 | - api 49 | volumes: 50 | - ./api/docker/varnish/conf:/usr/local/etc/varnish:ro 51 | tmpfs: 52 | - /usr/local/var/varnish:exec 53 | ports: 54 | - "8081:80" 55 | 56 | db: 57 | # In production, you may want to use a managed database service 58 | image: postgres:10-alpine 59 | environment: 60 | - POSTGRES_DB=api 61 | - POSTGRES_USER=api-platform 62 | # You should definitely change the password in production 63 | - POSTGRES_PASSWORD=!ChangeMe! 64 | volumes: 65 | - db-data:/var/lib/postgresql/data:rw 66 | # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! 67 | # - ./docker/db/data:/var/lib/postgresql/data:rw 68 | ports: 69 | - "5432:5432" 70 | 71 | client: 72 | # Use a static website hosting service in production 73 | # See https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment 74 | image: ${CONTAINER_REGISTRY_BASE}/client 75 | build: 76 | context: ./client 77 | cache_from: 78 | - ${CONTAINER_REGISTRY_BASE}/client 79 | env_file: 80 | - ./client/.env 81 | volumes: 82 | - ./client:/usr/src/client:rw,cached 83 | - /usr/src/client/node_modules 84 | ports: 85 | - "80:3000" 86 | 87 | admin: 88 | # Use a static website hosting service in production 89 | # See https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment 90 | image: ${CONTAINER_REGISTRY_BASE}/admin 91 | build: 92 | context: ./admin 93 | cache_from: 94 | - ${CONTAINER_REGISTRY_BASE}/admin 95 | volumes: 96 | - ./admin:/usr/src/admin:rw,cached 97 | - /usr/src/admin/node_modules 98 | ports: 99 | - "81:3000" 100 | 101 | h2-proxy: 102 | # Don't use this proxy in prod 103 | build: 104 | context: ./h2-proxy 105 | depends_on: 106 | - client 107 | - admin 108 | - api 109 | - cache-proxy 110 | ports: 111 | - "443:443" 112 | - "444:444" 113 | - "8443:8443" 114 | - "8444:8444" 115 | 116 | volumes: 117 | db-data: {} 118 | -------------------------------------------------------------------------------- /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 http://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.1/8 is 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 http://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 http://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 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /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 http://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.1/8 is 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 http://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 http://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 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /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 | } 137 | 138 | .other__bloc:not(:last-of-type) { 139 | margin-right: 10px; 140 | } 141 | 142 | .other__bloc h3:not(:first-child) { 143 | margin-top: 15px; 144 | padding-top: 5px; 145 | } 146 | 147 | .other__circle { 148 | width: 110px; 149 | height: 110px; 150 | background-color: #afe5e5; 151 | border-radius: 50%; 152 | margin-right:20px; 153 | } 154 | 155 | .other__circle svg{ 156 | width: 110px; 157 | } 158 | 159 | .buttons__group { 160 | display: inline-flex; 161 | vertical-align: center; 162 | } 163 | 164 | .buttons__group .buttons__or { 165 | width: 4px; 166 | position: relative; 167 | text-align:center; 168 | } 169 | 170 | .buttons__group .buttons__or:before { 171 | content: 'or'; 172 | font-size: 12px; 173 | color: #aaa; 174 | line-height: 18px; 175 | position: absolute; 176 | border-radius: 50%; 177 | top: 50%; 178 | left: 50%; 179 | transform: translate(-50%, -50%); 180 | background-color: white; 181 | width: 18px; 182 | height: 18px; 183 | } 184 | 185 | .buttons__group .other__button:first-child { 186 | border-radius: 5px 0 0 5px; 187 | padding-right: 15px; 188 | } 189 | 190 | .buttons__group .other__button:last-child { 191 | border-radius: 0 5px 5px 0; 192 | padding-left: 15px; 193 | } 194 | 195 | a.other__button { 196 | background-color: #e0e1e2; 197 | font-size: 11px; 198 | color: #686e63; 199 | cursor: pointer; 200 | padding: 5px 10px; 201 | display: inline-block; 202 | transition: all ease 0.2s; 203 | text-transform: uppercase; 204 | } 205 | 206 | .other__button:hover { 207 | background-color: #afe5e5; 208 | color: #339ba5; 209 | } 210 | 211 | .main__button { 212 | display: inline-block; 213 | padding: 10px 50px 10px 10px; 214 | border: 3px solid #339ba5; 215 | font-size: 22px; 216 | color: #339ba5; 217 | text-transform: uppercase; 218 | margin: 15px 0; 219 | overflow: hidden; 220 | transition: all ease 0.3s; 221 | cursor: pointer; 222 | position: relative; 223 | } 224 | 225 | .main__button svg { 226 | position: absolute; 227 | right: 10px; 228 | top: 50%; 229 | transform: translateY(-50%); 230 | transition: transform ease 0.2s; 231 | } 232 | 233 | .main__button:hover { 234 | background-color: #afe5e5; 235 | } 236 | 237 | .main__button:hover svg { 238 | transform: translateY(-50%) rotate(35deg); 239 | } 240 | 241 | /***** HELP *****/ 242 | 243 | .welcome__help { 244 | background-color: white; 245 | box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2); 246 | padding: 10px; 247 | position: fixed; 248 | right: -5px; 249 | top: 50%; 250 | transform: translateY(-50%); 251 | border-radius: 5px; 252 | text-align: center; 253 | } 254 | 255 | .welcome__help h2 { 256 | color: #aaa; 257 | font-size: 12px; 258 | margin: 10px 0; 259 | } 260 | 261 | .help__circle { 262 | width: 36px; 263 | height: 36px; 264 | border-radius: 50%; 265 | border: 2px solid #ccc; 266 | display: block; 267 | margin: 10px auto; 268 | transition: all ease 0.2s; 269 | position:relative; 270 | } 271 | 272 | .help__circle svg { 273 | position:absolute; 274 | left: 50%; 275 | top: 50%; 276 | transform:translate(-50%, -50%); 277 | } 278 | 279 | .help__circle:hover { 280 | border-color: #67cece; 281 | background-color: #afe5e5; 282 | } 283 | 284 | /***** MEDIAS *****/ 285 | 286 | @media (max-width: 1200px) { 287 | .main__aside, 288 | .welcome__help { 289 | display: none; 290 | } 291 | .main__content { 292 | width: 100%; 293 | text-align: center; 294 | padding: 20px; 295 | } 296 | } 297 | 298 | @media (max-width: 600px) { 299 | .welcome__main { 300 | width: calc(100% - 40px); 301 | } 302 | .welcome h1 { 303 | display: none; 304 | } 305 | .welcome__flag, 306 | .main__other { 307 | display: none; 308 | } 309 | .main__content { 310 | padding: 10px; 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /api/symfony.lock: -------------------------------------------------------------------------------- 1 | { 2 | "api-platform/api-pack": { 3 | "version": "v1.0.1" 4 | }, 5 | "api-platform/core": { 6 | "version": "2.1", 7 | "recipe": { 8 | "repo": "github.com/symfony/recipes", 9 | "branch": "master", 10 | "version": "2.1", 11 | "ref": "b50f8aa321cc40c380dda7605aa5cef66f476802" 12 | } 13 | }, 14 | "api-platform/schema-generator": { 15 | "version": "v2.1.0-beta.1" 16 | }, 17 | "composer/semver": { 18 | "version": "1.4.2" 19 | }, 20 | "composer/xdebug-handler": { 21 | "version": "1.1.0" 22 | }, 23 | "doctrine/annotations": { 24 | "version": "1.0", 25 | "recipe": { 26 | "repo": "github.com/symfony/recipes", 27 | "branch": "master", 28 | "version": "1.0", 29 | "ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672" 30 | } 31 | }, 32 | "doctrine/cache": { 33 | "version": "v1.7.1" 34 | }, 35 | "doctrine/collections": { 36 | "version": "v1.5.0" 37 | }, 38 | "doctrine/common": { 39 | "version": "v2.8.1" 40 | }, 41 | "doctrine/dbal": { 42 | "version": "v2.6.3" 43 | }, 44 | "doctrine/doctrine-bundle": { 45 | "version": "1.6", 46 | "recipe": { 47 | "repo": "github.com/symfony/recipes", 48 | "branch": "master", 49 | "version": "1.6", 50 | "ref": "44d3aa7752dd46f77ba11af2297a25e1dedfb4d0" 51 | } 52 | }, 53 | "doctrine/doctrine-cache-bundle": { 54 | "version": "1.3.2" 55 | }, 56 | "doctrine/event-manager": { 57 | "version": "v1.0.0" 58 | }, 59 | "doctrine/inflector": { 60 | "version": "v1.3.0" 61 | }, 62 | "doctrine/instantiator": { 63 | "version": "1.1.0" 64 | }, 65 | "doctrine/lexer": { 66 | "version": "v1.0.1" 67 | }, 68 | "doctrine/orm": { 69 | "version": "v2.6.0" 70 | }, 71 | "doctrine/persistence": { 72 | "version": "v1.0.0" 73 | }, 74 | "doctrine/reflection": { 75 | "version": "v1.0.0" 76 | }, 77 | "easyrdf/easyrdf": { 78 | "version": "0.9.1" 79 | }, 80 | "friendsofphp/php-cs-fixer": { 81 | "version": "2.2", 82 | "recipe": { 83 | "repo": "github.com/symfony/recipes", 84 | "branch": "master", 85 | "version": "2.2", 86 | "ref": "bb31a3bbec00a8fc8aa1c9fbf9b0ef9fc492f93d" 87 | } 88 | }, 89 | "guzzlehttp/guzzle": { 90 | "version": "6.3.0" 91 | }, 92 | "guzzlehttp/promises": { 93 | "version": "v1.3.1" 94 | }, 95 | "guzzlehttp/psr7": { 96 | "version": "1.4.2" 97 | }, 98 | "jdorn/sql-formatter": { 99 | "version": "v1.2.17" 100 | }, 101 | "lcobucci/jwt": { 102 | "version": "3.2.5" 103 | }, 104 | "league/html-to-markdown": { 105 | "version": "4.6.2" 106 | }, 107 | "lexik/jwt-authentication-bundle": { 108 | "version": "2.5", 109 | "recipe": { 110 | "repo": "github.com/symfony/recipes", 111 | "branch": "master", 112 | "version": "2.5", 113 | "ref": "509b1542f3180a1cfe248ade8b7bf5092c2d4127" 114 | } 115 | }, 116 | "namshi/jose": { 117 | "version": "7.2.3" 118 | }, 119 | "nelmio/cors-bundle": { 120 | "version": "1.5", 121 | "recipe": { 122 | "repo": "github.com/symfony/recipes", 123 | "branch": "master", 124 | "version": "1.5", 125 | "ref": "7b6cbc842f8cd3d550815247d12294f6f304a8c4" 126 | } 127 | }, 128 | "php-cs-fixer/diff": { 129 | "version": "v1.2.0" 130 | }, 131 | "phpdocumentor/reflection-common": { 132 | "version": "1.0.1" 133 | }, 134 | "phpdocumentor/reflection-docblock": { 135 | "version": "4.2.0" 136 | }, 137 | "phpdocumentor/type-resolver": { 138 | "version": "0.4.0" 139 | }, 140 | "psr/cache": { 141 | "version": "1.0.1" 142 | }, 143 | "psr/container": { 144 | "version": "1.0.0" 145 | }, 146 | "psr/http-message": { 147 | "version": "1.0.1" 148 | }, 149 | "psr/log": { 150 | "version": "1.0.2" 151 | }, 152 | "psr/simple-cache": { 153 | "version": "1.0.0" 154 | }, 155 | "ralouphie/getallheaders": { 156 | "version": "2.0.5" 157 | }, 158 | "ramsey/uuid": { 159 | "version": "3.8.0" 160 | }, 161 | "ramsey/uuid-doctrine": { 162 | "version": "1.3", 163 | "recipe": { 164 | "repo": "github.com/symfony/recipes-contrib", 165 | "branch": "master", 166 | "version": "1.3", 167 | "ref": "471aed0fbf5620b8d7f92b7a5ebbbf6c0945c27a" 168 | } 169 | }, 170 | "symfony/asset": { 171 | "version": "v4.0.3" 172 | }, 173 | "symfony/cache": { 174 | "version": "v4.0.3" 175 | }, 176 | "symfony/config": { 177 | "version": "v4.0.3" 178 | }, 179 | "symfony/console": { 180 | "version": "3.3", 181 | "recipe": { 182 | "repo": "github.com/symfony/recipes", 183 | "branch": "master", 184 | "version": "3.3", 185 | "ref": "9f94d3ea453cd8a3b95db7f82592d7344fe3a76a" 186 | } 187 | }, 188 | "symfony/contracts": { 189 | "version": "v1.0.2" 190 | }, 191 | "symfony/debug": { 192 | "version": "v4.0.3" 193 | }, 194 | "symfony/dependency-injection": { 195 | "version": "v4.0.3" 196 | }, 197 | "symfony/doctrine-bridge": { 198 | "version": "v4.0.3" 199 | }, 200 | "symfony/dotenv": { 201 | "version": "v4.2.1" 202 | }, 203 | "symfony/event-dispatcher": { 204 | "version": "v4.0.3" 205 | }, 206 | "symfony/expression-language": { 207 | "version": "v4.0.3" 208 | }, 209 | "symfony/filesystem": { 210 | "version": "v4.0.3" 211 | }, 212 | "symfony/finder": { 213 | "version": "v4.0.3" 214 | }, 215 | "symfony/flex": { 216 | "version": "1.0", 217 | "recipe": { 218 | "repo": "github.com/symfony/recipes", 219 | "branch": "master", 220 | "version": "1.0", 221 | "ref": "cc1afd81841db36fbef982fe56b48ade6716fac4" 222 | } 223 | }, 224 | "symfony/framework-bundle": { 225 | "version": "3.3", 226 | "recipe": { 227 | "repo": "github.com/symfony/recipes", 228 | "branch": "master", 229 | "version": "3.3", 230 | "ref": "137a14eeb6b3f5370e7147af8aff6518504f50c7" 231 | } 232 | }, 233 | "symfony/http-foundation": { 234 | "version": "v4.0.3" 235 | }, 236 | "symfony/http-kernel": { 237 | "version": "v4.0.3" 238 | }, 239 | "symfony/inflector": { 240 | "version": "v4.0.3" 241 | }, 242 | "symfony/options-resolver": { 243 | "version": "v4.0.3" 244 | }, 245 | "symfony/phpunit-bridge": { 246 | "version": "4.1", 247 | "recipe": { 248 | "repo": "github.com/symfony/recipes", 249 | "branch": "master", 250 | "version": "4.1", 251 | "ref": "0e548dd90adba18fabd4ef419b14d361fe4d6c74" 252 | } 253 | }, 254 | "symfony/polyfill-ctype": { 255 | "version": "v1.8.0" 256 | }, 257 | "symfony/polyfill-mbstring": { 258 | "version": "v1.6.0" 259 | }, 260 | "symfony/polyfill-php72": { 261 | "version": "v1.6.0" 262 | }, 263 | "symfony/process": { 264 | "version": "v4.0.3" 265 | }, 266 | "symfony/profiler-pack": { 267 | "version": "v1.0.3" 268 | }, 269 | "symfony/property-access": { 270 | "version": "v4.0.3" 271 | }, 272 | "symfony/property-info": { 273 | "version": "v4.0.3" 274 | }, 275 | "symfony/routing": { 276 | "version": "4.0", 277 | "recipe": { 278 | "repo": "github.com/symfony/recipes", 279 | "branch": "master", 280 | "version": "4.0", 281 | "ref": "cda8b550123383d25827705d05a42acf6819fe4e" 282 | } 283 | }, 284 | "symfony/security-bundle": { 285 | "version": "3.3", 286 | "recipe": { 287 | "repo": "github.com/symfony/recipes", 288 | "branch": "master", 289 | "version": "3.3", 290 | "ref": "34eb245ce4194e7ae713558084180e2c532e7afd" 291 | } 292 | }, 293 | "symfony/security-core": { 294 | "version": "v4.2.1" 295 | }, 296 | "symfony/security-csrf": { 297 | "version": "v4.2.1" 298 | }, 299 | "symfony/security-guard": { 300 | "version": "v4.2.1" 301 | }, 302 | "symfony/security-http": { 303 | "version": "v4.2.1" 304 | }, 305 | "symfony/serializer": { 306 | "version": "v4.0.3" 307 | }, 308 | "symfony/stopwatch": { 309 | "version": "v4.0.3" 310 | }, 311 | "symfony/twig-bridge": { 312 | "version": "v4.0.3" 313 | }, 314 | "symfony/twig-bundle": { 315 | "version": "3.3", 316 | "recipe": { 317 | "repo": "github.com/symfony/recipes", 318 | "branch": "master", 319 | "version": "3.3", 320 | "ref": "f75ac166398e107796ca94cc57fa1edaa06ec47f" 321 | } 322 | }, 323 | "symfony/validator": { 324 | "version": "v4.0.3" 325 | }, 326 | "symfony/var-dumper": { 327 | "version": "v4.1.1" 328 | }, 329 | "symfony/var-exporter": { 330 | "version": "v4.2.1" 331 | }, 332 | "symfony/web-profiler-bundle": { 333 | "version": "3.3", 334 | "recipe": { 335 | "repo": "github.com/symfony/recipes", 336 | "branch": "master", 337 | "version": "3.3", 338 | "ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6" 339 | } 340 | }, 341 | "symfony/yaml": { 342 | "version": "v4.0.3" 343 | }, 344 | "twig/twig": { 345 | "version": "v2.4.4" 346 | }, 347 | "webmozart/assert": { 348 | "version": "1.2.0" 349 | }, 350 | "willdurand/negotiation": { 351 | "version": "v2.3.1" 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /client/src/Welcome.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './welcome.css'; 3 | 4 | const Welcome = () => ( 5 |
6 |
7 | 12 | 13 | 14 | 19 | 20 | 21 |
22 |
23 |
24 |
25 | 26 |
27 |
28 |

29 | Welcome to API Platform! 30 |

31 |
32 |

33 | This container will host your Progressive Web App{' '} 34 | ({'http:' === document.location.protocol ? HTTPS : HTTP}). 35 | Learn how to create your first API and generate a PWA: 36 |

37 | 43 | Get started 44 | 45 |
46 |
47 |

Available services:

48 |
49 |
50 | 51 |
52 |
53 |

API

54 | 58 |

Cached API

59 | 63 |
64 |
65 |
66 |
67 | 68 |
69 |
70 |

Admin

71 | 75 |
76 |
77 |
78 |
79 |
80 |
81 |

Need help?

82 | 87 | 92 |
93 |
94 | ); 95 | 96 | const ButtonsGroup = ({ httpLink, httpsLink }) => ( 97 |
98 | 104 | http 105 | 106 |
107 | 113 | https 114 | 115 |
116 | ); 117 | 118 | const HelpButton = ({ Image, url, title }) => ( 119 | 126 | 127 | 128 | ); 129 | 130 | export default Welcome; 131 | 132 | const Logo = () => ( 133 | 139 | 146 | 150 | 154 | 158 | 162 | 166 | 170 | 174 | 178 | 182 | 186 | 190 | 191 | 192 | 193 | 194 | 195 | 199 | 203 | 207 | 211 | 215 | 219 | 223 | 227 | 231 | 235 | 239 | 243 | 247 | 251 | 255 | 259 | 263 | 267 | 274 | 278 | 279 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | ); 293 | 294 | const Flag = () => ( 295 | 302 | 309 | 310 | 314 | 318 | 322 | 326 | 330 | 334 | 338 | 342 | 346 | 350 | 354 | 358 | 362 | 366 | 370 | 374 | 378 | 382 | 386 | 390 | 394 | 398 | 402 | 406 | 410 | 414 | 418 | 422 | 423 | ); 424 | 425 | const SpiderWelcome = () => ( 426 | 432 | 439 | 443 | 447 | 451 | 455 | 459 | 463 | 467 | 471 | 475 | 479 | 483 | 487 | 491 | 495 | 499 | 506 | 510 | 517 | 518 | 525 | 529 | 530 | 531 | 539 | 547 | 551 | 555 | 559 | 563 | 567 | 571 | 575 | 579 | 583 | 587 | 591 | 592 | ); 593 | 594 | const Api = () => ( 595 | 601 | 608 | 609 | 613 | 617 | 621 | 625 | 629 | 633 | 637 | 641 | 645 | 649 | 653 | 657 | 661 | 665 | 669 | 673 | 677 | 681 | 685 | 689 | 693 | 701 | 709 | 713 | 721 | 725 | 733 | 741 | 745 | 753 | 757 | 761 | 765 | 769 | 773 | 781 | 785 | 793 | 801 | 809 | 813 | 821 | 829 | 833 | 837 | 841 | 845 | 849 | 853 | 857 | 861 | 862 | ); 863 | 864 | const Admin = () => ( 865 | 871 | 878 | 882 | 886 | 890 | 894 | 898 | 902 | 906 | 910 | 914 | 918 | 922 | 926 | 930 | 934 | 938 | 942 | 946 | 950 | 954 | 958 | 962 | 966 | 970 | 974 | 978 | 982 | 986 | 990 | 994 | 998 | 1002 | 1006 | 1010 | 1014 | 1018 | 1022 | 1026 | 1030 | 1034 | 1038 | 1046 | 1054 | 1061 | 1068 | 1075 | 1082 | 1089 | 1096 | 1103 | 1110 | 1117 | 1124 | 1131 | 1138 | 1145 | 1152 | 1159 | 1166 | 1173 | 1180 | 1187 | 1194 | 1201 | 1205 | 1212 | 1216 | 1223 | 1230 | 1237 | 1244 | 1251 | 1258 | 1265 | 1272 | 1279 | 1286 | 1293 | 1300 | 1307 | 1314 | 1321 | 1328 | 1335 | 1336 | 1343 | 1350 | 1357 | 1364 | 1371 | 1378 | 1385 | 1392 | 1399 | 1406 | 1413 | 1420 | 1427 | 1434 | 1441 | 1448 | 1449 | 1456 | 1463 | 1470 | 1477 | 1484 | 1491 | 1498 | 1505 | 1512 | 1519 | 1526 | 1533 | 1540 | 1547 | 1554 | 1561 | 1568 | 1575 | 1582 | 1583 | 1584 | 1588 | 1589 | 1593 | 1594 | 1598 | 1602 | 1603 | 1607 | 1611 | 1615 | 1616 | 1620 | 1624 | 1628 | 1632 | 1636 | 1640 | 1644 | 1648 | 1652 | 1656 | 1660 | 1664 | 1668 | 1672 | 1676 | 1680 | 1684 | 1688 | 1692 | 1696 | 1704 | 1712 | 1713 | ); 1714 | 1715 | const Arrow = () => ( 1716 | 1722 | 1729 | 1730 | 1731 | 1732 | 1736 | 1737 | ); 1738 | 1739 | const Sto = () => ( 1740 | 1746 | 1752 | 1756 | 1761 | 1762 | ); 1763 | 1764 | const Slack = () => ( 1765 | 1771 | 1775 | 1779 | 1783 | 1784 | 1788 | 1792 | 1796 | 1800 | 1801 | ); 1802 | --------------------------------------------------------------------------------