Add Help contact form and PHP mail endpoint for support@katerion.ru, with validated Reply-To, explicit diagnostics consent, CSRF/origin checks, hashed rate limits and duplicate protection. Preserve drafts on error and avoid serializing customer data or SDK internals. Full PR QA passed in 35559317001; isolated PHP and 30 browser cases passed in 35559179372. Standard production gates unchanged. Publication checks do not send real mail; inbox receipt remains unverified. No training photo assets or unfinished training lifecycle changes.
44 lines
3.7 KiB
PHP
44 lines
3.7 KiB
PHP
<?php
|
||
/** Same-origin public help endpoint; no credentials or customer data required. */
|
||
declare(strict_types=1);
|
||
require_once __DIR__.'/support-lib.php';
|
||
use Caterium\Support\Problem;
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
header('Cache-Control: private, no-store');
|
||
header('X-Content-Type-Options: nosniff');
|
||
header('Referrer-Policy: no-referrer');
|
||
try {
|
||
if (strtolower(explode(':',$_SERVER['HTTP_HOST']??'')[0])!=='app.caterium.ru') throw new Problem(404,'Not found');
|
||
$method=$_SERVER['REQUEST_METHOD']??'';
|
||
if (!in_array($method,['GET','POST'],true)) {header('Allow: GET, POST');throw new Problem(405,'Метод не поддерживается.');}
|
||
$origin=$_SERVER['HTTP_ORIGIN']??'';
|
||
if (($method==='POST' && $origin!=='https://app.caterium.ru') || ($origin!=='' && $origin!=='https://app.caterium.ru') || ($_SERVER['HTTP_SEC_FETCH_SITE']??'same-origin')==='cross-site') throw new Problem(403,'Откройте форму в приложении Caterium.');
|
||
if (!function_exists('mail')) throw new Problem(503,'Отправка почты временно недоступна. Напишите на '.Caterium\Support\RECIPIENT.'.');
|
||
$dir=Caterium\Support\directory();
|
||
session_name('ct_support');session_set_cookie_params(['lifetime'=>0,'path'=>'/api/','secure'=>true,'httponly'=>true,'samesite'=>'Strict']);
|
||
ini_set('session.use_strict_mode','1');
|
||
if (!session_start()) throw new Problem(503,'Не удалось подготовить форму.');
|
||
if ($method==='GET') {
|
||
if (!isset($_SESSION['csrf']) || ($_SESSION['issued']??0)<time()-7200) {$_SESSION['csrf']=bin2hex(random_bytes(32));$_SESSION['issued']=time();}
|
||
$out=['csrf'=>$_SESSION['csrf'],'recipient'=>Caterium\Support\RECIPIENT,'max_message_chars'=>4000];session_write_close();
|
||
echo json_encode($out,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR);exit;
|
||
}
|
||
if (strtolower(trim(explode(';',$_SERVER['CONTENT_TYPE']??'')[0]))!=='application/json') throw new Problem(415,'Ожидается форма JSON.');
|
||
if ((int)($_SERVER['CONTENT_LENGTH']??0)>24000) throw new Problem(413,'Сообщение слишком длинное.');
|
||
$raw=file_get_contents('php://input',false,null,0,24001);
|
||
if ($raw===false || strlen($raw)>24000) throw new Problem(413,'Сообщение слишком длинное.');
|
||
try {$input=json_decode($raw,true,8,JSON_THROW_ON_ERROR);}catch(\JsonException $e){throw new Problem(400,'Не удалось прочитать форму.');}
|
||
if (!is_array($input) || !is_string($input['csrf']??null) || !isset($_SESSION['csrf']) || ($_SESSION['issued']??0)<time()-7200 || !hash_equals($_SESSION['csrf'],$input['csrf'])) throw new Problem(403,'Форма устарела. Откройте её заново; текст не нужно удалять.');
|
||
session_write_close();
|
||
$row=Caterium\Support\validate($input);
|
||
$out=Caterium\Support\submit($row,$_SERVER['REMOTE_ADDR']??'unknown',$dir,'Caterium\\Support\\deliver');
|
||
http_response_code(202);echo json_encode($out,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR);
|
||
} catch (Problem $e) {
|
||
if(session_status()===PHP_SESSION_ACTIVE)session_write_close();
|
||
http_response_code($e->status);if($e->status===429)header('Retry-After: 3600');
|
||
echo json_encode(['ok'=>false,'message'=>$e->getMessage()],JSON_UNESCAPED_UNICODE);
|
||
} catch (\Throwable $e) {
|
||
if(session_status()===PHP_SESSION_ACTIVE)session_write_close();
|
||
http_response_code(503);echo json_encode(['ok'=>false,'message'=>'Не удалось подтвердить отправку. Сохраните текст и напишите на '.Caterium\Support\RECIPIENT.'.'],JSON_UNESCAPED_UNICODE);
|
||
}
|