1{ lib, ... }:
2{
3 name = "pufferpanel";
4 meta.maintainers = [ lib.maintainers.tie ];
5
6 nodes.machine =
7 { pkgs, ... }:
8 {
9 environment.systemPackages = [ pkgs.pufferpanel ];
10 services.pufferpanel = {
11 enable = true;
12 extraPackages = [ pkgs.netcat ];
13 environment = {
14 PUFFER_PANEL_REGISTRATIONENABLED = "false";
15 PUFFER_PANEL_SETTINGS_COMPANYNAME = "NixOS";
16 };
17 };
18 };
19
20 testScript = ''
21 import shlex
22 import json
23
24 curl = "curl --fail-with-body --silent"
25 baseURL = "http://localhost:8080"
26 adminName = "admin"
27 adminEmail = "admin@nixos.org"
28 adminPass = "admin"
29 adminCreds = json.dumps({
30 "email": adminEmail,
31 "password": adminPass,
32 })
33 stopCode = 9 # SIGKILL
34 serverPort = 1337
35 serverDefinition = json.dumps({
36 "name": "netcat",
37 "node": 0,
38 "users": [
39 adminName,
40 ],
41 "type": "netcat",
42 "run": {
43 "stopCode": stopCode,
44 "command": f"nc -l {serverPort}",
45 },
46 "environment": {
47 "type": "standard",
48 },
49 })
50
51 start_all()
52
53 machine.wait_for_unit("pufferpanel.service")
54 machine.wait_for_open_port(5657) # SFTP
55 machine.wait_for_open_port(8080) # HTTP
56
57 # Note that PufferPanel does not initialize database unless necessary.
58 # /api/config endpoint creates database file and triggers migrations.
59 # On success, we run a command to create administrator user that we use to
60 # interact with HTTP API.
61 resp = json.loads(machine.succeed(f"{curl} {baseURL}/api/config"))
62 assert resp["branding"]["name"] == "NixOS", "Invalid company name in configuration"
63 assert resp["registrationEnabled"] == False, "Expected registration to be disabled"
64
65 machine.succeed(f"pufferpanel --workDir /var/lib/pufferpanel user add --admin --name {adminName} --email {adminEmail} --password {adminPass}")
66
67 resp = json.loads(machine.succeed(f"{curl} -d '{adminCreds}' {baseURL}/auth/login"))
68 assert "servers.admin" in resp["scopes"], "User is not administrator"
69 token = resp["session"]
70 authHeader = shlex.quote(f"Authorization: Bearer {token}")
71
72 resp = json.loads(machine.succeed(f"{curl} -H {authHeader} -H 'Content-Type: application/json' -d '{serverDefinition}' {baseURL}/api/servers"))
73 serverID = resp["id"]
74 machine.succeed(f"{curl} -X POST -H {authHeader} {baseURL}/proxy/daemon/server/{serverID}/start")
75 machine.wait_for_open_port(serverPort)
76 '';
77}