{ depot, ... }: let inherit (depot) lib ; inherit (depot.users.sterni.nix) url fun string ; inherit (depot.web.bubblegum) pathInfo scriptName respond absolutePath ; # substituted using substituteAll in default.nix blogdir = "@blogdir@"; # blogdir = toString ./posts; # for local testing parseDate = post: let matched = builtins.match "/?([0-9]+)-([0-9]+)-([0-9]+)-.+" post; in if matched == null then [ 0 0 0 ] else builtins.map builtins.fromJSON matched; parseTitle = post: let matched = builtins.match "/?[0-9]+-[0-9]+-[0-9]+-(.+).html" post; in if matched == null then "no title" else builtins.head matched; dateAtLeast = a: b: builtins.all fun.id (lib.zipListsWith (partA: partB: partA >= partB) a b); byPostDate = a: b: dateAtLeast (parseDate a) (parseDate b); posts = builtins.sort byPostDate (builtins.attrNames (lib.filterAttrs (_: v: v == "regular") (builtins.readDir blogdir))); generic = { title, inner, ... }: '' ${title} ${inner} ''; index = posts: ''

blog posts

''; formatDate = let # Assume we never deal with years < 1000 formatDigit = d: string.fit { char = "0"; width = 2; } (toString d); in lib.concatMapStringsSep "-" formatDigit; post = title: post: ''

${title}

${builtins.readFile (blogdir + "/" + post)}
''; validatePathInfo = pathInfo: let chars = string.toChars pathInfo; in builtins.length chars > 1 && !(builtins.elem "/" (builtins.tail chars)); response = if pathInfo == "/" then { title = "blog"; status = "OK"; inner = index posts; } else if !(validatePathInfo pathInfo) then { title = "Bad Request"; status = "Bad Request"; inner = "No slashes in post names 😡"; } # CGI should already url.decode for us else if builtins.pathExists (blogdir + "/" + pathInfo) then rec { title = parseTitle pathInfo; status = "OK"; inner = post title pathInfo; } else { title = "Not Found"; status = "Not Found"; inner = "

404 — not found

"; }; in respond response.status { "Content-type" = "text/html"; } (generic response)