From 30cba364f3eb0bf07d1ea9f408029366e5a02a7e Mon Sep 17 00:00:00 2001 From: Junior-Shyko Date: Thu, 27 Mar 2025 14:57:48 -0300 Subject: [PATCH 01/11] =?UTF-8?q?=F0=9F=94=A8=20#13=20-=20add=20supervisor?= =?UTF-8?q?=20para=20varios=20comandos=20artisan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 19 +------------------ docker/php-fpm/Dockerfile | 4 +++- docker/php-fpm/supervisord.conf | 31 +++++++++++++++++++++++++++++++ docker/supervisord.conf | 29 ----------------------------- 4 files changed, 35 insertions(+), 48 deletions(-) create mode 100644 docker/php-fpm/supervisord.conf delete mode 100644 docker/supervisord.conf diff --git a/docker-compose.yml b/docker-compose.yml index a9db9fb..999dcbe 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,6 +49,7 @@ services: build: context: "./docker/php-fpm" container_name: php_api + command: ["supervisord", "-c", "/etc/supervisord.conf"] volumes: - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG} networks: @@ -71,22 +72,6 @@ services: networks: - sail - lab -# rabbitmq: -# image: rabbitmq:3-management -# container_name: rabbitmq -# hostname: rabbitmq -# ports: -# - "5672:5672" -# - "15672:15672" -# networks: -# - lab -# volumes: -# - $PWD/storage/rabbitmq1:/var/lib/rabbitmq -# environment: -# - RABBITMQ_ERLANG_COOKIE=This_is_my_secret_phrase -# - RABBITMQ_DEFAULT_USER=mqadmin -# - RABBITMQ_DEFAULT_PASS=Admin123XX_ -# - CLUSTERED=true networks: sail: driver: bridge @@ -97,5 +82,3 @@ volumes: driver: local sail-redis: driver: local - # sail-meilisearch: - # driver: local diff --git a/docker/php-fpm/Dockerfile b/docker/php-fpm/Dockerfile index e5037d7..6e7d082 100644 --- a/docker/php-fpm/Dockerfile +++ b/docker/php-fpm/Dockerfile @@ -27,7 +27,8 @@ RUN apt-get update && \ libreadline-dev \ libgmp-dev \ mariadb-client \ - unzip + unzip \ + supervisor # Install soap extention RUN docker-php-ext-install soap @@ -155,6 +156,7 @@ RUN usermod -u 1000 www-data WORKDIR /var/www/html COPY ./docker-entrypoint.sh /usr/local/bin/ +COPY ./supervisord.conf /etc/supervisord.conf RUN chmod +x /usr/local/bin/docker-entrypoint.sh RUN ln -s /usr/local/bin/docker-entrypoint.sh / ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/docker/php-fpm/supervisord.conf b/docker/php-fpm/supervisord.conf new file mode 100644 index 0000000..4c6b060 --- /dev/null +++ b/docker/php-fpm/supervisord.conf @@ -0,0 +1,31 @@ +[supervisord] +nodaemon=true +### RODAR OS WORKS +[program:diligence_consumer] +command=php artisan diligence:consumer +autostart=true +autorestart=true +stderr_logfile=/dev/stderr +stdout_logfile=/dev/stdout + +[program:rabbitmq_consumer] +command=php artisan rabbitmq:consume-published-recourse-emails +autostart=true +autorestart=true +stderr_logfile=/dev/stderr +stdout_logfile=/dev/stdout + +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +# [program:php] +# command=%(ENV_SUPERVISOR_PHP_COMMAND)s +#user=%(ENV_SUPERVISOR_PHP_USER)s +# environment=LARAVEL_SAIL="1" +# stdout_logfile=/dev/stdout +# stdout_logfile_maxbytes=0 +# stderr_logfile=/dev/stderr +# stderr_logfile_maxbytes=0 diff --git a/docker/supervisord.conf b/docker/supervisord.conf deleted file mode 100644 index 65cac38..0000000 --- a/docker/supervisord.conf +++ /dev/null @@ -1,29 +0,0 @@ -#[supervisord] -#nodaemon=true -#user=root -#logfile=/var/log/supervisor/supervisord.log -#pidfile=/var/run/supervisord.pid - -#[program:php] -# command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 -#command=%(ENV_SUPERVISOR_PHP_COMMAND)s -#; user=sail -#environment=LARAVEL_SAIL="1" -#stdout_logfile=/dev/stdout -#stdout_logfile_maxbytes=0 -#stderr_logfile=/dev/stderr -#stderr_logfile_maxbytes=0 -[supervisord] -nodaemon=true -user=root -logfile=/var/log/supervisor/supervisord.log -pidfile=/var/run/supervisord.pid - -[program:php] -command=%(ENV_SUPERVISOR_PHP_COMMAND)s -#user=%(ENV_SUPERVISOR_PHP_USER)s -environment=LARAVEL_SAIL="1" -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 From 08c82deb94b7cecba17634c0282b49873344e814 Mon Sep 17 00:00:00 2001 From: Junior-Shyko Date: Mon, 31 Mar 2025 14:05:08 -0300 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=93=9D=20Add=20info=20no=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 11 +++++++++++ docker/logs/nginx/error.log | 2 ++ 3 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 622b65a..5fdd7b2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ docker-data data storage /docker/logs/ +docker/logs/nginx/error.log \ No newline at end of file diff --git a/README.md b/README.md index 1d88531..e8b5002 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,14 @@ Instruções [aqui](https://github.com/secultce/api-email/blob/develop/docs/TASK ### JWT Auth É usado o pacote jwt-auth (https://jwt-auth.readthedocs.io/en/develop/) para criar tokens de acesso que são enviados nas requisições, para garantir que pessoas não autorizadas acessem os dados retornados nos endpoints usados na api. + +### Workers + +Para cada novo componente que precise trabalhar com as tarefas de filas por exemplo, deve ser adicionar o comando no arquivo *supervisord.conf* , que se encontra no caminho ***docker/php-fpm/*** e escrever algo como: + + [program:name_command] + command=php artisan command:command + autostart=true + autorestart=true + stderr_logfile=/dev/stderr + stdout_logfile=/dev/stdout \ No newline at end of file diff --git a/docker/logs/nginx/error.log b/docker/logs/nginx/error.log index a99eb36..887ffa4 100644 --- a/docker/logs/nginx/error.log +++ b/docker/logs/nginx/error.log @@ -1,3 +1,5 @@ 2024/03/20 00:52:56 [emerg] 12#12: host not found in upstream "php-fpm:9000" in /etc/nginx/conf.d/upstream.conf:1 2024/03/20 00:58:24 [emerg] 12#12: host not found in upstream "php-fpm:9000" in /etc/nginx/conf.d/upstream.conf:1 2024/03/20 01:00:32 [emerg] 12#12: host not found in upstream "php-fpm:9000" in /etc/nginx/conf.d/upstream.conf:1 +2025/03/28 00:00:00 [notice] 65#65: signal process started +2025/03/28 00:00:00 [notice] 66#66: signal process started From c4d56033dd70f397b122036bac152fa2d96540a4 Mon Sep 17 00:00:00 2001 From: Ronny John Date: Mon, 31 Mar 2025 14:34:00 -0300 Subject: [PATCH 03/11] chore: Update PHP Version --- .editorconfig | 3 - .env.testing | 8 +- .gitignore | 4 +- composer.json | 8 +- composer.lock | 219 ++++++++++++++++++++++++++++++++++- docker-compose.yml | 33 +++--- docker/php-fpm/99-xdebug.ini | 9 ++ docker/php-fpm/Dockerfile | 4 +- phpunit.xml | 1 - 9 files changed, 258 insertions(+), 31 deletions(-) create mode 100644 docker/php-fpm/99-xdebug.ini diff --git a/.editorconfig b/.editorconfig index 8f0de65..dd9a2b5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,6 +13,3 @@ trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 - -[docker-compose.yml] -indent_size = 4 diff --git a/.env.testing b/.env.testing index faafb24..8601ad6 100644 --- a/.env.testing +++ b/.env.testing @@ -1,5 +1,5 @@ APP_NAME=Laravel -APP_ENV=local +APP_ENV=testing APP_KEY=base64:Bq7kBLzjHsoonNahT0xa08HqG4jQD62dw1LRXMCNdCE= APP_DEBUG=true APP_TIMEZONE=UTC @@ -20,9 +20,9 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=pgsql -DB_HOST=172.19.18.161 +DB_HOST=172.19.18.213 DB_PORT=5433 -DB_DATABASE=api_email +DB_DATABASE=api_email_test DB_USERNAME=postgres DB_PASSWORD=12345678 @@ -83,7 +83,7 @@ NGINX_PHP_UPSTREAM_CONTAINER=php-fpm NGINX_PHP_UPSTREAM_PORT=9000 NGINX_SSL_PATH=./docker/nginx/ssl/ ### PARA AS FILAS -MAPA_URL=http://localhost:8088/ +MAPA_URL=http://172.19.18.213:8088/ #### RABBITMW #### RABBITMQ_DEFAULT_HOST=rabbitmq diff --git a/.gitignore b/.gitignore index 5fdd7b2..2cac750 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ docker-data data storage /docker/logs/ -docker/logs/nginx/error.log \ No newline at end of file +docker/logs/nginx/error.log + +xdebug.log diff --git a/composer.json b/composer.json index 5b730fb..edf587f 100644 --- a/composer.json +++ b/composer.json @@ -5,19 +5,21 @@ "keywords": ["laravel", "framework"], "license": "MIT", "require": { - "php": "^8.2", + "php": "^8.4", "guzzlehttp/guzzle": "^7.0", "juniorshyko/phpextensive": "^1.0", "laravel/framework": "^12.0", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.9", "php-amqplib/php-amqplib": "^3.2", - "tymon/jwt-auth": "^2.1" + "tymon/jwt-auth": "^2.1", + "ext-pdo": "*" }, "require-dev": { + "brianium/paratest": "^7.8", "fakerphp/faker": "^1.23", "laravel/breeze": "^2.0", - "laravel/pint": "^1.13", + "laravel/pint": "^1.21", "laravel/sail": "^1.26", "lucascudo/laravel-pt-br-localization": "^3.0", "mockery/mockery": "^1.6", diff --git a/composer.lock b/composer.lock index 4a2a16f..e7eacb8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cdabfb1ddb3c492f8353a47e1b654f65", + "content-hash": "c517e5610ee4326849a3e0b32f2eadbe", "packages": [ { "name": "brick/math", @@ -6421,6 +6421,99 @@ } ], "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.8.3", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "a585c346ddf1bec22e51e20b5387607905604a71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/a585c346ddf1bec22e51e20b5387607905604a71", + "reference": "a585c346ddf1bec22e51e20b5387607905604a71", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.1.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^11.0.9 || ^12.0.4", + "phpunit/php-file-iterator": "^5.1.0 || ^6", + "phpunit/php-timer": "^7.0.1 || ^8", + "phpunit/phpunit": "^11.5.11 || ^12.0.6", + "sebastian/environment": "^7.2.0 || ^8", + "symfony/console": "^6.4.17 || ^7.2.1", + "symfony/process": "^6.4.19 || ^7.2.4" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^2.1.6", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.4", + "phpstan/phpstan-strict-rules": "^2.0.3", + "squizlabs/php_codesniffer": "^3.11.3", + "symfony/filesystem": "^6.4.13 || ^7.2.0" + }, + "bin": [ + "bin/paratest", + "bin/paratest_for_phpstorm" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.8.3" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2025-03-05T08:29:11+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -6484,6 +6577,67 @@ }, "time": "2024-11-21T13:46:39+00:00" }, + { + "name": "fidry/cpu-core-counter", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^1.9.2", + "phpstan/phpstan-deprecation-rules": "^1.0.0", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, { "name": "filp/whoops", "version": "2.18.0", @@ -6606,6 +6760,66 @@ }, "time": "2020-07-09T08:09:16+00:00" }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, { "name": "laravel/breeze", "version": "v2.3.6", @@ -9125,7 +9339,8 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.4", + "ext-pdo": "*" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/docker-compose.yml b/docker-compose.yml index 999dcbe..13a87bc 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,19 @@ -version: '3' services: - db: - image: postgres:15 + db-email: + image: postgres:16 + restart: always ports: - "5433:5432" environment: - - DEBUG=true - - POSTGRES_DB=api_email - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=12345678 + - POSTGRES_DB=${DB_DATABASE} + - POSTGRES_USER=${DB_USERNAME} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_HOST_AUTH_METHOD=trust volumes: - - ./data:/var/lib/postgresql + - sail-db:/var/lib/postgresql/data + networks: + - sail + redis: image: 'redis:alpine' ports: @@ -21,10 +24,7 @@ services: - sail - lab healthcheck: - test: - - CMD - - redis-cli - - ping + test: [ "CMD", "redis-cli", "ping" ] retries: 3 timeout: 5s @@ -39,12 +39,12 @@ services: - ${NGINX_HOST_LOG_PATH}:/var/log/nginx - ${NGINX_SITES_PATH}:/etc/nginx/sites-available ports: - # port mappings, host to docker - "${NGINX_HOST_HTTPS_PORT}:443" - "${NGINX_HOST_HTTP_PORT}:80" networks: - sail - lab + fpm-email: build: context: "./docker/php-fpm" @@ -52,11 +52,13 @@ services: command: ["supervisord", "-c", "/etc/supervisord.conf"] volumes: - ${APP_CODE_PATH_HOST}:${APP_CODE_PATH_CONTAINER}${APP_CODE_CONTAINER_FLAG} + - ./docker/php-fpm/99-xdebug.ini:/usr/local/etc/php/conf.d/99-xdebug.ini networks: - sail - lab expose: - "9000" + mailpit: image: 'axllent/mailpit:latest' ports: @@ -65,6 +67,7 @@ services: networks: - sail - lab + memcached: image: 'memcached:alpine' ports: @@ -72,13 +75,15 @@ services: networks: - sail - lab + networks: sail: driver: bridge lab: external: true + volumes: - sail-mysql: + sail-db: driver: local sail-redis: driver: local diff --git a/docker/php-fpm/99-xdebug.ini b/docker/php-fpm/99-xdebug.ini new file mode 100644 index 0000000..c07042c --- /dev/null +++ b/docker/php-fpm/99-xdebug.ini @@ -0,0 +1,9 @@ +zend_extension=xdebug.so +xdebug.mode=off +xdebug.start_with_request=trigger +xdebug.discover_client_host=1 +xdebug.client_host=host.docker.internal +xdebug.output_dir=/var/www/html +xdebug.log=/var/www/html/xdebug.log +xdebug.log_level=10 +xdebug.show_local_vars=1 diff --git a/docker/php-fpm/Dockerfile b/docker/php-fpm/Dockerfile index 6e7d082..bae1e4e 100644 --- a/docker/php-fpm/Dockerfile +++ b/docker/php-fpm/Dockerfile @@ -1,8 +1,6 @@ # Copied from cyberduck/php-fpm-laravel #https://github.com/Cyber-Duck/php-fpm-laravel/blob/8.1/Dockerfile -FROM php:8.3-fpm - -MAINTAINER support@cyber-duck.co.uk +FROM php:8.4-fpm ENV COMPOSER_MEMORY_LIMIT='-1' ARG NODE_VERSION=18 diff --git a/phpunit.xml b/phpunit.xml index 06c7ff8..5c3671d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,7 +22,6 @@ - From 4f26ccc7bc5037cfe4dbc8c1d9c136e33db89d6d Mon Sep 17 00:00:00 2001 From: Junior-Shyko Date: Wed, 9 Apr 2025 13:46:55 -0300 Subject: [PATCH 04/11] =?UTF-8?q?=F0=9F=9A=80=20#13=20-=20Adicionado=20wor?= =?UTF-8?q?kers=20no=20supervisord?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/nginx/.gitignore | 4 ++ docker/prod/nginx/Dockerfile | 34 ++++++++++++++ docker/prod/nginx/conf.d/app.conf | 17 +++++++ docker/prod/nginx/logrotate/nginx | 14 ++++++ docker/prod/nginx/nginx.conf | 34 ++++++++++++++ docker/prod/nginx/sites/laravel.conf | 57 ++++++++++++++++++++++++ docker/prod/nginx/ssl/generate-keys.sh | 4 ++ docker/prod/nginx/startup.sh | 23 ++++++++++ docker/prod/php-fpm/Dockerfile | 46 +++++++++++++++++++ docker/prod/php-fpm/docker-entrypoint.sh | 43 ++++++++++++++++++ docker/prod/php-fpm/laravel.ini | 13 ++++++ docker/prod/php-fpm/published.sh | 7 +++ docker/prod/php-fpm/supervisord.conf | 39 ++++++++++++++++ docker/prod/php-fpm/works.sh | 7 +++ 14 files changed, 342 insertions(+) create mode 100644 docker/prod/nginx/.gitignore create mode 100644 docker/prod/nginx/Dockerfile create mode 100644 docker/prod/nginx/conf.d/app.conf create mode 100644 docker/prod/nginx/logrotate/nginx create mode 100644 docker/prod/nginx/nginx.conf create mode 100644 docker/prod/nginx/sites/laravel.conf create mode 100644 docker/prod/nginx/ssl/generate-keys.sh create mode 100644 docker/prod/nginx/startup.sh create mode 100644 docker/prod/php-fpm/Dockerfile create mode 100644 docker/prod/php-fpm/docker-entrypoint.sh create mode 100644 docker/prod/php-fpm/laravel.ini create mode 100755 docker/prod/php-fpm/published.sh create mode 100644 docker/prod/php-fpm/supervisord.conf create mode 100755 docker/prod/php-fpm/works.sh diff --git a/docker/prod/nginx/.gitignore b/docker/prod/nginx/.gitignore new file mode 100644 index 0000000..003cd8e --- /dev/null +++ b/docker/prod/nginx/.gitignore @@ -0,0 +1,4 @@ +*.crt +*.csr +*.key +*.pem \ No newline at end of file diff --git a/docker/prod/nginx/Dockerfile b/docker/prod/nginx/Dockerfile new file mode 100644 index 0000000..b6e1a60 --- /dev/null +++ b/docker/prod/nginx/Dockerfile @@ -0,0 +1,34 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/ + +RUN apk update \ + && apk upgrade \ + && apk --update add logrotate \ + && apk add --no-cache openssl \ + && apk add --no-cache bash + +RUN apk add --no-cache curl + +RUN set -x ; \ + addgroup -g 82 -S www-data ; \ + adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1 + +ARG PHP_UPSTREAM_CONTAINER=fpm-email +ARG PHP_UPSTREAM_PORT=9000 + +# Create 'messages' file used from 'logrotate' +RUN touch /var/log/messages + +# Copy 'logrotate' config file +COPY logrotate/nginx /etc/logrotate.d/ + +# Set upstream conf and remove the default conf +RUN echo "upstream php-upstream { server ${PHP_UPSTREAM_CONTAINER}:${PHP_UPSTREAM_PORT}; }" > /etc/nginx/conf.d/upstream.conf \ + && rm /etc/nginx/conf.d/default.conf + +ADD ./startup.sh /opt/startup.sh +RUN sed -i 's/\r//g' /opt/startup.sh +CMD ["/bin/bash", "/opt/startup.sh"] + +EXPOSE 80 81 443 diff --git a/docker/prod/nginx/conf.d/app.conf b/docker/prod/nginx/conf.d/app.conf new file mode 100644 index 0000000..cdaf0ad --- /dev/null +++ b/docker/prod/nginx/conf.d/app.conf @@ -0,0 +1,17 @@ +server { + listen 80; + index index.php; + server_name localhost; + root /var/www/public; + location / { + try_files $uri $uri/ /index.php?$query_string; + } + location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass app:9000; # Aponta para o serviço `app` no docker-compose + 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/docker/prod/nginx/logrotate/nginx b/docker/prod/nginx/logrotate/nginx new file mode 100644 index 0000000..8c89a83 --- /dev/null +++ b/docker/prod/nginx/logrotate/nginx @@ -0,0 +1,14 @@ +/var/log/nginx/*.log { + daily + missingok + rotate 32 + compress + delaycompress + nodateext + notifempty + create 644 www-data root + sharedscripts + postrotate + [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid` + endscript +} diff --git a/docker/prod/nginx/nginx.conf b/docker/prod/nginx/nginx.conf new file mode 100644 index 0000000..56ededb --- /dev/null +++ b/docker/prod/nginx/nginx.conf @@ -0,0 +1,34 @@ +user www-data; +worker_processes 4; +pid /run/nginx.pid; +daemon off; + +events { + worker_connections 2048; + multi_accept on; + use epoll; +} + +http { + server_tokens off; + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 15; + types_hash_max_size 2048; + client_max_body_size 20M; + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /dev/stdout; + error_log /dev/stderr; + gzip on; + gzip_disable "msie6"; + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-available/*.conf; + open_file_cache off; # Disabled for issue 619 + charset UTF-8; +} diff --git a/docker/prod/nginx/sites/laravel.conf b/docker/prod/nginx/sites/laravel.conf new file mode 100644 index 0000000..5e7b01c --- /dev/null +++ b/docker/prod/nginx/sites/laravel.conf @@ -0,0 +1,57 @@ + +server { + + listen 80; + listen [::]:80; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; + charset utf-8; + + # For https + listen 443 ssl; + listen [::]:443 ssl ipv6only=on; + ssl_certificate /etc/nginx/ssl/default.crt; + ssl_certificate_key /etc/nginx/ssl/default.key; + + server_name laravel.dev.local; + root /var/www/html/public; + index index.php index.html index.htm; + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php$ { + try_files $uri /index.php =404; + fastcgi_pass php-upstream; + fastcgi_index index.php; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + #fixes timeouts + fastcgi_read_timeout 600; + include fastcgi_params; + } + + location ~ /\.ht { + deny all; + } + + location /.well-known/acme-challenge/ { + root /var/www/letsencrypt/; + log_not_found off; + } + location = /favicon.ico { + access_log off; + log_not_found off; + } + location = /robots.txt { + access_log off; + log_not_found off; + } + + + error_log /var/log/nginx/laravel_error.log; + access_log /var/log/nginx/laravel_access.log; +} diff --git a/docker/prod/nginx/ssl/generate-keys.sh b/docker/prod/nginx/ssl/generate-keys.sh new file mode 100644 index 0000000..8f315d5 --- /dev/null +++ b/docker/prod/nginx/ssl/generate-keys.sh @@ -0,0 +1,4 @@ +openssl genrsa -out "./default.key" 2048 +chmod 644 ./default.key +openssl req -new -key "./default.key" -out "./default.csr" -subj "/CN=default/O=default/C=UK" +openssl x509 -req -days 365 -in "./default.csr" -signkey "./default.key" -out "./default.crt" diff --git a/docker/prod/nginx/startup.sh b/docker/prod/nginx/startup.sh new file mode 100644 index 0000000..b5bbe4f --- /dev/null +++ b/docker/prod/nginx/startup.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ ! -f /etc/nginx/ssl/default.crt ]; then + openssl genrsa -out "/etc/nginx/ssl/default.key" 2048 + openssl req -new -key "/etc/nginx/ssl/default.key" -out "/etc/nginx/ssl/default.csr" -subj "/CN=default/O=default/C=UK" + openssl x509 -req -days 365 -in "/etc/nginx/ssl/default.csr" -signkey "/etc/nginx/ssl/default.key" -out "/etc/nginx/ssl/default.crt" + chmod 644 /etc/nginx/ssl/default.key +fi + + +# cron job to restart nginx every 6 hour +(crontab -l ; echo "0 0 */4 * * nginx -s reload") | crontab - + +# Start crond in background +crond -l 2 -b + + +#* * * * * root nginx -s reload >> /var/log/cron.log + +# Start nginx in foreground +echo "NGINX started, daemon will restart every 6 hours now."; +nginx + diff --git a/docker/prod/php-fpm/Dockerfile b/docker/prod/php-fpm/Dockerfile new file mode 100644 index 0000000..1ea2231 --- /dev/null +++ b/docker/prod/php-fpm/Dockerfile @@ -0,0 +1,46 @@ +FROM php:8.4-fpm +RUN apt-get update && \ + apt-get install -y --force-yes --no-install-recommends \ + libmemcached-dev \ + libzip-dev \ + libz-dev \ + libpq-dev \ + libjpeg-dev \ + libpng-dev \ + libfreetype6-dev \ + libssl-dev \ + openssh-server \ + libmagickwand-dev \ + git \ + cron \ + nano \ + libxml2-dev \ + libreadline-dev \ + libgmp-dev \ + mariadb-client \ + unzip \ + supervisor + +RUN docker-php-ext-install soap exif pcntl zip pdo_mysql pdo_pgsql bcmath intl gmp sockets + +RUN pecl install redis && docker-php-ext-enable redis +RUN pecl install memcached && docker-php-ext-enable memcached + +RUN docker-php-ext-install gd && \ + docker-php-ext-configure gd --with-freetype --with-jpeg && \ + docker-php-ext-install gd + +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer +WORKDIR /var/www +COPY . /var/www +COPY ./docker/prod/php-fpm/supervisord.conf /etc/supervisord.conf +COPY ./docker/prod/php-fpm/published.sh /etc/published.sh +COPY ./docker/prod/php-fpm/works.sh /etc/works.sh +COPY ./docker/prod/php-fpm/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN composer install --no-dev --optimize-autoloader +RUN chown -R www-data:www-data /var/www && chmod -R 755 /var/www/storage +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +EXPOSE 9000 +ENTRYPOINT ["docker-entrypoint.sh"] \ No newline at end of file diff --git a/docker/prod/php-fpm/docker-entrypoint.sh b/docker/prod/php-fpm/docker-entrypoint.sh new file mode 100644 index 0000000..97da946 --- /dev/null +++ b/docker/prod/php-fpm/docker-entrypoint.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Destination of env file inside container +ENV_FILE="/var/www/.env" + +for VAR in XDEBUG PHP_IDE_CONFIG REMOTE_HOST +do + if [ -z "${!VAR}" ] && [ -f "${ENV_FILE}" ]; then + VALUE=$(grep $VAR $ENV_FILE | cut -d '=' -f 2-) + if [ ! -z "${VALUE}" ]; then + sed -i "/$VAR/d" ~/.bashrc + echo "export $VAR=$VALUE" >> ~/.bashrc; + fi + fi +done + +if [ -z "${REMOTE_HOST}" ]; then + REMOTE_HOST="host.docker.internal" + sed -i "/REMOTE_HOST/d" ~/.bashrc + echo "export REMOTE_HOST=\"$REMOTE_HOST\"" >> ~/.bashrc; +fi + +. ~/.bashrc + +service cron start + +if [ "true" == "$XDEBUG" ] && [ ! -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ]; then + sed -i '/PHP_IDE_CONFIG/d' /etc/cron.d/laravel-scheduler + if [ ! -z "${PHP_IDE_CONFIG}" ]; then + echo -e "PHP_IDE_CONFIG=\"$PHP_IDE_CONFIG\"\n$(cat /etc/cron.d/laravel-scheduler)" > /etc/cron.d/laravel-scheduler + fi + docker-php-ext-enable xdebug && \ + echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ + echo "xdebug.remote_autostart=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ + echo "xdebug.remote_connect_back=0" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ + echo "xdebug.remote_host=$REMOTE_HOST" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; +elif [ -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ]; then + sed -i '/PHP_IDE_CONFIG/d' /etc/cron.d/laravel-scheduler + rm -rf /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini +fi + +echo "Starting supervisord with command: $@" +exec "$@" \ No newline at end of file diff --git a/docker/prod/php-fpm/laravel.ini b/docker/prod/php-fpm/laravel.ini new file mode 100644 index 0000000..486a2ca --- /dev/null +++ b/docker/prod/php-fpm/laravel.ini @@ -0,0 +1,13 @@ +date.timezone=UTC +display_errors=Off +log_errors=On + +; Maximum amount of memory a script may consume (128MB) +; http://php.net/memory-limit +memory_limit = 128M +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = 20M +; Sets max size of post data allowed. +; http://php.net/post-max-size +post_max_size = 20M diff --git a/docker/prod/php-fpm/published.sh b/docker/prod/php-fpm/published.sh new file mode 100755 index 0000000..f14c4df --- /dev/null +++ b/docker/prod/php-fpm/published.sh @@ -0,0 +1,7 @@ +#!/bin/bash +while true +do + php artisan rabbitmq:consume-published-recourse-emails + echo "Worker da publicação caiu. Reiniciando em 60s..." + sleep 60 +done diff --git a/docker/prod/php-fpm/supervisord.conf b/docker/prod/php-fpm/supervisord.conf new file mode 100644 index 0000000..aedcdce --- /dev/null +++ b/docker/prod/php-fpm/supervisord.conf @@ -0,0 +1,39 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid +logfile_maxbytes=0 # + +[program:php-fpm] +command=/usr/local/sbin/php-fpm --nodaemonize +autostart=true +autorestart=true +stderr_logfile=/var/log/php-fpm.err.log +stdout_logfile=/var/log/php-fpm.out.log +priority=100 + +### RODAR OS WORKS +[program:rabbitmq_published] +command=/bin/bash /etc/published.sh +autostart=true +autorestart=true +stderr_logfile=/var/log/rabbitmq_published.err.log +stdout_logfile=/var/log/rabbitmq_published.out.log +user=www-data +numprocs=1 +priority=200 +logfile_maxbytes=50MB ; Tamanho máximo do log antes de rotacionar (opcional) +logfile_backups=5 + +[program:rabbitmq_pc] +command=/bin/bash /etc/works.sh +autostart=true +autorestart=true +stderr_logfile=/var/log/rabbitmq_consumer.err.log +stdout_logfile=/var/log/rabbitmq_consumer.out.log +user=www-data +numprocs=1 +priority=200 +logfile_maxbytes=50MB ; Tamanho máximo do log antes de rotacionar (opcional) +logfile_backups=5 \ No newline at end of file diff --git a/docker/prod/php-fpm/works.sh b/docker/prod/php-fpm/works.sh new file mode 100755 index 0000000..1455b26 --- /dev/null +++ b/docker/prod/php-fpm/works.sh @@ -0,0 +1,7 @@ +#!/bin/bash +while true +do + php artisan diligence:consumer + echo "Worker da PC caiu. Reiniciando em 60s..." + sleep 60 +done From 7b254b16963f6d896d4660affc1c6bcd8e8e1a68 Mon Sep 17 00:00:00 2001 From: Junior-Shyko Date: Wed, 9 Apr 2025 14:34:00 -0300 Subject: [PATCH 05/11] =?UTF-8?q?=F0=9F=93=9D=20#13=20-=20Add=20doc=20de?= =?UTF-8?q?=20como=20buildar=20a=20imagem=20manualmente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/docs/README.MD | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker/prod/docs/README.MD diff --git a/docker/prod/docs/README.MD b/docker/prod/docs/README.MD new file mode 100644 index 0000000..2740b50 --- /dev/null +++ b/docker/prod/docs/README.MD @@ -0,0 +1,12 @@ + +### ✨ CRIAR IMAGEM MANUALMENTE + +✏️ Deve está na raiz do projeto e rodar o seguinte comando: + + - `docker build --no-cache -t secultceara/api-email:latest -f docker/prod/php-fpm/Dockerfile .` + +✏️ Para enviar a imagem criada manualemente para o Docker Hub deve realizar os passos: + + 1. Criar login da conta da secultceara com: `docker login -u {username}` + 2. Depois digitar a senha e após realizar o login com sucesso; + 3. Rodar o comando: `docker push docker.io/secultceara/api-email:latest` \ No newline at end of file From 4948633748d4d4bbc7b68ef8bf11ddb2d00f6a29 Mon Sep 17 00:00:00 2001 From: Ronny John Date: Fri, 11 Apr 2025 11:51:05 -0300 Subject: [PATCH 06/11] chore: Add tests and code style checks --- .env.testing | 11 +- .gitignore | 1 + .../ConsumePublishedRecourseEmails.php | 48 ++- app/Console/Commands/ConsumerCommand.php | 63 ++-- app/Http/Controllers/UserController.php | 58 +--- app/Jobs/NotificationAccountability.php | 12 +- app/Mail/AnswerNotification.php | 4 +- app/Mail/DeadlineForAccountability.php | 20 +- app/Mail/EmailRegistrationOpp.php | 4 +- app/Mail/PublishedRecourse.php | 1 - app/Mail/SendRegistration.php | 1 - app/Models/User.php | 1 + bootstrap/app.php | 17 +- composer.json | 3 +- composer.lock | 179 +++++------ config/app.php | 12 + database/seeders/DatabaseSeeder.php | 2 - lang/pt_BR/validation.php | 300 +++++++++--------- phpunit.xml | 5 + pint.json | 3 + .../deadline-for-accountability.blade.php | 2 +- routes/api.php | 7 +- routes/auth.php | 28 +- routes/console.php | 6 +- routes/web.php | 13 +- tests/Feature/PC/EmailTest.php | 20 +- tests/TestCase.php | 5 +- .../ConsumePublishedRecourseEmailsTest.php | 128 ++++++++ .../Console/Commands/ConsumerCommandTest.php | 111 +++++++ tests/Unit/ExampleTest.php | 16 - tests/Unit/Mail/AnswerNotificationTest.php | 52 +++ .../Mail/DeadlineForAccountabilityTest.php | 173 ++++++++++ tests/Unit/Mail/EmailRegistrationOppTest.php | 90 ++++++ tests/Unit/Mail/PublishedRecourseTest.php | 82 +++++ 34 files changed, 1040 insertions(+), 438 deletions(-) delete mode 100644 app/Mail/SendRegistration.php create mode 100644 tests/Unit/Console/Commands/ConsumePublishedRecourseEmailsTest.php create mode 100644 tests/Unit/Console/Commands/ConsumerCommandTest.php delete mode 100644 tests/Unit/ExampleTest.php create mode 100644 tests/Unit/Mail/AnswerNotificationTest.php create mode 100644 tests/Unit/Mail/DeadlineForAccountabilityTest.php create mode 100644 tests/Unit/Mail/EmailRegistrationOppTest.php create mode 100644 tests/Unit/Mail/PublishedRecourseTest.php diff --git a/.env.testing b/.env.testing index 8601ad6..6306635 100644 --- a/.env.testing +++ b/.env.testing @@ -1,7 +1,7 @@ APP_NAME=Laravel APP_ENV=testing APP_KEY=base64:Bq7kBLzjHsoonNahT0xa08HqG4jQD62dw1LRXMCNdCE= -APP_DEBUG=true +APP_DEBUG=false APP_TIMEZONE=UTC APP_URL=http://localhost @@ -20,11 +20,11 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=pgsql -DB_HOST=172.19.18.213 -DB_PORT=5433 +DB_HOST=db-email +DB_PORT=5432 DB_DATABASE=api_email_test -DB_USERNAME=postgres -DB_PASSWORD=12345678 +DB_USERNAME=api_email +DB_PASSWORD=pass SESSION_DRIVER=database SESSION_LIFETIME=120 @@ -55,7 +55,6 @@ MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" - AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 diff --git a/.gitignore b/.gitignore index 2cac750..40d2b49 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ storage docker/logs/nginx/error.log xdebug.log +tests/coverage-html diff --git a/app/Console/Commands/ConsumePublishedRecourseEmails.php b/app/Console/Commands/ConsumePublishedRecourseEmails.php index 9b3e356..b05da36 100644 --- a/app/Console/Commands/ConsumePublishedRecourseEmails.php +++ b/app/Console/Commands/ConsumePublishedRecourseEmails.php @@ -3,9 +3,11 @@ namespace App\Console\Commands; use App\Mail\PublishedRecourse; +use Exception; use Illuminate\Console\Command; use Illuminate\Support\Facades\Mail; use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Message\AMQPMessage; class ConsumePublishedRecourseEmails extends Command { @@ -25,28 +27,24 @@ class ConsumePublishedRecourseEmails extends Command /** * Execute the console command. + * + * @throws Exception */ - public function handle() + public function handle(): void { - // Conectar ao RabbitMQ - $connection = new AMQPStreamConnection(env('RABBITMQ_DEFAULT_HOST'), env('RABBITMQ_DEFAULT_PORT'), env('RABBITMQ_DEFAULT_USER'), env('RABBITMQ_DEFAULT_PASS')); + $queue = config('app.rabbitmq.queues.published_recourses_queue'); + $connection = new AMQPStreamConnection( + config('app.rabbitmq.host'), + config('app.rabbitmq.port'), + config('app.rabbitmq.user'), + config('app.rabbitmq.pass'), + ); $channel = $connection->channel(); + $channel->queue_declare($queue, false, true, false, false); - $channel->queue_declare('published_recourses_queue', false, true, false, false); $this->info('🎯 Aguardando e-mails para envio...'); - $callback = function ($msg) { - $data = json_decode($msg->body, true); - - if ($this->sendEmail($data)) { - $msg->ack(); // Confirma o processamento - $this->info("📧 E-mail enviado para: {$data['email']}"); - } else { - $this->error("❌ Falha ao enviar e-mail para: {$data['email']}"); - } - }; - - $channel->basic_consume('published_recourses_queue', '', false, false, false, false, $callback); + $channel->basic_consume(queue: $queue, callback: $this->processMessage(...)); while ($channel->is_consuming()) { $channel->wait(); @@ -62,8 +60,24 @@ private function sendEmail($data): bool Mail::to($data['email'])->send(new PublishedRecourse($data)); return true; - } catch (\Exception $e) { + } catch (Exception $e) { + logger($e->getMessage()); + return false; } } + + protected function processMessage(AMQPMessage $msg): void + { + $data = json_decode($msg->body, true); + + if ($this->sendEmail($data)) { + $msg->ack(); // Confirma o processamento + $this->info("📧 E-mail enviado para: {$data['email']}"); + + return; + } + + $this->error("❌ Falha ao enviar e-mail para: {$data['email']}"); + } } diff --git a/app/Console/Commands/ConsumerCommand.php b/app/Console/Commands/ConsumerCommand.php index 7d103e5..c51fa36 100644 --- a/app/Console/Commands/ConsumerCommand.php +++ b/app/Console/Commands/ConsumerCommand.php @@ -3,10 +3,11 @@ namespace App\Console\Commands; use App\Mail\AnswerNotification; -use Illuminate\Support\Facades\Mail; use App\Mail\EmailRegistrationOpp; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Mail; use PhpAmqpLib\Connection\AMQPStreamConnection; +use PhpAmqpLib\Message\AMQPMessage; class ConsumerCommand extends Command { @@ -22,51 +23,57 @@ class ConsumerCommand extends Command * * @var string */ - protected $description = 'Cosumidor das filas do Rabbitmq para as diligências'; + protected $description = 'Consumidor das filas do Rabbitmq para as diligências'; /** * Execute the console command. + * + * @throws \Exception */ - public function handle() + public function handle(): int { - $queue = env('RABBITMQ_QUEUE_PC'); + $queue = config('app.rabbitmq.queues.accountability'); $connection = new AMQPStreamConnection( - env('RABBITMQ_DEFAULT_HOST'), - env('RABBITMQ_DEFAULT_PORT'), - env('RABBITMQ_DEFAULT_USER'), - env('RABBITMQ_DEFAULT_PASS'), - '/' + config('app.rabbitmq.host'), + config('app.rabbitmq.port'), + config('app.rabbitmq.user'), + config('app.rabbitmq.pass'), + '/', ); $channel = $connection->channel(); $channel->queue_declare($queue, false, true, false, false); - $callback = function ($msg) { - $data = json_decode($msg->body); - if( $msg->getRoutingKey() == env('RABBITMQ_QUEUE_PC_ROUTE_KEY_PROP') ) - { - Mail::to($data->email)->send(new EmailRegistrationOpp( - $data->name, - $data->number, - $data->days - )); - } - if($msg->getRoutingKey() == env('RABBITMQ_QUEUE_PC_ROUTE_KEY_ADM')) - { - Mail::to($data->comission)->cc($data->owner)->send(new AnswerNotification( - $data->registration, - )); - }; - $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); - }; $channel->basic_qos(null, 1, null); - $channel->basic_consume($queue, '', false, false, false, false, $callback); + $channel->basic_consume(queue: $queue, callback: $this->processMessage(...)); + while ($channel->is_consuming()) { + $this->info('🎯 Aguardando e-mails para envio...'); $channel->wait(); } $channel->close(); return Command::SUCCESS; + } + + protected function processMessage(AMQPMessage $msg): void + { + $data = json_decode($msg->body); + + if ($msg->getRoutingKey() == config('app.rabbitmq.route_key_prop')) { + Mail::to($data->email)->send(new EmailRegistrationOpp( + $data->name, + $data->number, + $data->days + )); + } + + if ($msg->getRoutingKey() == config('app.rabbitmq.route_key_adm')) { + Mail::to($data->comission)->cc($data->owner)->send(new AnswerNotification( + $data->registration + )); + } + $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); } } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 9e3c5a2..dac8294 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -9,22 +9,6 @@ class UserController extends Controller { - /** - * Display a listing of the resource. - */ - public function index() - { - // - } - - /** - * Show the form for creating a new resource. - */ - public function create() - { - // - } - /** * Handle an incoming registration request. * @@ -32,55 +16,15 @@ public function create() */ public function store(Request $request): JsonResponse { - - // $request->validate([ - // 'name' => ['required', 'string', 'max:255'], - // 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], - // 'password' => ['required', 'confirmed', Rules\Password::defaults()], - // ]); - $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), ]); -// event(new Registered($user)); $token = $user->createToken('here-token-name'); - return response()->json( ['token' => $token->plainTextToken]); - } - - /** - * Display the specified resource. - */ - public function show(string $id) - { - // - } - - /** - * Show the form for editing the specified resource. - */ - public function edit(string $id) - { - // - } - - /** - * Update the specified resource in storage. - */ - public function update(Request $request, string $id) - { - // - } - - /** - * Remove the specified resource from storage. - */ - public function destroy(string $id) - { - // + return response()->json(['token' => $token->plainTextToken]); } public function draft(Request $request) diff --git a/app/Jobs/NotificationAccountability.php b/app/Jobs/NotificationAccountability.php index 3fe2a04..67ec48b 100644 --- a/app/Jobs/NotificationAccountability.php +++ b/app/Jobs/NotificationAccountability.php @@ -16,15 +16,13 @@ class NotificationAccountability implements ShouldQueue use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $tries = 3; + public $backoff = 60; /** * Create a new job instance. */ - public function __construct(protected $infos) - { - - } + public function __construct(protected $infos) {} /** * Execute the job. @@ -32,13 +30,13 @@ public function __construct(protected $infos) public function handle(): void { foreach ($this->infos as $info) { - $info = (object)$info; + $info = (object) $info; $emailSent = Mail::to($info->user_email)->send(new DeadlineForAccountability($info)); // $info->is_last_notification, último dia para enviar a notificação if ($emailSent instanceof \Illuminate\Mail\SentMessage && $info->is_last_notification) { - Http::post(config('app.mapa_url') . 'bigsheet/updateNotificationStatus', [ + Http::post(config('app.mapa_url').'/bigsheet/updateNotificationStatus', [ 'registration_number' => $info->registration_number, - 'access_token' => config('jwt.secret') + 'access_token' => config('jwt.secret'), ]); } } diff --git a/app/Mail/AnswerNotification.php b/app/Mail/AnswerNotification.php index db08d72..ecdf52c 100644 --- a/app/Mail/AnswerNotification.php +++ b/app/Mail/AnswerNotification.php @@ -3,7 +3,6 @@ namespace App\Mail; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; @@ -18,8 +17,7 @@ class AnswerNotification extends Mailable */ public function __construct( public string $number, - ) - { } + ) {} /** * Get the message envelope. diff --git a/app/Mail/DeadlineForAccountability.php b/app/Mail/DeadlineForAccountability.php index 2543c4a..6a516eb 100644 --- a/app/Mail/DeadlineForAccountability.php +++ b/app/Mail/DeadlineForAccountability.php @@ -3,7 +3,6 @@ namespace App\Mail; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; @@ -13,8 +12,11 @@ class DeadlineForAccountability extends Mailable { use Queueable, SerializesModels; + public string $complementText = 'está na hora de preencher e enviar'; + public string $titleReport = ''; + /** * Create a new message instance. */ @@ -38,18 +40,22 @@ public function envelope(): Envelope */ public function content(): Content { - $e = new Extensive(); - if($this->info->days_current === 85 || $this->info->days_current === 55){ + $e = new Extensive; + if ($this->info->days_current === 85 || $this->info->days_current === 55) { $this->complementText = 'faltam 05 (cinco) dias para o envio'; } - // Mudando o titulo do relatorio - $this->info->notification_type === "REFO" ? $this->titleReport = 'Relatório de Execução Final do Objeto - REFO' : $this->titleReport = 'Relatório de Avaliação Intermediária do Objeto - RAIO'; + + // Mudando o titulo do relatório + $this->titleReport = $this->info->notification_type === 'REFO' + ? 'Relatório de Execução Final do Objeto - REFO' + : 'Relatório de Avaliação Intermediária do Objeto - RAIO'; + return new Content( view: 'emails.deadline-for-accountability', with: [ 'info' => $this->info, - 'days_current' => $e->extensive( $this->info->days_current, Extensive::MALE_NUMBER ), - 'complment_text' => $this->complementText, + 'days_current' => $e->extensive($this->info->days_current, Extensive::MALE_NUMBER), + 'complement_text' => $this->complementText, 'title_report' => $this->titleReport, ] ); diff --git a/app/Mail/EmailRegistrationOpp.php b/app/Mail/EmailRegistrationOpp.php index 9ed395d..17f354a 100644 --- a/app/Mail/EmailRegistrationOpp.php +++ b/app/Mail/EmailRegistrationOpp.php @@ -3,7 +3,6 @@ namespace App\Mail; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; @@ -20,8 +19,7 @@ public function __construct( public string $nameUser, public string $number, public string $days - ) - { } + ) {} /** * Get the message envelope. diff --git a/app/Mail/PublishedRecourse.php b/app/Mail/PublishedRecourse.php index e71a8c7..619b620 100644 --- a/app/Mail/PublishedRecourse.php +++ b/app/Mail/PublishedRecourse.php @@ -3,7 +3,6 @@ namespace App\Mail; use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; diff --git a/app/Mail/SendRegistration.php b/app/Mail/SendRegistration.php deleted file mode 100644 index b3d9bbc..0000000 --- a/app/Mail/SendRegistration.php +++ /dev/null @@ -1 +0,0 @@ -withRouting( @@ -13,15 +15,18 @@ ) ->withMiddleware(function (Middleware $middleware) { $middleware->api(prepend: [ - \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + EnsureFrontendRequestsAreStateful::class, ]); $middleware->alias([ - 'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class, + 'verified' => EnsureEmailIsVerified::class, ]); - - // }) ->withExceptions(function (Exceptions $exceptions) { - // - })->create(); + // $exceptions->alias([ + // 'auth' => \App\Exceptions\AuthenticationException::class, + // 'http' => \App\Exceptions\HttpException::class, + // 'validation' => \App\Exceptions\ValidationException::class, + // ]); + }) + ->create(); diff --git a/composer.json b/composer.json index edf587f..e3fd1bb 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.0", "phpunit/phpunit": "^11.0", - "spatie/laravel-ignition": "^2.4" + "spatie/laravel-ignition": "^2.4", + "ext-pcntl": "*" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index e7eacb8..3a0a2c7 100644 --- a/composer.lock +++ b/composer.lock @@ -645,16 +645,16 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.9.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", "shasum": "" }, "require": { @@ -751,7 +751,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.9.3" }, "funding": [ { @@ -767,20 +767,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-03-27T13:37:11+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.4", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", + "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", "shasum": "" }, "require": { @@ -834,7 +834,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" + "source": "https://github.com/guzzle/promises/tree/2.2.0" }, "funding": [ { @@ -850,20 +850,20 @@ "type": "tidelift" } ], - "time": "2024-10-17T10:06:22+00:00" + "time": "2025-03-27T13:27:01+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", "shasum": "" }, "require": { @@ -950,7 +950,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.7.1" }, "funding": [ { @@ -966,7 +966,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2025-03-27T12:30:47+00:00" }, { "name": "guzzlehttp/uri-template", @@ -1096,16 +1096,16 @@ }, { "name": "laravel/framework", - "version": "v12.3.0", + "version": "v12.7.2", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "ca0412e978f78ecea0cafbe34dd8b18010064f73" + "reference": "a4ba76e06fe6dd02312359f8184ab259900a7780" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/ca0412e978f78ecea0cafbe34dd8b18010064f73", - "reference": "ca0412e978f78ecea0cafbe34dd8b18010064f73", + "url": "https://api.github.com/repos/laravel/framework/zipball/a4ba76e06fe6dd02312359f8184ab259900a7780", + "reference": "a4ba76e06fe6dd02312359f8184ab259900a7780", "shasum": "" }, "require": { @@ -1307,7 +1307,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-03-18T13:49:19+00:00" + "time": "2025-04-03T18:00:49+00:00" }, { "name": "laravel/prompts", @@ -1434,16 +1434,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v2.0.3", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "f379c13663245f7aa4512a7869f62eb14095f23f" + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f379c13663245f7aa4512a7869f62eb14095f23f", - "reference": "f379c13663245f7aa4512a7869f62eb14095f23f", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", "shasum": "" }, "require": { @@ -1491,7 +1491,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2025-02-11T15:03:05+00:00" + "time": "2025-03-19T13:51:03+00:00" }, { "name": "laravel/tinker", @@ -2353,16 +2353,16 @@ }, { "name": "nesbot/carbon", - "version": "3.8.6", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd" + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/ff2f20cf83bd4d503720632ce8a426dc747bf7fd", - "reference": "ff2f20cf83bd4d503720632ce8a426dc747bf7fd", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/6d16a8a015166fe54e22c042e0805c5363aef50d", + "reference": "6d16a8a015166fe54e22c042e0805c5363aef50d", "shasum": "" }, "require": { @@ -2455,7 +2455,7 @@ "type": "tidelift" } ], - "time": "2025-02-20T17:33:38+00:00" + "time": "2025-03-27T12:57:33+00:00" }, { "name": "nette/schema", @@ -2521,16 +2521,16 @@ }, { "name": "nette/utils", - "version": "v4.0.5", + "version": "v4.0.6", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" + "reference": "ce708655043c7050eb050df361c5e313cf708309" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309", + "reference": "ce708655043c7050eb050df361c5e313cf708309", "shasum": "" }, "require": { @@ -2601,9 +2601,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.5" + "source": "https://github.com/nette/utils/tree/v4.0.6" }, - "time": "2024-08-07T15:39:19+00:00" + "time": "2025-03-30T21:06:30+00:00" }, { "name": "nikic/php-parser", @@ -3912,16 +3912,16 @@ }, { "name": "symfony/console", - "version": "v7.2.1", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + "reference": "e51498ea18570c062e7df29d05a7003585b19b88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88", + "reference": "e51498ea18570c062e7df29d05a7003585b19b88", "shasum": "" }, "require": { @@ -3985,7 +3985,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.1" + "source": "https://github.com/symfony/console/tree/v7.2.5" }, "funding": [ { @@ -4001,7 +4001,7 @@ "type": "tidelift" } ], - "time": "2024-12-11T03:49:26+00:00" + "time": "2025-03-12T08:11:12+00:00" }, { "name": "symfony/css-selector", @@ -4137,16 +4137,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.2.4", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "aabf79938aa795350c07ce6464dd1985607d95d5" + "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5", - "reference": "aabf79938aa795350c07ce6464dd1985607d95d5", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", + "reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b", "shasum": "" }, "require": { @@ -4192,7 +4192,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.4" + "source": "https://github.com/symfony/error-handler/tree/v7.2.5" }, "funding": [ { @@ -4208,7 +4208,7 @@ "type": "tidelift" } ], - "time": "2025-02-02T20:27:07+00:00" + "time": "2025-03-03T07:12:39+00:00" }, { "name": "symfony/event-dispatcher", @@ -4432,16 +4432,16 @@ }, { "name": "symfony/http-foundation", - "version": "v7.2.3", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" + "reference": "371272aeb6286f8135e028ca535f8e4d6f114126" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", - "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/371272aeb6286f8135e028ca535f8e4d6f114126", + "reference": "371272aeb6286f8135e028ca535f8e4d6f114126", "shasum": "" }, "require": { @@ -4490,7 +4490,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" + "source": "https://github.com/symfony/http-foundation/tree/v7.2.5" }, "funding": [ { @@ -4506,20 +4506,20 @@ "type": "tidelift" } ], - "time": "2025-01-17T10:56:55+00:00" + "time": "2025-03-25T15:54:33+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.2.4", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "9f1103734c5789798fefb90e91de4586039003ed" + "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed", - "reference": "9f1103734c5789798fefb90e91de4586039003ed", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b1fe91bc1fa454a806d3f98db4ba826eb9941a54", + "reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54", "shasum": "" }, "require": { @@ -4604,7 +4604,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.4" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.5" }, "funding": [ { @@ -4620,7 +4620,7 @@ "type": "tidelift" } ], - "time": "2025-02-26T11:01:22+00:00" + "time": "2025-03-28T13:32:50+00:00" }, { "name": "symfony/mailer", @@ -5424,16 +5424,16 @@ }, { "name": "symfony/process", - "version": "v7.2.4", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d", + "reference": "87b7c93e57df9d8e39a093d32587702380ff045d", "shasum": "" }, "require": { @@ -5465,7 +5465,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.4" + "source": "https://github.com/symfony/process/tree/v7.2.5" }, "funding": [ { @@ -5481,7 +5481,7 @@ "type": "tidelift" } ], - "time": "2025-02-05T08:33:46+00:00" + "time": "2025-03-13T12:21:46+00:00" }, { "name": "symfony/routing", @@ -7214,38 +7214,39 @@ }, { "name": "nunomaduro/collision", - "version": "v8.7.0", + "version": "v8.8.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "586cb8181a257a2152b6a855ca8d9598878a1a26" + "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/586cb8181a257a2152b6a855ca8d9598878a1a26", - "reference": "586cb8181a257a2152b6a855ca8d9598878a1a26", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/4cf9f3b47afff38b139fb79ce54fc71799022ce8", + "reference": "4cf9f3b47afff38b139fb79ce54fc71799022ce8", "shasum": "" }, "require": { - "filp/whoops": "^2.17.0", + "filp/whoops": "^2.18.0", "nunomaduro/termwind": "^2.3.0", "php": "^8.2.0", - "symfony/console": "^7.2.1" + "symfony/console": "^7.2.5" }, "conflict": { - "laravel/framework": "<11.39.1 || >=13.0.0", - "phpunit/phpunit": "<11.5.3 || >=12.0.0" + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" }, "require-dev": { - "larastan/larastan": "^2.10.0", - "laravel/framework": "^11.44.2", + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.2", + "laravel/framework": "^11.44.2 || ^12.6", "laravel/pint": "^1.21.2", "laravel/sail": "^1.41.0", "laravel/sanctum": "^4.0.8", "laravel/tinker": "^2.10.1", - "orchestra/testbench-core": "^9.12.0", - "pestphp/pest": "^3.7.4", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "orchestra/testbench-core": "^9.12.0 || ^10.1", + "pestphp/pest": "^3.8.0", + "sebastian/environment": "^7.2.0 || ^8.0" }, "type": "library", "extra": { @@ -7308,7 +7309,7 @@ "type": "patreon" } ], - "time": "2025-03-14T22:37:40+00:00" + "time": "2025-04-03T14:33:09+00:00" }, { "name": "phar-io/manifest", @@ -9212,16 +9213,16 @@ }, { "name": "symfony/yaml", - "version": "v7.2.3", + "version": "v7.2.5", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", - "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", + "reference": "4c4b6f4cfcd7e52053f0c8bfad0f7f30fb924912", "shasum": "" }, "require": { @@ -9264,7 +9265,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.2.3" + "source": "https://github.com/symfony/yaml/tree/v7.2.5" }, "funding": [ { @@ -9280,7 +9281,7 @@ "type": "tidelift" } ], - "time": "2025-01-07T12:55:42+00:00" + "time": "2025-03-03T07:12:39+00:00" }, { "name": "theseer/tokenizer", diff --git a/config/app.php b/config/app.php index ee550c1..2dbd3c5 100644 --- a/config/app.php +++ b/config/app.php @@ -125,4 +125,16 @@ 'mapa_url' => env('MAPA_URL'), + 'rabbitmq' => [ + 'host' => env('RABBITMQ_DEFAULT_HOST'), + 'port' => env('RABBITMQ_DEFAULT_PORT'), + 'user' => env('RABBITMQ_DEFAULT_USER'), + 'pass' => env('RABBITMQ_DEFAULT_PASS'), + 'queues' => [ + 'accountability' => env('RABBITMQ_QUEUE_PC'), + 'published_recourses' => env('RABBITMQ_QUEUE_PUBLISHED_RECOURSES'), + ], + 'route_key_prop' => env('RABBITMQ_QUEUE_PC_ROUTE_KEY_PROP'), + 'route_key_adm' => env('RABBITMQ_QUEUE_PC_ROUTE_KEY_ADM'), + ], ]; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index d01a0ef..5b566ca 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -13,8 +13,6 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // User::factory(10)->create(); - User::factory()->create([ 'name' => 'Test User', 'email' => 'test@example.com', diff --git a/lang/pt_BR/validation.php b/lang/pt_BR/validation.php index 056d64a..778243b 100644 --- a/lang/pt_BR/validation.php +++ b/lang/pt_BR/validation.php @@ -13,154 +13,154 @@ | */ - 'accepted' => 'O campo :attribute deve ser aceito.', - 'accepted_if' => 'O :attribute deve ser aceito quando :other for :value.', - 'active_url' => 'O campo :attribute não é uma URL válida.', - 'after' => 'O campo :attribute deve ser uma data posterior a :date.', - 'after_or_equal' => 'O campo :attribute deve ser uma data posterior ou igual a :date.', - 'alpha' => 'O campo :attribute só pode conter letras.', - 'alpha_dash' => 'O campo :attribute só pode conter letras, números e traços.', - 'alpha_num' => 'O campo :attribute só pode conter letras e números.', - 'array' => 'O campo :attribute deve ser uma matriz.', - 'ascii' => 'O campo :attribute deve conter somente caracteres alfanuméricos.', - 'before' => 'O campo :attribute deve ser uma data anterior :date.', - 'before_or_equal' => 'O campo :attribute deve ser uma data anterior ou igual a :date.', - 'between' => [ + 'accepted' => 'O campo :attribute deve ser aceito.', + 'accepted_if' => 'O :attribute deve ser aceito quando :other for :value.', + 'active_url' => 'O campo :attribute não é uma URL válida.', + 'after' => 'O campo :attribute deve ser uma data posterior a :date.', + 'after_or_equal' => 'O campo :attribute deve ser uma data posterior ou igual a :date.', + 'alpha' => 'O campo :attribute só pode conter letras.', + 'alpha_dash' => 'O campo :attribute só pode conter letras, números e traços.', + 'alpha_num' => 'O campo :attribute só pode conter letras e números.', + 'array' => 'O campo :attribute deve ser uma matriz.', + 'ascii' => 'O campo :attribute deve conter somente caracteres alfanuméricos.', + 'before' => 'O campo :attribute deve ser uma data anterior :date.', + 'before_or_equal' => 'O campo :attribute deve ser uma data anterior ou igual a :date.', + 'between' => [ 'numeric' => 'O campo :attribute deve ser entre :min e :max.', - 'file' => 'O campo :attribute deve ser entre :min e :max kilobytes.', - 'string' => 'O campo :attribute deve ser entre :min e :max caracteres.', - 'array' => 'O campo :attribute deve ter entre :min e :max itens.', + 'file' => 'O campo :attribute deve ser entre :min e :max kilobytes.', + 'string' => 'O campo :attribute deve ser entre :min e :max caracteres.', + 'array' => 'O campo :attribute deve ter entre :min e :max itens.', ], - 'boolean' => 'O campo :attribute deve ser verdadeiro ou falso.', - 'can' => 'O campo :attribute contém valores não autorizado.', - 'confirmed' => 'O campo :attribute de confirmação não confere.', - 'contains' => 'O campo :attribute está faltando um valor obrigatório.', - 'current_password' => 'A senha está incorreta.', - 'date' => 'O campo :attribute não é uma data válida.', - 'date_equals' => 'O campo :attribute deve ser uma data igual a :date.', - 'date_format' => 'O campo :attribute não corresponde ao formato :format.', - 'decimal' => 'O campo :attribute deve ter :decimal casas decimais.', - 'declined' => 'O :attribute deve ser recusado.', - 'declined_if' => 'O :attribute deve ser recusado quando :other for :value.', - 'different' => 'Os campos :attribute e :other devem ser diferentes.', - 'digits' => 'O campo :attribute deve ter :digits dígitos.', - 'digits_between' => 'O campo :attribute deve ter entre :min e :max dígitos.', - 'dimensions' => 'O campo :attribute tem dimensões de imagem inválidas.', - 'distinct' => 'O campo :attribute tem um valor duplicado.', - 'doesnt_end_with' => 'O campo :attribute não pode terminar com um dos seguintes: :values.', - 'doesnt_start_with' => 'O :attribute não pode começar com um dos seguintes: :values.', - 'email' => 'O campo :attribute deve ser um endereço de e-mail válido.', - 'ends_with' => 'O campo :attribute deve terminar com um dos seguintes: :values', - 'enum' => 'O :attribute selecionado é inválido.', - 'exists' => 'O campo :attribute selecionado é inválido.', - 'extensions' => 'O campo :attribute deve conter uma das seguintes extensões: :values.', - 'file' => 'O campo :attribute deve ser um arquivo.', - 'filled' => 'O campo :attribute deve ter um valor.', + 'boolean' => 'O campo :attribute deve ser verdadeiro ou falso.', + 'can' => 'O campo :attribute contém valores não autorizado.', + 'confirmed' => 'O campo :attribute de confirmação não confere.', + 'contains' => 'O campo :attribute está faltando um valor obrigatório.', + 'current_password' => 'A senha está incorreta.', + 'date' => 'O campo :attribute não é uma data válida.', + 'date_equals' => 'O campo :attribute deve ser uma data igual a :date.', + 'date_format' => 'O campo :attribute não corresponde ao formato :format.', + 'decimal' => 'O campo :attribute deve ter :decimal casas decimais.', + 'declined' => 'O :attribute deve ser recusado.', + 'declined_if' => 'O :attribute deve ser recusado quando :other for :value.', + 'different' => 'Os campos :attribute e :other devem ser diferentes.', + 'digits' => 'O campo :attribute deve ter :digits dígitos.', + 'digits_between' => 'O campo :attribute deve ter entre :min e :max dígitos.', + 'dimensions' => 'O campo :attribute tem dimensões de imagem inválidas.', + 'distinct' => 'O campo :attribute tem um valor duplicado.', + 'doesnt_end_with' => 'O campo :attribute não pode terminar com um dos seguintes: :values.', + 'doesnt_start_with' => 'O :attribute não pode começar com um dos seguintes: :values.', + 'email' => 'O campo :attribute deve ser um endereço de e-mail válido.', + 'ends_with' => 'O campo :attribute deve terminar com um dos seguintes: :values', + 'enum' => 'O :attribute selecionado é inválido.', + 'exists' => 'O campo :attribute selecionado é inválido.', + 'extensions' => 'O campo :attribute deve conter uma das seguintes extensões: :values.', + 'file' => 'O campo :attribute deve ser um arquivo.', + 'filled' => 'O campo :attribute deve ter um valor.', 'gt' => [ 'numeric' => 'O campo :attribute deve ser maior que :value.', - 'file' => 'O campo :attribute deve ser maior que :value kilobytes.', - 'string' => 'O campo :attribute deve ser maior que :value caracteres.', - 'array' => 'O campo :attribute deve conter mais de :value itens.', + 'file' => 'O campo :attribute deve ser maior que :value kilobytes.', + 'string' => 'O campo :attribute deve ser maior que :value caracteres.', + 'array' => 'O campo :attribute deve conter mais de :value itens.', ], 'gte' => [ 'numeric' => 'O campo :attribute deve ser maior ou igual a :value.', - 'file' => 'O campo :attribute deve ser maior ou igual a :value kilobytes.', - 'string' => 'O campo :attribute deve ser maior ou igual a :value caracteres.', - 'array' => 'O campo :attribute deve conter :value itens ou mais.', + 'file' => 'O campo :attribute deve ser maior ou igual a :value kilobytes.', + 'string' => 'O campo :attribute deve ser maior ou igual a :value caracteres.', + 'array' => 'O campo :attribute deve conter :value itens ou mais.', ], - 'hex_color' => 'O campo :attribute deve ser uma cor hexadecimal válida.', - 'image' => 'O campo :attribute deve ser uma imagem.', - 'in' => 'O campo :attribute selecionado é inválido.', - 'in_array' => 'O campo :attribute não existe em :other.', - 'integer' => 'O campo :attribute deve ser um número inteiro.', - 'ip' => 'O campo :attribute deve ser um endereço de IP válido.', - 'ipv4' => 'O campo :attribute deve ser um endereço IPv4 válido.', - 'ipv6' => 'O campo :attribute deve ser um endereço IPv6 válido.', - 'json' => 'O campo :attribute deve ser uma string JSON válida.', + 'hex_color' => 'O campo :attribute deve ser uma cor hexadecimal válida.', + 'image' => 'O campo :attribute deve ser uma imagem.', + 'in' => 'O campo :attribute selecionado é inválido.', + 'in_array' => 'O campo :attribute não existe em :other.', + 'integer' => 'O campo :attribute deve ser um número inteiro.', + 'ip' => 'O campo :attribute deve ser um endereço de IP válido.', + 'ipv4' => 'O campo :attribute deve ser um endereço IPv4 válido.', + 'ipv6' => 'O campo :attribute deve ser um endereço IPv6 válido.', + 'json' => 'O campo :attribute deve ser uma string JSON válida.', 'list' => 'O campo :attribute deve ser uma lista.', 'lowercase' => 'O campo :attribute deve estar em letras minúsculas.', 'lt' => [ 'numeric' => 'O campo :attribute deve ser menor que :value.', - 'file' => 'O campo :attribute deve ser menor que :value kilobytes.', - 'string' => 'O campo :attribute deve ser menor que :value caracteres.', - 'array' => 'O campo :attribute deve conter menos de :value itens.', + 'file' => 'O campo :attribute deve ser menor que :value kilobytes.', + 'string' => 'O campo :attribute deve ser menor que :value caracteres.', + 'array' => 'O campo :attribute deve conter menos de :value itens.', ], 'lte' => [ 'numeric' => 'O campo :attribute deve ser menor ou igual a :value.', - 'file' => 'O campo :attribute deve ser menor ou igual a :value kilobytes.', - 'string' => 'O campo :attribute deve ser menor ou igual a :value caracteres.', - 'array' => 'O campo :attribute não deve conter mais que :value itens.', + 'file' => 'O campo :attribute deve ser menor ou igual a :value kilobytes.', + 'string' => 'O campo :attribute deve ser menor ou igual a :value caracteres.', + 'array' => 'O campo :attribute não deve conter mais que :value itens.', ], 'mac_address' => 'O campo :attribute deve ser um MAC address válido.', 'max' => [ 'numeric' => 'O campo :attribute não pode ser superior a :max.', - 'file' => 'O campo :attribute não pode ser superior a :max kilobytes.', - 'string' => 'O campo :attribute não pode ser superior a :max caracteres.', - 'array' => 'O campo :attribute não pode ter mais do que :max itens.', + 'file' => 'O campo :attribute não pode ser superior a :max kilobytes.', + 'string' => 'O campo :attribute não pode ser superior a :max caracteres.', + 'array' => 'O campo :attribute não pode ter mais do que :max itens.', ], - 'max_digits' => 'O campo :attribute não pode ser superior a :max dígitos', - 'mimes' => 'O campo :attribute deve ser um arquivo do tipo: :values.', - 'mimetypes' => 'O campo :attribute deve ser um arquivo do tipo: :values.', + 'max_digits' => 'O campo :attribute não pode ser superior a :max dígitos', + 'mimes' => 'O campo :attribute deve ser um arquivo do tipo: :values.', + 'mimetypes' => 'O campo :attribute deve ser um arquivo do tipo: :values.', 'min' => [ 'numeric' => 'O campo :attribute deve ser pelo menos :min.', - 'file' => 'O campo :attribute deve ter pelo menos :min kilobytes.', - 'string' => 'O campo :attribute deve ter pelo menos :min caracteres.', - 'array' => 'O campo :attribute deve ter pelo menos :min itens.', + 'file' => 'O campo :attribute deve ter pelo menos :min kilobytes.', + 'string' => 'O campo :attribute deve ter pelo menos :min caracteres.', + 'array' => 'O campo :attribute deve ter pelo menos :min itens.', ], - 'missing' => 'O campo :attribute deve estar ausente.', - 'missing_if' => 'O campo :attribute deve estar ausente quando :other for :value.', - 'missing_unless' => 'O campo :attribute deve estar ausente, a menos que :other seja :value.', - 'missing_with' => 'O campo :attribute não deve estar presente quando houver :values.', - 'missing_with_all' => 'O campo :attribute deve estar ausente quando :values estiverem presentes.', - 'min_digits' => 'O campo :attribute deve ter pelo menos :min dígitos', - 'not_in' => 'O campo :attribute selecionado é inválido.', - 'multiple_of' => 'O campo :attribute deve ser um múltiplo de :value.', - 'not_regex' => 'O campo :attribute possui um formato inválido.', - 'numeric' => 'O campo :attribute deve ser um número.', + 'missing' => 'O campo :attribute deve estar ausente.', + 'missing_if' => 'O campo :attribute deve estar ausente quando :other for :value.', + 'missing_unless' => 'O campo :attribute deve estar ausente, a menos que :other seja :value.', + 'missing_with' => 'O campo :attribute não deve estar presente quando houver :values.', + 'missing_with_all' => 'O campo :attribute deve estar ausente quando :values estiverem presentes.', + 'min_digits' => 'O campo :attribute deve ter pelo menos :min dígitos', + 'not_in' => 'O campo :attribute selecionado é inválido.', + 'multiple_of' => 'O campo :attribute deve ser um múltiplo de :value.', + 'not_regex' => 'O campo :attribute possui um formato inválido.', + 'numeric' => 'O campo :attribute deve ser um número.', 'password' => [ - 'letters' => 'O campo :attribute deve conter pelo menos uma letra.', - 'mixed' => 'O campo :attribute deve conter pelo menos uma letra maiúscula e uma letra minúscula.', - 'numbers' => 'O campo :attribute deve conter pelo menos um número.', - 'symbols' => 'O campo :attribute deve conter pelo menos um símbolo.', - 'uncompromised' => 'A senha que você inseriu em :attribute está em um vazamento de dados.' - . ' Por favor escolha uma senha diferente.', + 'letters' => 'O campo :attribute deve conter pelo menos uma letra.', + 'mixed' => 'O campo :attribute deve conter pelo menos uma letra maiúscula e uma letra minúscula.', + 'numbers' => 'O campo :attribute deve conter pelo menos um número.', + 'symbols' => 'O campo :attribute deve conter pelo menos um símbolo.', + 'uncompromised' => 'A senha que você inseriu em :attribute está em um vazamento de dados.' + .' Por favor escolha uma senha diferente.', ], - 'present' => 'O campo :attribute deve estar presente.', - 'present_if' => 'O campo :attribute deve estar presente quando :other for :value.', - 'present_unless' => 'O campo :attribute deve estar presente, a menos que :other seja :value.', - 'present_with' => 'O campo :attribute deve estar presente quando :values estiver presente.', - 'present_with_all' => 'O campo :attribute deve estar presente quando :values estiverem presentes.', - 'regex' => 'O campo :attribute tem um formato inválido.', - 'required' => 'O campo :attribute é obrigatório.', - 'required_array_keys' => 'O campo :attribute deve conter entradas para: :values.', - 'required_if' => 'O campo :attribute é obrigatório quando :other for :value.', + 'present' => 'O campo :attribute deve estar presente.', + 'present_if' => 'O campo :attribute deve estar presente quando :other for :value.', + 'present_unless' => 'O campo :attribute deve estar presente, a menos que :other seja :value.', + 'present_with' => 'O campo :attribute deve estar presente quando :values estiver presente.', + 'present_with_all' => 'O campo :attribute deve estar presente quando :values estiverem presentes.', + 'regex' => 'O campo :attribute tem um formato inválido.', + 'required' => 'O campo :attribute é obrigatório.', + 'required_array_keys' => 'O campo :attribute deve conter entradas para: :values.', + 'required_if' => 'O campo :attribute é obrigatório quando :other for :value.', 'required_if_accepted' => 'O campo :attribute é obrigatório quando :other for aceito.', 'required_if_declined' => 'O campo :attribute é obrigatório quando :other for recusado.', - 'required_unless' => 'O campo :attribute é obrigatório exceto quando :other for :values.', - 'required_with' => 'O campo :attribute é obrigatório quando :values está presente.', - 'required_with_all' => 'O campo :attribute é obrigatório quando :values está presente.', - 'required_without' => 'O campo :attribute é obrigatório quando :values não está presente.', + 'required_unless' => 'O campo :attribute é obrigatório exceto quando :other for :values.', + 'required_with' => 'O campo :attribute é obrigatório quando :values está presente.', + 'required_with_all' => 'O campo :attribute é obrigatório quando :values está presente.', + 'required_without' => 'O campo :attribute é obrigatório quando :values não está presente.', 'required_without_all' => 'O campo :attribute é obrigatório quando nenhum dos :values estão presentes.', - 'prohibited' => 'O campo :attribute é proibido.', - 'prohibited_if' => 'O campo :attribute é proibido quando :other for :value.', - 'prohibited_unless' => 'O campo :attribute é proibido exceto quando :other for :values.', - 'prohibits' => 'O campo :attribute proíbe :other de estar presente.', - 'same' => 'Os campos :attribute e :other devem corresponder.', - 'size' => [ + 'prohibited' => 'O campo :attribute é proibido.', + 'prohibited_if' => 'O campo :attribute é proibido quando :other for :value.', + 'prohibited_unless' => 'O campo :attribute é proibido exceto quando :other for :values.', + 'prohibits' => 'O campo :attribute proíbe :other de estar presente.', + 'same' => 'Os campos :attribute e :other devem corresponder.', + 'size' => [ 'numeric' => 'O campo :attribute deve ser :size.', - 'file' => 'O campo :attribute deve ser :size kilobytes.', - 'string' => 'O campo :attribute deve ser :size caracteres.', - 'array' => 'O campo :attribute deve conter :size itens.', + 'file' => 'O campo :attribute deve ser :size kilobytes.', + 'string' => 'O campo :attribute deve ser :size caracteres.', + 'array' => 'O campo :attribute deve conter :size itens.', ], - 'starts_with' => 'O campo :attribute deve começar com um dos seguintes valores: :values', - 'string' => 'O campo :attribute deve ser uma string.', - 'timezone' => 'O campo :attribute deve ser uma zona válida.', - 'unique' => 'O campo :attribute já está sendo utilizado.', - 'uploaded' => 'Ocorreu uma falha no upload do campo :attribute.', - 'uppercase' => 'O campo :attribute deve conter letras maiúsculas.', - 'url' => 'O campo :attribute tem um formato inválido.', - 'ulid' => 'O campo :attribute deve ser um ULID válido.', - 'uuid' => 'O campo :attribute deve ser um UUID válido.', + 'starts_with' => 'O campo :attribute deve começar com um dos seguintes valores: :values', + 'string' => 'O campo :attribute deve ser uma string.', + 'timezone' => 'O campo :attribute deve ser uma zona válida.', + 'unique' => 'O campo :attribute já está sendo utilizado.', + 'uploaded' => 'Ocorreu uma falha no upload do campo :attribute.', + 'uppercase' => 'O campo :attribute deve conter letras maiúsculas.', + 'url' => 'O campo :attribute tem um formato inválido.', + 'ulid' => 'O campo :attribute deve ser um ULID válido.', + 'uuid' => 'O campo :attribute deve ser um UUID válido.', /* |-------------------------------------------------------------------------- @@ -191,43 +191,43 @@ */ 'attributes' => [ - 'address' => 'endereço', - 'age' => 'idade', - 'body' => 'conteúdo', - 'cell' => 'célula', - 'city' => 'cidade', - 'country' => 'país', - 'date' => 'data', - 'day' => 'dia', - 'excerpt' => 'resumo', + 'address' => 'endereço', + 'age' => 'idade', + 'body' => 'conteúdo', + 'cell' => 'célula', + 'city' => 'cidade', + 'country' => 'país', + 'date' => 'data', + 'day' => 'dia', + 'excerpt' => 'resumo', 'first_name' => 'primeiro nome', - 'gender' => 'gênero', + 'gender' => 'gênero', 'marital_status' => 'estado civil', 'profession' => 'profissão', 'nationality' => 'nacionalidade', - 'hour' => 'hora', + 'hour' => 'hora', 'last_name' => 'sobrenome', - 'message' => 'mensagem', - 'minute' => 'minuto', - 'mobile' => 'celular', - 'month' => 'mês', - 'name' => 'nome', - 'zipcode' => 'cep', - 'company_name' => 'razão social', + 'message' => 'mensagem', + 'minute' => 'minuto', + 'mobile' => 'celular', + 'month' => 'mês', + 'name' => 'nome', + 'zipcode' => 'cep', + 'company_name' => 'razão social', 'neighborhood' => 'bairro', - 'number' => 'número', - 'password' => 'senha', - 'phone' => 'telefone', - 'second' => 'segundo', - 'sex' => 'sexo', - 'state' => 'estado', - 'street' => 'rua', - 'subject' => 'assunto', - 'text' => 'texto', - 'time' => 'hora', - 'title' => 'título', - 'username' => 'usuário', - 'year' => 'ano', + 'number' => 'número', + 'password' => 'senha', + 'phone' => 'telefone', + 'second' => 'segundo', + 'sex' => 'sexo', + 'state' => 'estado', + 'street' => 'rua', + 'subject' => 'assunto', + 'text' => 'texto', + 'time' => 'hora', + 'title' => 'título', + 'username' => 'usuário', + 'year' => 'ano', 'description' => 'descrição', 'password_confirmation' => 'confirmação da senha', 'current_password' => 'senha atual', @@ -235,7 +235,7 @@ 'modality' => 'modalidade', 'category' => 'categoria', 'blood_type' => 'tipo sanguíneo', - 'birth_date' => 'data de nascimento' + 'birth_date' => 'data de nascimento', ], ]; diff --git a/phpunit.xml b/phpunit.xml index 5c3671d..6120840 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -28,4 +28,9 @@ + + + + + diff --git a/pint.json b/pint.json index e69de29..93061b6 100644 --- a/pint.json +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "laravel" +} diff --git a/resources/views/emails/deadline-for-accountability.blade.php b/resources/views/emails/deadline-for-accountability.blade.php index e2a50d8..9ce2ba2 100644 --- a/resources/views/emails/deadline-for-accountability.blade.php +++ b/resources/views/emails/deadline-for-accountability.blade.php @@ -12,7 +12,7 @@ A Secult vem por meio deste informar que já se passaram {{$info->days_current}} ({{$days_current}}) dias da data do pagamento do seu projeto cultural. - {{ $complment_text }} + {{ $complement_text }} {{ $title_report }} que deverá ser enviado através da plataforma Mapa Cultural.

