This commit is contained in:
Pitchaya Boonsarngsuk
2019-07-05 16:54:32 +01:00
commit c3d3dcbff7
96 changed files with 32248 additions and 0 deletions

22
api/.dockerignore Normal file
View File

@@ -0,0 +1,22 @@
**/*.log
**/*.md
**/*.php~
**/._*
**/.dockerignore
**/.DS_Store
**/.git/
**/.gitattributes
**/.gitignore
**/.gitmodules
**/Dockerfile
**/Thumbs.db
.editorconfig
.env.*
.php_cs.cache
bin/*
!bin/console
docker/db/data/
helm/
public/bundles/
var/
vendor/

40
api/.env Normal file
View File

@@ -0,0 +1,40 @@
# In all environments, the following files are loaded if they exist,
# the later taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
MERCURE_SUBSCRIBE_URL=http://localhost:1337/hub
VARNISH_URL=http://cache-proxy
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=!ChangeMe!
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
TRUSTED_HOSTS='^localhost|api$'
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# Configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=postgres://api-platform:!ChangeMe!@db/api
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://localhost(:[0-9]+)?$
###< nelmio/cors-bundle ###
###> symfony/mercure-bundle ###
MERCURE_PUBLISH_URL=http://mercure/hub
MERCURE_JWT_SECRET=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyJmb28iLCJiYXIiXSwicHVibGlzaCI6WyJmb28iXX19.LRLvirgONK13JgacQ_VbcjySbVhkSmHy3IznH3tA9PM
###< symfony/mercure-bundle ###

15
api/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
/helm/api/charts
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
###> friendsofphp/php-cs-fixer ###
/.php_cs
/.php_cs.cache
###< friendsofphp/php-cs-fixer ###

14
api/.php_cs.dist Normal file
View File

@@ -0,0 +1,14 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude('var')
;
return PhpCsFixer\Config::create()
->setRules([
'@Symfony' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;

121
api/Dockerfile Normal file
View File

@@ -0,0 +1,121 @@
# the different stages of this Dockerfile are meant to be built into separate images
# https://docs.docker.com/develop/develop-images/multistage-build/#stop-at-a-specific-build-stage
# https://docs.docker.com/compose/compose-file/#target
# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION=7.3
ARG NGINX_VERSION=1.15
ARG VARNISH_VERSION=6.2
# "php" stage
FROM php:${PHP_VERSION}-fpm-alpine AS api_platform_php
# persistent / runtime deps
RUN apk add --no-cache \
acl \
file \
gettext \
git \
;
ARG APCU_VERSION=5.1.17
RUN set -eux; \
apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
icu-dev \
libzip-dev \
postgresql-dev \
zlib-dev \
; \
\
docker-php-ext-configure zip --with-libzip; \
docker-php-ext-install -j$(nproc) \
intl \
pdo_pgsql \
zip \
; \
pecl install \
apcu-${APCU_VERSION} \
; \
pecl clear-cache; \
docker-php-ext-enable \
apcu \
opcache \
; \
\
runDeps="$( \
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \
| tr ',' '\n' \
| sort -u \
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
)"; \
apk add --no-cache --virtual .api-phpexts-rundeps $runDeps; \
\
apk del .build-deps
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
RUN ln -s $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
COPY docker/php/conf.d/api-platform.ini $PHP_INI_DIR/conf.d/api-platform.ini
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
# install Symfony Flex globally to speed up download of Composer packages (parallelized prefetching)
RUN set -eux; \
composer global require "symfony/flex" --prefer-dist --no-progress --no-suggest --classmap-authoritative; \
composer clear-cache
ENV PATH="${PATH}:/root/.composer/vendor/bin"
WORKDIR /srv/api
# build for production
ARG APP_ENV=prod
# prevent the reinstallation of vendors at every changes in the source code
COPY composer.json composer.lock symfony.lock ./
RUN set -eux; \
composer install --prefer-dist --no-dev --no-scripts --no-progress --no-suggest; \
composer clear-cache
# do not use .env files in production
COPY .env ./
RUN composer dump-env prod; \
rm .env
# copy only specifically what we need
COPY bin bin/
COPY config config/
COPY public public/
COPY src src/
RUN set -eux; \
mkdir -p var/cache var/log; \
composer dump-autoload --classmap-authoritative --no-dev; \
composer run-script --no-dev post-install-cmd; \
chmod +x bin/console; sync
VOLUME /srv/api/var
COPY docker/php/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint
ENTRYPOINT ["docker-entrypoint"]
CMD ["php-fpm"]
# "nginx" stage
# depends on the "php" stage above
FROM nginx:${NGINX_VERSION}-alpine AS api_platform_nginx
COPY docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
WORKDIR /srv/api
COPY --from=api_platform_php /srv/api/public public/
# "varnish" stage
# does not depend on any of the above stages, but placed here to keep everything in one Dockerfile
FROM cooptilleuls/varnish:${VARNISH_VERSION}-alpine AS api_platform_varnish
COPY docker/varnish/conf/default.vcl /usr/local/etc/varnish/default.vcl

42
api/bin/console Executable file
View File

@@ -0,0 +1,42 @@
#!/usr/bin/env php
<?php
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
if (false === in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.\PHP_SAPI.' SAPI'.\PHP_EOL;
}
set_time_limit(0);
require dirname(__DIR__).'/vendor/autoload.php';
if (!class_exists(Application::class)) {
throw new RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.');
}
$input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}
if ($input->hasParameterOption('--no-debug', true)) {
putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
}
require dirname(__DIR__).'/config/bootstrap.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
if (class_exists(Debug::class)) {
Debug::enable();
}
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new Application($kernel);
$application->run($input);

67
api/composer.json Normal file
View File

@@ -0,0 +1,67 @@
{
"type": "project",
"license": "proprietary",
"require": {
"php": "^7.1.3",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/api-pack": "^1.1",
"guzzlehttp/guzzle": "^6.3",
"symfony/console": "4.3.*",
"symfony/dotenv": "4.3.*",
"symfony/flex": "^1.1",
"symfony/framework-bundle": "4.3.*",
"symfony/mercure-bundle": "*",
"symfony/yaml": "4.3.*"
},
"require-dev": {
"api-platform/schema-generator": "^2.1",
"symfony/maker-bundle": "^1.11",
"symfony/profiler-pack": "^1.0"
},
"config": {
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"paragonie/random_compat": "2.*",
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php71": "*",
"symfony/polyfill-php70": "*",
"symfony/polyfill-php56": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "4.3.*"
}
}
}

5874
api/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

23
api/config/bootstrap.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
// Load cached env vars if the .env.local.php file exists
// Run "composer dump-env prod" to create it (requires symfony/flex >=1.2)
if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) {
foreach ($env as $k => $v) {
$_ENV[$k] = $_ENV[$k] ?? (isset($_SERVER[$k]) && 0 !== strpos($k, 'HTTP_') ? $_SERVER[$k] : $v);
}
} elseif (!class_exists(Dotenv::class)) {
throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.');
} else {
// load all the .env files
(new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
}
$_SERVER += $_ENV;
$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';

14
api/config/bundles.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
];

View File

@@ -0,0 +1,24 @@
parameters:
# Adds a fallback VARNISH_URL if the env var is not set.
# This allows you to run cache:warmup even if your
# environment variables are not available yet.
# You should not need to change this value.
env(VARNISH_URL): ''
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
title: Hello API Platform
version: 1.0.0
# Varnish integration, remove if unwanted
http_cache:
invalidation:
enabled: true
varnish_urls: ['%env(VARNISH_URL)%']
max_age: 0
shared_max_age: 3600
vary: ['Content-Type', 'Authorization']
public: true
# Mercure integration, remove if unwanted
mercure:
hub_url: '%env(MERCURE_SUBSCRIBE_URL)%'

View File

@@ -0,0 +1,19 @@
framework:
cache:
# Put the unique name of your app here: the prefix seed
# is used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name
# The app cache caches to the filesystem by default.
# Other options include:
# Redis
#app: cache.adapter.redis
#default_redis_provider: redis://localhost
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
#app: cache.adapter.apcu
# Namespaced pools use the above "app" backend by default
#pools:
#my.dedicated.cache: ~

View File

@@ -0,0 +1,3 @@
framework:
router:
strict_requirements: true

View File

@@ -0,0 +1,6 @@
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler: { only_exceptions: false }

View File

@@ -0,0 +1,18 @@
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_pgsql'
server_version: '10'
url: '%env(resolve:DATABASE_URL)%'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App

View File

@@ -0,0 +1,16 @@
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
#http_method_override: true
# Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support.
session:
handler_id: ~
cookie_secure: auto
cookie_samesite: lax
#esi: true
#fragments: true
php_errors:
log: true

View File

@@ -0,0 +1,5 @@
mercure:
hubs:
default:
url: '%env(MERCURE_PUBLISH_URL)%'
jwt: '%env(MERCURE_JWT_SECRET)%'

View File

@@ -0,0 +1,10 @@
nelmio_cors:
defaults:
origin_regex: true
allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
allow_headers: ['Content-Type', 'Authorization']
expose_headers: ['Link']
max_age: 3600
paths:
'^/': ~

View File

@@ -0,0 +1,32 @@
doctrine:
orm:
auto_generate_proxy_classes: false
metadata_cache_driver:
type: service
id: doctrine.system_cache_provider
query_cache_driver:
type: service
id: doctrine.system_cache_provider
result_cache_driver:
type: service
id: doctrine.result_cache_provider
services:
doctrine.result_cache_provider:
class: Symfony\Component\Cache\DoctrineProvider
public: false
arguments:
- '@doctrine.result_cache_pool'
doctrine.system_cache_provider:
class: Symfony\Component\Cache\DoctrineProvider
public: false
arguments:
- '@doctrine.system_cache_pool'
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

View File

@@ -0,0 +1,4 @@
framework:
router:
strict_requirements: ~
utf8: true

View File

@@ -0,0 +1,22 @@
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
in_memory: { memory: ~ }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }

View File

@@ -0,0 +1,4 @@
framework:
test: true
session:
storage_id: session.storage.mock_file

View File

@@ -0,0 +1,3 @@
framework:
router:
strict_requirements: true

View File

@@ -0,0 +1,3 @@
framework:
validation:
not_compromised_password: false

View File

@@ -0,0 +1,6 @@
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

View File

@@ -0,0 +1,4 @@
twig:
default_path: '%kernel.project_dir%/templates'
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'

View File

@@ -0,0 +1,8 @@
framework:
validation:
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []

3
api/config/routes.yaml Normal file
View File

@@ -0,0 +1,3 @@
#index:
# path: /
# controller: App\Controller\DefaultController::index

View File

@@ -0,0 +1,3 @@
controllers:
resource: ../../src/Controller/
type: annotation

View File

@@ -0,0 +1,3 @@
api_platform:
resource: .
type: api_platform

View File

@@ -0,0 +1,3 @@
_errors:
resource: '@TwigBundle/Resources/config/routing/errors.xml'
prefix: /_error

View File

@@ -0,0 +1,7 @@
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

27
api/config/services.yaml Normal file
View File

@@ -0,0 +1,27 @@
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones

View File

@@ -0,0 +1,38 @@
server {
root /srv/api/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
# Comment the next line and uncomment the next to enable dynamic resolution (incompatible with Kubernetes)
fastcgi_pass php:9000;
#resolver 127.0.0.11;
#set $upstream_host php;
#fastcgi_pass $upstream_host:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
}

View File

@@ -0,0 +1,11 @@
apc.enable_cli = 1
date.timezone = UTC
session.auto_start = Off
short_open_tag = Off
# http://symfony.com/doc/current/performance.html
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.memory_consumption = 256
realpath_cache_size = 4096K
realpath_cache_ttl = 600

View File

@@ -0,0 +1,34 @@
#!/bin/sh
set -e
# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
set -- php-fpm "$@"
fi
if [ "$1" = 'php-fpm' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-production"
if [ "$APP_ENV" != 'prod' ]; then
PHP_INI_RECOMMENDED="$PHP_INI_DIR/php.ini-development"
fi
ln -sf "$PHP_INI_RECOMMENDED" "$PHP_INI_DIR/php.ini"
mkdir -p var/cache var/log
setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
if [ "$APP_ENV" != 'prod' ]; then
composer install --prefer-dist --no-progress --no-suggest --no-interaction
fi
echo "Waiting for db to be ready..."
until bin/console doctrine:query:sql "SELECT 1" > /dev/null 2>&1; do
sleep 1
done
if [ "$APP_ENV" != 'prod' ]; then
bin/console doctrine:schema:update --force --no-interaction
fi
fi
exec docker-php-entrypoint "$@"

View File

@@ -0,0 +1,95 @@
vcl 4.0;
import std;
backend default {
.host = "api";
.port = "80";
# Health check
#.probe = {
# .url = "/";
# .timeout = 5s;
# .interval = 10s;
# .window = 5;
# .threshold = 3;
#}
}
# Hosts allowed to send BAN requests
acl invalidators {
"localhost";
"php";
# local Kubernetes network
"10.0.0.0"/8;
"172.16.0.0"/12;
"192.168.0.0"/16;
}
sub vcl_recv {
if (req.restarts > 0) {
set req.hash_always_miss = true;
}
# Remove the "Forwarded" HTTP header if exists (security)
unset req.http.forwarded;
# To allow API Platform to ban by cache tags
if (req.method == "BAN") {
if (client.ip !~ invalidators) {
return (synth(405, "Not allowed"));
}
if (req.http.ApiPlatform-Ban-Regex) {
ban("obj.http.Cache-Tags ~ " + req.http.ApiPlatform-Ban-Regex);
return (synth(200, "Ban added"));
}
return (synth(400, "ApiPlatform-Ban-Regex HTTP header must be set."));
}
# For health checks
if (req.method == "GET" && req.url == "/healthz") {
return (synth(200, "OK"));
}
}
sub vcl_hit {
if (obj.ttl >= 0s) {
# A pure unadulterated hit, deliver it
return (deliver);
}
if (std.healthy(req.backend_hint)) {
# The backend is healthy
# Fetch the object from the backend
return (restart);
}
# No fresh object and the backend is not healthy
if (obj.ttl + obj.grace > 0s) {
# Deliver graced object
# Automatically triggers a background fetch
return (deliver);
}
# No valid object to deliver
# No healthy backend to handle request
# Return error
return (synth(503, "API is down"));
}
sub vcl_deliver {
# Don't send cache tags related headers to the client
unset resp.http.url;
# Comment the following line to send the "Cache-Tags" header to the client (e.g. to use CloudFlare cache tags)
unset resp.http.Cache-Tags;
}
sub vcl_backend_response {
# Ban lurker friendly header
set beresp.http.url = bereq.url;
# Add a grace in case the backend is down
set beresp.grace = 1h;
}

22
api/helm/api/.helmignore Normal file
View File

@@ -0,0 +1,22 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

7
api/helm/api/Chart.yaml Normal file
View File

@@ -0,0 +1,7 @@
apiVersion: v1
appVersion: 0.1.0
description: A Helm chart for an API Platform API
name: api
version: 0.1.0
home: https://api-platform.com
icon: https://api-platform.com/logo-250x250.png

View File

@@ -0,0 +1,9 @@
dependencies:
- name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/
version: 3.9.5
- name: mercure
repository: https://kubernetes-charts.storage.googleapis.com/
version: 1.0.0
digest: sha256:6466d376987cc08b70fe36945dd93474836aab05edc155955951bcb0774a0896
generated: 2019-03-26T18:14:32.4074+01:00

View File

@@ -0,0 +1,9 @@
dependencies:
- name: postgresql
version: ~3.9.0
repository: https://kubernetes-charts.storage.googleapis.com/
condition: postgresql.enabled
- name: mercure
version: ~1.0.0
repository: https://kubernetes-charts.storage.googleapis.com/
condition: mercure.enabled

View File

@@ -0,0 +1,5 @@
API deployed.
To get the ingress's IP:
kubectl --namespace {{ .Release.Namespace }} get ingress -o jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}"

View File

@@ -0,0 +1,27 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "postgresql.fullname" -}}
{{- printf "%s-%s" .Release.Name "postgresql" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname" . }}
labels:
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
env: {{ .Values.php.env | quote }}
debug: {{ .Values.php.debug | quote }}
cors-allow-origin: {{ .Values.php.corsAllowOrigin | quote }}
varnish-url: {{ if .Values.varnish.enabled }}http://varnish{{ else }}{{ .Values.varnish.url | quote }}{{ end }}
trusted-hosts: {{ .Values.php.trustedHosts | quote }}
trusted-proxies: {{ join "," .Values.php.trustedProxies }}
mercure-publish-url: {{ .Values.mercure.publishUrl | quote }}
mercure-subscribe-url: {{ .Values.mercure.subscribeUrl | quote }}

View File

@@ -0,0 +1,35 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "fullname" . }}
labels:
app.kubernetes.io/name: {{ include "name" . }}-ingress
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ .host | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
- path: /*
backend:
serviceName: {{ .serviceName }}
servicePort: {{ .servicePort | default 80 }}
{{- end }}

View File

@@ -0,0 +1,33 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}-nginx
labels:
app.kubernetes.io/name: {{ include "name" . }}-nginx
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.nginx.replicaCount }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "name" . }}-nginx
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
containers:
- name: {{ .Chart.Name }}-nginx
image: "{{ .Values.nginx.repository }}:{{ .Values.nginx.tag }}"
imagePullPolicy: {{ .Values.nginx.pullPolicy }}
ports:
- containerPort: 80
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: api
labels:
app.kubernetes.io/name: {{ include "name" . }}-nginx
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app.kubernetes.io/name: {{ include "name" . }}-nginx
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,89 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}-php
labels:
app.kubernetes.io/name: {{ include "name" . }}-php
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.php.replicaCount }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "name" . }}-php
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
containers:
- name: {{ .Chart.Name }}-php
image: "{{ .Values.php.repository }}:{{ .Values.php.tag }}"
imagePullPolicy: {{ .Values.php.pullPolicy }}
ports:
- containerPort: 9000
env:
- name: TRUSTED_HOSTS
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: trusted-hosts
- name: TRUSTED_PROXIES
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: trusted-proxies
- name: APP_ENV
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: env
- name: APP_DEBUG
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: debug
- name: CORS_ALLOW_ORIGIN
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: cors-allow-origin
- name: VARNISH_URL
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: varnish-url
- name: APP_SECRET
valueFrom:
secretKeyRef:
name: {{ template "fullname" . }}
key: secret
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ template "fullname" . }}
key: database-url
- name: MERCURE_PUBLISH_URL
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: mercure-publish-url
- name: MERCURE_SUBSCRIBE_URL
valueFrom:
configMapKeyRef:
name: {{ template "fullname" . }}
key: mercure-subscribe-url
- name: MERCURE_JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ template "fullname" . }}
key: mercure-jwt-secret
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: php
labels:
app.kubernetes.io/name: {{ include "name" . }}-php
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: ClusterIP
ports:
- port: 9000
selector:
app.kubernetes.io/name: {{ include "name" . }}-php
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,20 @@
{{- $postgresqlServiceName := include "postgresql.fullname" . -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ template "fullname" . }}
labels:
app.kubernetes.io/name: {{ include "name" . }}
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
type: Opaque
data:
{{ if .Values.postgresql.enabled }}
database-url: {{ printf "pgsql://%s:%s@%s/%s?serverVersion=9.6" .Values.postgresql.postgresqlUsername .Values.postgresql.postgresqlPassword $postgresqlServiceName .Values.postgresql.postgresqlDatabase | b64enc | quote }}
{{ else }}
database-url: {{ .Values.postgresql.url | b64enc | quote }}
{{ end }}
secret: {{ .Values.php.secret | default (randAlphaNum 40) | b64enc | quote }}
mercure-jwt-secret: {{ .Values.php.mercure.jwtSecret | b64enc | quote }}

View File

@@ -0,0 +1,44 @@
{{- if .Values.varnish.enabled -}}
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{ template "fullname" . }}-varnish
labels:
app.kubernetes.io/name: {{ include "name" . }}-varnish
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.varnish.replicaCount }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "name" . }}-varnish
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
containers:
- name: {{ .Chart.Name }}-varnish
image: "{{ .Values.varnish.repository }}:{{ .Values.varnish.tag }}"
imagePullPolicy: {{ .Values.varnish.pullPolicy }}
command: ["varnishd"]
args: ["-F", "-f", "/usr/local/etc/varnish/default.vcl", "-p", "http_resp_hdr_len=65536", "-p", "http_resp_size=98304"]
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 80
readinessProbe:
httpGet:
path: /healthz
port: 80
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- if .Values.nodeSelector }}
nodeSelector:
{{ toYaml .Values.nodeSelector | indent 8 }}
{{- end }}
{{- end -}}

View File

@@ -0,0 +1,21 @@
{{- if .Values.varnish.enabled -}}
apiVersion: v1
kind: Service
metadata:
name: varnish
labels:
app.kubernetes.io/name: {{ include "name" . }}-varnish
app.kubernetes.io/part-of: {{ include "name" . }}
helm.sh/chart: {{ include "chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
selector:
app.kubernetes.io/name: {{ include "name" . }}-varnish
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}

92
api/helm/api/values.yaml Normal file
View File

@@ -0,0 +1,92 @@
# Default values for api.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
php:
repository: quay.io/api-platform/php
tag: latest
pullPolicy: Always
replicaCount: 1
mercure:
jwtSecret: ""
env: prod
debug: '0'
secret: ""
corsAllowOrigin: "^https?://.*?\\.example\\.com$"
trustedHosts: "^.*\\.example\\.com$"
trustedProxies:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
nginx:
repository: quay.io/api-platform/nginx
tag: latest
pullPolicy: Always
replicaCount: 1
varnish:
enabled: true
#url: https://example.com
repository: quay.io/api-platform/varnish
tag: latest
pullPolicy: Always
replicaCount: 1
postgresql:
enabled: true
imageTag: 10-alpine
# If bringing your own PostgreSQL, the full uri to use
#url: pgsql://api-platform:!ChangeMe!@example.com/api?serverVersion=10.1
postgresqlUsername: "example"
postgresqlPassword: "!ChangeMe!"
postgresqlDatabase: "api"
# Persistent Volume Storage configuration.
# ref: https://kubernetes.io/docs/user-guide/persistent-volumes
persistence:
enabled: false
pullPolicy: IfNotPresent
# image:
# repository: postgres
# tag: alpine
mercure:
enabled: true
publishUrl: http://mercure/hub
subscribeUrl: https://mercure.example.com/hub
allowAnonymous: "1"
corsAllowedOrigins: "^https?://.*?\\.example\\.com$"
acmeHosts: "" # TODO: Fix the Mercure chart
service:
type: NodePort
port: 80
ingress:
annotations:
# kubernetes.io/ingress.global-static-ip-name: chart-ip
# kubernetes.io/ingress.class: gce
# kubernetes.io/tls-acme: "true"
tls:
# Secrets must be manually created in the namespace, you can also use cert-manager.
# - hosts:
# - example.com
# - mercure.example.com
hosts:
api:
host: example.com
serviceName: varnish
mercure:
host: mercure.example.com
serviceName: mercure
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi

BIN
api/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

27
api/public/index.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
use App\Kernel;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
require dirname(__DIR__).'/config/bootstrap.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
Debug::enable();
}
if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) {
Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? $_ENV['TRUSTED_HOSTS'] ?? false) {
Request::setTrustedHosts([$trustedHosts]);
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

0
api/src/Controller/.gitignore vendored Normal file
View File

0
api/src/Entity/.gitignore vendored Normal file
View File

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* This is a dummy entity. Remove it!
*
* @ApiResource
* @ORM\Entity
*/
class Greeting
{
/**
* @var int The entity Id
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @var string A nice person
*
* @ORM\Column
* @Assert\NotBlank
*/
public $name = '';
public function getId(): int
{
return $this->id;
}
}

