Commit 703a52ce by Damien Moulard

Merge branch '8931-export-adherents-data-scp' into 'develop'

create command to export data and upload it to distant server via scp

See merge request !156
parents d73ae436 c0cdaeab
......@@ -50,7 +50,9 @@ Copier le fichier .env.dist en .env et configurer :
Exemple : Sur Mysql on a : Version du serveur : 10.3.31-MariaDB - MariaDB Server
On ajoute à DATABASE_URL : => ?serverVersion=10.3.31-MariaDB
- APP_ENV=dev et APP_DEBUG=1
- APP_ENV=dev et APP_DEBUG=1 en environnement de développement
- APP_ENV=prod et APP_DEBUG=0 sinon (preprod inclue)
- PREPROD_ENV=true en preprod
- l'envoi de mail (MAILER_URL (penser à saisir MLC_NOTIF_EMAIL compatible avec le MAILER utilisé))
- la variable APP_SECRET (variable secrète que vous pouvez générer à partir de cette url : http://nux.net/secret
- s'il s'agit d'une instance TAV, mettre la variable TAV_ENV à 1 (sinon la laisser à zéro)
......@@ -201,6 +203,10 @@ Quand CCAS_MODE = 1, pour que les reconversions CCAS soient effectuées et les e
57 1 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:export-ccas-transactions
27 2 10 * * kohinos rm -f /home/kohinos/kohinos/ccastransactions/*
Pour activer l'export de données vers un serveur distant :
0 0 1 * * kohinos php /home/kohinos/kohinos/bin/console kohinos:ssa:export-and-upload-data
## Lancer le Kohinos en local
Installer le client symfony
......
......@@ -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": "110165f3dccdbce570dcdac22ab55ce0",
"content-hash": "4d8de6c53c55b6448a5d31f39f6b0409",
"packages": [
{
"name": "alcohol/iso4217",
......@@ -6028,6 +6028,73 @@
"time": "2019-08-11T08:31:54+00:00"
},
{
"name": "paragonie/constant_time_encoding",
"version": "v2.8.2",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/e30811f7bc69e4b5b6d5783e712c06c8eabf0226",
"reference": "e30811f7bc69e4b5b6d5783e712c06c8eabf0226",
"shasum": ""
},
"require": {
"php": "^7|^8"
},
"require-dev": {
"phpunit/phpunit": "^6|^7|^8|^9",
"vimeo/psalm": "^1|^2|^3|^4"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"time": "2025-09-24T15:12:37+00:00"
},
{
"name": "payum/core",
"version": "1.6.1",
"target-dir": "Payum/Core",
......@@ -6969,6 +7036,116 @@
"time": "2020-12-31T18:03:49+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.51",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "d59c94077f9c9915abb51ddb52ce85188ece1748"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/d59c94077f9c9915abb51ddb52ce85188ece1748",
"reference": "d59c94077f9c9915abb51ddb52ce85188ece1748",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"type": "library",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.51"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2026-04-10T01:33:53+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "0.4.11",
"source": {
......@@ -19734,5 +19911,5 @@
"ext-mbstring": "*"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.9.0"
}
<?php
namespace App\Command;
use App\Utils\CustomEntityManager;
use phpseclib3\Net\SFTP;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Entity\Adherent;
use App\Entity\Prestataire;
use App\Entity\Flux;
class ExportAndUploadDataCommand extends Command
{
protected static $defaultName = 'kohinos:ssa:export-and-upload-data';
protected $em;
private LoggerInterface $logger;
private string $host;
private string $user;
private string $password;
private string $remotePath;
private string $localPath;
public function __construct(
CustomEntityManager $em,
LoggerInterface $logger
) {
parent::__construct();
$this->em = $em;
$this->logger = $logger;
$this->host = $_ENV['EXPORT_SSH_HOST'];
$this->user = $_ENV['EXPORT_SSH_USER'];
$this->password = $_ENV['EXPORT_SSH_PASSWORD'];
$this->remotePath = $_ENV['EXPORT_REMOTE_PATH'];
$this->localPath = $_ENV['EXPORT_LOCAL_PATH'];
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// 1. Get data
$data = [
"adherents" => [],
"prestataires" => [],
"flux" => [],
];
$adherents = $this->em->getRepository(Adherent::class)->findAllForSsaExport();
foreach ($adherents as $a) {
$adh = [
"lastname" => $a->getUser()->getLastname(),
"firstname" => $a->getUser()->getFirstname(),
"email" => $a->getUser()->getEmail(),
"id" => $a->getId(),
"phone" => $a->getUser()->getPhone(),
"address" => $a->getGeoloc() == null ? null : [
"address" => $a->getGeoloc()->getAdresse(),
"zip" => $a->getGeoloc()->getCpostal(),
"city" => $a->getGeoloc()->getVille(),
],
"household_composition" => $a->getHouseholdCount(),
"cotisation_amount" => $a->getCotisationAmount(),
"allocation_amount" => $a->getAllocationAmount(),
"cohort" => $a->getExternalData() == null ? null : $a->getExternalData()->getCohort(),
"external_id" => $a->getExternalData() == null ? null : $a->getExternalData()->getExternalId(),
"dated_data" => [],
];
foreach ($a->getExternalDatedDataCollection() as $dated_data) {
$adh["dated_data"][] = [
"year" => $dated_data->getYear(),
"annual_income" => $dated_data->getAnnualIncome(),
"monthly_income" => $dated_data->getMonthlyIncome(),
];
}
$data["adherents"][] = $adh;
}
$prestataires = $this->em->getRepository(Prestataire::class)->findAllForSsaExport();
foreach ($prestataires as $p) {
$presta = [
"raison" => $p->getRaison(),
"addresses" => [],
"rubrics" => [],
];
foreach ($p->getGeolocs() as $g) {
$presta["addresses"][] = [
"address" => $a->getGeoloc()->getAdresse(),
"zip" => $a->getGeoloc()->getCpostal(),
"city" => $a->getGeoloc()->getVille(),
];
}
foreach ($p->getRubriques() as $r) {
$presta["rubrics"][] = [
"name" => $r->getName()
];
}
$data["prestataires"][] = $presta;
}
$flux = $this->em->getRepository(Flux::class)->findAll();
foreach ($flux as $f) {
$data["flux"][] = [
"date" => $f->getCreatedAt() ? $f->getCreatedAt()->format('Y-m-d H:i:s') : '',
"amount" => $f->getMontant(),
"type" => $f->getType(),
"sender_id" => empty($f->getExpediteur()) ? null : $f->getExpediteur()->getId(),
"recipient_id" => empty($f->getDestinataire()) ? null : $f->getDestinataire()->getId(),
];
}
// 2. Génération JSON
if (!is_dir($this->localPath)) {
mkdir($this->localPath, 0777, true);
}
if ('dev' == $_ENV['APP_ENV']) {
$filename = 'test_export_' . date('Ymd_His') . '.json';
} else if (true == $_ENV['PREPROD_ENV']) {
$filename = 'preprod_export_' . date('Ymd_His') . '.json';
} else {
$filename = 'export_' . date('Ymd_His') . '.json';
}
$filePath = $this->localPath . '/' . $filename;
file_put_contents($filePath, json_encode($data, JSON_PRETTY_PRINT));
$output->writeln('Fichier exporté : ' . $filePath);
// 3. Connexion SSH
$sftp = new SFTP($this->host);
if (!$sftp->login($this->user, $this->password)) {
throw new \Exception('Échec authentification SSH');
}
// 4. Upload
$remoteFile = rtrim($this->remotePath, '/') . '/' . $filename;
if (!$sftp->put($remoteFile, $filePath, SFTP::SOURCE_LOCAL_FILE)) {
throw new \Exception('Échec upload SFTP');
}
$output->writeln('Upload réussi');
// 5. Delete local file
unlink($filePath);
$output->writeln('Fichier supprimé : ' . $filePath);
return 0;
} catch (\Throwable $e) {
$this->logger->error('Erreur export', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$output->writeln('<error>' . $e->getMessage() . '</error>');
return 0;
}
}
}
\ No newline at end of file
......@@ -142,13 +142,13 @@ class AdherentRepository extends ServiceEntityRepository
*/
public function findbyExclude(Adherent $adherent)
{
$qb = $this->createQueryBuilder('p');
$qb = $this->createQueryBuilder('a');
return $qb
->leftjoin('p.user', 'u')
->where('p.id != :presta')
->andWhere('p.enabled = :enabled')
->setParameter('presta', $adherent->getId())
->leftjoin('a.user', 'u')
->where('a.id != :adh')
->andWhere('a.enabled = :enabled')
->setParameter('adh', $adherent->getId())
->setParameter('enabled', true)
->orderBy('u.lastname', 'ASC')
->getQuery()
......@@ -174,4 +174,22 @@ class AdherentRepository extends ServiceEntityRepository
->getResult()
;
}
public function findAllForSsaExport()
{
$qb = $this->createQueryBuilder('a');
return $qb
->leftjoin('a.user', 'u')
->leftjoin('a.externalData', 'e')
->leftjoin('a.externalDatedDataCollection', 'ed')
->leftjoin('a.geoloc', 'g')
// ->where('p.enabled = :enabled')
// ->andWhere('u.enabled = :userEnabled')
// ->setParameters(['enabled' => true, 'userEnabled' => true])
->orderBy('u.lastname', 'ASC')
->getQuery()
->getResult()
;
}
}
......@@ -337,4 +337,20 @@ class PrestataireRepository extends ServiceEntityRepository
return $return;
}
public function findAllForSsaExport()
{
$qb = $this->createQueryBuilder('p');
return $qb
->leftjoin('p.geolocs', 'g')
->leftjoin('p.rubriques', 'r')
// ->where('p.enabled = :enabled')
// ->andWhere('u.enabled = :userEnabled')
// ->setParameters(['enabled' => true, 'userEnabled' => true])
->orderBy('p.raison', 'ASC')
->getQuery()
->getResult()
;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment