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

The public ICS export has recently been reintroduced via

GET /discourse-post-event/events.ics, which is a great improvement following the work in

Re-Add full ICS export.

At the moment, this endpoint appears to be limited to events visible to anonymous users. As a result, events in private categories or categories restricted from the default everyone group cannot be subscribed to in external calendar clients (e.g. Google Calendar, Outlook).

Would it be feasible to support authenticated access to this endpoint, similar to how Discourse handles private RSS/Atom feeds (for example, via a per-user token or read-only API key)?

This wouldn’t change any permission rules - it would simply allow calendar clients to access events the user is already authorised to see.

I’m raising this as a separate, scoped request following the reintroduction of the public ICS feed, as previously suggested.

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 лайк