Waline: un sistema di commenti adatto a Docusaurus

| |

Quando ho messo in piedi la Knowledge Base di Marvellous Codeworks (blog, documentazione, changelog dei progetti, TMS in primis), a un certo punto ho sentito la mancanza, e quindi la necessità, di integrare i commenti all’interno di Docusaurus, perché non tutti gli utenti sono disposti a compilare una richiesta di supporto o registrare un account su GitHub per inserire un commento.
Volevo che chiunque potesse lasciare un feedback, segnalare un problema, ringraziare senza affidarsi al solito colosso SaaS che ti pianta tracker ovunque solo per farti scrivere due righe sotto un post.

Ho quindi fatto qualche ricerca per capire cosa offrisse il mercato del self-hosting, evitando però di tenere in considerazione l’idea e possibilità di ospitare il tutto in casa. Avevo bisogno di qualcosa che si integrasse con il mondo serverless.

Prima scelta: Cusdis (e doccia fredda)

Il primo nome che mi era saltato all’occhio era Cusdis, presentato proprio come “alternativa leggera e privacy-friendly a Disqus”. Sulla carta perfetto: piccolo, self-hostabile, pensato apposta per blog e siti statici.

Poi sono andato a controllare il repository su GitHub, per abitudine, prima di adottare qualsiasi cosa guardo sempre lo stato di manutenzione, e djyde/cusdis risulta archiviato. Un progetto che ha avuto il suo pubblico, ma da metà luglio 2026 non riceve più commit e la repository è chiusa in sola lettura. Nessun annuncio ufficiale che spieghi il perché, semplicemente: fine corsa.

Occhio, non sto dicendo che sia stata una scelta sbagliata a monte, magari l’autore ha semplicemente deciso di chiudere un capitolo, succede. Ma per un progetto pubblico che è sempre esposto su Internet, e che deve restare in piedi per anni e magari ricevere patch di sicurezza, adottare qualcosa di già archiviato il primo giorno sarebbe stato un rischio che non valeva la pena correre.

Waline

Incrociando ulteriori ricerche sono arrivato a Waline, altro sistema di commenti self-hosted, e la differenza si vede subito: repository attiva (commit praticamente ogni giorno), community viva e un set di feature parecchio più ricco rispetto al necessario: supporto Markdown nei commenti, notifiche via email/Telegram/Discord/WeChat e altri canali ancora, pannello di amministrazione integrato, supporto per un bel po’ di database (Postgres, MySQL, SQLite, MongoDB …).

Deploy semplice, tra l’altro: il pacchetto @waline/vercel lo rende pronto per girare come funzione serverless su Vercel, con Neon come Postgres gestito dietro. Repository dedicata (ne ho messa in piedi una sul mio account personale), un index.cjs di poche righe che carica Waline, un vercel.json per i build, e in pochi minuti hai un endpoint di commenti collegato al frontend Docusaurus della KB.

Se vuoi provarlo, ti consiglio di iniziare da qui: waline.js.org/it/guide/get-started.

Le personalizzazioni

Che poi è il classico vaso di pandora rispetto al “set & forget”, quel frangente dello spazio-tempo dove la teoria del “lo metto in piedi in 5 minuti” incontra la pratica di un progetto vero e con alcuni ritocchi sotto al cofano praticamente necessari.

Favicon statiche, per silenziare i log

Prima cosa: ogni browser richiede automaticamente /favicon.ico e /favicon.png quando apre il sito. Waline, non avendoli, li trattava come richieste non gestite e ogni volta finiva un NotFoundError nei log di produzione. Innocuo, ma fastidioso da leggere quando stai cercando altro nei log. Basta aggiungere due file statici e dire a Vercel di servirli direttamente, senza passare dalla funzione Node:

{
  "builds": [
    { "src": "favicon.ico", "use": "@vercel/static" },
    { "src": "favicon.png", "use": "@vercel/static" }
  ],
  "rewrites": [
    { "source": "/((?!favicon\\.ico$|favicon\\.png$).*)", "destination": "index.cjs" }
  ]
}

Le notifiche Telegram (e il template che non ne voleva sapere)

