<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<atom:link href="https://codebam.ca/rss.xml" rel="self" type="application/rss+xml" />
		<title>codebam</title>
		<link>https://codebam.ca</link>
		<description>codebam&apos;s website and blog</description>
		<language>en-US</language>
		<lastBuildDate>Mon, 10 Aug 2026 03:25:38 GMT</lastBuildDate>
		<item>
		<guid isPermaLink="true">https://codebam.ca/posts/usb-cable-testing</guid>
		<title><![CDATA[USB Cable Testing on Linux]]></title>
		<description><![CDATA[usbmon turns Wireshark into a cable tester, and finds retransmissions on cables that supposedly work.]]></description>
		<link>https://codebam.ca/posts/usb-cable-testing</link>
		<pubDate>Wed, 15 Apr 2026 07:11:06 GMT</pubDate>
		<category>usb</category><category>linux</category><category>wireshark</category><category>test</category>
		<content:encoded><![CDATA[<!--[--><p>I was interested in testing my USB cables that I know work, but wanted to see if they had any retransmission going on.</p> <p>Then today, I found out Linux has a kernel module that allows you to “monitor” usb interfaces. Like… using wireshark.</p> <p>I won’t explain how to set up wireshark for your distro, that’s for you to figure out.</p> <p>Once you have wireshark set up, you just <code>sudo modprobe usbmon</code> then <code>lsusb</code>.
Find the USB interface you want to monitor in the list, then select it when starting wireshark (gui).</p> <p>That’s about it… you can filter like this:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>usb.urb_status != 0 &#x26;&#x26; !(usb.urb_status == -115)</span></span></code></pre><!----> <p>Short post, but hope this helps you test your own USB cables for any errors that aren’t shown in dmesg, like failed packets.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/nixos-uki</guid>
		<title><![CDATA[Secure Boot NixOS UKI Installer]]></title>
		<description><![CDATA[A flake that builds a signed unified kernel image, for rescuing a Steam Deck locked out by Secure Boot.]]></description>
		<link>https://codebam.ca/posts/nixos-uki</link>
		<pubDate>Sun, 06 Jul 2025 17:57:04 GMT</pubDate>
		<category>nixos</category><category>secure boot</category><category>steam deck</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>In this post I simply want to share a NixOS flake that might help you in the
case you enabled secure boot on a device such as the Steam Deck OLED, still
have keys because you backed them up, and want to get back in to either disable
secure boot with sbctl, or fix your unbootable NixOS system.</p> <h3>Configuration</h3> <p>First, here is the flake that will load your configuration:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:NixOS/nixpkgs/nixos-unstable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs &#125;:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "x86_64-linux"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> import</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">inherit</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; &#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      nixosImage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">nixosSystem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        inherit</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        modules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">          ./image-config.nix</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    in</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.$&#123;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">disk-image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixosImage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"image"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      defaultPackage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.$&#123;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">packages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$&#123;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">disk-image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <p>Now the config that you can use to build an image.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    modulesPath</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;:</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#123;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    imports</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">modulesPath</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/image/repart.nix"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">loader</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">grub</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">initrd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">availableKernelModules</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"usb_storage"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    boot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">supportedFilesystems</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"btrfs"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    hardware</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">enableAllHardware</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    environment</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">systemPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">      btrfs-progs</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">      # sbctl # if you want it to be able to disable secure boot</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">     ];</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    fileSystems</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">device</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/dev/disk/by-label/nixos"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    networking</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      networkmanager</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        wifi</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">backend</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "iwd"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      wireless</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">iwd</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        enable</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      users</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        codebam</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          isNormalUser</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          home</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "/home/codebam"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "Sean Behan"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          extraGroups</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "wheel"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "networkmanager"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "libvirtd"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "video"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "uinput"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "wireshark"</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "pipewire"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          hashedPassword</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "$6$TIP8YR83obmkq8T2$T3lYdPbPj9wysMznNlS5J0qHo2eyTr43aF/ZWSMWHdNRob4dkBB0s3KpBLUgYRTyPZxbb1ZgeqCrrx.DEEkQX1"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    image</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">repart</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "image"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      partitions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "esp"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          contents</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "/EFI/BOOT/BOOT</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">toUpper</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">stdenv</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hostPlatform</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">efiArch</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.EFI"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">source</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">              "</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">systemd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/lib/systemd/boot/efi/systemd-boot</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">stdenv</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hostPlatform</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">efiArch</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">.efi"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">            "/EFI/Linux/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">boot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">loader</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ukiFile</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">source</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">              "</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">uki</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">$&#123;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">boot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">loader</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">ukiFile</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          &#125;;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          repartConfig</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "esp"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Format</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "vfat"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            SizeMinBytes</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "96M"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#125;;</span></span>
<span class="line"><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">        "root"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          storePaths</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [ </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">config</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">build</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">toplevel</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ];</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">          repartConfig</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "root"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Format</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "ext4"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "nixos"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">            Minimize</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "guess"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">          &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre><!----> <h3>Building</h3> <h3>Building the Image</h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">nix</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span></span></code></pre><!----> <p>Make sure you change the user and password, or you won’t be able to login. Also
note that if you are using a Steam Deck OLED like me, WiFi won’t work so you’ll
need to use either ethernet or an external wireless adapter that has support in
the kernel.</p> <h3>Signing for Secure Boot</h3> <p>So ok you burn it to your USB drive:</p> <p>(replace X with your USB drive letter, and add sudo or elevate)</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> if=./result/image.raw</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> of=/dev/sdX</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> bs=4M</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> status=progress</span></span></code></pre><!----> <p>Now you have to sign it if you want secure boot to work. Assuming you already
have keys set up you just run:</p> <p>(again with sudo or elevate)</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sdX1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sbctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sign</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt/EFI/BOOT/BOOTX64.EFI</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sbctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sign</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt/EFI/Linux/nixos.efi</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sync</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">umount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><!----> <h3>Booting and Repair</h3> <p>Now you’re ready to boot it, so plug it into your USB hub or however you planned
on plugging it into the Steam Deck or your device.</p> <p>Once you’re in you will have access to a NixOS installer, and you should know
what to do next to repair.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/quadlets</guid>
		<title><![CDATA[Podman Quadlets]]></title>
		<description><![CDATA[Quadlets describe a container as a systemd unit, so systemd owns the lifecycle instead of Podman.]]></description>
		<link>https://codebam.ca/posts/quadlets</link>
		<pubDate>Fri, 28 Mar 2025 14:39:58 GMT</pubDate>
		<category>linux</category><category>systemd</category><category>podman</category><category>containers</category><category>quadlet</category>
		<content:encoded><![CDATA[<!--[--><h3>What are Quadlets?</h3> <p>In this post I’ll show you what a quadlet is and how you can use it to manage
your containers.</p> <p>Quadlets are a new way to manage containers using systemd. They are a new
format for systemd units that allow you to manage containers using systemd.</p> <h3>Container Quadlets</h3> <p>Here’s an example of a container quadlet.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/alpine:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span></code></pre><!----> <p>If you put this in ~/.config/containers/systemd/alpine.container you can start
it with <code>systemctl --user start alpine</code> and it will start a new container.</p> <p>Just make sure to <code>systemctl --user daemon-reload</code> to reload the systemd daemon
after you create the quadlet. You have to do this every time, I won’t repeat
this throughout the blog post.</p> <h3>Pod Quadlets</h3> <p>You can also use quadlets to manage pods. Here’s an example of a pod quadlet.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Pod]</span></span></code></pre><!----> <p>Yep, that’s it. If you put it in <code>~/.config/containers/systemd/mypod.pod</code> you can
start it with <code>systemctl --user start mypod-pod</code> and it will start a new pod.</p> <p>But the best part is this. If you want to add a container to a pod all you have
to do is this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/alpine:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Pod</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mypod.pod</span></span></code></pre><!----> <p>Now when you start <code>mypod-pod</code> it will start the container as well.</p> <h3>Dependencies</h3> <p>You can add dependencies.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Unit]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">After</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">alpine.service</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/library/archlinux:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">sleep infinity</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Pod</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">mypod.pod</span></span></code></pre><!----> <p>Now archlinux will start after alpine.</p> <p>There are even more options. You can start .kube, .network, and .build quadlets
too and they can depend on each other. See the <a href="https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html" rel="nofollow noopener noreferrer" target="_blank">docs</a> for more info!</p> <hr/> <h3>Example: Nginx and Certbot</h3> <p>Okay so now lets run something useful :)</p> <p>How about an nginx container and a certbot container to get a certificate for it?</p> <h3>Nginx Container</h3> <p>First lets create a directory to hold our files.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -p</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ~/.config/containers/systemd/www</span></span></code></pre><!----> <p>Next we create a quadlet for our nginx container.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./www:/usr/share/nginx/html</span></span></code></pre><!----> <p>This will run a web server for us. If you aren’t root, you’ll need to enable unprivleged ports.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> sysctl</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> net.ipv4.ip_unprivileged_port_start=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span></span></code></pre><!----> <p>Now we can start it with <code>systemctl --user start nginx</code> and it will start a new container.</p> <p>If you browse to <code>http://localhost</code> you should see the nginx welcome page.</p> <h3>Certbot Container</h3> <p>Now we need to get a certificate for it. We can use certbot for this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/certbot/certbot:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./www:/mnt</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">certonly --webroot --webroot-path /mnt --agree-tos --email your@email.com -d your.domain.com</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span></code></pre><!----> <p>Make sure to replace <a href="mailto:your@email.com">your@email.com</a> and your.domain.com with your own email and domain.</p> <p>Then start the container with <code>systemctl --user start certbot</code> and it will get a certificate for you.</p> <h3>Nginx with SSL</h3> <p>Now you can use the certificate in your nginx container.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span></code></pre><!----> <p>That’s great, but now we need to configure nginx to use the certificate. We can
do this by adding a volume for the configuration file.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/nginx:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">80</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:80</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">PublishPort</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:443</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/nginx/certs</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">./nginx.conf:/etc/nginx/conf.d</span></span></code></pre><!----> <p>Along with a configuration file for nginx.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">server</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    listen </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">443</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ssl;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    server_name </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">your.domain.com;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/certs/live/your.domain.com/fullchain.pem;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    ssl_certificate_key </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/etc/nginx/certs/live/your.domain.com/privkey.pem;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    location</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> / </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        root </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/usr/share/nginx/html;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        index </span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">index.html;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <p>Again, make sure to replace your.domain.com with your own domain.</p> <p>Now you can start the container with <code>systemctl --user start nginx</code> and it will
start a new container with the certificate. You can verify this by going to <code>https://your.domain.com</code>.</p> <p>Now you have a web server with HTTPS.</p> <h3>Automatic Certificate Renewal</h3> <p>If you want to renew your certificate automatically it’s as simple as this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Container]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Image</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">docker.io/certbot/certbot:latest</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Exec</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">renew</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">AutoUpdate</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">registry</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Volume</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">letsencrypt:/etc/letsencrypt</span></span></code></pre><!----> <p>Now you can create a timer for it in <code>~/.config/systemd/user/certbot.timer</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Unit]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Renew Certbot certificates</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Timer]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">OnCalendar</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Sun </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">00</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:00:00</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Persistent</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">true</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">Unit</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">certbot.service</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">[Install]</span></span>
<span class="line"><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">WantedBy</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">timers.target</span></span></code></pre><!----> <p>Now you enable the timer.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">systemctl</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --user</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> enable</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --now</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> certbot.timer</span></span></code></pre><!----> <p>Now your certificate will be renewed every Sunday at midnight.</p> <p>That’s it! Now you have a web server with HTTPS and a certificate that will be
renewed automatically.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/email-bot</guid>
		<title><![CDATA[Email bot]]></title>
		<description><![CDATA[Cloudflare email routing plus Workers AI: an inbox that answers its own mail.]]></description>
		<link>https://codebam.ca/posts/email-bot</link>
		<pubDate>Wed, 04 Dec 2024 20:29:41 GMT</pubDate>
		<category>typescript</category><category>serverless</category><category>cloudflare</category>
		<content:encoded><![CDATA[<!--[--><h3>How it Works</h3> <p>In this post I’m just going to show you something cool I made using cloudflare workers.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; EmailMessage &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'cloudflare:email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; createMimeMessage &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'mimetext'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">message</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> content</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.raw).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> messages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#123; role: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'system'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, content: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'you are an email responder, reply to the given message'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125;,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#123; role: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'user'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, content &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		];</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">		// @ts-expect-error broken bindings</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">AI</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@cf/meta/llama-3.2-11b-vision-instruct'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, &#123; messages &#125;);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> createMimeMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setHeader</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'In-Reply-To'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message.headers.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Message-ID'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSender</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(&#123; name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Sean Behan'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, addr: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'contact@seanbehan.ca'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125;);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setRecipient</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(message.from);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setSubject</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'RE'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">addMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(&#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			contentType: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'text/plain'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			data: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#96;$&#123;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">response</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">&#125;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">&#92;n&#92;n</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">This was an automated message. If this is urgent, message Sean on Telegram https://t.me/codebam&#96;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> replyMessage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> EmailMessage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'contact@seanbehan.ca'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, message.from, msg.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">asRaw</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(replyMessage);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> message.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">forward</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'codebam@riseup.net'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">satisfies</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ExportedHandler</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>;</span></span></code></pre><!----> <p>This small amount of code is all it takes to automatically reply to all my emails with llama3.2!</p> <p>Feel free to copy it and use it yourself. The source code is on <a href="https://github.com/codebam/email-bot" rel="nofollow noopener noreferrer" target="_blank">GitHub</a>.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/nixos</guid>
		<title><![CDATA[NixOS Flakes]]></title>
		<description><![CDATA[What a flake actually is: inputs, outputs, and a NixOS configuration with every dependency pinned.]]></description>
		<link>https://codebam.ca/posts/nixos</link>
		<pubDate>Sat, 01 Jun 2024 22:38:41 GMT</pubDate>
		<category>nixos</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction to Flakes</h3> <p>Flakes in NixOS allow you to write Nix code that has custom inputs and outputs.
For example this is a simple example of a Flake. You can get a copy for
yourself by running <code>nix flake init</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  description</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "A very basic flake"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    nixpkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:nixos/nixpkgs?ref=nixos-unstable"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs &#125;: &#123;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">x86_64-linux</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hello</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">legacyPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">x86_64-linux</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hello</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    packages</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">x86_64-linux</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">packages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">x86_64-linux</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">hello</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <h3>Flake Structure</h3> <p>As you can see there are inputs (nixpkgs), and outputs (self, nixpkgs).</p> <h3>Managing Dependencies with flake.lock</h3> <p>If you build this Flake using <code>nix build</code> you’ll end up with a new directory
called <code>result</code> which is a symlink to where the package was built in your Nix
store.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> -</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /nix/store/yh6x9ia5kxxw3w90vkb09vqgfvhb416k-hello-2.12.1</span></span></code></pre><!----> <p>You’ll also end up with a <code>flake.lock</code>. If you’ve used <code>npm</code> before <code>flake.lock</code> is very similar to <code>package-lock.json</code>. If you haven’t, a quick
explanation is that it keeps track of package versions and keeps them locked
between updates. If you want to update your lockfile so you can get a newer
package version you can do so with <code>nix flake update</code></p> <p>Now so far I’ve only showed a very basic example of a Flake, the example from <code>nix flake init</code>. You can see more templates with <code>nix flake show templates</code>.</p> <h3>Development Shells (devShell)</h3> <p>One thing I like using Flakes for is to create a <code>devShell</code>. You can initialize
a Flake with a <code>devShell</code> preconfigured with <code>nix flake init -t templates#utils-generic</code></p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  inputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    utils</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> "github:numtide/flake-utils"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  outputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; self</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">,</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> utils &#125;: </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">utils</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">lib</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">eachDefaultSystem</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (system:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    let</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> nixpkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">legacyPackages</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$&#123;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">system</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    in</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">      devShell</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mkShell</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        buildInputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  );</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <p>In this example you can add packages from nixpkgs to <code>buildInputs</code> for them to
be installed temporarily. You can activate it with <code>nix develop</code>. This is the <code>devShell</code> I use for my typescript projects.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># ...</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">      devShell</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic"> =</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">mkShell</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">        buildInputs</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> with</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70"> pkgs</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; [</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          efm-langserver</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nil</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">nodejs</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">typescript-language-server</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">prettier</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          vscode-langservers-extracted</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">          nodePackages_latest</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">bash-language-server</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        ];</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;</span><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"># ...</span></span></code></pre><!----> <h3>Building Packages with Flakes</h3> <p>You can use Flakes to build packages as well. You can look at just about any
Nix package for an example. Just choose one in <a href="https://github.com/nixos/nixpkgs" rel="nofollow noopener noreferrer" target="_blank">nixpkgs</a>. Flakes have many functions to
build different types of packages as you can see in nixpkgs.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/cf-workers-telegram-bot</guid>
		<title><![CDATA[Cloudflare Workers Telegram Bot]]></title>
		<description><![CDATA[A tour of the rewritten cf-workers-telegram-bot API for running a Telegram bot on Cloudflare Workers.]]></description>
		<link>https://codebam.ca/posts/cf-workers-telegram-bot</link>
		<pubDate>Wed, 15 May 2024 07:11:06 GMT</pubDate>
		<category>typescript</category><category>cloudflare</category>
		<content:encoded><![CDATA[<!--[--><p>This past week I spent rewriting <a href="https://github.com/codebam/cf-workers-telegram-bot" rel="nofollow noopener noreferrer" target="_blank"><code>cf-workers-telegram-bot</code></a>.
Here I’m going to show you the newly updated API that anyone can use to run a
Telegram Bot on Cloudflare Workers or any other serverless platform.</p> <h3>Project Setup</h3> <p>First you need to set up a new Cloudflare Workers project using wrangler.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> cloudflare@latest</span></span></code></pre><!----> <p>This will prompt you for a project name first. Choose a name for your new
project or use the default.</p> <p>Next you’ll be prompted for the type of application you want to deploy. You
should choose a “Hello World” Worker.</p> <p>After that you’ll be asked a few more questions, like if you want to use
TypeScript, and Git (optional, but recommended). You don’t need to deploy just
yet.</p> <h3>Adding Your Bot Token</h3> <p>Next you can add the token you get from <code>@BotFather</code> on Telegram as a secret so
you don’t have to keep it in the code.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npx</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> secret</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> put</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> TOKEN</span></span></code></pre><!----> <p>Once you’ve created your project and created your secret you’ll want to install
the <code>cf-workers-telegram-bot</code> library to start coding.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-new-project</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> i</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> @codebam/cf-workers-telegram-bot</span></span></code></pre><!----> <p>This will give you access to my <code>TelegramBot</code> class for creating a Telegram Bot.</p> <h3>Writing the Bot</h3> <p>To get started you can open <code>src/index.ts</code> in your favorite editor such as
vscode or vim. Then you can import like this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> TelegramBot, &#123; TelegramExecutionContext &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@codebam/cf-workers-telegram-bot'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><!----> <p>Then you can simply write your bot like this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  async</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">request</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Request</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">env</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Env</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">ctx</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ExecutionContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> bot</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TelegramBot</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">TOKEN</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> bot.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'start'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> function</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">context</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> TelegramExecutionContext</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      switch</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (context.update_type) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	case</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'message'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> context.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">reply</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'Hello from Cloudflare workers'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  break</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	  break</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">handle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">clone</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    return</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Response</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'ok'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  &#125;,</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span></code></pre><!----> <h3>Deployment and Webhook</h3> <p>Now you’re ready to deploy.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npx</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> deploy</span></span></code></pre><!----> <p>You can set your webhook by going to the URL you’ve been given plus your token and <code>?command=set</code>.</p> <p>Like this. <code>https://telegram-bot.username.workers.dev/YOURTOKENHERE?command=set</code></p> <p>Now when you send your bot a message on Telegram it should respond with <code>Hello from Cloudflare workers</code>.</p> <h3>Starter Template</h3> <p>If you’ve got this far and you don’t want to code it all by yourself, you can
fork my repository <a href="https://github.com/codebam/cwtb-consumer" rel="nofollow noopener noreferrer" target="_blank">here</a> to get
started with a working copy.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/svelte-search</guid>
		<title><![CDATA[Search bar in Sveltekit]]></title>
		<description><![CDATA[The fuzzy search on this site, built with fuse.js.]]></description>
		<link>https://codebam.ca/posts/svelte-search</link>
		<pubDate>Wed, 06 Dec 2023 20:29:41 GMT</pubDate>
		<category>svelte</category><category>sveltekit</category><category>javascript</category><category>typescript</category><category>serverless</category><category>cloudflare</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>In this post I’m going to show you how I made the search bar on the posts page
and main page of my website.</p> <h3>Implementing the Search Bar</h3> <p>I used a library called <code>fuse.js</code> for fuzzy searching for titles, here’s how it
works.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">script lang</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; Heading &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'flowbite-svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Post </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/components/Post.svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Fuse </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'fuse.js'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; afterUpdate &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> posts</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">path</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">meta</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">title</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">date</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; &#125;[];</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> query </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '*'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> posts;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> options</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; keys: [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'meta.title'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">] &#125;;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> fuse</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Fuse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(posts, options);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	afterUpdate</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results_ </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> fuse.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">search</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(query);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results_.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">map</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">result</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (&#123; path: result.item.path, meta: result.item.meta &#125;));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">script</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Heading</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	tag</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"h4"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	style</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"display: inline"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ml-8 my-4 text-secondary dark:text-dark-secondary w-auto"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Posts</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Heading</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">input class</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text-secondary"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:value</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;query&#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ul</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#123;#each results </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Post &#123;post&#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#123;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">each&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#123;#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> results.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">length</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ===</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#123;#each posts </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">as</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> post</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			&#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">Post &#123;post&#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#123;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">each&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#123;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">/if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ul</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span></code></pre><!----> <h3>How it Works</h3> <p>You can see I initialize the query to <code>'*'</code>. All this really does is sets the
search field to <code>*</code> instead of being empty. Then in the input element I bind to
the value of query so that when the user types in the field the value of query
will change.</p> <p>I do some more initialization of <code>fuse.js</code> to search and then I register <code>afterUpdate()</code>. This allows me to search only when the page changes.</p> <p>Finally I added some logic to display all the posts if there are no results,
like for example when the page loads.</p> <p>All this together looks like the search bar you see on <code>/posts</code> and <code>/</code>.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/svelte-auth</guid>
		<title><![CDATA[Auth and Databases in Sveltekit]]></title>
		<description><![CDATA[HTTP Basic Auth with a session cookie in SvelteKit, backed by Cloudflare D1.]]></description>
		<link>https://codebam.ca/posts/svelte-auth</link>
		<pubDate>Sun, 12 Nov 2023 05:40:33 GMT</pubDate>
		<category>svelte</category><category>sveltekit</category><category>javascript</category><category>typescript</category><category>serverless</category><category>cloudflare</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Recently I implemented HTTP Basic Auth using a session cookie in SvelteKit on
Cloudflare Pages using Cloudflare D1 as a database backend. Here I’ll show some
code snippets so you can do the same.</p> <h3>Project Setup</h3> <p>First you would want to set up your project according to <a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site" rel="nofollow noopener noreferrer" target="_blank">this</a>.
So you can have a basic project set up.</p> <h3>Database Setup</h3> <p>Next you’ll want to add the database to your project. It’s easy to create using
wrangler. Just remember to link it to your project in your cloudflare dash
under settings > functions.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> create</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-database-name</span></span></code></pre><!----> <p>Then you can copy it into your <a href="https://github.com/codebam/svelte-auth/blob/master/wrangler.toml" rel="nofollow noopener noreferrer" target="_blank"><code>wrangler.toml</code></a> so your local dev works with it. To run with support for D1 you can use this
oneliner.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">npm</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> build</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#x26;&#x26; </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> pages</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dev</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> .svelte-kit/cloudflare</span></span></code></pre><!----> <p>My final schema looks like this. Yours might be a bit different, but mine
allows for adding songs to a playlist which will be the purpose of my <a href="https://music.seanbehan.ca" rel="nofollow noopener noreferrer" target="_blank">website</a>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Users (id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">CREATE</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TABLE</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> IF</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> NOT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> EXISTS</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Playlist (id </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> PRIMARY KEY</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, email </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">date</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> TEXT</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre><!----> <p>You’ll want to create these tables using wrangler both locally (on your dev
server) and remotely.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> execute</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> svelte-auth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --local</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --file=./schema.sql</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">wrangler</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> d1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> execute</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> svelte-auth</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --file=./schema.sql</span></span></code></pre><!----> <p>Now you can add it to your <a href="https://github.com/codebam/svelte-auth/blob/master/src/app.d.ts" rel="nofollow noopener noreferrer" target="_blank"><code>app.d.ts</code></a> like this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">declare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> global &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	namespace</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> App</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		interface</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Platform</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			env</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">?:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">				DB</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> D1Database</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#125;;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			context</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">				waitUntil</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">promise</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>)</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#125;;</span></span>
<span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">			caches</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> CacheStorage</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> &#x26;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">default</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Cache</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;&#125;;</span></span></code></pre><!----> <h3>Authentication Implementation</h3> <p>Now you’re ready to set up authentication. It’s probably easier to just show
you how I did it. I imported <code>sha256</code> which is just a simple function I wrote
to call web crypto to get the sha256sum of a string. <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/login/%2Bpage.server.ts" rel="nofollow noopener noreferrer" target="_blank">Here</a> is my code.</p> <p>I added <a href="https://kit.svelte.dev/docs/form-actions" rel="nofollow noopener noreferrer" target="_blank">form actions</a>. These
allow you to quickly write code that will work with HTML <code>&lt;form></code>‘s.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; redirect &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@sveltejs/kit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> sha256 </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/sha256'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	register</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sha256</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'password'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">??</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">success</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'INSERT INTO Users VALUES (?, ?)'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email, password)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (success) &#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stringify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(&#123; email, password &#125;));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'email'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> password</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> sha256</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(data.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'password'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">??</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> results</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'SELECT password FROM Users WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (results.results[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">].password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> password) &#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">set</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">stringify</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(&#123; email, password &#125;));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span></code></pre><!----> <h3>Checking Authentication Status</h3> <p>Now this is enough to save a cookie with the user auth, but to re-auth on a
page we need to check if their cookie is valid. So how I do this is I wrote
another function called <a href="https://github.com/codebam/svelte-auth/blob/master/src/lib/tryLogin.ts" rel="nofollow noopener noreferrer" target="_blank"><code>tryLogin.ts</code></a>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; D1Database &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cloudflare/workers-types'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">session_cookie</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">DB</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> D1Database</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (session_cookie) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">email</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">password</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">parse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(session_cookie);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> results</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'SELECT password FROM Users WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(email).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (results.results[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">].password </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">===</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> password) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	return</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin;</span></span></code></pre><!----> <p>Now I can call this inside <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.server.ts" rel="nofollow noopener noreferrer" target="_blank"><code>+page.server.ts</code></a>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/tryLogin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> getCurrentSong </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/getCurrentSong'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (&#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	auth: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">),</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	song: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> getCurrentSong</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;);</span></span></code></pre><!----> <p>Using it inside the <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte" rel="nofollow noopener noreferrer" target="_blank"><code>+page.svelte</code></a> is as simple as this one line.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>export let data: &#123;auth: boolean, song: &#123;id: string, url: string&#125;&#125;;</span></span></code></pre><!----> <h3>Playlist Management</h3> <p>Now I wrote more form actions for adding and removing songs from the playlist <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/playlist/%2Bpage.server.ts" rel="nofollow noopener noreferrer" target="_blank">here</a>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; redirect &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@sveltejs/kit'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> tryLogin </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/tryLogin'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> actions</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	add</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> session</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> JSON</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">parse</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">));</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> email</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> session.email;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formdata</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> v</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(formdata.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'url'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)).searchParams.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'v'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'INSERT INTO Playlist VALUES (?, ?, ?, ?)'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">				.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(crypto.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">randomUUID</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(), email, v, Math.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">floor</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Date</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">().</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getTime</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">				.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;,</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	remove</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">async</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">event</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">await</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> tryLogin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event.cookies.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'session'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">), event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> formdata</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">formData</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> formdata.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">get</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'id'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">			await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> event.platform?.env?.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">DB</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">prepare</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'DELETE FROM Playlist WHERE id=?'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(id).</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">all</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#125;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">		throw</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> redirect</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">303</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'/'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span></code></pre><!----> <h3>User Interface</h3> <p>Now this allows us to add songs, we just need the interface for it. So I wrote
the <a href="https://github.com/codebam/svelte-auth/blob/master/src/routes/%2Bpage.svelte" rel="nofollow noopener noreferrer" target="_blank">main page</a> to allow logged in users to submit songs.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; enhance &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$app/forms'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> YouTube </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '$lib/svelte-youtube.svelte'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> let</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">auth</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">song</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">id</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">url</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; &#125;;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">script</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">data.auth&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Visit &#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> href</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/login"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>login&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">YouTube</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	on</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">end</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=&#123;() </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> document.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">getElementById</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'remove_song'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)?.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">click</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()&#125;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	videoId</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=&#123;data.song.url&#125;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">	options</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=&#123;&#123; playerVars: &#123; autoplay: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; &#125;&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">/></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> style</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"display: none;"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"POST"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/playlist?/remove"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> use</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">enhance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">input</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"id"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=&#123;data.song.id&#125; /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> id</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"remove_song"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>remove&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;#</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> data.auth&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> method</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"POST"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> action</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/playlist?/add"</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> use</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">enhance</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">label</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			>Submit a song</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">input</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"url"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> type</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"url"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> placeholder</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"paste a youtube link"</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> pattern</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">".*v=.*"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Submit&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">button</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> href</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/logout"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Log out&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">a</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;/</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <h3>Conclusion</h3> <p>If you have any problems or you want to see the entire repo. The code is <a href="https://github.com/codebam/svelte-auth" rel="nofollow noopener noreferrer" target="_blank">here</a> and the live demo is <a href="https://music.seanbehan.ca" rel="nofollow noopener noreferrer" target="_blank">here</a>.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/llama2</guid>
		<title><![CDATA[Using Llama2 on Cloudflare Workers]]></title>
		<description><![CDATA[Wiring Workers AI into a Telegram bot: the AI binding, the model call, and getting a reply back.]]></description>
		<link>https://codebam.ca/posts/llama2</link>
		<pubDate>Thu, 12 Oct 2023 18:21:27 GMT</pubDate>
		
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>You might know I’ve made a Chat Bot <a href="https://github.com/codebam/cf-workers-telegram-bot" rel="nofollow noopener noreferrer" target="_blank">cf-workers-telegram-bot</a>.
Recently I added AI to it using Cloudflare AI. Here I’m going to show you how
and how you too can make your own chatbot using Cloudflare AI.</p> <h3>Cloudflare Workers AI Setup</h3> <p>First you’ll want to add the AI section to your wrangler.toml.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic">[ai]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">binding = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"AI"</span></span></code></pre><!----> <h3>Using the AI Model</h3> <p>Then you can simply import</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123; Ai &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> '@cloudflare/ai'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><!----> <p>And you can use it like this</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> ai</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> new</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Ai</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(env.</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">AI</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">prompt </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'hello world'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> result</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> await</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> ai.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">run</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">'@cf/meta/llama-2-7b-chat-int8'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, &#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	prompt</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;);</span></span></code></pre><!----> <h3>Other Models and Tips</h3> <p>That’s it!</p> <p>There are other models you can use as well on Cloudflare AI so you should check
them out here for other cool things you can do <a href="https://developers.cloudflare.com/workers-ai/models/" rel="nofollow noopener noreferrer" target="_blank">https://developers.cloudflare.com/workers-ai/models/</a></p> <p>I’m hoping for a 70B model soon :)</p> <p>If you want to use this to make a chat bot make sure when you restore the
prompt you wrap user input in <code>[INST] [/INST]</code> tags so llama2 knows what was
user input and what it said.</p> <p>If you want to try out my Chat Bot it’s on <a href="https://t.me/TuxRobot" rel="nofollow noopener noreferrer" target="_blank">Telegram</a>.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/ostree</guid>
		<title><![CDATA[Using Ostree for file snapshots]]></title>
		<description><![CDATA[Ostree is not just for Silverblue. Using it as a content-addressed snapshot store for your own files.]]></description>
		<link>https://codebam.ca/posts/ostree</link>
		<pubDate>Sat, 27 May 2023 11:47:26 GMT</pubDate>
		<category>linux</category><category>backup</category><category>ostree</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Ostree is the software that allows rpm-ostree in Fedora Silverblue to keep snapshots of previous deployments and do incremental updates, but did you know it can also be used to keep snapshots of your own files? It’s actually quite easy once it’s all set up.</p> <p>For this I would strongly suggest you use root for your ostree. Although you can use it without, you will be able to use files that regular users can’t use such as hardlinks if you’re root, which will speed up incremental snapshots very much.</p> <h3>Initializing the Ostree Repository</h3> <p>First you’ll want to create your inital ostree. Do this in a new folder, on a drive with lots of free space for the files you want to snapshot. You’ll need as much free space as all the files you want to snapshot.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkdir</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> tree</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> init</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span></span></code></pre><!----> <h3>Committing and Restoring Files</h3> <p>Next you can start committing files to your new tree. Be sure to check the man pages for <code>ostree-commit</code> and <code>ostree-init</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> commit</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --branch=master</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> $PWD</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/dir</span></span></code></pre><!----> <p>Keep in mind you cannot commit individual files, only entire directories at a time. Now when you change something inside <code>/dir</code> you can commit again with the exact same command and you will have both copies in your ostree. To restore the original copy you can then use <code>ostree-checkout</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> refs</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> master</span></span></code></pre><!----> <p>This will show you a list of all your commits. Now choose the one you want to restore.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ostree</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> checkout</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --repo=tree</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --union</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> your-commit-sha</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> dir</span></span></code></pre><!----> <h3>Important Considerations</h3> <p>Make sure you check <code>ostree -h</code> for a list of ostree commands and read each of their man pages if you want to use them fully.</p> <p>I use this on a filesystem that is already a RAID1. Make sure you keep proper backups, ostree can create snapshots but it won’t help you if your entire drive fails.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/flatpak-mesa</guid>
		<title><![CDATA[Flatpak Mesa Git]]></title>
		<description><![CDATA[Swapping the Flatpak Mesa runtime for mesa-git so a new AMD card stops falling back to software OpenGL.]]></description>
		<link>https://codebam.ca/posts/flatpak-mesa</link>
		<pubDate>Thu, 25 May 2023 17:48:28 GMT</pubDate>
		<category>linux</category><category>graphics</category>
		<content:encoded><![CDATA[<!--[--><h3>The Problem</h3> <p>If you’ve switched to Flatpak for gaming you may notice that Mesa is sometimes a little bit old. The issue I had myself was that my 6000 series AMD graphics card didn’t have up to date graphics drivers, resulting in OpenGL games not running on the graphics card, but rather the CPUs OpenGL implementation, which made games such as CS:GO and Minecraft run at 5 or 10 FPS.</p> <h3>The Solution</h3> <p>To get the latest Mesa git on Flatpak you can install it like this:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> org.freedesktop.Platform.GL.mesa-git</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> org.freedesktop.Platform.GL32.mesa-git</span></span></code></pre><!----> <p>This will allow you to install the latest Mesa graphics drivers to allow applications to detect hardware properly and run better. Make sure when you are prompted for a version of Mesa to install you choose the latest version number that doesn’t have “beta” at the end.</p> <h3>Usage</h3> <p>Once you’ve done this you can run your Flatpak like this to use Mesa git.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">FLATPAK_GL_DRIVERS</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">mesa-git</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> run</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam</span></span></code></pre><!----> <p>You can also export <code>FLATPAK_GL_DRIVERS=mesa-git</code> in your <code>.bashrc</code> or <code>.profile</code> to use Mesa git for all Flatpak applications. This may cause issues though, and I’d personally advise you only use it when it’s needed.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/btrfs</guid>
		<title><![CDATA[BTRFS RAID1 and how to fix it]]></title>
		<description><![CDATA[Setting up a two-disk BTRFS RAID1 for a Steam library, and repairing it when a drive starts throwing errors.]]></description>
		<link>https://codebam.ca/posts/btrfs</link>
		<pubDate>Tue, 23 May 2023 14:39:58 GMT</pubDate>
		<category>linux</category><category>backup</category><category>raid</category>
		<content:encoded><![CDATA[<!--[--><h3>BTRFS RAID1 Setup</h3> <p>I mentioned in my previous post that I use an external drive to keep my Steam
games on. In an attempt to not have to re-download hundreds of gigabytes of
games I keep them on a RAID1 with 2x5TB drives that I use BTRFS to manage. In
this post I’m going to outline how I set it all up, and how I manage it when
issues arise.</p> <h3>Formatting Drives</h3> <p>First of all I had to format the drives, so I made sure they were empty and
everything was backed up. Don’t do this unless you know for sure that all the
data on all your drives is backed up somewhere else, or you want to delete it
permanently.</p> <p>Okay so the commands are easy, you just have to make sure you’re using the
right ones as not to lose your data. Don’t run these commands without
understanding what they do. Replace _ with your drive letter. A lot of these
commands need to be run with root, make sure to either elevate to root (<code>sudo -s</code>)
or prepend the commands with <code>sudo</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">lsblk</span></span></code></pre><!----> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkfs.btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mkfs.btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span></code></pre><!----> <h3>Creating the RAID1 Array</h3> <p>Now you can mount the newly formatted drives to a single RAID1.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">mount</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><!----> <p>The last command might take a little bit of time, but once it’s finished your
drives will be all ready to be used.</p> <h3>Managing the Array</h3> <p>Now if you need to swap out a drive, add another, or you unplug one when it’s
writing you can simply balance again and btrfs should be able to balance back
onto the drive.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><!----> <p>You can view the status of the balance using:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> status</span></span></code></pre><!----> <p>You can remove a drive or add another using:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> remove</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> device</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/sd_1</span></span></code></pre><!----> <p>Just make sure you balance right afterwards (which will take a VERY long time
if you have lots of data, beware)</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">btrfs</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> balance</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> start</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -dconvert=raid1</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> -mconvert=raid1</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /mnt</span></span></code></pre><!----><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/flatpak</guid>
		<title><![CDATA[Using any Linux Distribution with Flatpak]]></title>
		<description><![CDATA[Flatpak makes the host distribution almost irrelevant, down to proprietary codecs and games on musl Alpine.]]></description>
		<link>https://codebam.ca/posts/flatpak</link>
		<pubDate>Tue, 23 May 2023 13:49:32 GMT</pubDate>
		<category>linux</category><category>graphics</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>I was using Fedora Silverblue for nearly a year before I realized I had been using Flatpak’s for nearly all my software, which mind you isn’t much, but I can use Firefox, Chrome, and play any video game I want using Flatpak alone so it makes sense for me.</p> <p>To get this working all you need is a distribution where you can install Flatpak. I’m using Alpine Linux edge with musl and even that works. Using Flatpak allows me to use proprietary video and audio codecs in Firefox and Chrome since musl doesn’t support those codecs as of right now.</p> <h3>Setting up Flatpak</h3> <p>If your distribution doesn’t already come with FlatHub like Alpine, you’ll need to <a href="https://flatpak.org/setup/" rel="nofollow noopener noreferrer" target="_blank">add it</a>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> remote-add</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> --if-not-exists</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> flathub</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> https://flathub.org/repo/flathub.flatpakrepo</span></span></code></pre><!----> <h3>Installing Applications</h3> <p>This will allow you access to the entire Flatpak library. You can search for software using:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> search</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam</span></span></code></pre><!----> <p>Then install using:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam</span></span></code></pre><!----> <p>Make sure you periodically update your Flatpak’s as well using:</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> update</span></span></code></pre><!----> <h3>Steam and Gaming</h3> <p>For Steam to work you’ll likely need to install some other Flatpak’s as well, but once they’re installed all your other gaming Flatpak’s should “just work”.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.valvesoftware.Steam.CompatibilityTool.Proton</span></span></code></pre><!----> <p>You might also want to install your distribution’s <code>steam-devices</code> package if you want to use a game controller.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">sudo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apt-get</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam-devices</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D"> # ubuntu / debian</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">doas</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> apk</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> add</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> steam-devices</span><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">         # alpine</span></span></code></pre><!----> <h3>Managing Permissions with Flatseal</h3> <p>I like to give the Steam Flatpak access to my external drives so I can keep games there instead of on my hard drive. To do this I simply install Flatseal and use Flatseal to give rw (read+write) access to the directory where my drives mount to.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.github.tchx84.Flatseal</span></span></code></pre><!----> <h3>Google Chrome</h3> <p>Next if you want to install Google Chrome.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">flatpak</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> install</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> com.google.Chrome</span></span></code></pre><!----> <h3>Conclusion</h3> <p>That’s all you have to do, then you would have Google Chrome installed.</p> <p>Flatpak uses it’s own runtime, remember to update all your Flatpak’s periodically with <code>flatpak update</code> to avoid security vulnerabilities and other issues.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/8814au</guid>
		<title><![CDATA[Fedora 8814au Kernel Module Compilation]]></title>
		<description><![CDATA[Compiling the out-of-tree 8814au driver for an Alfa AC1900 on Fedora, kernel headers and all.]]></description>
		<link>https://codebam.ca/posts/8814au</link>
		<pubDate>Thu, 12 May 2022 19:21:37 GMT</pubDate>
		<category>linux</category><category>kernel</category><category>development</category>
		<content:encoded><![CDATA[<!--[--><p>For my wireless card, the Alfa AC1900, I had to compile the module to get it to
work on Linux since it wasn’t already part of the kernel.</p> <h3>Prerequisites</h3> <p>To do this you will need:</p> <ol><li><code>make</code> and <code>gcc</code> or <code>clang</code></li> <li><code>kernel-debug-devel</code> package</li> <li>sudo permission to <code>insmod</code> the compiled module</li></ol> <p>This<a href="https://github.com/morrownr/8814au" rel="nofollow noopener noreferrer" target="_blank">^1</a> is the only one I found that worked for me on the latest stable linux
kernel, which as of today on Fedora is 5.17.6.</p> <h3>Kernel Header Installation</h3> <p>First you need to install the kernel development header package.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span># Fedora</span></span>
<span class="line"><span>sudo dnf install kernel-debug-devel</span></span>
<span class="line"><span></span></span>
<span class="line"><span># Ubuntu</span></span>
<span class="line"><span>sudo apt install linux-headers-$(uname -r)</span></span></code></pre><!----> <p><a href="https://www.tecmint.com/install-kernel-headers-in-ubuntu-and-debian" rel="nofollow noopener noreferrer" target="_blank">^2</a></p> <h3>Building and Loading the Module</h3> <p>Next you need to make the module.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git clone https://github.com/morrownr/8814au.git</span></span>
<span class="line"><span>cd 8814au</span></span></code></pre><!----> <p>Now simply <code>make</code> the module using the <code>make</code> command and use <code>insmod</code> to load
it.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>make -j16</span></span>
<span class="line"><span>sudo sh -c "modprobe cfg80211; insmod /home/codebam/8814au/8814au.ko"</span></span></code></pre><!----> <p>If everything worked successfully, you should now have a loaded 8814au module
that you can use along with your wireless card to connect to WiFi.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/x11docker</guid>
		<title><![CDATA[Using X11Docker for secure GUI applications]]></title>
		<description><![CDATA[Running GUI applications like telegram-desktop inside a container with x11docker, xpra and Wayland.]]></description>
		<link>https://codebam.ca/posts/x11docker</link>
		<pubDate>Thu, 07 Oct 2021 20:51:51 GMT</pubDate>
		<category>docker</category><category>podman</category><category>graphics</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>X11Docker is a nice command line tool that allows you to run graphical
applications inside podman or docker by passing them to xpra or your wayland
socket. To do this you first need a <code>Containerfile</code> or <code>Dockerfile</code> to build an
image with the application you want to run. In this example I’m going to run <code>telegram-desktop</code>.</p> <h3>Building a Container Image</h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>FROM ubuntu:latest</span></span>
<span class="line"><span></span></span>
<span class="line"><span>RUN apt-get update</span></span>
<span class="line"><span>RUN apt-get install -y telegram-desktop</span></span>
<span class="line"><span></span></span>
<span class="line"><span>CMD ["telegram-desktop"]</span></span></code></pre><!----> <h3>Running the Application with X11Docker</h3> <p>We build this image with <code>podman build . -t telegram</code> and wait for it to
install our packages. When it’s done we can simply run it with <code>x11docker localhost/telegram</code> and it starts Telegram in Xpra.</p> <h3>Running with Wayland</h3> <p>If we wanted to start it under Wayland instead, we could run <code>x11docker --wayland localhost/telegram</code> however the package for Telegram on Ubuntu
doesn’t seem to have support for Wayland when I tried it.</p> <p>This should work for any graphical software, even that which isn’t in the
repositories. You can even install more than one piece of software in a single
image. Then you pass the CMD as an argument to x11docker like so <code>x11docker localhost/telegram telegram-desktop</code>.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/podman-systemd</guid>
		<title><![CDATA[Podman Auto Updating Systemd Services]]></title>
		<description><![CDATA[podman generate systemd plus an autoupdate label: containers that start on boot and update themselves.]]></description>
		<link>https://codebam.ca/posts/podman-systemd</link>
		<pubDate>Thu, 23 Sep 2021 14:34:43 GMT</pubDate>
		<category>linux</category><category>docker</category><category>podman</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Podman<a href="https://podman.io" rel="nofollow noopener noreferrer" target="_blank">^1</a> has a nice feature called <code>generate</code> that lets you generate
different kinds of configuration files for starting pods. One that I use often
is <code>podman generate systemd</code> which generates a new user service that can be
started with <code>systemctl --user start container-yourcontainer.service</code> or
enabled.</p> <h3>Auto-Updating Containers</h3> <p>If you want your container to auto update you can simply pass it the flag <code>--label "io.containers.autoupdate=registry"</code> when you’re creating it. The
systemd service will automatically pull the latest versions of containers
before starting them. You can manually update your container images now using <code>podman auto-update</code>.</p> <p>If you want to update your images on a schedule you can enable the <code>podman-auto-update.timer</code> using <code>systemctl --user enable --now podman-auto-update.timer</code> which will auto update your containers every Monday
morning.<a href="https://fedoramagazine.org/auto-updating-podman-containers-with-systemd/" rel="nofollow noopener noreferrer" target="_blank">^2</a></p> <h3>Kubernetes YAML Generation</h3> <p>You can also generate a Kubernetes YAML configurations using <code>podman generate kube</code>. This can then be loaded in Kubernetes to run your containers.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/podman-crun</guid>
		<title><![CDATA[Podman crun and cgroups v2]]></title>
		<description><![CDATA[Getting Podman onto cgroups v2 and crun on distributions that still default to cgroups v1 and runc.]]></description>
		<link>https://codebam.ca/posts/podman-crun</link>
		<pubDate>Sun, 19 Sep 2021 03:09:02 GMT</pubDate>
		<category>linux</category><category>docker</category><category>podman</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Podman<a href="https://podman.io" rel="nofollow noopener noreferrer" target="_blank">^1</a> is a container management software similar to Docker that can run
OCI containers as regular users (not root) by using container management
libraries such as crun or runc, and cgroups v1 or v2. It can be used as a
drop-in replacement for Docker and supports docker-compose using
podman-compose.</p> <p>Unfortunately for some distributions after installing podman the operating
system defaults to cgroups v1 and runc, not cgroups v2 and crun. As was the
case on Rocky Linux for me.</p> <h3>Switching to crun</h3> <p>First we can switch to crun instead of runc by installing crun with our package
manager. It was already installed for me, but on Fedora you would use <code>sudo dnf install crun</code></p> <p>Then you can edit the file <code>/usr/share/containers/containers.conf</code> and change
the line <code>runtime = "runc"</code> to <code>runtime = "crun"</code>. Now you can verify that
you’re using crun with <code>podman info | grep crun</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>name: crun</span></span>
<span class="line"><span>package: crun-0.20.1-1.module+el8.4.0+643+525e162a.x86_64</span></span>
<span class="line"><span>path: /usr/bin/crun</span></span>
<span class="line"><span>  crun version 0.20.1</span></span></code></pre><!----> <h3>Enabling cgroups v2</h3> <p>Now the harder part was enabling cgroups v2. Luckily I found a <a href="https://github.com/containers/podman/issues/9410#issuecomment-785840320" rel="nofollow noopener noreferrer" target="_blank">comment</a> on a GitHub issue outlining the steps you need to complete to get cgroups v2 to
work.</p> <p>First we can enable it in the kernel with</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"</span></span></code></pre><!----> <p>Then we can enable linger. This will also allow our containers to remain
running after logging out.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo loginctl enable-linger codebam</span></span></code></pre><!----> <p>Then the last step which made everything work for me is to set the <code>pid_limit=0</code>. For me it was in <code>/usr/share/containers/containers.conf</code> instead
of <code>/etc/containers/</code>.</p> <h3>Verification</h3> <p>Now you can reboot. <code>podman info | grep cgroup</code> should say</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>cgroupControllers: []</span></span>
<span class="line"><span>cgroupManager: systemd</span></span>
<span class="line"><span>cgroupVersion: v2</span></span></code></pre><!----> <p>Now we’re using cgroups v2 and crun.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/react-state</guid>
		<title><![CDATA[Managing State in React]]></title>
		<description><![CDATA[useState, what triggers a re-render, and where React state should actually live.]]></description>
		<link>https://codebam.ca/posts/react-state</link>
		<pubDate>Sat, 04 Sep 2021 20:31:25 GMT</pubDate>
		<category>programming</category><category>javascript</category><category>react</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction to useState</h3> <p>In React when we want to manage individual state there is a function we can
import called <code>useState</code> that gives us a getter and setter letting React know
when it needs to re-render.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> React, &#123; useState &#125; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> 'react'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> myComponent</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  const</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">setCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  ...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <p>This initializes count as a state variable that can be accessed with <code>count</code> and set with <code>setCount</code>.</p> <h3>Incrementing a Counter</h3> <p>If we wanted to write a function that incremented count, we could add a button.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Count </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">is</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;&#x3C;/</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">      &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button onClick</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;() => </span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">setCount</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">count</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> + 1)&#125;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">>+</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">button</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">    &#x3C;/></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">);</span></span></code></pre><!----> <p>Here we wrapped our text and button in a <a href="https://reactjs.org/docs/fragments.html" rel="nofollow noopener noreferrer" target="_blank">React
Fragment</a>. Since components can only
return one DOM parent element, Fragments allow us to return more than one
without creating more divs in our markup.</p> <p>Then I set the <code>onClick</code> for button to an inline function that increases count
by 1 using <code>setCount</code>. If we were to do this without a state variable, i.e. <code>const count = 0</code> React wouldn’t know when to re-render our data.</p> <h3>Managing List State</h3> <p>This can be used to hold any state or object however, not just numbers and
strings. If we wanted to instead append to a list we might write a function
that gets passed to the <code>onSubmit</code> of a form.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[list, setList] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">([]);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> onFormSubmit</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(event) &#123;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  event.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">preventDefault</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">  list.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">push</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(form.name);</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  setList</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(list);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <h3>Handling Form Input</h3> <p>Changing all our input <code>onChange</code> methods as well to update the state of <code>form</code> when the data changes.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">label</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">p className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text-label"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">:&#x3C;/</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  &#x3C;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">input</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    onChange</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#123;handleSetForm&#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    type</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"text"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    name</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"name"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">  /></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">&#x3C;/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">label</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">></span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span></code></pre><!----> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[form, setForm] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(&#123; name: </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">''</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125;);</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">const</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> handleSetForm</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> =</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (&#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">target</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: &#123; </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">name</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">value</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#125; &#125;</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">  setForm</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">((</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">form</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=></span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (&#123; </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">...</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">form, [name]: value &#125;));</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;;</span></span></code></pre><!----> <p>This updates our form state whenever the data changes. Allowing us to use <code>form</code> to append to our list above whenever the submit button is clicked.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/react-card</guid>
		<title><![CDATA[React.js Card]]></title>
		<description><![CDATA[Building a reusable, nestable card component while learning how React composition works.]]></description>
		<link>https://codebam.ca/posts/react-card</link>
		<pubDate>Thu, 18 Feb 2021 00:27:04 GMT</pubDate>
		<category>programming</category><category>javascript</category><category>css</category><category>react</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>I’ve been learning React so that I can build my web applications for others and
myself. React is component based. You create reusable components and render
them with data. Here I created a card component that lets me insert cards. They
can even contain other cards and are easy to understand from the App.js.</p> <h3>Creating the Card Component</h3> <p>To create a new component I just create a new file called <code>Card.js</code> and import
it at the top of <code>App.js</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Card </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">from</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './Card.js'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span></code></pre><!----> <p>Then inside <code>Card.js</code> I can create my Card. At the top I import my styles and
return the JSX Card object.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">import</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> './Card.css'</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">function</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70">props</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">) &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">	return</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Card"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">h2</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CardTitle"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#123;props.cardTitle&#125;&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">h2</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">			&#x3C;</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> className</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"CardContent"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>&#123;props.children&#125;&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">p</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">		&#x3C;/</span><span style="--shiki-light:#22863A;--shiki-dark:#85E89D">div</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">	);</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> default</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> Card;</span></span></code></pre><!----> <p>At the bottom I set the default export to Card so that we can import it
properly in <code>App.js</code>.</p> <h3>Styling the Card</h3> <p>My styles are fairly simple, they just create a nice looking card.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	text-align</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">center</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	background-color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">lightgrey</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">px</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> solid</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF"> grey</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	margin</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	height</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">20</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	overflow</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">hidden</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.CardTitle</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	font-size</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">.CardContent</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	background-color</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">white</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	height</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">100</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">%</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0.5</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-top-left-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	border-top-right-radius</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">0</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">	padding</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">: </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">1</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">em</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <h3>Using the Card Component</h3> <p>That’s great but we still have to insert our card in App.js. Now that we’ve
created a component and imported it all I have to do in App.js to create a card
is this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">Card</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> cardTitle</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Example Card Title"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">>Hello World&#x3C;/</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">Card</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre><!----> <h3>Conclusion</h3> <p>Finally we can see that this builds a card.</p> <p><img src="/img/reactjs-card.webp" alt="react card"/></p> <p>This can be used for building any kind of component, such as dialog boxes,
forms, or anything you can imagine in HTML/CSS.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/silverblue</guid>
		<title><![CDATA[Fedora Silverblue]]></title>
		<description><![CDATA[Why an atomic rpm-ostree desktop stuck: updates that check out like git, and roll back the same way.]]></description>
		<link>https://codebam.ca/posts/silverblue</link>
		<pubDate>Sat, 13 Feb 2021 20:21:43 GMT</pubDate>
		<category>linux</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Over the past few months I experimented with some new Linux distributions. Here
I’ll show you the one I’m currently using and why I think it’s great.</p> <p><img src="/img/20210213_15h31m29s_grim.webp" alt="Fedora Silverblue desktop screenshot"/></p> <h3>Atomic Updates and Rollbacks</h3> <p>Fedora Silverblue is what’s known as an Atomic operating system. On Fedora
Silverblue every update is checked out similar to how git checks out a new
reference. If you want to install packages into the base image you can use
rpm-ostree install like you usually would, the only difference is that you need
to reboot to boot into your new base system reference.</p> <h3>Toolbox for Development</h3> <p>If you don’t want to reboot there is a better way though. Instead of layering
packages into your base image you can use a tool called toolbox which is simply
a wrapper around podman for creating containers with the current Fedora image.
You can use toolbox enter and install and run any software as well as access
files inside your home directory.</p> <h3>Rebasing and Customization</h3> <p>If at some point you do updates and your system doesn’t boot, you don’t have to
worry. You can simply reboot and choose the last working base image and your
system will boot up as normal, allowing you to fix any breakage.</p> <p>Silverblue is so much more powerful than that though. You can rebase your
entire system on different base images or Fedora versions. Now instead of
installing your new desktop environment and risk breaking your operating system
you can do an <code>rpm-ostree rebase</code> and rebase on another desktop environment. I
used this to rebase on an image from <a href="https://fcos.siosm.fr/kinoite/refs/heads/fedora/33/x86_64/" rel="nofollow noopener noreferrer" target="_blank">Kinoite</a> called
base which just includes all the base packages and no desktop environment. Then
I installed my window manager of choice and it’s dependencies. Now I have a
fully modular Fedora installation along with <a href="https://swaywm.org/" rel="nofollow noopener noreferrer" target="_blank">Sway</a> and
all the dependencies I need to use it.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/alpine-lbu</guid>
		<title><![CDATA[Alpine Linux LBU]]></title>
		<description><![CDATA[Alpine can commit changes back onto its own live ISO. Using LBU to build a persistent maintenance drive.]]></description>
		<link>https://codebam.ca/posts/alpine-lbu</link>
		<pubDate>Fri, 04 Dec 2020 03:09:46 GMT</pubDate>
		<category>linux</category><category>alpine</category>
		<content:encoded><![CDATA[<!--[--><h3>What is LBU?</h3> <p>Alpine Linux has a cool feature called LBU that lets you create a backup on top
of the Live ISO. These backups are called local backups.</p> <p>I plan on using LBU as a system maintenance drive. It allows you to save
changes you make to the Live ISO and restore them automatically when you boot
the next time.</p> <h3>Setup and Committing</h3> <p>To set up LBU I booted the Alpine Linux ISO, ran <code>setup-alpine</code>, mounted my
drive to <code>/media/alpine_usb</code> and when I got to the disk selection I selected no
disk, and <code>alpine_usb</code> as where to store the configs, and the default for
cache.</p> <p>Now when I make changes in the Alpine Linux Live ISO I can commit them.</p> <p>To see what is being committed I first check <code>lbu status</code> which lists all the
files being added or removed, then <code>lbu commit</code> to commit the changes.</p> <p>When I was rebooting I had an issue where the drive would automount itself to
/media/sdb and then wouldn’t work with lbu when I wanted to commit or check the
status because it was already mounted. To fix this I just had to <code>umount /dev/sdb</code> on boot and then lbu worked as expected.</p> <h3>Caching and Configuration</h3> <p>To use cache you have to mount it to <code>/media/alpine_usb</code> so that it can write
the cache files when you’re installing packages. I want this because it makes
reinstalling my packages on reboot a lot faster, they can just be loaded from
cache instead of from online repositories.</p> <p>Upon reboot I had to reinstall all the packages I had installed using <code>apk upgrade</code> but since the cache is there it just installs everything really
quickly.</p> <p>I uncommented and changed the number of backups in <code>/etc/lbu/lbu.conf</code> to 20 so
that I would be able to revert up to 20 commits in case I broke my configs.</p> <p>I see Alpine LBU as a good way to keep a backup of changes to the ISO so it
could be used as a system recovery disk with the tools that are needed.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/2fa-everything</guid>
		<title><![CDATA[2FA Everything]]></title>
		<description><![CDATA[Moving a password store between pass and KeePass, and getting two YubiKeys to guard all of it.]]></description>
		<link>https://codebam.ca/posts/2fa-everything</link>
		<pubDate>Fri, 04 Dec 2020 01:38:10 GMT</pubDate>
		<category>security</category><category>linux</category>
		<content:encoded><![CDATA[<!--[--><h3>Password Management</h3> <p>I’ll start this post by saying one thing. Are you SURE you want to wipe that
yubikey? I spent the last 2 weeks redoing my entire password manager. Silly me
forgot that I had my passwords encrypted using the yubikey I wiped to create
new PGP keys.</p> <p>Anyways over the past week or two I moved all my passwords from <a href="https://www.passwordstore.org/" rel="nofollow noopener noreferrer" target="_blank">pass</a> into <a href="https://keepass.info/" rel="nofollow noopener noreferrer" target="_blank">keepass</a> and then back into pass when keepass didn’t meet all my needs for a password
manager. KeePass supported using yubikeys for decryption, but not more than one
at a time. If I were to lose the key I would get permanently locked out.</p> <p>Features I want in my password manager:</p> <ul><li>2FA for password manager decryption</li> <li>Mobile and desktop apps</li> <li>Secure encryption</li></ul> <p>What I settled on was this. I bought myself a <a href="https://www.yubico.com/ca/product/yubikey-5c-nfc/" rel="nofollow noopener noreferrer" target="_blank">Yubikey 5C
NFC</a> to go alongside my
previous <a href="https://www.yubico.com/ca/product/yubikey-5c/" rel="nofollow noopener noreferrer" target="_blank">Yubikey 5C</a>. There
might be better alternatives, but this is the one I purchased. This one had NFC
and was convenient for accessing passwords on my phone and laptop.</p> <h3>Yubikey PGP Key Generation</h3> <p>To generate my PGP keys I actually generated them on the Yubikey itself, this
way the private key never touches the laptop I used to generate the key.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>$ gpg --card-edit</span></span>
<span class="line"><span>gpg/card> admin</span></span>
<span class="line"><span>gpg/card> key-attr</span></span>
<span class="line"><span>gpg/card> generate</span></span></code></pre><!----> <p>The <code>key-attr</code> command lets you choose the type and size of key you’re
generating on the device. I used RSA 4096 for my Yubikey 5C and ed25519 for the
Yubikey 5C NFC. I can’t use RSA or larger keys if I want to be able to use it
with NFC.</p> <p>I backed up my revokal certificates that were put in the <code>~/.gnupg/</code> directory
as indicated after they were generated, and I exported and backed up the public
keys, then I uploaded the keys to a keyserver.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>gpg --export --armor 0F6D5021A87F92BA > 0F6D5021A87F92BA.asc</span></span>
<span class="line"><span>gpg --send-keys 0F6D5021A87F92BA</span></span>
<span class="line"><span></span></span>
<span class="line"><span>gpg --export --armor F9BC985B3BF972C7 > F9BC985B3BF972C7.asc</span></span>
<span class="line"><span>gpg --send-keys F9BC985B3BF972C7</span></span></code></pre><!----> <p>I started putting passwords into pass using <code>pass init 0F6D5021A87F92BA F9BC985B3BF972C7</code>. That lets me encrypt to both Yubikeys at once. Now if I lose
or accidentally wipe one of the Yubikeys again the other will be able to
decrypt my passwords. I also signed the keys with each other, just to show that
both are valid if one were to be lost.</p> <p>While I did all this I thought I might as well enable 2FA using both the
Yubikeys on every account that supported it. Google actually has something
cool called Advanced Protection which allows you to set up your account with 2
yubikeys to login with, so I enabled that for my Google account.</p> <p>I downloaded the <a href="https://play.google.com/store/apps/details?id=dev.msfjarvis.aps" rel="nofollow noopener noreferrer" target="_blank">password
store</a> Android
app for my phone, along with <a href="https://www.openkeychain.org/" rel="nofollow noopener noreferrer" target="_blank">Openkeychain</a>.
This part wasn’t too hard to set up, I just searched for my key on the
keyserver that I just uploaded it to, then imported it. I went through the
setup to add a security token and imported it so that it was recognized as one
of my keys.</p> <h3>Git and Password Store Sync</h3> <p>At this point I set up my password store with git to use for syncing. I set it
up on some mirrors just in case one went down, and I backed it up to Nextcloud.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git remote add all git@seanbehan.dev:pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@seanbehan.dev:pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@git.sr.ht:~codebam/pass</span></span>
<span class="line"><span>git remote set-url --add --push all git@git.maych.in:codebam/password-store</span></span>
<span class="line"><span>git push --set-upstream all master</span></span></code></pre><!----> <p>I actually set up git on my own server here too. To do this I added a new user
named <code>git</code> and added my ssh keys to it, then inside it’s home directory I made
git repositories to push to using.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>mkdir pass</span></span>
<span class="line"><span>cd pass</span></span>
<span class="line"><span>git init --bare</span></span></code></pre><!----> <h3>Yubikey for SSH</h3> <p>I generated a new SSH key from within the password store app and used that for
authentication from my phone. On my laptop I set up gpg-agent with ssh support
so that I could use my yubikey as an SSH key as well as a PGP key.</p> <h3><code>~/.bashrc</code></h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> GPG_TTY</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"$(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">tty</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">)"</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">export</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> SSH_AUTH_SOCK</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">=</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"/run/user/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">$UID</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">/gnupg/S.gpg-agent.ssh"</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">gpg-connect-agent</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> updatestartuptty</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /bye</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> ></span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> /dev/null</span></span></code></pre><!----> <h3><code>~/.gnupg/gpg.conf</code></h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">use-agent</span></span></code></pre><!----> <p>Then I used <code>ssh-add -L</code> to show my public SSH key for each yubikey to put on
the server.</p> <p>I used pass for 2FA as well because the password store app supports that. I
just scanned the code and installed <code>pass-otp</code> on Fedora to get support for
OTPs.</p> <p>I installed the <a href="https://github.com/passff/passff" rel="nofollow noopener noreferrer" target="_blank">PassFF</a> Firefox extention to
get support for pass in my browser, and turned on autofill on my phone so that
it can autofill everywhere.</p> <p>I set up git to sign all my commits so that it shows that my commits are signed
and valid.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>git config --global user.signingkey 0F6D5021A87F92BA</span></span>
<span class="line"><span>git config --global commit.gpgsign true</span></span></code></pre><!----> <h3>System-level U2F Authentication</h3> <p>As an extra layer of security on Fedora I installed the <code>pam-u2f</code> package on
and generated U2F logins.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>sudo pamu2fcfg -uroot >> /etc/u2f_mappings</span></span></code></pre><!----> <p>I ran it once for each key, then opened the file and edit it so it looked like
this.</p> <h3>Before</h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>root:key1root:key2</span></span></code></pre><!----> <h3>After</h3> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>root:key1:key2</span></span>
<span class="line"><span>codebam:key1:key2</span></span></code></pre><!----> <p>In <code>/etc/pam.d/system-auth</code> I added a line to the very top above all the other <code>auth</code>.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span>auth	    required              pam_u2f.so authfile=/etc/u2f_mappings</span></span></code></pre><!----> <p>I added the same line to <code>/etc/pam.d/su</code> as well so that my user and root
requires a yubikey to <code>su</code>.</p> <p>This makes it so that <code>sudo</code>, <code>su</code>, and logging in through a <code>getty</code> (not in
GDM for some reason, but I haven’t fixed it) require you to tap the yubikey
before typing the password. Two factors of authentication for my computer, and
every online service that supports it, backed by hardware.</p> <p>Once I was done all this I committed my changes to my dotfiles pushed them to
git so that I could easily set the same thing up on my desktop which is also
running Fedora.</p><!--]-->]]></content:encoded>
	</item>
<item>
		<guid isPermaLink="true">https://codebam.ca/posts/websockets-rust</guid>
		<title><![CDATA[WebSockets in Rust]]></title>
		<description><![CDATA[Part one of a Rust series: a WebSocket server built on the ws crate.]]></description>
		<link>https://codebam.ca/posts/websockets-rust</link>
		<pubDate>Wed, 27 Nov 2019 02:56:22 GMT</pubDate>
		<category>rust</category><category>websocket</category><category>programming</category>
		<content:encoded><![CDATA[<!--[--><h3>Introduction</h3> <p>Today I’ll show you how you can write incredibly fast code in Rust. This is
part one of a tutorial series.</p> <p>This is an intermediate tutorial, if you are unfamiliar with Rust basics I
would suggest reading the Rust book for free at [https://doc.rust-lang.org/stable/book/].</p> <h3>Project Setup</h3> <p>Lets dive right in.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">cargo</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> new</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rust-tutorial</span></span>
<span class="line"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF">cd</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> rust-tutorial</span></span></code></pre><!----> <p>Let’s build our websocket server.</p> <p>Edit your <code>Cargo.toml</code> and add <a href="https://docs.rs/ws/" rel="nofollow noopener noreferrer" target="_blank"><code>ws</code></a> to your <code>[dependencies]</code>.</p> <p>Your <code>Cargo.toml</code> should now look something like this.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">package</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">name = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"rust-tutorial"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">version = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"0.1.0"</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">authors = [</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"Sean Behan &#x3C;codebam@riseup.net>"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">edition = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"2018"</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">[</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">dependencies</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">ws = </span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"*"</span></span></code></pre><!----> <h3>Building the WebSocket Server</h3> <p>Import our libraries.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">use</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">listen;</span></span></code></pre><!----> <p>Start a websocket listener.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">fn</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> main</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">() &#123;</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">    listen</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"127.0.0.1:5000"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">        move</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583"> |</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">:</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0"> ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Message</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">|</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8"> &#123;</span></span>
<span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D">            // handle the msg here</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">        &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">    &#125;</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">&#125;</span></span></code></pre><!----> <h3>Handling Messages</h3> <p>We’ll respond to recieved messages and just echo them back for now.</p> <p>Of course you could easily pass this data into any function and use it to
generate a response. In fact the incoming and outgoing data doesn’t even have
to be text.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">send</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">format!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF">"recieved message: &#123;&#125;"</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">, msg</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">into_text</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unwrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">()))</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">unwrap</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">out</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">close</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">(</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">ws</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">CloseCode</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583">::</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">Normal</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre><!----> <h3>Testing the Server</h3> <p>If we connect to this now using websocat, which can be installed with <code>cargo install websocat</code>, we can see the server echos back messages that we send to
it.</p> <!----><pre class="shiki shiki-themes github-light github-dark" style="--shiki-light:#24292e;--shiki-dark:#e1e4e8;--shiki-light-bg:#fff;--shiki-dark-bg:#24292e" tabindex="0"><code><span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">$</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> websocat</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> ws://127.0.0.1:5000</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">hello</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> world</span></span>
<span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0">recieved</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> message:</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> hello</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF"> world</span></span></code></pre><!----> <p>This isn’t that interesting. It’s cool that we can do all this in just 8 lines
of Rust though!</p> <h3>Next Steps</h3> <p>In the next part of this series I’ll show you how to use use <a href="https://docs.rs/tokio/" rel="nofollow noopener noreferrer" target="_blank"><code>tokio</code></a> to create an asyncronous server so we can
manage simultaneous connections. Stay tuned!</p><!--]-->]]></content:encoded>
	</item>
	</channel>
</rss>