Skip to content

Latest commit

 

History

115 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Пошук па экстрэмісцкіх спісах Беларусі

Статычны сайт (React + Vite) з хуткім пошукам адразу па трох афіцыйных спісах — Рэспубліканскім спісе экстрэмісцкіх матэрыялаў (суды), пераліку экстрэмісцкіх фарміраванняў (МУС/КДБ) і пераліку фізічных асоб, прычастных да экстрэмісцкай дзейнасці (МУС), — і па чацвёртай, неафіцыйнай базе: беларусы ў вышуку МУС РФ паводле штодзённай выбаркі Медыязоны-Беларусь. Выдача змяшаная; запісы першага спісу маюць чырвоную палоску і плашку «Матэрыял · <арт>», другога — фіялетавую палоску і плашку «Фарміраванне · МУС/КДБ», трэцяга — бірузовую палоску і плашку «Асоба · ...», чацвёртага — янтарную палоску і плашку «Вышук РФ · <ведамства>».

Архітэктура

data/materials.json        база — крыніца праўды (тып, назва, суд, дата, №, дададзена/выдалена)
data/meta.json             дата абнаўлення, крыніца, колькасць
data/formations.json       другі спіс: экстрэмісцкія фарміраванні МУС/КДБ (назва, спасылкі, падстава, даты, даведка)
data/formations-meta.json  дата праверкі і стан крыніцы другога спісу (асобна, каб не чапаць meta.json)
data/persons.json          трэці спіс: фізічныя асобы з пераліку МУС (імя, транслітарацыя, грамадзянства, дата нараджэння, прысуд, даты, даведка, нумар .doc-часткі)
data/persons-meta.json     дата праверкі, стан крыніцы і спіс .doc-частак трэцяга спісу
data/wanted.json           чацвёрты спіс: беларусы ў базе вышуку МУС РФ паводле Медыязоны (імя, год нараджэння, нацыянальнасць, рэгіён і ведамства-ініцыятар, даты вышуку, іншыя напісанні, катэгорыя)
data/wanted-meta.json      дата праверкі, стан крыніцы і дата файла Медыязоны (sourceDate) чацвёртага спіса
data/notified.json         id запісаў, ужо дасланых у Telegram — абарона ад паўторных дайджэстаў
scripts/update.mjs         спампоўвае афіцыйны спіс → разбор табліцы → даўнясенне новых запісаў у базу
scripts/parse.mjs          разбор табліцы .doc → запісы (тып, назва, суд, дата, стабільны id)
scripts/update-formations.mjs  другі спіс: старонка МУС (пререндэр ?_escaped_fragment_=) → .xlsx → даўнясенне ў data/formations.json
scripts/parse-formations.mjs   разбор радкоў xlsx → запісы (id = назва + першае рашэнне, устойлівы да дапісаных рашэнняў)
scripts/update-persons.mjs     трэці спіс: тая ж старонка МУС → усе .doc «Перечень граждан … Часть N» (з паўторамі спампоўкі) → data/persons.json
scripts/parse-persons.mjs      разбор табліцы .doc па «якары» (падстава + дата ўключэння): id = імя + дата нараджэння + першая дата ўключэння; пазнаванне праўак
scripts/update-wanted.mjs      чацвёрты спіс: JSON Медыязоны (brotli) → data/wanted.json; калі файл пераехаў — пошук адраса праз старонку і бандл віджэта
scripts/parse-wanted.mjs       разбор JSON (id = імя + год нараджэння), зліццё mergeWanted: выключаныя ў крыніцы → removed без даты; пазнаванне праўак; пошук адраса файла
scripts/xlsx.mjs           мінімальны чытач .xlsx без залежнасцяў (ZIP праз node:zlib + XML рэгулярнымі выразамі)
scripts/common.mjs         агульнае для абнаўляльнікаў і зборкі: шляхі, fetch/спампоўка з таймаўтамі і праверкай паўнаты файла, readJson, запіс sourceError у мету, runMain
scripts/merge.mjs          агульнае зліццё mergeList для чатырох абнаўляльнікаў + пазнаванне праўак: «зніклы» + «новы» з тым жа судом і амаль той жа назвай — адзін запіс
scripts/alert.mjs          службовыя паведамленні адміну ў Telegram: збой джоба, змена стану крыніцы
scripts/alert-logic.mjs    чыстае параўнанне стану крыніц (для alert.mjs)
scripts/notify.mjs         дайджэст новых запісаў у Telegram-канал (калі зададзеныя сакрэты)
scripts/digest.mjs         чыстае фарматаванне Telegram-дайджэсту (для notify.mjs)
scripts/build-index.mjs    усе чатыры базы → public/data/{index.json, chunks/N.json, meta.json}: адзін індэкс з пазнакай спіса (генеруецца пры зборцы, у git не трапляе)
scripts/index-rows.mjs     радкі індэкса, фрагменты, крос-спасылкі пералік МУС ↔ вышук РФ (linkLists) і RSS (для build-index.mjs)
scripts/geo.mjs            GEO: robots.txt, llms.txt, llms-full.txt, sitemap.xml, opensearch.xml, 404.html, security.txt, статычныя FAQ-старонкі
scripts/geo-plugin.mjs     Vite-плагін: JSON-LD, meta з жывымі лічбамі, кантэнт для краўлераў без JS, CSP у <meta>
scripts/csp.mjs            CSP: палітыка і sha256-хэшы інлайн-скрыптоў і стыляў, палічаныя з гатовага HTML
src/lib/                   чыстая логіка, агульная для Node і браўзера
  normalize.js             нармалізацыя (захоўвае даўжыню → дакладная падсветка), парсер запыту, дыяпазоны супадзенняў
  identity.js              замарожаная нармалізацыя для id запісаў (не мяняць: ад яе залежаць спасылкі і спісы назірання)
  court.js                 назва суда, дата рашэння
  person.js                трэці спіс: суд і артыкулы КК з прысуду, група артыкулаў для статыстыкі
  wanted.js                чацвёрты спіс: ведамства-ініцыятар → серыя статыстыкі, даты вышуку («да …»), імя ВЯЛІКІМІ → звычайны рэгістр, removed без даты
  search.js                фільтр + сартаванне
  results.js               канвеер пошуку (runSearch): дакладна → транслітарацыя → прыблізныя словы; падлік па спісах
  translit.js              кірыліца ↔ лацінка (руская транслітарацыя і беларуская лацінка), і ↔ и
  fuzzy.js                 прыблізны пошук (Левенштэйн па словах корпуса), калі дакладных супадзенняў няма
  watch.js                 праверка спісу назірання па індэксе
  whatsnew.js              логіка старонкі «Новае»: дададзеныя/зніклыя за 30 дзён, групоўка па даце, фолбэк без гісторыі
  stats.js                 агрэгацыя запісаў для таймлайна статыстыкі
  format.js                фарматаванне дат, акно «Новага» (30 дзён), парог састарэласці (36 гадзін)
  api.js                   загрузка індэкса і ленівых фрагментаў (з кэшам)
  entry.js                 запыт з уваходнай спасылкі (?q= / #q=) → history.state, адрас ачышчаецца
  i18n.js                  тэксты інтэрфейсу (be/en) і спасылкі
  notifications.js         браўзернае апавяшчэнне спісу назірання (праз service worker, дзе іначай нельга)
  wipe.js                  «Ачысціць усё»: localStorage, sessionStorage, кэшы, service worker
  faq.js                   факты пра сайт і FAQ (бе/en) — крыніца для старонак, llms.txt і Schema.org
src/hooks/                 useIndex (з «праверыць зноў»), useRecord (ленівы фрагмент), useQuery (запыт у history.state, не ў адрасе), useHashRoute (#/new, #/stats, #/r/<id>), useWatchlist, useOnline, useLocalStorage, useLang
src/components/            Header, Nav, SearchBar, Options, Facets, WatchPanel, ResultList, ResultItem, Highlight, WhatsNew, RecordPage, Consequences, StatsPage, Timeline, HelpDialog, LangToggle, ThemeToggle, ErrorBoundary, icons (агульныя SVG)
public/sw.js               service worker: афлайн-рэжым (абалонка + база ў кэшы)
public/feed.xml            RSS новых запісаў (генеруецца пры зборцы, у git не трапляе)
test/                      Vitest
.github/workflows/         абнаўленне базы двойчы на дзень + зборка і дэплой на GitHub Pages
.github/dependabot.yml     абнаўленні дзеянняў і npm

Другі спіс: экстрэмісцкія фарміраванні (МУС/КДБ)

Пералік «организаций, формирований, ИП, причастных к экстремистской деятельности» МУС публікуе .xlsx-файлам у навіне на сваім сайце (старонка — SPA, пререндэраны HTML аддаецца з ?_escaped_fragment_=). npm run update:formations знаходзіць файл, разбірае яго без залежнасцяў (scripts/xlsx.mjs) і даўносіць новыя запісы ў data/formations.json; у CI гэта асобны крок, які ідзе раз на суткі (ранішні запуск) і не спыняе абнаўленне матэрыялаў пры збоі. Пры зборцы ўсе чатыры базы трапляюць у адзін індэкс з пазнакай спіса, таму пошук, спіс назірання, «Новае», RSS, Telegram-дайджэст і пастаянныя спасылкі працуюць па ўсіх спісах без асобнай логікі.

  • У выдачы запісы пераліку пазначаныя фіялетавай палоскай і плашкай «Фарміраванне · МУС/КДБ» (для «экстрэмісцкіх арганізацый», ліквідаваных судом, — «Арганізацыя · суд»); укладкі-спісы пад пошукам («Усе · Матэрыялы · Фарміраванні · Асобы · Вышук РФ», з лічбамі знойдзенага ў кожным спісе) абмяжоўваюць выдачу адным спісам. Старонка запісу паказвае ўсе палі пераліку: кароткую назву, спасылкі, адрас, падставу, дату ўключэння, даведку і апісанне лагатыпа.
  • id запісу — ад назвы і першага рашэння («18.10.2021 № 1ЭК»): дапісаныя пазней рашэнні і праўкі даведкі не ствараюць «новы» запіс, а моўчкі абнаўляюць тэкст.
  • «Новае», RSS і дайджэст пры першым імпарце маўчаць (added: null); новымі лічацца толькі запісы, якія з’явіліся ў наступных абнаўленнях.
  • Статыстыка (#/stats) мае пераключальнік «Матэрыялы · Фарміраванні · Асобы · Вышук РФ»: для другога спісу той жа таймлайн, але серыі — хто прыняў рашэнне (МУС / КДБ / суд). У адзін графік спісы не змешваюцца (сотні матэрыялаў у месяц схавалі б адзінкі фарміраванняў); GEO-факты па гадах — толькі матэрыялы.
  • Засцярогі тыя ж: менш за 100 запісаў у файле, знікненне больш чым max(20, 5 %) запісаў (г.зн. больш за 20 і разам з тым больш за 5 % базы) ці больш за MAX_ADDED (100) новых за раз спыняюць абнаўленне; крыніца недаступная або спрацавала засцярога — sourceError у formations-meta.json, папярэджанне ў шапцы і адмін-алерт. Адрас старонкі можна перавызначыць зменнай FORMATIONS_SOURCE; лакальны файл — node scripts/update-formations.mjs шлях/да/файла.xlsx.
  • Спісы фізічных асоб (.doc на той жа старонцы) — трэці спіс, гл. ніжэй.

Трэці спіс: фізічныя асобы, прычастныя да экстрэмісцкай дзейнасці (МУС)

«Перечень граждан Республики Беларусь, иностранных граждан или лиц без гражданства, причастных к экстремистской деятельности» МУС публікуе на той жа старонцы некалькімі .doc-файламі («Часть 1…N»): старыя часткі не мяняюцца, апошняя дапаўняецца амаль штотыдня. npm run update:persons знаходзіць усе часткі па подпісе спасылкі, спампоўвае іх (з паўторамі — сервер МУС часам абрывае вялікія файлы; абарваны файл не разбіраецца і бярэцца зноў), разбірае табліцу і даўносіць запісы ў data/persons.json. Усё ці нічога: калі хоць адна частка не спампавалася або спрацавала засцярога, база не кранаецца, а ў persons-meta.json пішацца sourceError (папярэджанне ў шапцы, адмін-алерт). На ўсе спампоўкі разам — ліміт 18 хвілін (PERSONS_BUDGET_MS), каб крок ніколі не паваліў увесь джоб. У CI гэта асобны крок пры кожным запуску (двойчы на дзень), які не спыняе абнаўленне матэрыялаў і дэплой.

  • Разбор ідзе не па радках, а па «якары»: ячэйка падставы («Вступивший в законную силу приговор … статьи 342 УК») з датай побач (нараджэння перад ёй ці ўключэння за ёй). Так пераносяцца нераўнамерныя межы радкоў word-extractor, пустыя ячэйкі на разрывах старонак, лішняя ячэйка пасля імя ў свежай частцы, пустое грамадзянства і новыя запісы яшчэ без нумара. Выключаныя асобы (нумар з прочыркамі) прапускаюцца.
  • id запісу — ад імя, даты нараджэння і першай даты ўключэння: перанумарацыя частак, дапісаная другая дата ўключэння, змена даведкі ці адраса моўчкі абнаўляюць тэкст. Выпраўленая памылка друку ў імені ці даце нараджэння пазнаецца як праўка (pairPersonEdits), а не як новы чалавек.
  • У выдачы — бірузовая палоска і плашка «Асоба · ...» (паводле артыкулаў), імя, лацінская транслітарацыя, дата нараджэння («д.н.») з грамадзянствам (каб адрозніць цёзак), прысуд. Нумар МУС (калі ўжо прысвоены) і спасылка на .doc-частку пераліку — сярод палёў на старонцы запісу. Шукаецца імя кірыліцай і лацінкай, дата нараджэння, артыкулы КК і суд з прысуду, дата ўключэння; адрас (месцазнаходжанне) і грамадзянства ў індэкс не трапляюць — толькі ў фрагменце запісу (картка і старонка). Укладка «Асобы» пад пошукам абмяжоўвае выдачу гэтым спісам.
  • Даведка з пераліку («судзімасць не пагашана», «адбывае пакаранне») разбіраецца і застаецца ў data/persons.json (па ёй бачныя праўкі ў крыніцы), але на сайт не ідзе — ні ў фрагмент, ні ў картку, ні на старонку запісу: МУС не абнаўляе яе ва ўжо апублікаваных частках, таму ў старых запісах яна часта састарэлая.
  • Спасылка на першакрыніцу: assignParts (update-persons.mjs) піша ў кожны запіс, які цяпер ёсць у крыніцы, нумар .doc-часткі (part), а publicMeta аддае адрасы частак у meta.persons.files (па парадку частак; лакальны запуск з file:// — не аддае). Старонка запісу робіць з іх знешнюю спасылку «Частка N (.doc)» з папярэджаннем ExtLink. Для запісу, які знік з крыніцы, спасылкі няма — у файле яго ўжо не будзе.
  • Статыстыка (#/stats/p): таймлайн па даце ўключэння ў пералік, серыі — група артыкулаў КК у прысудзе (342/293 групавыя дзеянні; 130, 367–370 выказванні; 361-х экстрэмізм; іншыя), src/lib/person.js.
  • Дайджэст, RSS і «Новае» — як і для іншых спісаў: імя, артыкулы, суд і дата ўключэння; дата нараджэння і адрас у канал не ідуць. Пры першым імпарце маўчаць (added: null).
  • Засцярогі: менш за 1000 запісаў разам, менш за 50 у любой частцы, акрамя апошняй (новая «Часть N» першыя тыдні законна маленькая), раней бачаная частка паменшылася больш чым на 2 % (partCounts у меце), знікненне больш чым max(20, 5 %) запісаў ці больш за MAX_ADDED (300) новых за раз спыняюць абнаўленне. Парсер лінейны і абразае ячэйкі даўжэйшыя за 5 000 сімвалаў — чужы файл не можа падвесіць джоб; лішнія ячэйкі сапсаванага радка адкідаюцца і лічацца (parse у меце), а не становяцца чыімсьці імем. Адрас старонкі — PERSONS_SOURCE, агульны ліміт часу на спампоўкі — PERSONS_BUDGET_MS; лакальныя файлы — node scripts/update-persons.mjs частка1.doc частка2.doc ….

Чацвёрты спіс: беларусы ў базе вышуку РФ (паводле Медыязоны)

Сама база вышуку МУС РФ аддае толькі пошук па імені з капчай і не адказвае з-за мяжы (у тым ліку з раннераў GitHub), таму адзіная практычная крыніца — статычны JSON, які Медыязона-Беларусь збірае штодня для свайго віджэта на старонцы «База розыска РФ» (грамадзяне і ўраджэнцы Беларусі) і адкрыта прапануе спампаваць; сайт спасылаецца на яе ў падвале, на старонцы запісу і ў FAQ. npm run update:wanted спампоўвае файл (WANTED_SOURCE, brotli — Node распакоўвае сам, сырыя brotli/gzip таксама разбіраюцца), разбірае і даўносіць запісы ў data/wanted.json. Калі файл пераехаў (HTTP 403/404), адрас шукаецца нанова: старонка Медыязоны (WANTED_PAGE) → бандл віджэта → імя файла даных. Усё ці нічога: недаступная крыніца, змена фармату ці засцярога пакідаюць базу як была, а ў wanted-meta.json пішацца sourceError (папярэджанне ў шапцы, адмін-алерт). У CI гэта асобны крок пры кожным запуску (двойчы на дзень), які не спыняе астатняе абнаўленне і дэплой. Медыязона заяўляе штодзённае абнаўленне, але файл мяняецца нерэгулярна — таму ў тултыпе шапкі пад радком вышуку асобным радком паказваецца дата іх файла (sourceDate з Last-Modified); у бачны радок шапкі яна не трапляе.

  • Запіс крыніцы: імя вялікімі літарамі (руская), год нараджэння, нацыянальнасць, рэгіён і ведамства Беларусі, па чыім запыце абвешчаны вышук (МВД, КГК, КГБ, ДИН, ГПК; у ~10 % не пазначана), дата абвяшчэння ў вышук (у часткі запісаў прыблізная — «<2026-05», у нас before: каля 10 % жывых, разам з выключанымі — каля траціны), дата першага абвяшчэння для паўторна дададзеных, іншыя напісанні імя, рэдакцыйныя катэгорыі Медыязоны («Полк Калиновского», «в списке «террористов»») і статус: дададзены за 7 дзён ці выключаны з базы. Артыкула КК няма. Кожнае поле правяраецца на тып і абразаецца; запіс без імя кірыліцай ці з чужым годам прапускаецца, а калі такіх больш за 1 % — абнаўленне спыняецца.
  • id — ад імя і года нараджэння (нумароў у крыніцы няма): два розныя запісы вышуку аднаго чалавека (розныя ведамствы) зліваюцца ў адзін, застаецца апошні. Выпраўленае напісанне пры тым жа годзе ці выпраўлены год пры тым жа імені пазнаюцца як праўка (isWantedEdit), а не новы чалавек.
  • Выключаныя ў крыніцы застаюцца ў файле з пазнакай, але без даты выключэння. Жывыя запісы ідуць праз mergeList як звычайна: знік з жывых (выключылі ці прыбралі з файла) — removed сёння, вярнуўся — зноў жывы. Выключаныя, якіх у базе яшчэ не было (першы імпарт ці чалавек трапіў у базу і выбыў між нашымі праверкамі), дадаюцца адразу з removed: '?' (UNDATED у src/lib/wanted.js) і added: null: для лічыльнікаў і статыстыкі гэта выдалены запіс, а для «Новага», карткі і дайджэсту — не дата. Стандартныя засцярогі mergeList: знікла (у тым ліку выключана) больш за max(20, 5 %) — памылка заўсёды, дадалося больш за MAX_ADDED (500: пасля паўзы ў крыніцы прыходзяць сотні за раз) — без UPDATE_FORCE=1; менш за 5 000 запісаў у файле — узяты не той файл.
  • У выдачы — янтарная палоска і плашка «Вышук РФ · МУС/КДК/КДБ» (ведамства-ініцыятар), імя ў звычайным рэгістры (даўжыня радка не мяняецца, таму падсветка працуе), іншыя напісанні, год нараджэння («г.н.») з нацыянальнасцю, рэгіёнам і ведамствам; дата — абвяшчэння ў вышук, прыблізная — «да 05.2026»; выключаны — «выключаны з базы» без даты, калі яе няма. Нумароў у крыніцы няма. Шукаецца імя (і іншыя напісанні) кірыліцай і лацінкай, год нараджэння, ведамства («розыск», «МВД», «КГБ») і рэгіён; нацыянальнасць у індэкс не ідзе.
  • Крос-спасылкі з пералікам фізічных асоб МУС (linkLists у scripts/index-rows.mjs, пры зборцы індэкса): той жа чалавек — тое ж імя (ці іншае напісанне з базы вышуку) і той жа год нараджэння, толькі паміж жывымі запісамі. На картцы асобы — «↔ Таксама ў базе вышуку РФ», на картцы вышуку — «↔ Ёсць у пераліку фізічных асоб МУС» (поле also у фрагменце). Пры першым імпарце звязалася ~450 чалавек (каля 7 % пераліку МУС).
  • Статыстыка (#/stats/w): таймлайн па даце абвяшчэння ў вышук, серыі — ведамства-ініцыятар (МУС, КДК, КДБ, іншае/невядома); запісы з прыблізнай датай на графік не трапляюць, іх колькасць — асобным радком.
  • Дайджэст, RSS і «Новае» — як і для іншых спісаў: імя ў звычайным рэгістры, ведамства, рэгіён і дата вышуку; год нараджэння ў канал і стужку не ідзе. Пры першым імпарце маўчаць (added: null). Даты «дададзены за 7 дзён» крыніцы не выкарыстоўваюцца: «новае» — тое, што з’явілася ў нашай базе.
  • Тэксты: блок «Што гэта значыць для мяне» тлумачыць, што расійскі вышук абвяшчаецца па запыце беларускіх ведамстваў, што чалавека могуць затрымаць і выдаць у Расіі і краінах, якія з ёй супрацоўнічаюць, і што выключэнне з базы не спыняе крымінальны пераслед; FAQ, llms.txt і JSON-LD згадваюць чацвёрты спіс як неафіцыйны, з атрыбуцыяй Медыязоны. Зменныя асяроддзя — WANTED_SOURCE (адрас JSON), WANTED_PAGE (старонка для пошуку адраса), MAX_ADDED; лакальны файл — node scripts/update-wanted.mjs файл.json.

Функцыі для тых, хто правярае сябе

  • Укладкі-спісы пад полем пошуку («Усе · Матэрыялы · Фарміраванні · Асобы · Вышук РФ») паказваюць, колькі знойдзена ў кожным спісе (без запыту — колькі запісаў у базе), і абмяжоўваюць выдачу адным спісам. У ліпкім радку пад полем — чыпы, якія пераносяцца на новы радок, а не пракручваюцца: заўсёды кнопка «Назіранне» (раскрывае панэль спісу назірання ніжэй) і сартаванне, прыціснутае да правага краю; пры запыце дадаюцца «Сачыць» і «Спасылка», а «Любое са слоў» — толькі для запытаў з двух і больш слоў. На вузкім экране кнопкі займаюць шырыню па змесціве (не расцягваюцца), а «Спасылка» згортваецца ў іконку — подпіс вяртаецца на момант «Скапіявана».
  • Спіс назірання — кнопка «Назіранне»: увядзіце нік/канал/спасылку і націсніце «Сачыць». Пры кожным адкрыцці сайт правярае ўсе запыты і паказвае колькасць супадзенняў і статус («Супадзенняў няма», «Ёсць супадзенні» або «Новыя супадзенні»). Запыты, налада апавяшчэнняў і стан адкрытасці панэлі захоўваюцца толькі ў localStorage; ёсць «Праверыць зноў», «Ачысціць усё» і неабавязковыя браўзерныя апавяшчэнні.
  • Новае (#/new) — запісы па даце з’яўлення ў спісе з фільтрацыяй па спісах («Усе · Матэрыялы · Фарміраванні · Асобы · Вышук РФ») і асобным пераключальнікам «Выдаленыя» для зніклых са спісаў; RSS feed.xml для чытачоў стужак.
  • Статыстыка (#/stats) — таймлайн колькасці запісаў па даце судовага рашэння: стос бараў па артыкуле (314 ГПК / 302 КГС / без артыкула) з лініяй тэндэнцыі, маштаб і пракрутка жэстамі, картка перыяду і табліца-дублёр (src/lib/stats.js, Timeline.jsx, StatsPage.jsx). Пераключальнік «Матэрыялы · Фарміраванні · Асобы · Вышук РФ» дае той жа таймлайн для кожнага спіса са сваімі датай і серыямі (гл. раздзелы спісаў вышэй); спасылкі #/stats/f, #/stats/p, #/stats/w адкрываюць патрэбны спіс адразу.
  • Пошук па спасылках, ніках і імёнахhttps://, www., @ у пачатку і / ці знак прыпынку ў канцы адкідаюцца, пошук падрадковы («nick» знойдзе і «t.me/nick»); кірыліца ↔ лацінка ў абодва бакі (для фізічных асоб — і афіцыйная транслітарацыя з пераліку); калі дакладна нічога няма — прыблізныя словы з пазнакай: адна памылка для слоў з 5–7 літар, дзве — ад 8 (карацейшыя — толькі дакладна).
  • Старонка запісу (#/r/<id>) — адкрываецца па даце ў картцы (старонка пры гэтым пракручваецца ўверх); значок спасылкі справа ў кожнай картцы капіюе пастаянную спасылку на запіс з падказкай «Скапіявана». На старонцы — радок з кнопкай «Да пошуку» злева і «Скапіяваць спасылку», «Сачыць» справа (поўнае тлумачэнне ў падказцы), ніжэй загаловак, картка і палі: нумар у спісе (пазіцыя ў публікацыі; для фізічнай асобы — нумар МУС або пазнака, што яшчэ не прысвоены), для фарміраванняў, асоб і запісаў вышуку РФ — усе палі крыніцы, для вышуку РФ у канцы карткі палёў, за пункцірнай лініяй (як адрыўны корак білета), — заўвага пра крыніцу са спасылкай на гэты запіс у віджэце Медыязоны (?q=імя, знешняя, з тым жа папярэджаннем, што і спасылкі на рэсурсы са спісаў); асоба і запіс вышуку пра таго ж чалавека спасылаюцца адно на аднаго.
  • «Што гэта значыць для мяне?» — агаворка «не юрыдычная кансультацыя», потым каляровы блок на кожны спіс, які ёсць у выдачы ці якому належыць адкрыты запіс (матэрыялы — адміністрацыйная адказнасць і падпіскі, фарміраванні — крымінальная, асобы — абмежаванні, вышук РФ — не афіцыйны спіс), парада «калі знайшлі сябе» і спасылкі на праваабаронцаў; кожны факт — у адным месцы, без агульнага пераказу ўсіх спісаў.
  • Афлайн (PWA) — пасля першага адкрыцця сайт і база працуюць без інтэрнэту; можна «ўсталяваць» на тэлефон. У шапцы — час апошняй праверкі крыніц («праверана …»; націск раскрывае тултып з датай па кожным спісе) і папярэджанні: крыніца не адказвае, узятая запасная, спіс матэрыялаў не правяраўся больш за 36 гадзін, афлайн.

Як працуе пошук. Пры адкрыцці грузіцца толькі індэкс (~1,1 МБ gzip): нармалізаваныя назвы і імёны, слоўнікі тыпаў, судоў і артыкулаў, даты. Пошук — падрадковы па ўсіх словах запыту (ці па любым), просты includes над ~22 тыс. радкоў — мілісекунды. Поўныя запісы ляжаць у фрагментах па 200 і падцягваюцца толькі для тых, што на экране.

Лакальна

npm install
npm run update     # спампаваць свежы спіс і абнавіць data/
npm run update:formations   # тое ж для пераліку экстрэмісцкіх фарміраванняў (МУС/КДБ)
npm run update:persons      # тое ж для пераліку фізічных асоб (МУС, некалькі .doc)
npm run update:wanted       # тое ж для базы вышуку РФ па беларусах (JSON Медыязоны)
npm run dev        # збярэ індэкс і адкрые dev-сервер
npm test
npm run build      # dist/

Разабраць лакальны файл: node scripts/update.mjs шлях/да/файла.

Зменныя асяроддзя: SITE_URL — адрас сайта для спасылак у RSS/Telegram і GEO-файлах (у CI зададзены ў воркфлоу); BASE_PATH — шлях, з якога аддаецца сайт (па змаўчанні /); FORMATIONS_SOURCE, PERSONS_SOURCE — адрас старонкі МУС для другога і трэцяга спісаў; WANTED_SOURCE, WANTED_PAGE — адрас JSON Медыязоны і старонкі, праз якую ён шукаецца нанова; PERSONS_BUDGET_MS — агульны ліміт часу на спампоўку ўсіх частак пераліку фізічных асоб у мілісекундах (па змаўчанні 18 хвілін); MAX_ADDED — колькі новых запісаў за раз лічыцца падазроным (400 / 100 / 300 / 500 для матэрыялаў / фарміраванняў / асоб / вышуку); UPDATE_FORCE=1 — прыняць абнаўленне насуперак гэтай засцярозе.

Telegram-канал з новымі запісамі (неабавязкова)

Стварыце бота (@BotFather), дадайце яго адміністратарам у публічны канал і задайце ў Settings → Secrets рэпазіторыя TELEGRAM_BOT_TOKEN і TELEGRAM_CHAT_ID (@назва_канала або лічбавы id). Пасля кожнага абнаўлення з новымі запісамі scripts/notify.mjs дасылае дайджэст. Ніякіх персанальных запытаў — толькі публічны спіс новага.

Кожны запіс трапляе ў канал адзін раз: дасланыя id пішуцца ў data/notified.json і камітуюцца разам з базай, таму другі запуск за суткі нічога не дублюе. Калі адпраўка ўпала на сярэдзіне, пазначаецца толькі тое, што сышло, а рэшта дабіраецца наступным запускам (акно — 7 дзён, адзнакі захоўваюцца 60 дзён).

Адмін-чат (неабавязкова). Дадайце сакрэт TELEGRAM_ADMIN_CHAT_ID (ваш асабісты chat id: напішыце боту і паглядзіце getUpdates, або спытайце ў @userinfobot) — і той жа бот прышле туды паведамленне, калі джоб упаў, крыніца перастала адказваць ці ўключылася запасная. Пробнае паведамленне (telegram_test) таксама ідзе ў адмін-чат, калі ён зададзены, і ніколі не пазначае рэальныя запісы дасланымі.

Праўкі ў крыніцы. Выпраўленая памылка друку ці абрэзаны радок пазнаюцца як праўка, а не як новы запіс (scripts/merge.mjs): у дайджэст, RSS і «Новае» такі запіс не трапляе, старая пастаянная спасылка вядзе на актуальную версію, а спіс назірання не трывожыць, калі папярэднюю версію ўжо бачылі.

Люстэрка

Сайт статычны: npm run build → змесціва dist/ можна выкласці на любы хостынг (Netlify, Cloudflare Pages, свой сервер, IPFS). Каб спасылкі былі правільныя, задайце BASE_PATH (шлях, з якога аддаецца сайт) і SITE_URL (поўны адрас).

GEO і SEO

Сайт — SPA, таму без JavaScript краўлер бачыць пусты <div id="root">, а генератыўныя рухавікі (ChatGPT, Claude, Perplexity, AI Overviews) цытуюць толькі тое, што ёсць у HTML. Каб сайт трапляў у адказы, пры зборцы генеруецца:

Файл Што гэта
faq.html, faq-en.html статычныя старонкі «пытанне → самадастатковы адказ» з FAQPage-разметкай
llms.txt карта сайта ў prompt-friendly выглядзе: факты, лічбы, спасылкі
llms-full.txt увесь тэкст адным файлам: факты, правілы пошуку, усе пытанні і адказы (бе + en)
robots.txt яўны дазвол GPTBot, ClaudeBot, PerplexityBot і іншым + Sitemap:
sitemap.xml тры старонкі з lastmod па даце базы
JSON-LD у index.html WebSite з SearchAction (?q=…), Dataset, WebApplication
фолбэк у #root загаловак, лічбы і <noscript> са спасылкамі — React замяняе яго пры мантаванні
opensearch.xml сайт можна дадаць як пошукавік у адрасны радок браўзера (#q=, хэш не ідзе на сервер)
404.html старонка памылкі са спасылкамі — GitHub Pages аддае яе на невядомых шляхах
.well-known/security.txt куды паведамляць пра ўразлівасці (RFC 9116), з копіяй у корані

Тэксты жывуць у src/lib/faq.js і адтуль трапляюць ва ўсе фарматы адразу, з жывымі лічбамі (колькасць запісаў, дата абнаўлення, доля тыпаў). Усе файлы генеруюцца, у git не трапляюць.

GitHub Pages

  1. Запушце main у рэпазіторый.
  2. Settings → Pages → Source: GitHub Actions.
  3. Сакрэты (Settings → Secrets): TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, неабавязкова TELEGRAM_ADMIN_CHAT_ID. Правы токена зададзеныя ў самім воркфлоу (толькі джоб абнаўлення мае contents: write), таму «Workflow permissions» можа заставацца «Read repository contents».
  4. Запусціце workflow уручную або дачакайцеся чарговага запуску (двойчы на дзень). Воркфлоу збірае сайт з BASE_PATH: / (уласны дамен, public/CNAME); калі сайт аддаецца з падтэчкі https://<user>.github.io/<рэпазіторый>/, пастаўце ў воркфлоу BASE_PATH: /<рэпазіторый>/.
  5. Калі абнаўленне спынілася з-за засцярогі «падазрона шмат новых запісаў» (больш за MAX_ADDED, па змаўчанні 400), праверце файл крыніцы і запусціце воркфлоу ўручную з галачкай force (лакальна — UPDATE_FORCE=1 npm run update).

Бяспека і прыватнасць

  • Запыты не пакідаюць укладку: пошук працуе ў браўзеры, запыт не пішацца ў адрасны радок, гісторыю, кэш service worker'а і логі хостынгу. Спасылка на запыт — толькі па кнопцы, у выглядзе #q=… (хэш не адпраўляецца на сервер); так жа працуе OpenSearch.
  • Спасылкі ў запісах вядуць на матэрыялы са спісу, таму адкрываюцца пасля папярэджання (адзін раз за сесію), з rel="noreferrer" і <meta name="referrer" content="no-referrer"> — рэсурс не бачыць, адкуль прыйшоў наведнік.
  • CSP праз <meta http-equiv> (GitHub Pages не аддае загалоўкаў): толькі свае скрыпты і стылі, інлайн-скрыпт тэмы — па sha256-хэшу, які лічыцца пры зборцы (scripts/csp.mjs); знешніх запытаў у кодзе няма.
  • «Ачысціць усё» выдаляе спіс назірання, налады, запыт укладкі, афлайн-копію базы і service worker. Гісторыю браўзера сайт ачысціць не можа.
  • Сапсаваная спасылка (напрыклад, #/r/%) не кладзе сайт: маршрут разбіраецца бяспечна, а ErrorBoundary паказвае паведамленне з выхадам на галоўную.
  • CI: правы токена — толькі патрэбныя кожнаму джобу; крэдэнцыялы не застаюцца ў .git/config падчас разбору чужога .doc і npm ci; джоб абнаўлення ставіць толькі runtime-залежнасці без install-скрыптоў; дзеянні прыпінутыя да SHA, абнаўляе іх Dependabot. Каміт базы робіцца праз fetch + rebase з паўторамі; пры канфлікце ў data/ (папярэдняя спроба ці ручны push ужо абнавілі базу) джоб бярэ свежы main і паўтарае разбор, каб не губляць адзнакі пра дасланыя дайджэсты і не валіць дэплой. Паўторны запуск (re-run) заўсёды бярэ актуальны main, а не каміт першай спробы.
  • Засцярогі даных: абнаўленне спыняецца, калі «знікла» больш чым max(50, 5 %) запісаў (матэрыялы) ці max(20, 5 %) (фарміраванні, фізічныя асобы, вышук РФ) або дадалося больш за MAX_ADDED запісаў за раз; сеткавыя запыты да крыніцы з таймаўтамі; калі на старонцы некалькі .doc спісу матэрыялаў, бярэцца той, дзе больш запісаў; часткі пераліку фізічных асоб бяруцца ўсе ці ніводная. Шапка сайта папярэджвае, калі спіс матэрыялаў не правяраўся больш за 36 гадзін (для пералікаў МУС і базы вышуку РФ такога таймера няма — толькі папярэджанне пра збой крыніцы), крыніца не адказвае або ўзятая запасная.
  • Персанальныя даныя трэцяга спісу паказваюцца як у афіцыйнай публікацыі, але ў пошукавы індэкс трапляюць толькі імя, транслітарацыя, дата нараджэння, артыкулы КК і суд з прысуду ды дата ўключэння; месцазнаходжанне і грамадзянства — толькі ў фрагменце запісу (картка і старонка запісу), а ў Telegram-дайджэст і RSS дата нараджэння і адрас не ідуць. Даведка пра судзімасць на сайт не трапляе зусім.
  • Персанальныя даныя чацвёртага спісу (вышук РФ) — як у выбарцы Медыязоны: у індэкс трапляюць імя з іншымі напісаннямі, год нараджэння, ведамства-ініцыятар, рэгіён і дакладная дата вышуку; нацыянальнасць і рэдакцыйныя катэгорыі Медыязоны — толькі ў фрагменце запісу, а ў Telegram-дайджэст і RSS год нараджэння не ідзе.
  • Пра ўразлівасці: .well-known/security.txt → GitHub Issues.

Пошук

  • Не ўлічваюцца рэгістр, «ё/е», лацінская/кірылічная «i», віды лапак. Фраза ў лапках — цалкам.
  • Па рашэнні суда шукаюцца назва суда і дата рашэння (словамі «20 августа 2026» ці лічбамі «20.08.2026»); для фізічных асоб — суд і артыкулы з прысуду, дата ўключэння ў пералік і дата нараджэння («14.10.1983»); для вышуку РФ — імя і іншыя напісанні, год нараджэння, ведамства-ініцыятар («розыск», «МВД», «КГБ»), рэгіён і дата вышуку.
  • Запыт не пішацца ў адрасны радок (і не трапляе ў гісторыю браўзера, кэш service worker'а і логі хостынгу): ён жыве ў history.state укладкі і перажывае перазагрузку. Спасылкай на запыт дзеляцца праз кнопку «Спасылка» (#q=…); пры адкрыцці прымаюцца і #q=, і ?q=, пасля чаго адрас ачышчаецца. / — фокус на поле пошуку.

About (English)

What this is. An unofficial, open-source search over Belarus's official extremist lists: the Republican list of extremist materials — the register of channels, websites, accounts, videos, books and symbols that Belarusian courts have ruled extremist — the Interior Ministry / KGB list of extremist formations, and the Interior Ministry list of individuals “involved in extremist activity” (people convicted under “extremism” articles of the Criminal Code); plus a fourth, unofficial database — Belarusians on the Russian Interior Ministry wanted list, per Mediazona Belarus's daily extract (Belarusians end up there at the request of Belarusian agencies), cross-linked with the persons list by name and year of birth. Live at elist.itbeard.com.

Key facts.

Entries ~6 000 materials, updated automatically twice a day (shown with a red left border and “Material” label), plus ~380 entries from the Interior Ministry / KGB list of extremist formations (checked once a day, shown with a purple left border and “Formation” label) and ~6 900 individuals from the Interior Ministry persons list (checked twice a day, shown with a teal left border and “Person” label), plus ~6 700 Belarusians wanted in Russia per Mediazona (checked twice a day, shown with an amber left border and “RF wanted” label)
Search runs entirely in the browser; Cyrillic ↔ Latin transliteration (plus the official Latin spelling of listed persons); when nothing matches exactly, typo tolerance of 1 letter for 5–7-letter words and 2 from 8 letters
Data collected none — no queries, no cookies, no analytics, no server
Offline yes, PWA; the database stays in the browser
Alerts watchlist with browser notifications, RSS feed.xml, Telegram @elist_by
Stack React 19 + Vite, static hosting, ~1.1 MB gzip index
Price free, no sign-up

Why it exists. Distribution, production, storage and transport of listed materials is an administrative offence in Belarus under Art. 19.11 of the Administrative Code — up to 20 base units or arrest for individuals. People need a fast way to check whether they, their channel or their bookshelf is on the list, without leaving a trace on a server.

Answers to common questions are on the static FAQ page (English).

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages