# Building my personal Swiss Army Knife website, Part 2

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

![Logo](https://utile.space/logo.svg)

I decided to call it [utile.space](https://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: 
* https://github.com/SonnyAD/utile-front The front-end made with bare Dart 
* https://github.com/SonnyAD/utile-nginx The NGINX configuration 
* https://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. 

%[https://sonny.alvesdi.as/building-my-personal-swiss-army-knife-services]

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](https://certbot.eff.org/) like described in my article. 

%[https://sonny.alvesdi.as/setup-https-on-your-website-for-free]

```nginx 
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 https://utile.space/pdf/ 

### Get your IP service

I already had my tool to get IP on [ip.alvesdi.as](https://ip.alvesdi.as) from the following article.

%[https://sonny.alvesdi.as/getting-your-ip-from-anywhere]

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: https://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: 
```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](https://hub.docker.com/_/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.

```golang
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.

```nginx
   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 <a href="https://unsplash.com/@pf91_photography?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Patrick</a> on <a href="https://unsplash.com/s/photos/swiss-army-knife?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>


