Nessa publicação vou falar um pouco de como é o meu fluxo de trabalho para publicar conteúdo. Eu acho bem simples e por isso resolvi compartilhar com vocês.
A primeira parte é conhecer a infraestrutura que construí para meu servidor. Meu blog e a pagina inicial residem no meu Raspberry Pi Zero W que está ligado no meu roteador com um pequeno cabo USB, para receber energia. E o roteador ligado no No-break. Essa parte foi feita dessa maneira pois queria me aventurar no mundo da hospedagem. E gostei dos resultados, porém é bem mais fácil pagar uma VM em qualquer nuvem.
Alguns opcionais que eu decidi ter, e com muita dificuldade, foram:
No servidor eu decidi usar templates, o go tem um pacote nativo muito bom. Por isso eu tenho um template da pagina inicial do blog, com a listagem dos artigos; e um template do artigo. Porém se eu quisesse poderia ter usado paginas estáticas também, isso seria ainda mais simples.
O código de listagem de artigos é simples:
postDir, err := os.ReadDir(path.Join(root, "b")) if err != nil { println("read posts error:", err.Error()) } for _, p := range postDir { if l := len(p.Name()); p.Name()[l-5:] != ".html" { continue } name := strings.ReplaceAll(p.Name(), "-", " ") name = name[3 : len(name)-5] info, _ := p.Info() data = append( data, post{ Name: name, Link: p.Name(), CTime: info.ModTime(), }, ) } sort.Slice(data, func(i, j int) bool {return data[i].Link > data[j].Link}) if err := temp.ExecuteTemplate(w, "blog.html", data); err != nil { println("execute error:", err.Error()) }
Daria pra fazer melhor e utilizar o que foi definido dentro do template para mostrar um nome mais interessante. Eu realizei dessa forma em um outro projeto meu, e ficou bem interessante. Mas aqui usei essa forma mais simples.
Para os artigos eu utilizei um método para não precisar reiniciar o servidor quando houvesse alguma alteração nas páginas, pois utilizando templates normalmente o parsing dos templates é feito no início do programa. Veja:
temp, err := posts.Clone() if err != nil { println("clone template", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } _, err = temp.ParseFiles(path.Join(root, r.URL.Path)) if err != nil { println("parse template", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } i := strings.LastIndex(r.URL.Path, "/") + 1 if err := temp.ExecuteTemplate(w, "template.html", r.URL.Path[i:]); err != nil { println("execute error:", err.Error()) }
Assim o template base (posts) é precarregado e a cada execução somente o artigo requisitado é lido. Portanto eu não preciso reiniciar o servidor para alterações nas páginas, somente quando há mudanças no código do servidor.
Para esses casos o RPi tem um cron job que reinicia o servidor a cada 1 hora, simples e funcional, aliás o cron é sem privilégios também. A atualização dos arquivos é feita usando o bom e velho scp que eu utilizo através do comando make para diminuir o risco de erros:
deploy-blog: $(wildcard www/b/*.*) cd www && rssgen -a blmayer.dev -c "b" -d "blog by b" -t "feed" -l "en_US" b/*.html > b/feed.xml scp $^ zero:blmayer.dev/www/b/
Aqui também gero o RSS usando um shell script simples que eu fiz, é um clássico fazer o seu próprio RSS a maioria dos blogs tem o seu próprio, uma pesquisa no google e você encontrará alguns.
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>posts</title> <link rel=icon href=data:,> <style> {{template "style.html"}} </style> </head> <body> // ← <a href=/>back</a> <h1>Blog!</h1> {{range .}} // <a href=/b/{{.Link}}>{{.Name}}</a><br> {{end}} <h2>what is this?</h2> this is a small space on the internet that i post topics like programming, linux, mathematics and some random stuff. we are a happy member of the <a href="//250kb.club">250Kb</a> and <a href="//512kb.club">512Kb</a> clubs. <hr> <center> <a href=/dp>←</a> derelict garden webring <a href=/dn>→</a> </center> <br> This work is licensed under <a href=//creativecommons.org/licenses/by/4.0/>CC BY 4.0</a>. </body> </html>
Essa é a página inicial do blog, daria pra reduzir ainda mais o tamanho mas já estou satisfeito. Note o trecho {{range .}} que cria a lista de artigos com os dados que foram passados no comando ExecuteTemplate. Assim os links são populados.
O template do artigo é um pouco diferente pois uso as redefinições na hora em que o artigo é requisitado:
<!DOCTYPE html> <html lang="{{template "lang"}}"> <head> <title>{{template "title"}}</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="{{template "desc"}}"> <meta name="revised" content="{{template "revised"}}"> <style> {{template "style.html"}} </style> </head> <body> // ← <a href=/b>back to posts</a> <h1>{{template "title"}}</h1> {{template "content"}} hr> <h3>responses</h3> {{range (responses .)}} <small> <b>From:</b> {{(index .From 0).Address}} <i style="float:right">{{.Date.Format "Mon, 02 Jan 2006 15:04:05 MST"}}</i> </small> <blockquote>{{.TextBody}}</blockquote> {{end}} <p><a href="mailto:blog@mail.blmayer.dev?subject={{.}}">+</a></p> </body> </html>
Basicamente a mesma coisa, porém aqui eu uso o {{template "content"}}, que substitui a si mesmo pelo conteúdo indicado, esse método é interessante pois é dinâmico, e os valores são preenchidos usando o ParseFiles() que mostrei anteriormente.
Por último o conteúdo em si:{{define "lang"}}en{{end}} {{define "title"}}titulo{{end}} {{define "desc"}}descricao{{end}} {{define "revised"}}2021-11-02{{end}} {{define "content"}} <p>O conteúdo do post.</p> {{end}}Os {{define ...}} justamente definem o valor do template que será usado acima. Desse modo é muito simples escrever um novo artigo: basta copiar esse molde, renomear o arquivo e preencher os campos.
Outro opcional aqui, coloquei uma integração com email para poder receber o feedback dos usuários. Aqui uso o Dovel, um servidor de emails rudimentar que tem uma interface go para ser utilizada com os templates.
A parte de código não é complicada e pode ser ainda mais facil ao se utilizar projetos prontos ou imagens docker preconfiguradas. Aqui eu quis fazer tudo do zero por dois motivos: 1. aprender como as muitas partes funcionam internamente, e 2. criar uma versão mais leve do que os projetos atuais.
É desse segundo ponto que me orgulho mais, tudo ficou muito leve e eficiente: a pagina inicial do blog tem 2700 bytes e carrega em 80ms, ela é HTML válido e simples, meu portfolio, a página superior ao blog tem 575 bytes; isso permite o projeto ser servido do meu Rpi Zero W que precisa de apenas 5V para funcionar. Além disso ele ainda carrega outros projetos como o meu git e servidor de email.
São essas as dificuldades que você poderá encontrar se decidir se aventurar por esse caminho. Espero ter ajudado de alguma forma. Eu digo que vale a pena se você preza for simplicidade extrema, gosta de fazer as coisas do seu jeito e quer sair da dependencia das famigeradas big techs e sobretudo aprender muito.