Замечания и предложения по нему, а также их обсуждение.Предыдущий тред тонет тут >>1000357.
>>1000685На это первое апреля была музыка же.>>/b/1006241
В первом приближении bulk deletion для Автобуса готово.В принципе, можно сюда, но без правок не выйдет. Даже по схеме успели разойтись! В одном столбце, правда.Раз уж такое дело, что-то подумалось мне, что не плохо бы сначала заняться огранизацией данных.1. Объеденить post_* в одну таблицу, выпилив текстоборды.2. Посолить KU_RANDOMSEED'ом ipmd5, ибо в текущем виде это от утечки БД не защита; look-up таблица в 4 * (2 ** 32) = 16 GiB в оперативке ноутбука уместится. Или, наоборот, выпилить и ipmd5, и ipenc, сделав простое и быстрое поле в 4 байта.3. Придумать индексы для таблиц. Скажем, (board_id, parent_id, id) в качестве кластерного для posts, (board_id, ip, id) для быстрого поиска по ip, (board_id, parent_id, posted_at) для бампов, и так далее.3.a) Есть ли смысл включать флаги в индексы? has_sage, в силу перманентности, наверное стоит, сделав (board_id, parent_id, posted_at) фильтрованным. MariaDB в такое, правда, не может, но повод перейти на Postgres как раз таки. А вот is_deleted, хоть по нему фильтрация почти везде, может включением в индекс существенно замедлить удаление из-за необходимости в ребалансировке. Или нет? Не знаю. Почему-то я помню, что InnoDB, даже если столбец-флаг в конец ключа поставить, всё равно при измении внутри ребалансировку делает: сначала удаляет leaf, потом добавляет на то же место. Но это не точно.3.b) Есть ли смысл поменять parentid=0 для открывающих постов тредов на parentid=id? Чтобы без union'ов select'нуть весь тред по const ref. Но тогда нельзя будет select'нуть ОП'ы тредов по const ref. Хотя опять же, проблема решается созданием filtered index. Так что всё-таки следует. Любопытно, что в class="ref|parentid|id" у нас именно такой формат.3.c) Добавить столбец, где кэшировать отрезок из последних 7 постов треда, и столбец, где кэшировать вывод для read.php.3.d) Пользуется ли кто-то watchedthreads? Если да, то не переделать ли на более щадящую архитектуру, где пользователь запоминает в local storage, какие он смотрит треды, и спрашивает у сервера только их последние IDшники? Если нет, то не выпилить ли?3.e) И так далее.Как смотрите? Сделать миграцию на Postgres, потом заняться конфигом, потом сделать-таки улучшенную версию скриптовой отправки поста, ибо одни только подкапотные изменения тоже не дело.
ПОЖЕЛАНИЕ!Сделайте лучше нормальный тёмный стиль! И чтобы с сотового работало! А то я даже не знаю как в мобильной версии переключить, чтобы хотябы на bluemoon. Который тоде недостаочно тёмен всё равно.На пк у меня расширение со своим стилем, а на мобильном вроде тоже можно как-то настроить но жто разбьираться надо. И с работы когда или с ноутбука то тоже везде надо настроить ведь. А сейчас только на домашнем пк!
>>1000693Android?
>>1000694У мкеня и то и другое!
> И чтобы с сотового работало!Тут тоже непонятно. Чтобы стиль работал, или вообще что-то другое работало. А то FBE всё адаптировали да адаптировали под эти мобилки, н ж уже респонсивный(tm) , а всё зря, постеры-то разбежались lol. Для будущих поколений работа прям..
Тоже есть FR. Раз уж аплоад-форма умеет выдавать сообщения теперь, пусть сообщает, в каких сообщениях была уже запощена картинка. Только не знаю, умеет ли это сообщение быть sticky, т.е остаться-показаться после перезагрузки страницы по прошедшему посту - т.к. мы не запрещаем отправку дубликатов.Другими словами: уведомление юзеру, а не ошибка, о наличии дубликатов файлов.
>>1000693Сейчас переключить можно, открыв desktop'ную версию сайта, и переключившись по выбору нужного стиля обратно на мобильную. Либо так, либо некими браузерными дополнениями, где можно задать пользовательские стили для сайта.Проблема в том, что, если я верно помню, некоторые свойства для мобилок у стилей, кроме Умночана, не определены.И надо подумать, куда ставить кнопку переключения в мобильной версии. Может, отдельную форму настроек, где стиль, вариант капчи, и подобное можно будет поменять; сделать значок-шестерёнку и вместо/слева от watchedthreads её поставить?> Сделайте лучше нормальный тёмный стиль!> На пк у меня расширение со своим стилемЗапостите же.>>1000697Sticky нет, не умеет. Умеет зависать (lingering) на какое-то количество секунд, если PHP неожиданно сделало echo, по истечению которых происходит перенаправление. Сделать кнопку, чтоб закреплялось? И сделать, чтобы перенаправляло сразу, но по перенаправлению восстанавливало показываемый status bar. Может, также дать возможность открепить движением мышки, как с формой быстрого ответа?Другое дело, что в текущем виде у status bar'а стиль ярко-красным кричащий об ошибке, а для чего-то нейтрального, наверное, и цвета понейтральнее?На сервере, если по-простому, добавить exitWithNotification($message, $redirect_to).Альтернативно, endpoint, проверяющий наличие hash'а в базе, чтобы можно было по выбору файла в JSе посчитать его md5 и тут же отправить на сервер, не дожидаясь отправки поста вместе с файлом целиком. read.php ведь позволяем без капч и ограничений; если сделать индекс по хэшу, будет не затратнее. Но это если индекс. Так что я бы сначала в целом базу поправил.
>>1000696Да, я про стиль. Так по работе особо ничего поломанного не замечалось. >>1000700>открыв desktop'ную версию сайта, и переключившись Я так на андроиде делаю, но айфон игнорирует переключение на полную версиюб и не пееключает почему-то. Хотя другие сайты переключает.>Запостите же.Там просто покрашеный умночан по мотивам одной другой картинколоски. Есть пару мест которые недоделаны, но я и так живу.У меня там с годами совсем каша была и кучас своих пометочек, так что попросила ИИ поправить, вроде ничего не сломалось.https://pastes.io/cBPq0Tsf
>>1000701> https://pastes.io/cBPq0TsfВ целом, цветовая схема понятна. Mint назвать?Если Якуй одобрит, займусь кнопкой, чтобы и на мобильной версии переключалось, после >>1000697, наверное.> У меня там с годами совсем каша была и кучас своих пометочек, так что попросила ИИ поправить, вроде ничего не сломалось.Но сам CSS, буде тот приведён в соответсвие с https://codeberg.org/yakui-lover/fbe-410/src/branch/kotocafe/css/umnochan.css, наверное, можно и сейчас добавить. Пока вижу, что стиль кнопки переключения стилей отличается, и что спойлер не сплошным цветом и что с задержкой по наведению курсора исчезает (наверное, серым в тон текста сделать).
> Заголовок слева у предпросмотра теперь показывает «Предпросмотр»Хорошо.
Сломан постинг видеов, поскольку теперь imgWidth/Height строго int и вывод ffprobe нужно конвертировать. Патч в архиве.
>>1000708Принято.>>1000690>Объеденить post_* в одну таблицу, выпилив текстоборды.Это надо будет не просто переписать всю логику запросов, но и логику инкремента счётчика постов. В Постгре-то точно счётчики есть...>Посолить KU_RANDOMSEED'ом ipmd5, ибо в текущем виде это от утечки БД не защитаПусть будет одно решение для паролей и ип, пусть и разные соли. Зачем в оригинале два столбца не слишком понятно....хотя "посмотреть посты с подсети" теоретически хорошая вещь.>(board_id, ip, id) для быстрого поиска по ipid тут не требуется.>Есть ли смысл включать флаги в индексы?Наверное не в наших объёмах. После всей фильтрации, они будут отрабатывать на, сколько, 250 постах максимум за раз? Ну, больше чтобы сделать индекс если мы будем бесконечно резиновыми или если подлодка.>Есть ли смысл поменять parentid=0 для открывающих постов тредов на parentid=id? Чтобы без union'ов select'нуть весь тред по const ref.Я в своей лабуде решал это через отдельную таблицу для тредов. Туда уже пихается sticky/locked/счётчики видимых постов.>последних 7 постов тредаПоследних N постов треда, это настраиваемая опция. И раз она конфиг-зависимая, наверное нет.>столбец, где кэшировать вывод для read.phpТам разве вывод хоть как-то отличается? Или в том плане, чтобы шаблонизатор не дёргать? Тогда лучше жсон отдавать, ибо головная боль будет при пересборке кешей ещё и всю БД переписывать.>>1000700>Но это если индексfile_md5 уже индексируется.>Альтернативно, endpoint, проверяющий наличие hash'а в базе, чтобы можно было по выбору файла в JSе посчитать его md5 и тут же отправить на сервер, не дожидаясь отправки поста вместе с файлом целиком.Удобно, можно и так.>read.php ведь позволяем без капч и ограниченийИ постопревью, да.>>1000706>Если Якуй одобритМне-то что, я не против даже что сейчас дали залить.
Алсо, я всё ещё не >>1000340
>>1000700Словом "sticky" хотелось передать поведение> по перенаправлению восстанавливало показываемый status barКоторый висит и ждет нажатия юзверем крестика, после чего пропадает, стирается, исчезает.> Сделать кнопку, чтоб закреплялось> возможность открепить движением мышки, как с формой быстрого ответа?Не нужно. Т.к. совсем не в том смысле.> а для чего-то нейтрального, наверное, и цвета понейтральнее?Да.> endpoint, проверяющий наличие hash'а в базеТут подумалось, что можно сообщение "было запощено тут и тут" выводить в качестве ("ложного") "post preview", переиспользуя эту фичу.>>1000710> 2024Так и вся жизнь пройдёт w.На основном fbe зачем-то убрали каноничную сажирующую стрелочку вниз в "теме" у постов, которая была с нами годами! Заменив какой-то непонятной фигней, которую я рассматривал через экранную лупу, пытаясь понять, что это. Расстроили.
>>1000709> но и логику инкремента счётчика постовДобавить в boards столбец new_post_id, и insert'ить в posts как-то так.WITH new_id_source AS ( UPDATE boards SET new_post_id = new_post_id + 1 WHERE board_id = :board_id RETURNING old.new_post_id AS post_id_to_insert)INSERT INTO postsSELECT :board_id, post_id_to_insert, :parent_id, ...FROM new_id_source> Зачем в оригинале два столбца не слишком понятно.Дело в том, что Kusaba'вый md5_encrypt шифрует с nonce'ом (N_once) — случайно генерируемым значением, из-за которого шифротекст всегда разный — даже если IPшники одинаковые.Поэтому, если бы не ipmd5 столбец, пришлось бы ходить foreach'ем по SELECT `id`, `ip` FROM `posts_*` WHERE NOT IS_DELETED, чтобы найти все посты по нужному IP. Хотя, now that I think of it, md5_decrypt можно написать и на SQL, чтобы не дёргать лишнего с базы, но это всё равно ходить по всем постам, пусть и не в PHP.Поэтому, если хочется этого избежать, то столбца нужно два. Либо два столбца; либо сменить алгоритм шифрования с md5_encrypt на что-то более удобное для поиска, что по-хорошему и стоит сделать.https://www.cryptologie.net/posts/key-wrapping-and-nonce-misuse-resistance/Насколько я понимаю, в самом PHP нету детерминистичных алгоритмов шифрования, не уязвимых к keywrap/keystream reuse attack, когда nonce константа.Надо будет добавить extension=openssl в php.ini и взять AES-128-SIV оттуда; впрочем, ничего сложного.> Пусть будет одно решение для паролей и ип, пусть и разные солиЕсли делать 1 столбец для IP, то решение для паролей будет другое. Думается взять в качестве хэша либо bcrypt(sha512(password)), либо openssl_pbkdf2() с SHA-256.Для файлов тоже SHA-256? Apparently, MD5 нету в браузерном Web Crypto API. А если какие-то дополнительные lib'ы тянуть, то появляется смысл перейти на blake2 (b2sum), который без коллизий и быстрее. Или даже на blake3, но тут lib'а уже и для PHP понадобится.> id тут не требуетсяДаже чтобы id-шники по IP шли в порядке возрастания, как оно, наверное, сейчас?InnoDB по-тихому добавляет незадействованные столбцы кластерного индекса в конец других индексов. Что, если делать (board_id, parent_id, post_id) кластерным индексом, думаю, привело бы к тому, что IDшники в индексе по IP шли бы в порядке тредов, а не в порядке возрастания, как оно, предполагаю, сейчас.Маны PostgreSQL говорят, что у них heap и нету кластерных индексов, так что будут отображаться в порядке добавления в таблицу? Наверное, и правда не надо. Хотя если вдруг захочется 'ORDER BY id DESC', могло бы быть полезно.> Я в своей лабуде решал это через отдельную таблицу для тредов. Туда уже пихается sticky/locked/счётчики видимых постов.С Postgres я всё-таки склоняюсь к тому, чтобы не нормализовать posts. Меньше join'ов писать и базе меньше join'ов делать. Postgres пропускает помеченные, как NULL, значения в записи строки, так что меньше места на диске база от выноса специфики про файлы и треды в отдельные талбицы занимать не станет.> Последних N постов треда, это настраиваемая опция. И раз она конфиг-зависимая, наверное нет.Добавить в таблицу posts столбец cache_timestamp, который заполнять и сравнивать с timestamp'ом конфига и timestamp'ом настроек доски для тредов, охватываемых генерацией их страниц и страниц досок.> Там разве вывод хоть как-то отличается? Или в том плане, чтобы шаблонизатор не дёргать?Отличается вывод или нет, точно не знаю, но да, чтобы отдать готовый HTML.Насколько я понимаю, этот HTML поста иммутабельный и меняется разве что если посту файл удалить или настройки для доски/глобальные поменяются. Как правило, не меняются последние N постов тредов на страницах досок. Как правило, не меняются и сами эти страницы. Не всегда меняются +50 и -100 при удалении постов или их прикреплённых файлов. Пока не меняются настройки доски, форма постинга на её страницах иммутабельна за исключением "Ныне N уникальных клиентов".Вообще говоря, при добавлении/удалении постов, из страниц всегда меняется только страница каталога.Так что тут простор для кэширования довольно большой. Мне не кажется, что это очень уж сложно, хотя и требует переписать FBE значительных переделок.Так что я бы пока добавил в boards и posts необходимые для кэшей столбцы, пусть будут DEFAULT NULL, чтобы не мешало тому, как оно сейчас есть.И как-нибудь потом потихоньку, не трогая Кусабу, завёл в inc отдельную директорию services, где держал бы новые DeletePostService, CreatePostService, UpdatePostCacheService, GenerateThreadPageService и так далее, потихоньку привязывая endpoint'ы к ним.Некоторое свободное время/желание пока есть.
WITH new_id_source AS ( UPDATE boards SET new_post_id = new_post_id + 1 WHERE board_id = :board_id RETURNING old.new_post_id AS post_id_to_insert)INSERT INTO postsSELECT :board_id, post_id_to_insert, :parent_id, ...FROM new_id_source
md5_encrypt
SELECT `id`, `ip` FROM `posts_*` WHERE NOT IS_DELETED
>>1000712> либо openssl_pbkdf2() с SHA-256On the other hand, всё-таки несколько медленноватое оно даже при не столь высоком количестве итераций. Если постер потрудится запомнить ещё 3—4 лишних символа или ещё одно слово, то hash_hmac('sha256', $pwd, KU_RANDOMSEED) будет с тем же эффектом, но без нагрузки на сервер. Явно указать где-нибудь в FAQ алгоритм хэширования и минимальную длину пароля, если хочется, чтобы и ASICом на 2**50 H/sec не проняло.
hash_hmac('sha256', $pwd, KU_RANDOMSEED)
>>1000712>Насколько я понимаю, в самом PHP нету детерминистичных алгоритмов шифрования, не уязвимых к keywrap/keystream reuse attack, когда nonce константа.У атакующего нет возможности сделать произвольный айпишник.>Даже чтобы id-шники по IP шли в порядке возрастания, как оно, наверное, сейчас?Eh, оверкилл в общем случае наверное. Но стоит.>Apparently, MD5 нету в браузерном Web Crypto API.Там вообще хеширования нет, лол.>то появляется смыслСмысл есть использовать то, что используют все, чтобы поискав картинку на данбуре не приходилось пересчитывать хеш для поиска тут.>Если делать 1 столбец для IP, то решение для паролей будет другое.Айпишник тот же пароль, только короче. Хотя можно ipv6 форсировать.>Не всегда меняются +50 и -100 при удалении постов или их прикреплённых файлов.-100 меняется всегда кроме удаления верхних файлов.>Вообще говоря, при добавлении/удалении постов, из страниц всегда меняется только страница каталога.И страница треда.>Так что тут простор для кэширования довольно большой.Люди для этого обычно кеширующий сервер ставят...>>1000714Если я хочу использовать :) в качестве пароля, никот не должен мне это запрещать, особенно когда под этим паролем ничего кроме возможности удалить пост нет. ИМО.
>>1000715> У атакующего нет возможности сделать произвольный айпишникhttps://hacker101.linuxsec.org/vulnerabilities/stream_reuseА произвольный и не обязательно. Если nonce константа, то в зависимости от алгоритма, насколько я понимаю, достаточно одного поста в утёкшей базе, для которого атакующему IPшник уже известен, чтобы получать IPшники к остальным.AES не streaming chipher, но там вроде какие-то свои проблемы, раз сделали AES-SIV специально для. Так что стоит использовать AES-SIV.> Там вообще хеширования нет, лол.Но оно там есть? Pic related.> Cмысл есть использовать то, что используют все, чтобы поискав картинку на данбуре не приходилось пересчитывать хеш для поиска тут.Действительно. Хотя какой-нибудь весельчак когда-нибудь начнёт пытаться постить файлы с одинаковыми или прикольными хэшами for lulz. Но, наверное, не особо страшно, хотя возможность одним запросом выбрать байт-в-байт одинаковые дубликаты теряется.Другое дело в том, что картинки бывает пережимаются, и как поиск дубликатов содержимого оно в таком случае так себе. Стоит ли когда-нибудь потом запилить хэш ещё и для пикселей? Forgery оно не остановит, достаточно один пиксель поменять, но поможет найти пережатый с cwebp или jpegoptim файл.> Айпишник тот же пароль, только короче.Если мы для IPшников храним только хэш, то разве не теряем возможность забанить оставившего пост по подсети? И разве IP постеров в модерке (например, на странице жалоб) отображаются не в голом виде? (Хотя, можно отображать и хэш.)А для паролей достаточно хранить только хэш.В этом разница.> Люди для этого обычно кеширующий сервер ставят...Redis? Хм. Могу и так, но с БД-only в первом приближении несколько проще сделать, мне кажется.> Если я хочу использовать :) в качестве пароля, никот не должен мне это запрещать, особенно когда под этим паролем ничего кроме возможности удалить пост нет. ИМО.А запрещать и не предлагается. Предлагается поставить совет в FAQ. Или, что точнее, заметку.
>>1000716>Хотя какой-нибудь весельчак когда-нибудь начнёт пытаться постить файлы с одинаковыми или прикольными хэшами for lulz.В очередной раз, не тот масштаб... Плюс у нас нет запрета на репост если только не в том же треде. В архиве у меня параноя-мод и при коллизии сравниваются разрешение, размеры и затем побитово.>Стоит ли ..?Если интересно, то пусть делается, единственные две преграды что я вижу: 1. Это не работает 2. Это недоделано.>картинки бывает пережимаютсяИли кто-то в них раржпег кидает, ага.>Если мы для IPшников храним только хэш, то разве не теряем возможность забанить оставившего пост по подсетиА, точно. И в голом, чтобы можно было подсеть определить в случае однотипных банов. Может для джениторов только хешем правда. Тогда вопроса нет. И я даже не уверен, влезет ли сейчас сюда ipv6.В постгре под них даже свой тип, но без встроенного шифрования... но удобно было бы смотреть посты с подсети наверное.Кстати о встроенном, есть https://dev.mysql.com/doc/refman/8.4/en/encryption-functions.html или https://www.postgresql.org/docs/current/pgcrypto.html. Хоть я и не люблю выносить код из кода в БД, и для постгри это отдельный модуль ставить, но может можно использовать так.>с БД-only в первом приближении несколько проще сделать, мне кажется.Мне просто кажется что проще просто выкинуть шаблоны и всё во фронте рисовать чем заниматься кешем ручками.
Попробовал посмотреть, насколько применим fingerprint картинок, который phash, к Булочным картинкам.Картинки брал с 15544700221 по 176095181827 без гифок, всего 16112 картинок.Находится 113 групп по одинаковому phash, состоящих из двух или более картинок: только в одной из этих групп картинок 3, а в остальных по паре дубликатов.Из них 6 false positive.(>>/b/5852, >>/b/10732), (>>/b/3154, >>/b/3161), (>>/b/2512, >>/b/8466), (>>/b/7767, >>/b/19060), (>>/b/11092, >>/b/11456)False positive в том плане, что это легитимные вариации той же картинки или творчество по мотивам другой, а нам скорее найти, было ли ровно то же самое уже запощено в оригинальном или переужатом виде или с другой шириной/высотой.41 из тех 113 — целиком посты, у которых MD5 пикселей совпадают, то бишь lossless переужатие/оптимизация или какое-нибудь echo 'new_hash' >> file.jpg.Остальные 113 - 41 - 6 = 66 — ужатые с потерями или с изменением разрешения дубликаты, которые попиксельным сравнением не найти. Например, (>>/b/12578, >>/b/12935), (>>/b/16642, >>/b/18597), (>>/b/3372, >>/b/4413).Однако, увы, находит оно их ужатые с потерями не все.Попробовал увеличить размер phash с 64 бит до 256, и, внезапно, нашло ранее не обнаруженый (>>/b/5816, >>/b/7377). Хотя казалось бы, большая специфичность, и я 16x16 окно чтобы false positives убрать тестировал. False positive там действительно получилось на 4 меньше, но появляется false negative и значимо чаще; всего найдено было 99 групп, когда phash 256 битов, против 113, когда phash 64.Если повышать чувствительность, группируя phash'и, не чтобы равны были, а чтобы на растоянии не более чем в N отличающихся битах, false positives становятся куда менее опрятными, хотя настоящих дубликатов и находится больше.Для 64-битного phash, когда N равно 4, посчитало равными >>/b/11948 и >>/b/16305, что уж совсем грубая ошибка. Впрочем, настолько грубая она там была единственная. Чаще (>>/b/22454, >>/b/22462), (>>/b/8097, >>/b/15811), (>>/b/8481, >>/b/8513, >>/b/8614). Что в определённых случаях даже и хорошо, когда интересно поискать похожие, а не только одинаковые картинки или удостовериться, что ничего одинакового почти наверняка нет. Но, хм. Скажем, видит человек >>/b/23618, решает >>/b/23620 edit сделать и запостить, а ему раздражающее уведомление про только что переделанный якобы дубликат, которое надо специально закрывать.Так что если делать автоподгрузку ссылок на посты, где картинки скорее всего одинаковые, пусть, наверное, будет либо только по равенству MD5 пикселей или только по равенству phash'ей (?), но не по расстоянию меж. Как и куда вынести возможность поиска по расстоянию, если делать, надо будет думать. Особенно так, чтобы и без скриптов работало.
echo 'new_hash' >> file.jpg
>>1000718> Чаще (>>/b/22454, >>/b/22462)Не >>/b/22454, а >>/b/22452, то бишь.Это, и Булочные, то бишь, bulochka.org.
Если запиливать IPv6, какой длины префикс брать для faptcha attempts?> https://dreamserver.ro/en/lir-services/ipv6-lease/5 евро в год за /48 и 100 евро в год за /32> https://www.ipxo.com/free-ipv6/Эти вот дают /36 на месяц даром.Наверное, тут по-хорошему нужен какой-то сложный rate limiter.
Маловероятно одновременное несовпадение фапчи у многих пользователей с той же /32 подсети. Так что пока пойду таким путём.Для /64 пусть ограничение будет, как есть: до двух провальных попыток.Смотрим на /64, но до этого смотрим на /n in range(32, 64, 4), где ставим threshold на количество провальных попыток, как 2 * (log(64 - n)**3), получая пределы 83, 73, 64, 53, 42, 30, 17, 5 на подсети с /32 по /64 соответственно.Конечно, текущая капча в целом скорее декоративный забор, чем серьёзное препятствие человеку с намереньями. Но если делать на чёрный день более сложные её варианты, делать rate limit на прокрутку или на что-то ещё, будет задел.
/n in range(32, 64, 4)
2 * (log(64 - n)**3)
Я думала, капча на сессию куки ставится, а не на айпи.
А, речь про неправильные попытки отгадывания, я бака. Наверно, имеет смысл взять принцип схожий с принципом, используемым с IPv4. То есть, если для каждого адреса IPv4 вводится ограничение (/32), то и для IPv6 адресов имеет смысл ограничить ровно такое же количесто энтропии (/32 подсеть для адреса). Но это так, рассуждения с дивана. Интуиция подсказывает, что это может быть слишком ограничительная мера, нужна реальная статистика использования v6 адресов на самом сайте и в Интернете вообще.
В RSS у ОПов тредов страница 0.html.
>014.cdn.ganbaranai.moeОднако.
>>1000728> то и для IPv6 адресов имеет смысл ограничить ровно такое же количесто энтропии (/32 подсеть для адреса)https://networkingtoolbox.net/reference/ipv6-prefix-lengths/32 подсеть в IPv6 — это целый провайдер. Но может быть и атакующий, арендовавший за $10 подсеть на месяц. В том проблема.Как пишут, конечным пользователям обычно дают /64. Но могут давать и больше или меньше.
>>1000731Блокировать /32 если в его пределах уже есть несколько (сколько?) заблокированных /64?
>>1000732> сколькоПока ничего умнее, чем> Смотрим на /64, но до этого смотрим на /n in range(32, 64, 4), где ставим threshold на количество провальных попыток, как 2 * (log(64 - n)**3), получая пределы 83, 73, 64, 53, 42, 30, 17, 5 на подсети с /32 по /64 соответственно.Не придумал.Разве что добавлять некий scaling factor на основании hash(subnet, salt) и/или hash(time, salt), чтобы было сложнее понять, есть ли кто-то ещё в той же подсети или нет, перегрузив систему.По схожему принципу ограничивать количество фапчесессий и частоту отправки тредов/постов, наверное.
Небольшое замечание:https://014chan.org/favicon.png > 404https://014chan.org/favicon.ico > favicon.ico который СОВСЕМ не ico - [0x89 0x50 0x4E 0x47] вместо [0x00 0x00 0x01 0x00]Забавно.
Хотелось бы выяснить - где-то есть актуальная версия автобусодвижка в том виде, в каком он используется тут? https://codeberg.org/yakui-lover/fbe-410 явно не соответствует ситуации на данный момент - даже сравнивая справку по разметке с тем что тут в коде. Это намеренно так?
>>1000743https://codeberg.org/yakui-lover/fbe-410/src/branch/kotocafe
>>1000744Вот же бака...Благодарю.
>>1000745Там всё ещё может быть лаг в пару незначительных правок.Но всё же, ссылка снизу страницы ведёт именно туда.
Обсуждаете IPv6, который общее место что почти мёртв. Там давно в СМИ предлагают новые IPs сделать, сразу удобные для гос-в.
>>1000747>предлагаютВот когда в странах будет 80% adoption rate, тогда и приходите
>>1000747Ты про https://www.ietf.org/archive/id/draft-thain-ipv8-00.html это?
>>1000749Нет, раньше. Про https://www.linux.org.ru/forum/talks/17212183 (2023) и КНР с New IP (2020).
>>/b/1006585> спойлерЭто-то я помню. Мне про https://410chan.org/dev/res/26066.html#28190 было интересно.> Сейчас. Над движком 0141Давайте тогда обсудим архитектуру. Не могу сказать, что она у меня в каком-то чётко оформленном состоянии в голове. Но попробую описать, как мне пока видится.Мне кажется, здесь оказывается довольно уместной инкапсуляция по действиям, нежели по entities. То бишь, что-то ближе к transaction scripts. Скажем, DeletePostAction с функциями для удаления постов, CreatePostAction, ReportPostAction, ArchiveThreadAction, GenerateBoardPagesAction, и так далее. Для какой-нибудь entity или набора action'ов, где разделение на разные файлы излишне — какой-нибудь общий *Service или *Controller класс.Endpoint'ы вроде board.php занимаются wiring'ом входящего JSONа или $_GET/$_POST переменных к Action'ам и Service'ам и высокоуровневой логикой.Выполнение Action'а возвращает (анонимный) класс со статусом и результатами. Статусы соответствуют HTTP кодам. If applicable, должна быть функция, которая сообразно его типу этот результат в HTML или в JSON и выходит с ним. Не думаю пока, что где-то есть смысл делать отдельные View классы.Пока это без особой мороки или overhead'а, если Action затрагивает несколько entities/таблиц, тянуть (lateral) join'ами информацию сразу с нескольких одним запросом. Например для modDelete пусть оно в один запрос получает board_id соответствующий board_name и проверяет права модератора для этого board_id, буде тот найден. Для CreatePostAction, пусть оно вместе с board_id, если enable_attachments, тянет строку про файлоформат для приложенного файла, а также thumbnail_width и thumbnail_height из таблицы-синглтона settings. Этот контекст пусть передаётся AttachmentService'у или Handler'у.Распихать config.php с его KU_THUMBNAILWIDTH и KU_THUMBNAILHEIGHT по таблицам. Оставить в config.php параметры подключения к БД, соль и домен/пути. Для вещей, которые пока не понятно, как удобным образом хранить в табличном виде, хранить JSON. Сделать соответствующий пункт меню в админке. Не знаю, сейчас распихивать или как-нибудь в следующий раз.
Черновик схемы.Posts по-прежнему wide table. Но IP выносим вовне, поскольку для IPv6 точно нужен будет поиск по подсети, а значит фильтровать IP'шники в PHP, а не в базе.
Черновик board.php.
Такой момент. Чтобы избежать blind index и двух столбцов для IP, как в оригинале: один шифрованный, другой с хэшем, я хочу использовать AES-SIV.Увы, разработчики PHP случайно AEAD шифры.https://github.com/php/php-src/pull/21872https://github.com/php/php-src/pull/21874Как я понимаю, пока ни в master, ни в 8.5 не смёржили.Мой пока выход — скомпилировать версию предложившего исправление.Авось, через месяц-другой оно станет официально.
>>1000751Вы предлагаете абсолютно полностью переписать всю логику. Это будет уже не FBE и даже не форк Кусабы. Скорее что-то Вакабоподобное. Это долго, рискованно и вы можете повторить судьбу Котобы и других деятелей, когда я откажусь ставить явно экспериментальную сборку наживую, оно повисит на тестовой доске с годик и сдохнет под весом нехватки фич.>Например для modDelete пусть оно в один запрос получает board_id соответствующий board_name и проверяет права модератора для этого board_id, буде тот найден.Чтобы потом копировать ту же самую проверку для всех других мод-действий? No. Проверка и синхронизация прав должна быть строго в одном месте с точки зрения кода, но в одной транзакции с точки зрения БД.>Статусы соответствуют HTTP кодам.No. Зачем эта головная боль и велосипеды? Что должен возвращать CreatePost: 200, 201, 204, 205, 302, 303? А если пост не удалось отправить потому что тред залочен, это 304, 401, 403, 405, 409? 415 на попытку отправить непонятный файл? А может проще ошибку выкинуть или вернуть 0? В целом, это имеет смысл делать только когда мы уже полностью переходим на апи вместо board.php. И то ошибки проще для 4xx-5xx.>Не думаю пока, что где-то есть смысл делать отдельные View классы.У нас уже есть два эндпойнта для рендеринга отдельных постов, так что есть. Даже если вы выкините весь рендеринг в бд, всё ещё есть превью поста в постформе.>>1000752>poster_ipsНормализация, допустим. А смысл в двух строках статистики и привязке к доске?>_checked_atЗачем? Уж тем более не надо на все вариации.>thread_Если мы нормализуем ip, нормализуйте и тред.>attachment_*Если мы нормализуем ip, нормализуйте и файлы. Уж тем более когда их столько строк, мы позволяем постить файл в несколько тредов и это даёт мультиаттач в будущем. Количество стримов и кейфрейм выглядят ерундой, кейфреймы могут меняться а видеть больше одного стрима в проигрывателе всё равно нельзя. Флаги что метаданные вообще в изображении есть не дают ничего после процессинга: конечный пользователь будет хотеть смотреть всю метадату, если захочет, и там уже может методом тыка определить. extension обязано совпадать с именем файла. Вместо thumbnail_extension должно быть имя тумбнейла, потому что статичные превью или мультиспойлер со стороны сервера если мы когда-либо решим быть менее завязаны на жс.>индексыНет индекса по lasthit и posted_at. lasthit и в самой таблице нет, впрочем.>can_beВо-первых, я не знаю формата анимации который не может быть статикой. Во-вторых, я не вижу, что даёт нам can_be_lossy/lossless что мы не будем скорее всего спрашивать у анализатора в любом случае. Зачем там parent?>show_poster_ip_hashesЗначит нам надо их тоже хранить.Индекс на блоттере имо переоценён.>faptchasТри айдишника явно перебор.>posting_statsЗачем и айпишник в inet инвалидирует любые попытки его скрыть.>а значит фильтровать IP'шники в PHP, а не в базе.Возможно имеет смысл хранить шифрованные подсети или просто класть айпишники в другую БД которая точно не может всплыть без доступа к серверу.>>1000753Если мы не даём постить с мылом, то нет смысла мыло хранить. Брать board_id из функции под названием ban_warning_check абсолютно по-дурацки выглядит, название надо сменить. Хотя бы потому что проверка должна проверять что доска вообще существует в том числе. Необходимость инстациировать Action чтобы затем делать ->perform выглядит глупо, особенно если мы дважды передаём db_conn. Алсо, почему ban_check_result не 100?>>1000754>компилировать собственную версию пыхаHa-ha, no.Отвлекаясь от: если мы таки будем делать возможность убирать метеданные, то это же меняет чексумму файла. Так что тем более phash было бы неплохо.
>Статусы соответствуют HTTP котамhttp://http.cat/
>>1000751> Это-то я помню. Мне про https://410chan.org/dev/res/26066.html#28190 было интересно.Нет, это не я. Но руководство по установке хорошо бы сделать.> Давайте тогда обсудим архитектуру. Не могу сказать, что она у меня в каком-то чётко оформленном состоянии в голове. Но попробую описать, как мне пока видится.Ох, мудрёные вещи вы предлагаете, сестрица. И не совсем понятно мне, как оно связано со списком >>/b/1006573.Пока предлагаю скоординироваться по списку задач, кто чем будет заниматься в какую очередь. Поразбираться с phash и окружающей математикой мне было бы интересно.Я ещё думаю, что нужно запилить хотя бы минимальные тесты. Чтобы была уверенность при переходе на новые версии и вещей вроде включения strict_types (с ними есть проблемы, но код, который им подвержен, на 0141 не выполняется, насколько я понимаю).Кроме того, мною найдено несколько проблем, о которых сообщу позже.
>>1000760>о которых сообщу позже
>>1000756> Вы предлагаете абсолютно полностью переписать всю логикуYou are quite right.По большей части, у меня логика та и так по ходу дела частенько оказывается переписанной, как в случае с удалением и репортами на Автобусе вот. Или как в случае с парсером, от которого Кусабового осталось шиш да маленька.> Зачем эта головная боль и велосипеды?Чуть более дескриптивный, чем either ok or not, статус; полезно, если есть нужда как-то обработать результат с ошибкой или этой ошибки последствия. Ещё с этим статусом результат можно сразу деть в качестве ответа клиенту.> Что должен возвращать CreatePost: 200, 201, 204, 205, 302, 303? А если пост не удалось отправить потому что тред залочен, это 304, 401, 403, 405, 409? 415 на попытку отправить непонятный файл? А может проще ошибку выкинуть или вернуть 0?Можно пока остановиться на subset'е из 200, 400, 401, 403, 404 и 500. CreatePost пуст возвращает 200, а если тред залочен и у пользователя нету прав на постинг в залоченном — 403, несъедобный файл — 400.А так, писать в документации к конкретным Action'ам, что оно возвращает и когда.> В целом, это имеет смысл делать только когда мы уже полностью переходим на апи вместо board.phpУ нас на постинг и так уже квази-API через fetch-call костыль, где exitWithErrorPage возвращает отнюдь не Page. А можно сдедующий major version сделать сразу и по-хорошему, раз кучу всего менять. Но пока без разбивки board.php на отдельные endpoint'ы, чтобы минимальные изменения в kusaba.js и чтобы не сломать Overchan/Куклоскрипт.> У нас уже есть два эндпойнта для рендеринга отдельных постов, так что естьread.php, а второй где? Есть показ удалённых тредов, и новости на главной может быть ещё. Только это вот разве что.> всё ещё есть превью поста в постформеIn so far у нас preformat_post про BBCode у Parse, а не про BuildPost у Board. Стало быть, ParseAction, результат которого preformat_post и будет возвращать; не будет такого, что у этого результата бывает несколько разных отображений для передачи обратно клиенту; возможные HTML и JSON разницы не в счёт.> Чтобы потом копировать ту же самую проверку для всех других мод-действий?Чтобы было меньше запросов в базу. Но и правда не очень удобно, пусть будет AuthService, который мод-действия дёргают.> Нормализация, допустимНормализация, но оно не ради нормализации нормализовано, а ради быстрого поиска и удаления по подсети. Уникальных клиентов в /b/ в разы меньше, чем постов. Меньше шифротекстов тянуть из базы.> А смысл в двух строках статистики и привязке к доске?Ныне n уникальных клиентов. Хотя можно отдельный счётчик в boards для того. У Postgres SELECT count(*); — всегда full scan.> *_checked_atВариации в RegenerateThread(s) нужно обновлять в зависимости от posted_at, deleted_at и contents_modified_at у тредовых постов. При выполнении UPDATE запроса для этих вариаций нужно писать/сравнивать то время, которое было на момент проверки, поскольку за время 'рендеринга' что-то могло измениться. Но, наверное, действительно можно ограничиться одним _checked_at.> Если мы нормализуем ip, нормализуйте и тред.> Если мы нормализуем ip, нормализуйте и файлы.Shall we? IP ведь нормализовано только ради быстрого поиска по подсети.ЕМНИП, в некогда деланых мною бенчмарках уже два JOIN'а по foreign key в 10 раз замедляли SELECT'ы по сравнению с wide table и null столбцами. Although, в wide table где-то в 2 раза медленнее UPDATE'ы и INSERT'ы. Так что удобства/скорости ради я хотел бы в целом оставить wide table.Мультиатач хороший аргумент нормализовать attachments вовне, но me thinks когда решим запиливать, чтобы attachment'ы по нескольку штук на пост, тогда уже и делать отдельную таблицу.> Нет индекса по lasthit и posted_atМне тут подумалось, а нужен ли общий index по posted_at? Посты в тредах отображаются в порядке id, а не в порядке posted_at. bumped_at, когда не все посты с сажей, можно также считать по where id = max(id) без сажи. RSS тоже по ID тогда, если оно сейчас нет.Now that I look at it, забыл,CREATE INDEX ON posts (board_id, thread_is_stickied, bumped_at) INCLUDING (thread_id, post_id) WHERE thread_id = post_idДля тредов.>attachment_*> Количество стримов и кейфрейм выглядит ерундойКоличество стримов, наверное, да, уберу. Хотя для модерации могло бы оказаться полезно. А вот keyframe, позвольте не согласиться. Для статичных изборажений keyframe — это само изображение и есть, а для видео ширину, высоту мы только для первого keyframe'а reliably посчитать и можем, если по-быстрому. phash тоже только для первого kf. У других кадров ширина и высота формально может отличаться. В интернете такие видео — скорее редкость, чем нет, но браузерами они поддерживаются. Помню, Мицгол как-то запостил проигрывающийся экземпляр.> конечный пользователь будет хотеть смотреть всю метадату, если захочет, и там уже может методом тыка определитьМне, как конечному пользователю, будет приятно понимать, есть ли у картинки следующие метаданные: EXIF, ICC (не очень-то и мета) и XMP. C php-vips это проверяется просто. Учитывая любовь Булочек к reencodes (и что circa все из них забывают -metadata all), полезно для сравнения, дубликаты перед тобой/ты постишь или нет.> extension обязано совпадать с именем файла. Вместо thumbnail_extension должно быть имя тумбнейлаЗачем отдельно хранить имя тумбнейла? В текущем FBE cобственных thumbnail'ов у одного файла в FBE бывает несколько: один с 'c', другой с 's', но в целом их имя соответствует столбцу `filename`, который на самом деле basename и который хранится без extension'а. Extension к файлу (не к тумбнейлу) хранится в столбце `filetype`. У приложенных архивов и других вещей со статичным thumbnail'ом своих thumbnail-ов не бывает и определяется он в BuildPost. > Во-первых, я не знаю формата анимации который не может быть статикойМожно сделать is_video, is_picture и can_be_animated_picture, но количество столбцов будет то же.> Во-вторых, я не вижу, что даёт нам can_be_lossy/lossless такого, что мы не будем скорее всего спрашивать у анализатора в любом случаеОно чтобы для JPEGов не выводить lossy, а для PNG lossless, ибо из формата и так понятно. Также для того, чтобы не выводить lossy/lossless информацию для тех форматов, где анализатор понять lossy/lossless не может.> Зачем там parent?Затем, что у нас есть расширение jpg, у которого несколько алиасов. Указать свойства только для jpg, и указать его parent'ом к jpeg и jfif.> show_poster_ip_hashes> значит нам надо их тоже хранить.Не обязательно. Во-первых, можно IPшник дешифровать и взять от него хэш. Во-вторых можно ничего не дешифровывать и взять хэш прямо от шифротекста или от UUID-ключа: оно для досок вроде 410chan'ового /d/. Можно назвать show_poster_ids или enable_poster_ids.> faptchas> Три айдишника явно переборIDшника там два: ID класса фапчи (кулинарная, как в /cu/, обычная, как в /b/ и /a/) и ID, собственно, капчи. class_name — текстовое имя для человека. On the other hand, может ли одна фапча быть сразу в нескольких классах? Наверное, может. Значит, разбивать на несколько таблиц indeed.>posting_stats> Зачем и айпишник в inet инвалидирует любые попытки его скрыть.In so far me thinks that IPv6 вынуждает делать контроль за частотой постинга/выдачей капчесессий по подсетям, а фильтровать шифрованные IP — тянуть все их из таблицы, если дополнительно не хранить хэши подсетей.Пока я решил, что раз faptcha_attempts прямо сейчас тоже хранит IP в plain text'е, если я тоже их временно в plain text'е буду хранить, будет по крайней мере не хуже, чем в оригинале.Но плоховасто, да. Пока не вижу, как сделать по-хорошему. On the other hand, так ли страшно ходить foreach'ем по всем шифрованным за последний час IP? Не то, чтобы их очень уж много. Особенно если не по всем, а у которых совпадает хэш /64 подсети. Тем не менее, видится уязвимым для DoS и в какой-то степени для timing oracle атаки.Поскольку оно целиком рантаймовое, вот это вот точно стоило бы вынести в какую-нибудь (отдельную) in-memory DB. Но как-нибудь в другой раз.
SELECT count(*);
CREATE INDEX ON posts (board_id, thread_is_stickied, bumped_at) INCLUDING (thread_id, post_id) WHERE thread_id = post_id
>>1000762>А так, писать в документации к конкретным Action'ам, что оно возвращает и когда.Тогда уж https://www.php.net/manual/en/language.types.enumerations.php>У Postgres SELECT count(*); — всегда full scan.IASM_pg_db=# EXPLAIN ANALYZE SELECT COUNT(*) FROM board_b.comments; QUERY PLAN--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Finalize Aggregate (cost=34033.26..34033.27 rows=1 width=8) (actual time=75.114..81.980 rows=1.00 loops=1) Buffers: shared hit=47966 -> Gather (cost=34033.04..34033.25 rows=2 width=8) (actual time=74.874..81.973 rows=3.00 loops=1) Workers Planned: 2 Workers Launched: 2 Buffers: shared hit=47966 -> Partial Aggregate (cost=33033.04..33033.05 rows=1 width=8) (actual time=70.871..70.877 rows=1.00 loops=3) Buffers: shared hit=47966 -> Parallel Index Only Scan using thread_re_indx on comments (cost=0.43..30978.87 rows=821668 width=0) (actual time=0.164..45.123 rows=649968.00 loops=3) Heap Fetches: 57416 Index Searches: 1 Buffers: shared hit=47966 Planning: Buffers: shared hit=1 Planning Time: 0.091 ms Execution Time: 82.235 ms(16 rows)IASM_pg_db=# \d board_b.thread_re_indx Index "board_b.thread_re_indx" Column | Type | Key? | Definition--------+---------+------+------------ parent | integer | yes | parentbtree, for table "board_b.comments"~2m записей btw. Ну да записью проще. Но это не "Ныне n уникальных клиентов", это "этот постер запостил N постов и удалены". За пределами мод-интроспекции это не нужно, а когда нужно оно будет использовать индекс.>Посты в тредах отображаются в порядке id, а не в порядке posted_atНасколько я помню все тайм парадоксы, наоборот. Плюс в нашем случае с импортом тем более.> у которого несколько алиасовТогда лучше список алиасов, потому что быстро возникает вопрос что делать с парентом парента и как всё наследуется/перебивается.>IDшника там дваА, я неправильно понял суть. Хмм. Так-то простоты ради можно не пихать это в БД а оставить парсер того текстофайла что есть и просто его переписывать, проблемы только с flock() чтобы двое его в одно время не редактировали.
IASM_pg_db=# EXPLAIN ANALYZE SELECT COUNT(*) FROM board_b.comments; QUERY PLAN--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Finalize Aggregate (cost=34033.26..34033.27 rows=1 width=8) (actual time=75.114..81.980 rows=1.00 loops=1) Buffers: shared hit=47966 -> Gather (cost=34033.04..34033.25 rows=2 width=8) (actual time=74.874..81.973 rows=3.00 loops=1) Workers Planned: 2 Workers Launched: 2 Buffers: shared hit=47966 -> Partial Aggregate (cost=33033.04..33033.05 rows=1 width=8) (actual time=70.871..70.877 rows=1.00 loops=3) Buffers: shared hit=47966 -> Parallel Index Only Scan using thread_re_indx on comments (cost=0.43..30978.87 rows=821668 width=0) (actual time=0.164..45.123 rows=649968.00 loops=3) Heap Fetches: 57416 Index Searches: 1 Buffers: shared hit=47966 Planning: Buffers: shared hit=1 Planning Time: 0.091 ms Execution Time: 82.235 ms(16 rows)IASM_pg_db=# \d board_b.thread_re_indx Index "board_b.thread_re_indx" Column | Type | Key? | Definition--------+---------+------+------------ parent | integer | yes | parentbtree, for table "board_b.comments"
>>1000756> Это долго, рискованно и вы можете повторить судьбу КотобыЕсли продолжу и основательно за это возьмусь, к концу августа что-то вполне может быть готово, думаю. Тем более если писать это буду не я один, сразу довольно чётко определив внутренний API и погодя обвешивая тестами.Другое дело, будет ли у вас желание приложить effort за-try'ить получившееся?> сдохнет под весом нехватки фичПусть те же фичи, которые сейчас есть, будут воспроизведены? Except watchedthreads и текстодоски как отдельный board type; и надо будет разобраться, как работать oekaki постам — по-хорошему, устроить бы это так, чтобы шло одним одним запросом к board.php, а не выхватывалось из KU_CGIDIR . '/kusabaoek'.Разбить на Action'ы/Service'ы проверку модправ, капчей, постинг, удаление, репорты, архивацию и генерацию страниц, сделав board.php простым endpoint-ом–оркестратором и расчленив распиханное по разным файлам на роли. Структуру manage.class.php at large не трогать, делая изменения лишь в существующих therein функциях. board.php по отдельным endpoint'ом пока не разносить, either, чтобы Кукла/Overchan не сломались и HTML не править, хотя погодя его выносить в шаблоны where applicable/makes sence as per #3.Отчасти я хочу сделать это потому, чтобы код для конкретного сложного действия и релевантных им функций был +- в одном месте.Так логика удаления сейчас разнесена по board.php, board-post.class.php и manage.class.php. Пусть board.php и manage.class.php занимаются +- только загоном $_POST или JSON в DeleteAction, где будет вся логика касательно удаления, и пусть они дальше pipe'ят результат в BanService и в BoardPageGenerator, если надо. Висящее в public изменение про удаление для Автобуса как-то так и работает, although физически находится в Board классе и вызывает регенерацию страниц само—без middling'а со стороны board.php.BTW, удаление постов и репорты ныне в board-post.class.php, но не создание, которое в board.php и posting.class.php.Отчасти ещё потому, что для report'ов незачем инициализировать необходимые для бордогенерации вещи, такие как постформу или набор спойлеров; для удаления, пока оно не завершилось успешно, также незачем инициализировать генератор страниц и так далее. Although, как я понимаю, в ORMах оно решается lazy load'ом, но это дробить на мелкие запросы то, что можно сразу и меньшим их количеством. Ну и лично мне кажется удобней инкапсуляция по действиям, чем по entities.BanWarningCheck был с той идеей, что вместе с board_id можно сразу выхватить enable_warnings, и проверять наличие/выхватывать информацию о warning'е к борде, если enable_warnings и не был найден бан. Что можно одним запросом по большому счёту.> мы дважды передаём db_connВ черновике ошибка.> Необходимость инстациировать Action чтобы затем делать ->perform выглядит глупоТам, где инстанциация would do nothing other than приёмку $db_conn, делать perform функцию static и принимающей $db_conn?>>1000763> Посты в тредах отображаются в порядке id, а не в порядке posted_at> Насколько я помню все тайм парадоксы, наоборот. Плюс в нашем случае с импортом тем более.OK, будет добавлено. В manage.class.php есть показ последних изображений и статистика, где это надо. Although, судя по порядку >>/b/24480 и >>/b/1000046 оно именно в порядке id, а не posted_at.> Но это не "Ныне n уникальных клиентов", это "этот постер запостил N постов и удалены"Если не делать борде счётчик, то можно SELECT count(*) FROM poster_ips WHERE board_id = $1 AND rows_count - deleted_posts_count > 0 же. Вполне может оказаться на порядок быстрее, чем SELECT count(DISTINCT poster_ip_id) WHERE board_id = $1 AND NOT is_deleted AND poster_ip_id IS NOT NULL.
SELECT count(*) FROM poster_ips WHERE board_id = $1 AND rows_count - deleted_posts_count > 0
SELECT count(DISTINCT poster_ip_id) WHERE board_id = $1 AND NOT is_deleted AND poster_ip_id IS NOT NULL
>>1000765> SELECT count(DISTINCT poster_ip_id) WHERE board_id = $1 AND NOT is_deleted AND poster_ip_id IS NOT NULLИли точнее> SELECT count(DISTINCT poster_ip_id) WHERE board_id = $1 AND deleted_at IS NOT NULL AND poster_ip_id IS NOT NULLЦк.>>1000763> Тогда лучше список алиасов, потому что быстро возникает вопрос что делать с парентом парента и как всё наследуется/перебивается.OK.> Так-то простоты ради можно не пихать это в БД а оставить парсер того текстофайла что есть и просто его переписывать, проблемы только с flock() чтобы двое его в одно время не редактировали.Мне свиду кажется, что тут проще переписать, поскольку я хочу ограничить максимальное количество капчесессий на IP/подсеть, чтобы не давать пользователю генерировать тьму валидных капчесессий, что сейчас seems very doable, а стало быть там надо многое менять. В черновике схемы забыто enable_adaptive_faptcha в boards таблице.Надо выкладывать новый черновик/скетч.
SELECT count(DISTINCT poster_ip_id) WHERE board_id = $1 AND deleted_at IS NOT NULL AND poster_ip_id IS NOT NULL
>>1000764>is_deletedЖелательно тоже в индексы добавить, как минимум на отрисовку/подбор сколько постов в треде это будет всегда запрашиваться.>rows_count - deleted_posts_countНе проще ль сразу декрементировать rows_count.>Отчасти я хочу сделать это потому, чтобы код для конкретного сложного действия и релевантных им функций был +- в одном месте.Для этого не обязательно уходить от объектности, для этого достаточно причесать тот зоопарк что у нас есть сейчас. Более того, вам тогда тем более нужны View, чтобы объединить различные виды отображения поста в одном месте - по крайней мере пока мы не скинем всё в шаблоны. И тем более нужны Model чтобы было приведение типов при разговоре с базой (или даже между сервисом и экшном, как передача всех-всех-всех параметров при обработке файла) и функция конверции размера из битов в читабельное не валялась где-то в utils. В итоге из Action остаётся только manage.php, который уже и так так написан, а остальные приводятся к классам достаточно просто и лишь семантика.>для report'ов незачем инициализировать необходимые для бордогенерации вещи, такие как постформу или набор спойлеровЭто всё же больше проблема как раз портянки board.php и того как мы делаем импорты.>Другое дело, будет ли у вас желание приложить effort за-try'ить получившееся?Явно не в конце августа, спросите в конце сентября.
Да, ящитаю, переписывать нужно. В том, что есть, можно замучаться проблемы исправлять.Переписывать сразу или постепенно?P.S. См. PR на Codeberg. Но вообще там всё плохо.
>>1000769Тогда надобно и имя новое взять.>Оформить как issue?Yes please.
>>1000770> Тогда надобно и имя новое взятьВы о имени движка? Предлагаю, пускай будет Higanbana. Но с именами как-нибудь потом, наверное.>>1000769> Переписывать сразу или постепенноМне думалось, скорее сразу >>1000765, но по частям.> Разбить на Action'ы/Service'ы проверку модправ, капчей, постинг, удаление, репорты, архивацию и генерацию страниц, сделав board.php простым endpoint-ом–оркестратором и расчленив распиханное по разным файлам на роли. Структуру manage.class.php at large не трогать, делая изменения лишь в существующих therein функциях. board.php по отдельным endpoint'ом пока не разносить, either, чтобы Кукла/Overchan не сломались и HTML не править, хотя погодя его выносить в шаблоны where applicable/makes sence as per #3.Потом остальное, although из-за изменения СУБД и схемы всё равно в nigh каждой функции в manage.class.php код хоть, маленько, но менять.Either way, мне кажется, сначала определить внутренний API, какие классы будут, какие у них будут public функции и их поведение. Если не API, то хотя бы схему.> fix using bitwise OR in inappropriate placesВообще, покуда у нас есть assertion, что strlen возвращает неотрицательный int, (strlen($a) | strlen($b)) > N и strlen($a) > N || strlen($b) > N должны быть, пусть не по скорости, идентичны результатом. Первый вариант чуть короче, и поэтому я его написал. Если inappropriate, не буду.
(strlen($a) | strlen($b)) > N
strlen($a) > N || strlen($b) > N
>>100077170 | 9 > 75
>>1000772Мощивакегозаймасен. Видно, оно так работает только при сравнении со степенями двойки.
>>1000760Про phash, вот реализация, на которой я остановился. Та самая, которую я описывал в >>/b/1006362.Помимо вроде ничего не давших попыток сравнивать не с глобальной медианой, а с медианой сегмента/сектора, пробовал ещё считать трёхмерный DCT по saliency zoom cube'у. Оно получается несколько более устойчиво к crop'у и padding'у (например, нашло >>/b/4173 и >>/b/6116 пару), но такие схожести оно объединяет в один кластер на моменте, когда начинает «разваливаться», а частота false positives начинает возрастать при куда меньших дистанциях на ту же длину хэша.В phash.zip.webp также лежит hamming_evo.py. Если скормить ему CSV файл с хэшами, который можно получить с php do_round_phash.php > stats_file_name.csv, сделает директории stats_file_name/hamming_distance/cluster_hash с симлинками на указанные в CSV файлы. do_round_phash.php не рекурсивен.Если делать, чтобы информация про есть/нет похожие файлы шла в статусную строку по выбору файла пользователем, на сервер нужно отправлять phash, а не файл. Стало быть, повторить реализацию в JS. I wonder насколько проблема то, что алгоритм уменьшения изображений и приведение их к оттенкам серого отличается от оного у libvips.https://github.com/libvips/libvips/wiki/HOWTO----Image-shrinkingОчень серьёзное узкое место по скорости — уменьшение картнки. У libvips есть поддержка codec-специфичных оптимизаций, но оно, помнится, не как надо обрабатывало прозрачность у WebP или какие-то ошибки были. Поэтому сейчас оно в do_round_phash, как есть.
php do_round_phash.php > stats_file_name.csv
Ещё денёк поиграюсь с платными ИИшками для поиска проблем, потом буду думать про архитектуру и ковырять phash.Но там и так уже много найдено.Будет «десерт», но он только Соуса заинтересует. Да и вообще хорошо бы было бэкпортировать все исправления в его репу.
>>1000777> [Pr. 2]Скорее всего, задумка была в том, чтобы проверить, CurrentUserIsModeratorOfBoard что-то там.Другое дело, что с удалениями, как я понимаю, тут тоже https://410chan.org/dev/res/26066.html#28080 проблема. На Автобусе решил через https://codeberg.org/FBE410/fbe-410/pulls/62/commits/934f8deb566c139758a5061057870c6a01bd6122 и https://codeberg.org/FBE410/fbe-410/pulls/64/commits/7bdccad9f45014c9ebbeefa164cb4d8453e6472d. Чтобы это сюда вмёржить, надо будет сделать некоторые исправления, поскольку выкинуто логирование, выкинут KU_APC (clearPostCache не только удалял из репортов, но очищал его), выкинут KU_SEARCH, и сажа не в отдельном столбце. Also, я не добавил в BulkDeletePosts проверку access_level у модератора. В board.php её вроде и не было, но если и другие вещи к ней привязывать...> [Pr. 4]Зашифровать ту COOKIE хотя бы Кусабовым md5_encrypt. А по-хорошему, брать openssl и аутентифицированное шифрование для такого.> [Pr. 12]> По идее, проблема с substr должна была проявляться в рантайме. Возможно, я чего-то не понимаю.Если для символа русской кириллицы в UTF-8 всегда два байта и режет substr по чётному числу, то не проявилось поэтому, скорее всего.> [Pr. 19]Тут так и задумано, если я всё верно понимаю.> [Pr. 22]> А ещё оно пытается обратиться к KU_ROOTDIR . $board_from . '/thumb/' . $line['filename'] . 'c.' . $thumb_file_type, которого не существует (я не понимаю, что это за фича вообще)As far as I know, это для просмотра изображений в удалённых тредах. https://410chan.org/b/thumb/178043101649c.png На 410 есть. А тут почему их ныне нет, не помню или не знаю.
Also, $board_from не обязательно в KU_ROOTDIR, но обаязательно в KU_BOARDSDIR.
Или даже не так, потому что loadbalancer.
>>1000778c это для каталога, выпилено здесь потому что пикрелейтед.
>>1000769PR merged.>Pr. 2Как сказано, проще просто переписать всё через bulkdelete.>Pr. 3Low priority. Если включено KU_DBUSEPERSISTENT то вообще ничего не будет работать, потому что PConnect не объявлено. Если не включено, то вывалиться с одной ошибкой или другой вопрос сугубо красивости.>Pr. 4Low priority. В там нет никакой интересной информации.>Pr. 5Да, в сейчас оно строка и в БД как строка. По идее пра запихивании в БД только должно стать строкой.>Pr. 6Low priority. Капча и так чтобы отвадить совсем уж хлебушков.>Pr. 7random_appeal использовался на Автобусе в первый год регулярных обращений, когда их было два или три, по одному из членов Супермаркета. В общем кода нафиг никому не сдался, да.>Pr. 9Должны. Но раз никто не спохватился, то пока хорошо. Удаляет один потому что Report() позволяет только один репорт на пост. Так что в запросе проблем нет.>PR. 10Вообще, звучит как хорошее, я не вижу причины НЕ делать глобалбан временным.>Pr. 11Проблемы пхпверсии, лол. 8.2.0 Case conversion no longer depends on the locale set with setlocale(). Only ASCII characters will be converted.>Pr. 19Not-a-bug>Pr. 21Low priority, текстодоска>Pr. 22Low priority. Там всё равно переписывать на выборочный перенос постов в том числе, и у нас всего полторы доски.>Pr. 24Я более чем уверен что поиск по БД даст гораздо больше информации чем сравнение в рантайме в этом случае. Low priority.>Pr. 30expand за пределами индекса никогда не вызывается, так что да.>Pr. 37Хм, и правда. Поправлено в PR>Pr. 41Low priority, текстодоска.
> [Pr. 41]> что такое relative_idВ тредах на текстодосках ссылки в тредах считаются не по глобальному ID, а по номеру поста в треде.> возможно, во все запросы, где он вычисляется (read.php, inc/classes/parse.class.php), нужно дописать AND NOT IS_DELETEDИе. Удалённые посты остаются и учитываются в относительной нумерации.Как по мне, текстодоски проще выпилить для дальнейших изменений и вместо этого определить их, как имиджборды, где нельзя прикладывать файлы.
>>/b/1004863Другое дело, что я не понял, зачем %2C остаётся %2C в prettify_url, и теперь оно декодирует все printable ASCII символы, too.https://datatracker.ietf.org/doc/html/rfc3986#section-2.2Снова убрать gen-delims и sub-delims, как декодируемые. Но хотя бы для тильды оставить percent декодирование.Есть ли смысл как-нибудь когда-нибудь задуматься об умном алгоритме parsing'а ссылок в человекочитаемое, который будет понимать, где, скажем, %2C можно декодировать как ',', а где %2C sub-delim и смысл поменяется?
>>1000782> [Pr. 24]> Я более чем уверен что поиск по БД даст гораздо больше информации чем сравнение в рантайме в этом случае.??Теоретически, неконстантное по времени сравнение строк может позволить после (большого) количества запросов восстановить часть password_hash у интересующего поста, тем более если хэш несолёный md5. Что можно использовать для идентификации пользователей. Насколько оно на практике опасно, не знаю.Проблема в том, что в Postgres, и даже в pg_crypto, не завезли функции для constant time сравнения. А значит, это тянуть хэши паролей из базы при bulk deletion, если делать сравнение time constant. Что, с другой стороны, наверное, не так уж и тяжко, если захардкодить максимальное количество постов на удаление по паролю за одно обращение к board.php, скажем, на 256.
> [Pr. 43]> Удаление тредов не атомарно, если новый пост появится во время удаления, будет плохо. Удаление отдельных постов по какой-то причине сделано атомарно (begin_transaction/commit).Мне кажется, для обеспечения consistency простых transaction'ов без select for update, lock'ов или дополнительных проверок, тут оказывается недостаточно.Time 00: Connection D начинает транзакцию;Time 01: Connection P начинает транзакцию;Time 02: Connection D select'ит информацию о треде и его дочерних постах, чтобы потом удалить;Time 03: Connection P select'ит информацию о неудалённом треде;Time 04: Connection D update'ит дочерние посты и тред, ставит ему IS_DELETED=1;Time 05: Connection D завершает транзакцию;Time 06: Connection P insert'ит новый пост;Time 05: Connection P завершает транзакцию;Итог: orhpan reply, и никто никого не блокирует.
> На 32-bit PHP все выражения (1 << self::SShift) в файле будут нулёмКорректностью работы на старше 410'ого железе и роботах-пылесосах можно принебречь, думаю. И то на всяких роботах-пылесосах уже 10 лет как aarch64, поди.> ($a & $b > $c) <==> ($a & ($b > $c))PHP не без сюрпризов, однако.
Also, спасибо за труд.
>Pr. 44No.> php -E '$htmlfile = "/res/2.html"; echo(substr(basename($htmlfile), 0, strpos(basename($htmlfile), ".html")));2list это текстодоска, low priority.>Pr. 44Medium priority, сейчас балансер конечно есть, но сделан в обход этой функциональности.>Pr. 55Технически, оно работает, потому что видимость итератора в пределах функции, а не цикла.>>1000787поэтому давайте выносить можно просто Constraint повесить на таблицу.Но в целом, в орфане ничего плохого нет, он просто не будет отображаться, емнип.
> php -E '$htmlfile = "/res/2.html"; echo(substr(basename($htmlfile), 0, strpos(basename($htmlfile), ".html")));2
>>1000789My pleasure.>>1000790> Технически, оно работает, потому что видимость итератора в пределах функции, а не цикла.А если $results не пустой, но цикл ничего не нашёл, то в $line будет последняя строка, а не false/null.
>>1000791>А если $results не пустой, но цикл ничего не нашёлТо выполняется условие ! $warning_record и функция завершается раньше.
! $warning_record
Yatta!
>PR. 57Работает не трогай. Плюс страницы с 0-индекса.>PR. 63Не работает, но в 8.4 без этого не работает.
Ну что, предлагаю поправить известные проблемы (и #9 — там много что нужно поправить в модерке) прежде, чем приступать к переписыванию.(А ещё там любой персонал может делать, по сути, что угодно. И это уже сложно исправить. Неактуально для нас?)По поводу переписывания, исходя из того, что оно будет по частям, предлагаю обсудить следующее:1. Нужен ли фреймворк? Можно постепенно переписывать фичи с использованием фреймворка.2. Какие библиотеки брать для (если без фреймворка или фреймворк не предоставляет такой функциональности):ORM или что-то подобное?Routing (можно и без библиотек)?Templating (сейчас используется Smarty вперемешку с конкатенацией строк)?Логирование?Тестирование (см. ниже)?Аутентификация и шифрование?3. Какая будет общая архитектура (MVC?) и структура кода?4. Нужно ли покрыть хоть какую-то функциональность тестами перед переписыванием?
>>1000795In my не humble opinion,> Нужен ли фреймворк?> ORM или что-то подобное?Предлагаю, давайте без Laravel'ов и Autowired магии через атрибуты с Exception stack'ами на 40 строк. Тем более, что у нас не простой CRUD.> Templating (сейчас используется Smarty вперемешку с конкатенацией строк)?Smarty. Или Twig или что-то ещё, где есть extension-интеграция с gettext.> Аутентификация и шифрование?Для шифрования и хэширования паролей обычные PHPшные функции и default'ный openssl extension у PHP. Для паролей hash_hmac('sha256') или openssl_pbkdf2 с SHA-256 на небольшое количество итераций, чтобы серверу было не сильно тяжело. Для шифрования IP в базе AES-256-SIV либо AES-256-GCM с hash_hmac blind index'ом, для шифрования печенек where applicable AES-256-GCM. Автентикация staff'а вытягиванием строки из staff таблицы. Для экспирации staff сессии, хранить её в cookie шифрованной с timestamp'ом.Для phash, наверное, PostgreSQL'у взять pgvector extension (https://github.com/pgvector/pgvector) для HNSW индекса для быстрого approximate поиска по расстоянию Хэмминга.Для phash и картинкофайлов libvips (php-vips), для нестатичных изображений и видео ffmpeg (брать ли обёртку? Популярная/поддерживаемая всё равно парсит консоль, а не вокруг libavcodec) для анимации/видео, file (https://www.darwinsys.com/file/, php-я-не-знаю) для проверок соответствия extension'а содержимому.> Какая будет общая архитектура (MVC?) и структура кода?Мои мысли насчёт описаны в >>1000751 и далее; что-то ближе к transaction scripts, SOA/modular monolith, но местами несколько более tightly coupled. Предлагайте, спрашивайте, критикуйте.Предложите своё и вы!
Надо добавить adaptive_config.php в .gitignore.
adaptive_config.php
.gitignore
>>1000798What for?>>1000796> file (https://www.darwinsys.com/file/, php-я-не-знаюlibmagic?https://www.php.net/manual/en/fileinfo.installation.phpНу как бы этот file, на который вы ссылаетесь, использует libmagic.> Предложите своё и вы!Ох, не ведаю я про всякие библиотеки для PHP и современные best practices. Поэтому просто доверюсь вам! Выбор основных компонент звучит разумно.>>1000751> Мне кажется, здесь оказывается довольно уместной инкапсуляция по действиям, нежели по entities. То бишь, что-то ближе к transaction scripts. Скажем, DeletePostAction с функциями для удаления постов, CreatePostAction, ReportPostAction, ArchiveThreadAction, GenerateBoardPagesAction, и так далее. Для какой-нибудь entity или набора action'ов, где разделение на разные файлы излишне — какой-нибудь общий Service или Controller класс.Хм, а зачем выносить их в классы? Это ведь могут быть просто функции.Какое внутреннее состояние будет у подобного класса? $tc_db и $smarty? Это должны быть если не глобальные переменные, то статические переменные одного-единственного класса.В PHP какие-то проблемы с глобальными переменными?А классы-entities-то нужны. Board, Post, User.> Выполнение Action'а возвращает (анонимный) класс со статусом и результатами.А почему анонимный?> Статусы соответствуют HTTP кодам. If applicable, должна быть функция, которая сообразно его типу этот результат в HTML или в JSON и выходит с ним.Это нужно будет создавать XxxResultToHtml()/XxxResultToJson()-двойники всех Action-классов?Но зачем, если функция/метод может сразу возвращать результат, который может быть HttpTemplateResponse/JsonResponse/Redirect/…?> Пока это без особой мороки или overhead'а, если Action затрагивает несколько entities/таблиц, тянуть (lateral) join'ами информацию сразу с нескольких одним запросом. Например для modDelete пусть оно в один запрос получает board_id соответствующий board_name и проверяет права модератора для этого board_id, буде тот найден. Для CreatePostAction, пусть оно вместе с board_id, если enable_attachments, тянет строку про файлоформат для приложенного файла, а также thumbnail_width и thumbnail_height из таблицы-синглтона settings. Этот контекст пусть передаётся AttachmentService'у или Handler'у.Нам точно нужны подобные оптимизации?>>1000775> Стало быть, повторить реализацию в JS. I wonder насколько проблема то, что алгоритм уменьшения изображений и приведение их к оттенкам серого отличается от оного у libvips.> Очень серьёзное узкое место по скорости — уменьшение картнки. У libvips есть поддержка codec-специфичных оптимизаций, но оно, помнится, не как надо обрабатывало прозрачность у WebP или какие-то ошибки были. Поэтому сейчас оно в do_round_phash, как есть.А нельзя просто скомпилировать phash и cIMG в WebAssembly и использовать его и с клиента, и с сервера?Если нельзя, можно переписать. Если я правильно понимаю, пайплайн такой: выбираем из изображения только luma-компонент → 7x7 convolution → nearest-neighbor interpolation (!) → 1000 строк в pHash на (два алгоритма + много левой лабуды).Другое дело, что поддерживать кто-то это должен будет.
>>1000795>Неактуально для насПри текущей скорости нет, но апрстрим (Соус) запрашивал.1.При том что это удобно, если мы будем пытаться придерживаться текущих идей, то чем меньше зависимостей, тем лучше.2. ORM без разбивки постов по доскам имеет больше смысла... но у нас не так много моделей. Routing без фреймворка смысла пожалуй мало, и это будет ломать куклу. Шаблоны все на смарти было бы неплохо. Логи... в принципе текущих должно хватать в 90% ситуаций.3. See >>10007674. Перед? Не обязательно. По крайней мере для здесь.>>1000798На самом деле, не вижу, что бы там такого могло слиться.
Лучи добра хорошему Зою с хорошим пингом! Спасибо Якуй-сан! Ура~!
>>1000799> libmagic?Да, I guess.> Хм, а зачем выносить их в классы? Это ведь могут быть просто функции.Для инкапсуляции действия и его вариаций в один класс-сервис: чтобы функции, которым можно с этим сервисом общаться, были public, а его внутренние функции и определения были private.> Какое внутреннее состояние будет у подобного класса?В основном, похоже, stateless. Или state, определяемый единожды при создании. В целом, stateless натура PHP не способствует statefull вещам.Скажем, пусть у условного BoardPageGenerator будет поле, куда при инстанциации пишется постформа для страниц досок и тредов. Или вот postbox. Конечно, после создания instance, например, количество unique user posts может поменяться, но поскольку по завершению действия, влияющего на это количество, следом должен создаваться/зваться генератор, страницы те не могут не оказаться eventually consistent.> $tc_db и $smarty? Это должны быть если не глобальные переменные, то статические переменные одного-единственного класса.https://codeberg.org/yakui-lover/fbe-410/issues/3> Никаких глобальных переменных. Синглетон или передача на этапе инициализации класса.Якуй определил такой guideline и переделал $tc_db согласно ему. Поэтому и я в своём скетче не стал менять этот аспект, но вернуть $tc_db на global или сделать статикой/singleton'ом я не против.A speculation, но передача connection'а классу может немного упростить тестирование или облегчить переход на connection с другими креденциалами в зависимости от полномочий пользователя: чтобы если делать persistence модераторские действия не ждали транзакции обычных пользователей.> создавать XxxResultToHtml()/XxxResultToJson()-двойники всех Action-классов?> А почему анонимный?Чтобы в простых случаях — скажем, массив IDшников проверить на валидное количество и формат — (в случае инвалидности) унасладовать от интерфейса или там AbstractResult, где to_html(), to_json() и emit_and_exit(), и in place определить классу только to_html() и to_json(), или даже только $status и только $description.> Но зачем, если функция/метод может сразу возвращать результат, который может быть HttpTemplateResponse/JsonResponse/RedirectЧтобы иметь возможность этот результат скормить другому Action или Service классу или что-то делать, в зависимости от того, каков этот резульутат. Пусть DeletePostsAction в своём успешном результате всегда хранит IDшнкики тредов, где были изменения, и IDшники тредов, которые оказались удалены полностью. Чтобы штука, которая DeletePostsAction вызвала, могла скормить result с этими ID'шниками BoardPageGenerator'у, но по успеху вывести результат DeletePostsAction. Или чтобы эти результаты можно было гибким образом компановать: при удалении с нескольких досок по IP в модерке, например.> Нам точно нужны подобные оптимизации?AuthService, indeed, пусть будет отдельным AuthService. То же с капчей/фапчей. А так, пока это без особой мороки и overhead'а, думаю, для, например, posting'а релевантные глобальные параметры, параметры доски и (опционально) параметры файлоформата можно одним запросом в базу без особой мороки, получая некий posting context, специфичный для действия: незачем тянуть всю информацию о борде, строить для неё постформу и прочее, пока создание поста не завершилось успехом. А для регенерации страниц не нужно тянуть banned_urls, banned_hashes и word_filter.> А классы-entities-то нужны. Board, PostНаиболее полным образом select'ит содержимое таблицы posts генерация треда и постов в нём. И то там никогда не нужны хэши файла/пароля. А для остального целиковая Post entity не нужна совсем, только некоторые её части. Возникает она разве что только при Insert'е нового поста — и то с некоторым количеством полей, поставленным в NULL.Board entity нужна целиком для отображения и редактирования в модерке, но для этого, как и сейчас, можно обойтись ассоциативными массивами, или анонимными классами.Есть и правда некая проблема с приведением типов.PDO обращается с bytea, как с blob'ами, и либо делать select encode(ip_aes, 'hex') и его hex2bin'ить, либо читать LBOs. По моим тестам, первый вариант где-то на 20% быстрее второго. На 50% быстрее LBO использовать pg и делать pg_unescape_bytea, но pg не умеет автоматически конвертировать Postgres'ный 'T'/'F' boolean в PHP'шный boolean, а стобцов-флагов у нас не мало. Как я понимаю, ни PDO, ни pg не умеют конвертировать Postgres'ные массивы ни туда, ни обратно. Такие вот Драйвера Базы Данных, хех. Есть ли быстрая библиотека-конвертер для таких массивов, I wonder.Делать ли в связи с этим отдельные Model классы? Как альтернатива, можно при чтении из базы столбцы, для которых необходимо декодирование, select'ить с hex или flattened суффиксом или преффиксом i.e.SELECT encode(bans.ip_subnet_aes, 'hex') AS ban_ip_subnet_aes_hex, ARRAY(SELECT boards.name FROM boards WHERE boards.id = ANY(bans.boards)) AS ban_board_names_flattened, ...FROM bansWHERE ...И сразу после запроса или по надобности в полученный с fetch_all ассоциативный массив добавлять ban_ip_subnet_aes (или даже сразу ban_ip_subnet) и ban_board_names.Вижу смысл, чтобы AuthService в результате успешной автентикации возвращал AuthContext, где помимо is_suspened, type и access_level будут can_create_boards(), can_delete_locked_threads(), can_post_in_locked_board(), can_skip_captcha() и другие функции расшифровки staff.type и staff.access_level. On the other hand, can_delete_locked_threads() же не на всех бордах, а только на тех, которые staff модерирует. Делать BoardAuthContext? Но вот для редактирования банов (оно же ведь есть) нужен полный список, чтобы для разницы (борды_в_бане_до ^ борды_в_бане_после) проверить, зарегистривованы ли id этих борд, как те, которые модератор может модерировать. Eeh..> Ох, не ведаю я про всякие библиотеки для PHP и современные best practices. Поэтому просто доверюсь вам!Вполне возможно, что я осведомлён ещё меньше, чем вы.Ну и best не best, но если код и его организация получатся такие, что вам глаза захочется вырвать или взять и всё переписать, а не работать с, то такой архитектуры не надо, наверное. Будет куда лучше, если вам будет приятно с этим работать. Так что ваша помощь очень нужна.Also, ЕМНИП, в staff добавлялся концепт VIP'ов.. которые не очень-то staff, если подумать. Так что, наверное, User и users лучше название.
select encode(ip_aes, 'hex')
SELECT encode(bans.ip_subnet_aes, 'hex') AS ban_ip_subnet_aes_hex, ARRAY(SELECT boards.name FROM boards WHERE boards.id = ANY(bans.boards)) AS ban_board_names_flattened, ...FROM bansWHERE ...
>>1000802>Наиболее полным образом select'ит содержимое таблицы posts генерация треда и постов в нём.Вот уж оптимизировать количество возвращаемых колонок такая микрооптимизация, что давайте не будем на ней заморачиваться.
>>1000802> Для инкапсуляции действия и его вариаций в один класс-сервис: чтобы функции, которым можно с этим сервисом общаться, были public, а его внутренние функции и определения были private.Понятно. В целом неплохо, но неясно, зачем вообще создавать инстансы класса, если это могут быть статические методы.Наверное, на функциях можно сделать что-то похожее, используя namespaces (namespace Xxx, namespace Xxx\details).Но я предпочту классы и статические методы, потому что там не нужно «переключать» неймспейсы при «переключении» public <-> private.> В основном, похоже, stateless. Или state, определяемый единожды при создании. В целом, stateless натура PHP не способствует statefull вещам.Предлагаю таскать везде первым аргументом один объект типа Wiring, в котором будет всё состояние. Так не придётся переписывать аргументы всех методов и функций везде, если вдруг добавится какое-то состояние (e.g. logging) или уберётся.> Скажем, пусть у условного BoardPageGenerator будет поле, куда при инстанциации пишется постформа для страниц досок и тредов. Или вот postbox. Конечно, после создания instance, например, количество unique user posts может поменяться, но поскольку по завершению действия, влияющего на это количество, следом должен создаваться/зваться генератор, страницы те не могут не оказаться eventually consistent.Это можно поместить в статическую переменную класса.> > Никаких глобальных переменных. Синглетон или передача на этапе инициализации класса.Okay. Но непонятно, зачем объект вообще нужно конструировать.> Чтобы иметь возможность этот результат скормить другому Action или Service классу или что-то делать, в зависимости от того, каков этот резульутат. Пусть DeletePostsAction в своём успешном результате всегда хранит IDшнкики тредов, где были изменения, и IDшники тредов, которые оказались удалены полностью. Чтобы штука, которая DeletePostsAction вызвала, могла скормить result с этими ID'шниками BoardPageGenerator'у, но по успеху вывести результат DeletePostsAction. Или чтобы эти результаты можно было гибким образом компановать: при удалении с нескольких досок по IP в модерке, например.Я думаю, эту логику следует вынести в классы-entities. Но нужно ещё подумать.> > А классы-entities-то нужны. Board, Post> Наиболее полным образом select'ит содержимое таблицы posts генерация треда и постов в нём. И то там никогда не нужны хэши файла/пароля. А для остального целиковая Post entity не нужна совсем, только некоторые её части. Возникает она разве что только при Insert'е нового поста — и то с некоторым количеством полей, поставленным в NULL.> […]> Делать ли в связи с этим отдельные Model классы? Как альтернатива, можно при чтении из базы столбцы, для которых необходимо декодирование, select'ить с hex или flattened суффиксом или преффиксом i.e.Давайте уж entities (aka models) нормальные сделаем. Без ассоциативных массивов и анонимных классов. Будет Model, View (темплейты), Controller (class *Action).> Вижу смысл, чтобы AuthService в результате успешной автентикации возвращал AuthContext, где помимо is_suspened, type и access_level будут can_create_boards(), can_delete_locked_threads(), can_post_in_locked_board(), can_skip_captcha() и другие функции расшифровки staff.type и staff.access_level.We can call it, class User.> On the other hand, can_delete_locked_threads() же не на всех бордах, а только на тех, которые staff модерирует. Делать BoardAuthContext?У class User может быть метод can_delete_locked_threads_on($board). И его реализация уже будет делом класса, а не торчать наружу. Ну и не столько уж много досок может быть, чтобы мы не могли позволить себе хранить их список в памяти.> Но вот для редактирования банов (оно же ведь есть) нужен полный список, чтобы для разницы (борды_в_бане_до ^ борды_в_бане_после) проверить, зарегистривованы ли id этих борд, как те, которые модератор может модерировать. Eeh..Ну и будет полный список в памяти. Или запрашивать через NOT IN. What’s the problem?
namespace Xxx, namespace Xxx\details
Добавилось. Обложили всякими минорными багами с четырёх сторон, нэ. Но есть и то, что нужно зарепортить в личку.Якуй, а куда-откуда-как тебе написать на почту, чтобы дошло? А то там в прошлый раз что-то пошло не так.
>>1000806Пишите с того, что принимается яндопочтой, или хотя бы не поганит кодировки чтобы экзим их читал. А так, contacts.html на yakuji.moe
>>1000807Okay, it’s nothingburger. Там всё интересное либо неочевидным образом mitigated, либо может эксплуатироваться только персоналом (что мы сейчас исправить не пытаемся, ибо такого слишком много).Sry.
Although, для этого надо искать/вспоминать команды openssl, но как альтернатива почте, есть вариант взять из HTTPS сертификата public key, зашифровать сообщение и выложить архивом или текстом сюда.>>1000808Раз nothingburger, намекните уж, где, а то заинтриговали-презаинтриговали.
> [Pr. 80]> ~ это unreserved character в URL вообще (RFC 3986).В RFC да. Я взял этот символ в качестве терминатора URLа, чтобы можно было явно укзазать URLа конец. Если отказываться от тильды в качестве терминатора, то брать другой символ-терминатор.https://www.rfc-editor.org/info/rfc3986/#section-2.4> For example, the octet corresponding to the tilde ("~") character is often encoded as "%7E" by older URI processing implementations; the "%7E" can be replaced by "~" without changing its interpretationМожно вводить тильду в URL через %7E без изменений в смысле.> То же самое с любой разметкой в URLБыло сделано, чтобы разметка тоже терминировала URL. Но, видимо, так не надо. Для исправления убрать строку с mark_subregex из определения url_end_regex.
>>1000804> В основном, похоже, stateless.Stateless за исключением circa $tc_db, I mean.> но неясно, зачем вообще создавать инстансы класса, если это могут быть статические методыЧтобы если одному методу сервиса передавать аргументы другому методу внутри того же сервиса, можно было при передаче условное $tc_db пропустить. Although, в PHP оно несколько менее актуально потому, что для обращения к состоянию класса надо всегда писать $this->, и аргументами при очень частом обращении может получиться короче и удобнее.Чтобы, if applicable, по этому $tc_db выхватить некое состояние, которое окажется общим для методов сервиса и которое потому нет нужды выхватывать в каждом методе отдельно.> то можно поместить в статическую переменную класса.И в каждом public static методе класса писать if(!self::is_initialized) self::init(); или self::init_if_not_already() тогда?Покуда статичное состояние нельзя инициализировать на этапе создания типа в статическом конструкторе без параметров (although, в PHP статических конструкторов похоже нет), есть резон делать объект объектом. Даже если объект создаётся единожды.> Предлагаю таскать везде первым аргументом один объект типа Wiring, в котором будет всё состояниеСовсем-то всё, наверное, не надо. Какие поля/свойства будут у Wiring? $tc_db ($db_conn), maybe распаршенный из $_POST/$_GET или JSON'а therein запрос, maybe Logger, maybe User или AuthContext (User + session token management). Такое видится разумным и достаточно общим.> Давайте уж entities (aka models)Мне distinction видится в том, что модель — достаточно простой объект, +- соответствующий сообщению, загоняемому в базу или получаемому из базы, передаваемому из-в сервис, в то время как entity — нечто целостное и с более сложными операциями и проверками валидности состояния. Although, тут есть разные определения.> Я думаю, эту логику следует вынести в классы-entitiesПроблема с инкапсуляцией кода в целостные entities в том, что далеко не для всех операций эти entities нужны целиком. Например, к каким данным привязывать ReportPosts()? К Board, как сейчас? Но зачем для report'а постов тащить информацию о том, какие бывают файлы спойлеров и другие необходимые для генерации бордовых страниц вещи? От Board там нужен только id и enable_reports. Можно это решать lazy load'ом. А можно в целом от такого способа организации кода отойти во что-то ближе к SOA и к (?) CQRS. Что я сейчас и предлагаю.Если затея причесать зоопарк таким образом по-прежнему кажется сомнительной, то давайте circa классический MVC или другие ваши (желательно, с хорошим описанием) предложения.
$this->
public static
if(!self::is_initialized) self::init();
self::init_if_not_already()
С чётким определением классов моделей вижу такую проблему, что местами это несколько негибко и что это дополнительный бойлерплейт, который муторно менять под конкретные запросы/действия. Вполне возможно, что много где окажется проще без него.> У class User может быть метод can_delete_locked_threads_on($board). И его реализация уже будет делом класса, а не торчать наружу. Ну и не столько уж много досок может быть, чтобы мы не могли позволить себе хранить их список в памяти.Хм, indeed.>>1000803Там при регенерации страниц в любом случае стоит сделать case when или self join на случай, когда хранимая в базе отрендеренная версия поста не соотвествует поменявшимся флагам/содержимому, чтобы эту отрендеренную версию не тянуть или не тянуть нужное для рендеринга: простого SELECT * не выходит.
SELECT *
Although, на регенерацию треда можно посты вытягивать в два запроса: один select'ит те, где можно переиспользовать нарендеренное, другой select'ит те, где были изменения и надо опять BuildPost.
Вы лучше распишите псевдокодом как выглядит перенос треда на другую доску, там быстро все особенности архитектуры проявятся.
https://codeberg.org/yakui-lover/fbe-410/src/commit/cce92a86749192ce7670e5a611178ce4e38b252a/inc/lang/ru/LC_MESSAGES/kusaba.po#L48-L53Странный перевод. Что имелось в виду?https://codeberg.org/yakui-lover/fbe-410/src/commit/cce92a86749192ce7670e5a611178ce4e38b252a/lib/javascript/kusaba.js#L471Зачем выставлять его в null здесь? add_navbar_check_job() же вызвал setTimeout().Ну и там ещё довольно много ошибок и мест, требующих обсуждений, в JS.
>>1000816> Странный перевод. Что имелось в виду?Когда-то Кусаба (или Серисса, dunno) позволяла embed'ить видео с других сайтов, вроде Youtube. Имелось ввиду это.> Зачем выставлять его в null здесь?Скорее всего, ошибка.
2 more PRs opened.Предлагаю прежде всего заняться сейчас issue #9. Остальное не так уж важно.XSS вроде более-менее понятно, как поправить. Про CSRF: расставлять везде проверку/выставление $_SESSION['${action_name}_csrf_token']? Инициализировать md5(openssl_random_pseudo_bytes(16))?Это не означает, что я думаю, что нет смысла обсуждать параллельно будущую архитектуру, though. Думаю и пишу >>1000815 вот это.>>1000789Красивые у вас картинки.
$_SESSION['${action_name}_csrf_token']
md5(openssl_random_pseudo_bytes(16))
>>1000809Извините, что заинтриговали. Нет там ничего. Казалось, что в RSS инъекцию любого текста можно сделать.
>>1000818> Про CSRF: расставлять везде проверку/выставление $_SESSION['${action_name}_csrf_token']?Разве не будет достаточно session_start(['cookie_samesite' => 'Strict'])? И то же для печенек, которые не session token.
session_start(['cookie_samesite' => 'Strict'])