Synchronisation/Cross-Posting von Themen zwischen verschiedenen Discourse-Seiten

Ich habe derzeit ein Thema auf einer Website, das ich auch auf einer anderen Website haben möchte. Obwohl ich es verlinken könnte, würde ich mir wirklich die Möglichkeit wünschen, es auf beiden Websites bearbeiten zu können und die Änderungen auf beiden widerzuspiegeln. Dies erspart mir eine veraltete Version des Themas und hält beide Themen ständig auf dem neuesten Stand. Es bietet mir auch eine Möglichkeit, die Informationen von meiner Website zu dezentralisieren.

Einige Gedanken zur Funktionalität

  • Als Ausgangspunkt würde eine Website als Host/Besitzer des Themas fungieren, und die andere(n) würde(n) es im Wesentlichen spiegeln. Weitergehend frage ich mich sogar, ob ein Thema irgendwie von einem Spiegelthema geerbt werden könnte, wenn das Original gelöscht wird.
  • Das Thema sollte die Möglichkeit behalten, auf den Spiegel-Websites ausgeblendet, geschlossen usw. zu werden.
  • Die Antworten sollten nicht synchronisiert werden – jede Website hat eine andere Benutzerbasis, daher sehe ich nicht, wie die Synchronisierung von Antworten funktionieren würde.

Ich verstehe, dass die Implementierung einer solchen Funktion alles andere als trivial ist, aber ich frage mich, ob dies jemals untersucht wurde und welche Ergebnisse/Experimente bereits existieren.

2 „Gefällt mir“

Doppelter Inhalt auf mehreren Websites ist ein großes SEO-No-No. Dies wird wahrscheinlich nicht unterstützt.

Was ist der Anwendungsfall oder Zweck? Sie möchten im Grunde, dass die anderen Websites ein Backup der primären Website sind?

2 „Gefällt mir“

Könnten Sie dazu etwas mehr Informationen geben? Aus meiner Sicht würde dies den doppelten Inhalt reduzieren.

Backup ist nicht das Ziel, sondern die Schaffung eines einzigen Wahrheitspunkts für Themen, die auf mehr als einer Website sinnvoll wären.

Um ein konkretes Beispiel zu geben: Ich ziehe gerade meinen Visumantrag in Betracht. Ich habe ein Thema in meinem privaten Discourse, das im Grunde eine Checkliste mit den zu erledigenden Dingen ist. Meine Freunde könnten dies jedoch auch nützlich finden, also erstelle ich dasselbe Thema auf unserem gemeinsamen Discourse. Das Problem ist, dass ich die Informationen beider Themen separat synchronisieren muss, anstatt nur ein einzelnes Thema zu aktualisieren. Das bedeutet, dass einem der Themen oft wichtige Informationen fehlen.

Ich schätze, dies wäre sogar mit nur einem API-Schlüssel zur anderen Website möglich? Vielleicht etwas wie ein Button/Bereich im Editor, in dem eine Liste von API-Schlüsseln und URLs für das Zielthema erstellt werden kann. Wenn Sie Änderungen an einem Quellthema vornehmen, können Sie auf etwas wie “Änderungen an Klone dieses Themas senden” klicken. Alles, was dies tun würde, ist, das Update an die Themen auf anderen Instanzen zu senden.

1 „Gefällt mir“

Legen Sie die Informationen an genau einer Stelle ab, wo jeder sie sehen kann. Ein Link ist der Weg dorthin.

Aber Sie haben geheime Informationen, die Sie woanders verfügbar machen möchten. Das ist etwas anderes. Mit einem Plugin ist das durchaus möglich. Es ist die Art von Problem, bei der die erzwungene Lösung 10-mal mehr Arbeit erfordert als das eigentliche Problem. (Ich verbringe zum Beispiel oft Stunden damit, eine Aufgabe zu automatisieren, die nur einmal erledigt werden muss.)

Aber Sie müssten es irgendwo ablegen, wo es nur für den Benutzer verfügbar ist, der es gepostet hat. Oder es seitenweit machen?

Auch hier müssen sie pro Benutzer und im Serializer nur für den aktuellen Benutzer sein (oder vielleicht im Benutzerprofil speichern?). Und Sie bräuchten eine Datenstruktur, um API-Schlüssel verschiedenen Seiten zuzuordnen. Das scheint etwas zu sein, von dem ich denke, dass ich es in 2-5 Stunden erledigen könnte.

Sie müssen also irgendwo die URL für die anderen Seiten speichern, die dieses Thema haben sollen. Wie Sie diesen Beitrag erstellen, könnte auch kompliziert sein; am einfachsten ist es, ihn von Hand zu erstellen und die URL dieses Themas auf der Quellseite einzufügen. Sie könnten das wahrscheinlich in dem Rohbeitrag in einer Art BBcode oder so etwas speichern. Das würde es Ihnen ermöglichen, eine Komponente zu erstellen, die den Button und den Link für jeden von ihnen erstellt, und dann hätten Sie Rails-Code, der einen Job in die Warteschlange stellt, der versuchen würde, ihn an die andere(n) Seite(n) zu senden. Aber die empfangenden Seiten bräuchten keinen Code – Sie könnten die API verwenden, um eine Bearbeitung des Beitrags zu senden.

Das scheint die Art von Sache zu sein, von der ich denke, dass ich sie in 5-10 Stunden erledigen könnte, aber wahrscheinlich doppelt so lange dauern würde. Wenn Ihnen das Spaß macht, könnte es ein cooles Projekt sein.

4 „Gefällt mir“

Dies könnte auch über eine externe kleine App erfolgen, die auf einen Webhook hört, der bei Änderungen auf Ihrer Quellseite gesendet wird, und dann die API verwendet, um auf der Spiegel-Website zu posten.

4 „Gefällt mir“

Ich habe wieder darüber nachgedacht.

Ein Plugin könnte ein benutzerdefiniertes Themafeld für die URL der Quelle des primären Dokuments hinzufügen. (Ich schätze, es bräuchte auch Felder für einen Remote-Benutzernamen und einen API-Schlüssel, wenn das Hauptdokument verborgen werden soll, was ich für Ihren Anwendungsfall halte, aber dieser Teil könnte warten. Oder vielleicht könnten sie in einem benutzerdefinierten Feld des Benutzers leben. Es wäre demjenigen überlassen, der den Schlüssel generiert hat, sicherzustellen, dass der API-Schlüssel Lesezugriff hat).

Beim Erstellen eines Themas würden Sie etwas wie “remote: https://meta.discourse.org/t/synchronising-crossposting-topics-across-different-discourse-sites/263269” eingeben und wenn das Thema erstellt wurde, würde Discourse den rohen Text des Remote-Themas abrufen, ihn in raw als Bearbeitung einfügen und das topic_custom_field mit der Remote-URL instanziieren, vielleicht mit einem “kopiert von url” am Anfang.

Zu diesem Zeitpunkt haben Sie das Remote-Thema lokal kopiert und eine Aufzeichnung davon.

Dann könnte es eine Schaltfläche “Quelle prüfen” geben, die das Remote-Thema abruft und die updated_at und vielleicht sogar die raw des Remote-Themas in anderen benutzerdefinierten Feldern speichert (ein Job könnte dies auch periodisch tun und etwas UX sparen). Sie könnten dann eine Update-Schaltfläche haben, die die vorhandene raw durch die Remote-Version als Bearbeitung ersetzt.

Wenn die primäre Seite öffentlich ist, ist dieser Teil wirklich einfach. Das Hinzufügen eines API-Schlüssels zum Abrufen von einer privaten Seite verkompliziert die Dinge, die Verwaltung einer Reihe von API-Schlüsseln über mehrere Seiten hinweg, verkompliziert es weiter. Wenn die ursprüngliche Quelle ersetzt werden müsste, könnten Sie das vielleicht mit der Remap-Rake-Aufgabe tun oder die Möglichkeit hinzufügen, das benutzerdefinierte Feld mit der Remote-URL zu bearbeiten, wenn Sie es benötigen.

Dieser Teil ist kostenlos, da diese Lösung die sekundären Websites die Daten von der primären abrufen lässt.

Richtig. Und es kann einen Link zurück zur Quellseite geben, damit die Leute zur Quelle gehen können, um diese Kommentare zu sehen, oder sie vielleicht sogar über Embed comments from Discourse in your single page app einbetten.

Wenn Sie überhaupt ein Budget dafür haben, können Sie mich gerne kontaktieren.

