TD-322: [BACK]. [реализовать сервис для подписания документов в контейнере signer] #2

Merged
akurilenko merged 22 commits from TD-322 into main 2024-10-28 14:44:12 +03:00
30 changed files with 607 additions and 335 deletions

25
.gitignore vendored
View File

@ -1,3 +1,26 @@
.DS_Store
.vscode
.idea
/.idea/
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
/.php-cs-fixer.php
/.php-cs-fixer.cache
.php_cs.cache

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Codeception">
<option name="configurations">
<list>
<Configuration>
<option name="path" value="$PROJECT_DIR$/tests" />
</Configuration>
<Configuration>
<option name="path" value="$PROJECT_DIR$/backend/tests" />
</Configuration>
<Configuration>
<option name="path" value="$PROJECT_DIR$/backend/tests" />
</Configuration>
</list>
</option>
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/signer.iml" filepath="$PROJECT_DIR$/.idea/signer.iml" />
</modules>
</component>
</project>

View File

@ -1,113 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpIncludePathManager">
<include_path>
<path value="$PROJECT_DIR$/backend/vendor/symfony/service-contracts" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/dotenv" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/security-http" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/string" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-intl-grapheme" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/twig-bridge" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/var-dumper" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/event-dispatcher-contracts" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/stopwatch" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/console" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/finder" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-mbstring" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/ux-chartjs" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/var-exporter" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/http-kernel" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/framework-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/event-dispatcher" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-php81" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/security-core" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/security-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-php80" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-intl-normalizer" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-uuid" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/cache" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/process" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/twig-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/common" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/doctrine-migrations-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/inflector" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/annotations" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/event-manager" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/doctrine-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/sql-formatter" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/instantiator" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/orm" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/lexer" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/cache" />
<path value="$PROJECT_DIR$/backend/vendor/easycorp/easyadmin-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/persistence" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/dbal" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/deprecations" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/collections" />
<path value="$PROJECT_DIR$/backend/vendor/doctrine/migrations" />
<path value="$PROJECT_DIR$/backend/vendor/composer" />
<path value="$PROJECT_DIR$/backend/vendor/psr/cache" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/validator" />
<path value="$PROJECT_DIR$/backend/vendor/nikic/php-parser" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/stimulus-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/doctrine-bridge" />
<path value="$PROJECT_DIR$/backend/vendor/psr/log" />
<path value="$PROJECT_DIR$/backend/vendor/psr/event-dispatcher" />
<path value="$PROJECT_DIR$/backend/vendor/twig/twig" />
<path value="$PROJECT_DIR$/backend/vendor/psr/container" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/translation-contracts" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/filesystem" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/translation" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/asset" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/polyfill-intl-icu" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/property-access" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/yaml" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/password-hasher" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/dependency-injection" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/form" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/config" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/deprecation-contracts" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/intl" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/http-foundation" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/error-handler" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/maker-bundle" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/routing" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/property-info" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/security-csrf" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/runtime" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/uid" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/flex" />
<path value="$PROJECT_DIR$/backend/vendor/symfony/cache-contracts" />
<path value="$PROJECT_DIR$/backend/vendor/psr/http-message" />
<path value="$PROJECT_DIR$/backend/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/backend/vendor/guzzlehttp/psr7" />
<path value="$PROJECT_DIR$/backend/vendor/guzzlehttp/guzzle" />
<path value="$PROJECT_DIR$/backend/vendor/guzzlehttp/promises" />
<path value="$PROJECT_DIR$/backend/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/backend/vendor/ralouphie/getallheaders" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.3" />
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PhpUnit">
<phpunit_settings>
<PhpUnitSettings custom_loader_path="$PROJECT_DIR$/backend/vendor/autoload.php" />
</phpunit_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PHPSpec">
<suites>
<PhpSpecSuiteConfiguration>
<option name="myPath" value="$PROJECT_DIR$" />
</PhpSpecSuiteConfiguration>
<PhpSpecSuiteConfiguration>
<option name="myPath" value="$PROJECT_DIR$/backend" />
</PhpSpecSuiteConfiguration>
<PhpSpecSuiteConfiguration>
<option name="myPath" value="$PROJECT_DIR$/backend" />
</PhpSpecSuiteConfiguration>
</suites>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PHPUnit">
<option name="directories">
<list>
<option value="$PROJECT_DIR$/backend/tests" />
</list>
</option>
</component>
</project>

View File

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/backend/src" isTestSource="false" packagePrefix="App\" />
<sourceFolder url="file://$MODULE_DIR$/backend/tests" isTestSource="true" packagePrefix="App\Tests\" />
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/annotations" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/cache" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/collections" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/common" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/dbal" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/deprecations" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/doctrine-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/doctrine-migrations-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/event-manager" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/inflector" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/lexer" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/migrations" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/orm" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/persistence" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/doctrine/sql-formatter" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/easycorp/easyadmin-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/nikic/php-parser" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/cache" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/asset" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/cache" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/cache-contracts" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/config" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/dependency-injection" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/deprecation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/doctrine-bridge" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/dotenv" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/error-handler" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/event-dispatcher-contracts" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/filesystem" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/finder" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/flex" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/form" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/framework-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/intl" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/maker-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/options-resolver" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/password-hasher" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-intl-grapheme" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-intl-icu" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-intl-normalizer" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-php80" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-php81" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/polyfill-uuid" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/process" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/property-access" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/property-info" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/routing" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/runtime" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/security-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/security-core" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/security-csrf" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/security-http" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/service-contracts" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/stimulus-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/stopwatch" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/string" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/translation" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/translation-contracts" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/twig-bridge" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/twig-bundle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/uid" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/ux-chartjs" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/validator" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/var-dumper" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/var-exporter" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/symfony/yaml" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/twig/twig" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/guzzlehttp/guzzle" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/http-client" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/ralouphie/getallheaders" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -24,7 +24,7 @@ RUN apt update && apt install -y \
libonig-dev \
libxslt1-dev \
acl \
&& echo 'alias sf="php bin/console"' >> ~/.bashrc \
&& echo 'alias sf="php bin/console"' >> ~/.bashrc
RUN wget -q -O /etc/apt/trusted.gpg.d/lab50.gpg http://packages.lab50.net/lab50.gpg
RUN echo 'deb http://packages.lab50.net/okular jammy main non-free' > /etc/apt/sources.list.d/okulargost.list
@ -54,7 +54,7 @@ RUN apt update && apt install -y okular-csp-utils
RUN mkdir -p /root/.config
COPY license.key /license.key
RUN pdfcpro install-license /license.key
RUN echo Y | pdfcpro install-license /license.key
COPY Inter-Bold.ttf /usr/local/share/fonts/Inter-Bold.ttf
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
@ -64,12 +64,16 @@ COPY ./backend /usr/src/signer
COPY ./docker/php/www.conf /etc/php/8.3/fpm/pool.d/www.conf
RUN curl curl https://frankenphp.dev/install.sh | sh
RUN mv /usr/src/signer/frankenphp /usr/local/bin/
ENV COMPOSER_ALLOW_SUPERUSER=1
RUN composer install
RUN service php8.3-fpm start
#COPY entrypoint.sh /entrypoint.sh
#
#ENTRYPOINT [ "/entrypoint.sh" ]
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["php-fpm8.3", "-F"]
EXPOSE 9000

View File

@ -1,23 +1,5 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
# https://symfony.com/doc/current/configuration/secrets.html
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=850da55654c68f779822ea80d2b66a94
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
@ -28,3 +10,6 @@ APP_SECRET=850da55654c68f779822ea80d2b66a94
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
DOT_DOT_URL='http://dot-dot.local'
API_TOKEN='secret'

View File

@ -17,6 +17,7 @@
"symfony/flex": "^2",
"symfony/framework-bundle": "6.2.*",
"symfony/runtime": "6.2.*",
"symfony/serializer": "6.2.*",
"symfony/ux-chartjs": "*",
"symfony/yaml": "6.2.*"
},

103
backend/composer.lock generated
View File

