diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 593e56f..1b1a3b8 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -203,7 +203,22 @@ args: chdir: "{{ path }}" when: ansible_os_family == "Debian" + - name: Exécuter app:git-log-update pour mettre à jour le journal client + ansible.builtin.command: php bin/console app:git-log-update + become: false + args: + chdir: "{{ path }}" + # On ignore les erreurs pour ne pas bloquer le déploiement si l'IA ou Git échoue + ignore_errors: yes + - name: S'assurer que le fichier update.json a les bonnes permissions + ansible.builtin.file: + path: "{{ path }}/var/update.json" + owner: bot + group: www-data + mode: '0664' + state: file + ignore_errors: yes - name: Exécuter liip:imagine:cache:remove dans le répertoire de l application ansible.builtin.command: php bin/console liip:imagine:cache:remove become: false diff --git a/composer.json b/composer.json index cb3cfb2..df4e354 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "exbil/mailcow-php-api": ">=0.15.0", "fkrzski/robots-txt": "^2.0", "fpdf/fpdf": "^1.86", + "gemini-api-php/client": "^1.7", "google/apiclient": "^2.19.0", "google/cloud": "^0.296.0", "healey/robots": "^1.0.1", diff --git a/composer.lock b/composer.lock index c15b985..2bfefb0 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": "d69b2f764686c771554adde51917a972", + "content-hash": "04441998f46efc5154a83945a7ca9a46", "packages": [ { "name": "async-aws/core", @@ -2536,6 +2536,73 @@ }, "time": "2025-12-08T14:03:59+00:00" }, + { + "name": "gemini-api-php/client", + "version": "v1.7.2", + "source": { + "type": "git", + "url": "https://github.com/gemini-api-php/client.git", + "reference": "a48e61285d82b24117a5c8928dd1e504818f908b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/gemini-api-php/client/zipball/a48e61285d82b24117a5c8928dd1e504818f908b", + "reference": "a48e61285d82b24117a5c8928dd1e504818f908b", + "shasum": "" + }, + "require": { + "php": "^8.1", + "php-http/discovery": "^1.19", + "psr/http-client": "^1.0", + "psr/http-client-implementation": "*", + "psr/http-factory": "^1.0.2", + "psr/http-factory-implementation": "*", + "psr/http-message": "^1.0.1 || ^2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.41", + "guzzlehttp/guzzle": "^7.8.0", + "guzzlehttp/psr7": "^2.0.0", + "phpstan/phpstan": "^1.10.50", + "phpunit/phpunit": "^10.5" + }, + "suggest": { + "ext-curl": "Required for streaming responses" + }, + "type": "library", + "autoload": { + "psr-4": { + "GeminiAPI\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Erdem Köse", + "email": "erdemkose@gmail.com" + } + ], + "description": "API client for Google's Gemini API", + "keywords": [ + "Gemini", + "ai", + "api", + "client", + "gemini pro", + "gemini pro vision", + "google", + "php", + "sdk" + ], + "support": { + "issues": "https://github.com/gemini-api-php/client/issues", + "source": "https://github.com/gemini-api-php/client/tree/v1.7.2" + }, + "time": "2025-01-31T15:31:02+00:00" + }, { "name": "google/apiclient", "version": "v2.19.0", diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 8abba82..fc1b833 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -54,25 +54,6 @@ RUN pecl install redis && docker-php-ext-enable redis # Installer Excimer via pecl RUN pecl install excimer && docker-php-ext-enable excimer -# Configuration et installation de Xdebug -# Utilisation de --no-install-recommends pour éviter l'installation de paquets inutiles -RUN apt-get update && apt-get install -y --no-install-recommends git \ - && mkdir -p /usr/src/php/ext/xdebug \ - && git clone https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \ - && cd /usr/src/php/ext/xdebug \ - && phpize \ - && ./configure --enable-xdebug \ - && make && make install \ - && rm -rf /usr/src/php/ext/xdebug \ - && apt-get autoremove -y build-essential git \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -COPY ./docker/php/custom.ini /usr/local/etc/php/conf.d/custom.ini - -RUN echo "zend_extension=xdebug" > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini -RUN echo "xdebug.mode=develop,debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ - && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ - && echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini RUN mkdir -p /opt/phpstorm-coverage && \ chmod -R 777 /opt/phpstorm-coverage diff --git a/src/Command/GitSyncLogCommand.php b/src/Command/GitSyncLogCommand.php new file mode 100644 index 0000000..094bf50 --- /dev/null +++ b/src/Command/GitSyncLogCommand.php @@ -0,0 +1,124 @@ +run(); + + if (!$process->isSuccessful()) { + $io->error("Erreur Git : " . $process->getErrorOutput()); + return Command::FAILURE; + } + + $outputGit = explode('|', trim($process->getOutput())); + $rawMessage = $outputGit[0] ?? ''; + $commitDate = $outputGit[1] ?? date('Y-m-d H:i:s'); + $commitHash = $outputGit[2] ?? 'unknown'; + + // 2. Détermination du TYPE (feature, fix, optimise, new) + $type = 'new'; + $lowerMsg = strtolower($rawMessage); + + if (preg_match('/(fix|bug|patch|correct)/', $lowerMsg)) { + $type = 'fix'; + } elseif (preg_match('/(feat|add|create|nouveau|new)/', $lowerMsg)) { + $type = 'feature'; + } elseif (preg_match('/(perf|opti|refactor|clean|speed)/', $lowerMsg)) { + $type = 'optimise'; + } + + // 3. Vérification anti-doublon (Basée sur le Hash) + $data = []; + if (file_exists($filePath)) { + $data = json_decode(file_get_contents($filePath), true) ?? []; + } + + if (!empty($data) && $data[0]['hash'] === $commitHash) { + $io->info("Le commit [$commitHash] est déjà dans le journal client."); + return Command::SUCCESS; + } + + // 4. Appel IA Gemini-3-Pro-Preview pour la reformulation + $friendlyMessage = $rawMessage; + try { + // Ta clé API + $client = new Client("AIzaSyDTPJERlUC47bcvhZU51Lwpqb1uxXS8SIg"); + $model = 'gemini-3-pro-preview'; + + $prompt = "Tu es un expert en communication web pour Ludik Event. Ta mission est de transformer + un message de commit technique en une note de mise à jour élégante pour ton client. + + MESSAGE TECHNIQUE : \"$rawMessage\" + + DIRECTIVES : + 1. Reformule pour un propriétaire de site non-technique. + 2. Sois court, positif et rassurant. + 3. Ne commence JAMAIS par 'Voici la phrase' ou 'Mise à jour'. + 4. Donne uniquement le texte final prêt à être affiché. + + RÉSULTAT ATTENDU :"; + + $response = $client->withV1BetaVersion()->generativeModel($model)->generateContent( + new TextPart($prompt) + ); + + // Adaptation selon la structure de retour du SDK + $aiText = $response->text(); + if ($aiText) { + $friendlyMessage = trim($aiText); + } + } catch (\Exception $e) { + $io->warning("L'IA n'a pas pu traiter le message. Utilisation du texte brut."); + } + + // 5. Création de l'entrée JSON + $newEntry = [ + 'type' => $type, + 'message' => $friendlyMessage, + 'date' => $commitDate, + 'hash' => $commitHash + ]; + + // 6. Sauvegarde et rotation (5 max) + array_unshift($data, $newEntry); + $data = array_slice($data, 0, 5); + + if (!is_dir('var')) { + mkdir('var', 0777, true); + } + + file_put_contents($filePath, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + + $io->success("Journal client mis à jour avec succès (Type: $type)."); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/Dashboard/HomeController.php b/src/Controller/Dashboard/HomeController.php index cb64285..7705832 100644 --- a/src/Controller/Dashboard/HomeController.php +++ b/src/Controller/Dashboard/HomeController.php @@ -29,53 +29,65 @@ class HomeController extends AbstractController // 1. Récupération sécurisée du Token (Cache 2h) $token = $cache->get('umami_token', function (ItemInterface $item) use ($httpClient, $baseUrl) { $item->expiresAfter(7200); - $response = $httpClient->request('POST', "$baseUrl/auth/login", [ 'json' => [ 'username' => $_ENV['UMAMI_USER'], 'password' => $_ENV['UMAMI_PASSWORD'], ] ]); - return $response->toArray()['token'] ?? null; }); - // 2. Récupération des Stats (Cache 15 min pour la fluidité du CRM) + // 2. Récupération des Stats Umami $stats = $cache->get('umami_stats_24h', function (ItemInterface $item) use ($httpClient, $baseUrl, $token, $websiteId) { - $item->expiresAfter(900); // 15 minutes - + $item->expiresAfter(900); if (!$token) return ['visitors' => 0, 'views' => 0]; try { $startAt = (time() - (24 * 3600)) * 1000; $endAt = time() * 1000; - - $response = $httpClient->request('GET', "$baseUrl/websites/$websiteId/stats", [ 'headers' => ['Authorization' => "Bearer $token"], - 'query' => [ - 'startAt' => $startAt, - 'endAt' => $endAt, - ], + 'query' => ['startAt' => $startAt, 'endAt' => $endAt], ]); - $data = $response->toArray(); return [ - 'visitors' => $data['visitors'] ?? 0, - 'views' => $data['pageviews'] ?? 0 + 'visitors' => $data['visitors']['value'] ?? $data['visitors'] ?? 0, + 'views' => $data['pageviews']['value'] ?? $data['pageviews'] ?? 0 ]; } catch (\Exception $e) { return ['visitors' => 0, 'views' => 0]; } }); + // 3. Récupération des Updates (Journal de bord client) + $updateFile = $this->getParameter('kernel.project_dir') . '/var/update.json'; + $updates = []; + + if (file_exists($updateFile)) { + $rawUpdates = json_decode(file_get_contents($updateFile), true) ?? []; + + // On enrichit les données avec les couleurs Tailwind + $updates = array_map(function ($update) { + $update['tag_color'] = match ($update['type'] ?? 'new') { + 'feature' => 'bg-emerald-100 text-emerald-700 border-emerald-200', + 'fix' => 'bg-rose-100 text-rose-700 border-rose-200', + 'optimise' => 'bg-amber-100 text-amber-700 border-amber-200', + 'new' => 'bg-slate-100 text-slate-700 border-slate-200', + default => 'bg-gray-100 text-gray-700 border-gray-200', + }; + return $update; + }, $rawUpdates); + } + return $this->render('dashboard/home.twig', [ 'product' => $productRepository->count(), 'devis_wait_sign' => $devisRepository->waitSign(), 'customers' => $customerRepository->count(), 'nbVisitor' => $stats['visitors'], 'nbView' => $stats['views'], - 'statview' => "https://tools-security.esy-web.dev/share/o9j2XMjV4Trnnbfb" + 'statview' => "https://tools-security.esy-web.dev/share/o9j2XMjV4Trnnbfb", + 'updates' => $updates // Passage des mises à jour au template ]); } } diff --git a/templates/dashboard/base.twig b/templates/dashboard/base.twig index c6f65cc..158b42d 100644 --- a/templates/dashboard/base.twig +++ b/templates/dashboard/base.twig @@ -16,7 +16,6 @@