Например, Вы учитель и хотите для своего класса или школы сделать сайт, где ученики решают задачи по программированию - или, может, математике, физике.

А может вы набираете стажёров в компанию - на позицию junior разработчика или тестера - или на внутренние курсы - тут не помешала бы возможность провести мини-соревнование - чтобы отфильтровать более целеустремлённых кандидатов. (конечно нужна аккуратность с любителями ИИ-генерации кода, но это отдельная тема) Можно использовать также и для сессий live-coding на собеседованиях.

Это просто! Вы можете за один присест развернуть готовый опенсорсный сайт (моё творение) на бесплатном PHP-хостинге. В зависимости от потребностей этого может хватить на годы. Я расскажу об основных возможностях и недостатках - и вкратце поясню как скачать, как задеплоить, как кастомизировать - так что вы сможете решить, подойдёт ли эта штука для ваших целей.

Предыстория

Сайт CodeAbbey зародился давно в 2013. Он в основном посвящён задачкам и небольшим челленджам по программированию. Однако работает он немного не так как привычные сайты олимпиадных задач, LeetCode и прочее. Большинство задач генерируют рандомизированные входные данные - и пользователь должен написать код считающий ответ - и этот ответ отправить. Это похоже на то как проходит отбор Facebook/Meta Hackercup например. Код (обычно) не выполняется поэтому писать можно на любом языке - а сайтовладельцу не нужно (не обязательно) заботиться о серверах для запуска. Строго говоря "олимпиадный" подход тоже возможен но требуется чуть больше усилий. В статье я об этом рассказывать не буду (по крайней мере пока) - но если нужно, всегда можно спросить - объясню!

Несколько месяцев назад сайт отправился в OpenSource - то есть его код теперь доступен на github - благодаря чему его и может переиспользовать любой желающий. Не то чтобы раньше было жалко, просто вывод проекта в опенсорс это оказывается довольно нудная и кропотливая работа (если это не было предусмотрено изначально) - просто было страшно браться.

Интерфейс англоязычный - но если вы поменяете слова в шаблонах страниц на русский или какой-нибудь ещё - ничего не сломается :) К сожалению до полноценной локализации руки не дошли (точнее не было надобности) - хотя перевод задач поддерживается.

Возможности сайта

По умолчанию у нас есть простой логин с паролем, почту нужно указывать но она не проверяется (используется только для восстановления пароля).

Для регистрации нужно заполнить кроме двух верхних полей и два нижних.

На основном сайте используется дополнительно регистрация через GitHub или Google - но такие штуки в опенсорсный код пока не включены - впрочем опять же я всегда готов проконсультировать - хоть через телеграм логин устроить несложно.

Залогинившись пользователь попадает на страницу задач

Здесь доступна категоризация - точнее это фильтрация по тегам - можно разбить задачи на уровни сложности или например семантически - головоломки, популярные алгоритмы и т.п. Администратору сайта доступна и кнопка создания новой задачи :)

Цифры показывают сколько участников решили ту или иную задачу и исходя из этого какова текущая "ценность" той или иной задачи. Это позволяет ранжировать участников по сумме очков за решенные ими задачи. Вот страничка рейтинга:

Как видим, тут можно фильтровать по странам или языкам программирования. Однако вернёмся на шаг назад и полюбопытствуем - как выглядит интерфейс создания или редактирования задачи для администратора?

Тут всё слегка неказисто - но в общем понятно - заполняете название задачи, урл генерируется из него но можно поправить. Описание задачи - на языке Markdown - а генератор входных данных и ожидаемого результата - на PHP. Вообще возможны и другие языки - если ваш хостинг поддерживает их.

У некоторых задач может быть больше одного решения - в этом случае функция checker(...) умеет вместо генерации готового ответа оставлять заметку чтобы её при проверке вызвали ещё раз и она разобрала ответ и решила правильный он или нет. В общем возможностей довольно много но для первого знакомства не будем перегружать мозг :)

Как же видит задачу пользователь? Это возможно одна из наиболее "нагруженных" функционалом страниц:

В целом всё понятно - сверху условие, теги, ссылки на переводы задачи (если они есть). Дальше два блока - с тестовыми данными (они каждый раз разные) - и для ввода ответа.

Ещё ниже есть поле для редактирования кода задачи - можно решать задачу прямо здесь. Если подключены "запускалки" для каких-то языков - пользователь сможет автоматически обработать входные данные и заполнить поле ответа. По умолчанию доступна как минимум кнопка для выполнения кода на JavaScript - т.к. ей не требуется сервер "песочницы" - код выполняет сам браузер пользователя. Аналогично можно подключить и языки которые компилируются или выполняются в JS - есть реализация для Python, Lua, Scheme например. Более сложные - например C++ и Java - для них "песочница" нужна - но это только если вы захотите предоставить пользователю "запускалку".

Так-то на любом языке можно решить задачу скопировав данные себе в консоль - и скопировав ответ из консоли обратно. Благодаря этому, например, на основном сайте появляются решения на совершенно диковинных языках - от архаичных до новейших.

Другие возможности - давайте перечислим их вкратце, чтобы не надоедать - есть встроенный форум, на котором пользователи могут жаловаться на жизнь, предлагать задачи, сообщать об ошибках или написать что-то о себе. Есть секция страниц (мы называем их wiki) на которых можно писать всякие инструкции, заметки и прочее.

Можно создавать Соревнования - задачи у которых ответ не "бинарный" - решено или нет - а имеет и некоторую оценку - и можно смотреть кто решил хуже, кто лучше. Это актуально для более крупных задач, обычно не имеющих чёткого алгоритма решения (как задача коммивояжёра - или упражнение на написание самого короткого кода, например, на ассемблере для интел-4004 или брейнфаке).

А как с задачами по физике / математике

Тут зависит от того как вы хотите видеть эти задачи. Если у них таким же образом задаются входные данные и ожидаемый (численный) ответ - то никаких дополнительных действий не требуется. Их тоже можно (и полезно) рандомизировать - но в общем это мы все видели.

Может быть нам захочется создать задачи в которых ответом является формула а не число. Такую версию я создал с помощью несложного интерпретатора выражений подключенного к исходному коду. Посмотреть что получилось можно на демо-проекте Alferov's Peace - она тоже опенсорсная и там можно посмотреть как это подключается - либо переиспользовать целиком.

Что там насчет деплоя?

Сайт изначально был сделан так чтобы его можно было содержать "минимальной ценой" - а куда уж минимальнее чем бесплатно? :)

Эта идеология продержалась и до сих пор - и без большого ущерба для функционала. Основной сайт задеплоен на недорогом платном хостинге (плюс крохотный VPS для "песочницы") - но это абсолютно не является необходимым. Для нужд школы или компании вы вполне можете использовать бесплатный хостинг - работать будет почти всё.

Какие ограничения? Бесплатные хостинги не всегда имеют HTTPS например. У них обычно нет возможности исходящих запросов (поэтому ни песочница, ни логин через oauth работать не будут - и потому их функционал пока не перенесен в опенсорсный код). Скорее всего это не катастрофа (например логин через телеграм-бота вы скорее всего сможете устроить все равно).

На странице проекта на гитхабе есть демо-видео, где полностью показывается весь путь деплоя на awardspace.com - это хостинг не требующий замысловатой регистрации - и притом мы его используем для некоторых целей (интерактивные задачи) годами - без проблем.

Один из нюансов деплоя - нужно использовать Filezilla или SFTP чтобы закачать все файлы разом а не ковыряться поштучно.

Многие элементы сайта сразу настраиваются в файле кастомизации - так что вы в значительной степени сможете придать ему "свой облик" ничего не трогая в коде. Ну и в код конечно тоже не запрещается влезть. Чуть подробнее про это ниже.

Где же код проекта :)

Да конечно на гитхабе: https://github.com/codeabbey/src

Здесь быть может наиболее важный момент - для тех кому захочется что-то менять в коде - и потому в нём разобраться. Я добавлял файлы "пошагово", условно говоря по частям функционала. Каждый из таких шагов оформлен отдельной веткой - благодаря чему можно смотреть что добавляется на каждом шаге (там и в README.md соответствующие пояснения) - и как это все устроено.

