From eb691522193e15ea87816460a79df5f44672a075 Mon Sep 17 00:00:00 2001 From: Viktor Date: Thu, 14 Apr 2022 21:49:53 +0300 Subject: [PATCH 1/2] hw --- .gitignore | 3 + README.md | 1 - code/app.php | 11 ++ code/app_src/Application.php | 61 ++++++++++ code/app_src/Interface/StorageInterface.php | 12 ++ code/app_src/RequestValidator.php | 27 +++++ code/app_src/Response.php | 18 +++ code/app_src/StatisticsManager.php | 63 +++++++++++ code/app_src/Storage/ESStorage.php | 82 ++++++++++++++ code/composer.json | 14 +++ code/index.html | 105 ++++++++++++++++++ docker-compose.yml | 92 +++++++++++++++ nginx-balancer/Dockerfile | 11 ++ .../hosts/nginx-balancer.local.conf | 19 ++++ nginx-webserver/Dockerfile | 11 ++ nginx-webserver/hosts/mysite.conf | 67 +++++++++++ php-fpm/Dockerfile | 30 +++++ php-fpm/php.ini | 3 + 18 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 .gitignore delete mode 100644 README.md create mode 100644 code/app.php create mode 100644 code/app_src/Application.php create mode 100644 code/app_src/Interface/StorageInterface.php create mode 100644 code/app_src/RequestValidator.php create mode 100644 code/app_src/Response.php create mode 100644 code/app_src/StatisticsManager.php create mode 100644 code/app_src/Storage/ESStorage.php create mode 100644 code/composer.json create mode 100644 code/index.html create mode 100644 docker-compose.yml create mode 100644 nginx-balancer/Dockerfile create mode 100644 nginx-balancer/hosts/nginx-balancer.local.conf create mode 100644 nginx-webserver/Dockerfile create mode 100644 nginx-webserver/hosts/mysite.conf create mode 100644 php-fpm/Dockerfile create mode 100644 php-fpm/php.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d6af03b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.idea/ +code/vendor/ diff --git a/README.md b/README.md deleted file mode 100644 index 6490de86..00000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# PHP2021 \ No newline at end of file diff --git a/code/app.php b/code/app.php new file mode 100644 index 00000000..289e1685 --- /dev/null +++ b/code/app.php @@ -0,0 +1,11 @@ +run(); +} +catch(Exception $e) { + App\Response::generateBadRequestResponse($e->getMessage()); +} \ No newline at end of file diff --git a/code/app_src/Application.php b/code/app_src/Application.php new file mode 100644 index 00000000..c58aa4c2 --- /dev/null +++ b/code/app_src/Application.php @@ -0,0 +1,61 @@ +ElasticSearchInterface = new ESStorage(); + $this->statisticsManager = new StatisticsManager($this->ElasticSearchInterface); + + if (!RequestValidator::checkMainFields($_POST, $this->ElasticSearchInterface)) { + throw new \Exception('Main fields missing or empty'); + } + + $this->request = $_POST; + } + + public function run() + { + switch($this->request['tab-btn']) { + case 'add': + $result = $this->ElasticSearchInterface->insert($this->request); + break; + + case 'delete': + $result = $this->ElasticSearchInterface->delete($this->request); + break; + + case 'info': + $result = $this->statisticsManager->getChannelSummary($this->request['id']); + break; + + case 'top': + $result = $this->statisticsManager->getTopRatedChannels(); + break; + + default: + throw new \Exception('Empty request'); + break; + } + + print_r($result); + } +} \ No newline at end of file diff --git a/code/app_src/Interface/StorageInterface.php b/code/app_src/Interface/StorageInterface.php new file mode 100644 index 00000000..ef859eae --- /dev/null +++ b/code/app_src/Interface/StorageInterface.php @@ -0,0 +1,12 @@ +getMainFields() as $field) { + if (!isset($request[$field]) || empty($request[$field])) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/code/app_src/Response.php b/code/app_src/Response.php new file mode 100644 index 00000000..61766d9f --- /dev/null +++ b/code/app_src/Response.php @@ -0,0 +1,18 @@ +storageInterface = $storageInterface; + } + + public function getChannelSummary($channelId) + { + $arChannelVideos = $this->storageInterface->search( + [ + 'index' => 'youtube_video', + 'body' => [ + 'query' => [ + 'match' => [ + 'channel_id' => $channelId + ] + ] + ] + ] + ); + + $likesCount = 0; + $dislikesCount = 0; + foreach ($arChannelVideos as $singleVideo) { + $likesCount += $singleVideo['_source']['likes']; + $dislikesCount += $singleVideo['_source']['dislikes']; + } + + return [ + 'likes' => $likesCount, + 'dislikes' => $dislikesCount, + ]; + } + + public function getTopRatedChannels() + { + $arRate = []; + $arChannels = $this->storageInterface->search( + [ + 'index' => 'youtube_channel', + ] + ); + + foreach ($arChannels as $channel) { + $arChannelSummary = $this->getChannelSummary($channel['_source']['channel_id']); + try { + $arRate[$channel['_source']['channel_name']] = $arChannelSummary['likes'] / $arChannelSummary['dislikes']; + } catch (Exception $e) { + $arRate[$channel['_source']['channel_name']] = PHP_MAX_INT; + } + } + + asort($arRate, SORT_NUMERIC); + return $arRate; + } +} \ No newline at end of file diff --git a/code/app_src/Storage/ESStorage.php b/code/app_src/Storage/ESStorage.php new file mode 100644 index 00000000..cdcde0ee --- /dev/null +++ b/code/app_src/Storage/ESStorage.php @@ -0,0 +1,82 @@ +client = ClientBuilder::create()->setHosts($this->hosts)->build(); + } + + public function insert($request) + { + return $this->client->index($this->getRequestBody($request)); + } + + public function delete($request) + { + return $this->client->delete($this->getRequestBody($request)); + } + + public function search($arData) + { + return $this->client->search($arData)['hits']['hits']; + } + + private function getRequestBody($request) + { + switch ($request['index']) { + case 'youtube_channel': + return $this->makeChannelArray($request); + + case 'youtube_video': + return $this->makeVideoArray($request); + + default: + throw new \Exception('Wrong index passed'); + + } + } + + private function makeChannelArray($request) + { + return [ + 'index' => $request['index'], + 'id' => $request['id'], + 'body' => [ + 'channel_id' => $request['id'], + 'channel_name' => $request['name'] + ] + ]; + } + + private function makeVideoArray($request) + { + return [ + 'index' => $request['index'], + 'id' => $request['id'], + 'body' => [ + 'video_id' => $request['id'], + 'title' => $request['name'], + 'likes' => $request['video_likes'], + 'dislikes' => $request['video_dislikes'], + 'channel_id' => $request['video_channel_id'], + ] + ]; + } + + public function getMainFields() + { + return $this->mainFields; + } +} \ No newline at end of file diff --git a/code/composer.json b/code/composer.json new file mode 100644 index 00000000..0cd02e36 --- /dev/null +++ b/code/composer.json @@ -0,0 +1,14 @@ +{ +"require": { +"php": "~7.4", +"ext-json": "*", +"ext-sockets": "*", +"elasticsearch/elasticsearch": "^7.0", +"google/apiclient": "~2.0" +}, +"autoload": { +"psr-4": { +"App\\": "app_src" +} +} +} diff --git a/code/index.html b/code/index.html new file mode 100644 index 00000000..c2b53546 --- /dev/null +++ b/code/index.html @@ -0,0 +1,105 @@ + + + + + + + Title + + +
+

