Atualmente, tenho um tópico em um site, que gostaria de ter também em outro site. Embora eu pudesse criar um link para ele, o que eu realmente gostaria é da capacidade de editá-lo em qualquer um dos sites, e que as alterações fossem refletidas em ambos. Isso me poupa de ter uma versão do tópico que está muito desatualizada e, em vez disso, mantém ambos os tópicos constantemente atualizados. Também fornece uma maneira de descentralizar as informações do meu site.
Algumas ideias sobre a funcionalidade
Como ponto de partida, um site permaneceria como host/proprietário do tópico, e o(s) outro(s) essencialmente o espelhariam. Indo mais longe, até me pergunto se um tópico poderia ser herdado por um tópico espelhado se o original for excluído.
O tópico deve manter a capacidade de ser ocultado, fechado, etc. nos sites espelhados.
As respostas não devem ser sincronizadas – cada site tem uma base de usuários diferente, então não consigo ver como a sincronização de respostas funcionaria.
Entendo que a implementação de tal recurso está longe de ser trivial, mas estou me perguntando se isso já foi considerado e quais resultados/experimentos já existem?
Você poderia dar um pouco mais de informação sobre isso? Do meu ponto de vista, isso reduziria o conteúdo duplicado.
Backup não é o objetivo, mas sim criar um único ponto de verdade para tópicos que fariam sentido em mais de um site.
Para dar um exemplo concreto, estou pensando em mudar meu Visto. Tenho um Tópico no meu Discourse privado que é basicamente uma lista de verificação do que precisa ser feito. No entanto, meus amigos também podem achar isso útil, então crio o mesmo tópico em nosso Discourse compartilhado. O problema é que preciso sincronizar as informações de ambos os tópicos separadamente, em vez de apenas atualizar um único tópico. Isso significa que muitas vezes um dos tópicos está faltando informações importantes.
Acho que isso pode ser possível apenas com uma chave de API para o outro site? Talvez algo como um botão/seção no editor onde uma lista de chaves de API e URLs para o tópico de destino possa ser criada. Quando você faz alterações em um tópico de origem, pode clicar em algo como “enviar alterações para clones deste tópico”. Tudo o que isso faria seria enviar a atualização para os tópicos em outras instâncias.
Coloque as informações em um único lugar onde todos possam ver. Um link é como fazer isso.
Mas você tem informações secretas que deseja disponibilizar em outro lugar. Isso é outra coisa. É bem possível com um plugin. É o tipo de problema em que a solução improvisada exige 10 vezes mais trabalho do que o problema real exigiria. (Eu costumo passar horas automatizando uma tarefa que só precisa acontecer uma vez, por exemplo.)
Mas você precisaria colocá-lo em algum lugar onde ele estaria disponível apenas para o usuário que o postou. Ou torná-lo para todo o site?
Novamente, eles precisam ser por usuário e no serializador apenas para o usuário atual (ou talvez armazená-lo no perfil do usuário?). E você precisaria de alguma estrutura de dados para mapear chaves de API para os diferentes sites. Parece algo que eu pensaria que poderia fazer em 2-5 horas.
Então você precisa armazenar em algum lugar o URL para os outros sites que deveriam ter este tópico. Como criar essa postagem também pode ser complicado; a maneira mais fácil é criá-la manualmente e incluir o URL desse tópico no site de origem. Você provavelmente poderia armazenar isso na postagem bruta em algum tipo de BBcode ou algo assim. Isso permitiria que você criasse um componente que criaria o botão e o link para cada um deles e, em seguida, você teria código Rails que enfileiraria um trabalho que tentaria postá-lo no(s) outro(s) site(s). Mas os sites receptores não precisariam de nenhum código - você poderia usar a API para enviar uma edição para a postagem.
Parece o tipo de coisa que eu pensaria que poderia fazer em 5-10 horas, mas provavelmente levaria o dobro disso. Se isso é divertido para você, então pode ser um projeto legal.
Isso também poderia ser feito através de um pequeno aplicativo externo que escutaria um webhook enviado em edições em seu site de origem e, em seguida, usaria a API para postar no site espelhado.
Um plugin poderia adicionar um campo de tópico personalizado para o URL da fonte do documento principal. (Acho que também precisaria de campos para um nome de usuário remoto e chave de API se o documento principal for oculto, como acho que é o seu caso, mas essa parte poderia esperar. Ou talvez eles pudessem viver em um campo personalizado do usuário. Caberia a quem gerou a chave garantir que a chave de API tenha privilégios somente leitura).
Ao criar um tópico, você digitaria algo como “remote: https://meta.discourse.org/t/synchronising-crossposting-topics-across-different-discourse-sites/263269” e, quando o tópico fosse criado, o Discourse puxaria o texto bruto do tópico remoto, o inseriria em raw como uma edição e instanciaria o topic_custom_field com o URL remoto, talvez adicionando um “copiado de url” no topo.
Neste ponto, você copiou o tópico remoto localmente e tem um registro dele.
Poderia haver então um botão “verificar fonte” que puxaria o tópico remoto e salvaria o updated_at do tópico remoto e talvez até o raw em outros campos personalizados (um trabalho também poderia fazer isso periodicamente, economizando um pouco de UX). Você poderia então ter um botão de atualização que substituiria o raw existente pelo remoto como uma edição.
Se o site principal for público, então essa parte é realmente fácil. Adicionar uma chave de API para puxar de um site privado complica as coisas, gerenciar um conjunto de chaves de API em vários sites, complica ainda mais. Se a fonte original precisasse ser substituída, você poderia talvez fazer isso com a tarefa rake de remapeamento, ou adicionar a capacidade de editar o campo personalizado com o URL remoto quando você precisasse.
Esta parte vem de graça, já que esta solução tem os sites secundários puxando os dados do principal.
Certo. E pode haver um link de volta para o site de origem, para que as pessoas possam ir à origem para ver esses comentários, ou talvez até incorporá-los via Embed comments from Discourse in your single page app.
Se você tiver algum orçamento para isso, sinta-se à vontade para me contatar.
Tenho pensado nisso há um tempo, embora não tenha progredido muito até recentemente. Infelizmente, a sincronização do Discourse para Discourse perdeu valor para mim (eram apenas alguns tópicos), mas o que ganhou valor foi o desejo de sincronizar arquivos markdown de outras plataformas em geral.
Nosso caso de uso dominante na empresa era ter readmes e wikis de projetos do Gitlab disponíveis dentro do Discourse (para feedback e pesquisa), mas com o arquivo do gitlab permanecendo a única fonte da verdade. Minha falta de conhecimento em ruby resultou em um script python que é definitivamente exagerado em sua implementação, mas satisfatório em sua funcionalidade. A primeira versão decente faz parte do que você descreveu também. Algumas funcionalidades:
contém link para a fonte original (arquivo no gitlab)
contém link para a revisão específica (commit # do gitlab)
lida com imagens e URLs de imagens
baixa imagens do repositório gitlab
as carrega no discourse
substitui a URL original da imagem pelo shorturl do upload
adiciona uma tag “synced_with_gitlab” para que seja fácil encontrá-las todas
Funcionou mais ou menos da mesma forma com o github também. Ambos têm mais ou menos o mesmo sabor de markdown, tornando-o muito elegante.
Eu adoraria tornar isso open source, mas precisarei ver o que o jurídico diz. Além disso, ainda é uma bagunça meio “hacky” em python. A intenção é converter isso em um plugin ruby em algum momento, mas precisarei ver se consigo encontrar o tempo necessário.
Verdade. Estou tentando novamente este projeto e atualmente estou olhando para criar um banco de dados com:
Uma tabela por plataforma (tabela Discourse, tabela Gitlab etc.) para contabilizar possíveis nuances
Cada tabela de plataforma suportando webhooks e polling via chave de API
Criptografia de banco de dados ou chave de API - atualmente, estou pensando que é melhor criptografar todo o banco de dados e interagir com ele via script e uma senha
Isso parece loucura e excessivamente complicado? Parte de mim quer construir uma solução adequada para isso, o que significaria contabilizar ambas as possibilidades - webhook e polling.
Além disso, ficaria muito grato por sugestões em termos de manter o conteúdo do banco de dados seguro. O pensamento atual é criptografar o banco de dados com uma senha que deve ser fornecida como argumento ao iniciar o script, por exemplo:
discourse-sync run password123
Apenas como inspiração, o webhook pode ser super rápido:
Estes eram dois tópicos em duas instâncias diferentes. O tópico à esquerda é o tópico de origem, o tópico à direita é o destino.
Pelo que vale, essa ideia também me ocorreu várias vezes. Não tenho certeza se já temos um tópico dedicado a isso aqui, mas se não tivermos, deveríamos!
Concordo teoricamente, infelizmente o mundo corporativo complica as coisas:
Webhooks não são possíveis na empresa devido a políticas de TI (somos hospedados fora da empresa pela CDCK + não podemos permitir encaminhamento de porta para o mundo exterior sem um processo pesado) - portanto, a versão da API é obrigatória
Webhooks são rápidos, adoráveis e perfeitamente razoáveis para todos os outros, então faz sentido acomodá-los também
Então, consegui uma versão aproximada disso há um tempo, funciona muito bem, mas não é extensível. Mau design da minha parte: eu estava explorando a ideia de usar um tópico com uma tabela markdown como entrada. Ótimo até ter mais de 30 entradas, aí vira uma bagunça.
Parte do caso de uso de discourse para discourse que vejo é: um único ponto de verdade para documentação (Meta) sincroniza com posts respectivos em outras instâncias. Isso significa que se a equipe mudar o Core e atualizar a documentação do usuário Meta, terei uma versão atualizada dessa documentação em minha instância nativamente para todos os usuários encontrarem.
Para a V2, estou planejando um banco de dados SQLlite como entrada, como acima, e provavelmente escrevendo em Rust desta vez em vez de python.
Abaixo está um esboço aproximado.
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
Ficaria muito feliz em receber feedback e sugestões sobre isso
Sim, isso agora é de fato suportado pelo plugin ActivityPub. Estamos muito perto de usá-lo internamente para sincronizar a documentação entre o meta e uma instância interna, está na minha lista de tarefas para a próxima semana, de fato.
Ele se aplica a instâncias privadas, na versão atual do plugin AP, instâncias privadas podem seguir categorias em instâncias públicas do Discourse e, portanto, receber atividades publicadas dessas instâncias. (Mas o conteúdo na instância privada não é publicado, então é uma sincronização unidirecional, apenas do público para o privado.)
Portanto, parece que o ActivityPub pode funcionar da seguinte maneira:
Público -- Público
Público -- Privado
Privado -- Público
Privado -- Privado
Isso é certamente útil em muitos casos, como a transmissão de documentação da Meta. Infelizmente, isso ainda não atende a um dos meus casos de uso, que é postar de uma instância privada para outra instância privada. Pelo que pesquisei, estou correto em pensar que o Plugin ActivityPub provavelmente não suportará esses casos de uso no futuro? Parece-me que o ActivityPub foi projetado com o público para o público em mente.
@Tris20 Interessante! Obrigado por compartilhar seus pensamentos e alguns detalhes sobre isso.
O que você descreveu é (um dos) problemas que o ActivityPub foi construído para resolver. Não quero desanimar muito você, mas, para ser honesto, você enfrentará uma ampla gama de desafios ao tentar realizar isso da maneira que está descrevendo. Não darei uma contagem exaustiva de todos os desafios que você precisará superar, mas para ter uma ideia, o plugin ActivityPub já tem quase 700 testes rspec e, após um ano de desenvolvimento, ele só recentemente passou a suportar a sincronização completa de tópico a tópico.
Não há nenhuma limitação inerente que eu possa ver para suportar a publicação de Privado para Público e Privado para Privado via ActivityPub. A questão é garantir que as preocupações de acesso e segurança sejam atendidas ao trabalhar com instâncias privadas.
Se eu pudesse fazer uma sugestão. Talvez pense em como você pode construir sobre o trabalho que o plugin ActivityPub (e o padrão ActivityPub) já fez a esse respeito. Na verdade, existe uma solução para o problema de sincronização de conteúdo de Discourse para Discourse que funciona atualmente. Ele ainda não cobre seu caso de uso, mas resolve a maioria dos problemas que você precisará resolver para atender às suas necessidades.
Talvez você possa pensar em como uma sincronização privada para privada funcionaria no plugin, ou seja, como as questões de acesso e segurança poderiam ser abordadas? Então, talvez você e eu possamos até trabalhar em um PR juntos para adicioná-lo como um recurso. Talvez você chegue a um ponto em que sinta que realmente não é possível alcançar o que deseja alcançar no contexto do plugin (ou do ActivityPub como um padrão), mas o trabalho que você teria feito para chegar a esse ponto seria efetivamente o mesmo trabalho que você precisaria fazer para uma solução independente, então não seria em vão.
Existem muitas pessoas inteligentes no mundo do ActivityPub, e eu não ficaria surpreso se esse tipo de problema (ou seja, publicação privada) já foi considerado em profundidade antes. Um lugar onde você pode encontrar alguma arte anterior sobre isso é o SocialHub, o principal fórum da comunidade ActivityPub (Discourse, é claro), que agora está usando o plugin Discourse ActivityPub