💾 Archived View for warmedal.se › ~bjorn › posts › your-own-publishing-portal.gmi captured on 2023-04-19 at 23:59:42. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
Since I now dual post a lot of material to both the web and geminispace I've built tools to accomplish this. I thought I'd share some of this, because the issue of how to easiest or best publish ones stuff often comes up. This is *one* solution, and it works very well for me. Hopefully there's something you can take away from it too. Consider any code examples here to be MIT licensed.
Any executable file that I publish here with the .cgi suffix will be executed by the server when called. Your mileage may vary here; if you're on a tilde CGI scripts may be disallowed, or more often all located in ~user/public_html/cgi-bin -- check with your friendly sysadmin what applies, and place your file where it needs to be.
My publishing CGI script will serve up a HTML Form when called with an HTTP GET method, and handle input data to publish the way I want it to be published when called with an HTTP POST method. You may see this as two parts: the form and the publishing logic. In reality it's a lot more parts than that, because I decided to break my publishing routine into smaller parts and make a small tool for each. You'll see what I mean.
Here's my publishing CGI script, warts and all:
#!/bin/bash AUTH_TOKEN_SUM="[[[ HERE BE SECRET STUFF! ]]]" echo "Content-Type: text/html" echo echo echo '<!DOCTYPE html> <html> <head> <style> body { max-width: 45em; margin: auto; } </style> <meta charset="utf-8"> <title>Post Editor</title> </head> <body>' # Serve a form, if nothing was submitted if [[ $REQUEST_METHOD == "GET" ]]; then echo ' <form action="editor.cgi" method="post"> <label for="authtoken">Auth token: </label><input type="password" name="authtoken" id="authtoken"><br/> <label for="posttext">Post text:</label><br/> <textarea name="posttext" id="posttext" style="width: 100%" rows="20">Type here</textarea><br/> <input type="radio" name="publish" id="gemini" value="gemini"> <label for="gemini">Gemini</label> <input type="radio" name="publish" id="html" value="html"> <label for="html">HTML</label> <input type="radio" name="publish" id="both" value="both"> <label for="other">Both</label><br/> <input type="submit" value="Submit"> </form>' # When something is submitted, treat it and post elif [[ $REQUEST_METHOD == "POST" ]]; then POST_DATA=$(cat -) POST_VARS=(${POST_DATA//&/ }) for i in "${POST_VARS[@]}" do STRING=${i//+/ } POST_VAR_VALUE=$(echo -e "${STRING//%/\\x}") case "${POST_VAR_VALUE}" in authtoken=* ) AUTH_TOKEN="${POST_VAR_VALUE/authtoken=/}";; posttext=* ) export POST_TEXT="${POST_VAR_VALUE/posttext=}";; publish=* ) PUBLISH_TO="${POST_VAR_VALUE/publish=/}";; esac done if [[ $AUTH_TOKEN_SUM != $(echo "${AUTH_TOKEN}" | sha256sum | awk '{print $1}') ]]; then # Unauthorized! echo "<html><head><style>body { max-width: 45em; margin: auto;}</style><meta charset="utf-8"><title>Post Editor</title></head><body><h1>Authentication Failed!</h1><p>It appears you provided invalid credentials.</p></body></html>" else # Authorized! :D export POST_TITLE=$(echo "${POST_TEXT}" | grep -m 1 -E '^# ' | sed 's;^# ;;') export POST_FILENAME=$(echo "${POST_TITLE}" | grep -oiE '[a-z0-9]+' | xargs echo | sed -e 's; ;-;g' -e 's;[A-Z];\L&;g') cd ~/Journals echo "${POST_TEXT}" > "allposts/${POST_FILENAME}.txt" if [[ ${PUBLISH_TO} == "html" || ${PUBLISH_TO} == "both" ]]; then for script in $(find html -type f | sort); do $script done fi if [[ ${PUBLISH_TO} == "gemini" || ${PUBLISH_TO} == "both" ]]; then for script in $(find gemini -type f | sort); do $script done fi fi fi echo ' </body> </html>'
FFS! That's 80 lines of ugly bash! Explain!
Heh. Yeah.
Alright, the important bits:
I realised that I want to do a whole bunch of different things when I publish a post:
As you see these are pretty isolated things. By executing them one by one and providing the needed data (POST_TEXT and POST_TITLE) as environment variables they can be written and tested in that order without interfering with each other. Each script does one thing, the way *I* want it done. Some are written in bash, some are written in python. I won't show them here, because this is the part you really need to build yourself for your own setup. My gemlog is as spartan as they go, and my blog mimics that; I don't expect anyone else to want such a minimalist web setup.
One thing you should be aware is that a lot of data coming from your browser will contain CRLF (\r\n) line endings, because of legacy web reasons. I have dos2unix calls in a lot of places to correct that.
Let me know if you see any glaring problems with my code, or have any questions that I might be able to answer 🙂️
-- CC0 ew0k, 2021-01-06