about summary refs log tree commit diff
path: root/web/bubblegum/examples
diff options
context:
space:
mode:
Diffstat (limited to 'web/bubblegum/examples')
-rw-r--r--web/bubblegum/examples/blog.nix143
-rw-r--r--web/bubblegum/examples/default.nix82
-rw-r--r--web/bubblegum/examples/derivation-svg.nix13
-rw-r--r--web/bubblegum/examples/hello.nix94
-rw-r--r--web/bubblegum/examples/posts/2021-04-01-hello.html3
-rw-r--r--web/bubblegum/examples/posts/2021-04-02-a second post.html7
6 files changed, 342 insertions, 0 deletions
diff --git a/web/bubblegum/examples/blog.nix b/web/bubblegum/examples/blog.nix
new file mode 100644
index 0000000000..76b91168b8
--- /dev/null
+++ b/web/bubblegum/examples/blog.nix
@@ -0,0 +1,143 @@
+{ depot, ... }:
+
+let
+  inherit (depot.third_party.nixpkgs)
+    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, ... }: ''
+    <!doctype html>
+    <html>
+      <head>
+        <meta charset="utf-8">
+        <title>${title}</title>
+        <style>a:link, a:visited { color: blue; }</style>
+      </head>
+      <body>
+      ${inner}
+      </body>
+    </html>
+  '';
+
+  index = posts: ''
+    <main>
+      <h1>blog posts</h1>
+      <ul>
+  '' + lib.concatMapStrings
+    (post: ''
+      <li>
+        <a href="${absolutePath (url.encode {} post)}">${parseTitle post}</a>
+      </li>
+    '')
+    posts + ''
+      </ul>
+    </main>
+  '';
+
+  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: ''
+    <main>
+      <h1>${title}</h1>
+      <div id="content">
+        ${builtins.readFile (blogdir + "/" + post)}
+      </div>
+    </main>
+    <footer>
+      <p>Posted on ${formatDate (parseDate post)}</p>
+      <nav><a href="${scriptName}">index</a></nav>
+    </footer>
+  '';
+
+  validatePathInfo = pathInfo:
+    let
+      chars = string.toChars pathInfo;
+    in
+    builtins.length chars > 1
+    && !(builtins.elem "/" (builtins.tail chars));
+
+  response =
+    if pathInfo == "/"
+    then {
+      title = "blog";
+      status = 200;
+      inner = index posts;
+    }
+    else if !(validatePathInfo pathInfo)
+    then {
+      title = "Bad Request";
+      status = 400;
+      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 = 200;
+      inner = post title pathInfo;
+    } else {
+      title = "Not Found";
+      status = 404;
+      inner = "<h1>404 โ€” not found</h1>";
+    };
+in
+respond response.status
+{
+  "Content-type" = "text/html";
+}
+  (generic response)
diff --git a/web/bubblegum/examples/default.nix b/web/bubblegum/examples/default.nix
new file mode 100644
index 0000000000..89482f93ea
--- /dev/null
+++ b/web/bubblegum/examples/default.nix
@@ -0,0 +1,82 @@
+{ depot, pkgs, lib, ... }:
+
+let
+
+  scripts = [
+    ./hello.nix
+    ./derivation-svg.nix
+    (substituteAll {
+      src = ./blog.nix;
+      # by making this a plain string this
+      # can be something outside the nix store!
+      blogdir = ./posts;
+    })
+  ];
+
+  inherit (depot.nix)
+    writeExecline
+    runExecline
+    getBins
+    ;
+
+  inherit (depot.web.bubblegum)
+    writeCGI
+    ;
+
+  inherit (pkgs)
+    runCommandLocal
+    substituteAll
+    ;
+
+  bins = (getBins pkgs.thttpd [ "thttpd" ])
+    // (getBins pkgs.coreutils [ "printf" "cp" "mkdir" ]);
+
+  webRoot =
+    let
+      copyScripts = lib.concatMap
+        (path:
+          let
+            cgi = writeCGI
+              {
+                # assume we are on NixOS since thttpd doesn't set PATH.
+                # using third_party.nix is tricky because not everyone
+                # has a tvix daemon running.
+                binPath = "/run/current-system/sw/bin";
+              }
+              path;
+          in
+          [
+            "if"
+            [ bins.cp cgi "\${out}/${cgi.name}" ]
+          ])
+        scripts;
+    in
+    runExecline.local "webroot" { } ([
+      "importas"
+      "out"
+      "out"
+      "if"
+      [ bins.mkdir "-p" "$out" ]
+    ] ++ copyScripts);
+
+  port = 9000;
+
+in
+writeExecline "serve-examples" { } [
+  "foreground"
+  [
+    bins.printf
+    "%s\n"
+    "Running on http://localhost:${toString port}"
+  ]
+  "${bins.thttpd}"
+  "-D"
+  "-p"
+  (toString port)
+  "-l"
+  "/dev/stderr"
+  "-c"
+  "*.nix"
+  "-d"
+  webRoot
+]
diff --git a/web/bubblegum/examples/derivation-svg.nix b/web/bubblegum/examples/derivation-svg.nix
new file mode 100644
index 0000000000..9a625afb55
--- /dev/null
+++ b/web/bubblegum/examples/derivation-svg.nix
@@ -0,0 +1,13 @@
+# Warning: this is *very* slow on the first request
+{ depot, ... }:
+
+let
+  inherit (depot.web.bubblegum)
+    respond
+    ;
+in
+respond "OK"
+{
+  Content-type = "image/svg+xml";
+}
+  (builtins.readFile "${depot.tvix.docs.svg}/component-flow.svg")
diff --git a/web/bubblegum/examples/hello.nix b/web/bubblegum/examples/hello.nix
new file mode 100644
index 0000000000..bd4891f7d6
--- /dev/null
+++ b/web/bubblegum/examples/hello.nix
@@ -0,0 +1,94 @@
+{ depot, ... }:
+
+let
+  inherit (depot.third_party.nixpkgs)
+    lib
+    ;
+
+  inherit (depot.web.bubblegum)
+    pathInfo
+    respond
+    absolutePath
+    ;
+
+  routes = {
+    "/" = {
+      status = "OK";
+      title = "index";
+      content = ''
+        Hello World!
+      '';
+    };
+    "/clock" = {
+      status = "OK";
+      title = "clock";
+      content = ''
+        It is ${toString builtins.currentTime}s since 1970-01-01 00:00 UTC.
+      '';
+    };
+    "/coffee" = {
+      status = "I'm a teapot";
+      title = "coffee";
+      content = ''
+        No coffee, I'm afraid
+      '';
+    };
+    "/type-error" = {
+      status = 666;
+      title = "bad usage";
+      content = ''
+        Never gonna see this.
+      '';
+    };
+    "/eval-error" = {
+      status = "OK";
+      title = "evaluation error";
+      content = builtins.throw "lol";
+    };
+  };
+
+  notFound = {
+    status = "Not Found";
+    title = "404";
+    content = ''
+      This page doesn't exist.
+    '';
+  };
+
+  navigation =
+    lib.concatStrings (lib.mapAttrsToList
+      (p: v: "<li><a href=\"${absolutePath p}\">${v.title}</a></li>")
+      routes);
+
+  template = { title, content, ... }: ''
+    <!doctype html>
+    <html lang="en">
+    <head>
+      <meta charset="utf-8">
+      <title>${title}</title>
+      <style>a:link, a:visited { color: blue; }</style>
+    </head>
+    <body>
+      <hgroup>
+      <h1><code>//web/bubblegum</code></h1>
+      <h2>example app</h2>
+      </hgroup>
+      <header>
+        <nav>
+          <ul>${navigation}</ul>
+        </nav>
+      </header>
+      <main>
+        <p>${content}</p>
+      </main>
+    </body>
+  '';
+
+  response = routes."${pathInfo}" or notFound;
+
+in
+respond response.status
+{
+  "Content-type" = "text/html";
+}
+  (template response)
diff --git a/web/bubblegum/examples/posts/2021-04-01-hello.html b/web/bubblegum/examples/posts/2021-04-01-hello.html
new file mode 100644
index 0000000000..3c58be7953
--- /dev/null
+++ b/web/bubblegum/examples/posts/2021-04-01-hello.html
@@ -0,0 +1,3 @@
+<p>
+  This is it, the peak of cursed.
+</p>
diff --git a/web/bubblegum/examples/posts/2021-04-02-a second post.html b/web/bubblegum/examples/posts/2021-04-02-a second post.html
new file mode 100644
index 0000000000..050586c302
--- /dev/null
+++ b/web/bubblegum/examples/posts/2021-04-02-a second post.html
@@ -0,0 +1,7 @@
+<p>
+  <ul>
+    <li>โœ… sorting</li>
+    <li>โœ… url encoding (admire the spaces!)</li>
+    <li>โœ… classic Nix regex based parsing</li>
+  </ul>
+</p>