Questa è stata quella più tosta. Volevo essere avvisato su Telegram ogni volta che arriva un commento in attesa di moderazione, così non devo tenere aperta la dashboard tutto il giorno. Waline lo supporta nativamente: bastano tre variabili d’ambiente su Vercel (TG_BOT_TOKEN, TG_CHAT_ID, ed eventualmente un TG_TEMPLATE per personalizzare il messaggio).

Peccato che, appena personalizzato il template, la notifica smettesse di arrivare, e senza errori evidenti. Ci ho girato attorno un bel po’: token giusto (verificato a mano con una chiamata curl diretta all’API di Telegram), chat ID giusto, tutto giusto, eppure niente.
Alla fine, spulciando i log runtime di Vercel riga per riga, è saltato fuori il vero colpevole:

Telegram Notification Failed:{"ok":false,"error_code":400,"description":"Bad Request: can't parse entities: Character '.' is reserved and must be escaped with the preceding '\\'"}

Telegram, quando usi il parse_mode: MarkdownV2, pretende che caratteri come . - _ * [ ] ( ) ~ # + = | { } ! vengano “escapati” ovunque compaiano fuori da un blocco di codice o dalla parte URL di un link. Il mio template ci infilava l’URL del post e il nome del sito in punti non protetti, e ogni dominio ha un punto, ogni tanto un trattino: bastava questo per far cadere l’intero messaggio.

Ho provato a correggere il template più volte, ma affidarsi a un campo di testo multiriga su un pannello web per gestire un fence di codice ``` si è rivelato fragile: a ogni copia/incolla qualcosa andava storto tra a-capo reali e letterali. Alla fine ho spostato tutto nel codice: index.cjs costruisce il messaggio da sé, con una funzione di escaping scritta in JavaScript, e lo invia solo per i commenti con stato “in attesa di moderazione”, esattamente il caso che mi interessava:

function escapeMarkdownV2(text = '') {
  return String(text).replace(/[_*[\]()~`>#+=|{}.!\\-]/g, '\\$&');
}

module.exports = Application({
  plugins: [],
  async postSave(comment) {
    if (comment.status !== 'waiting') return;
    // costruzione messaggio + fetch verso l'API Telegram
  },
});

Per evitare la doppia notifica (quella integrata di Waline più la mia), ho disattivato la prima con DISABLE_AUTHOR_NOTIFY=true, lasciando intatte le email di risposta a chi commenta, che passano da un altro percorso.

Versione bloccata e Dependabot

Ultima rifinitura, più che altro una lezione imparata sul momento: il package.json puntava @waline/vercel sulla versione più aggiornata (latest), comodo perché a ogni deploy scarichi automaticamente l’ultima versione, ma anche un rischio silenzioso, un aggiornamento con un comportamento diverso può arrivare in produzione senza che tu lo sappia. Ho quindi scelto di utilizzare una versione precisa di Waline e ho aggiunto un dependabot.yml che apre una pull request quando viene pubblicata una nuova versione, così l’aggiornamento resta una scelta, non un evento a sorpresa.

In conclusione

La lezione più generale, al di là di Waline in sé: prima di adottare qualcosa, anche uno strumento piccolo come un sistema di commenti, vale sempre la pena guardare lo stato reale del progetto. Un repository archiviato oggi potrebbe risorgere domani con un fork, ma nel dubbio meglio partire da qualcosa di vivo. E il self-hosting, anche via serverless, resta assolutamente sostenibile, basta mettere in conto che i primi giorni si passano più a leggere log che a scrivere codice.

Se vuoi vedere il risultato, la sezione commenti la trovi in fondo a qualsiasi post della KB di Marvellous Codeworks.
Se invece ti va di parlarne o dirmi a che software ti sei affidato tu per gestire commenti su un sito web statico, l’area commenti è a tua disposizione anche qui di seguito, come sempre :-)

#KeepItSimple


Immagine di copertina: Thought Catalog on Unsplash

Correzioni, suggerimenti? Lascia un commento nell'apposita area qui di seguito o contattami privatamente.
Ti è piaciuto l'articolo? Offrimi un caffè! ☕ :-)

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Commenti
Oldest
Newest Most Voted