Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.50% covered (success)
87.50%
35 / 40
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConfigurationController
87.50% covered (success)
87.50%
35 / 40
75.00% covered (warning)
75.00%
3 / 4
10.20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sendTestMail
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 activateMaintenanceMode
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 testRedisConnection
70.59% covered (warning)
70.59%
12 / 17
0.00% covered (danger)
0.00%
0 / 1
4.41
1<?php
2
3/**
4 * The Admin Configuration Controller
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2023-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2023-10-26
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Controller\Administration\Api;
21
22use phpMyFAQ\Core\Exception;
23use phpMyFAQ\Enums\AdminLogType;
24use phpMyFAQ\Enums\PermissionType;
25use phpMyFAQ\Mail;
26use phpMyFAQ\Session\RedisSessionHandler;
27use phpMyFAQ\Session\Token;
28use phpMyFAQ\Translation;
29use Symfony\Component\HttpFoundation\JsonResponse;
30use Symfony\Component\HttpFoundation\Request;
31use Symfony\Component\HttpFoundation\Response;
32use Symfony\Component\Routing\Attribute\Route;
33
34final class ConfigurationController extends AbstractAdministrationApiController
35{
36    public function __construct(
37        private readonly Mail $mail,
38    ) {
39        parent::__construct();
40    }
41
42    /**
43     * @throws \Throwable
44     */
45    #[Route(path: 'configuration/send-test-mail', name: 'admin.api.configuration.send-test-mail', methods: ['POST'])]
46    public function sendTestMail(Request $request): JsonResponse
47    {
48        $this->userHasPermission(PermissionType::CONFIGURATION_EDIT);
49
50        $data = $this->getJsonObject($request);
51
52        if (!Token::getInstance($this->session)->verifyToken('configuration', (string) ($data->csrf ?? ''))) {
53            return $this->json(['error' => Translation::get(key: 'msgNoPermission')], Response::HTTP_UNAUTHORIZED);
54        }
55
56        try {
57            $this->mail->addTo($this->configuration->getAdminEmail());
58            $this->mail->setReplyTo($this->configuration->getNoReplyEmail());
59            $this->mail->subject = $this->configuration->getTitle() . ': Mail test successful.';
60            $this->mail->message = 'It works on my machine. ðŸš€';
61            $result = $this->mail->send();
62
63            return $this->json(['success' => $result], Response::HTTP_OK);
64        } catch (\Throwable $e) {
65            return $this->json(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
66        }
67    }
68
69    /**
70     * @throws \Exception
71     */
72    #[Route(
73        path: 'configuration/activate-maintenance-mode',
74        name: 'admin.api.configuration.activate-maintenance-mode',
75        methods: ['POST'],
76    )]
77    public function activateMaintenanceMode(Request $request): JsonResponse
78    {
79        $this->userHasPermission(PermissionType::CONFIGURATION_EDIT);
80
81        $data = $this->getJsonObject($request);
82
83        if (!Token::getInstance($this->session)->verifyToken(
84            'activate-maintenance-mode',
85            (string) ($data->csrf ?? ''),
86        )) {
87            return $this->json(['error' => Translation::get(key: 'msgNoPermission')], Response::HTTP_UNAUTHORIZED);
88        }
89
90        $this->configuration->set('main.maintenanceMode', 'true');
91
92        $this->adminLog->log($this->currentUser, AdminLogType::SYSTEM_MAINTENANCE_MODE_ENABLED->value);
93
94        return $this->json(['success' => Translation::get(key: 'healthCheckOkay')], Response::HTTP_OK);
95    }
96
97    /**
98     * @throws Exception|\Exception
99     */
100    #[Route(
101        path: 'configuration/test-redis-connection',
102        name: 'admin.api.configuration.test-redis-connection',
103        methods: ['POST'],
104    )]
105    public function testRedisConnection(Request $request): JsonResponse
106    {
107        $this->userHasPermission(PermissionType::CONFIGURATION_EDIT);
108
109        $data = $this->getJsonObject($request);
110
111        if (!Token::getInstance($this->session)->verifyToken('configuration', (string) ($data->csrf ?? ''))) {
112            return $this->json(['error' => Translation::get(key: 'msgNoPermission')], Response::HTTP_UNAUTHORIZED);
113        }
114
115        $redisDsn = trim((string) ($data->redisDsn ?? $this->configuration->get('storage.redisDsn') ?? ''));
116        $timeout = (float) ($data->timeout ?? $this->configuration->get('storage.redisConnectTimeout') ?? 1.0);
117        if ($timeout <= 0) {
118            $timeout = 1.0;
119        }
120
121        try {
122            RedisSessionHandler::validateConnection($redisDsn, $timeout);
123
124            return $this->json([
125                'success' => true,
126                'message' => 'Redis connection successful.',
127            ], Response::HTTP_OK);
128        } catch (\Throwable) {
129            return $this->json([
130                'error' => 'Redis connection failed. Please verify the DSN and ensure the server is running.',
131            ], Response::HTTP_BAD_REQUEST);
132        }
133    }
134}