Hexinverter ¬ The Miniaturest URL Shortener and Picpost Ever

So it was suggested to me that a better URL shortener (and picpost) would be a command-line tool that is run from some server, and pushes the url or pic to a target server. After noodling this idea for a little bit, I figured that the best way to do this would be to have a set of command-line utils that run on our individual workstations (or anywhere really) and use a private git repository to actually store the pictures and url redirects.

I also found out that Caddy has a CGI library (which after some hair pulling, and then actually following the guide on the Caddy site which once you understand how Go builds work makes a lot more sense), so I was like HEY I can write this ALL IN SHELL SCRIPTS. Weeeee.

In the code samples I use a fake example domain shrt.ex.

The general layout on the server is something like:

├── bin
│   ├── caddyfile
│   ├── fngen.py
│   ├── redir
│   ├── x-img
│   └── x-short
├── img
│   └── images get put here
├── index.html
└── redir
    └── redir files go here

The repo is checked out under the web directory on the web server, and also on our workstations (and the bin/ is added to our paths). Now when we want to shorten a URL or post a picture, we can simply run x-short or x-img and boom it's magic (as long as on the server, it automatically updates the git repository periodically).

Code

So here's the code for the various tools above!

caddyfile

This is linked into /etc/caddy/sites/ (see my previous post about setting up Caddy and Kanboard).

shrt.ex {
     log stdout
     root /srv/www/shrtex/
     gzip

     rewrite /i/ {
        r "(.*)"
        to /img/{1}
     }

     cgi /r /srv/www/shrtex/bin/redir
}

fngen.py

This is a generator for filenames, which makes a urlable, short identifier for each file by picking random numbers and printing them as Base64. I would use Base85 (and thus get a shorter string) but they are not URL friendly.

#!/bin/env python3

import random
import secrets
import base64

MINLEN=1
MAXLEN=3

print(base64.b64encode(secrets.token_bytes(random.randint(MINLEN,MAXLEN)), altchars=b'-_').decode('utf-8').strip('='))

redir

This is a simple CGI script which dereferences redirects. It reads the target from a file of the given filename in redir and prints a redirection. It is so dead simple.

#!/bin/sh
# read url pat0h
urltarg=$(echo "$PATH_INFO"|sed -e 's/^\///' -e 's/\/$//' -e 's/[^A-Za-z0-9_-]/_/g')

SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
targpath="$SCRIPTPATH"/../redir/"$urltarg"

# lookup map file
if [ -e "$targpath" ]; then
    # redir if map file exists
    printf "Location: $(cat $targpath)\n\n"
    logger "REDIRECT: origin: $PATH_INFO"
    exit 0
fi

# 404 if not
printf "Status: 404 Not Found\nContent-type: html\n\n"
cat "$SCRIPTPATH"/../index.html
logger "REDIRECT FAIL: origin: $PATH_INFO"

x-img

This is a simple shell script that takes an input image, resizes it, puts in into the repository with a random short name and commits the repository. It attempts to find a unique filename (in the future I'd like to add a trap that if it takes N tries it'll ask you to increase the maximum length of the filename).

#!/bin/sh
IMGSIZE="640x640"
BASEURL="https://shrt.ex/i/"
INFILE=$1
OUTFILE=$(tempfile --suffix=.jpg)
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")

# resize to web rez and copy to repo based on hash of file?
echo "Resizing ..."
convert "$INFILE" -strip -resize "$IMGSIZE" "$OUTFILE"

FINALOUT=''
BASEFN=''
while [ "$FINALOUT"z = 'z' ]; do
    BASEFN=$(python3 $SCRIPTPATH/fngen.py)
    FINALOUT="$SCRIPTPATH/../img/$BASEFN.jpg"
    if [ -e $FINALOUT ]; then
    FINALOUT=''
    fi
done


git pull && git rebase master

mv "$OUTFILE" "$FINALOUT"

# add commit file
git add $FINALOUT
git commit -m "automatic: Add $FINALOUT"
git push

# print url
echo "$BASEURL""$BASEFN.jpg"

x-short

This is another simple shell script that takes an input URL, figures out a random short filename for it, and writes it into a file in redir, and commits the repository.

#!/bin/sh
# create url map file for random id
BASEURL="https://shrt.ex/r/"
INURL=$1
SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
curpath=$(pwd)
cd "$SCRIPTPATH"
FINALOUT=''
BASEFN=''
while [ "$FINALOUT"z = 'z' ]; do
    BASEFN=$(python3 $SCRIPTPATH/fngen.py)
    FINALOUT="$SCRIPTPATH/../redir/$BASEFN"
    if [ -e $FINALOUT ]; then
    FINALOUT=''
    fi
done

git pull && git rebase master

echo "$1" > $FINALOUT

# add commit file
git add $FINALOUT
git commit -m "automatic: Add $FINALOUT"
git push

# print url
echo "$BASEURL""$BASEFN"
cd "$curpath"