Phase 116 complete: - add HostedSMS settings with encrypted password storage - add SimpleAPI real test SMS flow and integrations hub row - document schema, architecture, changelog, and PAUL state Co-Authored-By: Codex <noreply@openai.com>
155 lines
5.6 KiB
PHP
155 lines
5.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Settings;
|
|
|
|
use App\Core\Exceptions\IntegrationConfigException;
|
|
use App\Core\Http\RedirectPathResolver;
|
|
use App\Core\Http\Request;
|
|
use App\Core\Http\Response;
|
|
use App\Core\I18n\Translator;
|
|
use App\Core\Security\Csrf;
|
|
use App\Core\Support\Flash;
|
|
use App\Core\View\Template;
|
|
use App\Modules\Auth\AuthService;
|
|
use Throwable;
|
|
|
|
final class HostedSmsIntegrationController
|
|
{
|
|
public function __construct(
|
|
private readonly Template $template,
|
|
private readonly Translator $translator,
|
|
private readonly AuthService $auth,
|
|
private readonly HostedSmsIntegrationRepository $repository,
|
|
private readonly HostedSmsApiClient $apiClient,
|
|
private readonly IntegrationsRepository $integrations
|
|
) {
|
|
}
|
|
|
|
public function index(Request $request): Response
|
|
{
|
|
$html = $this->template->render('settings/hostedsms', [
|
|
'title' => $this->translator->get('settings.hostedsms.title'),
|
|
'activeMenu' => 'settings',
|
|
'activeSettings' => 'integrations',
|
|
'user' => $this->auth->user(),
|
|
'csrfToken' => Csrf::token(),
|
|
'settings' => $this->repository->getSettings(),
|
|
'errorMessage' => (string) Flash::get('settings_error', ''),
|
|
'successMessage' => (string) Flash::get('settings_success', ''),
|
|
'testMessage' => (string) Flash::get('hostedsms_test', ''),
|
|
], 'layouts/app');
|
|
|
|
return Response::html($html);
|
|
}
|
|
|
|
public function save(Request $request): Response
|
|
{
|
|
$redirectTo = $this->resolveRedirect($request);
|
|
|
|
if (!Csrf::validate((string) $request->input('_token', ''))) {
|
|
Flash::set('settings_error', $this->translator->get('auth.errors.csrf_expired'));
|
|
return Response::redirect($redirectTo);
|
|
}
|
|
|
|
try {
|
|
$this->repository->saveSettings([
|
|
'user_email' => (string) $request->input('user_email', ''),
|
|
'password' => (string) $request->input('password', ''),
|
|
'sender' => (string) $request->input('sender', ''),
|
|
'convert_message_to_gsm7' => $request->input('convert_message_to_gsm7', ''),
|
|
'is_active' => $request->input('is_active', ''),
|
|
]);
|
|
Flash::set('settings_success', $this->translator->get('settings.hostedsms.flash.saved'));
|
|
} catch (Throwable $exception) {
|
|
Flash::set(
|
|
'settings_error',
|
|
$this->translator->get('settings.hostedsms.flash.save_failed') . ' ' . $exception->getMessage()
|
|
);
|
|
}
|
|
|
|
return Response::redirect($redirectTo);
|
|
}
|
|
|
|
public function test(Request $request): Response
|
|
{
|
|
$redirectTo = $this->resolveRedirect($request);
|
|
|
|
if (!Csrf::validate((string) $request->input('_token', ''))) {
|
|
Flash::set('settings_error', $this->translator->get('auth.errors.csrf_expired'));
|
|
return Response::redirect($redirectTo);
|
|
}
|
|
|
|
try {
|
|
$phone = $this->validatePhone((string) $request->input('phone', ''));
|
|
$message = $this->validateMessage((string) $request->input('message', ''));
|
|
$credentials = $this->repository->getCredentials();
|
|
|
|
if ($credentials === null) {
|
|
throw new IntegrationConfigException('Najpierw zapisz kompletna konfiguracje HostedSMS.');
|
|
}
|
|
|
|
$result = $this->apiClient->sendSms(
|
|
$credentials['user_email'],
|
|
$credentials['password'],
|
|
$credentials['sender'],
|
|
$phone,
|
|
$message,
|
|
$credentials['convert_message_to_gsm7']
|
|
);
|
|
|
|
$status = $result['ok'] ? 'ok' : 'fail';
|
|
$this->integrations->updateTestResult(
|
|
$credentials['integration_id'],
|
|
$status,
|
|
(int) $result['http_code'],
|
|
(string) $result['message']
|
|
);
|
|
|
|
if ($result['ok']) {
|
|
Flash::set('hostedsms_test', $this->translator->get('settings.hostedsms.flash.test_success', [
|
|
'message_id' => (string) $result['message_id'],
|
|
]));
|
|
} else {
|
|
Flash::set('settings_error', $this->translator->get('settings.hostedsms.flash.test_failed') . ' ' . $result['message']);
|
|
}
|
|
} catch (Throwable $exception) {
|
|
Flash::set('settings_error', $this->translator->get('settings.hostedsms.flash.test_failed') . ' ' . $exception->getMessage());
|
|
}
|
|
|
|
return Response::redirect($redirectTo);
|
|
}
|
|
|
|
private function resolveRedirect(Request $request): string
|
|
{
|
|
return RedirectPathResolver::resolve(
|
|
(string) $request->input('return_to', '/settings/integrations/hostedsms'),
|
|
['/settings/integrations'],
|
|
'/settings/integrations/hostedsms'
|
|
);
|
|
}
|
|
|
|
private function validatePhone(string $value): string
|
|
{
|
|
$phone = preg_replace('/[\s+\-()]/', '', trim($value)) ?? '';
|
|
if (preg_match('/^\d{8,15}$/', $phone) !== 1) {
|
|
throw new IntegrationConfigException('Podaj numer telefonu w formacie miedzynarodowym, np. 48xxxxxxxxx.');
|
|
}
|
|
|
|
return $phone;
|
|
}
|
|
|
|
private function validateMessage(string $value): string
|
|
{
|
|
$message = trim($value);
|
|
if ($message === '') {
|
|
throw new IntegrationConfigException('Podaj tresc testowego SMS.');
|
|
}
|
|
if (strlen($message) > 4000) {
|
|
throw new IntegrationConfigException('Tresc SMS nie moze przekraczac 4000 znakow.');
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
}
|