1 „Gefällt mir“

Hallo Jay,

Das beschäftigt mich schon eine Weile, auch wenn es bis vor kurzem kaum Fortschritte gab. Leider ist die Synchronisierung von Discourse zu Discourse für mich weniger wertvoll geworden (es waren nur eine Handvoll Themen), aber gestiegen ist der Wunsch, Markdown-Dateien von anderen Plattformen im Allgemeinen zu synchronisieren.

Unser dominierender Anwendungsfall im Unternehmen war es, Readmes und Wikis von GitLab-Projekten in Discourse verfügbar zu machen (für Feedback und Suche), wobei die GitLab-Datei die einzige Quelle der Wahrheit bleibt. Mein Mangel an Ruby-Kenntnissen führte zu einem Python-Skript, das in seiner Implementierung definitiv übertrieben ist, aber in seiner Funktionalität zufriedenstellend ist. Die erste brauchbare Version macht auch einige der von Ihnen skizzierten Dinge. Einige Funktionen:

  • Enthält einen Link zur Originalquelle (Datei in GitLab)
  • Enthält einen Link zur spezifischen Revision (GitLab-Commit-Nummer)
  • Verarbeitet Bilder und Bild-URLs
    • Lädt Bilder aus dem GitLab-Repository herunter
    • Lädt sie nach Discourse hoch
    • Ersetzt die ursprüngliche Bild-URL durch die Kurz-URL des Uploads
  • Fügt ein Tag “synced_with_gitlab” hinzu, damit sie leicht zu finden sind

Es funktionierte mehr oder weniger genauso gut mit GitHub. Beide haben mehr oder weniger den gleichen Markdown-Geschmack, was es ziemlich reibungslos macht.

Ich würde das gerne Open Source machen, aber ich muss sehen, was die Rechtsabteilung sagt. Außerdem ist es immer noch ein etwas hackischer Python-Matsch. Die Absicht ist, dies irgendwann in ein Ruby-Plugin umzuwandeln, aber ich muss sehen, ob ich die nötige Zeit dafür finde.

3 „Gefällt mir“

Stimmt. Ich versuche mich erneut an diesem Projekt und schaue mir gerade an, wie ich eine Datenbank mit Folgendem erstellen kann:

  1. Eine Tabelle pro Plattform (Discourse-Tabelle, Gitlab-Tabelle usw.), um mögliche Nuancen zu berücksichtigen
  2. Jede Plattformtabelle unterstützt Webhooks und Abfragen über API-Schlüssel
  3. Datenbank- oder API-Schlüsselverschlüsselung – derzeit denke ich, dass es am besten ist, die gesamte Datenbank zu verschlüsseln und über das Skript und eine Passphrase mit ihr zu interagieren

Die Discourse-Tabelle würde wie folgt aussehen:

Typ Intervall (Min.) Letzter Lauf (Min.) src_domain src_post_ID src_usr src_key tgt_domain tgt_post_id tgt_usr tgt_key
Webhook - 120 meta.discourse.com 1280952 - - discourse.mysite.com 120 Tris xyz12345
Polling 60 40 meta.discourse.com 1280953 Tris20 12345xyz discourse.mysite.com 121 Tris xyz12345
Polling 60 35 meta.discourse.com 1750968 Tris20 12345xyz discourse.mysite.com 221 Tris xyz12345
Polling 60 40 meta.discourse.com 1123292 Tris20 12345xyz discourse.mysite.com 131 Tris xyz12345
Webhook - 4800 meta.discourse.com 1283678 - - discourse.mysite.com 129 Tris xyz12345

Klingt das verrückt und übermäßig kompliziert? Ein Teil von mir möchte eine richtige Lösung dafür bauen, was bedeuten würde, beide Möglichkeiten zu berücksichtigen – Webhook und Polling.

Außerdem wäre ich sehr dankbar für Vorschläge zur sicheren Aufbewahrung des Datenbankinhalts. Derzeit denke ich daran, die Datenbank mit einer Passphrase zu verschlüsseln, die beim Starten des Skripts als Argument übergeben werden muss, z. B.

discourse-sync run password123

Nur zur Inspiration, der Webhook kann super schnell sein:


Dies waren zwei Themen auf zwei verschiedenen Instanzen. Das Thema links ist das Quellthema, das Thema rechts ist das Zielthema.

