Making a WebApp from a shell script
Tags: webdev, microservices, shell, tutorial
Published: 2022-11-29 21:27:20

I've recently written a shellscript for a personal need and thought it would be useful to other people. But how could I make this usable to non-tech people?

I've converted it to a simple web application: 
- a form
- the script

Let's go through the steps that make this possible.

## Select a light and convenient Web server

The Web server needs to be lightweight and able to execute code. [mini_httpd](https://acme.com/software/mini_httpd/) is the perfect choice for this!

We'll run it as into a Docker container. Let's create a file named `Dockerfile` with the following contents:

```Dockerfile
FROM alpine:latest

RUN apk add --update mini_httpd bash

COPY . /app

EXPOSE 80

CMD [ "mini_httpd", "-h", "0.0.0.0", "-dd", "/app", "-r", "-c", "**.sh", "-D" ]
```

The `CMD` line starts the Web server:
- with document root configured in `/app` folder
- in a `chroot` for higher security
- that will run `**.sh` files (all files ending with `.sh` extension)

You can now build and run this Docker image:

```sh
docker build . -t shellscript-cgi
docker run --rm -t -p 8080:80 shellscript-cgi
```

If you load [http://localhost:8080](http://localhost:8080) in your Web browser, you'll see the index of the working dir:

```
Index of ./

.                                   29Nov2022 20:39           4096
..                                  29Nov2022 20:43           4096
Dockerfile                          29Nov2022 20:37            162    

mini_httpd/1.30 26Oct2018
```

## Add a simple script

Let's add a non-interactive script. This script will return the output of a command (in this example, we'll get the system `uptime`).

Create a file named `uptime.sh` with the following contents:

```sh
#!/usr/bin/env sh

echo "Content-type: text/plain"
echo

uptime
```

Make it executable:

```sh
chmod +x uptime.sh
```

Then stop the running Docker container (`Ctrl-C`), rebuild the image and run the new container:

```sh
docker build . -t shellscript-cgi
docker run --rm -t -p 8080:80 shellscript-cgi
```

Now if you load [http://localhost:8080/uptime.sh](http://localhost:8080/uptime.sh) you'll get the system uptime:

```
 20:49:47 up 7 days, 13:39,  0 users,  load average: 5.09, 2.89, 1.96
```

## Handle input arguments

Now let's deal with input arguments. We'll only cover `GET` arguments in this article.

Parsing of input arguments will be done through [ColasNahaboo's Cgibashopts](https://github.com/ColasNahaboo/cgibashopts). Download the `cgibashopts` into your working directory.

Now create the `hello.sh` script with the following contents:

```sh
#!/bin/bash

source cgibashopts

echo "Content-type: text/plain; charset=utf-8"
echo

echo "Hello, $FORM_name"
```

Make it executable:

```sh
chmod +x hello.sh
```

Then stop the running Docker container (`Ctrl-C`), rebuild the image and run the new container:

```sh
docker build . -t shellscript-cgi
docker run --rm -t -p 8080:80 shellscript-cgi
```

Now if you load [http://localhost:8080/hello.sh?name=Sébastien](http://localhost:8080/hello.sh?name=Sébastien) you'll get a nice welcome message 🙂 :

```
Hello, Sébastien!
```

## Going further

In this article we only went through simple cases. Real-life applications will probably have more advanced needs.

The script I was talking about at the beginning of this article is a PDF watermarking tool. This tool deals with:
- `POST` request
- file upload
- advanced external commands (image & PDF manipulation)

You can look at its code there: [https://code.pipoprods.org/web/watermark-pdf](https://code.pipoprods.org/web/watermark-pdf).