2021-01-29 15:58:33 +01:00
|
|
|
|
{ depot, pkgs, lib, ... }:
|
|
|
|
|
|
|
|
|
|
let
|
|
|
|
|
bins = depot.nix.getBins pkgs.lowdown [ "lowdown" ]
|
|
|
|
|
// depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
|
2021-08-07 19:44:17 +02:00
|
|
|
|
// depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "test" ]
|
2021-01-29 15:58:33 +01:00
|
|
|
|
// depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
|
|
|
|
|
// depot.nix.getBins pkgs.time [ "time" ]
|
|
|
|
|
;
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# /
|
2021-08-07 18:57:24 +02:00
|
|
|
|
toplevel = [
|
|
|
|
|
{
|
|
|
|
|
route = [ "notes" ];
|
|
|
|
|
name = "Notes";
|
|
|
|
|
page = router;
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# /notes/*
|
2021-01-29 15:58:33 +01:00
|
|
|
|
notes = [
|
|
|
|
|
{
|
|
|
|
|
route = [ "notes" "preventing-oom" ];
|
|
|
|
|
name = "Preventing OOM";
|
2021-05-04 00:11:14 +02:00
|
|
|
|
page = renderNote "preventing-oom" ./notes/preventing-oom.md;
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
route = [ "notes" "rust-string-conversions" ];
|
|
|
|
|
name = "Converting between different String types in Rust";
|
|
|
|
|
page = renderNote "rust-string-conversions" ./notes/rust-string-conversions.md;
|
2021-01-29 15:58:33 +01:00
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# convert a note to html via lowdown
|
|
|
|
|
renderNote = name: note: depot.nix.runExecline "${name}.html" {} [
|
|
|
|
|
"importas" "out" "out"
|
|
|
|
|
bins.lowdown "-s" "-Thtml" "-o" "$out" note
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
# all notes with `route` converted to an absolute path
|
2021-08-07 18:57:24 +02:00
|
|
|
|
notesFullRoute = lib.pipe notes [
|
2021-01-29 15:58:33 +01:00
|
|
|
|
(map (x@{route, ...}: x // { route = mkRoute route; }))
|
2021-08-07 18:57:24 +02:00
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# a cdb from route to a netencoded version of data for each route
|
2021-08-07 18:57:24 +02:00
|
|
|
|
router = lib.pipe notesFullRoute [
|
2021-01-29 15:58:33 +01:00
|
|
|
|
(map (x: {
|
|
|
|
|
name = x.route;
|
2021-08-07 19:19:33 +02:00
|
|
|
|
value = depot.users.Profpatsch.netencode.gen.dwim x;
|
2021-01-29 15:58:33 +01:00
|
|
|
|
}))
|
|
|
|
|
lib.listToAttrs
|
|
|
|
|
(cdbMake "notes-router")
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# look up a route by path ($1)
|
2021-01-29 15:58:33 +01:00
|
|
|
|
router-lookup = depot.nix.writeExecline "router-lookup" { readNArgs = 1; } [
|
|
|
|
|
cdbLookup router "$1"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
runExeclineStdout = name: args: cmd: depot.nix.runExecline name args ([
|
|
|
|
|
"importas" "-ui" "out" "out"
|
|
|
|
|
"redirfd" "-w" "1" "$out"
|
|
|
|
|
] ++ cmd);
|
|
|
|
|
|
2021-08-07 18:57:24 +02:00
|
|
|
|
index = runExeclineStdout "index" {
|
2021-08-07 19:19:33 +02:00
|
|
|
|
stdin = depot.users.Profpatsch.netencode.gen.dwim notesFullRoute;
|
2021-08-07 18:57:24 +02:00
|
|
|
|
} [
|
|
|
|
|
"withstdinas" "-in" "TEMPLATE_DATA"
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"pipeline" [
|
|
|
|
|
bins.printf ''
|
|
|
|
|
<ul>
|
|
|
|
|
{{#.}}
|
2021-08-07 18:57:24 +02:00
|
|
|
|
<li><a href="{{route}}">{{name}}<a></li>
|
2021-01-29 15:58:33 +01:00
|
|
|
|
{{/.}}
|
|
|
|
|
</ul>
|
|
|
|
|
''
|
|
|
|
|
]
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.netencode-mustache
|
2021-01-29 15:58:33 +01:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
arglibNetencode = val: depot.nix.writeExecline "arglib-netencode" { } [
|
2021-08-07 19:19:33 +02:00
|
|
|
|
"export" "ARGLIB_NETENCODE" (depot.users.Profpatsch.netencode.gen.dwim val)
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"$@"
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# A simple http server that serves the site. Yes, it’s horrible.
|
2021-01-29 15:58:33 +01:00
|
|
|
|
notes-server = { port }: depot.nix.writeExecline "blog-server" {} [
|
2021-08-07 19:19:33 +02:00
|
|
|
|
(depot.users.Profpatsch.lib.runInEmptyEnv [ "PATH" ])
|
2021-01-29 15:58:33 +01:00
|
|
|
|
bins.s6-tcpserver "127.0.0.1" port
|
|
|
|
|
bins.time "--format=time: %es" "--"
|
|
|
|
|
runOr return400
|
|
|
|
|
"pipeline" [
|
|
|
|
|
(arglibNetencode {
|
|
|
|
|
what = "request";
|
|
|
|
|
})
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.read-http
|
2021-01-29 15:58:33 +01:00
|
|
|
|
]
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.record-splice-env
|
2021-01-29 15:58:33 +01:00
|
|
|
|
runOr return500
|
|
|
|
|
"importas" "-i" "path" "path"
|
2021-04-04 04:04:20 +02:00
|
|
|
|
"if" [ depot.tools.eprintf "GET \${path}\n" ]
|
2021-01-29 15:58:33 +01:00
|
|
|
|
runOr return404
|
|
|
|
|
"backtick" "-ni" "TEMPLATE_DATA" [
|
|
|
|
|
"ifelse" [ bins.test "$path" "=" "/notes" ]
|
|
|
|
|
[ "export" "content-type" "text/html"
|
|
|
|
|
"export" "serve-file" index
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.env-splice-record
|
2021-01-29 15:58:33 +01:00
|
|
|
|
]
|
|
|
|
|
# TODO: ignore potential query arguments. See 404 message
|
|
|
|
|
"pipeline" [ router-lookup "$path" ]
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.record-splice-env
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"importas" "-ui" "page" "page"
|
|
|
|
|
"export" "content-type" "text/html"
|
|
|
|
|
"export" "serve-file" "$page"
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.env-splice-record
|
2021-01-29 15:58:33 +01:00
|
|
|
|
]
|
|
|
|
|
runOr return500
|
|
|
|
|
"if" [
|
|
|
|
|
"pipeline" [ bins.printf ''
|
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
|
Content-Type: {{{content-type}}}; charset=UTF-8
|
|
|
|
|
Connection: close
|
|
|
|
|
|
|
|
|
|
'' ]
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.netencode-mustache
|
2021-01-29 15:58:33 +01:00
|
|
|
|
]
|
|
|
|
|
"pipeline" [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.netencode.record-splice-env
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"importas" "-ui" "serve-file" "serve-file"
|
|
|
|
|
bins.cat "$serve-file"
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# run argv or $1 if argv returns a failure status code.
|
2021-01-29 15:58:33 +01:00
|
|
|
|
runOr = depot.nix.writeExecline "run-or" { readNArgs = 1; } [
|
|
|
|
|
"foreground" [ "$@" ]
|
|
|
|
|
"importas" "?" "?"
|
|
|
|
|
"ifelse" [ bins.test "$?" "-eq" "0" ]
|
|
|
|
|
[]
|
2021-04-04 04:04:20 +02:00
|
|
|
|
"if" [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"$1"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return400 = depot.nix.writeExecline "return400" {} [
|
|
|
|
|
bins.printf "%s" ''
|
|
|
|
|
HTTP/1.1 400 Bad Request
|
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
|
Connection: close
|
|
|
|
|
|
|
|
|
|
''
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return404 = depot.nix.writeExecline "return404" {} [
|
|
|
|
|
bins.printf "%s" ''
|
|
|
|
|
HTTP/1.1 404 Not Found
|
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
|
Connection: close
|
|
|
|
|
|
|
|
|
|
This page doesn’t exist! Query arguments are not handled at the moment.
|
|
|
|
|
''
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return500 = depot.nix.writeExecline "return500" {} [
|
|
|
|
|
bins.printf "%s" ''
|
|
|
|
|
HTTP/1.1 500 Internal Server Error
|
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
|
Connection: close
|
|
|
|
|
|
|
|
|
|
Encountered an internal server error. Please try again.
|
|
|
|
|
''
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
capture-stdin = depot.nix.writers.rustSimple {
|
|
|
|
|
name = "capture-stdin";
|
2021-08-07 19:19:33 +02:00
|
|
|
|
dependencies = [ depot.users.Profpatsch.execline.exec-helpers ];
|
2021-01-29 15:58:33 +01:00
|
|
|
|
} ''
|
|
|
|
|
extern crate exec_helpers;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
fn main() {
|
|
|
|
|
let (args, prog) = exec_helpers::args_for_exec("capture-stdin", 1);
|
|
|
|
|
let valname = &args[1];
|
|
|
|
|
let mut v : Vec<u8> = vec![];
|
|
|
|
|
std::io::stdin().lock().read_to_end(&mut v).unwrap();
|
|
|
|
|
exec_helpers::exec_into_args("capture-stdin", prog, vec![(valname, v)]);
|
|
|
|
|
}
|
|
|
|
|
'';
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# go from a list of path elements to an absolute route string
|
2021-01-29 15:58:33 +01:00
|
|
|
|
mkRoute = route: "/" + lib.concatMapStringsSep "/" urlencodeAscii route;
|
|
|
|
|
|
|
|
|
|
# urlencodes, but only ASCII characters
|
|
|
|
|
# https://en.wikipedia.org/wiki/Percent-encoding
|
|
|
|
|
urlencodeAscii = urlPiece:
|
|
|
|
|
let
|
|
|
|
|
raw = [ "!" "#" "$" "%" "&" "'" "(" ")" "*" "+" "," "/" ":" ";" "=" "?" "@" "[" "]" ];
|
|
|
|
|
enc = [ "%21" "%23" "%24" "%25" "%26" "%27" "%28" "%29" "%2A" "%2B" "%2C" "%2F" "%3A" "%3B" "%3D" "%3F" "%40" "%5B" "%5D" ];
|
|
|
|
|
rest = [ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "-" "_" "." "~" ];
|
|
|
|
|
in
|
|
|
|
|
assert lib.assertMsg (lib.all (c: builtins.elem c (raw ++ rest)) (lib.stringToCharacters urlPiece))
|
|
|
|
|
"urlencodeAscii: the urlPiece must only contain valid url ASCII characters, was: ${urlPiece}";
|
|
|
|
|
builtins.replaceStrings raw enc urlPiece;
|
|
|
|
|
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# create a cdb record entry, as required by the cdbmake tool
|
2021-01-29 15:58:33 +01:00
|
|
|
|
cdbRecord = key: val:
|
|
|
|
|
"+${toString (builtins.stringLength key)},${toString (builtins.stringLength val)}:"
|
|
|
|
|
+ "${key}->${val}\n";
|
2021-08-07 19:44:17 +02:00
|
|
|
|
|
|
|
|
|
# create a full cdbmake input from an attribute set of keys to values (strings)
|
2021-01-29 15:58:33 +01:00
|
|
|
|
cdbRecords =
|
|
|
|
|
with depot.nix.yants;
|
|
|
|
|
defun [ (attrs (either drv string)) string ]
|
|
|
|
|
(attrs:
|
|
|
|
|
(lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# run cdbmake on a list of key/value pairs (strings
|
2021-01-29 15:58:33 +01:00
|
|
|
|
cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb" {
|
|
|
|
|
stdin = cdbRecords attrs;
|
|
|
|
|
} [
|
|
|
|
|
"importas" "out" "out"
|
2021-08-07 19:19:33 +02:00
|
|
|
|
depot.users.Profpatsch.lib.eprint-stdin
|
2021-01-29 15:58:33 +01:00
|
|
|
|
"if" [ bins.cdbmake "db" "tmp" ]
|
|
|
|
|
bins.mv "db" "$out"
|
|
|
|
|
];
|
|
|
|
|
|
2021-08-07 19:44:17 +02:00
|
|
|
|
# look up a key ($2) in the given cdb ($1)
|
2021-01-29 15:58:33 +01:00
|
|
|
|
cdbLookup = depot.nix.writeExecline "cdb-lookup" { readNArgs = 2; } [
|
|
|
|
|
# cdb ($1) on stdin
|
|
|
|
|
"redirfd" "-r" "0" "$1"
|
|
|
|
|
# key ($2) lookup
|
|
|
|
|
bins.cdbget "$2"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
in depot.nix.utils.drvTargets {
|
|
|
|
|
inherit
|
|
|
|
|
router
|
|
|
|
|
notes-server
|
|
|
|
|
index
|
|
|
|
|
router-lookup
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
}
|