Todos os artigos

ResearchGo

Em breve

Uma plataforma para o CIBES Research Centre que leva um artigo acadêmico do envio à publicação. Pronta e nas últimas verificações antes do lançamento.

Meu papel
Arquitetura e desenvolvimento
Ano
2026
Feito com
Next.js · Supabase (EU) · Python · FastAPI · GROBID · OpenAlex · Stripe
AUTHOR: PDF OR WORDNEXT.JS + SUPABASEPYTHON WORKER: FASTAPI + GROBIDVERSIONS: ENCRYPTEDTRACK CHANGES, REFERENCE CHECKJOURNAL MATCH: OPENALEXSIGN INSIGNED JOBPARSED TEXTCOMPAREEMBEDDINGALL DATA IN THE EURULES ONLY, NO LANGUAGE MODELTEXT STAYS ON OUR WORKER

Role para o lado para ver o diagrama inteiro.

ArquiteturaUPLOAD → PARSE ON ARRIVAL → ENCRYPTED VERSIONS → EDITOR TOOLS

O problema

As submissões acadêmicas acontecem em trocas de e-mails e planilhas. Versões, comentários dos revisores e exigências das revistas se desencontram assim que mais de uma pessoa entra no processo, e os editores respondem às mesmas perguntas manualmente toda vez: qual versão é esta, o que mudou, as referências batem, qual revista combina?

Como foi construído

Um app em Next.js no Supabase, com todos os dados mantidos na UE. Os autores fazem login com ORCID ou e-mail e enviam um manuscrito em PDF ou Word. Um worker em Python (FastAPI) faz o parsing assim que o arquivo chega, os PDFs via GROBID, e devolve o texto estruturado por um callback assinado e com timestamp que rejeita jobs forjados ou repetidos. Cada parsing bem-sucedido vira uma nova versão, armazenada criptografada, mas só se o texto realmente mudou. Os editores comparam quaisquer duas versões com controle de alterações no estilo do Word, conferem as citações no texto com a lista de referências e recebem sugestões de revistas ordenadas por significado, usando embeddings calculados no nosso próprio worker com dados de revistas do OpenAlex. Os pagamentos rodam no Stripe.

Decisões importantes

  • Sem modelo de linguagem onde uma regra resolve

    O controle de alterações e a checagem de referências são código determinístico, sem IA envolvida. Um editor precisa confiar em um diff, e uma regra dá a mesma resposta toda vez.

  • Os manuscritos nunca saem dos nossos servidores

    O matching de revistas gera o embedding do artigo no nosso próprio worker, então nenhum texto do manuscrito vai para terceiros. O ranking é pura similaridade. Indexação, acesso aberto e taxas aparecem como etiquetas e nunca mudam a ordem.

  • Todo job é assinado

    Os jobs entre o web app e o worker levam uma assinatura HMAC e um timestamp. Um job forjado ou repetido é rejeitado, e, se um worker cai no meio de um job, o manuscrito não perde o lugar.

Código do repositório

apps/web/lib/versions/diff.tsControle de alterações entre duas versões de um manuscrito, sem modelo de linguagem
function diffParagraphPair(before: string, after: string): DiffSegment[] {
  return diffWordsWithSpace(before, after).map((part) => ({1
    op: part.added ? "ins" : part.removed ? "del" : "equal",
    text: part.value,
  }));
}
 
export function diffManuscripts(before: string, after: string): DiffResult {
  const a = splitParagraphs(before);
  const b = splitParagraphs(after);
  // …
  const parts = diffArrays(a, b);2
  let i = 0;
  while (i < parts.length) {
    const part = parts[i];
    if (!part) break;
    if (!part.added && !part.removed) {
      // …
      i++;
      continue;
    }
    const next = parts[i + 1];
    const paired = Boolean(part.removed && next?.added);3
    const removedList = part.removed ? part.value : [];
    const addedList = part.added ? part.value : paired && next ? next.value : [];
 
    const pairs = Math.min(removedList.length, addedList.length);
    for (let k = 0; k < pairs; k++) {
      const segments = diffParagraphPair(removedList[k] as string, addedList[k] as string);
      paragraphs.push({ kind: "changed", segments });
      // …
    }
    // …
    i += paired ? 2 : 1;
  }
 
  return { paragraphs, summary };
}
  1. Linha 2. Dentro de um parágrafo alterado, o diff é feito palavra por palavra e mantém os espaços, então fica parecido com o controle de alterações do Word.

  2. Linha 12. Primeira passada: um diff de maior subsequência comum sobre parágrafos inteiros encontra o que ficou, o que foi adicionado e o que foi removido.

  3. Linha 23. Uma remoção seguida diretamente de uma adição é um único parágrafo editado, não duas mudanças separadas.

Como está hoje

Pronta e nas últimas verificações. Entra no ar assim que elas forem concluídas.

Próximo artigoÜrünereklam