Este artigo é para muitos desenvolvedores e criadores temáticos que encontram os ajudantes padrão oferecidos por Ghost (https://ghost.org/docs/themes/helpers/) insuficiente. É completamente normal procurar maneiras de estender as capacidades de nossos temas que usam guidão fornecido pelo fantasma. Antes de publicar este artigo e encontrar uma solução para o meu tema, pesquisei a Internet inteira e conduzi uma análise do código -fonte do fantasma.
descobri que é possível estender o código -fonte do Ghost com ajudantes adicionais. Consegui isso adicionando um novo diretório no Current/Core/Frontend/Apps. Usei o exemplo de um "aplicativo" existente chamado AMP, cujo código é muito simples, para começar a criar um novo ajudante disponível no tema. Nesses aplicativos existentes, a estrutura é direta porque os ajudantes são registrados em LIB/ajudantes. No final do processo, você precisa adicionar o nome do seu diretório nos aplicativos à Current/Core/Shared/config/substituir.json na seção Apps.internal JSON.
Um exemplo de conteúdo do arquivo index.js em nosso aplicativo seria:
const path = require('path'); module.exports = { activate: function activate(ghost) { ghost.helperService.registerDir(path.resolve(__dirname, './lib/helpers')); } };
a seguir, no diretório Lib deste aplicativo, criamos uma pasta chamada Helpers. No interior, criamos um novo arquivo, que será o nome do ajudante a ser chamado em um modelo de guidão. Por exemplo, vamos nomear o valor superior.js.
abaixo está um exemplo do código desse ajudante, que simplesmente converte as letras do texto fornecido no argumento auxiliar na maçaneta:
const {SafeString, escapeExpression} = require('../../../../services/handlebars'); module.exports = function uppercase(text) { return `${text.toUpperCase()}`; };! Depois de reiniciar o fantasma, tudo deve estar pronto.
Método 2 (sem modificar o código principal)
a arquitetura que usaremos neste método:
Nginx Server ← Node.js Middleware ← Ghost Instância
O middleware é um servidor expresso em execução no Node.js com a biblioteca expreste-http-proxy (https://github.com/villadora/express-http-proxy), que simplifica significativamente o trabalho. Configuramos o proxy para se comunicar com a instância fantasma. A Biblioteca Express-Http-Proxy possui uma propriedade UserResDecorator que podemos usar para "decorar a resposta do servidor proxado". Simplificando, podemos modificar a resposta do fantasma antes de enviá -la para o navegador do usuário.
Nosso UserResDecorator será assíncrono para não bloquear o encadeamento principal. Voltaremos ao tópico do processamento assíncrono ao criar ajudantes. Por enquanto, você precisa saber que nem tudo o que as solicitações do navegador do usuário precisam ser decoradas. Portanto, a primeira etapa será verificar o cabeçalho do tipo de conteúdo da resposta do Ghost. Você pode fazer isso da seguinte
// onde 'proxyres' é sua resposta de proxy dentro de 'userResdecorator'
const contentType = proxyres.headers ['content-type'] || '';
if (! contentType.includes ('text/html')) {
// retorna conteúdo original se a resposta não for 'texto/html'
retornar proxyresdata;
}
Seja htmlContent = proxyresdata.toString ('utf8');
// Faça algo com 'htmlContent' e retorne
retornar htmlContent;
// Where 'proxyRes' is your proxy response inside 'userResDecorator' const contentType = proxyRes.headers['content-type'] || ''; if (!contentType.includes('text/html')) { // Return original content if response is not 'text/html' return proxyResData; } let htmlContent = proxyResData.toString('utf8'); // Do something with 'htmlContent' and return return htmlContent;
Neste artigo, criarei um ajudante personalizado no arquivo index.hbs (página inicial) do meu tema. Em um local visível no modelo do guidão, adiciono um exemplo de ajudante personalizado, nomeando -o {{hello_world}}.
⚠️ Então, eu o coloco em um local visível na página inicial - mas observe o que acontece quando eu refresco a página fantasma!
{{!
{{!After refreshing, I get an error message from Ghost because the {{hello_world}} helper doesn’t exist in Ghost's default helpers. For our logic to work, we must escape this helper so that it’s not treated as a helper by Ghost’s built-in Handlebars.
The correct way is to write this helper as \{{hello_world}}. This way, Ghost treats it as plain text. After refreshing the Ghost homepage, you should see the plain text {{hello_world}}. If this happens, you are on the right track. Let’s now return to the middleware server file, where we will use the response decorator.
⚠️ Remember to escape custom helpers in your theme! Don’t forget to add the \ character.
let htmlContent = proxyResData.toString('utf8');
// Compile Response HTML com guidão e modelo renderizado de retorno Seja htmlContent = proxyresdata.toString ('utf8'); const modelo = handlebars.compile (htmlcontent); htmlContent = modelo ({});// Compile response HTML with Handlebars, and return rendered template let htmlContent = proxyResData.toString('utf8'); const template = handlebars.compile(htmlContent); htmlContent = template({});
// retorna 'Olá do middleware!' Com o registro de data e hora atual handlebars.registerhelper ('hello_world', função (opções) { Retorne `Olá do Middleware! $ {new Date (). ToisSoString ()} `; });// Returns 'Hello from middleware!' with the current timestamp handlebars.registerHelper('hello_world', function (options) { return `Hello from middleware! ${new Date().toISOString()}`; });Nesta fase, você pode estender seu tema fantasma com ajudantes personalizados adicionais, que você adicionará ao código do servidor de middleware.
Segurança
Em algum momento, você pode querer devolver várias coisas com seus ajudantes. Por padrão, a biblioteca protege contra ataques XSS, mas quando você usa o método de segurança, essa proteção para de funcionar. Evite usá -lo sempre que possível.
outra coisa! Imagine um usuário adiciona esse ajudante na seção de comentários em uma postagem e adiciona conteúdo malicioso no parâmetro. Esteja atento à segurança. Por exemplo, se você renderizar todos os HTML completamente, poderá estar vulnerável a ataques XSS. É recomendável compilar e renderizar guidão.js em áreas específicas e fechadas. Você pode usar a biblioteca Cheerio (https://cheerio.js.org/) para analisar HTML e renderizar guidão quando necessário. Aqui está um exemplo de como você pode se proteger modificando o código de renderização anterior:
// renderizar guidão apenas dentrocom> Neste código, nossos ajudantes e guidão personalizados são renderizados apenas dentro de umcontêiner com>// Render handlebars only insidewith>In this code, our custom helpers and Handlebars are rendered only within a
container with>Asynchronous Processing
If you intend to create dynamic helpers that return more complex data, you’ll probably need to implement asynchronous helpers in Handlebars over time. This is useful in cases like:
- Fetching values from a database (e.g., Ghost database)
- Sending API requests and processing their responses
You can use an extension called handlebars-async-helpers (https://www.npmjs.com/package/handlebars-async-helpers) for this purpose. It enables asynchronous operations in Handlebars.js, making potentially lengthy and dynamic tasks possible. Here’s a simple example of how you can implement asynchronous processing in your middleware:
// Register async helpers with Handlebars const hb = asyncHelpers(handlebars); hb.registerHelper('hello_world', async function (options) { // You can use await's here! // ... });Comunicação e objetos do banco de dados
Se você deseja fazer consultas de banco de dados em fantasma buscar, por exemplo, a postagem atual, é possível e não é difícil. Você pode usar uma biblioteca como o Knex (https://knexjs.org/), que é um construtor de consultas SQL claro e rápido. Lembre-se de que você precisará de hidroces-helpers para isso. Configure o Knex corretamente para se conectar ao banco de dados do Ghost.
inicialize Knex como a variável DB e tente o seguinte código:
// Retorna o título atual da postagem do banco de dados HB.RegisterHelper ('post_title', função assíncrona (opções) { const uuid = options.hash.uuid; tentar { const {title} = aguarda db ("postagens") .Select ("título") . Onde ("uuid", uuid) .limit (1) .primeiro(); título de retorno; } catch (error) {return `error: $ {error.message}`; } });// Return current post title from database hb.registerHelper('post_title', async function (options) { const uuid = options.hash.uuid; try { const { title } = await db("posts") .select("title") .where("uuid", uuid) .limit(1) .first(); return title; } catch (error) { return `Error: ${error.message}`; } });você também pode usar o AXIOS para fazer solicitações HTTP para a API de conteúdo fantasma, mas isso é significativamente mais lento que a comunicação direta do banco de dados.
Desempenho
eu sei que uma solução baseada em middleware pode não ser a melhor em termos de velocidade, mas eu pessoalmente uso essa solução e não notei uma queda significativa nos tempos de carregamento da página. O tempo médio de resposta para uma única solicitação estava abaixo de 100ms (de acordo com o monitor de status expresso), e eu uso um ajudante personalizado que recupera alguns valores do banco de dados em todas as páginas.
Você pode, é claro, adicionar mecanismos de cache para melhorar o desempenho do middleware ou usar soluções alternativas em vez de expresso-http-proxy.
Implementação da arquitetura
use o Docker ou outro mecanismo de contêinerização. Eu o usei no meu projeto e funciona muito bem. Adicione imagens de fantasma e banco de dados para a imagem Ghost, Nginx e Node.js. Conecte -os a uma rede compartilhada (Driver: Bridge), configure o Nginx e o Node.js Server de acordo - é tudo muito simples!
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3