diff --git a/.env b/.env index 72d334f..6ac6765 100644 --- a/.env +++ b/.env @@ -120,10 +120,14 @@ MERCURE_URL=https://127.0.0.1/.well-known/mercure MERCURE_PUBLIC_URL=https://127.0.0.1/.well-known/mercure MERCURE_JWT_SECRET="!ChangeMe!" -# Messenger -# MESSENGER_TRANSPORT_DSN=doctrine://default -# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages -# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages +# Minio +MINIO_URL=http://127.0.0.1:9000 +MINIO_KEY=minio +MINIO_SECRET=changeme +MINIO_BUCKET=nine +MINIO_ROOT= +MINIO_PATH_STYLE=1 +MINIO_SECURE=0 # Lock LOCK_DSN="postgresql://symfony:ChangeMe@127.0.0.1:5432/app?serverVersion=13&charset=utf8" diff --git a/composer.json b/composer.json index f898b90..a64f2a9 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "php": ">=8.1", "ext-ctype": "*", "ext-iconv": "*", + "aws/aws-sdk-php": "^3.234", "doctrine/annotations": "^1.0", "doctrine/doctrine-bundle": "^2.6", "doctrine/doctrine-migrations-bundle": "^3.2", diff --git a/composer.lock b/composer.lock index 1513f3f..4bab223 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,150 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "55a8fc0f19cbcfaa9fd74ecf5d8ff8d5", + "content-hash": "b10025eb97ca0ec25b18857a1f4c520f", "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "3942776a8c99209908ee0b287746263725685732" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/3942776a8c99209908ee0b287746263725685732", + "reference": "3942776a8c99209908ee0b287746263725685732", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|^5.4.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.0.2" + }, + "time": "2021-09-03T22:57:30+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.234.0", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "d2113f1e5ec9f7f19de2472f5063333b39a55280" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d2113f1e5ec9f7f19de2472f5063333b39a55280", + "reference": "d2113f1e5ec9f7f19de2472f5063333b39a55280", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.0.2", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/psr7": "^1.8.5 || ^2.3", + "mtdowling/jmespath.php": "^2.6", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^1.10.22", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "paragonie/random_compat": ">= 2", + "phpunit/phpunit": "^4.8.35 || ^5.6.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.234.0" + }, + "time": "2022-08-22T18:20:42+00:00" + }, { "name": "brick/math", "version": "0.9.3", @@ -1962,6 +2104,130 @@ }, "time": "2022-01-11T08:28:06+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", + "reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.9 || ^2.4", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-06-20T22:16:13+00:00" + }, { "name": "guzzlehttp/promises", "version": "1.5.1", @@ -2701,6 +2967,67 @@ ], "time": "2022-06-09T09:09:00+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1" + }, + "time": "2021-06-14T00:11:39+00:00" + }, { "name": "nelmio/api-doc-bundle", "version": "v4.9.0", @@ -12879,5 +13206,5 @@ "ext-iconv": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/config/routes.yaml b/config/routes.yaml index f86600f..fa16e6a 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -18,11 +18,6 @@ app_modo_home: controller: App\Controller\HomeController::homemodo defaults: { access: modo } -app_ckeditor_upload: - path: /user/upload - controller: App\Controller\HomeController::upload - defaults: { access: user } - oneup_uploader: resource: . type: uploader @@ -160,6 +155,50 @@ app_user_crop02: path: /user/crop02/{type}/{reportinput} controller: App\Controller\CropController::crop02 +#== Minio ======================================================================================================= + +#-- Access admin +app_admin_minio_image: + path: /admin/minio/image + controller: App\Controller\MinioController::image + +app_admin_minio_document: + path: /admin/minio/document + controller: App\Controller\MinioController::document + +#-- Access modo +app_modo_minio_image: + path: /modo/minio/image + controller: App\Controller\MinioController::image + +app_modo_minio_document: + path: /modo/minio/document + controller: App\Controller\MinioController::document + +#-- Access user +app_user_minio_image: + path: /user/minio/image + controller: App\Controller\MinioController::image + +app_user_minio_document: + path: /user/minio/document + controller: App\Controller\MinioController::document + +#-- Access public +app_minio_image: + path: /minio/image + controller: App\Controller\MinioController::image + +app_minio_document: + path: /minio/document + controller: App\Controller\MinioController::document + +#== Ckeditor ==================================================================================================== +app_ckeditor_upload: + path: /user/upload + controller: App\Controller\MinioController::ckupload + defaults: { access: user } + #== Audit ======================================================================================================= #--Access admin @@ -168,7 +207,6 @@ app_admin_audit_renderid: controller: App\Controller\AuditController::auditrender defaults: { access: admin } -#--Access admin app_admin_audit_render: path: /admin/audit/{entityname} controller: App\Controller\AuditController::list diff --git a/config/services.yaml b/config/services.yaml index 0000b0d..6b64a05 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -92,6 +92,14 @@ parameters: proxyHost: '%env(resolve:PROXY_HOST)%' proxyPort: '%env(resolve:PROXY_PORT)%' + minioUrl: '%env(resolve:MINIO_URL)%' + minioKey: '%env(resolve:MINIO_KEY)%' + minioSecret: '%env(resolve:MINIO_SECRET)%' + minioBucket: '%env(resolve:MINIO_BUCKET)%' + minioRoot: '%env(resolve:MINIO_ROOT)%' + minioPathstyle: '%env(resolve:MINIO_PATH_STYLE)%' + minioSecure: '%env(resolve:MINIO_SECURE)%' + sondeUse: '%env(resolve:SONDE_USE)%' sondeUrl: '%env(resolve:SONDE_URL)%' @@ -216,5 +224,9 @@ services: App\Service\ApiService: public: true + App\Service\MinioService: + public: true + arguments: ["%kernel.project_dir%","%minioUrl%","%minioKey%","%minioSecret%","%minioBucket%","%minioRoot%","%minioPathstyle%","%minioSecure%","%kernel.environment%"] + App\Controller\RestController: public: true \ No newline at end of file diff --git a/containers/minio/nginx.conf b/containers/minio/nginx.conf new file mode 100644 index 0000000..2519220 --- /dev/null +++ b/containers/minio/nginx.conf @@ -0,0 +1,67 @@ + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + # include /etc/nginx/conf.d/*.conf; + + upstream minio { + server minio1:9000; + server minio2:9000; + server minio3:9000; + server minio4:9000; + } + + server { + listen 9000; + server_name localhost; + + # To allow special characters in headers + ignore_invalid_headers off; + # Allow any size file to be uploaded. + # Set to a value such as 1000m; to restrict file size to a specific value + client_max_body_size 0; + # To disable buffering + proxy_buffering off; + + location / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_connect_timeout 300; + # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 + proxy_http_version 1.1; + proxy_set_header Connection ""; + chunked_transfer_encoding off; + + proxy_pass http://minio; + } + } +} diff --git a/docker-compose.yml b/docker-compose.yml index eb8e188..bc981cc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,8 +55,8 @@ services: LDAP_LOG_LEVEL: "256" LDAP_ORGANISATION: "nineskeletor" LDAP_DOMAIN: "nine.fr" - LDAP_ADMIN_PASSWORD: "admin" - LDAP_CONFIG_PASSWORD: "config" + LDAP_ADMIN_PASSWORD: "changeme" + LDAP_CONFIG_PASSWORD: "changeme" LDAP_READONLY_USER: "true" LDAP_READONLY_USER_USERNAME: "readonly" LDAP_READONLY_USER_PASSWORD: "readonly" @@ -80,7 +80,99 @@ services: depends_on: - openldap + minio1: + image: minio/minio:RELEASE.2021-01-16T02-19-44Z + container_name: minio1 + volumes: + - data1-1:/data1 + - data1-2:/data2 + expose: + - "9000" + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: changeme + command: server http://minio{1...4}/data{1...2} + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + minio2: + image: minio/minio:RELEASE.2021-01-16T02-19-44Z + container_name: minio2 + volumes: + - data2-1:/data1 + - data2-2:/data2 + expose: + - "9000" + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: changeme + command: server http://minio{1...4}/data{1...2} + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + minio3: + image: minio/minio:RELEASE.2021-01-16T02-19-44Z + container_name: minio3 + volumes: + - data3-1:/data1 + - data3-2:/data2 + expose: + - "9000" + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: changeme + command: server http://minio{1...4}/data{1...2} + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + minio4: + image: minio/minio:RELEASE.2021-01-16T02-19-44Z + container_name: minio4 + volumes: + - data4-1:/data1 + - data4-2:/data2 + expose: + - "9000" + environment: + MINIO_ROOT_USER: minio + MINIO_ROOT_PASSWORD: changeme + command: server http://minio{1...4}/data{1...2} + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:9000/minio/health/live"] + interval: 30s + timeout: 20s + retries: 3 + + nginx: + image: nginx:1.19.2-alpine + volumes: + - ./containers/minio/nginx.conf:/etc/nginx/nginx.conf:ro + ports: + - "9000:9000" + depends_on: + - minio1 + - minio2 + - minio3 + - minio4 + volumes: db-data: mercure_data: mercure_config: + data1-1: + data1-2: + data2-1: + data2-2: + data3-1: + data3-2: + data4-1: + data4-2: \ No newline at end of file diff --git a/src/Controller/CropController.php b/src/Controller/CropController.php index c6c95f5..6825dbb 100644 --- a/src/Controller/CropController.php +++ b/src/Controller/CropController.php @@ -9,14 +9,17 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\HttpFoundation\Response; +use App\Service\MinioService; class CropController extends AbstractController { private $appKernel; + private $minio; - public function __construct(KernelInterface $appKernel) + public function __construct(KernelInterface $appKernel, MinioService $minio) { $this->appKernel = $appKernel; + $this->minio = $minio; } // Etape 01 - Téléchargement de l'image @@ -102,6 +105,11 @@ class CropController extends AbstractController $data = $form->getData(); $thumb_image_location = "uploads/$type/thumb_".$file; $cropped = $this->resizeThumbnailImage($thumb_image_location, $large_image_location,$data["ws"],$data["hs"],$data["xs"],$data["ys"],$scale); + + // Dépot des fichiers sur minio + $this->minio->upload($large_image_location,$large_image_location,true); + $this->minio->upload($thumb_image_location,$thumb_image_location,true); + $submited=true; } @@ -117,32 +125,6 @@ class CropController extends AbstractController ]); } - // Upload ckeditor - public function ckupload(Request $request) { - // Fichier temporaire uploadé - $tmpfile = $request->files->get('upload'); - $extention = $tmpfile->getClientOriginalExtension(); - - // Répertoire de Destination - $fs = new Filesystem(); - $rootdir = $this->appKernel->getProjectDir()."/public"; - $fs->mkdir($rootdir."/uploads/ckeditor"); - - // Fichier cible - $targetName = uniqid().".".$extention; - $targetFile = $rootdir."/uploads/ckeditor/".$targetName; - $targetUrl = "/".$this->getParameter('appAlias')."/uploads/ckeditor/".$targetName; - $message = ""; - - move_uploaded_file($tmpfile,$targetFile); - - $output["uploaded"]=1; - $output["fileName"]=$targetName; - $output["url"]=$targetUrl; - - return new Response(json_encode($output)); - } - // Calcul de la hauteur protected function getHeight($image) { $size = getimagesize($image); diff --git a/src/Controller/GroupController.php b/src/Controller/GroupController.php index c2fcce8..f893987 100644 --- a/src/Controller/GroupController.php +++ b/src/Controller/GroupController.php @@ -160,10 +160,7 @@ class GroupController extends AbstractController $userinfo=""; if($data->getOwner()) { - if(stripos($data->getOwner()->getAvatar(),"http")===0) - $userinfo.=""; - else - $userinfo.="getOwner()->getAvatar()."' class='avatar'>"; + $userinfo.=""uploads/avatar/".$data->getOwner()->getAvatar()])."' class='avatar'>"; $userinfo.="
".$data->getOwner()->getUsername(); } @@ -516,11 +513,7 @@ class GroupController extends AbstractController $action.=""; // Avatar - if(stripos($data->getAvatar(),"http")===0) - $avatar=""; - else - $avatar="getAvatar()."' class='avatar'>"; - + $avatar=""uploads/avatar/".$data->getAvatar()])."' class='avatar'>"; array_push($output["data"],array("DT_RowId"=>"user".$data->getId(),$action,$avatar,$data->getUsername(),$data->getEmail(),"","")); } @@ -654,10 +647,7 @@ class GroupController extends AbstractController $action.=""; // Avatar - if(stripos($data->getAvatar(),"http")===0) - $avatar=""; - else - $avatar="getAvatar()."' class='avatar'>"; + $avatar=""uploads/avatar/".$data->getAvatar()])."' class='avatar'>"; // Flag manager $rolegroup=""; diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php index e648f1a..fedc290 100644 --- a/src/Controller/HomeController.php +++ b/src/Controller/HomeController.php @@ -4,7 +4,7 @@ namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Filesystem\Filesystem; + class HomeController extends AbstractController { @@ -52,30 +52,6 @@ class HomeController extends AbstractController } - public function upload($access,Request $request): Response - { - // Fichier temporaire uploadé - $tmpfile = $request->files->get('upload'); - $extention = $tmpfile->getClientOriginalExtension(); - // Répertoire de Destination - $fs = new Filesystem(); - $rootdir = $this->getParameter('kernel.project_dir') . '/public'; - $fs->mkdir($rootdir."/uploads/ckeditor"); - - // Fichier cible - $targetName = uniqid().".".$extention; - $targetFile = $rootdir."/uploads/ckeditor/".$targetName; - $targetUrl = $this->getParameter('appAlias')."uploads/ckeditor/".$targetName; - $message = ""; - - move_uploaded_file($tmpfile,$targetFile); - - $output["uploaded"]=1; - $output["fileName"]=$targetName; - $output["url"]=$targetUrl; - - return new Response(json_encode($output)); - } } \ No newline at end of file diff --git a/src/Controller/MinioController.php b/src/Controller/MinioController.php new file mode 100644 index 0000000..84f402b --- /dev/null +++ b/src/Controller/MinioController.php @@ -0,0 +1,101 @@ +appKernel = $appKernel; + $this->minio = $minio; + } + + public function ckupload($access,Request $request): Response + { + // Fichier temporaire uploadé + $tmpfile = $request->files->get('upload'); + $extention = $tmpfile->getClientOriginalExtension(); + + // Répertoire de Destination + $fs = new Filesystem(); + $rootdir = $this->getParameter('kernel.project_dir') . '/public'; + $fs->mkdir($rootdir."/uploads/ckeditor"); + + // Fichier cible + $targetName = uniqid().".".$extention; + $targetFile = "uploads/ckeditor/".$targetName; + $targetUrl = $this->getParameter('appAlias')."uploads/ckeditor/".$targetName; + $targetUrl = $this->generateUrl('app_minio_document',["file"=>"uploads/ckeditor/".$targetName]); + $message = ""; + + //move_uploaded_file($tmpfile,$targetFile); + $this->minio->upload($tmpfile,$targetFile,true); + + $output["uploaded"]=1; + $output["fileName"]=$targetName; + $output["url"]=$targetUrl; + + return new Response(json_encode($output)); + } + + public function image(Request $request): Response + { + $file=$request->query->get("file"); + switch($file) { + case "uploads/avatar/admin.jpg": + case "uploads/avatar/noavatar.png": + case "uploads/avatar/system.jpg": + case "uploads/header/header.jpg": + case "uploads/logo/logo.png": + $filePath = $file; + $content = file_get_contents($file); + break; + + default: + // C'est une url = on affiche l'url + if(stripos($file,"http")===0) { + $filePath = $file; + $content = file_get_contents($file); + } + // C'est du contenu dynamique on download depuis minio + elseif(stripos($file,"uploads")===0) { + $filePath = $this->minio->download($file, $file, true); + $content = file_get_contents($filePath); + } + // C'est du contenu statique on download depuis minio + else { + $filePath = $file; + $content = file_get_contents($file); + } + break; + } + + return new Response($content, 200, [ + 'Content-Type' => mime_content_type($filePath), + 'Cache-Control' => 'max-age='.(60 * 60 * 24 * 7), + 'Expires' => gmdate(DATE_RFC1123, time() + 60 * 60 * 24 * 365), + ]); + } + + public function document(Request $request) + { + $file=$request->query->get("file"); + $filePath = $this->minio->download($file, $file, true); + $content = file_get_contents($filePath); + + return new Response($content, 200, [ + 'Content-Type' => mime_content_type($filePath), + 'Cache-Control' => 'max-age='.(60 * 60 * 24 * 7), + 'Expires' => gmdate(DATE_RFC1123, time() + 60 * 60 * 24 * 365), + ]); + } +} diff --git a/src/Controller/PublishController.php b/src/Controller/PublishController.php index 66c2345..ac24cf7 100644 --- a/src/Controller/PublishController.php +++ b/src/Controller/PublishController.php @@ -22,12 +22,8 @@ class PublishController extends AbstractController $ret["from"]["id"]=$this->getUser()->getId(); $ret["from"]["username"]=$this->getUser()->getUsername(); $ret["from"]["displayname"]=$this->getUser()->getDisplayname(); - + $ret["from"]["avatar"]=$this->generateUrl('app_minio_image',["file"=>"uploads/avatar/".$this->getUser()->getAvatar()]); - if(stripos($this->getUser()->getAvatar(),"http")===0) - $ret["from"]["avatar"]=$this->getUser()->getAvatar(); - else - $ret["from"]["avatar"]=$this->getParameter('appAlias')."uploads/avatar/".$this->getUser()->getAvatar(); $update = new Update( $channel."-".$id, diff --git a/src/Controller/RestController.php b/src/Controller/RestController.php index 3fc7c93..94eb845 100644 --- a/src/Controller/RestController.php +++ b/src/Controller/RestController.php @@ -399,8 +399,7 @@ class RestController extends AbstractFOSRestController $output["userposition"]=$user->getPosition(); $output["userpostaladress"]=$user->getPostaladress(); $output["usertelephonenumber"]=$user->getTelephonenumber(); - if(stripos($user->getAvatar(),"http")===0) $output["useravatar"]=$user->getAvatar(); - else $output["useravatar"]="https://".$this->getParameter("appWeburl").$this->getParameter("appAlias")."uploads/avatar/".$user->getAvatar(); + $output["useravatar"]="https://".str_replace("//","/",$this->getParameter("appWeburl").$this->getParameter("appAlias").$this->generateUrl('app_minio_image',["file"=>"uploads/avatar/".$user->getAvatar()],true)); $output["userniveau01"]=$this->niveau01Format($user->getNiveau01()); $output["userniveau02"]=$this->niveau02Format($user->getNiveau02()); $output["usergroups"]=[]; diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index f50f60a..32f6bcd 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -255,11 +255,8 @@ class UserController extends AbstractController $tmp=array(); if($access=="admin"||$access=="modo") array_push($tmp,$action); - if(stripos($data->getAvatar(),"http")===0) - array_push($tmp,""); - else - array_push($tmp,"getAvatar()."' class='avatar'>"); - + array_push($tmp,""uploads/avatar/".$data->getAvatar()])."' class='avatar'>"); + array_push($tmp,$data->getUsername()); array_push($tmp,$data->getLastname()); array_push($tmp,$data->getFirstname()); diff --git a/src/Service/MinioService.php b/src/Service/MinioService.php new file mode 100644 index 0000000..c2508e6 --- /dev/null +++ b/src/Service/MinioService.php @@ -0,0 +1,238 @@ +rootPath = $rootPath; + $this->minioBucket = $minioBucket; + $this->minioPathStyle = ($minioPathstyle==1?true:false); + $this->minioRoot = $minioRoot; + $this->client = $this->getClient($minioUrl, $minioKey, $minioSecret, $minioPathstyle, $minioSecure); + $this->initBucket(); + } + + public function download(string $file, string $filename, bool $returnFile = false) + { + // On s'assure que le repertoire temporaire de destination existe bien + $fs = new Filesystem(); + $tmpdir=$this->rootPath."/var/tmp"; + $fs->mkdir($tmpdir."/".dirname($filename)); + + // Approche repassant par le serveur d'appel + try { + $result = $this->client->getObject([ + 'Bucket' => $this->minioBucket, + 'Key' => $this->minioRoot.$file, + 'SaveAs' => $tmpdir.'/'.$filename, + ]); + } catch (S3Exception $e) { + dump($e); + switch ($e->getResponse()->getStatusCode()) { + case 404: + throw new NotFoundHttpException($this->translator->trans(self::ERR_FILE_NOT_FOUND, [], 'messages')); + break; + default: + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + break; + } + } catch (Exception $e) { + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + } + + if ($returnFile) { + return $tmpdir.'/'.$filename; + } else { + //Suppression de la copie locale + unlink($tmpdir.'/'.$filename); + $res = new Response($result['Body'], 200); + $res->headers->set('Content-Type', 'application/pdf'); + $res->headers->set('Content-Description', 'File Transfer'); + $res->headers->set('Content-Disposition', 'attachment; filename='.$filename); + $res->headers->set('Expires', '0'); + $res->headers->set('Cache-Control', 'must-revalidate'); + $res->headers->set('Pragma', 'public'); + + return $res; + } + } + + public function upload($file, $filename, $deleteSource = false) + { + try { + $this->client->putObject([ + 'Bucket' => $this->minioBucket, + 'Key' => $this->minioRoot.$filename, + 'SourceFile' => $file, + ]); + } catch (Exception $e) { + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + } + + if ($deleteSource) { + unlink($file); + } + } + + /** + * move. + * + * @param bool $deleteSource + * + * @return void + */ + public function move(string $from, string $to, $deleteSource = false) + { + try { + $this->client->copyObject([ + 'Bucket' => $this->minioBucket, + 'Key' => $this->minioRoot.$to, + 'CopySource' => $this->minioBucket.'/'.$this->minioRoot.$from, + ]); + } catch (Exception $e) { + \Sentry\captureException($e); + dump($this->minioRoot.$to); + dump($e->getMessage()); + throw new Exception(self::ERR_UNAVAILABLE); + } + + if ($deleteSource) { + $this->delete($from); + } + } + + /** + * delete. + * + * @return void + */ + public function delete(string $file) + { + try { + $this->client->deleteObject([ + 'Bucket' => $this->minioBucket, + 'Key' => $this->minioRoot.$file, + ]); + } catch (Exception $e) { + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + } + } + + /** + * countKeys. + * + * @return int + */ + public function countKeys(string $prefix) + { + //On utilise un path spécifique car listObjectsV2 utilise une autre config de client + try { + $response = $this->client->listObjectsV2([ + 'Bucket' => $this->minioBucket, + 'Prefix' => $prefix, + ]); + + return $response->get('KeyCount'); + } catch (Exception $e) { + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + } + } + + /** + * listKeys. + * + * @return array + */ + public function listKeys(string $prefix) + { + //On utilise un path spécifique car listObjectsV2 utilise une autre config de client + try { + $response = $this->client->listObjectsV2([ + 'Bucket' => $this->minioBucket, + 'Prefix' => $prefix, + ]); + + return $response->get('Contents'); + } catch (Exception $e) { + \Sentry\captureException($e); + throw new Exception(self::ERR_UNAVAILABLE); + } + } + + /** + * download. + * + * @param string $file Nom du fichier dans minio + * @param string $filename Nom du fichier dans la réponse + * @param bool $returnFile Retourner un fichier ou une réponse + */ + + + protected function getClient($minioUrl, $minioKey, $minioSecret, bool $minioPathstyle, bool $minioSecure) + { + $client = new \Aws\S3\S3Client([ + 'version' => 'latest', + 'region' => 'eu-west-1', + 'endpoint' => $minioUrl, + //On force le mode DNS + 'use_path_style_endpoint' => $minioPathstyle, + 'credentials' => [ + 'key' => $minioKey, + 'secret' => $minioSecret, + ], + //On désactive les checks SSL pour le moment + 'http' => [ + 'verify' => $minioSecure, + ], + ]); + + return $client; + } + + protected function initBucket() + { + try { + $bucketExists = false; + + $buckets = $this->client->listBuckets()->toArray()['Buckets']; + + foreach ($buckets as $bucket) { + if ($this->minioBucket == $bucket['Name']) { + $bucketExists = true; + } + } + + if (!$bucketExists) { + $this->client->createBucket([ + 'Bucket' => $this->minioBucket, + ]); + } + } catch (Exception $e) { + dump($e->getMessage()); + throw new Exception(self::ERR_UNAVAILABLE); + } + } +} diff --git a/src/Service/UploadListener.php b/src/Service/UploadListener.php index e86fa91..a3dec5a 100644 --- a/src/Service/UploadListener.php +++ b/src/Service/UploadListener.php @@ -3,14 +3,17 @@ namespace App\Service; use Doctrine\ORM\EntityManagerInterface; use Oneup\UploaderBundle\Event\PostPersistEvent; +use App\Service\MinioService; class UploadListener { private $em; + private $minio; - public function __construct(EntityManagerInterface $em) + public function __construct(EntityManagerInterface $em, MinioService $minio) { $this->em = $em; + $this->minio = $minio; } protected function getHeight($image) { @@ -76,6 +79,15 @@ class UploadListener $type=$event->getType(); switch($type) { + case "logo": + $file=$event->getFile(); + $filename=$file->getFilename(); + $response = $event->getResponse(); + $response['file'] = $filename; + + $this->minio->upload($file,"uploads/logo/".$filename,true); + break; + default: $file=$event->getFile(); $filename=$file->getFilename(); diff --git a/templates/Config/edit.html.twig b/templates/Config/edit.html.twig index 12b00bc..a18db9e 100755 --- a/templates/Config/edit.html.twig +++ b/templates/Config/edit.html.twig @@ -60,17 +60,17 @@ {% set color = app.session.get('colorbgbodylight') %} {% endif %} - + Modifier {% elseif config.type=="header" %}
- + Modifier
{% elseif config.type=="image" %}
- + Modifier
{% endif %} diff --git a/templates/Config/logo.html.twig b/templates/Config/logo.html.twig index f71cabf..fb21677 100644 --- a/templates/Config/logo.html.twig +++ b/templates/Config/logo.html.twig @@ -24,7 +24,7 @@