From 5e72b23161d3b6258c457e4153a5c52c97ae67de Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Mon, 21 Sep 2026 11:04:06 +0300 Subject: [PATCH] Correct support mailbox to support@caterium.ru (#42) Apply the user's explicit address correction to the PHP recipient, Help links, contact form and tests. Refresh the form URL and PWA cache. Exact-recipient PHP tests and support/Help browser tests passed in isolated run 35575721587. No real mail sent, delivery not claimed. Keep standard main QA and production publication gates unchanged; no auth, database or unrelated feature changes. --- docs/releases/2026-09-21-SUPPORT-FORM.md | 2 +- public/api/support-lib.php | 2 +- public/core/help-center.js | 4 ++-- public/core/support-form.js | 2 +- public/service-worker.js | 4 ++-- tests/production-support-form.mjs | 6 +++--- tests/support-form.spec.mjs | 2 +- tests/support-mail.php | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/releases/2026-09-21-SUPPORT-FORM.md b/docs/releases/2026-09-21-SUPPORT-FORM.md index cd55687..aa630fe 100644 --- a/docs/releases/2026-09-21-SUPPORT-FORM.md +++ b/docs/releases/2026-09-21-SUPPORT-FORM.md @@ -1,6 +1,6 @@ # Contact support from Help -Recipient is exactly `support@katerion.ru` (explicit user instruction; do not silently rewrite to caterium.ru). The Help dialog offers a human support tab and an unanswered-question call to action. It also works without login. The AI assistant stays separate. +Recipient is exactly `support@caterium.ru` (address explicitly corrected by the user on 2026-09-21). The Help dialog offers a human support tab and an unanswered-question call to action. It also works without login. The AI assistant stays separate. Submission uses same-origin `/api/support.php`, not mailto. Required: name, reply email, topic, subject and message. Diagnostics are opt-in and contain only browser, viewport, current section and release strings. No SDK, session, token, order, customer record or URL query/hash is serialized. Drafts remain in memory on errors/close and are cleared on account/workspace changes. Only confirmed `202 accepted` clears the message. No autoresponder, attachments, arbitrary recipient or database access. diff --git a/public/api/support-lib.php b/public/api/support-lib.php index 0d2f57b..c62ca27 100644 --- a/public/api/support-lib.php +++ b/public/api/support-lib.php @@ -2,7 +2,7 @@ /** Fixed-recipient contact form. No customer database access and no mail relay. */ declare(strict_types=1); namespace Caterium\Support; -const RECIPIENT = 'support@katerion.ru'; +const RECIPIENT = 'support@caterium.ru'; const SENDER = 'no-reply@caterium.ru'; const TOPICS = ['question'=>'Вопрос по приложению','problem'=>'Ошибка или проблема','access'=>'Вход и подписка','suggestion'=>'Предложение','other'=>'Другое']; final class Problem extends \RuntimeException { diff --git a/public/core/help-center.js b/public/core/help-center.js index b721512..a5d4123 100644 --- a/public/core/help-center.js +++ b/public/core/help-center.js @@ -6,7 +6,7 @@ const norm=s=>String(s).toLowerCase().replace(/ё/g,'е'); const BOT={origin:"https://ai-staff-alpha.vercel.app",agent:"e9cc0f28-aaa5-48d4-a020-ae9633988faf"}; let dialog,data,loading=false,returnFocus; - const supportUrl=new URL('support-form.js?v=20260921-support-mail',document.currentScript.src); + const supportUrl=new URL('support-form.js?v=20260921-support-recipient',document.currentScript.src); function loadContactForm(){ if(window.CateriumSupportForm){window.CateriumSupportForm.attach(dialog);return;} if(document.getElementById('ctSupportFormScript'))return; @@ -51,7 +51,7 @@ function ensureDialog(){ if(dialog)return; dialog=document.createElement('dialog');dialog.id='ctHelpDialog';dialog.setAttribute('aria-labelledby','ctHelpTitle'); - dialog.innerHTML=`

Помощь в Caterium

Руководство пользователя и поддержка

`; + dialog.innerHTML=`

Помощь в Caterium

Руководство пользователя и поддержка

`; document.body.appendChild(dialog); dialog.querySelector('#ctHelpClose').onclick=()=>dialog.close(); dialog.addEventListener('keydown',e=>{if(e.key==='Escape'){e.preventDefault();e.stopPropagation();dialog.close();}}); diff --git a/public/core/support-form.js b/public/core/support-form.js index 21094ac..61bf169 100644 --- a/public/core/support-form.js +++ b/public/core/support-form.js @@ -2,7 +2,7 @@ (()=>{ 'use strict'; if(window.CateriumSupportForm)return; - const EMAIL='support@katerion.ru',ENDPOINT=new URL('../api/support.php',document.currentScript.src).href; + const EMAIL='support@caterium.ru',ENDPOINT=new URL('../api/support.php',document.currentScript.src).href; const identity=()=>`${window.SunCloudV2?.getSession?.()?.user?.id||''}:${window.SunCloudV2?.getWorkspace?.()?.id||''}`; let root,form,tab,csrf='',busy=false,controller=null,scope=identity(),generation=0,lastPayload='',requestId=''; const $=id=>root?.querySelector('#'+id); diff --git a/public/service-worker.js b/public/service-worker.js index 20028c4..619bf0b 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,7 +1,7 @@ -const CACHE='sun-catering-pwa-v110-20260918-ui-stability-20260919-client-menu-support-bot-training-catalog-banquet-onepage-employee-session-mfa-recovery-promo-entry-support-mail'; +const CACHE='sun-catering-pwa-v110-20260918-ui-stability-20260919-client-menu-support-bot-training-catalog-banquet-onepage-employee-session-mfa-recovery-promo-entry-support-mail-correct-recipient'; const VERSION='20260918-ui-stability'; const CORE=[ - './core/support-form.js?v=20260921-support-mail', + './core/support-form.js?v=20260921-support-recipient', './core/trial-promo-developer-v181.js?v=20260921-promo-entry', './core/banquet-client-menu.js?v=20260920-onepage', './core/training-catalog.js?v=20260920-training', diff --git a/tests/production-support-form.mjs b/tests/production-support-form.mjs index ef228a6..854bca1 100644 --- a/tests/production-support-form.mjs +++ b/tests/production-support-form.mjs @@ -15,10 +15,10 @@ try{ await page.goto(base.href,{waitUntil:'domcontentloaded'}); await page.getByRole('button',{name:'Помощь со входом',exact:true}).click(); await page.locator('#ctContactTab').click();await expect(page.locator('#ctContactForm')).toBeVisible(); - await expect(page.locator('#ctContactSection')).toContainText('support@katerion.ru'); + await expect(page.locator('#ctContactSection')).toContainText('support@caterium.ru'); assert(await page.locator('#ctContactForm').evaluate(el=>el.scrollWidth<=el.clientWidth)); await page.screenshot({path:`${output}/support-form-${width}.png`,animations:'disabled'}); - results.push({width,form:true,recipient:'support@katerion.ru',draftOnly:true}); + results.push({width,form:true,recipient:'support@caterium.ru',draftOnly:true}); }finally{await context.close();} } const context=await browser.newContext(); @@ -31,7 +31,7 @@ try{ // cross-origin request cannot reach mail(). Inbox delivery is a separate check. const response=await context.request.get(new URL('api/support.php',base).href); assert.equal(response.status(),200);assert.match(response.headers()['cache-control'],/no-store/); - const data=await response.json();assert.equal(data.recipient,'support@katerion.ru');assert.match(data.csrf,/^[a-f0-9]{64}$/); + const data=await response.json();assert.equal(data.recipient,'support@caterium.ru');assert.match(data.csrf,/^[a-f0-9]{64}$/); const rejected=await context.request.post(new URL('api/support.php',base).href,{headers:{Origin:'https://example.invalid'},data:{}});assert.equal(rejected.status(),403); await fs.writeFile(`${output}/support-form.json`,JSON.stringify({checkedAt:new Date().toISOString(),results,phpSessionEndpoint:true,crossOriginBlocked:true,realEmailSent:false,inboxDeliveryVerified:false},null,2)); }finally{await context.close();} diff --git a/tests/support-form.spec.mjs b/tests/support-form.spec.mjs index 74d3a5f..9394491 100644 --- a/tests/support-form.spec.mjs +++ b/tests/support-form.spec.mjs @@ -1,7 +1,7 @@ import {test,expect} from '@playwright/test'; import fs from 'node:fs'; import {spawnSync} from 'node:child_process'; -const EMAIL='support@katerion.ru'; +const EMAIL='support@caterium.ru'; async function fixture(page){ await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:'
'})); await page.goto('/index.html');await page.addStyleTag({url:'/core/help-center.css'});await page.addScriptTag({url:'/core/help-center.js'}); diff --git a/tests/support-mail.php b/tests/support-mail.php index 5e376e1..145249f 100644 --- a/tests/support-mail.php +++ b/tests/support-mail.php @@ -16,7 +16,7 @@ try{ $answer=submit($row,'127.0.0.1',$dir,$send,1000000);check($answer['status']==='accepted','Mail accepted'); $repeat=submit($row,'127.0.0.2',$dir,$send,1000001);check($repeat===$answer&&$count===1,'Retries must not send duplicates'); $changed=$row;$changed['message']='Changed';denies(static function()use($changed,$dir,$send){submit($changed,'127.0.0.1',$dir,$send,1000002);},409); - [$to,$subject,$body,$headers,$params]=$GLOBALS['capturedMail'];check($to==='support@katerion.ru','Recipient exactly matches user request');check($headers['Reply-To']===$input['email'],'Reply to sender');check($params==='-fno-reply@caterium.ru','Fixed envelope'); + [$to,$subject,$body,$headers,$params]=$GLOBALS['capturedMail'];check($to==='support@caterium.ru','Recipient exactly matches user request');check($headers['Reply-To']===$input['email'],'Reply to sender');check($params==='-fno-reply@caterium.ru','Fixed envelope'); check(strpos(base64_decode($body),$input['message'])!==false,'UTF-8 message is intact');check(strpos($subject,'SUP-')!==false,'Request ID in subject'); $saved=file_get_contents($dir.'/limits.json');check(strpos($saved,$input['email'])===false&&strpos($saved,$input['message'])===false,'Do not store message/email in rate-limit file'); for($i=2;$i<=3;$i++){$next=$row;$next['request_id']=sprintf('10000000-0000-4000-8000-%012d',$i);submit($next,'127.0.0.1',$dir,$send,1000000+$i);}