A restaurant randomizer written with n0m templates and bash
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
5.7 KiB

#!/usr/bin/env n0m
$ #!/bin/bash
Content-Type: text/html
<{
# Constants
# =========
true=1;
false=0;
restaurant_file="restaurants.txt";
title="FeedMe";
# Components
# ==========
# $1: string: input value
# $2: number: 0=existing restaurant, 1=new restaurant
restaurant_form () {
enforce_arguments "${FUNCNAME[0]}" "$#" 2;
local icon_class;
if [ "$2" -eq 0 ]; then
icon_class="ri-save-2-fill";
else
icon_class="ri-add-line";
fi
}>
<form class="restaurant-form" method="POST" action="">
<input type="hidden" name="old" value="<% $1 %>" />
<input
type="text"
placeholder="Restaurant"
name="new"
value="<% $1 %>"
/>
<label class="button <% $icon_class %>">
$ local action;
$ if [ "$2" -eq 0 ]; then
$ action="save";
$ else
$ action="add";
$ fi;
<input type="submit" name="action" value="<% $action %>" />
</label>
$ if [ "$2" -eq 0 ]; then
<label class="button ri-delete-bin-2-fill">
<input type="submit" name="action" value="delete" />
</label>
$ fi
</form>
<{
}
# Utility Functions
# =================
# $1: string: function name
# $2: number: $#
# $3: number: expected number of arguments
enforce_arguments () {
if [ "$2" -lt "$3" ]; then
printf '%s %s %s\n' 'Function:' "$1" 'called with too few arguments';
exit 1;
elif [ "$2" -gt "$3" ]; then
printf '%s %s %s\n' 'Function:' "$1" 'called with too many arguments';
exit 1;
fi;
}
decode_uri () {
local i="${*//+/ }";
echo -e "${i//%/\\x}";
}
# Restaurant List Functions
# =========================
# $1: string: restaurant to remove
remove_restaurant () {
enforce_arguments "${FUNCNAME[0]}" "$#" 1;
local temp_file;
temp_file=$(mktemp);
grep -Fxv "$1" "$restaurant_file" > "$temp_file";
mv "$temp_file" "$restaurant_file";
}
# $1: string: restuarant to add
add_restaurant () {
enforce_arguments "${FUNCNAME[0]}" "$#" 1;
local temp_file;
temp_file=$(mktemp);
(grep -Fxv "$1" "$restaurant_file"; printf '%s\n' "$1") > "$temp_file";
mv "$temp_file" "$restaurant_file";
}
# $1 (optional): number: 0=don't overwrite, 1=overwrite
todays_restaurant () {
local today;
today="$(date +'%Y%m%d')";
local filename="$today.pick";
if [ -n "$1" ] && [ "$1" -ne 0 ]; then
rm "$filename";
fi;
if [ ! -f "$filename" ]; then
shuf -n 1 "$restaurant_file" > "$filename";
fi;
cat "$filename";
}
# Initialization
# ==============
# Make sure restaurant file eixsts
touch -a "$restaurant_file";
# Parse body
# ----------
declare -A body;
while IFS= read -d '&' -r pair || [ "$pair" ]; do
name=$(decode_uri "${pair%%=*}");
value=$(decode_uri "${pair#*=}");
if [ -n "$name" ]; then
body["$name"]="$value";
fi;
done
# Perform requested action
# ------------------------
case "${body['action']}" in
add)
add_restaurant "${body['new']}";
;;
delete)
remove_restaurant "${body['old']}";
;;
save)
remove_restaurant "${body['old']}";
add_restaurant "${body['new']}";
;;
new_restaurant)
todays_restaurant $true >/dev/null;
;;
esac
}><!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="FeedMe">
<meta property="og:type" content="website">
<meta property="og:url" content="https://n0m.org/feedme">
<meta property="og:image" content="https://n0m.org/feedme/images/food.png">
<meta property="og:description" content="Today's Restaurant: <$% todays_restaurant %>">
<title><% $title %></title>
<style>
:root {
--border-color: gray;
}
html, body {
margin: 0;
padding: 0;
}
html {
font-family: sans-serif;
}
body {
background-color: #f5f5f5;
}
\#title {
color: rgba(0, 0, 0, 0.15);
text-align: center;
}
\#content {
margin: 0 auto;
width: calc(min(400px, 100vw - 1.5em));
}
\#todays-restaurant {
text-align: center;
font-size: 1.3em;
}
\#restaurant-list {
list-style-type: none;
padding: 0;
}
.restaurant-form {
display: flex;
}
.restaurant-form input[type='text'] {
flex-grow: 1;
border: 1px solid var(--border-color);
padding: 0.4em 0.7em;
}
.restaurant-form label.button {
border-radius: 0;
}
.restaurant-form > :not(:first-child) {
border-left: 0;
}
\#restaurant-list > li:not(:last-child) .restaurant-form > * {
border-bottom: 0;
}
label.button {
display: flex;
border: 1px solid var(--border-color);
border-radius: 0.3em;
padding: 0.2em 0.5em;
background: #FDFDFD;
align-items: center;
}
label.button > span:first-of-type {
flex-grow: 1;
text-align: center;
}
label.button > input {
display: none;
}
</style>
<link
href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
rel="stylesheet"
>
</head>
<body>
<div id="content">
<h1 id="title"><% $title %></h1>
<p id="todays-restaurant">
Today's Restaurant: <strong><$% todays_restaurant %></strong>
<form method="POST" action="">
<label class="button">
<span>I want to go somewhere else</span>
<input type="submit" name="action" value="new_restaurant" />
</label>
</form>
</p>
<ul id="restaurant-list">
$ while IFS= read -r line || [ "$line" ]; do
<li><$!% restaurant_form "$line" $false %></li>
$ done <restaurants.txt #>
<li><$!% restaurant_form "" $true %></li>
</ul>
</div>
</body>
</html>