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:
version: '2'
services:
refresher:
build: .
volumes:
- /root/data/website/public:/website/public
- /root/data/website/post:/website/content/post
restart: always
ports:
- '127.0.0.1:9002:8080'
environment:
RACK_ENV: production
web:
image: "nginx:latest"
depends_on:
- refresher
volumes:
- /root/data/website/public:/usr/share/nginx/html
restart: always
ports:
- '127.0.0.1:9001:80'
Two services are there:
Refresher, builds a docker file as follows:
FROM ruby:latest
RUN wget -q https://github.com/gohugoio/hugo/releases/download/v0.30/hugo_0.30_Linux-64bit.deb
RUN dpkg -i hugo_0.30_Linux-64bit.deb
COPY . /website_source
WORKDIR /website_source
RUN bundle
RUN hugo
CMD ./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:
#!/usr/bin/env ruby
require 'rack'
secret_path = '/<secret-string-here>'
def write_post(post)
lines = post.split("\n")
title = lines[0]
body = lines[1..-1].join("\n")
post_body = <<-END_OF_POST
---
title: "#{title}"
date: #{Time.now}
---
#{body}
END_OF_POST
File.write("content/post/#{title}.md", post_body)
end
def generate_site
`hugo`
end
app = proc do |env|
request = Rack::Request.new(env)
if request.path != secret_path || !request.post?
[
301,
{
'Location' => 'http://www.<main-domain-here>.com',
'Content-Type' => 'text/html'
},
['Moved Permanently']
]
else
write_post(request.body.read)
generate_site
[200, { 'Content-Type' => 'text/plain' }, ['OK']]
end
end
Rack::Handler::WEBrick.run app
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.