Building my personal Swiss Army Knife services, Part 2

Building my personal Swiss Army Knife services, Part 2

ยท

4 min read

I would like to present you the v0.0.1 of my Swiss Army Knife services website!

Logo

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:

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.


Photo by Patrick on Unsplash

Did you find this article valuable?

Support Sonny Alves Dias by becoming a sponsor. Any amount is appreciated!

ย