Problema de CORS ao postar no Discourse a partir do Obsidian

Comecei a usar o Obsidian para escrever, então pensei em criar um plugin do Obsidian para postar no meu fórum Discourse. Em teoria, isso é fácil de fazer, mas a resposta da minha primeira tentativa foi:

Acesso a fetch em 'http://localhost:4200/posts.json' de origem 'app://obsidian.md' foi bloqueado pela política CORS: A resposta à solicitação preflight não passa na verificação de controle de acesso: Nenhum cabeçalho 'Access-Control-Allow-Origin' está presente no recurso solicitado. Se uma resposta opaca atende às suas necessidades, defina o modo da solicitação como 'no-cors' para buscar o recurso com CORS desativado.

A solicitação está sendo feita do cliente, mas o cliente é o dispositivo do usuário. Se estou entendendo as coisas corretamente, isso é seguro:

import DiscoursePlugin from "./main";
import { TFile } from "obsidian";

export async function publishToDiscourse(
	plugin: DiscoursePlugin,
	activeFile: TFile
): Promise<{ message: string }> {
	try {
		const content = await plugin.app.vault.read(activeFile);
		const baseUrl = plugin.settings.baseUrl;
		const apiKey = plugin.settings.apiKey;

		const headers = new Headers({
			"Content-Type": "application/json",
			"Api-Key": apiKey,
			"Api-Username": "scossar", // isso precisa ser uma configuração
		});
		const body = JSON.stringify({
			title: activeFile.name,
			raw: content, // TODO: analisar o conteúdo para corrigir links internos
			category: 1, // isso precisa ser uma configuração
		});
		// TODO: verificar se a nota já foi publicada
		const url = `${baseUrl}/posts.json`;

		const response = await fetch(url, { method: "POST", headers, body });

		if (!response.ok) {
			console.error("Erro ao publicar no Discourse:", response.status);
			return { message: "Erro ao publicar no Discourse" };
		}
        
		const jsonResponse = await response.json();
		// TODO: usar a resposta para adicionar uma propriedade de arquivo discoursePostId
		console.log(`jsonResponse: ${JSON.stringify(jsonResponse, null, 2)}`);
		return { message: "sucesso" };
	} catch (error) {
		console.error("Erro ao publicar no Discourse:", error);
		return { message: `Erro: ${error.message}` };
	}
}

É possível que haja uma solução óbvia que estou perdendo. Se não, há alguma maneira de o Discourse permitir que funcione? Um plugin Obsidian Discourse parece que poderia ser útil. (Uma implementação adequada seria mais complexa do que o que postei acima.)

3 curtidas

Fiquei confuso no início, mas entendi, você configurou seu próprio aplicativo Obsidian com sua chave e somente você tem acesso a ele. Isso não está acontecendo em um navegador, então as pessoas na internet não têm sua chave.

Eu tenho um plugin Discourse que processa formulários arbitrários (você pode usá-lo em https://www.formhoster.com/). Eu queria torná-lo capaz de funcionar como o usuário atual se o usuário estivesse logado no site que processa o formulário e encontrei o que acho que é o mesmo problema CORS e desisti bem rápido. O meu era em um navegador em vez de um aplicativo como o Obsidian, mas acho que o problema pode ser semelhante.

Tudo isso para dizer que acho que não tenho boas ideias, mas espero que alguém mais tenha. :person_shrugging:

1 curtida

Certo, Obsidian é um aplicativo Electron que está rodando localmente. Ele usa armazenamento local, então a chave da API permanece no dispositivo do usuário.

Acontece que existe uma solução para o problema do CORS. Eu só testei no meu computador desktop até agora. Além disso, Discourse é ótimo!

O código que postei acima precisou ser modificado para:

import DiscoursePlugin from "./main";
import { requestUrl, TFile } from "obsidian";

//...

		const response = await requestUrl({
			url: url,
			method: "POST",
			contentType: "application/json",
			body,
			headers,
		});
//...

A próxima questão será sobre permitir que os usuários solicitem chaves de API de usuário do aplicativo, mas isso é um problema separado.

3 curtidas

Este é um plugin em que você está trabalhando? Tenho um usuário (e eu mesmo) que adoraria poder usar isso. Posso tentar recriar o que você fez, mas se houver uma maneira mais fácil…

Foi o começo de algo. Para meus próprios propósitos, estou mais interessado em um aplicativo de linha de comando (CLI) que permite sincronizar um Cofre do Obsidian com um fórum Discourse. Um aplicativo CLI facilita o gerenciamento da sincronização de um cofre inteiro ou subdiretório, lida com links internos em Notas, gerencia uploads, etc. A abordagem de aplicativo CLI também se encaixa em outro trabalho que estou fazendo, então estou aprendendo algo útil com isso.

A grande desvantagem do aplicativo CLI é que ele exige que qualquer pessoa que o use tenha o Ruby instalado em seu computador. Ele também só funcionará em computadores desktop. Haveria alguns desafios técnicos com o uso em computadores com Windows.

Postarei um link para o aplicativo aqui assim que estiver pronto para ser compartilhado.

Se um plugin Obsidian Discourse, em oposição a um aplicativo CLI, for algo que as pessoas estão interessadas, eu o analisarei novamente no futuro. É possível que outra pessoa chegue a isso antes de mim. Não o levei muito além do exemplo de código postado acima.

1 curtida

Se é tudo o que você tem até agora, parece bem simples. Estou imaginando como isso lidaria com imagens neste momento. Eu estaria disposto a tentar. Eu nunca escrevi um plugin para Obsidian antes, mas pelo que eu vi (olhares de relance para outros plugins), não parece ter uma complicação terrível.

1 curtida

Este é o lugar para começar: Build a plugin - Developer Documentation. Eu gostaria de linkar o que eu fiz, mas é um dos poucos (espero que o único) projetos que eu não enviei para o Github antes de reinstalar o sistema operacional no meu computador na semana passada.

Você terá que descobrir como fazer isso com o Node. Existe um bom exemplo em Ruby aqui: discourse_api/examples/upload_file.rb at main · discourse/discourse_api · GitHub. Ele chama: discourse_api/lib/discourse_api/api/uploads.rb at main · discourse/discourse_api · GitHub.

Você pode compartilhar esta parte?

Hmm, perder esse código foi meio imprudente. Pelo que me lembro, eu estava importando DiscoursePlugin definido em main.ts para um arquivo que estava lidando com uma chamada para this.addCommand. Para começar, você pode fazer tudo em main.ts. Eu apenas seguiria o guia para começar com o plugin de exemplo. Comece mexendo nele e tentando fazer algo acontecer. Eu sei que usei esse plugin como modelo para o meu.

Existem alguns bons exemplos aqui também: obsidian-wordpress/src at main · devbean/obsidian-wordpress · GitHub.

1 curtida

Bem, vou deixar isso para lá por enquanto. Fiz um progresso razoável, mas há algumas coisas que não me agradam.

  • A chave da API é armazenada em texto simples (MUITO RUIM)
  • As imagens não são carregadas, há algumas configurações que ainda não explorei. Mas não gosto de configurar um bucket AWS S3. Vou olhar isso mais tarde.
  • Algumas escolhas estilísticas poderiam ser melhores, mas eu queria algo funcional
  • O README precisa ser atualizado. Mas, novamente, eu só queria algo funcional e postado.
1 curtida

Eu me pergunto se há uma maneira de contornar isso. No aplicativo CLI, eu a criptografo com uma senha. O usuário precisa fornecer a senha antes de qualquer operação que use a chave da API.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.