Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 190
0.00% covered (danger)
0.00%
0 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
InstallController
0.00% covered (danger)
0.00%
0 / 190
0.00% covered (danger)
0.00%
0 / 15
2070
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 index
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 step
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 requirements
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 database
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 runSeedAjax
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 seedStatus
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 license
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 admin
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
72
 finalize
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 nextStep
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 getProgress
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 testDatabase
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 migrationStatus
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 runMigrationsAjax
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Modules\Installer\Controllers;
4
5use App\Controllers\BaseController;
6use App\Modules\Installer\Core\InstallationManager;
7use App\Modules\Installer\Enums\InstallStep;
8
9class InstallController extends BaseController
10{
11    protected InstallationManager $manager;
12
13    public function __construct()
14    {
15        $this->manager = service('installationManager');
16    }
17
18    // -------------------------------------------------------------------------
19    // ENTRY POINT
20    // -------------------------------------------------------------------------
21
22    public function index()
23    {
24        if ($this->manager->isInstalled()) {
25            return redirect()->to('/');
26        }
27
28        return redirect()->to(
29            '/installer/etape/' . $this->manager->getCurrentStep()->value
30        );
31    }
32
33    // -------------------------------------------------------------------------
34    // STEP VIEW
35    // -------------------------------------------------------------------------
36
37 public function step(string $step)
38{
39    $stepEnum = InstallStep::tryFrom($step);
40
41    if (! $stepEnum) {
42        return redirect()->to('/installer/etape/' . $this->manager->getCurrentStep()->value);
43    }
44
45    if ($this->manager->isInstalled() && $stepEnum !== InstallStep::COMPLETE) {
46        return redirect()->to('/');
47    }
48
49    if (! $this->manager->canAccessStep($stepEnum)) {
50        return redirect()->to('/installer/etape/' . $this->manager->getCurrentStep()->value);
51    }
52
53    $data = [
54        'step'     => $stepEnum,
55        'manager'  => $this->manager,
56        'progress' => $this->getProgress(),
57    ];
58
59    if ($stepEnum === InstallStep::MIGRATION) {
60        $data['migrations'] = $this->manager->getMigrationStatus();
61    }
62
63    return view(
64        "App\Modules\Installer\Views\steps\\{$stepEnum->value}",
65        $data
66    );
67}
68
69    // -------------------------------------------------------------------------
70    // REQUIREMENTS
71    // -------------------------------------------------------------------------
72
73 public function requirements()
74{
75    $checks = [
76        'php'      => version_compare(PHP_VERSION, '8.1', '>='),
77        'openssl'  => extension_loaded('openssl'),
78        'pdo'      => extension_loaded('pdo'),
79        'writable' => is_writable(WRITEPATH),
80    ];
81
82    if (in_array(false, $checks, true)) {
83        return redirect()->back()
84            ->with('error', 'Pré-requis système non satisfaits.');
85    }
86
87    // Avancer jusqu'à DATABASE, peu importe l'étape actuelle
88    while (
89        $this->manager->getCurrentStep() !== InstallStep::DATABASE &&
90        $this->manager->getCurrentStep() !== InstallStep::COMPLETE
91    ) {
92        $this->manager->advance();
93    }
94
95    return redirect()->to(
96        '/installer/etape/' . $this->manager->getCurrentStep()->value
97    );
98}
99
100    // -------------------------------------------------------------------------
101    // DATABASE
102    // -------------------------------------------------------------------------
103
104    public function database()
105    {
106        $data = $this->request->getPost(['host', 'database', 'username', 'password']);
107
108        foreach (['host', 'database', 'username'] as $field) {
109            if (empty($data[$field])) {
110                return redirect()->back()
111                    ->with('error', "Champ requis : {$field}");
112            }
113        }
114
115        if (! $this->manager->saveDatabase($data)) {
116            return redirect()->back()
117                ->with('error', 'Connexion base de données échouée.');
118        }
119
120        $this->manager->advance();
121
122        return redirect()->to(
123            '/installer/etape/' . $this->manager->getCurrentStep()->value
124        );
125    }
126    // -------------------------------------------------------------------------
127    // SEED
128    // -------------------------------------------------------------------------
129    public function runSeedAjax()
130{
131    try {
132        $this->manager->runSeed();
133
134        // ✅ Avancer l'étape après le seed
135        $this->manager->advance();
136
137        return $this->response->setJSON([
138            'success' => true
139        ]);
140
141    } catch (\Throwable $e) {
142        return $this->response->setJSON([
143            'success' => false,
144            'error' => $e->getMessage()
145        ])->setStatusCode(500);
146    }
147}
148
149
150        public function seedStatus()
151    {
152        $status = $this->manager->getSeedStatus();
153
154        return $this->response->setJSON([
155            'seeds' => $status
156        ]);
157    }
158
159
160
161
162
163
164    // -------------------------------------------------------------------------
165    // LICENSE
166    // -------------------------------------------------------------------------
167
168    // ✅ Garder celle-ci
169public function license()
170{
171    $key = trim((string) $this->request->getPost('license_key'));
172
173    if ($key === '') {
174        return redirect()->back()
175            ->with('error', 'Clé de licence obligatoire.');
176    }
177
178    $this->manager->saveLicenseKey($key);
179
180    if (! $this->manager->canAdvance()) {
181        $status = $this->manager->getLicenseStatus();
182
183        $message = match ($status) {
184            'invalid'   => 'Licence introuvable ou inactive.',
185            'forbidden' => 'Licence non autorisée pour ce domaine.',
186            'error'     => 'Impossible de joindre le serveur de licence.',
187            default     => 'Licence invalide.',
188        };
189
190        return redirect()->back()->with('error', $message);
191    }
192
193    $this->manager->advance();
194
195    return redirect()->to(
196        '/installer/etape/' . $this->manager->getCurrentStep()->value
197    );
198}
199
200// ❌ Supprimer l'ancienne
201
202    // -------------------------------------------------------------------------
203    // ADMIN
204    // -------------------------------------------------------------------------
205
206    public function admin()
207    {
208        $data = $this->request->getPost([
209            'name',
210            'email',
211            'password',
212            'password_confirm'
213        ]);
214
215        if (
216            empty($data['name']) ||
217            empty($data['email']) ||
218            empty($data['password'])
219        ) {
220            return redirect()->back()
221                ->with('error', 'Tous les champs sont requis.');
222        }
223
224        if (! filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
225            return redirect()->back()
226                ->with('error', 'Email invalide.');
227        }
228
229        if (strlen($data['password']) < 8) {
230            return redirect()->back()
231                ->with('error', 'Mot de passe trop court.');
232        }
233
234        if ($data['password'] !== $data['password_confirm']) {
235            return redirect()->back()
236                ->with('error', 'Confirmation incorrecte.');
237        }
238
239        try {
240            $db = \Config\Database::connect();
241
242            $db->table('user')->insert([
243                'name'       => $data['name'],
244                'email'      => strtolower(trim($data['email'])),
245                'password'   => password_hash($data['password'], PASSWORD_BCRYPT),
246                'role'       => 'admin',
247                'created_at' => date('Y-m-d H:i:s'),
248            ]);
249
250        } catch (\Throwable) {
251            return redirect()->back()
252                ->with('error', 'Erreur création admin.');
253        }
254
255        $this->manager->advance();
256
257        return redirect()->to(
258            '/installer/etape/' . $this->manager->getCurrentStep()->value
259        );
260    }
261
262    // -------------------------------------------------------------------------
263    // FINALIZE
264    // -------------------------------------------------------------------------
265
266    public function finalize()
267    {
268        if ($this->manager->getCurrentStep() !== InstallStep::FINALIZE) {
269            return redirect()->to(
270                '/installer/etape/' . $this->manager->getCurrentStep()->value
271            );
272        }
273
274        $this->manager->finalize();
275
276        return redirect()->to('/installer/etape/' . InstallStep::COMPLETE->value);
277    }
278
279    // -------------------------------------------------------------------------
280    // AJAX NEXT STEP
281    // -------------------------------------------------------------------------
282
283    public function nextStep()
284    {
285        try {
286            $next = $this->manager->advance();
287
288            return $this->response->setJSON([
289                'success' => true,
290                'next'    => $next?->value,
291                'step'    => $this->manager->getCurrentStep()->value,
292            ]);
293
294        } catch (\RuntimeException $e) {
295            return $this->response
296                ->setStatusCode(422)
297                ->setJSON([
298                    'success' => false,
299                    'error'   => $e->getMessage(),
300                    'step'    => $this->manager->getCurrentStep()->value,
301                ]);
302        }
303    }
304
305    // -------------------------------------------------------------------------
306    // PROGRESS
307    // -------------------------------------------------------------------------
308
309    private function getProgress(): array
310    {
311        $pipeline = array_map(
312            fn($step) => $step->value,
313            $this->manager->getPipeline()
314        );
315
316        $current = $this->manager->getCurrentStep()->value;
317
318        $index = array_search($current, $pipeline, true);
319        $index = $index === false ? 0 : $index;
320
321        return [
322            'steps'         => $pipeline,
323            'current_index' => $index,
324            'percent'       => round((($index + 1) / count($pipeline)) * 100),
325        ];
326    }
327
328    public function testDatabase()
329{
330    $data = $this->request->getJSON(true);
331
332    if (
333        empty($data['host']) ||
334        empty($data['database']) ||
335        empty($data['username'])
336    ) {
337        return $this->response->setJSON([
338            'success' => false,
339            'error'   => 'Paramètres incomplets'
340        ]);
341    }
342
343    try {
344        $dsn = "mysql:host={$data['host']};dbname={$data['database']};port={$data['port']};charset=utf8mb4";
345
346        new \PDO($dsn, $data['username'], $data['password'], [
347            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
348        ]);
349
350        return $this->response->setJSON([
351            'success' => true
352        ]);
353
354    } catch (\Throwable $e) {
355        return $this->response->setJSON([
356            'success' => false,
357            'error'   => 'Connexion refusée'
358        ]);
359    }
360}
361
362
363    // -------------------------------------------------------------------------
364    // MIGRATION STATUS (AJAX)
365    // -------------------------------------------------------------------------
366public function migrationStatus()
367{
368    $status = $this->manager->getMigrationStatus();
369
370    $allDone = empty(array_filter(
371        $status,
372        fn($s) => $s !== 'done'
373    ));
374
375    return $this->response->setJSON([
376        'tables'  => $status,
377        'allDone' => $allDone
378    ]);
379}
380
381    // -------------------------------------------------------------------------
382    // RUN MIGRATIONS (AJAX)
383    // -------------------------------------------------------------------------
384public function runMigrationsAjax()
385{
386    try {
387        $result = $this->manager->runMigrations();
388
389        // ✅ Avancer de MIGRATION → SEED
390        $this->manager->advance();
391
392        return $this->response->setJSON([
393            'success' => true,
394            'result'  => $result
395        ]);
396    } catch (\Throwable $e) {
397        return $this->response->setJSON([
398            'success' => false,
399            'error'   => $e->getMessage()
400        ])->setStatusCode(500);
401    }
402}
403}
404
405