diff --git a/lists b/lists new file mode 100755 index 0000000..1ef3a5b --- /dev/null +++ b/lists @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Smol listing manager.""" + +import smolcgi as scgi + +if not (user_hash := scgi.cert_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", "/var/gemini/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:] + 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") + print("# Lists") + for child in user_dir.iterdir(): + if child.is_file(): + print(f"=> ./{child.name} 📄 {child.name}") + print("=> ./_/create ✨ Create a new list") + +def show_list(list_name): + list_file = get_user_dir() / list_name + try: + with open(list_file, "rt") as f: + content = f.read() + except FileNotFoundError: + scgi.not_found() + else: + scgi.header(20, "text/gemini") + print("# " + list_name) + print(content) + print(f"=> ./{list_name}/add Append item") + print(f"=> ./{list_name}/pop Pop first item") + +def process_action(list_name, action): + if list_name == "_" and action == "create": + process_creation() + elif action == "add": + process_add(list_name) + elif action == "pop": + process_pop(list_name) + else: + scgi.not_found() + +def process_creation(): + if not (new_list_name := scgi.query): + meta = "List name (allowed chars: lowercase, digits, underscore)" + scgi.require_input(meta) + else: + for char in new_list_name: + if char not in ALLOWED_LIST_NAME_CHARS: + scgi.temp_error(f"Illegal character in list name: {char}") + list_file = get_user_dir() / new_list_name + list_file.touch(mode=0o660) + redirect_to_list(new_list_name) + +def process_add(list_name): + if not (item := scgi.query): + scgi.require_input("Enter your item") + else: + if "\n" in item: + scgi.temp_error("No new line allowed in item.") + line = f"* {item}\n" + list_file = get_user_dir() / list_name + try: + with open(list_file, "at") as f: + f.write(line) + except FileNotFoundError: + scgi.not_found() + 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 + "/" + list_name) + +path_components = scgi.path.lstrip("/").split("/", maxsplit=1) +list_name = path_components[0] +action = path_components[1] if len(path_components) > 1 else '' +if not list_name: + show_lists() +elif not action and list_name != "_": + show_list(list_name) +else: + process_action(list_name, action)