Как пример, один из пользователей сайта, который ещё год назад ничего кроме VBA не знал - не поленился немного изучить PHP и с небольшими подсказками развернул вот такой собственный сайт https://the-athenaion.com/ - в основном с задачами на программирование в области математики, геометрии и статистики.

В общем если кому-то это пригодится - я буду рад ответить на вопросы, помочь, добавить в опенсорсный код тот или иной забытый функционал - спрашивайте, жалуйтесь, предлагайте :)

Комментарии (19)


  1. FanatPHP
    09.10.2024 18:32

    С одной стороны не хочется брюзжать, поскольку дело, в целом, благое, да и пост не про код. С другой - на что ещё смотреть в хабе про РНР, кроме как на код? А он, к сожалению, довольно грустный. Выглядит так, будто писался глубоко в позапрошлом десятилетии.

    • Объект-бог, который содержит в себе весь код приложения целиком. Но при этом всё равно передаётся через global.

    • Защита от SQL инъекций фактически отсутствует, и заменяется забором из костылей. Где-то это проверка на числовое значение, где-то кромсание наивной регуляркой типа "/[\'\;]/",(заведомо дырявой причём), где-то проверка через функцию validUrlParam(?!). Я больше чем уверен, что автор и сам путается, где что применять. Про несчастных пользователей я уж и не говорю. И хотя я не нашёл готовую инъекцию, по поговорке "У семи нянек дитя без глазу", она где-то да вылезет. Просто потому что защита не обеспечивается там где должна - в слое для работы с БД.

    • Защиты от XSS не нашёл вообще, но допускаю что она есть, в каком-нибудь совершенно неожиданном месте.

    • Валидациия входящих данных в зачаточном состоянии

    • Зависимость от апача прибита гвоздями.

    • Вместо исправления динамических пропертей к каждому классу старательно подставлен костыль

    • Ну и так далее, тут можно долго удивляться.

    Я понимаю, что проект разрабатывался давно, и ресурсов на переделку особо нет. Но в таком случае я бы на вашем месте ограничился хабами про вайти образование et al., а код скромно задвинул подальше за шкаф.


    1. RodionGork Автор
      09.10.2024 18:32

      Да, реакция мэтров PHP на этот код предсказуема :) Если вкратце - я профессионально PHP не занимался никогда - изначально всё задумывалось на Java (в основном на ней тогда я работал) - но первые же попытки показали что процесс разработки будет (особенно по тогдашним джаванским технологиям) настолько трудоёмким и мучительным, что это дело я забросил. Ну и хостить её родимую и решать кучу инфраструктурных проблем. Так что пошёл "от обратного" и взялся за PHP потому что понял что с ним-то захоститься проблем не будет и написать MVP можно будет гораздо быстрее (2 недели заняло).

      На вашем месте я бы постеснялась с таким лицом появляться на людях". А уж тем более учить других. 

      Ну PHP я никого учить не пытаюсь, не переживайте :) а в остальном - если сие творение кому-то помогло - то и хорошо. А ругательства меня наверное мало уж заденут. Действительно в ближайшее время глубочайший рефакторинг вряд ли случится - дело не столько в ресурсах сколько в целесообразности.

      Дырявую регулярку постараемся заменить, спасибо. насчет XSS я наверное не очень понимаю как вы заэксплойтить хотите, если не лень - поясните. Ну и в любом случае спасибо что не поленились хотя бы перечислить пункты вызывающие возмущение - и из этого какую-нибудь пользу да извлечем :)

      Я больше чем уверен, что автор и сам путается, где что применять

      Ну, больше скажу, я просто уже значительную часть этого добра не помню - когда приходится что-то добавлять или править - вспоминаю - но не всегда то что надо :)

      P.S. впрочем насчет XSS сейчас проглядывая примеры как минимум одно место сообразил. наверное дальше сдюжу сам поискать ещё.


      1. FanatPHP
        09.10.2024 18:32

        В том-то и дело, что вы не понимаете главного. Для вас защита принципиально сводится к латанию дыр. "Ну я сейчас поищу", "ну я заменю регулярку". Для того чтобы отбояриться от зануды на Хабре это может и сойдёт (нет). Но к реальной защите это не имеет никакого отношения.

        Защита работает только тогда, когда она применяется системно. Переменные попадают в SQL запрос всегда с защитой. А не в надежде, что где-то другом месте кода кто-то не забудет провалидировать. Данные выводятся в HTML всегда с защитой, а не только там где "я поискал".

        Причем такой подход помимо прочего ещё и упрощает код. Из него пропадают растущие там и сям бородавки типа "/[\'\;]/". Валидация - это хорошо и правильно. Но она не должна заменять защиту. Это две совершенно разные области, которые пересекаются только в головах у новичков.


    1. RodionGork Автор
      09.10.2024 18:32

      Уф, искал по коду упомянутую регулярку, по-моему нашёл только в Auth.php - это явно фрагмент один из самых ранних, с первых недель сайта - и по-моему там она не особо и нужна т.к. формат юзернейма проверяется раньше. Наслоение в общем, на свежую голову внимательнее посмотрю. Если ещё где-то заметили и не лень будет сказать - скажите пожалуйста :)

      Этот validUrlParam появился из-за того что тема sql-injection-а была изначально упущена и хотя спохватился довольно быстро, уже менять сами обращения к базе показалось затруднительно, да и проверку хотелось устроить на раннем этапе. В общем безусловно это костыль от которого в идеале стоит уйти, но действительно не факт что я наберусь решимости методически это зарефакторить :)


      1. FanatPHP
        09.10.2024 18:32

        Тут проблема на самом деле глубже, чем "менять сами обращения к базе". А в кривой архитектуре. У вас SQL торчит в контроллерах. Только вместо нормальных запросов вы туда высунули огрызки, типа "taskid = $id". Это такой очень наивный детский подход, "я в домике!".

        Причём даже его можно было сделать нормально, в виде $ctx->taskDataDao->makeLookup('type', 'taskid', $id);. И хотя это всё равно нарушение границ - в контроллере надо знать название таблицы БД, такой код по крайней мере легко сделать безопасным. Но по-хорошему в контроллере длолжен вызываться метод модели/сервиса, который сам знает детали реализации - в какой таблице и по какому полю искать.


        1. RodionGork Автор
          09.10.2024 18:32

          У вас SQL торчит в контроллерах.

          да-да с точки зрения архитектуры безобразие, даже в некоторых местах у таких огрызков уже можно найти дублирующие функции в контроллерах/дао - в общем это тоже признаки наиболее древних фрагментов кода не приведённых в порядок. понемногу будем искоренять


  1. MetaDone
    09.10.2024 18:32

    Как пример, один из пользователей сайта, который ещё год назад ничего кроме VBA не знал - не поленился немного изучить PHP и с небольшими подсказками развернул вот такой собственный сайт https://the-athenaion.com/ 

    у этого клиента доступна ссылка https://the-athenaion.com/sqlexec.php

    даже хорошо что не работает, но не сильно безопасно такой бэкдор оставлять


    1. RodionGork Автор
      09.10.2024 18:32

      Хе-хе :) действительно надо сделать чтобы в админском аккаунте появлялась плашка "Да ты же не удалил sqlexec", спасибо!


      1. RodionGork Автор
        09.10.2024 18:32

        upd. это обманка, она только локально работает, я уж запамятовал. но из кода это очевидно.


    1. baldr
      09.10.2024 18:32

      даже хорошо что не работает

      Возможно, это "уже не работает". Я попробовал сделать `drop table users`, но, возможно, это уже кто-то пробовал до меня.


      1. RodionGork Автор
        09.10.2024 18:32

        ерунда, простите. тогда бы юзеры с сайта пропали. эта штука просто только для докера. для локальной разработки. на хостинге работать не будет что из ее кода очевидно. ну и без выбора дб тем более.


        1. FanatPHP
          09.10.2024 18:32

          ну и без выбора дб тем более

          Ваше невежество начинает начинает уже немного напрягать.


    1. RodionGork Автор
      09.10.2024 18:32

      Фу, зря меня путаете, этот 'бэкдор' только в докере работает при локальном запуске. в инструкции где-то было.


      1. MetaDone
        09.10.2024 18:32

        это таки бекдор если пользователь веб-сервера сможет выполнить

        exec("echo -n '$input' | base64 --decode | mariadb -B 2>&1", $output, $code);

        если криво настроен сервер то можно много чего сделать интересного. так же отдельно про sql-инъекции надо посмотреть.

        так же все из репозитория болтается доступным

        https://the-athenaion.com/dbinit.sql

        https://the-athenaion.com/taskinit.sql

        это просто файлы, но такого быть не должно. как правильно - делаете папку public, в нем index и все, остальные файлы будут в директории выше и не будут так болтаться в общей доступности, можно сделать даже на шаред-хостингах


        1. RodionGork Автор
          09.10.2024 18:32

          это таки бекдор если пользователь веб-сервера сможет выполнить

          ну такое. хакер не был бы хакером если бы ему просто дали консоль с рутовыми правами

          это просто файлы, но такого быть не должно.

          да, потом может передвинем, но "быть не должно" звучит странновато. как вы правильно заметили "это просто файлы", копии из публичной репы.


      1. FanatPHP
        09.10.2024 18:32

        Вы правда, всерьёз думаете, что вот эта дырища позволяет выполнять только SQL команды? И предыдущая дискуссия вас ничему не научила?


        1. RodionGork Автор
          09.10.2024 18:32

          предыдущая дискуссия вас ничему не научила?

          простите за прямоту, ваши экивоки тоже не то чтоб напрягают, но выглядят как позиция человека которому охота немного потрепаться, немного показать свой ум и так далее

          я вообще не спорю что вы очень-очень умный, но если вы хотите помогать - можете говорить более прямо и не пытаться ёрничать. не стесняйтесь объяснить если считаете что что-то нужно объяснить. люди делятся на две примерно категории - одни стараются что-нибудь полезное сделать. другие не стараются. если в вас прям возбушует злоба и вы не придумаете ничего лучше как пойти для самоутверждения ломать сайты - ну тут ничего не поделаешь.


          1. FanatPHP
            09.10.2024 18:32

            Во-первых, с чего вы взяли, что я "хочу помогать"? Вы не в Армию Спасения пришли, чтобы вам тут все кидались помогать. Я комментирую статью на Хабре. И если изначально отношение у меня было нейтрально-благожелательное, то вам удалось его сильно испортить. И ладно если бы это было просто невежество (прикрытое фиговым листком "вообще-то я маститый программист на Яве, а это так - левой пяткой нацарапал для нубов"). Но оно усугубляется вашим пофигизмом в отношении замечаний, которые вам делают. Вы даже не даёте себе труда подумать, перед тем как строчить комментарии. И выставляете себя дураком, выдавая элементарные вещи за какие-то сокровенные знания, которые можно не знать.

            И начинаете рассказывать, будто "без выбора дб" SQL дамп не выполнится. Или показываете код, который до боли напоминает SQL инъекцию, и заявляете что с ним всё нормально. Вы реально мне сейчас хотите сказать, что mysql_query("SELECT * FROM table WHERE col='$input'"); - это плохо, а exec("command '$input'"); - это ОК? Или про первое вы тоже так и не поняли? И это я виноват что не объяснил с ложечки, почему это позор и профнепригодность?


            1. RodionGork Автор
              09.10.2024 18:32

              да ладно-ладно, Вы во всём правы

              просто сверху написали "не хотел брюзжать"... и понеслось: ах позор, ах профнепригодность, ах мерзавец, как смел вообще что-то написать и бла-бла-бла

              ну, развлекайтесь на здоровье

              P.S. где про маститого программиста вообще нашли? вот реально вы воспринимаете текст не так как написано а через какую-то свою призму. понимаете, мне код с учетом времени и недостатка скилла и т.п. может быть сложновато исправить - но потихоньку я этим занимаюсь. а вы себя не исправите потому что вы уверены что всё делаете правильно :)