Initial commit
commit
b1ffd2d347
|
@ -0,0 +1,9 @@
|
|||
**/*.log
|
||||
**/._*
|
||||
**/.DS_Store
|
||||
**/.gitignore
|
||||
**/.gitattributes
|
||||
**/Thumbs.db
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
docker-compose.yaml
|
|
@ -0,0 +1,4 @@
|
|||
.idea/
|
||||
client/node_modules/
|
||||
server/vendor/
|
||||
server/.env
|
|
@ -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
|
|
@ -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" ]
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
FROM nginx:1.15-alpine
|
||||
|
||||
COPY docker/nginx/conf.d /etc/nginx/conf.d/
|
||||
COPY ./server/public /srv/server/public/
|
|
@ -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
|
|
@ -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"
|
||||
}
|
|
@ -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,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'
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 "$@"
|
|
@ -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,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');
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"require": {
|
||||
"bramus/router": "~1.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\Controller\\": "src/Controller",
|
||||
"App\\Service\\": "src/Service"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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": []
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
$router = new \Bramus\Router\Router();
|
||||
|
||||
require __DIR__.'/../app/routes.php';
|
||||
|
||||
$router->run();
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
class Database
|
||||
{
|
||||
// code
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue