Аутентифицированные ICS-фиды для приватных событий календаря

Экспорт ICS для публичного доступа недавно был восстановлен через

GET /discourse-post-event/events.ics, что является значительным улучшением после работы, проделанной в теме

Восстановление полного экспорта ICS.

На данный момент этот эндпоинт, по-видимому, ограничен событиями, видимыми анонимным пользователям. В результате события в частных категориях или категориях, доступ к которым ограничен для группы по умолчанию «все», нельзя добавить в календарные клиенты (например, Google Calendar, Outlook).

Возможно ли реализовать поддержку аутентифицированного доступа к этому эндпоинту, аналогично тому, как Discourse обрабатывает приватные RSS/Atom-ленты (например, через персональный токен пользователя или ключ API только для чтения)?

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

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

1 лайк

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

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

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

Которая стремится сделать этот процесс максимально дружелюбным.

1 лайк

Я только что объединил эту функцию, не мог бы ты её проверить, @Ethsim2?

1 лайк

Спасибо — могу подтвердить, что аутентифицированная лента теперь генерируется корректно с моей стороны.

Оставшаяся проблема, похоже, связана с совместимостью клиентов: ни Google Calendar, ни Outlook не хотят подписываться напрямую на аутентифицированную ICS-ленту в таком виде, поэтому пока я планирую обойти это, установив обратный прокси Nginx на уровне хоста перед моей одноконтейнерной установкой Discourse и предоставляя там обычный файл .ics.

Поскольку я использую стандартную одноконтейнерную конфигурацию, это означает перенос портов 80/443 с контейнера, настройку Discourse на прослушивание внутреннего высокопортового порта, а затем использование Nginx на хосте для проксирования форума и обслуживания статического пути к календарной ленте.

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

# 1. Установка host nginx + certbot
sudo apt update
sudo apt install -y nginx snapd
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot

# 2. Остановка контейнера Discourse для освобождения портов 80/443
cd /var/discourse
sudo ./launcher stop app

# 3. Редактирование конфигурации контейнера, чтобы Discourse больше не привязывался напрямую к 80/443
sudo nano /var/discourse/containers/app.yml

Затем в app.yml изменить секцию expose с:

expose:
  - "80:80"
  - "443:443"

на что-то вроде:

expose:
  - "127.0.0.1:8080:80"

и, если есть, удалить шаблоны SSL / Let’s Encrypt контейнера, чтобы TLS завершался на обратном прокси хоста.

Затем пересобрать:

cd /var/discourse
sudo ./launcher rebuild app

Затем создать сайт Nginx на уровне хоста, например:

sudo nano /etc/nginx/sites-available/discourse

с чем-то вроде:

server {
    listen 80;
    listen [::]:80;
    server_name forum.example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /private-calendar/ethan.ics {
        alias /var/www/private-calendar/ethan.ics;
        default_type text/calendar;
        add_header Content-Type "text/calendar; charset=utf-8";
    }
}

Включить его и перезагрузить:

sudo mkdir -p /var/www/private-calendar
sudo mkdir -p /var/www/certbot
sudo ln -s /etc/nginx/sites-available/discourse /etc/nginx/sites-enabled/discourse
sudo nginx -t
sudo systemctl reload nginx

Затем получить сертификат Let’s Encrypt с помощью Certbot и разрешить ему обновить конфигурацию Nginx:

sudo certbot --nginx -d forum.example.com
sudo nginx -t
sudo systemctl reload nginx

После этого конфигурация Nginx обычно будет включать также блок сервера HTTPS, например:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name forum.example.com;

    ssl_certificate /etc/letsencrypt/live/forum.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/forum.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /private-calendar/ethan.ics {
        alias /var/www/private-calendar/ethan.ics;
        default_type text/calendar;
        add_header Content-Type "text/calendar; charset=utf-8";
    }
}

На этом этапе я могу использовать скрипт/таймер на хосте для получения аутентифицированной ICS-ленты Discourse и записи в:

/var/www/private-calendar/ethan.ics

что Google Calendar / Outlook смогут подписаться как на обычную публичную ICS-ссылку.

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

Я также предполагаю, что Certbot — самый простой путь здесь, поскольку он может напрямую управлять выпуском/обновлением сертификатов Let’s Encrypt для Nginx на хосте. Я также мог бы использовать acme.sh, но, по моим представлениям, это потребовало бы более ручного выбора и не было бы самым прямым путём для данной конкретной конфигурации.

Подождите, что?

Я спокойно использую это в своём Google Календаре, как и некоторые мои коллеги.

Что именно в этом несовместимо с Google Календарем?!

Ах, спасибо, это помогает сузить круг поиска.

С моей стороны Google Calendar принимает URL подписки (через «По URL»), но наблюдаемое мной поведение следующее:

  • календарь добавляется успешно
  • однако в нём нет никаких событий

Так что дело не в том, что URL отклонён — скорее, с точки зрения Google лента кажется пустой.

Учитывая, что в сыром ICS-файле явно содержатся записи VEVENT (например, с UID, DTSTART, SUMMARY и т. д.), я склонен думать, что причина может быть в чём-то вроде:

  • фильтрации Google прошедших событий (большая часть моих тестовых данных историческая)
  • или в особенностях интерпретации ленты (например, временной диапазон, кэширование или заголовки)

Дайте знать, если есть что-то конкретное, что вы хотели бы, чтобы я проверил в самой ленте.

2 лайка

Можете проверить /logs на наличие ошибок? Я только что исправил одну, из-за которой старые повторяющиеся события приводили к сбою ленты.

Если это была та же ошибка, вам нужно обновиться.

Мне нужно будет копаться в логах за время до 19:00, так как есть множество предупреждений/ошибок, связанных с Discourse AI — лимит токенов

1 лайк
1 лайк