at 22.05-pre 7.6 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.sourcehut; 6 cfgIni = cfg.settings; 7 scfg = cfg.meta; 8 iniKey = "meta.sr.ht"; 9 10 rcfg = config.services.redis; 11 drv = pkgs.sourcehut.metasrht; 12in 13{ 14 options.services.sourcehut.meta = { 15 user = mkOption { 16 type = types.str; 17 default = "metasrht"; 18 description = '' 19 User for meta.sr.ht. 20 ''; 21 }; 22 23 port = mkOption { 24 type = types.port; 25 default = 5000; 26 description = '' 27 Port on which the "meta" module should listen. 28 ''; 29 }; 30 31 database = mkOption { 32 type = types.str; 33 default = "meta.sr.ht"; 34 description = '' 35 PostgreSQL database name for meta.sr.ht. 36 ''; 37 }; 38 39 statePath = mkOption { 40 type = types.path; 41 default = "${cfg.statePath}/metasrht"; 42 description = '' 43 State path for meta.sr.ht. 44 ''; 45 }; 46 }; 47 48 config = with scfg; lib.mkIf (cfg.enable && elem "meta" cfg.services) { 49 assertions = 50 [ 51 { 52 assertion = with cfgIni."meta.sr.ht::billing"; enabled == "yes" -> (stripe-public-key != null && stripe-secret-key != null); 53 message = "If meta.sr.ht::billing is enabled, the keys should be defined."; 54 } 55 ]; 56 57 users = { 58 users = { 59 ${user} = { 60 isSystemUser = true; 61 group = user; 62 description = "meta.sr.ht user"; 63 }; 64 }; 65 66 groups = { 67 "${user}" = { }; 68 }; 69 }; 70 71 services.cron.systemCronJobs = [ "0 0 * * * ${cfg.python}/bin/metasrht-daily" ]; 72 services.postgresql = { 73 authentication = '' 74 local ${database} ${user} trust 75 ''; 76 ensureDatabases = [ database ]; 77 ensureUsers = [ 78 { 79 name = user; 80 ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; }; 81 } 82 ]; 83 }; 84 85 systemd = { 86 tmpfiles.rules = [ 87 "d ${statePath} 0750 ${user} ${user} -" 88 ]; 89 90 services = { 91 metasrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey { 92 after = [ "postgresql.service" "network.target" ]; 93 requires = [ "postgresql.service" ]; 94 wantedBy = [ "multi-user.target" ]; 95 96 description = "meta.sr.ht website service"; 97 98 preStart = '' 99 # Configure client(s) as "preauthorized" 100 ${concatMapStringsSep "\n\n" 101 (attr: '' 102 if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then 103 # Configure ${attr}'s OAuth client as "preauthorized" 104 psql ${database} \ 105 -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'" 106 107 printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth" 108 fi 109 '') 110 (builtins.attrNames (filterAttrs 111 (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null) 112 cfg.settings))} 113 ''; 114 115 serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}"; 116 }; 117 118 metasrht-api = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey { 119 after = [ "postgresql.service" "network.target" ]; 120 requires = [ "postgresql.service" ]; 121 wantedBy = [ "multi-user.target" ]; 122 123 description = "meta.sr.ht api service"; 124 125 preStart = '' 126 # Configure client(s) as "preauthorized" 127 ${concatMapStringsSep "\n\n" 128 (attr: '' 129 if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then 130 # Configure ${attr}'s OAuth client as "preauthorized" 131 psql ${database} \ 132 -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'" 133 134 printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth" 135 fi 136 '') 137 (builtins.attrNames (filterAttrs 138 (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null) 139 cfg.settings))} 140 ''; 141 142 serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b :${toString (port + 100)}"; 143 }; 144 145 metasrht-webhooks = { 146 after = [ "postgresql.service" "network.target" ]; 147 requires = [ "postgresql.service" ]; 148 wantedBy = [ "multi-user.target" ]; 149 150 description = "meta.sr.ht webhooks service"; 151 serviceConfig = { 152 Type = "simple"; 153 User = user; 154 Restart = "always"; 155 ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info"; 156 }; 157 158 }; 159 }; 160 }; 161 162 services.sourcehut.settings = { 163 # URL meta.sr.ht is being served at (protocol://domain) 164 "meta.sr.ht".origin = mkDefault "https://meta.${cfg.originBase}"; 165 # Address and port to bind the debug server to 166 "meta.sr.ht".debug-host = mkDefault "0.0.0.0"; 167 "meta.sr.ht".debug-port = mkDefault port; 168 # Configures the SQLAlchemy connection string for the database. 169 "meta.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql"; 170 # Set to "yes" to automatically run migrations on package upgrade. 171 "meta.sr.ht".migrate-on-upgrade = mkDefault "yes"; 172 # If "yes", the user will be sent the stock sourcehut welcome emails after 173 # signup (requires cron to be configured properly). These are specific to the 174 # sr.ht instance so you probably want to patch these before enabling this. 175 "meta.sr.ht".welcome-emails = mkDefault "no"; 176 177 # The redis connection used for the webhooks worker 178 "meta.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/6"; 179 180 # If "no", public registration will not be permitted. 181 "meta.sr.ht::settings".registration = mkDefault "no"; 182 # Where to redirect new users upon registration 183 "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${cfg.originBase}"; 184 # How many invites each user is issued upon registration (only applicable if 185 # open registration is disabled) 186 "meta.sr.ht::settings".user-invites = mkDefault 5; 187 188 # Origin URL for API, 100 more than web 189 "meta.sr.ht".api-origin = mkDefault "http://localhost:5100"; 190 191 # You can add aliases for the client IDs of commonly used OAuth clients here. 192 # 193 # Example: 194 "meta.sr.ht::aliases" = mkDefault { }; 195 # "meta.sr.ht::aliases"."git.sr.ht" = 12345; 196 197 # "yes" to enable the billing system 198 "meta.sr.ht::billing".enabled = mkDefault "no"; 199 # Get your keys at https://dashboard.stripe.com/account/apikeys 200 "meta.sr.ht::billing".stripe-public-key = mkDefault null; 201 "meta.sr.ht::billing".stripe-secret-key = mkDefault null; 202 }; 203 204 services.nginx.virtualHosts."meta.${cfg.originBase}" = { 205 forceSSL = true; 206 locations."/".proxyPass = "http://${cfg.address}:${toString port}"; 207 locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}"; 208 locations."/static".root = "${pkgs.sourcehut.metasrht}/${pkgs.sourcehut.python.sitePackages}/metasrht"; 209 }; 210 }; 211}