Ben Ashton
2 years ago
commit
bcf3b8dd7f
1 changed files with 236 additions and 0 deletions
@ -0,0 +1,236 @@ |
|||||||
|
#!/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; |
||||||
|
$ cat "$restaurant_file" | grep -Fxv "$1" | tee "$restaurant_file" >/dev/null; |
||||||
|
$ } |
||||||
|
# |
||||||
|
# $1: string: restuarant to add |
||||||
|
$ add_restaurant () { |
||||||
|
$ enforce_arguments "${FUNCNAME[0]}" "$#" 1; |
||||||
|
$ cat "$restaurant_file" |\ |
||||||
|
$ (grep -Fxv "$1"; printf '%s\n' "$1") |\ |
||||||
|
$ tee "$restaurant_file" >/dev/null; |
||||||
|
$ } |
||||||
|
# |
||||||
|
# $1 (optional): number: 0=don't overwrite, 1=overwrite |
||||||
|
$ todays_restaurant () { |
||||||
|
$ local 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> |
||||||
|
<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: fit-content; |
||||||
|
min-width: 400px; |
||||||
|
} |
||||||
|
|
||||||
|
\#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> |
Loading…
Reference in new issue