Elastic Search

+
+ + + + + + + + + +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ec04ca55 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,92 @@ +version: '3' + +services: + nginx-balancer: + build: + context: ./nginx-balancer + dockerfile: Dockerfile + container_name: nginx-balancer + image: localapp/nginx-balancer + ports: + - "80:80" + - "443:443" + depends_on: + - nginx-webserver1 + - nginx-webserver2 + networks: + - app-network + + nginx-webserver1: + build: + context: ./nginx-webserver + dockerfile: Dockerfile + container_name: nginx-webserver1 + image: localapp/nginx-webserver + ports: + - "8081:80" + volumes: + - ./code:/var/www/mysite.local + networks: + - app-network + + nginx-webserver2: + build: + context: ./nginx-webserver + dockerfile: Dockerfile + container_name: nginx-webserver2 + image: localapp/nginx-webserver + ports: + - "8082:80" + volumes: + - ./code:/var/www/mysite.local + networks: + - app-network + + php1: + build: + context: ./php-fpm + dockerfile: Dockerfile + image: localapp/php + container_name: php1 + volumes: + - ./code:/var/www/mysite.local + networks: + - app-network + + php2: + build: + context: ./php-fpm + dockerfile: Dockerfile + image: localapp/php + container_name: php2 + volumes: + - ./code:/var/www/mysite.local + networks: + - app-network + + elastic_search: + image: docker.elastic.co/elasticsearch/elasticsearch:7.16.3 + container_name: elastic_search + ports: + - "9200:9200" + - "9300:9300" + environment: + - "discovery.type=single-node" + networks: + - app-network + + kibana: + image: docker.elastic.co/kibana/kibana:7.16.3 + container_name: kibana + ports: + - "5601:5601" + environment: + - "ELASTICSEARCH_HOSTS=http://elastic_search:9200" + networks: + - app-network + depends_on: + - elastic_search + +networks: + app-network: + driver: bridge \ No newline at end of file diff --git a/nginx-balancer/Dockerfile b/nginx-balancer/Dockerfile new file mode 100644 index 00000000..cf52d591 --- /dev/null +++ b/nginx-balancer/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:latest + +RUN apt-get update && apt-get install -y nginx + +COPY ./hosts/nginx-balancer.local.conf /etc/nginx/sites-enabled/mysite.local.conf + +WORKDIR /var/www/mysite.local +VOLUME /var/www/mysite.local +EXPOSE 80 + +CMD [ "nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx-balancer/hosts/nginx-balancer.local.conf b/nginx-balancer/hosts/nginx-balancer.local.conf new file mode 100644 index 00000000..79aa339f --- /dev/null +++ b/nginx-balancer/hosts/nginx-balancer.local.conf @@ -0,0 +1,19 @@ +upstream nginx-webservers { + server nginx-webserver1; + server nginx-webserver2; +} + +server { + listen 80; + + server_name mysite.local; + error_log /var/log/nginx/error.log; + access_log /var/log/nginx/access.log; + + location / { + proxy_pass http://nginx-webservers; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + } +} diff --git a/nginx-webserver/Dockerfile b/nginx-webserver/Dockerfile new file mode 100644 index 00000000..91fe15c6 --- /dev/null +++ b/nginx-webserver/Dockerfile @@ -0,0 +1,11 @@ +FROM ubuntu:latest + +RUN apt-get update && apt-get install -y nginx + +COPY ./hosts/mysite.conf /etc/nginx/sites-enabled/mysite.conf + +WORKDIR /var/www/mysite.local +VOLUME /var/www/mysite.local +EXPOSE 80 + +CMD [ "nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx-webserver/hosts/mysite.conf b/nginx-webserver/hosts/mysite.conf new file mode 100644 index 00000000..b64247ee --- /dev/null +++ b/nginx-webserver/hosts/mysite.conf @@ -0,0 +1,67 @@ +upstream php-fpm { + + server php1:9000; + + server php2:9000; + +} + + + + +server { + + listen 80; + + index index.php index.html index.htm; + + server_name mysite.local; + + root /var/www/mysite.local; + + error_log /var/log/nginx/error.log; + + access_log /var/log/nginx/access.log; + + + + + location ~* .(jpg|jpeg|gif|css|png|js|html)$ { + + access_log off; + + expires max; + + } + + + + + location / { + + try_files $uri $uri/ /index.php?$query_string; + + } + + + + + location ~* .php$ { + + try_files $uri = 404; + + fastcgi_split_path_info (.+?\.php)(/.*)$; + + fastcgi_pass php-fpm; + + fastcgi_index index.php; + + include fastcgi_params; + + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + + fastcgi_param PATH_INFO $fastcgi_path_info; + + } + +} \ No newline at end of file diff --git a/php-fpm/Dockerfile b/php-fpm/Dockerfile new file mode 100644 index 00000000..cffffe45 --- /dev/null +++ b/php-fpm/Dockerfile @@ -0,0 +1,30 @@ +FROM php:8.0-fpm + +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + git \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libonig-dev \ + libzip-dev \ + libmcrypt-dev \ + libmemcached-dev\ + libmemcached-tools\ + && pecl install memcached \ + && docker-php-ext-enable memcached \ + && pecl install mcrypt-1.0.4 \ + && docker-php-ext-enable mcrypt \ + && docker-php-ext-install -j$(nproc) iconv mbstring mysqli pdo_mysql zip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j$(nproc) gd \ + && curl -sS https://getcomposer.org/installer \ + | php -- --install-dir=/usr/local/bin --filename=composer + +ADD php.ini /usr/local/etc/php/conf.d/40-custom.ini + +WORKDIR /var/www +VOLUME /var/www + +CMD ["php-fpm"] \ No newline at end of file diff --git a/php-fpm/php.ini b/php-fpm/php.ini new file mode 100644 index 00000000..6dccaac2 --- /dev/null +++ b/php-fpm/php.ini @@ -0,0 +1,3 @@ +extension=memcached.so +session.save_handler = memcache +session.save_path = "tcp://memcache:11211" \ No newline at end of file From 2e52cdaf04b30b3a5a331bc82f1dbb92aa721a13 Mon Sep 17 00:00:00 2001 From: Viktor Date: Sun, 17 Apr 2022 20:08:30 +0300 Subject: [PATCH 2/2] strict --- code/app_src/Application.php | 1 - code/app_src/Interface/StorageInterface.php | 8 ++++---- code/app_src/RequestValidator.php | 8 ++++---- code/app_src/Response.php | 6 +++--- code/app_src/StatisticsManager.php | 7 ++++--- code/app_src/Storage/ESStorage.php | 14 +++++++------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/code/app_src/Application.php b/code/app_src/Application.php index c58aa4c2..973bf526 100644 --- a/code/app_src/Application.php +++ b/code/app_src/Application.php @@ -11,7 +11,6 @@ class Application { private $request; private $ElasticSearchInterface; - private $appHelper; public function __construct() { diff --git a/code/app_src/Interface/StorageInterface.php b/code/app_src/Interface/StorageInterface.php index ef859eae..2d534237 100644 --- a/code/app_src/Interface/StorageInterface.php +++ b/code/app_src/Interface/StorageInterface.php @@ -1,12 +1,12 @@ getMainFields() as $field) { if (!isset($request[$field]) || empty($request[$field])) { diff --git a/code/app_src/Response.php b/code/app_src/Response.php index 61766d9f..e7396d3c 100644 --- a/code/app_src/Response.php +++ b/code/app_src/Response.php @@ -1,16 +1,16 @@ storageInterface = $storageInterface; } - public function getChannelSummary($channelId) + public function getChannelSummary(string $channelId) : array { $arChannelVideos = $this->storageInterface->search( [ @@ -39,7 +40,7 @@ public function getChannelSummary($channelId) ]; } - public function getTopRatedChannels() + public function getTopRatedChannels() : array { $arRate = []; $arChannels = $this->storageInterface->search( diff --git a/code/app_src/Storage/ESStorage.php b/code/app_src/Storage/ESStorage.php index cdcde0ee..5b51b371 100644 --- a/code/app_src/Storage/ESStorage.php +++ b/code/app_src/Storage/ESStorage.php @@ -1,5 +1,5 @@ client = ClientBuilder::create()->setHosts($this->hosts)->build(); } - public function insert($request) + public function insert(string $request) : array { return $this->client->index($this->getRequestBody($request)); } - public function delete($request) + public function delete(string $request) { return $this->client->delete($this->getRequestBody($request)); } - public function search($arData) + public function search(string $arData) : array { return $this->client->search($arData)['hits']['hits']; } - private function getRequestBody($request) + private function getRequestBody(array $request) : array { switch ($request['index']) { case 'youtube_channel': @@ -48,7 +48,7 @@ private function getRequestBody($request) } } - private function makeChannelArray($request) + private function makeChannelArray(array $request) : array { return [ 'index' => $request['index'], @@ -75,7 +75,7 @@ private function makeVideoArray($request) ]; } - public function getMainFields() + public function getMainFields() : array { return $this->mainFields; }