53
api/src/Kernel.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
private const CONFIG_EXTS = '.{php,xml,yaml,yml}';
public function registerBundles(): iterable
{
$contents = require $this->getProjectDir().'/config/bundles.php';
foreach ($contents as $class => $envs) {
if ($envs[$this->environment] ?? $envs['all'] ?? false) {
yield new $class();
}
}
}
public function getProjectDir(): string
{
return \dirname(__DIR__);
}
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
$container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
$container->setParameter('container.dumper.inline_class_loader', true);
$confDir = $this->getProjectDir().'/config';
$loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
}
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$confDir = $this->getProjectDir().'/config';
$routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
}
}

0
api/src/Repository/.gitignore vendored Normal file
View File

425
api/symfony.lock Normal file
View File

@@ -0,0 +1,425 @@
{
"api-platform/api-pack": {
"version": "v1.2.0"
},
"api-platform/core": {
"version": "2.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.1",
"ref": "18727d8f229306860b46955f438e1897421da689"
},
"files": [
"config/packages/api_platform.yaml",
"config/routes/api_platform.yaml",
"src/Entity/.gitignore"
]
},
"api-platform/schema-generator": {
"version": "v2.1.0"
},
"composer/semver": {
"version": "1.5.0"
},
"composer/xdebug-handler": {
"version": "1.3.3"
},
"doctrine/annotations": {
"version": "1.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "cb4152ebcadbe620ea2261da1a1c5a9b8cea7672"
},
"files": [
"config/routes/annotations.yaml"
]
},
"doctrine/cache": {
"version": "v1.8.0"
},
"doctrine/collections": {
"version": "v1.6.2"
},
"doctrine/common": {
"version": "v2.10.0"
},
"doctrine/dbal": {
"version": "v2.9.2"
},
"doctrine/doctrine-bundle": {
"version": "1.6",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.6",
"ref": "02bc9e7994b70f4fda004131a0c78b7b1bf09789"
},
"files": [
"config/packages/doctrine.yaml",
"config/packages/prod/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-cache-bundle": {
"version": "1.3.5"
},
"doctrine/event-manager": {
"version": "v1.0.0"
},
"doctrine/inflector": {
"version": "v1.3.0"
},
"doctrine/instantiator": {
"version": "1.2.0"
},
"doctrine/lexer": {
"version": "1.0.2"
},
"doctrine/orm": {
"version": "v2.6.3"
},
"doctrine/persistence": {
"version": "1.1.1"
},
"doctrine/reflection": {
"version": "v1.0.0"
},
"easyrdf/easyrdf": {
"version": "0.9.1"
},
"fig/link-util": {
"version": "1.0.0"
},
"friendsofphp/php-cs-fixer": {
"version": "2.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.2",
"ref": "cc05ab6abf6894bddb9bbd6a252459010ebe040b"
},
"files": [
".php_cs.dist"
]
},
"guzzlehttp/guzzle": {
"version": "6.3.3"
},
"guzzlehttp/promises": {
"version": "v1.3.1"
},
"guzzlehttp/psr7": {
"version": "1.5.2"
},
"jdorn/sql-formatter": {
"version": "v1.2.17"
},
"league/html-to-markdown": {
"version": "4.8.1"
},
"nelmio/cors-bundle": {
"version": "1.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.5",
"ref": "f0436fc35fca88eada758311f8de43bfb61f1980"
},
"files": [
"config/packages/nelmio_cors.yaml"
]
},
"nikic/php-parser": {
"version": "v4.2.2"
},
"php-cs-fixer/diff": {
"version": "v1.3.0"
},
"phpdocumentor/reflection-common": {
"version": "1.0.1"
},
"phpdocumentor/reflection-docblock": {
"version": "4.3.1"
},
"phpdocumentor/type-resolver": {
"version": "0.4.0"
},
"psr/cache": {
"version": "1.0.1"
},
"psr/container": {
"version": "1.0.0"
},
"psr/http-message": {
"version": "1.0.1"
},
"psr/link": {
"version": "1.0.0"
},
"psr/log": {
"version": "1.1.0"
},
"ralouphie/getallheaders": {
"version": "2.0.5"
},
"symfony/asset": {
"version": "v4.3.1"
},
"symfony/cache": {
"version": "v4.3.1"
},
"symfony/config": {
"version": "v4.3.1"
},
"symfony/console": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.3",
"ref": "482d233eb8de91ebd042992077bbd5838858890c"
},
"files": [
"bin/console",
"config/bootstrap.php"
]
},
"symfony/contracts": {
"version": "v1.1.3"
},
"symfony/debug": {
"version": "v4.3.1"
},
"symfony/dependency-injection": {
"version": "v4.3.1"
},
"symfony/doctrine-bridge": {
"version": "v4.3.1"
},
"symfony/dotenv": {
"version": "v4.3.1"
},
"symfony/event-dispatcher": {
"version": "v4.3.1"
},
"symfony/expression-language": {
"version": "v4.3.1"
},
"symfony/filesystem": {
"version": "v4.3.1"
},
"symfony/finder": {
"version": "v4.3.1"
},
"symfony/flex": {
"version": "1.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "dc3fc2e0334a4137c47cfd5a3ececc601fa61a0b"
},
"files": [
".env"
]
},
"symfony/framework-bundle": {
"version": "4.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "4.2",
"ref": "0dfae0b1cd8349ae47659b35602f772b4a7e2280"
},
"files": [
"config/bootstrap.php",
"config/packages/cache.yaml",
"config/packages/framework.yaml",
"config/packages/test/framework.yaml",
"config/services.yaml",
"public/index.php",
"src/Controller/.gitignore",
"src/Kernel.php"
]
},
"symfony/http-client": {
"version": "v4.3.1"
},
"symfony/http-foundation": {
"version": "v4.3.1"
},
"symfony/http-kernel": {
"version": "v4.3.1"
},
"symfony/inflector": {
"version": "v4.3.1"
},
"symfony/maker-bundle": {
"version": "1.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/mercure": {
"version": "v0.2.0"
},
"symfony/mercure-bundle": {
"version": "0.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "0.1",
"ref": "c78ab1f56e700004fc5cd675c26b3c1c26be281d"
},
"files": [
"config/packages/mercure.yaml"
]
},
"symfony/mime": {
"version": "v4.3.1"
},
"symfony/options-resolver": {
"version": "v4.3.1"
},
"symfony/polyfill-intl-idn": {
"version": "v1.11.0"
},
"symfony/polyfill-mbstring": {
"version": "v1.11.0"
},
"symfony/polyfill-php72": {
"version": "v1.11.0"
},
"symfony/polyfill-php73": {
"version": "v1.11.0"
},
"symfony/process": {
"version": "v4.3.1"
},
"symfony/profiler-pack": {
"version": "v1.0.4"
},
"symfony/property-access": {
"version": "v4.3.1"
},
"symfony/property-info": {
"version": "v4.3.1"
},
"symfony/routing": {
"version": "4.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "4.2",
"ref": "5374e24d508ba8fd6ba9eb15170255fdb778316a"
},
"files": [
"config/packages/dev/routing.yaml",
"config/packages/routing.yaml",
"config/packages/test/routing.yaml",
"config/routes.yaml"
]
},
"symfony/security-bundle": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.3",
"ref": "9a2034eca6d83d9cda632014e06995b8d9d9fd09"
},
"files": [
"config/packages/security.yaml"
]
},
"symfony/security-core": {
"version": "v4.3.1"
},
"symfony/security-csrf": {
"version": "v4.3.1"
},
"symfony/security-guard": {
"version": "v4.3.1"
},
"symfony/security-http": {
"version": "v4.3.1"
},
"symfony/serializer": {
"version": "v4.3.1"
},
"symfony/stopwatch": {
"version": "v4.3.1"
},
"symfony/twig-bridge": {
"version": "v4.3.1"
},
"symfony/twig-bundle": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.3",
"ref": "369b5b29dc52b2c190002825ae7ec24ab6f962dd"
},
"files": [
"config/packages/twig.yaml",
"config/routes/dev/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/validator": {
"version": "4.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "4.3",
"ref": "d902da3e4952f18d3bf05aab29512eb61cabd869"
},
"files": [
"config/packages/test/validator.yaml",
"config/packages/validator.yaml"
]
},
"symfony/var-dumper": {
"version": "v4.3.1"
},
"symfony/var-exporter": {
"version": "v4.3.1"
},
"symfony/web-link": {
"version": "v4.3.1"
},
"symfony/web-profiler-bundle": {
"version": "3.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.3",
"ref": "6bdfa1a95f6b2e677ab985cd1af2eae35d62e0f6"
},
"files": [
"config/packages/dev/web_profiler.yaml",
"config/packages/test/web_profiler.yaml",
"config/routes/dev/web_profiler.yaml"
]
},
"symfony/yaml": {
"version": "v4.3.1"
},
"twig/twig": {
"version": "v2.11.3"
},
"webmozart/assert": {
"version": "1.4.0"
},
"willdurand/negotiation": {
"version": "v2.3.1"
}
}

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>