Fuentes ICS autenticadas para eventos de calendario privados

La exportación pública de ICS se ha reintroducido recientemente a través de

GET /discourse-post-event/events.ics, lo cual es una gran mejora tras el trabajo en

Re-Add full ICS export.

Por el momento, este endpoint parece estar limitado a eventos visibles para usuarios anónimos. Como resultado, los eventos en categorías privadas o categorías restringidas del grupo predeterminado everyone no se pueden suscribir en clientes de calendario externos (por ejemplo, Google Calendar, Outlook).

¿Sería factible admitir acceso autenticado a este endpoint, de manera similar a cómo Discourse maneja las fuentes RSS/Atom privadas (por ejemplo, a través de un token por usuario o una clave API de solo lectura)?

Esto no cambiaría ninguna regla de permisos, simplemente permitiría a los clientes de calendario acceder a los eventos que el usuario ya está autorizado a ver.

Estoy planteando esto como una solicitud separada y acotada tras la reintroducción de la fuente ICS pública, como se sugirió anteriormente.

1 me gusta

Después de revisar el código, terminé programando un proxy muy simple que maneja varios obstáculos necesarios para crear una clave de API de usuario y pasarla a la API; también genera un enlace que los usuarios deben pegar en sus aplicaciones de calendario:

Espero que todo esto llegue algún día al código de Discourse y ya no sea necesario; mientras tanto, comparto esto con la esperanza de facilitar la vida a otros.

Añadí eso a principios de esta semana en este PR. Sin embargo, la ergonomía para generar Claves de API de Usuario no es ideal para usuarios no técnicos. Para que esto sea fluido, estoy dando seguimiento con:

Lo cual intenta hacerlo lo más amigable posible

1 me gusta

Acabo de fusionar esta característica, ¿puedes probarla @Ethsim2?

1 me gusta

Gracias: puedo confirmar que la fuente autenticada en sí se está generando correctamente ahora de mi lado.
El problema restante parece ser la compatibilidad del cliente: ni Google Calendar ni Outlook parecen contentos de suscribirse directamente a una fuente ICS autenticada en esta forma, por lo que por ahora planeo evitarlo colocando un proxy inverso Nginx a nivel de host delante de mi instalación de Discourse en un solo contenedor y sirviendo un archivo .ics simple allí en su lugar.
Dado que estoy en la configuración estándar de contenedor único, creo que esto significa quitar los puertos 80/443 del contenedor, haciendo que Discourse escuche en un puerto interno alto, y luego hacer que Nginx del host proxifique el foro y también sirva una ruta de archivo de calendario estático.
Aproximadamente, los comandos que espero usar son:

# 1. Instalar nginx del host + 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. Detener el contenedor de Discourse para liberar los puertos 80/443
cd /var/discourse
sudo ./launcher stop app

# 3. Editar la configuración del contenedor para que Discourse ya no se vincule directamente a 80/443
sudo nano /var/discourse/containers/app.yml

Luego, en app.yml, cambie la sección expose de:

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

a algo como:

expose:
  - "127.0.0.1:8080:80"

y, si está presente, elimine las plantillas SSL/Let’s Encrypt del contenedor para que la terminación TLS se realice en el proxy inverso del host en su lugar.

Luego reconstruir:

cd /var/discourse
sudo ./launcher rebuild app

Luego crear un sitio Nginx a nivel de host como:

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

con algo parecido a:

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";
    }
}

Habilitarlo y recargar:

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

Luego obtener un certificado Let’s Encrypt con Certbot y dejar que actualice la configuración de Nginx:

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

Después de eso, la configuración de Nginx típicamente incluirá un bloque de servidor HTTPS también, por ejemplo:

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";
    }
}

En ese momento, puedo usar un script/temporizador en el host para obtener el ICS de Discourse autenticado y escribir:

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

al cual Google Calendar / Outlook puede suscribirse como una URL ICS pública normal.
Así que desde mi punto de vista, el lado de Discourse parece resuelto ahora; la brecha práctica restante es en su mayor parte que los clientes de calendario importantes no manejan bien las fuentes ICS autenticadas, razón por la cual estoy recurriendo a un enfoque de proxy público/archivo estático por ahora.
También asumo que Certbot es la ruta más simple aquí, ya que puede administrar la emisión/renovación de Let’s Encrypt directamente contra Nginx del host. También podría usar acme.sh, pero mi impresión es que sería más una elección manual que el camino más sencillo para esta configuración específica.

¿Espera, qué?

Lo estoy usando con mi Google Calendar sin problemas, al igual que algunos de mis colegas.

¿¡Qué parte de eso es incompatible con Google Calendar!?

Ah, gracias, eso ayuda a acotar el problema.

Por mi parte, Google Calendar sí acepta la URL de suscripción (a través de “Desde URL”), pero el comportamiento que estoy viendo es:

  • el calendario se añade correctamente
  • sin embargo, no muestra ningún evento en absoluto

Así que no es un caso de que la URL sea rechazada, sino más bien que el feed parece vacío desde la perspectiva de Google.

Dado que el ICS sin procesar claramente contiene entradas VEVENT (por ejemplo, con UID, DTSTART, SUMMARY, etc.), esto me hace pensar que podría ser algo como:

  • Google filtrando eventos pasados (la mayor parte de mis datos de prueba son históricos)
  • o algo sobre cómo se está interpretando el feed (por ejemplo, rango de tiempo, caching o cabeceras)

Avísame si hay algo específico que te gustaría que comprobara en el feed en sí.

2 Me gusta

¿Puedes revisar /logs en busca de errores? Acabo de arreglar uno donde los eventos recurrentes antiguos estaban causando que el feed fallara.

Si fue el mismo error, necesitas actualizar.

Tendría que buscar más atrás, antes de las 7 p.m., ya que hay muchas advertencias/errores relacionados con Discourse AI: límite de tokens.

1 me gusta
1 me gusta