feat: publish open alpha policies
This commit is contained in:
@@ -15,6 +15,6 @@ const path = Astro.url.pathname;
|
||||
<p class="live-badge"><span></span> Свежие данные и честная оценка</p>
|
||||
</header>
|
||||
<main id="main-content" tabindex="-1"><slot /></main>
|
||||
<footer><a href="/" class="brand"><span class="brand-mark"><FishingIcon name="hook" size={24}/></span><span class="brand-name"><span>Ни хвоста,</span><strong>ни чешуи</strong></span></a><p>Неофициальный проект для игроков Russian Fishing 4. Данные seed демонстрационные.</p><span>НХНЧ · 2026</span></footer>
|
||||
<footer><a href="/" class="brand"><span class="brand-mark"><FishingIcon name="hook" size={24}/></span><span class="brand-name"><span>Ни хвоста,</span><strong>ни чешуи</strong></span></a><p>Неофициальный проект для игроков Russian Fishing 4. <a href="/rules">Правила</a> · <a href="/privacy">Конфиденциальность</a></p><span>НХНЧ · 2026</span></footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
<Layout title="Конфиденциальность — RF4 Spotter">
|
||||
<article class="legal-page"><span class="eyebrow">Открытая альфа · 7 сентября 2026</span><h1>Конфиденциальность</h1>
|
||||
<p>RF4 Spotter собирает только сведения, необходимые для публикации и проверки рыболовных наблюдений, защиты от злоупотреблений и работы сервиса.</p>
|
||||
<h2>Что обрабатывается</h2><ul><li>рыба, водоём, координаты, вес и необязательные сведения об улове;</li><li>необязательные ник и скриншот;</li><li>HMAC-отпечаток адреса для ограничения частоты — исходный IP не хранится;</li><li>источник, внешний идентификатор и URL импортированных наблюдений.</li></ul>
|
||||
<h2>Защита и сроки</h2><p>Все заявки проходят модерацию, из изображений удаляются метаданные, база и объектное хранилище не публикуются напрямую. Непроверенные заявки хранятся до 30 дней, персональные поля одобренных — до 180 дней, непубликуемый staging — до 90 дней.</p>
|
||||
<h2>Удаление и жалобы</h2><p>Для удаления своей заявки напишите на <a href="mailto:privacy@rf4spotter.ru">privacy@rf4spotter.ru</a>, приложив её идентификатор и сведения для поиска. О злоупотреблении сообщайте на <a href="mailto:abuse@rf4spotter.ru">abuse@rf4spotter.ru</a>.</p>
|
||||
<h2>Сторонние источники</h2><p>Лицензия проекта не передаёт права на игровые материалы, сторонние данные или пользовательские изображения. Community-данные сначала попадают в staging и проверяются вручную.</p>
|
||||
</article>
|
||||
</Layout>
|
||||
@@ -15,7 +15,7 @@ const reportId = Astro.url.searchParams.get("report_id");
|
||||
{state === "screenshot_error" && reportId && <form class="screenshot-retry" method="post" action="/api/report-screenshot" enctype="multipart/form-data"><input type="hidden" name="report_id" value={reportId} /><label>Повторная загрузка скриншота<input name="screenshot" type="file" accept="image/jpeg,image/png,image/webp" required /></label><button type="submit">Загрузить скриншот</button></form>}
|
||||
{unavailable ? <div class="state"><h2>Форма временно недоступна</h2></div> : <form class="report-form" method="post" action="/api/report" enctype="multipart/form-data">
|
||||
<fieldset><legend>Главное <span>обязательно</span></legend><div class="form-grid"><label>Рыба *<select name="fish_slug" required>{fishes.map(x => <option value={x.slug}>{x.name_ru}</option>)}</select></label><label>Водоём *<select name="waterbody_slug" required>{waterbodies.map(x => <option value={x.slug}>{x.name_ru}</option>)}</select></label><label>Координата X *<input name="x" type="number" min="-10000" max="10000" placeholder="Например, 72" required /></label><label>Координата Y *<input name="y" type="number" min="-10000" max="10000" placeholder="Например, 84" required /></label><label>Вес, граммы *<input name="weight_g" type="number" min="1" max="3000000" placeholder="1250" required /></label><label>Приманка<input name="bait_name" maxlength="200" placeholder="Название в игре" /></label></div><p class="field-help">Координаты — два целых числа с карты. Вес: 1,25 кг = 1250 г.</p></fieldset>
|
||||
<details class="optional-fields"><summary>Дополнительные сведения <span>необязательно</span></summary><div class="form-grid"><label>Способ ловли<select name="fishing_method"><option value="">Не указан</option><option value="spinning">Спиннинг</option><option value="bottom">Донная</option><option value="float">Поплавочная</option></select></label><label>Проводка<input name="retrieve_method" maxlength="100" /></label><label>Скорость проводки<input name="retrieve_speed" type="number" min="0" max="100" /></label><label>Ник игрока<input name="player_name" maxlength="100" /></label></div><label class="wide">Комментарий<textarea name="comment" maxlength="1000" rows="4"></textarea></label><label class="wide">Скриншот, JPEG/PNG/WebP до 8 МБ<input name="screenshot" type="file" accept="image/jpeg,image/png,image/webp" /></label></details><label class="honeypot" aria-hidden="true">Сайт<input name="website" tabindex="-1" autocomplete="off" /></label><p class="privacy">Ник и скриншот необязательны. Из изображения удаляются EXIF и прочие метаданные.</p><button type="submit">Отправить на проверку</button>
|
||||
<details class="optional-fields"><summary>Дополнительные сведения <span>необязательно</span></summary><div class="form-grid"><label>Способ ловли<select name="fishing_method"><option value="">Не указан</option><option value="spinning">Спиннинг</option><option value="bottom">Донная</option><option value="float">Поплавочная</option></select></label><label>Проводка<input name="retrieve_method" maxlength="100" /></label><label>Скорость проводки<input name="retrieve_speed" type="number" min="0" max="100" /></label><label>Ник игрока<input name="player_name" maxlength="100" /></label></div><label class="wide">Комментарий<textarea name="comment" maxlength="1000" rows="4"></textarea></label><label class="wide">Скриншот, JPEG/PNG/WebP до 8 МБ<input name="screenshot" type="file" accept="image/jpeg,image/png,image/webp" /></label></details><label class="honeypot" aria-hidden="true">Сайт<input name="website" tabindex="-1" autocomplete="off" /></label><p class="privacy">Ник и скриншот необязательны. Из изображения удаляются EXIF и прочие метаданные.</p><label class="consent"><input name="consent" type="checkbox" required /> <span>Я отправляю собственное наблюдение и принимаю <a href="/rules" target="_blank">правила</a> и <a href="/privacy" target="_blank">политику конфиденциальности</a>.</span></label><button type="submit">Отправить на проверку</button>
|
||||
</form>}
|
||||
<script is:inline define:vars={{ state }}>
|
||||
const form = document.querySelector(".report-form"); const key = "rf4-report-draft";
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
<Layout title="Правила открытой альфы — RF4 Spotter">
|
||||
<article class="legal-page"><span class="eyebrow">Открытая альфа · 7 сентября 2026</span><h1>Правила участия</h1>
|
||||
<p>RF4 Spotter — неофициальный экспериментальный сервис сообщества. Данные могут быть неполными или устаревшими и не гарантируют результат в игре.</p>
|
||||
<h2>Можно</h2><ul><li>отправлять собственные достоверные наблюдения из Russian Fishing 4;</li><li>указывать необязательный ник и прикладывать свой скриншот;</li><li>сообщать об ошибках, устаревших точках и злоупотреблениях.</li></ul>
|
||||
<h2>Нельзя</h2><ul><li>выдавать чужие материалы за свои или публиковать персональные данные;</li><li>размещать рекламу, оскорбления или вредоносный контент;</li><li>автоматически отправлять заявки и обходить ограничения частоты;</li><li>использовать сервис для нарушения правил RF4.</li></ul>
|
||||
<h2>Модерация</h2><p>Любая заявка может быть отклонена или удалена. До одобрения она не влияет на статистику. При злоупотреблении доступ может быть ограничен.</p>
|
||||
<h2>Контакты</h2><p>Удаление и данные: <a href="mailto:privacy@rf4spotter.ru">privacy@rf4spotter.ru</a>. Жалобы: <a href="mailto:abuse@rf4spotter.ru">abuse@rf4spotter.ru</a>.</p>
|
||||
</article>
|
||||
</Layout>
|
||||
@@ -27,3 +27,4 @@ footer{min-height:118px;background:var(--deep);color:#dbe4df;padding:28px max(32
|
||||
@media(max-width:720px){.record-row:not(.record-head)>*{display:grid;grid-template-columns:88px 1fr;text-align:left!important;gap:8px}.record-row:not(.record-head)>*:before{content:attr(data-label);font:700 9px Inter,sans-serif;text-transform:uppercase;letter-spacing:.08em;color:#82908e}.record-row{grid-template-columns:1fr}.record-filters>a{height:auto;padding:8px}}
|
||||
/* WCAG AA secondary text on light surfaces */
|
||||
.eyebrow,.overline,.result-count,.spot-rank,.spot-topline,.spot-meta,.bait-line div span,.spot-stats span,.data-note,.state,.principles p,.principles article>span,.official-note,.privacy,.field-help,.report-form legend span,.optional-fields summary span,.record-row>span,.record-row>time{color:#4d605d}
|
||||
.consent{display:flex!important;align-items:flex-start;gap:10px;margin:20px 0;text-transform:none!important;letter-spacing:0!important;font-size:14px!important;line-height:1.45}.consent input{width:20px;height:20px;margin:0;flex:none}.legal-page{width:min(760px,calc(100% - 32px));margin:60px auto 100px}.legal-page h1{font:400 clamp(48px,7vw,82px)/.95 Georgia,serif;letter-spacing:-.05em}.legal-page h2{margin-top:36px;font:400 28px Georgia,serif}.legal-page p,.legal-page li{color:#405552;line-height:1.7}.legal-page a{text-underline-offset:3px}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test } from "@playwright/test";
|
||||
|
||||
for (const path of ["/", "/records", "/report"]) {
|
||||
for (const path of ["/", "/records", "/report", "/rules", "/privacy"]) {
|
||||
test(`${path} has no serious accessibility violations`, async ({ page }) => {
|
||||
await page.goto(path);
|
||||
const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze();
|
||||
|
||||
@@ -11,6 +11,7 @@ test("production bootstrap supports submission and moderation", async ({ page, r
|
||||
await page.getByLabel("Координата X *").fill(String(x));
|
||||
await page.getByLabel("Координата Y *").fill(String(y));
|
||||
await page.getByLabel("Вес, граммы *").fill("4321");
|
||||
await page.getByLabel(/Я отправляю собственное наблюдение/).check();
|
||||
await page.getByLabel("Ник игрока").fill(player);
|
||||
await page.getByRole("button", { name: "Отправить на проверку" }).click();
|
||||
await expect(page.getByText("Улов отправлен на модерацию. Спасибо!")).toBeVisible();
|
||||
|
||||
@@ -25,6 +25,7 @@ test("submitted catch appears publicly only after moderation", async ({ page })
|
||||
await page.getByLabel("Координата Y *").fill(String(y));
|
||||
await page.getByLabel("Вес, граммы *").fill("6789");
|
||||
await page.getByLabel("Ник игрока").fill(player);
|
||||
await page.getByLabel(/Я отправляю собственное наблюдение/).check();
|
||||
await page.getByRole("button", { name: "Отправить на проверку" }).click();
|
||||
await expect(page.getByText("Улов отправлен на модерацию. Спасибо!")).toBeVisible();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user