@ -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": "bf87fa37fe523eb581063fa93c0fda64",
"content-hash": "0d9c45fd694083582028c80344c66518",
"packages": [
{
"name": "composer/package-versions-deprecated",
@ -5393,6 +5393,107 @@
],
"time": "2023-07-13T14:28:09+00:00"
},
{
"name": "symfony/serializer",
"version": "v6.2.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
"reference": "19083104e606ecf8a48baa8ed310c7a073887037"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/serializer/zipball/19083104e606ecf8a48baa8ed310c7a073887037",
"reference": "19083104e606ecf8a48baa8ed310c7a073887037",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
"doctrine/annotations": "<1.12",
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/dependency-injection": "<5.4",
"symfony/property-access": "<5.4",
"symfony/property-info": "<5.4.24|>=6,<6.2.11",
"symfony/uid": "<5.4",
"symfony/yaml": "<5.4"
},
"require-dev": {
"doctrine/annotations": "^1.12|^2",
"phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0",
"symfony/cache": "^5.4|^6.0",
"symfony/config": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/error-handler": "^5.4|^6.0",
"symfony/filesystem": "^5.4|^6.0",
"symfony/form": "^5.4|^6.0",
"symfony/http-foundation": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/mime": "^5.4|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/property-info": "^5.4.24|^6.2.11",
"symfony/uid": "^5.4|^6.0",
"symfony/validator": "^5.4|^6.0",
"symfony/var-dumper": "^5.4|^6.0",
"symfony/var-exporter": "^5.4|^6.0",
"symfony/yaml": "^5.4|^6.0"
},
"suggest": {
"psr/cache-implementation": "For using the metadata cache.",
"symfony/config": "For using the XML mapping loader.",
"symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.",
"symfony/property-access": "For using the ObjectNormalizer.",
"symfony/property-info": "To deserialize relations.",
"symfony/var-exporter": "For using the metadata compiler.",
"symfony/yaml": "For using the default YAML mapping loader."
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Serializer\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/serializer/tree/v6.2.13"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-07-27T16:18:16+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v3.5.0",

View File

@ -14,11 +14,20 @@ services:
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
resource: '../src/*'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
- '../src/*/Api/{Request,Response}'
- '../src/*/{Exception,Entity,Dto,Enum,Helper,Model}'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
guzzle.http_client:
class: GuzzleHttp\Client
GuzzleHttp\Client: '@guzzle.http_client'
App\Api\ApiParams:
arguments:
$endPointUrl: '%env(DOT_DOT_URL)%'

56
backend/src/Api/Api.php Normal file
View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Api;
use App\Infrastructure\External\Api\AbstractApi;
use App\Infrastructure\External\Api\BinaryStringFileResult;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
class Api extends AbstractApi
{
public ApiParams $apiParams;
public function send(string $token, string $path, int $batch): array
{
$params = [
RequestOptions::HEADERS => [
'Authorization' => $token,
'Accept' => 'application/json',
],
RequestOptions::MULTIPART => [
[
'name' => 'file',
'contents' => fopen($path, 'r'),
'filename' => $path,
'headers' => [
'Content-Type' => '<Content-type header>',
],
],
[
'name' => 'type',
'contents' => 'attorney-signed'
],
],
];
$response = $this->client->post(sprintf('%s%s%s', $this->apiParams->endPointUrl, '/api/v1/document/upload/batch/', $batch), $params);
return $this->responseHandler->setResponse($response)->getContentJsonToArray();
}
public function download(string $url, string $token): BinaryStringFileResult
{
$params = [
RequestOptions::HEADERS => [
'Authorization' => $token,
],
];
$response = $this->client->get($url, $params);
return $this->responseHandler->setResponse($response)->getContentAsBinaryStingResult();
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App\Api;
class ApiParams
{
public function __construct(
public string $endPointUrl
) {
}
}

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace App\Api\Request;
use App\Infrastructure\Http\RequestDtoInterface;
class SignRequest implements RequestDtoInterface
{
public string $url;
public int $batch;
public string $apiToken;
}

View File

@ -4,15 +4,26 @@ declare(strict_types=1);
namespace App\Controller;
use App\Api\Request\SignRequest;
use App\SignService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
class SignController extends AbstractController
{
#[Route(path: '/test', name: 'app.test')]
public function __invoke(): Response
public function __construct(
private readonly SignService $signService
){
}
#[Route(path: '/sign', name: 'app.sign', methods: ['POST'])]
public function __invoke(Request $request, SignRequest $signRequest): Response
{
return new Response('test123');
$token = $request->server->get('HTTP_AUTHORIZATION');
return new JsonResponse($this->signService->signDocument($signRequest, $token));
}
}

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App;
class DevSignService
{
public function sign(string $documentUrl): void
{
exec(sprintf('cp %s %s_sign.pdf', $documentUrl, $documentUrl));
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Infrastructure\External\Api;
use GuzzleHttp\Client;
/**
* Class ApiAbstract Абстрактный API класс
*
* Универсальный клас для создания API интерфейсов
*/
abstract class AbstractApi
{
protected Client $client;
protected ResponseHandler $responseHandler;
public function __construct(Client $client, ResponseHandler $responseHandler)
{
$this->client = $client;
$this->responseHandler = $responseHandler;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Infrastructure\External\Api;
trait ApiSleep
{
protected function sleep(int $time = 5): void
{
if ($time > 0) {
sleep($time);
}
}
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\External\Api;
/**
* Враппер бинарных фалов, при создании класса содает новый временный файл,
* при необходимости сохраняет файл под новым именем
*/
class BinaryStringFileResult
{
public string $tempFileName;
public function __construct(
string $content,
) {
$this->saveToTempFile($content);
}
private function saveToTempFile(string $content): void
{
$this->tempFileName = sprintf('%s/%s_%s', sys_get_temp_dir(), 'Document', time());
file_put_contents($this->tempFileName, $content);
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace App\Infrastructure\External\Api;
use Psr\Http\Message\ResponseInterface;
use stdClass;
/**
* Class ResponseHandler Обработчик ответа API.
*
* После того как получили ответ api, оборачиваем его обработчик из которого мы можем получить ответ в любом формате,
* а так же применит дополнительную фильтрацию или корректировку ответа
*/
class ResponseHandler
{
protected ResponseInterface $response;
private array $contentFilters = [];
public function contentIsEmpty(): bool
{
return empty($this->response->getBody()->getSize());
}
/**
* Возвращает ответ в текстовом виде, применит фильтры при их наличие.
*
* @return string
*/
public function getContent(): string
{
$content = (string)$this->response->getBody();
return $this->filterContent($content);
}
/**
* Добавляет фильтр контента иногда необходимо поправить ответ, что бы в итоге возвращался нужный тип данных.
* Бывает иногда сервер, возвращает массив данных и при преобразовании json_decode возвращается
* массив вместо stdClass.
*/
public function addContentFilter(callable $filter): self
{
$this->contentFilters[] = $filter;
return $this;
}
/**
* Преобразует json в объект
*
* @return stdClass
*/
public function getContentStdFromJson(): stdClass
{
return json_decode($this->getContent());
}
public function getContentAsBinaryStingResult(): BinaryStringFileResult
{
return new BinaryStringFileResult($this->getContent());
}
/**
* @return stdClass[]
*/
public function getContentArrayStdFromJson(): array
{
return json_decode($this->getContent());
}
/**
* Преобразует json в массив.
*/
public function getContentJsonToArray(): array
{
return $this->contentIsEmpty() ? [] : json_decode($this->getContent(), true);
}
/**
* Устанавливает ответ в обработчик.
*/
public function setResponse(ResponseInterface $response): self
{
$this->response = $response;
return $this;
}
/**
* Фильтруем ответ
*
* @param string $content
*
* @return string
*/
private function filterContent(string $content): string
{
if (!$this->contentFilters) {
return $content;
}
foreach ($this->contentFilters as $filter) {
$content = $filter($content);
}
$this->contentFilters = [];
return $content;
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace App\Infrastructure\Http;
interface RequestDtoInterface
{
}

View File

@ -0,0 +1,63 @@
<?php
namespace App\Infrastructure\Http;
use Exception;
use ReflectionClass;
use RuntimeException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class RequestDtoResolver implements ValueResolverInterface
{
public function __construct(
private readonly SerializerInterface $serializer,
private readonly ValidatorInterface $validator
) {
}
public function supports(Request $request, ArgumentMetadata $argument): bool
{
$type = $argument->getType();
if (empty($type) || !class_exists($type)) {
return false;
}
try {
$reflection = new ReflectionClass($type);
} catch (Exception $e) {
return false;
}
return $reflection->implementsInterface(RequestDtoInterface::class);
}
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
if (($class = $argument->getType()) === null || !class_exists($class)) {
throw new RuntimeException('dto exception');
}
$object = $this->serializer->deserialize($request->getContent(), $class, 'json');
if (is_object($object)) {
$this->validateObject($object);
}
yield $object;
}
private function validateObject(object $object): void
{
$errors = $this->validator->validate($object);
if (count($errors) > 0) {
/* todo получать из вне сообщение для ошибки */
throw new RuntimeException($errors, 'Ошибка валидации');
}
}
}

View File

@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App;
class ProdSignService
{
public function sign(string $documentUrl): void
{
exec(sprintf('cp %s %s.pdf', $documentUrl, $documentUrl));
exec(sprintf('pdfcpro sign %s.pdf -out %s_sign.pdf -cert cbfc28b024316bfe43066008cae177713533a6dd -text "\n\t\tПодписано ЭП\n\t\t{subject/cn}\n\t\tСертификат {sha1}\n\t\tДействителен от {since} до {until}\n\t\tДата {date}\n\t\t{subject/t}\n\t\t{subject/fullname}\n\t\t" -fontfile /usr/local/share/fonts/Inter-Bold.ttf -fontsize 8 -x 2 -y 2 -w 96 -h 9', $documentUrl, $documentUrl));
}
}

View File

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace App;
use App\Api\Api;
use App\Api\ApiParams;
use App\Api\Request\SignRequest;
use App\Infrastructure\External\Api\BinaryStringFileResult;
use Exception;
use RuntimeException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class SignService
{
private DevSignService $devSignService;
private ProdSignService $prodSignService;
public function __construct(
private Api $api,
private ApiParams $apiParams
){
$this->devSignService = new DevSignService();
$this->prodSignService = new ProdSignService();
}
public function signDocument(SignRequest $request,string $token): array
{
if ($_ENV['API_TOKEN'] !== $request->apiToken) {
throw new AccessDeniedHttpException('Доступ запрещен');
}
$this->api->apiParams = $this->apiParams;
try {
$document = $this->api->download($request->url, $token);
$this->sign($document->tempFileName);
$response = $this->api->send($token, $document->tempFileName . '_sign.pdf', $request->batch);
$this->removeExistingDocument($document);
return $response;
} catch (Exception $e) {
throw new RuntimeException($e->getMessage());
}
}
private function sign (string $documentUrl): void
{
match ($_ENV['APP_ENV']) {
'dev' => $this->devSignService->sign($documentUrl),
'prod' => $this->prodSignService->sign($documentUrl),
};
}
private function removeExistingDocument(BinaryStringFileResult $document): void
{
if (file_exists($document->tempFileName)) {
unlink($document->tempFileName);
}
}
}

View File

@ -1,6 +1,8 @@
#!/bin/bash -x
if [[ $SITE == "prod"]];then
APP_ENV="${APP_ENV:-dev}"
if [[ ${APP_ENV#*=} == "prod" ]]; then
service pcscd start
cert=$(/opt/cprocsp/bin/amd64/csptest -keyset -enum_cont -verifyc -fq | grep Aktiv | awk -F'00 00' '{print $2}' | tr -d '\\')
/opt/cprocsp/bin/amd64/certmgr -inst -cont "${cert}" -store uMy

View File

@ -1,22 +1,22 @@
-----BEGIN PGP MESSAGE-----
owGbwMvMwMX4m2NrnreKjyDj6QN7khjSNt7+XK1UllpUnJmfp2SloGSopKOglJOZ
nJpXnArmJxmkmBmaJukamRpY6pqYp5noJqYkJeomWViamCdZmqYYGIG1FBTlp5Qm
l4C05GeX5iQW6RanFgHNRZKLR7IGJJyXmAu2IqbUwMTICEQap4JIE3MwOxFMGuji
l1YAUYZJCGljYzBpAVZqCCaNECIwbSD7U3MTM3NADsjLL07Ny853yCzRS8kv0QVi
vaJSkIriksSikmKQEiMDIxNdAwtdA7MQAwMrMIoCm1FRkFmUiqTEEqTEyNjK1BKI
wErySnOTUovi89PiocEKUm1Y28lkzMLAyMUgK6bIskDMKPAlS1X1ul379sDih5UJ
FDcMXJwCMJFVJvz/naTc7D4zPFV0Z8rd1HWtvfZuBsenCs1DXVpJ919fN5DamxEc
tPnJWy3X4JuP9+nsCIqXz1d4ofrd/NS7KB5RbX9Bxg9bV075eMOBsbLysJPOnnn9
FbFf9nJ1bdro06nlmm0sHHx/UcHXDgbz1qcKcttnmZnxP81h19XzrZ5r4K27p3fm
4WbOdc+v56twRPJef5r+3znp3eLSWqsOtYZgy6vdRzcEJa18Yai76U/ODt2Nfw/x
7HTX2vvxa9Wcqo1pO/2P+kx3f3GTbXeyQUfVnb9FCyfm28kb/K7/76+hnuH3dvvU
feXvjJ7Oys9Sm3ybdd6HoBk3I3utbr/5YLM63zIoML3pYJX2izNLQ07tPfRg4u9t
J/iT//BGPF3zm3nb/hXimerFpxbsbg5cGZkfJR8n0rXE55yr/JV9mmohf4QPS2uX
nLLzerB4/1cz9s1Ca6Z/2OtnInHMhpXvxTNJpc5lcRe7e5q7+WUvGHSHPUjNeSQe
qSc7q5sxd/qBc82v5M0f9Eeqma5qP7nzgqZ55QP7KWKt51h6y/2PXemtO88sbF1r
GMqf/69hto1qaFsdi7vJnpN32+3+/nuycvKDOYlPuT4mnde9xv5n4luLDfeu/Ki2
6GDxLivKP2fqZlVXYp295veBA8U337gnX3RMa9nm2dnS/otHUKQnsmraLZl3Ducl
V57lnFC7XEyhi1lFsMcv7Ai/sbP5PAA=
=FMIf
owGbwMvMwMX4m2NrnreKjyDj6QN7khjSpXteViuVpRYVZ+bnKVkpKBkq6Sgo5WQm
p+YVp4L4ZhbmaakmSYm6BhaWqbomSamJupYpyca6aYYGScZJxsbJ5qmpIC0FRfkp
pcklIC352aU5iUW6xalFQHOR5OKRrAEJ5yXmgq2IKTUwMTICkcapINLEHMxOBJMG
uvilFUCUYRJC2tgYTFqAlRqCSSOECEwbyP7U3MTMHJAD8vKLU/Oy8x0yS/RS8kt0
gVivqBSkorgksaikGKTEyMDIRNfQQNfINMTAwAqMosBmVBRkFqUiKTHUNTIJMTK2
MrUEIrCSvNLcpNSi+Py0eGiwglQb1nYyGbMwMHIxyIopsiwQMwp8yVJVvW7Xvj2w
+GFlAsUNAxenAExk6QL+PxwK68QK1M50+n6+5/nqf67rlhvB1Yef7bh99zJzstHz
LSdsOD4ee3Ik5rf+FtaUfX+nGBpwGmrMb/IqOzC3VfN73/WFEgJb/s5ZocZguCI9
aM214nT57238r+tcp/LOfrHoxLEt+j8V5rO17S65PaHVRr3Tv6th0icNiZJD3/er
pxtUmHzQKw7h22jIypd3bV3W5teZ04U68jbX3np0wOuZ/fdFB/fUzm2bvHzn2b6p
jJ7vt1WkLpz87dSD2ee47nx/MOUEa8cf3aN7Y+ten47zY/vieMFLSCD5Q9cMz8Kt
5bEJfP1AByw+qXEiLNqt8Mi2ig1MT76fKje6vlelXuH6iqtrpn/9Grx7xcWTDkdD
RF4bfJUzfHH1bsAqlxDL6pXvsxrDGZItzs+YtNPEYtXEzxGHA8vcW45M0lFaNyE9
a9WTZcVZQVvCYk79nfktzmhiZ7voZqY6BqsD82/cPHjPTYA58dvKvNoK5jm2p3lc
i05nXhGwcPwz80H/tvglz1S3rlj1NuCniG7j+8Vn3pdpTdpksTfgqv3ujf4WRzu4
CrWidbcJSRe+2FDD4tRmdydlDmPKNoGEKd+nZv4okAtaey6r8cixDzn3o/6d7XD3
YjUP/uijsCBSvfiM7a0l+QuKJkzg/t7vv261S9LjMk/dSLYdxoZ3d3Lp+FZuDP0u
fnm1QWZSluMsx5972jPC+3d3zrjqXfnJ5Nt1XRYA
=UzI0
-----END PGP MESSAGE-----