I would like to present you the v0.0.1 of my Swiss Army Knife services website!
I decided to call it utile.space.
Utile because it is a synonym of useful and comes from French! And .space
because it sounded adequate.
Also, I made the source code freely available:
- github.com/SonnyAD/utile-front The front-end made with bare Dart
- github.com/SonnyAD/utile-nginx The NGINX configuration
- github.com/SonnyAD/utile-api The Golang API backend serving the website, but also aimed to be useable programmatically by anybody
As I introduced in my introductory article, I am going to share with you my development journal.
Note that there is already too much to talk about in this version. So I am going to split my journey into many articles.
Start easy with NGINX
I started the journey by getting a new NGINX server conf for my new domain utile.space. And setup SSL with certbot like described in my article.
server {
server_name utile.space;
location /ip/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8080/;
}
location /pdf/ {
alias /usr/share/nginx/html/pdf/;
autoindex on;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/utile.space/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/utile.space/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
You can notice two extras things.
Sharing PDF files
I have lots of free and useful PDF resources that I wanted to reshare. So I declared a location
to allow me to share them. Note that to make those files visible publicly, I used the instructions: autoindex on
.
See the results at utile.space/pdf
Get your IP service
I already had my tool to get IP on ip.alvesdi.as from the following article.
So I added a reverse proxy to reuse it. I got some issues because I was missing a trailing slash on the reverse proxy and location, I believe. Something I should investigate later to properly understand the problem.
See the results at: utile.space/ip
Then to continue easy, the API
I had a sample go project running on Docker in my personal library. So I started from that template.
Docker
Here was the Dockerfile:
FROM golang:latest AS build
WORKDIR /go/src
COPY server.go .
RUN CGO_ENABLED=0 go build server.go
FROM scratch AS runtime
COPY --from=build /go/src/server /
EXPOSE 3000/tcp
CMD ["/server"]
I use the latest golang image to compile server.go
into a binary. Then I copy that binary into a new image based on scratch. Scratch, which is an empty image basically.
You may have noticed I prepend CGO_ENABLED=0
in front of go build
. This is to make sure the binary would be compatible with the scratch image. Otherwise, it would not.
Go code
I started with something easy. I started with the rolling dice feature. My goal was to have an endpoint that could allow me to roll a die of any faces between 2 and 100.
Please find the code below.
package main
import (
"fmt"
"net/http"
"math/rand"
"strconv"
"regexp"
)
func home(w http.ResponseWriter, r *http.Request) {
// evaluate only if i in [2-100]
re := regexp.MustCompile("/d(100|1[0-9]|[2-9][0-9]?)")
match := re.FindStringSubmatch(r.URL.Path)
if match == nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
dice, err := strconv.Atoi(match[1])
if err != nil {
http.Error(w, "Unknown error", http.http.StatusInternalError)
return
}
fmt.Fprintf(w, strconv.Itoa(rand.Intn(dice)+1))
}
func main() {
http.HandleFunc("/", home)
http.ListenAndServe(":3000", nil)
}
I went with one endpoint only and would parse the location using a regular expression. For those allergic to regular expression, sorry, that's kind of my kink ^^
Anyway, let me explain it. "/d(100|1[0-9]|[2-9][0-9]?)"
means first I am expecting to receive a request that will start with /d
. Then in parentheses, I specified 3 different cases, consider the |
as a simple "or". Then the request is either /d100
, between /d10
to /d19
, or finally between /d2
to /d9
, and because of the ?
, /d20
to /d99
. I end up covering any case between /d2
to /d100
.
With the regex, I capture the number n
of the faces. And I use it with the function rand.Intn
to generate a pseudo-random number. I also add 1 to it to make sure the result is between 1 and n
.
Plug it with NGINX
With that, I ran a docker container on my server and plugged it into a new location in my NGINX server conf.
location /api/ {
proxy_pass http://127.0.0.1:3000/;
}
Conclusion
As I said, I am doing this as a hobby, so it has to give me quick results and not take me too much time to develop. So I started easy ;)
In the next articles, I will describe the following of my journey. I will discuss the beginning of my experience with Dart and the first problems I had on the front-end. I will also go through the deployment scripts I wrote to have a rough automated process to publish my work. Lastly, I will explain how I made the logo, I am not a designer, and I think I came with something not bad. And I want to show you it's not that complicated to do.