diff --git a/routes/api.php b/routes/api.php index e64fb65..7b991b7 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,9 +1,10 @@ middleware('guest') diff --git a/routes/auth.php b/routes/auth.php index ae6cc20..9cf5ffd 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -9,29 +9,29 @@ use Illuminate\Support\Facades\Route; Route::post('/register', [RegisteredUserController::class, 'store']) - ->middleware('guest') - ->name('register'); + ->middleware('guest') + ->name('register'); Route::post('/login', [AuthenticatedSessionController::class, 'store']) - ->middleware('guest') - ->name('login'); + ->middleware('guest') + ->name('login'); Route::post('/forgot-password', [PasswordResetLinkController::class, 'store']) - ->middleware('guest') - ->name('password.email'); + ->middleware('guest') + ->name('password.email'); Route::post('/reset-password', [NewPasswordController::class, 'store']) - ->middleware('guest') - ->name('password.store'); + ->middleware('guest') + ->name('password.store'); Route::get('/verify-email/{id}/{hash}', VerifyEmailController::class) - ->middleware(['auth', 'signed', 'throttle:6,1']) - ->name('verification.verify'); + ->middleware(['auth', 'signed', 'throttle:6,1']) + ->name('verification.verify'); Route::post('/email/verification-notification', [EmailVerificationNotificationController::class, 'store']) - ->middleware(['auth', 'throttle:6,1']) - ->name('verification.send'); + ->middleware(['auth', 'throttle:6,1']) + ->name('verification.send'); Route::post('/logout', [AuthenticatedSessionController::class, 'destroy']) - ->middleware('auth') - ->name('logout'); + ->middleware('auth') + ->name('logout'); diff --git a/routes/console.php b/routes/console.php index 3e30e4e..ea1bdd4 100644 --- a/routes/console.php +++ b/routes/console.php @@ -1,11 +1,9 @@ purpose('Display an inspiring quote')->hourly(); Schedule::call(function () { - $response = Http::get( config('app.mapa_url') . 'bigsheet/infoForNotificationsAccountability', [ - 'access_token' => config('jwt.secret') + $response = Http::get(config('app.mapa_url').'/bigsheet/infoForNotificationsAccountability', [ + 'access_token' => config('jwt.secret'), ]); $infos = $response->json(); diff --git a/routes/web.php b/routes/web.php index c2b1e86..1128a7c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -7,17 +7,16 @@ return ['Laravel' => app()->version()]; }); Route::get('/test', function () { -// $words = SpellNumber::integer(85)->toLetters(); // 'pt' para português, -// dump(SpellNumber); - $e = new Extensive(); - dump($e->extensive( 185421.99 )); // mil e um reais + // $words = SpellNumber::integer(85)->toLetters(); // 'pt' para português, + // dump(SpellNumber); + $e = new Extensive; + dump($e->extensive(185421.99)); // mil e um reais - dd($e->extensive( 54001.99, Extensive::MALE_NUMBER )); + dd($e->extensive(54001.99, Extensive::MALE_NUMBER)); Route::view('/welcome', 'emails.deadline-for-accountability', [ - 'days' => '' + 'days' => '', ]); }); - require __DIR__.'/auth.php'; diff --git a/tests/Feature/PC/EmailTest.php b/tests/Feature/PC/EmailTest.php index 2cf6ad4..c923259 100644 --- a/tests/Feature/PC/EmailTest.php +++ b/tests/Feature/PC/EmailTest.php @@ -2,31 +2,29 @@ namespace Tests\Feature\PC; -use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Foundation\Testing\WithFaker; +use App\Jobs\NotificationAccountability; use Illuminate\Support\Facades\Http; use Tests\TestCase; -use App\Jobs\NotificationAccountability; class EmailTest extends TestCase { /** * A basic feature test example. */ - public function test_return_api_and_passing_params_to_metod(): void + public function test_return_api_and_passing_params_to_method(): void { - $response = Http::get( 'http://172.19.18.161:8088/bigsheet/infoForNotificationsAccountability/', [ - 'access_token' => config('jwt.secret') + $response = Http::get(config('app.mapa_url').'/bigsheet/infoForNotificationsAccountability/', [ + 'access_token' => config('jwt.secret'), ]); - // Confirma que o status da resposta foi 200 (opcional, dependendo do teste) + $this->assertEquals(200, $response->status()); - // Converte o corpo da resposta para um array JSON + $jsonResponse = $response->json(); NotificationAccountability::dispatch($jsonResponse); - $this->assertIsArray($jsonResponse); // Confirma que a resposta é um array - // Se tiver array preenchido no retorno - if(count($jsonResponse) > 0){ + $this->assertIsArray($jsonResponse); + + if (count($jsonResponse) > 0) { // Verifica se a resposta contém uma chave específica $this->assertArrayHasKey('registration_number', $jsonResponse[0]); } diff --git a/tests/TestCase.php b/tests/TestCase.php index fe1ffc2..ee63ad0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,7 +4,4 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase; -abstract class TestCase extends BaseTestCase -{ - // -} +abstract class TestCase extends BaseTestCase {} diff --git a/tests/Unit/Console/Commands/ConsumePublishedRecourseEmailsTest.php b/tests/Unit/Console/Commands/ConsumePublishedRecourseEmailsTest.php new file mode 100644 index 0000000..2e03e81 --- /dev/null +++ b/tests/Unit/Console/Commands/ConsumePublishedRecourseEmailsTest.php @@ -0,0 +1,128 @@ +start(); + + sleep(1); + $this->assertTrue($process->isRunning()); + + $process->signal(SIGINT); + + // Give it a moment to process the signal + sleep(1); + + $this->assertFalse($process->isRunning()); + $this->assertEquals(130, $process->getExitCode()); + } + + public function test_send_email_true(): void + { + Mail::fake(); + + // Create a mock AMQPMessage for prop route + $msgMock = $this->createMockAMQPMessage( + json_encode([ + 'email' => 'test@email.test', + 'agentId' => 123456, + 'opportunityName' => 'Test Opportunity', + ]), + ); + + $command = new ConsumePublishedRecourseEmails; + + $reflection = new \ReflectionClass($command); + $method = $reflection->getMethod('sendEmail'); + $method->setAccessible(true); + $method->invoke($command, json_decode($msgMock->body, true)); + + // Asserts that the email was not sent + Mail::assertSent(PublishedRecourse::class); + } + + public function test_send_email_false(): void + { + Mail::fake(); + + // Create a mock AMQPMessage for prop route + $msgMock = $this->createMockAMQPMessage( + json_encode([ + 'emal' => 'test@email.fail', + 'agent' => 123456, + ]), + ); + + $command = new ConsumePublishedRecourseEmails; + + $reflection = new \ReflectionClass($command); + $method = $reflection->getMethod('sendEmail'); + $method->setAccessible(true); + $send = $method->invoke($command, json_decode($msgMock->body, true)); + + // Asserts that the email was not sent + $this->assertFalse($send); + Mail::assertNotSent(PublishedRecourse::class); + } + + public function test_process_message_with_success(): void + { + Mail::fake(); + + $msgMock = $this->createMockAMQPMessage( + json_encode([ + 'email' => 'test@example.com', + 'agentId' => 123456, + 'opportunityName' => 'Test Opportunity', + ]) + ); + $msgMock->shouldReceive('ack') + ->once() + ->andReturnNull(); + + $output = $this->createMock(OutputStyle::class); + $command = new ConsumePublishedRecourseEmails; + $command->setOutput($output); + + $reflection = new \ReflectionClass($command); + $method = $reflection->getMethod('processMessage'); + $method->setAccessible(true); + $method->invoke($command, $msgMock); + + Mail::assertSent(PublishedRecourse::class); + } + +// public function test_process_message_with_failure(): void +// { + // @TODO: Needs discovery +// } + + private function createMockAMQPMessage(string $body): MockInterface + { + $channelMock = Mockery::mock(AMQPChannel::class); + + $msgMock = Mockery::mock(AMQPMessage::class); + $msgMock->body = $body; + $msgMock->delivery_info = [ + 'channel' => $channelMock, + 'delivery_tag' => 1, + ]; + + return $msgMock; + } +} diff --git a/tests/Unit/Console/Commands/ConsumerCommandTest.php b/tests/Unit/Console/Commands/ConsumerCommandTest.php new file mode 100644 index 0000000..e3e2fa9 --- /dev/null +++ b/tests/Unit/Console/Commands/ConsumerCommandTest.php @@ -0,0 +1,111 @@ +start(); + + sleep(1); + $this->assertTrue($process->isRunning()); + + $process->signal(SIGINT); + + // Give it a moment to process the signal + sleep(1); + + $this->assertFalse($process->isRunning()); + $this->assertEquals(130, $process->getExitCode()); + } + + public function test_process_message_for_prop_route() + { + Mail::fake(); + + // Create a mock AMQPMessage for prop route + $msgMock = $this->createMockAMQPMessage( + json_encode([ + 'email' => 'test@example.com', + 'name' => 'Test User', + 'number' => '12345', + 'days' => 30, + ]), + config('app.rabbitmq.route_key_prop'), + ); + + // Create a partial mock of the command + $command = new ConsumerCommand; + + // Use reflection to call the protected method + $reflection = new \ReflectionClass($command); + $method = $reflection->getMethod('processMessage'); + $method->setAccessible(true); + $method->invoke($command, $msgMock); + + // Assert mail was sent + Mail::assertSent(EmailRegistrationOpp::class); + } + + public function test_process_message_for_adm_route() + { + Mail::fake(); + + // Create a mock AMQPMessage for adm route + $msgMock = $this->createMockAMQPMessage( + json_encode([ + 'comission' => 'comission@example.com', + 'owner' => 'owner@example.com', + 'registration' => '54321', + ]), + config('app.rabbitmq.route_key_adm') + ); + + // Create a partial mock of the command + $command = new ConsumerCommand; + + // Use reflection to call the protected method + $reflection = new \ReflectionClass($command); + $method = $reflection->getMethod('processMessage'); + $method->setAccessible(true); + $method->invoke($command, $msgMock); + + // Assert mail was sent + Mail::assertSent(AnswerNotification::class); + } + + /** + * Create a mock AMQPMessage for testing + */ + private function createMockAMQPMessage(string $body, string $routingKey): MockInterface + { + $channelMock = Mockery::mock(AMQPChannel::class); + $channelMock->shouldReceive('basic_ack') + ->once() + ->andReturnNull(); + + $msgMock = Mockery::mock(AMQPMessage::class); + $msgMock->body = $body; + $msgMock->shouldReceive('getRoutingKey') + ->andReturn($routingKey); + $msgMock->delivery_info = [ + 'channel' => $channelMock, + 'delivery_tag' => 1, + ]; + + return $msgMock; + } +} diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php deleted file mode 100644 index 5773b0c..0000000 --- a/tests/Unit/ExampleTest.php +++ /dev/null @@ -1,16 +0,0 @@ -assertTrue(true); - } -} diff --git a/tests/Unit/Mail/AnswerNotificationTest.php b/tests/Unit/Mail/AnswerNotificationTest.php new file mode 100644 index 0000000..0ba3e84 --- /dev/null +++ b/tests/Unit/Mail/AnswerNotificationTest.php @@ -0,0 +1,52 @@ +assertEquals($number, $mail->number); + } + + public function test_it_has_the_correct_subject(): void + { + $mail = new AnswerNotification('12345'); + $envelope = $mail->envelope(); + + $this->assertEquals('Diligência Respondida', $envelope->subject); + } + + public function test_it_uses_the_correct_view(): void + { + $mail = new AnswerNotification('12345'); + $content = $mail->content(); + + $this->assertEquals('emails.answer', $content->view); + } + + public function test_it_has_no_attachments(): void + { + $mail = new AnswerNotification('12345'); + + $this->assertEmpty($mail->attachments()); + } + + public function test_it_passes_number_to_the_view(): void + { + $number = '12345'; + $mail = new AnswerNotification($number); + + $rendered = $mail->render(); + + // This assumes your view includes the number somewhere + // You may need to adjust this assertion based on your view's actual content + $this->assertStringContainsString($number, $rendered); + } +} diff --git a/tests/Unit/Mail/DeadlineForAccountabilityTest.php b/tests/Unit/Mail/DeadlineForAccountabilityTest.php new file mode 100644 index 0000000..4cd9282 --- /dev/null +++ b/tests/Unit/Mail/DeadlineForAccountabilityTest.php @@ -0,0 +1,173 @@ + 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + + $this->assertInstanceOf(DeadlineForAccountability::class, $mail); + } + + public function test_it_sets_correct_envelope_subject_for_refo(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + $envelope = $mail->envelope(); + + $this->assertInstanceOf(Envelope::class, $envelope); + $this->assertEquals('Mapa Cultural - Prazo para envio da prestação de contas (REFO)', $envelope->subject); + } + + public function test_it_sets_correct_envelope_subject_for_raio(): void + { + $info = (object) [ + 'notification_type' => 'RAIO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + $envelope = $mail->envelope(); + + $this->assertInstanceOf(Envelope::class, $envelope); + $this->assertEquals('Mapa Cultural - Prazo para envio da prestação de contas (RAIO)', $envelope->subject); + } + + public function test_it_sets_default_complement_text_when_days_current_is_not_85_or_55(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + + // Trigger content method to set complementText + $mail->content(); + + $this->assertEquals('está na hora de preencher e enviar', $mail->complementText); + } + + public function test_it_sets_special_complement_text_when_days_current_is_85(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 85, + ]; + + $mail = new DeadlineForAccountability($info); + + // Trigger content method to set complementText + $mail->content(); + + $this->assertEquals('faltam 05 (cinco) dias para o envio', $mail->complementText); + } + + public function test_it_sets_special_complement_text_when_days_current_is_55(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 55, + ]; + + $mail = new DeadlineForAccountability($info); + + // Trigger content method to set complementText + $mail->content(); + + $this->assertEquals('faltam 05 (cinco) dias para o envio', $mail->complementText); + } + + public function test_it_sets_correct_title_report_for_refo(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + + // Trigger content method to set titleReport + $mail->content(); + + $this->assertEquals('Relatório de Execução Final do Objeto - REFO', $mail->titleReport); + } + + public function test_it_sets_correct_title_report_for_raio(): void + { + $info = (object) [ + 'notification_type' => 'RAIO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + + // Trigger content method to set titleReport + $mail->content(); + + $this->assertEquals('Relatório de Avaliação Intermediária do Objeto - RAIO', $mail->titleReport); + } + + public function test_it_returns_correct_content_view(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + $content = $mail->content(); + + $this->assertInstanceOf(Content::class, $content); + $this->assertEquals('emails.deadline-for-accountability', $content->view); + } + + public function test_it_passes_correct_data_to_view(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + $content = $mail->content(); + + $extensive = new Extensive; + $expectedDaysCurrent = $extensive->extensive(90, Extensive::MALE_NUMBER); + + $viewData = $content->with; + $this->assertSame($info, $viewData['info']); + $this->assertEquals($expectedDaysCurrent, $viewData['days_current']); + $this->assertEquals('está na hora de preencher e enviar', $viewData['complement_text']); + $this->assertEquals('Relatório de Execução Final do Objeto - REFO', $viewData['title_report']); + } + + public function test_it_has_no_attachments(): void + { + $info = (object) [ + 'notification_type' => 'REFO', + 'days_current' => 90, + ]; + + $mail = new DeadlineForAccountability($info); + + $this->assertEmpty($mail->attachments()); + } +} diff --git a/tests/Unit/Mail/EmailRegistrationOppTest.php b/tests/Unit/Mail/EmailRegistrationOppTest.php new file mode 100644 index 0000000..3f4d25b --- /dev/null +++ b/tests/Unit/Mail/EmailRegistrationOppTest.php @@ -0,0 +1,90 @@ +assertEquals($nameUser, $mail->nameUser); + $this->assertEquals($number, $mail->number); + $this->assertEquals($days, $mail->days); + } + + public function test_it_sets_correct_envelope_subject(): void + { + $mail = new EmailRegistrationOpp( + nameUser: 'John Doe', + number: '12345', + days: '30', + ); + + $envelope = $mail->envelope(); + + $this->assertInstanceOf(Envelope::class, $envelope); + $this->assertEquals('Diligência Aberta - Resposta Necessária', $envelope->subject); + } + + public function test_it_uses_correct_view_template(): void + { + $mail = new EmailRegistrationOpp( + nameUser: 'John Doe', + number: '12345', + days: '30', + ); + + $content = $mail->content(); + + $this->assertInstanceOf(Content::class, $content); + $this->assertEquals('emails.registration', $content->view); + } + + public function test_it_passes_properties_to_view(): void + { + $nameUser = 'John Doe'; + $number = '12345'; + $days = '30'; + + $mail = new EmailRegistrationOpp( + nameUser: $nameUser, + number: $number, + days: $days, + ); + + // This test confirms that the properties are accessible to the view + // by checking that they're public properties on the mailable + $this->assertTrue(property_exists($mail, 'nameUser')); + $this->assertTrue(property_exists($mail, 'number')); + $this->assertTrue(property_exists($mail, 'days')); + + $this->assertEquals($nameUser, $mail->nameUser); + $this->assertEquals($number, $mail->number); + $this->assertEquals($days, $mail->days); + } + + public function test_it_has_no_attachments(): void + { + $mail = new EmailRegistrationOpp( + nameUser: 'John Doe', + number: '12345', + days: '30', + ); + + $this->assertEmpty($mail->attachments()); + } +} diff --git a/tests/Unit/Mail/PublishedRecourseTest.php b/tests/Unit/Mail/PublishedRecourseTest.php new file mode 100644 index 0000000..1122e36 --- /dev/null +++ b/tests/Unit/Mail/PublishedRecourseTest.php @@ -0,0 +1,82 @@ + 'Test Opportunity', + 'agentId' => '12345', + ]; + + $mail = new PublishedRecourse($data); + + $this->assertInstanceOf(PublishedRecourse::class, $mail); + } + + public function test_it_sets_correct_envelope_subject(): void + { + $data = [ + 'opportunityName' => 'Test Opportunity', + 'agentId' => '12345', + ]; + + $mail = new PublishedRecourse($data); + $envelope = $mail->envelope(); + + $this->assertInstanceOf(Envelope::class, $envelope); + $this->assertEquals('Resposta do recurso publicada', $envelope->subject); + } + + public function test_it_uses_correct_view_template(): void + { + $data = [ + 'opportunityName' => 'Test Opportunity', + 'agentId' => '12345', + ]; + + $mail = new PublishedRecourse($data); + $content = $mail->content(); + + $this->assertInstanceOf(Content::class, $content); + $this->assertEquals('emails.published-recourse', $content->view); + } + + public function test_it_passes_correct_data_to_view(): void + { + $opportunityName = 'Test Opportunity'; + $agentId = '12345'; + + $data = [ + 'opportunityName' => $opportunityName, + 'agentId' => $agentId, + ]; + + $mail = new PublishedRecourse($data); + $content = $mail->content(); + + $this->assertEquals([ + 'opportunityName' => $opportunityName, + 'agentId' => $agentId, + ], $content->with); + } + + public function test_it_has_no_attachments(): void + { + $data = [ + 'opportunityName' => 'Test Opportunity', + 'agentId' => '12345', + ]; + + $mail = new PublishedRecourse($data); + + $this->assertEmpty($mail->attachments()); + } +} From d736be5feca1b93bec8e8f6db59af36548b773dc Mon Sep 17 00:00:00 2001 From: Jefferson Oliveira Date: Mon, 28 Apr 2025 12:20:35 -0300 Subject: [PATCH 07/11] =?UTF-8?q?feat:=20comando=20de=20execu=C3=A7=C3=A3o?= =?UTF-8?q?=20das=20filas=20no=20supervisor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/php-fpm/supervisord.conf | 10 ++++++++++ docker/prod/php-fpm/supervisord.conf | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/docker/php-fpm/supervisord.conf b/docker/php-fpm/supervisord.conf index 4c6b060..214c70b 100644 --- a/docker/php-fpm/supervisord.conf +++ b/docker/php-fpm/supervisord.conf @@ -1,5 +1,6 @@ [supervisord] nodaemon=true + ### RODAR OS WORKS [program:diligence_consumer] command=php artisan diligence:consumer @@ -15,6 +16,15 @@ autorestart=true stderr_logfile=/dev/stderr stdout_logfile=/dev/stdout +[program:accountability_queue] +process_name=%(program_name)s +command=php /var/www/html/artisan queue:work +autostart=true +autorestart=true +user=www-data +stdout_logfile=/var/www/html/storage/logs/worker.log +redirect_stderr=true + [supervisord] nodaemon=true user=root diff --git a/docker/prod/php-fpm/supervisord.conf b/docker/prod/php-fpm/supervisord.conf index aedcdce..c99b341 100644 --- a/docker/prod/php-fpm/supervisord.conf +++ b/docker/prod/php-fpm/supervisord.conf @@ -36,4 +36,13 @@ user=www-data numprocs=1 priority=200 logfile_maxbytes=50MB ; Tamanho máximo do log antes de rotacionar (opcional) -logfile_backups=5 \ No newline at end of file +logfile_backups=5 + +[program:accountability_queue] +process_name=%(program_name)s +command=php /var/www/html/artisan queue:work +autostart=true +autorestart=true +user=www-data +stdout_logfile=/var/www/html/storage/logs/worker.log +redirect_stderr=true From c5b68ff6537f5ce4fffeb470ba581828400811df Mon Sep 17 00:00:00 2001 From: Rafael Silva Domingos Date: Tue, 20 May 2025 10:30:55 -0300 Subject: [PATCH 08/11] Ajustes no dockerfile para diminuir a quantidade de camadas --- docker/prod/php-fpm/Dockerfile | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/docker/prod/php-fpm/Dockerfile b/docker/prod/php-fpm/Dockerfile index 1ea2231..aed3a13 100644 --- a/docker/prod/php-fpm/Dockerfile +++ b/docker/prod/php-fpm/Dockerfile @@ -19,16 +19,13 @@ RUN apt-get update && \ libgmp-dev \ mariadb-client \ unzip \ - supervisor - -RUN docker-php-ext-install soap exif pcntl zip pdo_mysql pdo_pgsql bcmath intl gmp sockets - -RUN pecl install redis && docker-php-ext-enable redis -RUN pecl install memcached && docker-php-ext-enable memcached - -RUN docker-php-ext-install gd && \ - docker-php-ext-configure gd --with-freetype --with-jpeg && \ - docker-php-ext-install gd + supervisor \ + && docker-php-ext-install soap exif pcntl zip pdo_mysql pdo_pgsql bcmath intl gmp sockets \ + && pecl install redis && docker-php-ext-enable redis \ + && pecl install memcached \ + && docker-php-ext-enable memcached \ + && docker-php-ext-install gd \ + && docker-php-ext-configure gd --with-freetype --with-jpeg COPY --from=composer:latest /usr/bin/composer /usr/bin/composer WORKDIR /var/www From 70ea1420e967701316b7889146f9986b164106fc Mon Sep 17 00:00:00 2001 From: Rafael Silva Domingos Date: Tue, 20 May 2025 11:37:04 -0300 Subject: [PATCH 09/11] =?UTF-8?q?Ajusta=20diret=C3=B3rio=20da=20aplica?= =?UTF-8?q?=C3=A7=C3=A3o=20no=20Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/prod/php-fpm/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/prod/php-fpm/Dockerfile b/docker/prod/php-fpm/Dockerfile index aed3a13..94038b0 100644 --- a/docker/prod/php-fpm/Dockerfile +++ b/docker/prod/php-fpm/Dockerfile @@ -28,15 +28,15 @@ RUN apt-get update && \ && docker-php-ext-configure gd --with-freetype --with-jpeg COPY --from=composer:latest /usr/bin/composer /usr/bin/composer -WORKDIR /var/www -COPY . /var/www +WORKDIR /var/www/html +COPY . /var/www/html COPY ./docker/prod/php-fpm/supervisord.conf /etc/supervisord.conf COPY ./docker/prod/php-fpm/published.sh /etc/published.sh COPY ./docker/prod/php-fpm/works.sh /etc/works.sh COPY ./docker/prod/php-fpm/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN composer install --no-dev --optimize-autoloader -RUN chown -R www-data:www-data /var/www && chmod -R 755 /var/www/storage +RUN chown -R www-data:www-data /var/www && chmod -R 755 /var/www/html/storage RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 9000 From d1996b19bf822c4c99d9670c83544f76282c6186 Mon Sep 17 00:00:00 2001 From: Rafael Silva Domingos Date: Tue, 20 May 2025 12:01:43 -0300 Subject: [PATCH 10/11] =?UTF-8?q?Cria=20arquivo=20de=20configura=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20CICD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cicd | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/cicd diff --git a/.github/workflows/cicd b/.github/workflows/cicd new file mode 100644 index 0000000..9d78a83 --- /dev/null +++ b/.github/workflows/cicd @@ -0,0 +1,49 @@ +name: CICD + +on: + pull_request: + branches: + - main + types: + - closed + + workflow_dispatch: + +jobs: + BUILD: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v4.1.1 + + - name: Read version file + id: get_version + run: | + VERSION=$(cat version.txt) + echo "app_version=$VERSION" >> $GITHUB_ENV + + - name: Docker Login + uses: docker/login-action@v3.0.0 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Criação da Imagem docker + uses: docker/build-push-action@v5.0.0 + with: + context: ./ + file: ./Dockerfile + push: true + tags: | + secultceara/api-email:latest + secultceara/api-email:${{ env.app_version }} + + #DEPLOY: + # needs: BUILD + # runs-on: mapahomolog + # steps: + # - name: Pull da imagem do dockerhub + # run: sudo docker pull secultceara/mapasculturais:homolog + # - name: Restart do docker-compose para atualizar o container com a nova imagem + # run: cd /opt/docker/mapa5 && sudo docker-compose down && sudo docker-compose up -d \ No newline at end of file From e225ac255cea9601b52e1b171c33c9ddbd5a4a82 Mon Sep 17 00:00:00 2001 From: Rafael Silva Domingos Date: Tue, 20 May 2025 12:05:37 -0300 Subject: [PATCH 11/11] =?UTF-8?q?Corrige=20extens=C3=A3o=20do=20arquivo=20?= =?UTF-8?q?de=20configura=C3=A7=C3=A3o=20do=20cicd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{cicd => cicd.yaml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{cicd => cicd.yaml} (98%) diff --git a/.github/workflows/cicd b/.github/workflows/cicd.yaml similarity index 98% rename from .github/workflows/cicd rename to .github/workflows/cicd.yaml index 9d78a83..fc9fa4b 100644 --- a/.github/workflows/cicd +++ b/.github/workflows/cicd.yaml @@ -28,7 +28,7 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - + - name: Criação da Imagem docker uses: docker/build-push-action@v5.0.0 with: