|
|
|
#!/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>
|