Es gibt eine Rails-Methode, um ein einzelnes Feld zu verschlüsseln. Das habe ich in meinem Dashboard gemacht.

Siehe Active Record Encryption — Ruby on Rails Guides

Sowohl Polling als auch Webhooks scheinen redundant zu sein. Ich würde einen Ansatz wählen.

1 „Gefällt mir“

Ich glaube, das ActivityPub-Plugin wird in naher Zukunft eine Discourse-zu-Discourse-Föderation ermöglichen, wenn das auch eine Möglichkeit ist?

3 „Gefällt mir“

Was das wert ist, diese Idee ist mir auch schon ein paar Mal gekommen. Ich bin mir nicht sicher, ob wir hier schon ein eigenes Thema dazu haben, aber wenn nicht, sollten wir das tun!

1 „Gefällt mir“

Theoretisch stimme ich zu, leider verkompliziert die Unternehmenswelt die Dinge:

  1. Webhooks sind im Unternehmen aufgrund von IT-Richtlinien nicht möglich (wir werden von CDCK außerhalb des Unternehmens gehostet + können kein Portforwarding in die Außenwelt zulassen, ohne einen aufwändigen Prozess) - daher ist die API-Version ein Muss
  2. Webhooks sind für alle anderen schnell, schön und absolut vernünftig, daher ist es sinnvoll, sie auch zu unterstützen :slight_smile:

Ich habe dies vor einiger Zeit grob umgesetzt, es funktioniert einwandfrei, ist aber nicht erweiterbar. Schlechte Designentscheidung meinerseits: Ich habe die Idee untersucht, ein Thema mit einer Markdown-Tabelle als Eingabe zu verwenden. Großartig, bis man 30+ Einträge hat, dann ist es ein Durcheinander.

Teil des Discourse-zu-Discourse-Anwendungsfalls, den ich sehe, ist: Ein einziger Wahrheitsort für Dokumentation (Meta) synchronisiert sich mit den jeweiligen Beiträgen in anderen Instanzen. Das bedeutet, wenn das Team Core ändert und die Benutzerdokumentation Meta aktualisiert, habe ich eine aktuelle Version dieser Dokumentation in meiner Instanz nativ, damit alle Benutzer sie finden können.

Für V2 plane ich eine SQLite-Datenbank als Eingabe wie oben und werde wahrscheinlich diesmal in Rust statt Python schreiben.

Unten ist eine grobe Skizze.

graph TB
    A[terminal] -- ~\u003ediscourse-sync run $PASSWORD --\u003e B[Rust Script]
    B -- SQLCipher:Decrypt DB using Password --\u003e C[( sqlite: sources-and-targets')]
    C -- 'Discourse' Table Data --\u003e B
    B -.-\u003e D{Decision on Type}
    D -- Webhook --\u003e E[Listen for Webhook Info]
    D -- Polling --\u003e F[Polling API]
    E --\u003e G[Receive New Information]
    F --\u003e G
    G --\u003e H[Parse and Process Data]
    H --\u003e I[POST\n tgt_domain, tgt_usr, tgt_key, post_id]
    I --'raw' and images--\u003e J[ Target Post ]

    subgraph Rust Script Operations
    B
    D
E
F
G
H
I
    end

Ich würde mich sehr über Feedback und Vorschläge dazu freuen :slightly_smiling_face:

1 „Gefällt mir“

@angus wahrscheinlich für Sie von Interesse, ich denke, wir haben das meiste davon bereits mit dem ActivityPub-Plugin gelöst

6 „Gefällt mir“

Ja, dies wird nun tatsächlich durch das ActivityPub-Plugin unterstützt. Wir sind sehr nah dran, es intern zu verwenden, um Dokumentationen zwischen Meta und einer internen Instanz zu synchronisieren. Es steht tatsächlich nächste Woche auf meiner To-Do-Liste.

7 „Gefällt mir“

Gilt dies für beide Instanzen? z. B. Quelle ist öffentlich, aber Ziel ist privat, würde dies trotzdem funktionieren?

So gut das ActivityPub-Plugin auch aussieht, ich befürchte, dass es private Instanzen möglicherweise nicht berücksichtigt.

Es gilt auch für private Instanzen. In der aktuellen Version des AP-Plugins können private Instanzen Kategorien in öffentlichen Discourse-Instanzen folgen und somit veröffentlichte Aktivitäten von diesen Instanzen empfangen. (Inhalte in der privaten Instanz werden jedoch nicht veröffentlicht, daher ist es eine Einwegsynchronisierung, nur von öffentlich zu privat.)

3 „Gefällt mir“

Es scheint also, dass ActivityPub auf folgende Weise funktionieren kann:

Öffentlich --:white_check_mark: Öffentlich
Öffentlich --:white_check_mark: Privat

Privat --:x: Öffentlich
Privat --:x: Privat

Dies ist sicherlich in vielen Fällen nützlich, z. B. zum Übertragen von Dokumentationen von Meta. Leider erfüllt dies noch nicht einen meiner Anwendungsfälle, nämlich das Posten von einer privaten Instanz an eine andere private Instanz. Wenn ich mich nicht irre, ist meine Annahme richtig, dass das ActivityPub-Plugin diese Anwendungsfälle in Zukunft wahrscheinlich nicht unterstützen wird? Es sieht für mich so aus, als wäre ActivityPub mit dem Gedanken an öffentlich-zu-öffentlich entwickelt worden.

2 „Gefällt mir“

@Tris20 Interessant! Danke für das Teilen deiner Gedanken und einiger Details dazu.

Was du beschrieben hast, ist (eines der) Probleme, für deren Lösung ActivityPub entwickelt wurde. Ich möchte deine Begeisterung nicht zu sehr dämpfen, aber ehrlich gesagt wirst du auf eine breite Palette von Herausforderungen stoßen, wenn du versuchst, dies auf die von dir beschriebene Weise zu erreichen. Ich werde dir keine erschöpfende Aufzählung aller Herausforderungen geben, die du meistern musst, aber um dir eine Vorstellung zu geben: Das ActivityPub-Plugin hat bereits fast 700 rspec-Tests und nach einem Jahr Entwicklung unterstützt es erst seit kurzem die vollständige Synchronisation von Thema zu Thema.

Es gibt keine inhärente Einschränkung, die ich erkennen kann, um die Veröffentlichung von Private-to-Public und Private-to-Private über ActivityPub zu unterstützen. Die Frage ist, wie Zugangs- und Sicherheitsbedenken bei der Arbeit mit privaten Instanzen berücksichtigt werden.

Wenn ich einen Vorschlag machen dürfte: Denke vielleicht darüber nach, wie du auf der Arbeit aufbauen kannst, die das ActivityPub-Plugin (und der ActivityPub-Standard) in dieser Hinsicht bereits geleistet hat. Es gibt tatsächlich eine Lösung für das Problem der Synchronisation von Discourse-zu-Discourse-Inhalten, die derzeit funktioniert. Sie deckt deinen Anwendungsfall noch nicht ab, aber sie löst die Mehrheit der Probleme, die du lösen musst, um deine Bedürfnisse zu erfüllen.

Vielleicht könntest du darüber nachdenken, wie eine Private-to-Private-Synchronisation im Plugin funktionieren könnte, d. h. wie die Zugangs- und Sicherheitsfragen geklärt werden könnten? Dann könnten wir vielleicht sogar gemeinsam an einem PR arbeiten, um es als Funktion hinzuzufügen. Vielleicht erreichst du einen Punkt, an dem du das Gefühl hast, dass es im Kontext des Plugins (oder des ActivityPub-Standards) wirklich nicht möglich ist, das zu erreichen, was du erreichen willst, aber die Arbeit, die du geleistet hast, um diesen Punkt zu erreichen, wäre effektiv die gleiche Arbeit, die du für eine unabhängige Lösung benötigen würdest, sodass sie nicht umsonst wäre.

Es gibt viele kluge Leute in der ActivityPub-Welt, und ich wäre nicht überrascht, wenn diese Art von Problem (d. h. private Veröffentlichung) bereits eingehend betrachtet wurde. Ein Ort, an dem du möglicherweise einige frühere Arbeiten dazu findest, ist SocialHub, das Hauptforum der ActivityPub-Community (natürlich Discourse), das jetzt das Discourse ActivityPub-Plugin verwendet :slight_smile:

7 „Gefällt mir“