commit fed63d67f11b90a924880e23a78bad81c41b9d89
parent 8bbcf54a438e3148ce7c5f1f45c32fd079d2f1df
Author: Silas Brack <silasbrack@gmail.com>
Date: Sun, 3 Nov 2024 14:46:29 +0100
Major changes
Diffstat:
17 files changed, 677 insertions(+), 287 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1 +1,2 @@
+nixos.qcow2
diff --git a/apps/my_app/README.md b/apps/my_app/README.md
@@ -22,3 +22,12 @@ Further documentation can be found at <https://hexdocs.pm/my_app>.
gleam run # Run the project
gleam test # Run the tests
```
+
+```sh
+#! /usr/bin/env nix-shell
+#! nix-shell -i bash -p gleam
+(cd shared && gleam clean) \
+&& (cd client && gleam clean && gleam run -m lustre/dev build --outdir=../server/priv/static --minify) \
+&& (cd server && gleam deps download && gleam export erlang-shipment && cp -r ./build/erlang-shipment/ ../)
+```
+
diff --git a/apps/my_app/build.sh b/apps/my_app/build.sh
@@ -1,6 +0,0 @@
-#! /usr/bin/env nix-shell
-#! nix-shell -i bash -p gleam
-(cd shared && gleam clean) \
-&& (cd client && gleam clean && gleam run -m lustre/dev build --outdir=../server/priv/static --minify) \
-&& (cd server && gleam deps download && gleam export erlang-shipment && cp -r ./build/erlang-shipment/ ../)
-
diff --git a/apps/my_app/flake.nix b/apps/my_app/flake.nix
@@ -2,17 +2,13 @@
description = "My gleam monorepo";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
- # nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
- nixos-shell.url = "github:Mic92/nixos-shell";
flake-utils.url = "github:numtide/flake-utils";
nix-gleam.url = "github:arnarg/nix-gleam";
};
outputs =
{
- self,
nixpkgs,
- nixos-shell,
flake-utils,
nix-gleam,
}:
@@ -44,23 +40,6 @@
pkgs.nodejs_22
];
};
- # nixosConfigurations.vm = nixpkgs.lib.nixosSystem {
- # system = system;
- # modules = [
- # (import ./vm.nix ( { pkgs = import nixpkgs {}; lib = nixpkgs.lib; my_app = pkgs.my_app; } ))
- # nixos-shell.nixosModules.nixos-shell
- # ];
- # };
}
- ))
- // {
- nixosConfigurations.vm = nixpkgs.lib.nixosSystem {
- system = "x86_64-linux";
- modules = [
- (import ./vm.nix ( { pkgs = import nixpkgs {}; lib = nixpkgs.lib; my_app = self.packages."x86_64-linux".default; } ))
- nixos-shell.nixosModules.nixos-shell
- ];
- };
- }
- ;
+ ));
}
diff --git a/apps/my_app/gleam.toml b/apps/my_app/gleam.toml
@@ -1,5 +1,6 @@
name = "my_app"
version = "1.0.0"
+target = "erlang"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
@@ -13,14 +14,23 @@ version = "1.0.0"
# https://gleam.run/writing-gleam/gleam-toml/.
[dependencies]
-gleam_stdlib = ">= 0.34.0 and < 2.0.0"
-mist = ">= 3.0.0 and < 4.0.0"
-gleam_erlang = ">= 0.27.0 and < 1.0.0"
-gleam_http = ">= 3.7.0 and < 4.0.0"
-gleam_otp = ">= 0.12.1 and < 1.0.0"
-wisp = ">= 1.2.0 and < 2.0.0"
-decode = ">= 0.2.1 and < 1.0.0"
+gleam_json = ">= 1.0.1 and < 2.0.0"
+gleam_stdlib = ">= 0.40.0 and < 1.0.0"
+decipher = ">= 1.2.1 and < 2.0.0"
+decode = ">= 0.3.0 and < 1.0.0"
glenv = ">= 0.4.0 and < 1.0.0"
+lustre = ">= 4.5.1 and < 5.0.0"
+lustre_http = ">= 0.5.2 and < 1.0.0"
+wisp = ">= 1.2.0 and < 2.0.0"
+# gleam_stdlib = ">= 0.34.0 and < 2.0.0"
+# mist = ">= 3.0.0 and < 4.0.0"
+# gleam_erlang = ">= 0.27.0 and < 1.0.0"
+# gleam_http = ">= 3.7.0 and < 4.0.0"
+# gleam_otp = ">= 0.12.1 and < 1.0.0"
+# wisp = ">= 1.2.0 and < 2.0.0"
+# decode = ">= 0.2.1 and < 1.0.0"
+# glenv = ">= 0.4.0 and < 1.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
+lustre_dev_tools = ">= 1.6.0 and < 2.0.0"
diff --git a/apps/my_app/manifest.toml b/apps/my_app/manifest.toml
@@ -2,40 +2,62 @@
# You typically do not need to edit this file
packages = [
+ { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
{ name = "birl", version = "1.7.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "5C66647D62BCB11FE327E7A6024907C4A17954EF22865FE0940B54A852446D01" },
- { name = "decode", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "decode", source = "hex", outer_checksum = "4727FC627B803E4F4FF0075B5F93BC022F9293A19B03BF44B5A4C64BF9E8EAE1" },
+ { name = "decipher", version = "1.2.1", build_tools = ["gleam"], requirements = ["birl", "gleam_json", "gleam_stdlib", "stoiridh_version"], otp_app = "decipher", source = "hex", outer_checksum = "4F82516A5FF09BD7DF352DE38F1691C2254508066152F5DEA8665B216A9C9909" },
+ { name = "decode", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "decode", source = "hex", outer_checksum = "EE9B979C0D8A5E058E2519EC0EE9CA4C7CEE15B12997BFF50492636CDC53D0C7" },
{ name = "directories", version = "1.1.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "BDA521A4EB9EE3A7894F0DC863797878E91FF5C7826F7084B2E731E208BDB076" },
{ name = "envoy", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "CFAACCCFC47654F7E8B75E614746ED924C65BD08B1DE21101548AC314A8B6A41" },
{ name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" },
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
+ { name = "fs", version = "8.6.1", build_tools = ["rebar3"], requirements = [], otp_app = "fs", source = "hex", outer_checksum = "61EA2BDAEDAE4E2024D0D25C63E44DCCF65622D4402DB4A2DF12868D1546503F" },
+ { name = "gleam_community_ansi", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "4CD513FC62523053E62ED7BAC2F36136EC17D6A8942728250A9A00A15E340E4B" },
+ { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
{ name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" },
{ name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" },
+ { name = "gleam_fetch", version = "0.4.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_javascript", "gleam_stdlib"], otp_app = "gleam_fetch", source = "hex", outer_checksum = "7446410A44A1D1328F5BC1FF4FC9CBD1570479EA69349237B3F82E34521CCC10" },
{ name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" },
- { name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" },
+ { name = "gleam_httpc", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "091CDD2BEC8092E82707BEA03FB5205A2BBBDE4A2F551E3C069E13B8BC0C428E" },
+ { name = "gleam_javascript", version = "0.13.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_javascript", source = "hex", outer_checksum = "F98328FCF573DA6F3A35D7F6CB3F9FF19FD5224CCBA9151FCBEAA0B983AF2F58" },
+ { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
{ name = "gleam_otp", version = "0.12.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BFACC1513410DF5A1617169A9CD7EA334973AC71D860A17574BA7B2EADD89A6F" },
+ { name = "gleam_package_interface", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "CF3BFC5D0997750D9550D8D73A90F4B8D71C6C081B20ED4E70FFBE1E99AFC3C2" },
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
+ { name = "glearray", version = "0.2.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "9C207E05F38D724F464FA921378DB3ABC2B0A2F5821116D8BC8B2CACC68930D5" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
{ name = "glenv", version = "0.4.0", build_tools = ["gleam"], requirements = ["decode", "envoy", "gleam_stdlib"], otp_app = "glenv", source = "hex", outer_checksum = "46164B9FFEB08927FD2CEBD96C3AFBE082AD5CC2C6F2FC4A78EFDA9EB61E3510" },
- { name = "glisten", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "912132751031473CB38F454120124FFC96AF6B0EA33D92C9C90DB16327A2A972" },
+ { name = "glint", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "A3F1B7C665FD216BE6A886D56537F0E095FB07DF62146074109270B798F8CEC4" },
+ { name = "glisten", version = "5.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging", "telemetry"], otp_app = "glisten", source = "hex", outer_checksum = "48EF7F6D1DCA877C2F49AF35CC33946C7129EEB05A114758A2CC569C708BFAF8" },
{ name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" },
{ name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" },
{ name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" },
- { name = "marceau", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "5188D643C181EE350D8A20A3BDBD63AF7B6C505DE333CFBE05EF642ADD88A59B" },
- { name = "mist", version = "3.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "CDA1A74E768419235E16886463EC4722EFF4AB3F8D820A76EAD45D7C167D7282" },
+ { name = "lustre", version = "4.5.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "B592DA442F6577143CAFA35D4506DB2018DAEED9C707A921E33559E09F001DF1" },
+ { name = "lustre_dev_tools", version = "1.6.0", build_tools = ["gleam"], requirements = ["argv", "filepath", "fs", "gleam_community_ansi", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_httpc", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "glisten", "mist", "simplifile", "spinner", "term_size", "tom", "wisp"], otp_app = "lustre_dev_tools", source = "hex", outer_checksum = "5A1C7D20FA2C0D77D59F259EAE0E14BB3F5359CC1DE7C5ED6922B65FFCBD4C31" },
+ { name = "lustre_http", version = "0.5.2", build_tools = ["gleam"], requirements = ["gleam_fetch", "gleam_http", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_http", source = "hex", outer_checksum = "FB0478CBFA6B16DBE8ECA326DAE2EC15645E04900595EF2C4F039ABFA0512ABA" },
+ { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" },
+ { name = "mist", version = "2.0.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "981F12FC8BA0656B40099EC876D6F2BEE7B95593610F342E9AB0DC4E663A932F" },
{ name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" },
{ name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" },
+ { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" },
{ name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" },
+ { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" },
+ { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" },
+ { name = "stoiridh_version", version = "0.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "stoiridh_version", source = "hex", outer_checksum = "EEF8ADAB9755BD33EB202F169376F1A7797AEF90823FDCA671D8590D04FBF56B" },
{ name = "telemetry", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "7015FC8919DBE63764F4B4B87A95B7C0996BD539E0D499BE6EC9D7F3875B79E6" },
+ { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" },
+ { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
+ { name = "tom", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "tom", source = "hex", outer_checksum = "228E667239504B57AD05EC3C332C930391592F6C974D0EFECF32FFD0F3629A27" },
{ name = "wisp", version = "1.2.0", build_tools = ["gleam"], requirements = ["directories", "exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "F71265D2F1DE11426535A2FA1DA3B11D2FFB783B116DF9496BC8C41983EBADB4" },
]
[requirements]
-decode = { version = ">= 0.2.1 and < 1.0.0" }
-gleam_erlang = { version = ">= 0.27.0 and < 1.0.0" }
-gleam_http = { version = ">= 3.7.0 and < 4.0.0" }
-gleam_otp = { version = ">= 0.12.1 and < 1.0.0" }
-gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
+decipher = { version = ">= 1.2.1 and < 2.0.0" }
+decode = { version = ">= 0.3.0 and < 1.0.0" }
+gleam_json = { version = ">= 1.0.1 and < 2.0.0" }
+gleam_stdlib = { version = ">= 0.40.0 and < 1.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
glenv = { version = ">= 0.4.0 and < 1.0.0" }
-mist = { version = ">= 3.0.0 and < 4.0.0" }
+lustre = { version = ">= 4.5.1 and < 5.0.0" }
+lustre_dev_tools = { version = ">= 1.6.0 and < 2.0.0" }
+lustre_http = { version = ">= 0.5.2 and < 1.0.0" }
wisp = { version = ">= 1.2.0 and < 2.0.0" }
diff --git a/apps/my_app/src/my_app.gleam b/apps/my_app/src/my_app.gleam
@@ -1,29 +1,136 @@
-import gleam/erlang/process
-import mist
-import my_app/env
-import my_app/router
-import wisp
-import wisp/wisp_mist
+import decipher
+import gleam/dynamic
+import gleam/int
+import gleam/list
+import gleam/result
+import lustre
+import lustre/attribute
+import lustre/effect.{type Effect}
+import lustre/element.{type Element}
+import lustre/element/html
+import lustre/event
pub fn main() {
- // This sets the logger to print INFO level logs, and other sensible defaults
- // for a web application.
- wisp.configure_logger()
-
- // Here we generate a secret key, but in a real application you would want to
- // load this from somewhere so that it is not regenerated on every restart.
- let secret_key_base = wisp.random_string(64)
- let env = env.get_env()
-
- // Start the Mist web server.
- let assert Ok(_) =
- wisp_mist.handler(router.handle_request, secret_key_base)
- |> mist.new
- |> mist.bind("0.0.0.0")
- |> mist.port(env.port)
- |> mist.start_http
-
- // The web server runs in new Erlang process, so put this one to sleep while
- // it works concurrently.
- process.sleep_forever()
+ let app = lustre.application(init, update, view)
+ let assert Ok(_) = lustre.start(app, "#app", Nil)
+
+ Nil
+}
+
+type Model =
+ List(#(String, Int))
+
+fn init(_) -> #(Model, Effect(Msg)) {
+ let model = []
+ let effect = effect.none()
+
+ #(model, effect)
+}
+
+type Msg {
+ ServerSavedList(Result(Nil, String))
+ UserAddedProduct(name: String)
+ UserSavedList
+ UserUpdatedQuantity(name: String, amount: Int)
+}
+
+fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
+ case msg {
+ ServerSavedList(_) -> #(model, effect.none())
+ UserAddedProduct(_) -> #(model, effect.none())
+ UserSavedList -> #(model, effect.none())
+ UserUpdatedQuantity(_, _) -> #(model, effect.none())
+ }
+}
+
+fn view(model: Model) -> Element(Msg) {
+ let styles = [
+ #("max-width", "30ch"),
+ #("margin", "0 auto"),
+ #("display", "flex"),
+ #("flex-direction", "column"),
+ #("gap", "1em"),
+ ]
+
+ html.div([attribute.style(styles)], [
+ view_grocery_list(model),
+ view_new_item(),
+ html.div([], [html.button([], [html.text("Sync")])]),
+ ])
+}
+
+fn view_new_item() -> Element(Msg) {
+ let handle_click = fn(event) {
+ let path = ["target", "previousElementSibling", "value"]
+
+ event
+ |> decipher.at(path, dynamic.string)
+ |> result.map(UserAddedProduct)
+ }
+
+ html.div([], [
+ html.input([]),
+ html.button([event.on("click", handle_click)], [html.text("Add")]),
+ ])
+}
+
+fn view_grocery_list(model: Model) -> Element(Msg) {
+ let styles = [#("display", "flex"), #("flex-direction", "column-reverse")]
+
+ element.keyed(html.div([attribute.style(styles)], _), {
+ use #(name, quantity) <- list.map(model)
+ let item = view_grocery_item(name, quantity)
+
+ #(name, item)
+ })
+}
+
+fn view_grocery_item(name: String, quantity: Int) -> Element(Msg) {
+ let handle_input = fn(e) {
+ event.value(e)
+ |> result.nil_error
+ |> result.then(int.parse)
+ |> result.map(UserUpdatedQuantity(name, _))
+ |> result.replace_error([])
+ }
+
+ html.div([attribute.style([#("display", "flex"), #("gap", "1em")])], [
+ html.span([attribute.style([#("flex", "1")])], [html.text(name)]),
+ html.input([
+ attribute.style([#("width", "4em")]),
+ attribute.type_("number"),
+ attribute.value(int.to_string(quantity)),
+ attribute.min("0"),
+ event.on("input", handle_input),
+ ]),
+ ])
}
+// import gleam/erlang/process
+// import mist
+// import my_app/env
+// import my_app/router
+// import wisp
+// import wisp/wisp_mist
+//
+// pub fn main() {
+// // This sets the logger to print INFO level logs, and other sensible defaults
+// // for a web application.
+// wisp.configure_logger()
+//
+// // Here we generate a secret key, but in a real application you would want to
+// // load this from somewhere so that it is not regenerated on every restart.
+// let secret_key_base = wisp.random_string(64)
+// let env = env.get_env()
+//
+// // Start the Mist web server.
+// let assert Ok(_) =
+// wisp_mist.handler(router.handle_request, secret_key_base)
+// |> mist.new
+// |> mist.bind("0.0.0.0")
+// |> mist.port(env.port)
+// |> mist.start_http
+//
+// // The web server runs in new Erlang process, so put this one to sleep while
+// // it works concurrently.
+// process.sleep_forever()
+// }
diff --git a/apps/my_app/src/my_app/router.gleam b/apps/my_app/src/my_app/router.gleam
@@ -1,26 +1,31 @@
-import gleam/string_builder
-import my_app/web
-import wisp.{type Request, type Response}
-
-pub fn handle_request(req: Request) -> Response {
- use req <- web.middleware(req)
- let segments = wisp.path_segments(req)
- case segments {
- [] -> {
- asdf(req)
- }
- ["api", ..] -> api_routes(req, segments)
- _ -> wisp.not_found()
- }
-}
-
-fn asdf(_req: Request) {
- let body = string_builder.from_string("<h1>Hello, Silas!</h1>")
- wisp.html_response(body, 200)
-}
-
-fn api_routes(req: Request, segments: List(String)) {
- case segments {
-
- }
-}
+// import gleam/string_builder
+// import my_app/web
+// import wisp.{type Request, type Response}
+//
+// pub fn handle_request(req: Request) -> Response {
+// use req <- web.middleware(req)
+// let segments = wisp.path_segments(req)
+// case segments {
+// [] -> {
+// asdf(req)
+// }
+// ["api", ..] -> api_routes(req, segments)
+// _ -> wisp.not_found()
+// }
+// }
+//
+// fn asdf(_req: Request) {
+// let body = string_builder.from_string("<h1>Hello, Silas!</h1>")
+// wisp.html_response(body, 200)
+// }
+//
+// fn api_routes(req: Request, segments: List(String)) {
+// case segments {
+// ["api", "games", game_id] -> get_game_state(game_id)
+// _ -> wisp.not_found()
+// }
+// }
+//
+// fn get_game_state() {
+// todo
+// }
diff --git a/apps/my_app/vm.nix b/apps/my_app/vm.nix
@@ -1,56 +0,0 @@
-{ pkgs, lib, my_app, ... }:
-let
- port = 8008;
- pg_port = 44537;
-in {
- system.stateVersion = "24.11";
-
- environment.sessionVariables = rec {
- PORT = port;
- PG_PORT = pg_port;
- };
-
- imports = [ ./module.nix ];
-
- services.my_app = {
- enable = true;
- package = my_app;
- port = port;
- };
- virtualisation.forwardPorts = [ { from = "host"; host.port = port; guest.port = port; } ];
-
- # Enable postgres
- services.postgresql = {
- enable = true;
- package = pkgs.postgresql_16;
- extraPlugins = ps: with ps; [ pgvector ];
- ensureDatabases = [ "mydb" ];
- ensureUsers = [
- {
- name = "datascientist";
- }
- {
- name = "silas";
- }
- ];
- authentication = pkgs.lib.mkOverride 10 ''
- #TYPE DATABASE USER AUTH-METHOD OPTIONAL_IDENT_MAP
- local all all peer map=superuser_map
- #TYPE DATABASE USER ADDRESS AUTH-METHOD
- host all all 127.0.0.1/32 trust
- host all all ::1/128 trust
- '';
- identMap = ''
- # MAPNAME SYSTEM-USERNAME PG-USERNAME
- superuser_map root postgres
- superuser_map postgres postgres
- superuser_map /^(.*)$ \1
- '';
- initialScript = pkgs.writeText "backend-initScript" ''
- GRANT ALL PRIVILEGES ON DATABASE mydb TO silas;
- '';
- settings = {
- port = pkgs.lib.mkForce pg_port;
- };
- };
-}
diff --git a/configuration.nix b/configuration.nix
@@ -0,0 +1,128 @@
+{
+ pkgs,
+ sops,
+ modulesPath,
+ my_app,
+ ...
+}:
+{
+ # Generic
+ system.stateVersion = "24.05"; # Do not change lightly!
+ environment.systemPackages = with pkgs; [
+ vim
+ wget
+ curl
+ rsync
+ rclone
+ zip
+ unzip
+ ];
+ services.openssh.enable = true;
+ networking.firewall.allowedTCPPorts = [ 80 ];
+
+ boot.loader.grub = {
+ enable = false;
+ };
+ fileSystems."/" = {
+ device = "/dev/sda1";
+ fsType = "ext4";
+ };
+ nix.gc = {
+ automatic = true;
+ dates = "weekly";
+ options = "--delete-older-than 30d";
+ };
+
+ # Specific
+ networking.hostName = "nixos-s-1vcpu-512mb-10gb-ams3-01";
+ time.timeZone = "Europe/Amsterdam";
+
+ imports = [
+ (modulesPath + "/virtualisation/digital-ocean-image.nix")
+ ./vpn.nix
+ ./apps/my_app/module.nix
+ ];
+ # security.pki.certificates = [ (builtins.readFile "/run/secrets/ca_certificate") ];
+
+ sops.defaultSopsFile = ./secrets.yaml;
+ sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
+ sops.secrets.server_wireguard_key = {
+ restartUnits = [ "wg-quick-wg0.service" ];
+ };
+ sops.secrets.ca_certificate = { };
+
+ users.users.silas = {
+ isNormalUser = true;
+ description = "Silas Brack";
+ home = "/home/silas";
+ extraGroups = [
+ "networkmanager"
+ "wheel"
+ ]; # "keys"
+ openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFPYO29H4+yDvJbaoUqTg5V6IpD3CMFlB2F47MCzHNpY silasbrack@gmail.com" # Thinkpad (obsolete)
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJtyTJ7fQ/dF4ObtXcjmjJ3OFtjkYEJdr/szcugDEA3C silasbrack@gmail.com" # Thinkpad
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop
+ ];
+ };
+
+ services.my_app = {
+ enable = false;
+ package = my_app.packages."x86_64-linux".default;
+ port = 8009;
+ };
+
+ services.nginx = {
+ enable = true;
+ virtualHosts."localhost" = {
+ locations."/" = {
+ return = "200 'Hello, NixOS!'";
+ extraConfig = ''
+ default_type text/plain;
+ '';
+ };
+ # locations."/chat" = {
+ # proxyPass = "http://10.100.0.4:8080";
+ # };
+ };
+ };
+
+ # services.open-webui = {
+ # enable = true;
+ # };
+
+ # # Enable postgres
+ # services.postgresql = {
+ # enable = true;
+ # package = pkgs.postgresql_16;
+ # extraPlugins = ps: with ps; [ pgvector ];
+ # ensureDatabases = [ "mydb" ];
+ # ensureUsers = [
+ # {
+ # name = "datascientist";
+ # }
+ # {
+ # name = "silas";
+ # }
+ # ];
+ # authentication = pkgs.lib.mkOverride 10 ''
+ # #TYPE DATABASE USER AUTH-METHOD OPTIONAL_IDENT_MAP
+ # local all all peer map=superuser_map
+ # #TYPE DATABASE USER ADDRESS AUTH-METHOD
+ # host all all 127.0.0.1/32 trust
+ # host all all ::1/128 trust
+ # '';
+ # identMap = ''
+ # # MAPNAME SYSTEM-USERNAME PG-USERNAME
+ # superuser_map root postgres
+ # superuser_map postgres postgres
+ # superuser_map /^(.*)$ \1
+ # '';
+ # initialScript = pkgs.writeText "backend-initScript" ''
+ # GRANT ALL PRIVILEGES ON DATABASE mydb TO silas;
+ # '';
+ # settings = {
+ # port = 44537;
+ # };
+ # };
+}
diff --git a/db/mydb/migrations/000.sql b/db/mydb/migrations/000.sql
@@ -1,2 +1,2 @@
-CREATE TABLE migrations ( version BIGINT, applied_at TIMESTAMP WITH time zone default now() );
+CREATE TABLE migrations ( version BIGINT PRIMARY KEY, applied_at TIMESTAMP WITH time zone default now() );
diff --git a/db/mydb/migrations/003.sql b/db/mydb/migrations/003.sql
@@ -0,0 +1,23 @@
+CREATE SCHEMA myvec;
+CREATE EXTENSION vector;
+
+CREATE TABLE IF NOT EXISTS myvec.users (id IDENTITY PRIMARY KEY, embedding vector(3));
+INSERT INTO myvec.users (id, embedding) VALUES (10153815, '[1,2,3]'), (10153816, '[4,5,6]');
+
+CREATE TYPE myvec.mood AS ENUM ('sad', 'ok', 'happy');
+CREATE TABLE IF NOT EXISTS myvec.content (id IDENTITY PRIMARY KEY, embedding vector(3), asdf myvec.mood);
+INSERT INTO myvec.content (embedding, asdf) VALUES ('[1,2,3]', 'sad'), ('[4,5,6]', 'happy');
+CREATE INDEX CONCURRENTLY IF NOT EXISTS content_embedding_index ON myvec.content
+ USING hnsw (embedding vector_cosine_ops)
+ WITH (m = 4, ef_construction = 10);
+CREATE INDEX CONCURRENTLY IF NOT EXISTS content_asdf_index ON myvec.content USING HASH (asdf);
+
+-- EXPLAIN
+-- SELECT
+-- c.id AS content_id
+-- FROM
+-- myvec.content AS c
+-- WHERE c.asdf = 'happy'
+-- ORDER BY (c.embedding <=> (SELECT embedding FROM myvec.users WHERE id = 10153815)) ASC
+-- LIMIT 5;
+
diff --git a/db/mydb/migrations/shell.nix b/db/mydb/migrations/shell.nix
@@ -1,7 +0,0 @@
-let
- pkgs = import <nixpkgs> {};
-in pkgs.mkShell {
- buildInputs = [
- pkgs.squawk
- ];
-}
diff --git a/flake.lock b/flake.lock
@@ -27,7 +27,7 @@
},
"locked": {
"lastModified": 1,
- "narHash": "sha256-W0xI3AT7bo+wLWqKBVbG+RwJgxsgZVjsylK16zntZr8=",
+ "narHash": "sha256-tAz9G1582wkIoj+xzShFRUdvbyzfZfH1M4TarGsRylg=",
"path": "./apps/my_app",
"type": "path"
},
@@ -69,6 +69,24 @@
"type": "github"
}
},
+ "nixos-shell_2": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_3"
+ },
+ "locked": {
+ "lastModified": 1728308049,
+ "narHash": "sha256-wjAkG8f5orZ4+wgXmEaFAJkVJS6ORqfgM0zzLEvbUNw=",
+ "owner": "Mic92",
+ "repo": "nixos-shell",
+ "rev": "9373ca9522f844a3b8029720c74f340e560e4462",
+ "type": "github"
+ },
+ "original": {
+ "owner": "Mic92",
+ "repo": "nixos-shell",
+ "type": "github"
+ }
+ },
"nixpkgs": {
"locked": {
"lastModified": 1628465643,
@@ -86,11 +104,11 @@
},
"nixpkgs-stable": {
"locked": {
- "lastModified": 1728156290,
- "narHash": "sha256-uogSvuAp+1BYtdu6UWuObjHqSbBohpyARXDWqgI12Ss=",
+ "lastModified": 1730602179,
+ "narHash": "sha256-efgLzQAWSzJuCLiCaQUCDu4NudNlHdg2NzGLX5GYaEY=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "17ae88b569bb15590549ff478bab6494dde4a907",
+ "rev": "3c2f1c4ca372622cb2f9de8016c9a0b1cbd0f37c",
"type": "github"
},
"original": {
@@ -116,13 +134,44 @@
"type": "github"
}
},
+ "nixpkgs_24_05": {
+ "locked": {
+ "lastModified": 1730327045,
+ "narHash": "sha256-xKel5kd1AbExymxoIfQ7pgcX6hjw9jCgbiBjiUfSVJ8=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "080166c15633801df010977d9d7474b4a6c549d7",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-24.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
"nixpkgs_3": {
"locked": {
- "lastModified": 1728492678,
- "narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
+ "lastModified": 1628465643,
+ "narHash": "sha256-QSNw9bDq9uGUniQQtakRuw4m21Jxugm23SXLVgEV4DM=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
+ "rev": "6ef4f522d63f22b40004319778761040d3197390",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "ref": "nixos-unstable",
+ "type": "indirect"
+ }
+ },
+ "nixpkgs_4": {
+ "locked": {
+ "lastModified": 1730531603,
+ "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github"
},
"original": {
@@ -132,13 +181,13 @@
"type": "github"
}
},
- "nixpkgs_4": {
+ "nixpkgs_5": {
"locked": {
- "lastModified": 1728093190,
- "narHash": "sha256-CAZF2NRuHmqTtRTNAruWpHA43Gg2UvuCNEIzabP0l6M=",
+ "lastModified": 1730272153,
+ "narHash": "sha256-B5WRZYsRlJgwVHIV6DvidFN7VX7Fg9uuwkRW9Ha8z+w=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "e2f08f4d8b3ecb5cf5c9fd9cb2d53bb3c71807da",
+ "rev": "2d2a9ddbe3f2c00747398f3dc9b05f7f2ebb0f53",
"type": "github"
},
"original": {
@@ -151,21 +200,23 @@
"root": {
"inputs": {
"my_app": "my_app",
- "nixpkgs": "nixpkgs_3",
+ "nixos-shell": "nixos-shell_2",
+ "nixpkgs": "nixpkgs_4",
+ "nixpkgs_24_05": "nixpkgs_24_05",
"sops-nix": "sops-nix"
}
},
"sops-nix": {
"inputs": {
- "nixpkgs": "nixpkgs_4",
+ "nixpkgs": "nixpkgs_5",
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
- "lastModified": 1728345710,
- "narHash": "sha256-lpunY1+bf90ts+sA2/FgxVNIegPDKCpEoWwOPu4ITTQ=",
+ "lastModified": 1730605784,
+ "narHash": "sha256-1NveNAMLHbxOg0BpBMSVuZ2yW2PpDnZLbZ25wV50PMc=",
"owner": "Mic92",
"repo": "sops-nix",
- "rev": "06535d0e3d0201e6a8080dd32dbfde339b94f01b",
+ "rev": "e9b5eef9b51cdf966c76143e13a9476725b2f760",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
@@ -1,111 +1,106 @@
{
inputs = {
- # nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ nixpkgs_24_05.url = "github:NixOS/nixpkgs/nixos-24.05";
+ nixos-shell.url = "github:Mic92/nixos-shell";
sops-nix.url = "github:Mic92/sops-nix";
- # my_app.url = "github:silasbrack/test_gleam_app";
my_app.url = "path:./apps/my_app";
};
outputs =
- { nixpkgs, sops-nix, my_app, ... }:
- # inputs @ { nixpkgs, sops-nix, my_app, ... }:
{
- colmena = {
- meta = {
- # nixpkgs = (import nixos_24_05) { };
- nixpkgs = import nixpkgs {
- system = "x86_64-linux";
+ self,
+ nixpkgs,
+ nixpkgs_24_05,
+ nixos-shell,
+ sops-nix,
+ my_app,
+ ...
+ }:
+ let
+ system = "x86_64-linux";
+ pkgs = import nixpkgs {
+ inherit system;
+ };
+ pkgs_24_05 = import nixpkgs_24_05 { inherit system; };
+ start = pkgs.writeShellScriptBin "start" ''
+ set -e
+ export QEMU_NET_OPTS="hostfwd=tcp::8080-:80"
+ ${pkgs_24_05.nixos-shell}/bin/nixos-shell --flake .
+ '';
+ in
+ {
+ nixosConfigurations.default = nixpkgs.lib.nixosSystem {
+ inherit system;
+ modules = [
+ sops-nix.nixosModules.sops
+ (
+ inputs@{
+ pkgs,
+ lib,
+ config,
+ options,
+ specialArgs,
+ modulesPath,
+ sops,
+ }:
+ import ./configuration.nix (inputs // { inherit my_app; })
+ )
+ ];
+ };
+ devShells."${system}".default =
+ let
+ pkgs = import nixpkgs {
+ inherit system;
};
- # specialArgs.inputs = inputs;
+ in
+ pkgs.mkShell {
+ buildInputs = with pkgs; [
+ curl
+ pkgs_24_05.nixos-shell
+ sops
+ wireguard-tools
+ squawk
+ ];
};
-
- defaults =
- { pkgs, ... }:
- {
- system.stateVersion = "24.05"; # Do not change lightly!
- environment.systemPackages = with pkgs; [
- vim
- wget
- curl
- rsync
- rclone
- zip
- unzip
- ];
- services.openssh.enable = true;
- networking.firewall.allowedTCPPorts = [ 80 ];
-
- boot.loader.grub = {
- enable = false;
- };
- fileSystems."/" = {
- device = "/dev/sda1";
- fsType = "ext4";
- };
- };
-
- nixos-s-1vcpu-512mb-10gb-ams3-01 =
- { pkgs, lib, name, ... }:
- {
- deployment = {
- targetHost = "188.166.127.72";
- targetUser = "root";
- };
- networking.hostName = name;
- time.timeZone = "Europe/Amsterdam";
-
- imports = [
- ./vpn.nix
- sops-nix.nixosModules.sops
- ./apps/my_app/module.nix
- ];
- # security.pki.certificates = [ (builtins.readFile "/run/secrets/ca_certificate") ];
-
- sops.defaultSopsFile = ./secrets.yaml;
- sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
- sops.secrets.server_wireguard_key = {
- restartUnits = [ "wg-quick-wg0.service" ];
- };
- sops.secrets.ca_certificate = { };
-
- users.users.silas = {
- isNormalUser = true;
- description = "Silas Brack";
- home = "/home/silas";
- extraGroups = [
- "networkmanager"
- "wheel"
- ]; # "keys"
- openssh.authorizedKeys.keys = [
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFPYO29H4+yDvJbaoUqTg5V6IpD3CMFlB2F47MCzHNpY silasbrack@gmail.com" # Thinkpad
- "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop
- ];
- packages = with pkgs; [
- neovim
- gleam
- erlang_27
- rebar3
- ];
- };
-
- services.my_app = {
- enable = true;
- package = my_app.packages."x86_64-linux".default;
- port = 8009;
- };
-
- services.nginx = {
- enable = true;
- virtualHosts."localhost" = {
- locations."/" = {
- return = "200 'Hello, NixOS!'";
- extraConfig = ''
- default_type text/plain;
- '';
- };
- };
- };
- };
+ nixosConfigurations.vm = nixpkgs.lib.nixosSystem {
+ inherit system;
+ modules = [
+ # (
+ # inputs@{
+ # pkgs,
+ # lib,
+ # config,
+ # options,
+ # specialArgs,
+ # modulesPath,
+ # sops,
+ # }:
+ # import ./configuration.nix (inputs // { inherit my_app; })
+ # )
+ (
+ inputs@{
+ pkgs,
+ lib,
+ config,
+ options,
+ specialArgs,
+ modulesPath,
+ sops,
+ }:
+ import ./vm.nix (
+ inputs
+ // {
+ inherit self;
+ my_app = my_app.packages."${system}".default;
+ }
+ )
+ )
+ nixos-shell.nixosModules.nixos-shell
+ ];
+ };
+ packages = {
+ inherit start;
};
+ defaultPackage.x86_64-linux = start;
};
}
diff --git a/shell.nix b/shell.nix
@@ -1,11 +0,0 @@
-{
- pkgs ? import <nixpkgs> { },
-}:
-pkgs.mkShell {
- buildInputs = with pkgs; [
- curl
- colmena
- wireguard-tools
- sops
- ];
-}
diff --git a/vm.nix b/vm.nix
@@ -0,0 +1,140 @@
+{
+ pkgs,
+ self,
+ # my_app,
+ ...
+}:
+let
+ user = "root";
+ port = 8008;
+
+ pg_user = "postgres";
+ pg_host = "localhost";
+ pg_port = 44537;
+ pg_database = "mydb";
+ database_url = "postgres://${pg_user}@${pg_host}:${toString pg_port}/${pg_database}"; # ?sslmode=full
+in
+{
+ system.stateVersion = "24.11";
+
+ environment.sessionVariables = rec {
+ PORT = port;
+ PGUSER = pg_user;
+ PGHOST = pg_host;
+ PGPORT = pg_port;
+ PGDATABASE = pg_database;
+ DATABASE_URL = database_url;
+ };
+
+ # imports = [ ./module.nix ];
+
+ # services.my_app = {
+ # enable = true;
+ # package = my_app;
+ # port = port;
+ # };
+ virtualisation.forwardPorts = [
+ {
+ from = "host";
+ host.port = port;
+ guest.port = port;
+ }
+ ];
+
+ services.postgresql = {
+ enable = true;
+ package = pkgs.postgresql_16;
+ extraPlugins = ps: with ps; [ pgvector ];
+ ensureDatabases = [ "mydb" ];
+ ensureUsers = [
+ {
+ name = "datascientist";
+ }
+ {
+ name = "silas";
+ }
+ ];
+ authentication = pkgs.lib.mkOverride 10 ''
+ #TYPE DATABASE USER AUTH-METHOD OPTIONAL_IDENT_MAP
+ local all all peer map=superuser_map
+ #TYPE DATABASE USER ADDRESS AUTH-METHOD
+ host all all 127.0.0.1/32 trust
+ host all all ::1/128 trust
+ '';
+ identMap = ''
+ # MAPNAME SYSTEM-USERNAME PG-USERNAME
+ superuser_map root postgres
+ superuser_map postgres postgres
+ superuser_map /^(.*)$ \1
+ '';
+ # initialScript = pkgs.writeText "backend-initScript" ''
+ # GRANT ALL PRIVILEGES ON DATABASE mydb TO silas;
+ # '';
+ settings = {
+ port = pkgs.lib.mkForce pg_port;
+ };
+ };
+
+ systemd.services = {
+ db-migration = {
+ description = "DB migrations script";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "postgresql.service" ];
+ requires = [ "postgresql.service" ];
+
+ environment = {
+ DATABASE_URL = database_url;
+ };
+
+ script = ''
+ MIGRATIONS_FOLDER=${self}/db/mydb/migrations;
+ PSQL=/run/current-system/sw/bin/psql
+
+ MIGRATIONS_TABLE_EXISTS=$($PSQL $DATABASE_URL -AXqtc "SELECT EXISTS(SELECT * FROM information_schema.tables WHERE table_schema LIKE 'public' AND table_type LIKE 'BASE TABLE' AND table_name = 'migrations');")
+ >&2 echo "MIGRATIONS_TABLE_EXISTS=$MIGRATIONS_TABLE_EXISTS"
+ LATEST_VERSION=$(
+ if [ $MIGRATIONS_TABLE_EXISTS = 't' ]; then
+ $PSQL $DATABASE_URL -AXqtc 'SELECT MAX(version) FROM migrations;'
+ else
+ echo "-1"
+ fi
+ );
+ >&2 echo "LATEST_VERSION=$LATEST_VERSION"
+ MAX_VERSION=$(ls $MIGRATIONS_FOLDER | sort -n | tail -1 | xargs -I _ basename _ .sql | sed -r 's/0*([0-9]*)/\1/');
+ >&2 echo "MAX_VERSION=$MAX_VERSION"
+
+ for VERSION in $(seq -f "%03g" $(($LATEST_VERSION + 1)) $MAX_VERSION); do
+ >&2 echo "Running migration for file $MIGRATIONS_FOLDER/$VERSION.sql"
+ $PSQL $DATABASE_URL --single-transaction -a -f $MIGRATIONS_FOLDER/$VERSION.sql \
+ && $PSQL $DATABASE_URL -a -c 'INSERT INTO migrations (version) VALUES ('$(($VERSION))');' \
+ || exit 1
+ done
+ '';
+ serviceConfig = {
+ User = user;
+ Type = "oneshot";
+ };
+ };
+
+ # myapp = {
+ # wantedBy = [ "multi-user.target" ];
+ # description = "Start my app server.";
+ # after = [ "network.target" ];
+ # # requires = [ "db-migration.service" ];
+ #
+ # # environment = {
+ # # APP_PORT = toString cfg.port;
+ # # DATABASE_URL = "postgres:///${cfg.user}";
+ # # };
+ #
+ # serviceConfig = {
+ # Type = "simple";
+ # User = cfg.user;
+ # ExecStart = ''${server}/bin/server'';
+ # Restart = "always";
+ # KillMode = "process";
+ # };
+ # };
+ };
+
+}