infrastructure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit fed63d67f11b90a924880e23a78bad81c41b9d89
parent 8bbcf54a438e3148ce7c5f1f45c32fd079d2f1df
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sun,  3 Nov 2024 14:46:29 +0100

Major changes

Diffstat:
M.gitignore | 1+
Mapps/my_app/README.md | 9+++++++++
Dapps/my_app/build.sh | 6------
Mapps/my_app/flake.nix | 23+----------------------
Mapps/my_app/gleam.toml | 24+++++++++++++++++-------
Mapps/my_app/manifest.toml | 44+++++++++++++++++++++++++++++++++-----------
Mapps/my_app/src/my_app.gleam | 159++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mapps/my_app/src/my_app/router.gleam | 57+++++++++++++++++++++++++++++++--------------------------
Dapps/my_app/vm.nix | 56--------------------------------------------------------
Aconfiguration.nix | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdb/mydb/migrations/000.sql | 2+-
Adb/mydb/migrations/003.sql | 23+++++++++++++++++++++++
Ddb/mydb/migrations/shell.nix | 7-------
Mflake.lock | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mflake.nix | 191++++++++++++++++++++++++++++++++++++++-----------------------------------------
Dshell.nix | 11-----------
Avm.nix | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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"; + # }; + # }; + }; + +}