From 65f104373c2f18b34bb7ca9a494776e357686df2 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Tue, 11 Mar 2025 13:50:21 +0100 Subject: [PATCH] Add grist --- nki-personal-do/configuration.nix | 11 +++ nki-personal-do/grist.nix | 127 +++++++++++++++++++++++++++ nki-personal-do/secrets/secrets.yaml | 8 +- 3 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 nki-personal-do/grist.nix diff --git a/nki-personal-do/configuration.nix b/nki-personal-do/configuration.nix index 81dc64c..6cffe4c 100644 --- a/nki-personal-do/configuration.nix +++ b/nki-personal-do/configuration.nix @@ -27,6 +27,7 @@ ./outline.nix ./vikunja.nix ./n8n.nix + ./grist.nix ]; system.stateVersion = "21.11"; @@ -202,6 +203,16 @@ envFile = config.sops.secrets.gts-env.path; }; + # Grist + sops.secrets."grist/env" = { restartUnits = [ "arion-grist.service" ]; }; + cloud.grist = { + enable = true; + envFile = config.sops.secrets."grist/env".path; + host = "tables.dtth.ch"; + dataDir = "/mnt/data/grist"; + }; + + # ntfy cloud.traefik.hosts.ntfy-sh = { host = "ntfy.nkagami.me"; port = 11161; noCloudflare = true; }; services.ntfy-sh = { diff --git a/nki-personal-do/grist.nix b/nki-personal-do/grist.nix new file mode 100644 index 0000000..93813d7 --- /dev/null +++ b/nki-personal-do/grist.nix @@ -0,0 +1,127 @@ +{ pkgs, config, lib, ... }: + +with lib; +let + cfg = config.cloud.grist; + + mkImage = + { imageName, imageDigest, ... }: "${imageName}@${imageDigest}"; + # If we can pullImage we can just do + # mkImage = pkgs.dockerTools.pullImage; + + images = { + # https://hub.docker.com/r/gristlabs/grist/tags + grist = mkImage { + imageName = "docker.io/gristlabs/grist-oss"; + finalImageTag = "1.4.2"; + imageDigest = "sha256:508ed0024f08702ae8797a6607e42ca67e1a0be0ac95c02e75c2a226b5e9cb9b"; + }; + # https://hub.docker.com/r/valkey/valkey/tags + valkey = mkImage { + imageName = "docker.io/valkey/valkey"; + finalImageTag = "8.0.2-alpine"; + imageDigest = "sha256:0fae58181c223280867e8b6d9d5fa29fca507770aeb6819f36d059cab73fa2fd"; + }; + }; + defaultEnv = { + GRIST_HIDE_UI_ELEMENTS = lib.concatStringsSep "," [ "helpCenter" "billing" "multiAccounts" "supportGrist" ]; + GRIST_PAGE_TITLE_SUFFIX = " - DTTH Grist"; + GRIST_FORCE_LOGIN = "true"; + GRIST_WIDGET_LIST_URL = "https://github.com/gristlabs/grist-widget/releases/download/latest/manifest.json"; + GRIST_EXTERNAL_ATTACHMENTS_MODE = "snapshots"; + + GRIST_SANDBOX_FLAVOR = "gvisor"; + PYTHON_VERSION = "3"; + PYTHON_VERSION_ON_CREATION = "3"; + }; +in +{ + options.cloud.grist = { + enable = mkEnableOption "Grist database server"; + envFile = mkOption { + type = types.path; + description = "Path to an environment file that specifies GRIST_SESSION_SECRET and others"; + }; + host = mkOption { + type = types.str; + description = "Exposed hostname"; + }; + port = mkOption { + type = types.int; + description = "Exposed port"; + default = 9674; + }; + dataDir = mkOption { + type = types.str; + description = "Path to the data directory"; + }; + + settings = { + allowedWebhookDomains = mkOption { + type = types.listOf types.str; + description = "List of domains to be allowed in webhooks"; + default = [ "dtth.ch" "nkagami.me" "discord.com" ]; + }; + defaultEmail = mkOption { + type = types.str; + description = "Default email address for admin user"; + default = "nki@nkagami.me"; + }; + }; + }; + + config = mkIf cfg.enable { + cloud.traefik.hosts.grist = { + inherit (cfg) port host; + }; + systemd.services.arion-grist = { + serviceConfig.Type = "notify"; + serviceConfig.NotifyAccess = "all"; + serviceConfig.TimeoutSec = 300; + script = lib.mkBefore '' + ${lib.getExe pkgs.wait4x} http http://127.0.0.1:${toString cfg.port} -t 0 -q -- systemd-notify --ready & + ''; + unitConfig.RequiresMountsFor = [ cfg.dataDir ]; + unitConfig.ReadWritePaths = [ cfg.dataDir ]; + }; + virtualisation.arion.projects.grist.settings = { + services.grist-server.service = { + image = images.grist; + restart = "unless-stopped"; + volumes = [ "${cfg.dataDir}:/persist" ]; + environment = defaultEnv // { + APP_HOME_URL = "https://${cfg.host}"; + ALLOWED_WEBHOOK_DOMAINS = lib.concatStringsSep "," cfg.settings.allowedWebhookDomains; + GRIST_DEFAULT_EMAIL = cfg.settings.defaultEmail; + REDIS_URL = "redis://valkey/1"; + }; + env_file = [ cfg.envFile ]; + ports = [ + "127.0.0.1:${toString cfg.port}:8484" + ]; + }; + services.valkey.service = { + image = images.valkey; + command = "--save 60 1 --loglevel warning"; + restart = "unless-stopped"; + healthcheck = { + test = [ "CMD-SHELL" "valkey-cli ping | grep PONG" ]; + start_period = "20s"; + interval = "30s"; + retries = 5; + timeout = "3s"; + }; + volumes = [ "valkey:/data" ]; + }; + docker-compose.volumes = { + valkey.driver = "local"; + }; + }; + systemd.tmpfiles.settings."10-grist".${cfg.dataDir}.d = { + user = "root"; + group = "root"; + mode = "0700"; + }; + }; +} + diff --git a/nki-personal-do/secrets/secrets.yaml b/nki-personal-do/secrets/secrets.yaml index b72c7e1..13caa5c 100644 --- a/nki-personal-do/secrets/secrets.yaml +++ b/nki-personal-do/secrets/secrets.yaml @@ -47,6 +47,8 @@ vikunja: provider-clientsecret: ENC[AES256_GCM,data:/fN1rH2CKoaivhespd+/KamERjBQOdwR7QQ+hoB+pQ3ZSrBVIKbLMWyOJe8f7rVwXAByqDxQIZJEVPjcjhWSU1eicwpu57FBx+/xJLFazspTVZ+5XKyAwR+UxTHDGAgtFV00QHN53l7ygg4joWWko4IPN1JIpNIASaIWWzpsrIo=,iv:NLsZcmE1kKlzV7B/XPVfENMWlpQtOpESH0ByX1KQ8IQ=,tag:P+ZmsKq0KJAeRTTbvbduMg==,type:str] n8n: env: ENC[AES256_GCM,data:LA/6tMfGgX0cDNfhIZ+n2Ay+6OW5gPPebcXQnfO3qQJSjMjf9vwauF2+W3KpIvM1Dsg3hyNEwqLNRn/28bgWC/qpBpgU2/gVI2n5oxcQaYGgnS/jB0nZWXvORVTnXjH0R+HBFCWgMJe7v+o0EeBH6kni/Nc9geb8paRkxZOGVKeJQy9K4OB2CN6FVO9KeR7gpeQpsh5V5SVW1MoND2tpCOiIK7d0uM6OHF/7p2RFrEEAarvJssj/dZRHjA/jALuqbQ6UDAaAppqlkEgIdZdFEfgebfCWR4e4aWjznW1DGOQQYtg4k/Kj8J/df8CWXX+lUO+9nTo/lhhcH395w+CRE8GUwze15yxQppUwqyLKdYwgmpK1tFnLP/W/As2f97c1fBB9rXrZYOUEIq4GspHOTPgjzcRfWOxX8cMKG69EmeZ3mWPsIDaC1ZvkVQjjcH/o9aC7QeFCwPfcy+mgI+9RjAaCw7qdig1CwgQabAaCd2hzQ4FTXBFJoZRfYZ1v3Rdwe8zqMivIcw2AHv6kYx6c9A==,iv:KmyJ/CLAGrYfzHjSWygtgA/+am9fUrKnOsGRPgV9QfU=,tag:G3LhfdSujcaC9ZZFUse0DQ==,type:str] +grist: + env: ENC[AES256_GCM,data:eT4eFHMU6UgCr/lNbdqFivzZgNREHcM4b/7ZEoKYWWNssrgIJybLm709BG8Q8/kKnBHaFUlczRQNvjS/nexZ3LymMeiXEHcuxC7lEM5otMqKzYFar2Z4YzPaUWGGezYcYBK56Ia5CF8TdWDdTbgnaSBY6R2ViJYTvS3QSdK6AhWfgGrdTVbye2lL6b11TrfI4vC25DhHJbrhFCXf995Q4nnwoACGAQ+Pkk6dsuYme4plV4WfZ8w61Y4SyNhdWYPp/rsJiAQkPajVCFjfZhH6cQCxVmSVn4c59H8MFx4qTbbXCKp3tXV5eQPWQBri5rc1XXAxgHKyrBUpx3QiYI6UDjGbf6hjXPSEWHQPXFLiLVPgH0CjXZeyxcYsr2ZLcOF87grUTR+CcxbiP4CshuJbvKyWiQ4ISOAm3m84XjXNVqn2WEf4ndqpNEG8H+BVbQ9RrGvrdlJqcrqD6rNzw0y+4SEiVPpv1NQ5M7+f+SABO8S2T4BFp95fvX2DmR/z+c9g2xiAOmZDFMzjG97rPrKuZq8+b+H3jlBZ4mxUBiz7l0c3NJ1RGtIUgr+idYGMwpSyh4C1a6LEzlW72F4vF5e5sEyWAHApRGxQCIMAP7ACBotMYgIKSPAzwThHtoTcUdBlDiQw8blsU/1c/FzkpAF5kotyNnk4vJea0H1yZ7NSz12E61qH6utG1YW5BZzZn+TKj7RfPEqzFE1POA0H8v1egE0bu1ClrYDX112iGEGI4IQHXMOu2p15O0/D+Y+QBOhpDwQWbgDXTf3ZMS2tyBtOiYT//ejfExYzV99V9t86xqmAvHODc69WC5nzWUp7SqkLLTP6gBUPSrkPs4ugZRE0q3STOmcnfxGkgpW94oj+jcZZY5supmYWK6zvJsPF9mn6,iv:pfIiOiWVEl0wEK03gnWj+ZKxOBwtBtf1hqzYOSpTm10=,tag:il0r2A/Z8q88sAiVgsuEEA==,type:str] sops: kms: [] gcp_kms: [] @@ -80,8 +82,8 @@ sops: by9kZFlTRVdCZFkxYTVVb0RIRk8zUlkKCqMw9oL9RaYBV5Hhy3o8Nm5xmGrPH8Sd hv36sxRFFNZT/DCKaHaSRbT3mfpBZSTXJt1dgl4nZe6whH54t/1KmA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-12-07T23:38:16Z" - mac: ENC[AES256_GCM,data:GKCMZJVKj5Fq7Ak4wQgI/pAl8JKDdzAYCBRwnxHlg0Z10AstbchAYm+LLwCaE85ebl6m/JexmfJeutJo0yGXuOIQKcEgfyNq9O/i/y34ISc4looQ6cyH5Hcxsd9JXgrmgQzVPquBXQzDHz4rj93VhNrvqmw+SgDPZVwcUznvCBI=,iv:HtUmf0qjvbYW7ngocISpqycX7ceNv0YsILgZhOMTSMg=,tag:kJ7EFOLL8o/2OFkn5PhvJw==,type:str] + lastmodified: "2025-03-06T23:20:44Z" + mac: ENC[AES256_GCM,data:XSYbqif2lhjTW2yUnoqtQehXG3DjS9It1IX4LplRMSxPkK0cpK64z/ouFRi12a1tjzE5YkdmZ9DDIxkAVPG8/5kgcZr5c4ddS/Zt4d97bCeeij47yuvhhoKMRzgIDSokMMmoelOV1k4FkDOfTDuMp2+tjyCao//N9YJjhhRPFYc=,iv:YpNNrIPotZAAQOF8GXBTpMjTFxYDdAFSLB1CBvLOE+k=,tag:1eGTkLRlL4s8TruVDPCA/g==,type:str] pgp: [] unencrypted_suffix: _unencrypted - version: 3.9.1 + version: 3.9.4