forked from
lass.pds.lassul.us/superconfig
This repository has no description
3.8 kB
118 lines
1{
2 config,
3 lib,
4 pkgs,
5 self,
6 ...
7}:
8let
9 domain = "when.lassul.us";
10 port = 3002;
11 timeful = self.packages.${pkgs.system}.timeful;
12in
13{
14 # timeful (formerly schej): crab.fit/when2meet-style availability polls.
15 # Creating a poll and painting availability work without an account;
16 # signing in (GitHub OAuth, see pkgs/timeful/self-host.patch) keeps a
17 # dashboard of your polls.
18 services.mongodb = {
19 enable = true;
20 # prebuilt binary; the default `mongodb` builds from source for hours
21 package = pkgs.mongodb-ce;
22 };
23
24 clan.core.vars.generators.timeful = {
25 files.env = { };
26 runtimeInputs = [ pkgs.openssl ];
27 # ENCRYPTION_KEY is fed raw to aes.NewCipher, so it must be exactly
28 # 16/24/32 bytes (upstream's `openssl rand -base64 32` hint is 44 bytes).
29 script = ''
30 {
31 echo "SESSION_SECRET=$(openssl rand -hex 32)"
32 echo "ENCRYPTION_KEY=$(openssl rand -hex 16)"
33 } > $out/env
34 '';
35 };
36
37 # GitHub OAuth app (https://github.com/settings/developers), callback URL
38 # https://when.lassul.us/api/auth/github/callback
39 clan.core.vars.generators.timeful-github = {
40 files.env = { };
41 prompts.client_id = {
42 description = "GitHub OAuth app client ID for ${domain}";
43 persist = true;
44 };
45 prompts.client_secret = {
46 description = "GitHub OAuth app client secret for ${domain}";
47 type = "hidden";
48 persist = true;
49 };
50 script = ''
51 printf 'GITHUB_CLIENT_ID=%s\nGITHUB_CLIENT_SECRET=%s\n' \
52 "$(cat "$prompts"/client_id)" "$(cat "$prompts"/client_secret)" > "$out/env"
53 '';
54 };
55
56 systemd.services.timeful = {
57 description = "timeful availability polls";
58 wantedBy = [ "multi-user.target" ];
59 requires = [ "mongodb.service" ];
60 after = [
61 "network.target"
62 "mongodb.service"
63 ];
64 environment = {
65 LISTEN_ADDR = "127.0.0.1:${toString port}";
66 MONGODB_URI = "mongodb://127.0.0.1:27017";
67 CORS_ORIGINS = "https://${domain}";
68 # no listmonk mailing list; skip its HTTP calls on sign-up
69 LISTMONK_ENABLED = "false";
70 };
71 serviceConfig = {
72 ExecStart = "${lib.getExe timeful} -release";
73 # The server unconditionally appends everything it prints to
74 # ./logs.log, which would grow forever; the journal already has it.
75 ExecStartPre = "${pkgs.coreutils}/bin/ln -sfn /dev/null logs.log";
76 EnvironmentFile = [
77 config.clan.core.vars.generators.timeful.files.env.path
78 config.clan.core.vars.generators.timeful-github.files.env.path
79 ];
80 DynamicUser = true;
81 StateDirectory = "timeful";
82 WorkingDirectory = "/var/lib/timeful";
83 Restart = "on-failure";
84 CapabilityBoundingSet = "";
85 NoNewPrivileges = true;
86 PrivateDevices = true;
87 ProtectHome = true;
88 ProtectKernelTunables = true;
89 ProtectKernelModules = true;
90 ProtectControlGroups = true;
91 RestrictAddressFamilies = [
92 "AF_INET"
93 "AF_INET6"
94 "AF_UNIX"
95 ];
96 RestrictNamespaces = true;
97 LockPersonality = true;
98 SystemCallArchitectures = "native";
99 };
100 };
101
102 services.nginx.virtualHosts.${domain} = {
103 enableACME = true;
104 forceSSL = true;
105 locations."/" = {
106 proxyPass = "http://127.0.0.1:${toString port}";
107 # gin trusts every proxy and takes the leftmost X-Forwarded-For entry
108 # for its per-IP rate limiter, so overwrite instead of appending
109 # (recommendedProxySettings appends a client-controlled value).
110 extraConfig = ''
111 proxy_set_header Host $host;
112 proxy_set_header X-Real-IP $remote_addr;
113 proxy_set_header X-Forwarded-For $remote_addr;
114 proxy_set_header X-Forwarded-Proto $scheme;
115 '';
116 };
117 };
118}