forked from cadey/xesite
Compare commits
21 Commits
nix-flakes
...
main
Author | SHA1 | Date |
---|---|---|
|
f590fc71d1 | |
|
2e539512b7 | |
|
f51752ed3c | |
|
e825b1b904 | |
|
20aeb35890 | |
|
449ddabce1 | |
|
6b771b5503 | |
|
ea8e1e045a | |
|
3a4827c887 | |
|
fd6ac469a6 | |
|
fa2ada9747 | |
|
3a5c7adc42 | |
|
e5ee825c0a | |
|
e665412345 | |
|
828a5f277e | |
|
7c90296bf0 | |
|
1c8c3396a7 | |
|
0c0c5875e6 | |
|
7fdae76543 | |
|
1bedcb6a25 | |
|
66574582f2 |
|
@ -6,3 +6,5 @@ cw.tar
|
|||
/result
|
||||
.#*
|
||||
/target
|
||||
.patreon.json
|
||||
.direnv
|
||||
|
|
|
@ -130,9 +130,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
|||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.4.8"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9f346c92c1e9a71d14fe4aaf7c2a5d9932cc4e5e48d8fb6641524416eb79ddd"
|
||||
checksum = "5611d4977882c5af1c0f7a34d51b5d87f784f86912bb543986b014ea4995ef93"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
|
@ -142,6 +142,7 @@ dependencies = [
|
|||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
|
@ -160,9 +161,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbcda393bef9c87572779cb8ef916f12d77750b27535dd6819fa86591627a51"
|
||||
checksum = "95cd109b3e93c9541dcce5b0219dcf89169dcc58c1bebed65082808324258afb"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
|
@ -174,9 +175,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.1.5"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b6d79bc9c2975821d39c7df31ea766026beb9efe28c076a48cfd7d50f34f18"
|
||||
checksum = "ff3819ded1be91d7ee2cd9f0466aa345cc70ba0b0035ed47e3eac6427f83b81a"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
|
@ -191,9 +192,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5b2a9133b2658e684c8ea04157a8bd48dac7906a2eb884ffebfb051af123394"
|
||||
checksum = "63bcb0d395bc5dd286e61aada9fc48201eb70e232f006f9d6c330c9db2f256f5"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -1190,9 +1191,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
|||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.4.6"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833"
|
||||
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
|
@ -1514,6 +1515,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2467,9 +2469,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.9"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce"
|
||||
checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"sharded-slab",
|
||||
|
|
|
@ -9,9 +9,9 @@ repository = "https://github.com/Xe/site"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = "0.4"
|
||||
axum-macros = "0.1"
|
||||
axum-extra = "0.1"
|
||||
axum = "0.5"
|
||||
axum-macros = "0.2"
|
||||
axum-extra = "0.2"
|
||||
color-eyre = "0.6"
|
||||
chrono = "0.4"
|
||||
comrak = "0.12.1"
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
title: Compiling Code to Matter in My Living Room
|
||||
date: 2022-03-28
|
||||
tags:
|
||||
- openscad
|
||||
- 3dprinting
|
||||
---
|
||||
|
||||
In a moment of weakness, my husband and I got a 3d printer. It's mostly been sitting around and not doing much since we got it, but recently I found a great use for it: I wanted a controller stand for my Valve Index controllers and VR full body trackers.
|
||||
|
||||
After doing some digging on Thingiverse, I found [this stand](https://www.thingiverse.com/thing:4587097) that looked like it had promise. So I downloaded the model, sliced it and then sent it over to Kyubey:
|
||||
|
||||
<blockquote class="twitter-tweet"><p lang="tl" dir="ltr">Kyuubey is happy <a href="https://t.co/atTLN8MSgc">pic.twitter.com/atTLN8MSgc</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1507485129907871747?ref_src=twsrc%5Etfw">March 25, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
[Kyubey's name is a reference to <a href="https://madoka.fandom.com/wiki/Kyubey">Kyubey</a> from Puella Magi Madoka Magika</a>.](conversation://Mara/hacker)
|
||||
|
||||
Once it was done I ended up with a stand that I could feed [these cables I got from Amazon](https://www.amazon.ca/gp/product/B09LSF8XL9/) through. The tracker holes worked great, but the controller holes were just barely too small.
|
||||
|
||||
This was kinda frustrating and I almost gave up on the project, but then I remembered that [OpenSCAD](https://openscad.org) existed. OpenSCAD is a weird programming environment / 3D modeling hybrid program that I've seen used on Thingiverse. It works by letting you position platonic solids into a 3d environment, and from there you can create anything you want.
|
||||
|
||||
One of the primitives that OpenSCAD offers is a cylinder. So I wondered if I could use one of those to widen the hole in the index stand and then reprint the part with the wider hole.
|
||||
|
||||
[Wait, you're using a CAD program to fix your 3D print by modifying the model instead of using, I don't know, a drill and 5 minutes to make it fit that way?](conversation://Numa/dismay)
|
||||
|
||||
[There's no doing like overdoing!](conversation://Cadey/enby)
|
||||
|
||||
After some finangling, I managed to get the cylinders in the right place with this OpenSCAD code:
|
||||
|
||||
```scad
|
||||
//difference() {
|
||||
color("magenta") translate([0, 0, 0]) import("./assets/ValveTrackerDeckEditedByInugoro.stl");
|
||||
// bores for controller holders
|
||||
color([0, 1, 0]) translate([63, 44, 0]) cylinder(h = 55, r = 4.75);
|
||||
color([0, 1, 0]) translate([-63, 44, 0]) cylinder(h = 55, r = 4.75);
|
||||
//}
|
||||
```
|
||||
|
||||
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Some finagling required <a href="https://t.co/7T0R6x1XoP">pic.twitter.com/7T0R6x1XoP</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508566854926745614?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
And when I uncommented out the `difference()` block, it ends up looking good enough:
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/fiShvlN8QH">pic.twitter.com/fiShvlN8QH</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508567556759728141?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
So then I took a good solid look at the rest of the 3D printed part to see if I could improve on anything else before I sent it to another round of the printer. The last stand took _14 hours_ to print and used a lot of material. I want to avoid waste.
|
||||
|
||||
Something I noticed is that the front of the print where all the cables come out was a bit too thin. All 5 of the cables wouldn't fit in there (my braided cables must have been thicker than the ones that the original modeler used). So again I grabbed a few platonic solids and managed to make it work out:
|
||||
|
||||
```scad
|
||||
// widen the paths
|
||||
color("green") translate([0, -16, 1.3]) rotate([0, 0, 90]) cube([10, 57, 7.8], center = true);
|
||||
color("green") translate([0, 0, 1.7]) rotate([0, 0, 0]) cube([25, 30, 7], center = true);
|
||||
```
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/pKAVtiPfDS">pic.twitter.com/pKAVtiPfDS</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508568858650685440?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
Then I wanted to add some wedges into the underside of the part to help me get the print off the bed. Most people have a problem with bed adhesion being too little. I have too much bed adhesion. So I added some angled rectangles:
|
||||
|
||||
```scad
|
||||
// wedges to help get the print off the bed
|
||||
color([1, 1, 0]) translate([-120, 0, 0]) rotate([15, 0, 90]) cube([10, 11, 2], center = true); // right
|
||||
color([1, 1, 0]) translate([120, 0, 0]) rotate([-15, 0, 90]) cube([10, 11, 2], center = true); // left
|
||||
color([1, 1, 0]) translate([0, -85, 0]) rotate([0, 15, 90]) cube([10, 11, 2], center = true); // back
|
||||
color([1, 1, 0]) translate([60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front left
|
||||
color([1, 1, 0]) translate([-60, 56, 1]) rotate([0, -15, 90]) cube([10, 11, 2], center = true); // front right
|
||||
color([1, 1, 0]) translate([32.5, 41, 1]) rotate([0, -15, 130]) cube([10, 11, 2], center = true); // front left inner
|
||||
color([1, 1, 0]) translate([-32.5, 41, 1]) rotate([0, -15, 60]) cube([10, 11, 2], center = true); // front right inner
|
||||
```
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/XUQ9ZeYk1H">pic.twitter.com/XUQ9ZeYk1H</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508569796253827077?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
And then once I spun it around for a bit and thought it was good, I sliced it in PrusaSlicer and sent it off to Kyubey. It was going to take 14 hours, so I went off to do other things, ate dinner and then went to bed while the printer continued.
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="fr" dir="ltr">Diligent bean <a href="https://t.co/yPgnJA0ZdW">pic.twitter.com/yPgnJA0ZdW</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508397506031460352?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
Then when I woke up, Kyubey was done:
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="und" dir="ltr"><a href="https://t.co/2E1IS810EH">pic.twitter.com/2E1IS810EH</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508407046995156992?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
I was excited and chiseled the print off the bed (the wedges helped a little, but it ended up making the print look kinda weird so I don't know if I will do that again), but the hole for the middle tracker didn't fit perfectly. Everything else did though.
|
||||
|
||||
[If you want to get prints off your printer easier, see this video for the method we're starting to use: <br /><br /><iframe width="560" height="315" src="https://www.youtube.com/embed/VCCbzCvtRzU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>](conversation://Mara/hacker)
|
||||
|
||||
I looked on my desk and found that a random pen that I had sitting around for months was about the right size, so I pushed it into and out of the hole a few times and then the cables fit perfectly. I assume some plastic was in a weird state or something.
|
||||
|
||||
Then I set everything up and I had my Index controller stand:
|
||||
|
||||
<blockquote class="twitter-tweet" data-conversation="none" data-dnt="true"><p lang="en" dir="ltr">Victory! <a href="https://t.co/A3aCtQMQt5">pic.twitter.com/A3aCtQMQt5</a></p>— Xe Iaso (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1508426229464064001?ref_src=twsrc%5Etfw">March 28, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
|
||||
[I really need to get a table or something for this.](conversation://Cadey/facepalm)
|
||||
|
||||
I've uploaded my modified version to [Thingiverse](https://www.thingiverse.com/thing:5332988). If you want to see the OpenSCAD code, you can check it out on GitHub [here](https://github.com/Xe/3dstuff/blob/main/index_stand_hack.scad). I'm really liking OpenSCAD so far. It's very weird but it lets you do whatever you want by chaining together basic shapes to build up to what you want. I imagine I will be using it a lot in the future, especially once my husband's new sim racing gear comes in.
|
||||
|
||||
Having a 3D printer around is like having a very weird superpower on standby. You can compile matter in your living room, but you need a very pedantic description of what that should look like. You also can have any material you like as long as it's plastic. However when it's useful, it's a lifesaver. You can make something to fit a gap or mend something broken or even add functionality to something that lacked it. The cloud's the limit!
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: "</kubernetes>"
|
||||
title: "Goodbye Kubernetes"
|
||||
date: 2021-01-03
|
||||
---
|
||||
|
||||
|
|
|
@ -136,7 +136,10 @@ Then you can look at `flake.nix` to see what's up:
|
|||
pkgs = nixpkgsFor.${system};
|
||||
in
|
||||
{
|
||||
go-hello = pkgs.buildGoModule {
|
||||
# The default package for 'nix build'. This makes sense if the
|
||||
# flake provides only one package or there is a clear "main"
|
||||
# package.
|
||||
default = pkgs.buildGoModule {
|
||||
pname = "go-hello";
|
||||
inherit version;
|
||||
# In 'nix develop', we don't need a copy of the source tree
|
||||
|
@ -156,11 +159,6 @@ Then you can look at `flake.nix` to see what's up:
|
|||
vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo=";
|
||||
};
|
||||
});
|
||||
|
||||
# The default package for 'nix build'. This makes sense if the
|
||||
# flake provides only one package or there is a clear "main"
|
||||
# package.
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.go-hello);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -197,7 +195,6 @@ Let's take a closer look at the higher level things in the flake:
|
|||
|
||||
outputs = { self, nixpkgs }: {
|
||||
packages = { ... };
|
||||
defaultPackage = { ... };
|
||||
};
|
||||
}
|
||||
```
|
||||
|
@ -418,7 +415,7 @@ world. To use a private repo, your flake input URL should look something like
|
|||
this:
|
||||
|
||||
```
|
||||
ssh+git://git@github.com:user/repo
|
||||
git+ssh://git@github.com:user/repo
|
||||
```
|
||||
|
||||
[I'm pretty sure you could use private git repos outside of flakes, however it
|
||||
|
@ -462,7 +459,7 @@ nixosModules.bot = { config, lib, ... }: {
|
|||
Group = "mara-bot";
|
||||
Restart = "always";
|
||||
WorkingDirectory = "/var/lib/mara-bot";
|
||||
ExecStart = "${self.defaultPackage."${system}"}/bin/mara";
|
||||
ExecStart = "${self.packages."${system}".default}/bin/mara";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,6 +19,10 @@ chance of bitrotting. I will make every attempt to update it if things change,
|
|||
however flakes have been fairly consistent for a few years
|
||||
now.](conversation://Cadey/coffee)
|
||||
|
||||
[EDIT(20220327 14:13): A previous version of this article said to use
|
||||
`defaultPackage` for the default package. This is deprecated and you should use
|
||||
`packages.default` instead.](conversation://Cadey/coffee)
|
||||
|
||||
</div>
|
||||
|
||||
[What is a package? I've seen this term thrown around with phrases like "Nix is a
|
||||
|
@ -180,10 +184,10 @@ web server template by defining another package:
|
|||
```nix
|
||||
# flake.nix
|
||||
|
||||
# after defaultPackage
|
||||
packages = {
|
||||
default = ...;
|
||||
docker = let
|
||||
web = self.defaultPackage.${system};
|
||||
web = self.packages.${system}.default;
|
||||
in pkgs.dockerTools.buildLayeredImage {
|
||||
name = web.pname;
|
||||
tag = web.version;
|
||||
|
@ -373,7 +377,7 @@ the systemd unit:
|
|||
web-service = pkgs.substituteAll {
|
||||
name = "web-server.service";
|
||||
src = ./systemd/web-server.service.in;
|
||||
web = self.defaultPackage.${system};
|
||||
web = self.packages.${system}.default;
|
||||
};
|
||||
```
|
||||
|
||||
|
@ -399,7 +403,7 @@ Then you can add the bit that builds the portable service:
|
|||
|
||||
```nix
|
||||
portable = let
|
||||
web = self.defaultPackage.${system};
|
||||
web = self.packages.${system}.default;
|
||||
in pkgs.portableService {
|
||||
inherit (web) version;
|
||||
name = web.pname;
|
||||
|
|
|
@ -0,0 +1,443 @@
|
|||
---
|
||||
title: "Nix Flakes: Exposing and using NixOS Modules"
|
||||
date: 2022-04-07
|
||||
series: nix-flakes
|
||||
tags:
|
||||
- nixos
|
||||
vod:
|
||||
twitch: https://www.twitch.tv/videos/1437346416
|
||||
youtube: https://youtu.be/wCZ9SwmgSck
|
||||
---
|
||||
|
||||
Nix flakes allow you to expose NixOS modules. NixOS modules are templates for
|
||||
system configuration and they are the basis of how you configure NixOS. Today
|
||||
we're going to take our Nix flake [from the last
|
||||
article](/blog/nix-flakes-2-2022-02-27) and write a NixOS module for it so that
|
||||
we can deploy it to a container running locally. In the next post we will deploy
|
||||
this to a server.
|
||||
|
||||
[If you haven't read <a href="/blog/series/nix-flakes">the other articles in
|
||||
this series</a>, you probably should. This article builds upon the previous
|
||||
ones.](conversation://Mara/hacker)
|
||||
|
||||
NixOS modules are building blocks that let you configure NixOS servers. Modules
|
||||
expose customizable options that expand out into system configuration.
|
||||
Individually, each module is fairly standalone and self-contained, but they
|
||||
build up together into your server configuration like a bunch of legos build
|
||||
into a house. Each module describes a subset of your desired system
|
||||
configuration and any options relevant to that configuration.
|
||||
|
||||
[You can think about them like Ansible playbooks, but NixOS modules describe the
|
||||
desired end state instead of the steps you need to get to that end
|
||||
state. It's the end result of evaluating all of your options against all of the
|
||||
modules that you use in your configuration.](conversation://Mara/hacker)
|
||||
|
||||
NixOS modules are functions that take in the current state of the system and
|
||||
then return things to add to the state of the system. Here is a basic NixOS
|
||||
module that enables [nginx](https://nginx.org/):
|
||||
|
||||
```nix
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
{
|
||||
config = {
|
||||
services.nginx.enable = true;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
This function takes in the state of the world and returns additions to the state
|
||||
of the world. This will use the nginx module that ships with NixOS to give you a
|
||||
basic nginx setup that has the upstream default configuration in it.
|
||||
|
||||
NixOS has a way to run other instances of NixOS with [NixOS
|
||||
containers](https://nixos.org/manual/nixos/stable/index.html#ch-containers). We
|
||||
can use them to test our NixOS module as we write it.
|
||||
|
||||
[This probably won't work on a non-NixOS machine. You will need to
|
||||
install NixOS in order to test this. For an easy way to do this, see <a
|
||||
href="https://github.com/elitak/nixos-infect">nixos-infect</a>, a script you can
|
||||
put into a cloudconfig when spinning up a new server. You can also <a
|
||||
href="https://nixos.org/manual/nixos/stable/index.html#sec-installation">install
|
||||
NixOS manually</a> in a VM, but for now it may be better to use a cloud server
|
||||
as the path of least resistance. Installing NixOS with a flake will be a part of
|
||||
a future article in this series.](conversation://Mara/hacker)
|
||||
|
||||
In Nix you can merge two attribute sets using the `//` operator. This allows you
|
||||
to add two attribute sets into one larger one, such as like this:
|
||||
|
||||
```
|
||||
nix-repl> { foo = 1; } // { bar = 2; }
|
||||
{ bar = 2; foo = 1; }
|
||||
```
|
||||
|
||||
<xeblog-conv name="Mara" mood="hacker">
|
||||
Important pro tip: the merge operator is NOT recursive. If you try to do
|
||||
something like:
|
||||
|
||||
```
|
||||
nix-repl> foo = { bar = { baz = "foo"; }; }
|
||||
nix-repl> (foo // { bar = { spam = "eggs"; }; }).bar
|
||||
```
|
||||
|
||||
You will get:
|
||||
|
||||
```
|
||||
{ spam = "eggs"; }
|
||||
```
|
||||
|
||||
And not:
|
||||
|
||||
```
|
||||
{ baz = "foo"; spam = "eggs"; }
|
||||
```
|
||||
|
||||
This is because the `//` operator prefers things in the right hand side over the
|
||||
left hand side if both conflict. To recursively merge two attribute sets (using
|
||||
all elements from both sides), use
|
||||
[lib.recursiveUpdate](https://nixos.org/manual/nixpkgs/stable/#function-library-lib.attrsets.recursiveUpdate):
|
||||
|
||||
```
|
||||
nix-repl> (pkgs.lib.recursiveUpdate foo bar).bar
|
||||
{ baz = "foo"; spam = "eggs"; }
|
||||
```
|
||||
|
||||
</xeblog-conv>
|
||||
|
||||
We will use this to add the container configuration to the flake at the end of
|
||||
the flake.nix file. We need to do this because the upper part of the flake with
|
||||
the `forAllSystems` call will generate a bunch of system-specific attributes for
|
||||
each system we support. NixOS configurations don't support this level of
|
||||
granularity.
|
||||
|
||||
At the end of your flake.nix (just before the final closing `}`), there should
|
||||
be a line that looks like this:
|
||||
|
||||
```nix
|
||||
});
|
||||
```
|
||||
|
||||
This is what terminates the `outputs` declaration from all the way at the top.
|
||||
In order to add the container configuration, you should change this to look like
|
||||
this:
|
||||
|
||||
```nix
|
||||
}) // {
|
||||
|
||||
};
|
||||
```
|
||||
|
||||
Then we can add the container configuration to the flake:
|
||||
|
||||
```nix
|
||||
}) // {
|
||||
nixosConfigurations.container = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
({pkgs, ...}: {
|
||||
# Only allow this to boot as a container
|
||||
boot.isContainer = true;
|
||||
networking.hostName = "gohello";
|
||||
|
||||
# Allow nginx through the firewall
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
|
||||
services.nginx.enable = true;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
This will create a container (with the hostname "gohello") that starts nginx and
|
||||
allows traffic to go to nginx on TCP port 80. You can start up the container
|
||||
with the `nixos-container` command:
|
||||
|
||||
```console
|
||||
$ sudo nixos-container create gohello --flake .#container
|
||||
host IP is 10.233.1.1, container IP is 10.233.1.2
|
||||
```
|
||||
|
||||
Then you can start the container with this command:
|
||||
|
||||
```console
|
||||
$ sudo nixos-container start gohello
|
||||
```
|
||||
|
||||
And then we can try to connect to nginx to see if it's working:
|
||||
|
||||
```console
|
||||
$ curl http://10.233.1.2
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
<style>
|
||||
body {}
|
||||
width: 35em;
|
||||
margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to nginx!</h1>
|
||||
<p>If you see this page, the nginx web server is successfully installed and
|
||||
working. Further configuration is required.</p>
|
||||
|
||||
<p>For online documentation and support please refer to
|
||||
<a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
Commercial support is available at
|
||||
<a href="http://nginx.com/">nginx.com</a>.</p>
|
||||
|
||||
<p><em>Thank you for using nginx.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
We have nginx!
|
||||
|
||||
Now that we have our container to test with, let's write the configuration for
|
||||
the service. At a basic level we need the following things:
|
||||
|
||||
- A systemd unit for orchestrating the HTTP server process
|
||||
- nginx configuration to reverse proxy to that HTTP server
|
||||
|
||||
Above the container definition, add this basic NixOS module template:
|
||||
|
||||
```nix
|
||||
nixosModule = { config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
let cfg = config.xeserv.services.gohello;
|
||||
in {
|
||||
options.xeserv.services.gohello = {
|
||||
enable = mkEnableOption "Enables the gohello HTTP service";
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
This will create a NixOS module that will only be enabled when the configuration
|
||||
setting `xeserv.services.gohello.enable` is set to `true`. Everything else we do
|
||||
here will build on this.
|
||||
|
||||
[You can and probably do want to change the namespace `xeserv` here, it is a
|
||||
placeholder that is not likely to conflict with anything
|
||||
else.](conversation://Mara/happy)
|
||||
|
||||
Create a basic systemd service with this template:
|
||||
|
||||
```nix
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services."xeserv.gohello" = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = let pkg = self.packages.${system}.default;
|
||||
in {
|
||||
Restart = "on-failure";
|
||||
ExecStart = "${pkg}/bin/web-server";
|
||||
DynamicUser = "yes";
|
||||
RuntimeDirectory = "xeserv.gohello";
|
||||
RuntimeDirectoryMode = "0755";
|
||||
StateDirectory = "xeserv.gohello";
|
||||
StateDirectoryMode = "0700";
|
||||
CacheDirectory = "xeserv.gohello";
|
||||
CacheDirectoryMode = "0750";
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
<xeblog-conv name="Mara" mood="hacker">
|
||||
NOTE: If you have been following along since before this article was published,
|
||||
you will want to be sure to do the following things to your copy of gohello:
|
||||
|
||||
* Move the definition of `defaultPackage` into the `packages` attribute set with
|
||||
the name `default`
|
||||
* Update `defaultApp` and the other entries to point to
|
||||
`self.packages.${system}.default` instead of `self.defaultPackage.${system}`
|
||||
|
||||
We have updated previous articles and the template accordingly. Annoyingly it
|
||||
seems that this change is new enough that it isn't totally documented on the
|
||||
NixOS wiki. We are working on fixing this.
|
||||
|
||||
</xeblog-conv>
|
||||
|
||||
This will do the following things:
|
||||
|
||||
- Start the service on boot (`multi-user.target` fires once the system is "fully
|
||||
booted" and the network is active)
|
||||
- Automatically restarts the service when it crashes
|
||||
- Starts our `web-server` binary when running the service
|
||||
- Creates a random, unique user account for the service (see
|
||||
[here](http://0pointer.net/blog/dynamic-users-with-systemd.html) for more
|
||||
information on how/why this works)
|
||||
- Creates temporary, home and cache directories for the service, makes sure that
|
||||
random user has permission to use them (with the specified directory modes
|
||||
too)
|
||||
- Enables the service automatically
|
||||
|
||||
Then you need to add the nginx configuration. We want this application to have
|
||||
its own virtual host, so we will need to add that as a configuration option
|
||||
under the `enable` option:
|
||||
|
||||
```nix
|
||||
domain = mkOption rec {
|
||||
type = types.str;
|
||||
default = "gohello.local.cetacean.club";
|
||||
example = default;
|
||||
description = "The domain name for gohello";
|
||||
};
|
||||
```
|
||||
|
||||
[Pro tip: `anything.local.cetacean.club` points to `127.0.0.1`. You can use this
|
||||
when testing things.](conversation://Mara/happy)
|
||||
|
||||
And then we can add the nginx configuration under the systemd service definition:
|
||||
|
||||
```nix
|
||||
services.nginx.virtualHosts.${cfg.domain} = {
|
||||
locations."/" = { proxyPass = "http://127.0.0.1:3031"; };
|
||||
};
|
||||
```
|
||||
|
||||
Your module should look like this:
|
||||
|
||||
```nix
|
||||
nixosModule = { config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
let cfg = config.xeserv.services.gohello;
|
||||
in {
|
||||
options.xeserv.services.gohello = {
|
||||
enable = mkEnableOption "Enables the gohello HTTP service";
|
||||
|
||||
domain = mkOption rec {
|
||||
type = types.str;
|
||||
default = "gohello.local.cetacean.club";
|
||||
example = default;
|
||||
description = "The domain name for gohello";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
systemd.services."xeserv.gohello" = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = let pkg = self.packages.${pkgs.system}.default;
|
||||
in {
|
||||
Restart = "on-failure";
|
||||
ExecStart = "${pkg}/bin/web-server";
|
||||
DynamicUser = "yes";
|
||||
RuntimeDirectory = "xeserv.gohello";
|
||||
RuntimeDirectoryMode = "0755";
|
||||
StateDirectory = "xeserv.gohello";
|
||||
StateDirectoryMode = "0700";
|
||||
CacheDirectory = "xeserv.gohello";
|
||||
CacheDirectoryMode = "0750";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts.${cfg.domain} = {
|
||||
locations."/" = { proxyPass = "http://127.0.0.1:3031"; };
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
[The service name is overly defensive. It's intended to avoid conflicting with
|
||||
any other unit on the system named `gohello.service`. Feel free to remove this
|
||||
part, it is really just defensive devops by design to avoid name
|
||||
conflicts.](conversation://Mara/hacker)
|
||||
|
||||
Then you can add it to the container by importing our new module in its
|
||||
configuration and activating the gohello service:
|
||||
|
||||
```nix
|
||||
nixosConfigurations.container = nixpkgs.lib.nixosSystem {
|
||||
system = "x86_64-linux";
|
||||
modules = [
|
||||
self.nixosModule
|
||||
({ pkgs, ... }: {
|
||||
# Only allow this to boot as a container
|
||||
boot.isContainer = true;
|
||||
|
||||
# Allow nginx through the firewall
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
|
||||
services.nginx.enable = true;
|
||||
|
||||
xeserv.services.gohello.enable = true;
|
||||
})
|
||||
];
|
||||
};
|
||||
```
|
||||
|
||||
Then you can update the container's configuration with this command:
|
||||
|
||||
```console
|
||||
$ sudo nixos-container update gohello --flake .#container
|
||||
reloading container...
|
||||
```
|
||||
|
||||
And finally make a request to the gohello service running in that container:
|
||||
|
||||
```console
|
||||
$ curl http://10.233.1.2 -H "Host: gohello.local.cetacean.club"
|
||||
hello world :)
|
||||
```
|
||||
|
||||
<xeblog-conv name="Mara" mood="hacker">
|
||||
Exercises for the reader:
|
||||
|
||||
Try adding a [nixos
|
||||
option](https://nixos.org/manual/nixos/stable/index.html#sec-writing-modules)
|
||||
that correlates to the `--bind` flag that `gohello` uses as the TCP
|
||||
address to serve HTTP from. You will want to have the type be
|
||||
`types.port`. If you are stuck, see
|
||||
[here](https://github.com/Xe/nixos-configs/tree/master/common/services) for inspiration.
|
||||
|
||||
Also try adding `AmbientCapabilities = "CAP_NET_BIND_SERVICE"` and
|
||||
`CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"` to your `serviceConfig` and
|
||||
bind `gohello` to port 80 without nginx involved at all.
|
||||
|
||||
</xeblog-conv>
|
||||
|
||||
You can delete this container with `sudo nixos-container destroy gohello` when
|
||||
you are done with it.
|
||||
|
||||
These are the basics on how to use NixOS modules. Everything else you can do
|
||||
with them builds off of these fundamental ideas. Modules are templates that
|
||||
coordinate packages and configuration into your desired system state. Containers
|
||||
can let you test out modules without having to add them to your currently
|
||||
running system. Modules declare options and emit configuration based on those
|
||||
options.
|
||||
|
||||
You can also consume NixOS modules from flakes using the input system, however I
|
||||
will go into more details about this at a later date. If you want more examples
|
||||
of NixOS modules, I would suggest checking out my
|
||||
[nixos-configs](https://github.com/Xe/nixos-configs) repository. I have nearly
|
||||
everything neatly modularized and configurable. If you see anything in there
|
||||
that is confusing to you, please [reach out](/contact) and ask. I am happy to
|
||||
answer your questions and your feedback will help me write future posts in this
|
||||
series.
|
||||
|
||||
I also have my "next generation" flakes-based configuration experiments
|
||||
[here](https://tulpa.dev/cadey/nixos-configs) if you want to read through those.
|
||||
I have still been porting over things piecemeal, so it is not a complete replica
|
||||
of my existing configuration.
|
||||
|
||||
Next time I will cover how to install NixOS to a server and deploy system
|
||||
configurations using [deploy-rs](https://github.com/serokell/deploy-rs). This
|
||||
will allow you to have your workstation build configuration for your servers and
|
||||
push out all the changes from there.
|
||||
|
||||
---
|
||||
|
||||
Many thanks to Open Skies for being my fearless editor that helps make these
|
||||
things shine.
|
||||
|
||||
In part of this post I use my new Xeact-powered HTML component for some of the
|
||||
conversation fragments, but the sizing was off on my iPhone when I tested it. If
|
||||
you know what I am doing wrong, please [get in touch](/contact).
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
title: Stop Using Politics As A Cudgel To Discourage Experimentation
|
||||
date: 2022-04-21
|
||||
tags:
|
||||
- rant
|
||||
- systemd
|
||||
- communityhealth
|
||||
---
|
||||
|
||||
So let's say you get bored one day and you decide you want to do things that god
|
||||
and man have decreed impossible. Let's also say that this exact thing involves a
|
||||
tool that just happens to rustle all of the jimmies (for reasons that are not
|
||||
entirely clear). Then you get it all to a point where you want to submit it
|
||||
upstream so you can get help experimenting with this tool.
|
||||
|
||||
So you submit it to upstream in the experimental branch, expecting very little
|
||||
pushback so you can get help tinkering with things. But once you submit it
|
||||
upstream, [all hell breaks
|
||||
loose](https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/33329).
|
||||
|
||||
Stop using politics as a cudgel to discourage experimentation. Yes it involves
|
||||
systemd. Just because you think that the tool is overcomplicated doesn't mean
|
||||
that other people don't find it useful. Trying to shut down experimentation is
|
||||
how you get people to leave the community or give up participating in open
|
||||
source altogether.
|
||||
|
||||
The reactions in that thread are both disappointing and somewhat to be expected.
|
||||
I don't know why people have such a negative reaction to systemd. It's just an
|
||||
init system, not a religion. It wouldn't have become a good choice for so much
|
||||
of the Linux ecosystem without it having solid technical merits. If it is really
|
||||
that bad then the mantle of responsibility is on you for coming up with a better
|
||||
option.
|
||||
|
||||
[No, OpenRC is not that option. It can be PART OF an option, but it is not a
|
||||
competitor by itself.](conversation://Cadey/coffee)
|
||||
|
||||
I know I said I'd stop ranting on this blog as much, but really this stuff
|
||||
grinds my gears and I feel that I should use my platform for good in this
|
||||
regard. This is inexcusable. I want to reiterate that I have _no_ power in this
|
||||
regard. I am just some random person on a blog that got frustrated at the
|
||||
reactions to this contribution. Some pushback is acceptable. Accusing a
|
||||
contributor of ignorance is inexcusable. Comments like this have no place in
|
||||
open source contributions:
|
||||
|
||||
> SysTemD is the STD of operating systems. There is no "one little poke", you
|
||||
> can't be a little bit pregnant.
|
||||
|
||||
Jake, if you're out there reading this: keep doing this thing. It is a fantastic
|
||||
creation that I thought was impossible. You may have to soft-fork the
|
||||
distribution to get this to work reliably, but I really want to see where this
|
||||
rabbit-hole goes.
|
||||
|
||||
Keep hacking.
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
title: "What To Do As A Recruiter When A Gender-diverse Person Asks You To Update Their Name"
|
||||
date: 2022-04-01
|
||||
---
|
||||
|
||||
[I really wish this was an April Fool's post. I had a few ideas planned, but
|
||||
maybe you will get to see them next year.<br /><br />As a reminder, I am
|
||||
speaking for myself and not for my employer.](conversation://Cadey/coffee)
|
||||
|
||||
This post is directed at all of the recruiters that are reading this blog. This
|
||||
is a scenario that many of you may not have dealt with. After having an example
|
||||
of this with a recruiter recently I figure it's a teaching moment.
|
||||
|
||||
I am speaking up about this because I know many others who have gone through the
|
||||
same kinds of problems and have not felt safe to speak up about them. I am not
|
||||
speaking for those people in this post, but I want to use my platform as a
|
||||
blogger to amplify the sentiment of what I have heard over the years.
|
||||
|
||||
## To Recruiters
|
||||
|
||||
As a recruiter, if you are cold-emailing someone, please do the research to get
|
||||
their name correct. If you do not and someone asks you to correct it, do it.
|
||||
|
||||
When gender-diverse people like me get an email that references an out of date
|
||||
name, it is seen as a sign that the person sending that email has not done their
|
||||
research before sending that email out into the void.
|
||||
|
||||
When you correct that name in your system also make sure to cancel all outgoing
|
||||
automated emails to that person. The caching layer of the recruiting system may
|
||||
have already drafted those emails based on a template. If they go out, this will
|
||||
be seen as a _massive sign of disrespect_. It will also make the person
|
||||
receiving that email question if you _actually corrected_ the name in that
|
||||
system or not. It may make the recipient also question if you are just giving
|
||||
them lip service to save face instead of making a genuine effort to ensure that
|
||||
the recruiting system has accurate information in it.
|
||||
|
||||
This is not a good way to foster the kind of trust needed for a gender-diverse
|
||||
person to want to choose your employer as the single point of failure for access
|
||||
to medication, food and regular medical checkups. For many gender-diverse
|
||||
people, changing jobs can mean an interruption of access to life-saving
|
||||
medication.
|
||||
|
||||
You may get a slightly angry reply if you send out emails with incorrect
|
||||
information. This can happen because gender-diverse people are likely to feel
|
||||
like society really doesn't care about them and that they are not being
|
||||
respected to have agency over their identity. To some this is a fact and not
|
||||
a feeling. And with
|
||||
[all](https://www.theguardian.com/us-news/2022/mar/10/idaho-bill-trans-youth-treatment-ban-passes-house)
|
||||
[of](https://www.washingtonpost.com/dc-md-va/2022/03/17/texas-trans-child-abuse-investigations/)
|
||||
[the](https://www.nbcnews.com/nbc-out/out-politics-and-policy/alabama-bill-seeks-ban-hormone-treatments-trans-youth-rcna18512)
|
||||
[actions](https://www.hrc.org/press-releases/breaking-2021-becomes-record-year-for-anti-transgender-legislation)
|
||||
governments have been taking to directly attack the freedoms and rights to
|
||||
self-determination that gender-diverse people like me rely on, you can't blame
|
||||
them for being fed up with the situation. It is not fun to feel like your very
|
||||
existence is made out to be some black mark of doom on Western civilization. It
|
||||
is even less fun to be reminded of that when reading your email inbox. Please
|
||||
understand that we mean well, society is just broken in general.
|
||||
|
||||
The least you can do is ensure that you do _any amount of research_ to ensure
|
||||
that you are using the correct name. It may be a good idea to add the following
|
||||
text to your recruiting emails (before you brag about fundraising is probably
|
||||
best, I tune out about then):
|
||||
|
||||
> If I got your name incorrect, please let me know what name/pronouns you would
|
||||
> like me to update our system to use. I got this name from $SOURCE.
|
||||
|
||||
Adding the source of where you got that name from can help make this less
|
||||
stressful for gender-diverse people. People's names are spattered everywhere
|
||||
across the internet. Letting people know where you got that information from can
|
||||
help them know what to fix if a fix is needed.
|
||||
|
||||
Some chosen names may seem "weird" due to societal biases that serve to ensure
|
||||
that the primary way that people use to refer to eachother in particular are not
|
||||
chosen by the people being referred to. Trust that the person on the other end
|
||||
is being honest about their identity. The truth requires no belief.
|
||||
|
||||
If they ask you to update their pronouns, respect that and ensure you use them
|
||||
without failure. Using the wrong pronouns can be seen as an even worse
|
||||
disrespect than using the wrong name. You do not want this to happen if your
|
||||
goal is to find people to hire.
|
||||
|
||||
## To Gender-diverse People
|
||||
|
||||
Yeah, this situation sucks. I can't disagree. You really do need to assume good
|
||||
faith as much as you can. Most of these recruiter systems rely on ["data
|
||||
enrichment" APIs](https://clearbit.com/) and potentially outdated mass scraping
|
||||
of LinkedIn and people's blogs.
|
||||
|
||||
It can help if you make publicly available posts like
|
||||
[this](/blog/xe-2021-08-07) that unambiguously say what you want people to call
|
||||
you by. Keep it updated in case journalists decide to compare your chosen name
|
||||
to mercenary groups.
|
||||
|
||||
Try to be as polite and direct as possible. Here is an example of how I have
|
||||
asked recruiters to update their information in the past:
|
||||
|
||||
> Please update your files with the name "Xe Iaso" (capital I). I am
|
||||
> slowly moving away from "Christine Dodrill" as the name I use to
|
||||
> represent myself professionally.
|
||||
|
||||
If you are moving away from a "dead name", you may want to use something like
|
||||
this:
|
||||
|
||||
> I have no record of a "Christine Dodrill" at this email address. You may want
|
||||
> to look elsewhere. If you would like to proceed with me instead, here is
|
||||
> information about me: https://christine.website.
|
||||
|
||||
Throw in your pronouns too to be safe.
|
||||
|
||||
[I really need to change this blog's domain, but I have such amazing SEO that I
|
||||
really don't want to break it.](conversation://Cadey/coffee)
|
||||
|
||||
Also consider deleting the email and not replying to them. That's totally valid
|
||||
too unless you are in desperate need for a new employer.
|
||||
|
||||
You do not need to justify speaking up about an employer having the wrong name
|
||||
for you. The truth requires no belief. Speaking the truth to power is the
|
||||
essence of valor, which is one of the highest forms of love.
|
13
config.dhall
13
config.dhall
|
@ -1,7 +1,16 @@
|
|||
let Person =
|
||||
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
|
||||
{ Type =
|
||||
{ name : Text
|
||||
, tags : List Text
|
||||
, gitLink : Optional Text
|
||||
, twitter : Optional Text
|
||||
}
|
||||
, default =
|
||||
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
|
||||
{ name = ""
|
||||
, tags = [] : List Text
|
||||
, gitLink = None Text
|
||||
, twitter = None Text
|
||||
}
|
||||
}
|
||||
|
||||
let Author =
|
||||
|
|
|
@ -36,6 +36,7 @@ img {
|
|||
|
||||
.conversation-chat {
|
||||
align-self: center;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.gruvbox-dark pre, pre {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1649676176,
|
||||
"narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1650265945,
|
||||
"narHash": "sha256-SO8+1db4jTOjnwP++29vVgImLIfETSXyoz0FuLkiikE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "e8f9f8d037774becd82fce2781e1abdb7836d7df",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1650647760,
|
||||
"narHash": "sha256-Ng8CGYLSTxeI+oEux0x+tSRA6K7ydoyfJNQf56ld+Uo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b80f570a92d04e8ace67ff09c34aa48708a5c88c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1650161686,
|
||||
"narHash": "sha256-70ZWAlOQ9nAZ08OU6WY7n4Ij2kOO199dLfNlvO/+pf8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1ffba9f2f683063c2b14c9f4d12c55ad5f4ed887",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"ref": "nixos-unstable",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
{
|
||||
description = "A very basic flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils, naersk }:
|
||||
flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
naersk-lib = naersk.lib."${system}";
|
||||
src = ./.;
|
||||
in rec {
|
||||
packages = rec {
|
||||
bin = naersk-lib.buildPackage {
|
||||
pname = "xesite-bin";
|
||||
root = src;
|
||||
buildInputs = with pkgs; [ pkg-config openssl git ];
|
||||
};
|
||||
|
||||
config = pkgs.stdenv.mkDerivation {
|
||||
pname = "xesite-config";
|
||||
inherit (bin) version;
|
||||
inherit src;
|
||||
buildInputs = with pkgs; [ dhall ];
|
||||
|
||||
phases = "installPhase";
|
||||
|
||||
installPhase = ''
|
||||
cd $src
|
||||
mkdir -p $out
|
||||
dhall resolve < $src/config.dhall >> $out/config.dhall
|
||||
'';
|
||||
};
|
||||
|
||||
static = pkgs.stdenv.mkDerivation {
|
||||
pname = "xesite-static";
|
||||
inherit (bin) version;
|
||||
inherit src;
|
||||
|
||||
phases = "installPhase";
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -vrf $src/static $out
|
||||
cp -vrf $src/css $out
|
||||
'';
|
||||
};
|
||||
|
||||
posts = pkgs.stdenv.mkDerivation {
|
||||
pname = "xesite-posts";
|
||||
inherit (bin) version;
|
||||
inherit src;
|
||||
|
||||
phases = "installPhase";
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -vrf $src/blog $out
|
||||
cp -vrf $src/gallery $out
|
||||
cp -vrf $src/talks $out
|
||||
'';
|
||||
};
|
||||
|
||||
default = pkgs.symlinkJoin {
|
||||
name = "xesite-${bin.version}";
|
||||
paths = [ config posts static bin ];
|
||||
};
|
||||
};
|
||||
|
||||
devShell = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
# Rust
|
||||
rustc
|
||||
cargo
|
||||
rust-analyzer
|
||||
cargo-watch
|
||||
|
||||
# system dependencies
|
||||
openssl
|
||||
pkg-config
|
||||
|
||||
# kubernetes deployment
|
||||
dhall
|
||||
dhall-json
|
||||
|
||||
# dependency manager
|
||||
niv
|
||||
|
||||
# tools
|
||||
ispell
|
||||
];
|
||||
|
||||
SITE_PREFIX = "devel.";
|
||||
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
|
||||
RUST_LOG = "debug";
|
||||
RUST_BACKTRACE = "1";
|
||||
GITHUB_SHA = "devel";
|
||||
};
|
||||
|
||||
nixosModules.bot = { config, lib, ... }:
|
||||
with lib;
|
||||
let cfg = config.xeserv.services.xesite;
|
||||
in {
|
||||
options.within.services.xesite = {
|
||||
enable = mkEnableOption "Activates my personal website";
|
||||
useACME = mkEnableOption "Enables ACME for cert stuff";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 32837;
|
||||
example = 9001;
|
||||
description =
|
||||
"The port number xesite should listen on for HTTP traffic";
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.str;
|
||||
default = "xesite.akua";
|
||||
example = "christine.website";
|
||||
description =
|
||||
"The domain name that nginx should check against for HTTP hostnames";
|
||||
};
|
||||
|
||||
sockPath = mkOption rec {
|
||||
type = types.str;
|
||||
default = "/srv/within/run/xesite.sock";
|
||||
example = default;
|
||||
description =
|
||||
"The unix domain socket that xesite should listen on";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
users.users.xesite = {
|
||||
createHome = true;
|
||||
description = "github.com/Xe/site";
|
||||
isSystemUser = true;
|
||||
group = "within";
|
||||
home = "/srv/within/xesite";
|
||||
extraGroups = [ "keys" ];
|
||||
};
|
||||
|
||||
systemd.services.xesite = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
|
||||
serviceConfig = {
|
||||
User = "xesite";
|
||||
Group = "within";
|
||||
Restart = "on-failure";
|
||||
WorkingDirectory = "/srv/within/xesite";
|
||||
RestartSec = "30s";
|
||||
Type = "notify";
|
||||
|
||||
# Security
|
||||
CapabilityBoundingSet = "";
|
||||
DeviceAllow = [ ];
|
||||
NoNewPrivileges = "true";
|
||||
ProtectControlGroups = "true";
|
||||
ProtectClock = "true";
|
||||
PrivateDevices = "true";
|
||||
PrivateUsers = "true";
|
||||
ProtectHome = "true";
|
||||
ProtectHostname = "true";
|
||||
ProtectKernelLogs = "true";
|
||||
ProtectKernelModules = "true";
|
||||
ProtectKernelTunables = "true";
|
||||
ProtectSystem = "true";
|
||||
ProtectProc = "invisible";
|
||||
RemoveIPC = "true";
|
||||
RestrictSUIDSGID = "true";
|
||||
RestrictRealtime = "true";
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"~@reboot"
|
||||
"~@module"
|
||||
"~@mount"
|
||||
"~@swap"
|
||||
"~@resources"
|
||||
"~@cpu-emulation"
|
||||
"~@obsolete"
|
||||
"~@debug"
|
||||
"~@privileged"
|
||||
];
|
||||
UMask = "007";
|
||||
};
|
||||
|
||||
script = let site = packages.default;
|
||||
in ''
|
||||
export SOCKPATH=${cfg.sockPath}
|
||||
export DOMAIN=${toString cfg.domain}
|
||||
cd ${site}
|
||||
exec ${site}/bin/xesite
|
||||
'';
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."xesite" = {
|
||||
serverName = "${cfg.domain}";
|
||||
locations."/" = {
|
||||
proxyPass = "http://unix:${toString cfg.sockPath}";
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
forceSSL = cfg.useACME;
|
||||
useACMEHost = "christine.website";
|
||||
extraConfig = ''
|
||||
access_log /var/log/nginx/xesite.access.log;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
|
@ -14,6 +14,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
thiserror = "1"
|
||||
tracing = "0.1"
|
||||
tracing-futures = "0.2"
|
||||
url = "2"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use std::{fs, io, path::Path};
|
||||
|
||||
use chrono::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, error, instrument};
|
||||
use url::Url;
|
||||
|
||||
pub type Campaigns = Vec<Object<Campaign>>;
|
||||
pub type Pledges = Vec<Object<Pledge>>;
|
||||
|
@ -61,17 +64,33 @@ pub struct User {
|
|||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct RefreshGrant {
|
||||
pub access_token: String,
|
||||
pub refresh_token: String,
|
||||
pub expires_in: serde_json::Value,
|
||||
pub scope: serde_json::Value,
|
||||
pub token_type: String,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("json error: {0:?}")]
|
||||
#[error("json error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
#[error("request error: {0:?}")]
|
||||
|
||||
#[error("request error: {0}")]
|
||||
Request(#[from] reqwest::Error),
|
||||
|
||||
#[error("{0}")]
|
||||
IO(#[from] io::Error),
|
||||
|
||||
#[error("url parse error: {0}")]
|
||||
URLParse(#[from] url::ParseError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default, Eq, PartialEq)]
|
||||
pub struct Credentials {
|
||||
pub client_id: String,
|
||||
pub client_secret: String,
|
||||
|
@ -105,12 +124,18 @@ pub struct Links {
|
|||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(creds: Credentials) -> Self {
|
||||
Self {
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut creds = Credentials::default();
|
||||
|
||||
let p = Path::new(".patreon.json");
|
||||
let config = fs::read_to_string(p)?;
|
||||
creds = serde_json::from_str(&config)?;
|
||||
|
||||
Ok(Self {
|
||||
cli: reqwest::Client::new(),
|
||||
base_url: "https://api.patreon.com".into(),
|
||||
creds: creds,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
|
@ -157,4 +182,57 @@ impl Client {
|
|||
let data: Data<Vec<Object<Pledge>>, Object<User>> = serde_json::from_str(&data)?;
|
||||
Ok(data.included.unwrap())
|
||||
}
|
||||
|
||||
/*
|
||||
POST www.patreon.com/api/oauth2/token
|
||||
?grant_type=refresh_token
|
||||
&refresh_token=<the user‘s refresh_token>
|
||||
&client_id=<your client id>
|
||||
&client_secret=<your client secret>
|
||||
|
||||
1. grab new creds
|
||||
2. serialize new creds to disk
|
||||
3. reload current creds in ram
|
||||
4. ???
|
||||
5. profit!
|
||||
*/
|
||||
#[instrument(skip(self))]
|
||||
pub async fn refresh_token(&mut self) -> Result<()> {
|
||||
let mut u = Url::parse(&self.base_url)?;
|
||||
u.set_path("/api/oauth2/token");
|
||||
u.query_pairs_mut()
|
||||
.append_pair("grant_type", "refresh_token")
|
||||
.append_pair("refresh_token", &self.creds.refresh_token)
|
||||
.append_pair("client_id", &self.creds.client_id)
|
||||
.append_pair("client_secret", &self.creds.client_secret);
|
||||
|
||||
let rg: RefreshGrant = self
|
||||
.cli
|
||||
.post(&u.to_string())
|
||||
.header(
|
||||
"Authorization",
|
||||
format!("Bearer {}", self.creds.access_token),
|
||||
)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
let mut creds = self.creds.clone();
|
||||
|
||||
creds.access_token = rg.access_token;
|
||||
creds.refresh_token = rg.refresh_token;
|
||||
|
||||
let p = Path::new(".patreon.json");
|
||||
if p.exists() {
|
||||
fs::remove_file(p)?;
|
||||
}
|
||||
let mut fout = fs::File::create(p)?;
|
||||
serde_json::to_writer(&mut fout, &creds)?;
|
||||
|
||||
self.creds = creds;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
40
shell.nix
40
shell.nix
|
@ -1,40 +0,0 @@
|
|||
let
|
||||
sources = import ./nix/sources.nix;
|
||||
pkgs =
|
||||
import sources.nixpkgs { overlays = [ (import sources.nixpkgs-mozilla) ]; };
|
||||
dhallpkgs = import sources.easy-dhall-nix { inherit pkgs; };
|
||||
dhall-yaml = dhallpkgs.dhall-yaml-simple;
|
||||
dhall = dhallpkgs.dhall-simple;
|
||||
xepkgs = import sources.xepkgs { inherit pkgs; };
|
||||
rust = pkgs.callPackage ./nix/rust.nix { };
|
||||
in with pkgs;
|
||||
with xepkgs;
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
# Rust
|
||||
rust
|
||||
cargo-watch
|
||||
|
||||
# system dependencies
|
||||
openssl
|
||||
pkg-config
|
||||
|
||||
# kubernetes deployment
|
||||
dhall
|
||||
dhall-yaml
|
||||
|
||||
# dependency manager
|
||||
niv
|
||||
|
||||
# tools
|
||||
ispell
|
||||
];
|
||||
|
||||
SITE_PREFIX = "devel.";
|
||||
CLACK_SET = "Ashlynn,Terry Davis,Dennis Ritchie";
|
||||
RUST_LOG = "debug";
|
||||
RUST_BACKTRACE = "1";
|
||||
RUST_SRC_PATH =
|
||||
"${pkgs.latest.rustChannels.nightly.rust-src}/lib/rustlib/src/rust/library";
|
||||
GITHUB_SHA = "devel";
|
||||
}
|
|
@ -1,7 +1,16 @@
|
|||
let Person =
|
||||
{ Type = { name : Text, tags : List Text, gitLink : Text, twitter : Text }
|
||||
{ Type =
|
||||
{ name : Text
|
||||
, tags : List Text
|
||||
, gitLink : Optional Text
|
||||
, twitter : Optional Text
|
||||
}
|
||||
, default =
|
||||
{ name = "", tags = [] : List Text, gitLink = "", twitter = "" }
|
||||
{ name = ""
|
||||
, tags = [] : List Text
|
||||
, gitLink = None Text
|
||||
, twitter = None Text
|
||||
}
|
||||
}
|
||||
|
||||
in [ Person::{
|
||||
|
@ -20,8 +29,8 @@ in [ Person::{
|
|||
, "istio"
|
||||
, "typescript"
|
||||
]
|
||||
, gitLink = "https://github.com/euforic"
|
||||
, twitter = "https://twitter.com/euforic"
|
||||
, gitLink = Some "https://github.com/euforic"
|
||||
, twitter = Some "https://twitter.com/euforic"
|
||||
}
|
||||
, Person::{
|
||||
, name = "David Roberts"
|
||||
|
@ -41,8 +50,8 @@ in [ Person::{
|
|||
, "embedded"
|
||||
, "sql"
|
||||
]
|
||||
, gitLink = "https://github.com/ddr0"
|
||||
, twitter = "https://twitter.com/DDR_4"
|
||||
, gitLink = Some "https://github.com/ddr0"
|
||||
, twitter = Some "https://twitter.com/DDR_4"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Faizan Jamil"
|
||||
|
@ -65,8 +74,7 @@ in [ Person::{
|
|||
, "full-stack"
|
||||
, "linux"
|
||||
]
|
||||
, gitLink = "https://github.com/faizjamil"
|
||||
, twitter = "N/A"
|
||||
, gitLink = Some "https://github.com/faizjamil"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Joseph Crawley"
|
||||
|
@ -80,8 +88,8 @@ in [ Person::{
|
|||
, "bash"
|
||||
, "linux"
|
||||
]
|
||||
, gitLink = "https://github.com/espe-on"
|
||||
, twitter = "https://twitter.com/espe_on_"
|
||||
, gitLink = Some "https://github.com/espe-on"
|
||||
, twitter = Some "https://twitter.com/espe_on_"
|
||||
}
|
||||
, Person::{
|
||||
, name = "nicoo"
|
||||
|
@ -96,7 +104,7 @@ in [ Person::{
|
|||
, "security"
|
||||
, "SDR"
|
||||
]
|
||||
, gitLink = "https://github.com/nbraud"
|
||||
, gitLink = Some "https://github.com/nbraud"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Prajjwal Singh"
|
||||
|
@ -112,8 +120,8 @@ in [ Person::{
|
|||
, "google-cloud"
|
||||
, "typescript"
|
||||
]
|
||||
, gitLink = "https://github.com/Prajjwal"
|
||||
, twitter = "https://twitter.com/prajjwalsin"
|
||||
, gitLink = Some "https://github.com/Prajjwal"
|
||||
, twitter = Some "https://twitter.com/prajjwalsin"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Piyushh Bhutoria"
|
||||
|
@ -125,8 +133,8 @@ in [ Person::{
|
|||
, "php"
|
||||
, "google-cloud"
|
||||
]
|
||||
, gitLink = "https://github.com/Piyushhbhutoria"
|
||||
, twitter = "https://twitter.com/PiyushhB"
|
||||
, gitLink = Some "https://github.com/Piyushhbhutoria"
|
||||
, twitter = Some "https://twitter.com/PiyushhB"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Ryan Casalino"
|
||||
|
@ -143,8 +151,7 @@ in [ Person::{
|
|||
, "flask"
|
||||
, "unix"
|
||||
]
|
||||
, gitLink = "https://github.com/rjpcasalino"
|
||||
, twitter = "N/A"
|
||||
, gitLink = Some "https://github.com/rjpcasalino"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Jeremy White"
|
||||
|
@ -163,8 +170,8 @@ in [ Person::{
|
|||
, "google-cloud"
|
||||
, "azure"
|
||||
]
|
||||
, gitLink = "https://github.com/dudymas"
|
||||
, twitter = "https://twitter.com/dudymas"
|
||||
, gitLink = Some "https://github.com/dudymas"
|
||||
, twitter = Some "https://twitter.com/dudymas"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Zachary McKee"
|
||||
|
@ -181,14 +188,12 @@ in [ Person::{
|
|||
, "nginx"
|
||||
, "gunicorn"
|
||||
]
|
||||
, gitLink = "https://github.com/ZacharyRMcKee"
|
||||
, twitter = "N/A"
|
||||
, gitLink = Some "https://github.com/ZacharyRMcKee"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Muazzam Kazmi"
|
||||
, tags = [ "Rust", "C++", "x86assembly", "WinAPI", "Node.js", "React.js" ]
|
||||
, gitLink = "https://github.com/muazzamalikazmi"
|
||||
, twitter = "N/A"
|
||||
, gitLink = Some "https://github.com/muazzamalikazmi"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Jeffin Mathew"
|
||||
|
@ -202,8 +207,8 @@ in [ Person::{
|
|||
, "javascript"
|
||||
, "iot"
|
||||
]
|
||||
, gitLink = "https://github.com/mjeffin"
|
||||
, twitter = "https://twitter.com/mpjeffin"
|
||||
, gitLink = Some "https://github.com/mjeffin"
|
||||
, twitter = Some "https://twitter.com/mpjeffin"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Nasir Hussain"
|
||||
|
@ -218,24 +223,17 @@ in [ Person::{
|
|||
, "golang"
|
||||
, "rpm packaging"
|
||||
]
|
||||
, gitLink = "https://github.com/nasirhm"
|
||||
, twitter = "https://twitter.com/_nasirhm_"
|
||||
, gitLink = Some "https://github.com/nasirhm"
|
||||
, twitter = Some "https://twitter.com/_nasirhm_"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Avi Parshan"
|
||||
, tags =
|
||||
[ "python"
|
||||
, "windows"
|
||||
, "javascript"
|
||||
, "html"
|
||||
, "android"
|
||||
, "java"
|
||||
, "C#"
|
||||
]
|
||||
, gitLink = "https://github.com/avipars"
|
||||
, twitter = "https://twitter.com/aviinfinity"
|
||||
[ "python", "windows", "javascript", "html", "android", "java", "C#" ]
|
||||
, gitLink = Some "https://github.com/avipars"
|
||||
, twitter = Some "https://twitter.com/aviinfinity"
|
||||
}
|
||||
, Person:: {
|
||||
, Person::{
|
||||
, name = "Tommy Nguyen"
|
||||
, tags =
|
||||
[ "c++"
|
||||
|
@ -246,6 +244,13 @@ in [ Person::{
|
|||
, "web"
|
||||
, "google-cloud-platform"
|
||||
]
|
||||
, gitLink = "https://github.com/remyabel"
|
||||
, gitLink = Some "https://github.com/remyabel"
|
||||
}
|
||||
, Person::{
|
||||
, name = "Krish Jain"
|
||||
, tags =
|
||||
[ "c++", "linux", "c", "python", "ios", "nlp", "machine learning" ]
|
||||
, gitLink = Some "https://github.com/Krish-sysadmin"
|
||||
, twitter = Some "https://twitter.com/krishjain02"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use crate::{post::Post, signalboost::Person};
|
||||
use color_eyre::eyre::Result;
|
||||
use serde::Deserialize;
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tracing::{error, instrument};
|
||||
|
||||
pub mod markdown;
|
||||
|
@ -9,25 +12,26 @@ pub mod poke;
|
|||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct Config {
|
||||
#[serde(rename = "clackSet")]
|
||||
pub(crate) clack_set: Vec<String>,
|
||||
pub(crate) signalboost: Vec<Person>,
|
||||
pub(crate) port: u16,
|
||||
#[serde(rename = "resumeFname")]
|
||||
pub(crate) resume_fname: PathBuf,
|
||||
#[serde(rename = "webMentionEndpoint")]
|
||||
pub(crate) webmention_url: String,
|
||||
#[serde(rename = "miToken")]
|
||||
pub(crate) mi_token: String,
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn patrons() -> Result<Option<patreon::Users>> {
|
||||
use patreon::*;
|
||||
let creds: Credentials = envy::prefixed("PATREON_")
|
||||
.from_env()
|
||||
.unwrap_or(Credentials::default());
|
||||
let cli = Client::new(creds);
|
||||
let p = Path::new(".patreon.json");
|
||||
if !p.exists() {
|
||||
info!("{:?} does not exist", p);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut cli = patreon::Client::new()?;
|
||||
|
||||
if let Err(why) = cli.refresh_token().await {
|
||||
error!("error getting refresh token: {}", why);
|
||||
}
|
||||
|
||||
match cli.campaign().await {
|
||||
Ok(camp) => {
|
||||
|
|
|
@ -232,7 +232,7 @@ async fn main() -> Result<()> {
|
|||
let _ = std::fs::remove_file(&sockpath);
|
||||
let uds = UnixListener::bind(&sockpath)?;
|
||||
axum::Server::builder(ServerAccept { uds })
|
||||
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo, _>())
|
||||
.serve(app.into_make_service_with_connect_info::<UdsConnectInfo>())
|
||||
.await?;
|
||||
}
|
||||
Err(_) => {
|
||||
|
|
|
@ -36,7 +36,7 @@ impl Into<jsonfeed::Item> for Post {
|
|||
.date_published(self.date.to_rfc3339())
|
||||
.author(
|
||||
jsonfeed::Author::new()
|
||||
.name("Christine Dodrill")
|
||||
.name("Xe Iaso")
|
||||
.url("https://christine.website")
|
||||
.avatar("https://christine.website/static/img/avatar.png"),
|
||||
);
|
||||
|
@ -83,8 +83,7 @@ impl Post {
|
|||
|
||||
async fn read_post(dir: &str, fname: PathBuf, cli: &Option<mi::Client>) -> Result<Post> {
|
||||
debug!(
|
||||
"loading {}/{}",
|
||||
dir,
|
||||
"loading {}",
|
||||
fname.clone().into_os_string().into_string().unwrap()
|
||||
);
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ pub struct Person {
|
|||
pub tags: Vec<String>,
|
||||
|
||||
#[serde(rename = "gitLink")]
|
||||
pub git_link: String,
|
||||
pub git_link: Option<String>,
|
||||
|
||||
pub twitter: String,
|
||||
pub twitter: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -21,7 +21,12 @@
|
|||
<div class="cell -4of12 content">
|
||||
<big>@person.name</big>
|
||||
<p>@for tag in person.tags { @tag }</p>
|
||||
<a href="@person.git_link">GitHub</a> - <a href="@person.twitter">Twitter</a>
|
||||
@if person.git_link.is_some() {
|
||||
<a href="@person.git_link.unwrap()">GitHub</a>
|
||||
}
|
||||
@if person.twitter.is_some() {
|
||||
<a href="@person.twitter.unwrap()">Twitter</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue