Initial commit

pull/1/head
sundowndev 2018-07-08 20:17:19 +02:00
commit b1ffd2d347
24 changed files with 560 additions and 0 deletions

9
.dockerignore Normal file
View File

@ -0,0 +1,9 @@
**/*.log
**/._*
**/.DS_Store
**/.gitignore
**/.gitattributes
**/Thumbs.db
.dockerignore
Dockerfile
docker-compose.yaml

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.idea/
client/node_modules/
server/vendor/
server/.env

45
Dockerfile Normal file
View File

@ -0,0 +1,45 @@
FROM php:7.2-fpm-alpine
RUN apk add --no-cache --virtual .persistent-deps \
git \
icu-libs \
zlib
ENV APCU_VERSION 5.1.11
RUN set -xe \
&& apk add --no-cache --virtual .build-deps \
$PHPIZE_DEPS \
icu-dev \
zlib-dev \
&& docker-php-ext-install \
intl \
zip \
&& pecl install \
apcu-${APCU_VERSION} \
&& docker-php-ext-enable --ini-name 20-apcu.ini apcu \
&& docker-php-ext-enable --ini-name 05-opcache.ini opcache \
&& apk del .build-deps
RUN docker-php-ext-install pdo pdo_mysql
COPY docker/server/php.ini /usr/local/etc/php/php.ini
COPY --from=composer:1.6 /usr/bin/composer /usr/bin/composer
COPY docker/server/docker-entrypoint.sh /usr/local/bin/docker-app-entrypoint
RUN chmod +x /usr/local/bin/docker-app-entrypoint
WORKDIR /srv/server
ENTRYPOINT ["docker-app-entrypoint"]
CMD ["php-fpm"]
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER 1
# Use prestissimo to speed up builds
RUN composer global require "hirak/prestissimo:^0.3" --prefer-dist --no-progress --no-suggest --optimize-autoloader --classmap-authoritative --no-interaction
###> recipes ###
###< recipes ###
COPY ./server .
RUN composer install

28
Dockerfile.client Normal file
View File

@ -0,0 +1,28 @@
FROM node:9.3.0-alpine
# install static server
RUN npm install -g serve
# create a 'tmp' folder for the build and make it the current working directory
WORKDIR /app/tmp
# copy only the package.json to take advantage of cached Docker layers
COPY package.json .
# install project dependencies
RUN npm install
# copy project files and folders to the working directory
COPY . .
# build for production with minification
RUN npm run build
# make the 'app' folder the current working directory
WORKDIR /app
# clean up (i.e. extract 'dist' folder and remove everything else)
RUN mv tmp/dist dist && rm -fr tmp
EXPOSE 5000
CMD [ "serve", "--single", "--port", "3000", "dist" ]

17
Dockerfile.h2-proxy Normal file
View File

@ -0,0 +1,17 @@
FROM alpine:latest
RUN apk add --no-cache openssl
# Use this self-generated certificate only in dev, IT IS NOT SECURE!
RUN openssl genrsa -des3 -passout pass:NotSecure -out server.pass.key 2048
RUN openssl rsa -passin pass:NotSecure -in server.pass.key -out server.key
RUN rm server.pass.key
RUN openssl req -new -passout pass:NotSecure -key server.key -out server.csr \
-subj '/C=SS/ST=SS/L=Gotham City/O=Symfony/CN=localhost'
RUN openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt
FROM httpd:2.4-alpine
COPY --from=0 server.key /usr/local/apache2/conf/server.key
COPY --from=0 server.crt /usr/local/apache2/conf/server.crt
COPY ./docker/httpd/httpd.conf /usr/local/apache2/conf/httpd.conf

4
Dockerfile.nginx Normal file
View File

@ -0,0 +1,4 @@
FROM nginx:1.15-alpine
COPY docker/nginx/conf.d /etc/nginx/conf.d/
COPY ./server/public /srv/server/public/

46
README.md Normal file
View File

@ -0,0 +1,46 @@
# interview-v1
## Description
1/ Développer en PHP une mini API REST avec output en json
Cette api doit:
- Gérer 2 types d'objets:
User (id, name, email)
Task (id, user_id, title, description, creation_date, status)
- Mettre à disposition des endpoints permettant de récupérer les données d'un user et d'une task. (ex: /user/$id)
- L'api doit être capable de manipuler la liste des taches associées à un utilisateur en offrant la possibilité de:
Récupérer cette liste de taches
Créer et ajouter une nouvelle tache
Supprimer une tache
En développant cette API, vous devez garder en tête qu'elle est susceptible d'évoluer (nouveaux retours, nouveaux attributs dans les objets)
2/ Développer un front en HtML/JS/CSS (pas de design nécessaire)
Ce front doit communiquer avec l'api en ajax.
On doit pouvoir ajouter/supprimer un utilisateur
Gérer la liste des tâches d'un utilisateur (liste / ajout / suppression)
(pas de framework)
## Installation and usage
```bash
$ git clone git@github.com:Sundowndev/interview-v1.git
$ cd interview-v1/
$ docker-compose up -d
```
You can now browse the front app at `localhost:3000` and the API at `localhost:8000`.
## Architecture
## Database
## Security
## API endpoints

12
client/package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "client",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

12
client/public/index.html Normal file
View File

@ -0,0 +1,12 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- -->
</body>
</html>

0
client/src/main.js Normal file
View File

44
docker-compose.yaml Normal file
View File

@ -0,0 +1,44 @@
version: '3'
services:
db:
image: mysql:5.7
ports:
- "3311:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mysql
MYSQL_USER: mysql
MYSQL_PASSWORD: mysql
server:
build:
context: .
args:
STABILITY: stable
volumes:
# Comment out the next line in production
- ./server:/srv/server:rw
nginx:
build:
context: .
dockerfile: ./Dockerfile.nginx
depends_on:
- server
volumes:
# Comment out the next line in production
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
- ./server/public:/srv/server/public:ro
ports:
- '8000:80'
# This HTTP/2 proxy is not secure: it should only be used in dev
h2-proxy:
build:
context: .
dockerfile: ./Dockerfile.h2-proxy
volumes:
- ./docker/httpd/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro
ports:
- '443:443'

28
docker/httpd/httpd.conf Normal file
View File

@ -0,0 +1,28 @@
ServerName localhost
Listen 443
SSLEngine on
SSLCertificateFile "/usr/local/apache2/conf/server.crt"
SSLCertificateKeyFile "/usr/local/apache2/conf/server.key"
SSLSessionCache "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
User daemon
Group daemon
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""
Protocols h2 http/1.1
ProxyPass / http://nginx/
ProxyPassReverse / http://nginx/
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule http2_module modules/mod_http2.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule unixd_module modules/mod_unixd.so

View File

@ -0,0 +1,36 @@
server {
root /srv/server/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
#resolver 127.0.0.11;
#set $upstream_host server;
#fastcgi_pass $upstream_host:9000;
# Uncomment the previous lines and comment the next one to enable dynamic resolution (incompatible with Kubernetes)
fastcgi_pass server: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,9 @@
#!/bin/sh
set -e
# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
set -- php-fpm "$@"
fi
exec docker-php-entrypoint "$@"

9
docker/server/php.ini Normal file
View File

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

0
server/.env.dist Normal file
View File

15
server/app/routes.php Normal file
View File

@ -0,0 +1,15 @@
<?php
$router->setNamespace('\App\Controller');
$router->before('GET|POST', '/.*', function() use ($router) {
# This will be always executed
});
$router->get('/', 'DefaultController@index');
$router->mount('/product', function () use ($router) {
$router->get('/', 'TaskController@getAll');
});
$router->set404('DefaultController@error');

11
server/composer.json Normal file
View File

@ -0,0 +1,11 @@
{
"require": {
"bramus/router": "~1.3"
},
"autoload": {
"psr-4": {
"App\\Controller\\": "src/Controller",
"App\\Service\\": "src/Service"
}
}
}

64
server/composer.lock generated Normal file
View File

@ -0,0 +1,64 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "aaea3dbe99c937c6633f4a355bbb0d6b",
"packages": [
{
"name": "bramus/router",
"version": "1.3",
"source": {
"type": "git",
"url": "https://github.com/bramus/router.git",
"reference": "1d67c5e50f979c8137dcf3cadcb06cd3c5cfe330"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bramus/router/zipball/1d67c5e50f979c8137dcf3cadcb06cd3c5cfe330",
"reference": "1d67c5e50f979c8137dcf3cadcb06cd3c5cfe330",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/php-code-coverage": "^2.1.0",
"phpunit/phpunit": "4.8.*"
},
"type": "library",
"autoload": {
"psr-0": {
"Bramus": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bram(us) Van Damme",
"email": "bramus@bram.us",
"homepage": "http://www.bram.us"
}
],
"description": "A lightweight and simple object oriented PHP Router",
"homepage": "https://github.com/bramus/router",
"keywords": [
"router",
"routing"
],
"time": "2017-12-21T20:37:23+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

9
server/public/index.php Normal file
View File

@ -0,0 +1,9 @@
<?php
require_once __DIR__.'/../vendor/autoload.php';
$router = new \Bramus\Router\Router();
require __DIR__.'/../app/routes.php';
$router->run();

View File

@ -0,0 +1,37 @@
<?php
namespace App\Controller;
use App\Service\JsonResponse;
class DefaultController
{
private $jsonResponse;
public function __construct()
{
$this->jsonResponse = new JsonResponse();
}
/**
* API homepage
*/
public function index()
{
print $this->jsonResponse->create([
'code' => 200,
'message' => 'Hello! :)'
], 200);
}
/**
* API 404 not found callback
*/
public function error()
{
print $this->jsonResponse->create([
'code' => 404,
'message' => 'Resource not found.'
], 404);
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace App\Controller;
use App\Service\JsonResponse;
use App\Service\Database;
class TaskController
{
private $jsonResponse;
private $db;
public function __construct()
{
$this->jsonResponse = new JsonResponse();
$this->db = new Database();
}
/**
* Get all tasks
*
* Route: /task or /task/$page
* Method: GET
*/
public function getAll($page = 1)
{
print $this->jsonResponse->create([], 200);
}
/**
* Get all tasks
*
* Route: /task/$id
* Method: GET
*/
public function get($id)
{
$code = 200;
$data = [];
print $this->jsonResponse->create([
'code' => $code,
'data' => $data
], $code);
}
/**
* Create a task
*
* Route: /task
* Method: POST
*/
public function post()
{
$code = 200;
$data = [];
print $this->jsonResponse->create([
'code' => $code,
'data' => $data
], $code);
}
/**
* Update a task
*
* Route: /task/$id
* Method: PUT
*/
public function put($id)
{
$code = 200;
$data = [];
print $this->jsonResponse->create([
'code' => $code,
'data' => $data
], $code);
}
/**
* Delete a task
*
* Route: /task/$id
* Method: DELETE
*/
public function delete($id)
{
$code = 200;
$data = [];
print $this->jsonResponse->create([
'code' => $code,
'data' => $data
], $code);
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace App\Service;
class Database
{
// code
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Service;
class JsonResponse
{
public function create(array $data, $code)
{
$response = json_encode($data);
header('Content-Type: application/json');
http_response_code($code);
return $response;
}
}