1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8let
9 cfg = config.services.pingvin-share;
10 inherit (lib)
11 mkOption
12 mkEnableOption
13 mkIf
14 mkPackageOption
15 types
16 ;
17in
18
19{
20 options = {
21 services.pingvin-share = {
22 enable = mkEnableOption "Pingvin Share, a self-hosted file sharing platform";
23
24 user = mkOption {
25 type = types.str;
26 default = "pingvin";
27 description = ''
28 User account under which Pingvin Share runs.
29 '';
30 };
31
32 group = mkOption {
33 type = types.str;
34 default = "pingvin";
35 description = ''
36 Group under which Pingvin Share runs.
37 '';
38 };
39
40 openFirewall = mkOption {
41 type = types.bool;
42 default = false;
43 description = ''
44 Whether to open the firewall for the port in {option}`services.pingvin-share.frontend.port`.
45 '';
46 };
47
48 dataDir = mkOption {
49 type = types.path;
50 default = "/var/lib/pingvin-share";
51 example = "/var/lib/pingvin";
52 description = ''
53 The path to the data directory in which Pingvin Share will store its data.
54 '';
55 };
56
57 hostname = mkOption {
58 type = types.str;
59 default = "localhost:${toString cfg.backend.port}";
60 defaultText = lib.literalExpression "localhost:\${options.services.pingvin-share.backend.port}";
61 example = "pingvin-share.domain.tdl";
62 description = ''
63 The domain name of your instance. If null, the redirections will be made to localhost.
64 '';
65 };
66
67 https = mkOption {
68 type = types.bool;
69 default = false;
70 example = true;
71 description = ''
72 Whether to enable HTTPS for the domain.
73 '';
74 };
75
76 backend = {
77 package = mkPackageOption pkgs [
78 "pingvin-share"
79 "backend"
80 ] { };
81
82 port = mkOption {
83 type = types.port;
84 default = 8080;
85 example = 9000;
86 description = ''
87 The port that the backend service of Pingvin Share will listen to.
88 '';
89 };
90 };
91
92 frontend = {
93 package = mkPackageOption pkgs [
94 "pingvin-share"
95 "frontend"
96 ] { };
97
98 port = mkOption {
99 type = types.port;
100 default = 3000;
101 example = 8000;
102 description = ''
103 The port that the frontend service of Pingvin Share will listen to.
104 '';
105 };
106 };
107
108 nginx = {
109 enable = mkEnableOption "a Nginx reverse proxy for Pingvin Share.";
110 };
111 };
112 };
113
114 config = mkIf cfg.enable {
115
116 users.groups = mkIf (cfg.group == "pingvin") { pingvin = { }; };
117
118 users.users = mkIf (cfg.user == "pingvin") {
119 pingvin = {
120 group = cfg.group;
121 description = "Pingvin Share daemon user";
122 isSystemUser = true;
123 };
124 };
125
126 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.frontend.port ];
127
128 systemd.services.pingvin-share-backend = {
129 description = "Backend service of Pingvin Share, a self-hosted file sharing platform.";
130
131 wantedBy = [
132 "multi-user.target"
133 "pingvin-share-frontend.service"
134 ];
135 before = [ "pingvin-share-frontend.service" ];
136 after = [
137 "network.target"
138 "network-online.target"
139 ];
140 wants = [ "network-online.target" ];
141
142 environment = {
143 PRISMA_SCHEMA_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/schema-engine";
144 PRISMA_QUERY_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/query-engine";
145 PRISMA_QUERY_ENGINE_LIBRARY = "${pkgs.prisma-engines}/lib/libquery_engine.node";
146 PRISMA_INTROSPECTION_ENGINE_BINARY = "${pkgs.prisma-engines}/bin/introspection-engine";
147 PRISMA_FMT_BINARY = "${pkgs.prisma-engines}/bin/prisma-fmt";
148 BACKEND_PORT = toString cfg.backend.port;
149 DATABASE_URL = "file:${cfg.dataDir}/pingvin-share.db?connection_limit=1";
150 DATA_DIRECTORY = cfg.dataDir;
151 };
152
153 path = with pkgs; [
154 cfg.backend.package
155 openssl
156 prisma-engines
157 ];
158
159 serviceConfig = {
160 User = cfg.user;
161 Group = cfg.group;
162 Type = "simple";
163 Restart = "on-failure";
164 ExecStartPre = [
165 "${cfg.backend.package}/node_modules/.bin/prisma migrate deploy"
166 "${cfg.backend.package}/node_modules/.bin/prisma db seed"
167 ];
168 ExecStart = "${cfg.backend.package}/node_modules/.bin/ts-node dist/src/main";
169 StateDirectory = mkIf (cfg.dataDir == "/var/lib/pingvin-share") "pingvin-share";
170 WorkingDirectory = cfg.backend.package;
171 };
172 };
173
174 systemd.services.pingvin-share-frontend = {
175 description = "Frontend service of Pingvin Share, a self-hosted file sharing platform.";
176
177 wantedBy = [ "multi-user.target" ];
178 wants = [
179 "network-online.target"
180 "pingvin-share-backend.service"
181 ];
182 after = [
183 "network-online.target"
184 "pingvin-share-backend.service"
185 ];
186
187 environment = {
188 PORT = toString cfg.frontend.port;
189 API_URL = "${if cfg.https then "https" else "http"}://${cfg.hostname}";
190 };
191 path = [ cfg.frontend.package ];
192
193 serviceConfig = {
194 User = cfg.user;
195 Group = cfg.group;
196 Type = "simple";
197 Restart = "on-failure";
198 ExecStart = "${cfg.frontend.package}/node_modules/.bin/next start";
199 StateDirectory = mkIf (cfg.dataDir == "/var/lib/pingvin-share") "pingvin-share";
200 WorkingDirectory = cfg.frontend.package;
201 };
202 };
203
204 services.nginx = mkIf cfg.nginx.enable {
205 enable = lib.mkDefault true;
206 virtualHosts."${cfg.hostname}" = {
207 enableACME = cfg.https;
208 forceSSL = cfg.https;
209
210 locations."/" = {
211 proxyPass = "http://localhost:${toString cfg.frontend.port}";
212 recommendedProxySettings = true;
213 };
214 locations."/api" = {
215 proxyPass = "http://localhost:${toString cfg.backend.port}";
216 recommendedProxySettings = true;
217 };
218 };
219 };
220 };
221
222 meta = {
223 maintainers = with lib.maintainers; [ ratcornu ];
224 doc = ./pingvin-share.md;
225 };
226}