Compare commits

...

2 Commits

@ -1,4 +1,5 @@
#!/usr/bin/env python3
"""Wrapper around `check_if_email_exists`."""
import json
import pprint

22
lists

@ -1,28 +1,27 @@
#!/usr/bin/env python3
"""Smol listing manager."""
import smolcgi as scgi
if not (user_hash := scgi.tls_client_hash):
scgi.exit_with_header(60, "You need a certificate to use this app")
import string
import subprocess
from os import getenv
from pathlib import Path
STORAGE_PATH = Path(getenv("GEMINI_LISTS_PATH", "/home/gemini/storage/lists"))
import smolcgi as scgi
scgi.require_client_cert()
STORAGE_PATH = Path("/home/gemini/storage/lists")
if not STORAGE_PATH.exists():
STORAGE_PATH.mkdir(parents=True)
ALLOWED_LIST_NAME_CHARS = string.ascii_lowercase + string.digits + "_"
def get_user_dir():
user_dir = STORAGE_PATH / user_hash[7:]
user_dir = STORAGE_PATH / scgi.tls_client_hash[7:]
if not user_dir.is_dir():
user_dir.mkdir()
return user_dir
def show_lists():
user_dir = get_user_dir()
scgi.header(20, "text/gemini")
@ -32,6 +31,7 @@ def show_lists():
print(f"=> {scgi.script_name}/{child.name} 📄 {child.name}")
print(f"=> {scgi.script_name}/_/create ✨ Create a new list")
def show_list(list_name):
list_file = get_user_dir() / list_name
try:
@ -46,6 +46,7 @@ def show_list(list_name):
print(f"=> {scgi.script_name}/{list_name}/add Append item")
print(f"=> {scgi.script_name}/{list_name}/pop Pop first item")
def process_action(list_name, action):
if list_name == "_" and action == "create":
process_creation()
@ -56,6 +57,7 @@ def process_action(list_name, action):
else:
scgi.not_found()
def process_creation():
if not (new_list_name := scgi.query_string_dec):
meta = "List name (allowed chars: lowercase, digits, underscore)"
@ -68,6 +70,7 @@ def process_creation():
list_file.touch(mode=0o660)
redirect_to_list(new_list_name)
def process_add(list_name):
if not (item := scgi.query_string_dec):
scgi.require_input("Enter your item")
@ -84,14 +87,17 @@ def process_add(list_name):
else:
redirect_to_list(list_name)
def process_pop(list_name):
list_file = get_user_dir() / list_name
subprocess.run(["sed", "-i", "1d", str(list_file)])
redirect_to_list(list_name)
def redirect_to_list(list_name):
scgi.redirect_temp(scgi.script_name + "/" + list_name)
path_components = scgi.path_info.lstrip("/").split("/", maxsplit=1)
list_name = path_components[0]
action = path_components[1] if len(path_components) > 1 else ''

@ -1,9 +1,14 @@
"""Smol CGI utilities."""
from os import environ
from pathlib import Path
from urllib.parse import unquote
def getenv(name):
return environ.get(name, "")
gateway_interface = getenv("GATEWAY_INTERFACE")
remote_addr = getenv("REMOTE_ADDR")
remote_host = getenv("REMOTE_HOST")
@ -40,34 +45,68 @@ cgi_vars = [
"query_string_dec",
]
def get_storage_path():
script_filename = script_name.split("/")[-1]
path = getenv(f"GEMINI_{script_filename.upper()}_STORAGE")
if not path:
path = f"/home/gemini/storage/{script_filename}"
path = Path(path)
if not path.exists():
path.mkdir(parents=True)
return path
def get_user_dir():
user_dir = get_storage_path() / tls_client_hash[7:]
if not user_dir.is_dir():
user_dir.mkdir()
return user_dir
def header(code, meta):
print(f"{code} {meta}", end="\r\n")
def exit_with_header(code, meta):
header(code, meta)
exit()
def require_input(meta):
exit_with_header(10, meta)
def require_input(reason):
exit_with_header(10, reason)
def redirect_temp(url):
exit_with_header(30, url)
def redirect_perm(url):
exit_with_header(31, url)
def temp_error(meta):
exit_with_header(42, meta)
def not_found():
exit_with_header(51, "File not found.")
def temp_error(reason):
exit_with_header(42, reason)
def not_found(reason="Not found"):
exit_with_header(51, reason)
def bad_request(reason="Bad request"):
exit_with_header(59, reason)
def require_client_cert():
if auth_type != "CERTIFICATE":
exit_with_header(60, "You need a certificate to use this app")
def debug():
header(20, "text/plain")
print_env()
exit()
def print_env():
globz = globals()
for key in cgi_vars:
print(f"{key} = {repr(globz[key])}")
def link(path, text=""):
print(f"=> {script_name}{path} {text}")

