caterium-app/public/api/support.php
pavlov346346-source c3cb44419b
Add human support form with fixed email recipient (#40)
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.
2026-09-21 07:12:26 +03:00

44 lines
3.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);
}