This page is outdated. I moved to Xlog from Jekyll after trying Hugo
Hugo is a static site generator, it’s very fast generator built on Go, it can convert markdown and org files into a beautiful blog, I use it for my blog.
My current setup is a Hugo repository that is served from GitHub pages, linked it to my domain emadelsaid.com
, whenever I need to post a new post I use spacemacs and a package called easyhugo to create a new post, then a function called easy-hugo-github-deploy
that regenerates the site, commit and deploy it, it’s all open source, you can check it out from this repository.
I needed to create a similar website for my wife, a blog where she writes some articles about fitness and exercising, but we don’t want her to open spacemacs or any other editor of course, it would be great if she can write the article on her Facebook profile and it gets magically published to the Hugo website.
I use docker compose to serve 2 containers, one of them will have the HTTP server to serve the actual website, the other will run the webhook script.
the docker-compose.yml
file looks like so:
1version: '2'
2services:
3 refresher:
4 build: .
5 volumes:
6 - /root/data/website/public:/website/public
7 - /root/data/website/post:/website/content/post
8 restart: always
9 ports:
10 - '127.0.0.1:9002:8080'
11 environment:
12 RACK_ENV: production
13 web:
14 image: "nginx:latest"
15 depends_on:
16 - refresher
17 volumes:
18 - /root/data/website/public:/usr/share/nginx/html
19 restart: always
20 ports:
21 - '127.0.0.1:9001:80'
22
Two services are there:
Refresher, builds a docker file as follows:
1FROM ruby:latest
2
3RUN wget -q https://github.com/gohugoio/hugo/releases/download/v0.30/hugo_0.30_Linux-64bit.deb
4RUN dpkg -i hugo_0.30_Linux-64bit.deb
5
6COPY . /website_source
7WORKDIR /website_source
8
9RUN bundle
10RUN hugo
11CMD ./server
it links 2 volumes one of them is the posts directory where posts will be written, the other is the public directory generated by Hugo, it should be served from the second service.
the web service is an out-of-the-box nginx image, links the public directory generated from the first service to the nginx default HTML.
A ruby script that listens on a port, will wait for a specific path (secret path segment), with the article text as the POST body in plain text.
It will get the first post line and assume it’s the post title, and will create a post with that title and the rest of the post as the post content text.
The script is very simple, it’s as follows:
1#!/usr/bin/env ruby
2require 'rack'
3
4secret_path = '/<secret-string-here>'
5
6def write_post(post)
7 lines = post.split("\n")
8 title = lines[0]
9 body = lines[1..-1].join("\n")
10 post_body = <<-END_OF_POST
11---
12title: "#{title}"
13date: #{Time.now}
14---
15#{body}
16END_OF_POST
17
18 File.write("content/post/#{title}.md", post_body)
19end
20
21def generate_site
22 `hugo`
23end
24
25app = proc do |env|
26 request = Rack::Request.new(env)
27 if request.path != secret_path || !request.post?
28 [
29 301,
30 {
31 'Location' => 'http://www.<main-domain-here>.com',
32 'Content-Type' => 'text/html'
33 },
34 ['Moved Permanently']
35 ]
36 else
37 write_post(request.body.read)
38 generate_site
39 [200, { 'Content-Type' => 'text/plain' }, ['OK']]
40 end
41end
42
43Rack::Handler::WEBrick.run app
44
The secret string can be generated with uuid-gen
command, or rails secret
command.
IFTTT has a trigger for Facebook, one of the triggers listens if you posted a text post with hashtag, you can use it as your trigger.
then it should use a webhook
, the URL should be your refresher domain/secret-path
, the method should be POST, and the request body should be plain/text
with the post body without hashtag
, and you can use any hashtag you want for IFTTT to watch.
Now when you deploy that setup to your VPS server, linking your main HTTP proxy to both these services, I recommend a separate subdomain for your refresher service. and the www and naked domain to your web server.
The following is what will happen to trigger the whole setup:
Hugo
to regenerate the HTML.