@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""Smol “three good things” notepad."""
import datetime
import re
import smolcgi as scgi
scgi.require_client_cert()
def get_month_dir(year, month):
return scgi.get_user_dir() / f"{str(year).zfill(4)}-{str(month).zfill(2)}"
def get_day_file(year, month, day):
filename = str(day).zfill(2) + ".gmi"
return get_month_dir(year, month) / filename
def reply_home():
scgi.header(20, "text/gemini")
print("# Trikiff! 🌄🥰🌃")
print("## Actions")
scgi.link("/new/today", "Add kiffs of today")
scgi.link("/new/yesterday", "Add kiffs of yesterday")
scgi.link("/new", "Add kiffs for another day")
print("## This month")
today = datetime.date.today()
month_dir = get_month_dir(today.year, today.month)
if month_dir.is_dir():
print_month(month_dir)
else:
print("No kiffs yet!")
print("## Past months")
scgi.link("/browse", "Browse archives")
def reply_new(date: datetime.date):
if (kiffs := scgi.query_string_dec):
kiffs = filter(lambda s: s, kiffs.split(";"))
kiffs = map(lambda s: "* " + s.strip(), kiffs)
content = "\n".join(kiffs) + "\n"
day_file = get_day_file(date.year, date.month, date.day)
if not (parent_dir := day_file.parent).exists():
parent_dir.mkdir(parents=True)
with open(get_day_file(date.year, date.month, date.day), "at") as f:
f.write(content)
scgi.redirect_temp(f"{scgi.script_name}")
else:
scgi.require_input("Input one or more kiffs (separated with ';')")
def reply_prompt_day():
if (query := scgi.query_string_dec):
try:
if (match := re.match(r"^\d{1,2}$", query)):
date = datetime.date.today().replace(day=int(match.group(0)))
elif (match := re.match(r"^\d{4}-\d{2}-\d{2}$", query)):
date = datetime.date.fromisoformat(match.group(0))
else:
scgi.bad_request("Invalid day or date format")
except ValueError:
scgi.bad_request("Invalid day or date format")
scgi.redirect_temp(f"{scgi.script_name}/new/{date.isoformat()}")
else:
scgi.require_input(
"Which day? You can input only the day for the current month, "
"or a complete date using the YYYY-MM-DD format."
)
def reply_browse(directory=None):
if directory:
path = scgi.get_user_dir() / directory
if not path.is_dir():
scgi.not_found()
scgi.header(20, "text/gemini")
print(f"# Kiffs for {directory}")
print_month(path)
else:
scgi.header(20, "text/gemini")
print("# Trikiff archives 📜")
for path in sorted(scgi.get_user_dir().iterdir(), reverse=True):
if not path.is_dir():
continue
scgi.link(f"/browse/{path.name}", path.name)
def print_month(month_dir):
for day_filename in sorted(month_dir.iterdir(), reverse=True):
day = day_filename.name[:-4]
print(f"### {day}")
with open(day_filename, "rt") as f:
print(f.read())
if scgi.path_info == "/new/today":
reply_new(datetime.date.today())
elif scgi.path_info == "/new/yesterday":
reply_new(datetime.date.today() - datetime.timedelta(days=1))
elif scgi.path_info == "/new":
reply_prompt_day()
elif (match := re.match(r"^/new/(\d{4}-\d{2}-\d{2})$", scgi.path_info)):
try:
reply_new(datetime.date.fromisoformat(match.group(1)))
except ValueError:
scgi.not_found()
elif scgi.path_info == "/browse":
reply_browse()
elif (match := re.match(r"^/browse/(\d{4}-\d{2})$", scgi.path_info)):
reply_browse(directory=match.group(1))
else:
reply_home()
Loading…
Cancel
Save