Статья

Архив фактов (sugata.ru)

Домен (sugata) висел в воздухе и решил на нем сделать «архив фактов». Но только так, чтобы страницы на стороне клиента были созданы на html (для архивирования).

Архив фактов (sugata.ru)

Админка стоит на поддомене, скажем: admin.sugata.ru и она закрыта для доступа, а сами файлы генерируются на основном домене — sugata.ru.

Админка

Можно зайти, посмотреть сайт на html. Вот такая легкость была в сети, пока всеми любимый JS не заполнил мир и его не начали использовать где попало. 😄

В данном проекте мне стало интересно повторить на PHP Movable Type, LinkSQL которые изначально были написаны на Perl.

https://github.com/LibArea/Sugata

P.S. пока всё сыро, я тут вообще не спешу, как вдохновение приходит, делаю.

https://sugata.ru/info/

holiday holiday2

Опубликовано в Блог Evg

9 Ответов

  1. Ничего не скажешь, быстро работает.

  1. Попробовал запустить, по итогам сделал PR https://github.com/LibArea/Sugata/pull/1

    а сами файлы генерируются на основном домене

    Пока что не нашел в коде, как вывести эти файлы (создал локально тестовые факты) — или это в отдельном проекте?

    1. Спасибо!

      Файлы генерируется вот по этому пути:

      https://github.com/LibArea/Sugata/blob/main/config/general.php#L85

      Если смотреть каталог, то у меня вот так создаются домены:

      sugata.ru
      admin.sugata.ru

      Соотв. путь будет: 'path_html' => '/../sugata.ru/',

      Страница: инструменты, по адресу: /mod/admin/tools

      Там перестройка отдельная по каждому пункту. Отдельно создаются категории, индексные файлы и файлы с самими фактами. Переносится css и фото с домена admin.sugata.ru на домен sugata.ru для работы.

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

      И далее разобраться с этими кнопками, что-то объединить, сделать более понятным. Возможно перестройку с шагом ввести. В LinkSQL построение было с шагом 200, потом пауза и новый запуск. Это чтобы решить проблему больших объемов.

      На LinkSQL, например, работает: https://www.hotscripts.com/

      Тут пока этого ничего не делал, просто накидал в общем и глянул, что получается…

  1. По описанию интересный движок для ssg, попробую развернуть на выходных. Я для проектовы использую 11ty. Я правильно понял что Sugata запускает перегинерацию всех объектов при изменении? Нехватает только 404.html что бы не показывать серверное сообщение и можно прикрутить

    self.addEventListener('fetch', event => {
      event.respondWith(
        caches.open(CACHE_NAME)
          .then(cache => cache.match(event.request, { ignoreSearch: true }))
          .then(response => response || fetch(event.request))
          .catch(function() {
            return caches.match('/offline.html');
          })
      );
    });

    что бы красота была)

    + Забыл еще спросить, не смотрели в сторону SQLite, на первый взгляд он идеально подошел бы сюда.

    + Еще обратил внимание что отсутствует sitemap.xml, очень жаль что он не генерируется(

    1. Я правильно понял что Sugata запускает перегинерацию всех объектов при изменении?

      Мы можем выбирать, какие части проекта нам надо пересобрать. Но надо добавить вариант, генерацию только того, что изменено. А вот где всё по сегментам, сдалать шаг с задержкой.

      Там много чего нет. У меня времени нет прежде всего. (

      P.S. я не думаю, что стоит туда «сувать» JS.

Комментарий удален...
  1. было бы круто реализовать API для хранения реакций на записи, база уже есть на проекте

    // emoji 
    const REACTION_EMOJI = {
      like: "👍",
      dislike: "🥱",
      party: "🥳"
    };
    
    // иконки реакций
    const REACTION_ICONS = {
      like: "/assets/reactions/like.svg",
      dislike: "/assets/reactions/dislike.svg",
      party: "/assets/reactions/party.svg"
    };
    
    // API
    const API_URL =
      "https://api.sugata.ru/dev/posts/{slug}/likes";
    
    
    // получаем slug поста из URL: /posts/my-post-slug
    function getPostSlug() {
      return document.location.pathname.match(/^\/posts\/([a-z0-9-_]+)/)?.[1];
    }
    
    
    // рендер списка реакций
    function renderReactions(data, container) {
      const html = Object.entries(data).map(([type, count]) => `
        <li class="reactions__item reaction" data-type="${type}">
          <button aria-label="Поставить реакцию ${type}">
            <img
              class="reaction__image"
              src="${REACTION_ICONS[type]}"
              alt="${REACTION_EMOJI[type]}"
              width="64"
              height="64"
            />
            <div class="reaction__counter">
              ${count}
            </div>
          </button>
        </li>
      `);
    
      container.innerHTML = html.join("");
    }
    
    
    // переключение активной реакции
    function toggleReaction(container, selector, active) {
      const item = container.querySelector(selector);
      if (!item) return;
    
      const img = item.querySelector(".reaction__image");
      const counter = item.querySelector(".reaction__counter");
      const button = item.querySelector("button");
    
      if (active) {
        item.classList.add("reaction_active");
        button.setAttribute("aria-current", "true");
        img.src = img.src.replace(".svg", ".gif");
        counter.textContent = +counter.textContent + 1;
      } else {
        item.classList.remove("reaction_active");
        button.removeAttribute("aria-current");
        img.src = img.src.replace(".gif", ".svg");
        counter.textContent = +counter.textContent - 1;
      }
    }
    
    
    // === Инициализация ===
    
    function initReactions() {
      const container = document.querySelector(".reactions");
      if (!container) return;
    
      // стартовые значения
      renderReactions({
        shocked: 0,
        love: 0,
        like: 0,
        dislike: 0,
        rage: 0,
        party: 0,
        partyPopper: 0
      }, container);
    
      // загрузка данных с сервера
      fetch(API_URL.replace("{slug}", getPostSlug()))
        .then(r => r.json())
        .then(data => {
          renderReactions(data, container);
    
          // обработчик кликов
          container.addEventListener("click", (e) => {
            const reaction = e.target.closest(".reaction");
            if (!reaction) return;
    
            const type = reaction.dataset.type;
    
            // снимаем предыдущую активную
            toggleReaction(container, ".reaction_active", false);
    
            // активируем текущую
            toggleReaction(container, `.reaction[data-type="${type}"]`, true);
    
            // отправляем на сервер
            fetch(API_URL.replace("{slug}", getPostSlug()), {
              method: "POST",
              headers: { "Content-Type": "application/x-www-form-urlencoded" },
              body: JSON.stringify({ reactionId: type })
            });
          });
        })
        .catch(console.error);
    }
    
    document.addEventListener("DOMContentLoaded", initReactions);
    
    1. api.sugata нужно делать и расположить там ещё поиск, чтобы форма была доступна на сайте, где только html. Тут идея заключается в том, чтобы в сам sugata, не заносить по возможности скрипты.

      1. Сами результаты поиска показывать на другом домене?

        1. Раньше у меня было вот так: search.домен.ru. А на самом домене, в html, стояла просто форма поиска с отправкой именно на search.

Комментарий удален...