213 lines
5.4 KiB
Nix
213 lines
5.4 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
let
|
|
cfg = config.informis.cl-gemini;
|
|
|
|
lisp-libs = with pkgs.lispPackages; [
|
|
asdf-package-system
|
|
asdf-system-connections
|
|
alexandria
|
|
asdf-package-system
|
|
asdf-system-connections
|
|
cl_plus_ssl
|
|
cl-ppcre
|
|
quicklisp
|
|
quri
|
|
uiop
|
|
usocket
|
|
];
|
|
|
|
launchServer = ip: port: root: public-dir: key: cert: slynk-port: feeds-string: textfiles-archive:
|
|
pkgs.writeText "launch-server.lisp" ''
|
|
(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))
|
|
(ql:quickload :slynk)
|
|
(ql:quickload :cl-gemini)
|
|
${optionalString (slynk-port != null) "(slynk:create-server :port ${toString slynk-port} :dont-close t)"}
|
|
${feeds-string}
|
|
(cl-gemini:start-gemini-server "${ip}" "${key}" "${cert}"
|
|
:port ${toString port}
|
|
:document-root "${root}"
|
|
:textfiles-root "${textfiles-archive}"
|
|
:file-cmd "${pkgs.file}/bin/file"
|
|
:log-stream *standard-output*
|
|
:threaded t
|
|
:separate-thread t)
|
|
(loop (sleep 60))
|
|
'';
|
|
|
|
sbcl-with-ssl = pkgs.sbcl.overrideAttrs (oldAttrs: rec {
|
|
extraLibs = with pkgs; [
|
|
openssl_1_1.dev
|
|
];
|
|
});
|
|
|
|
feedOpts = with types; {
|
|
options = {
|
|
url = mkOption {
|
|
type = str;
|
|
description = "Base URI of the feed, i.e. the URI corresponding to the feed path.";
|
|
example = "gemini://my.server/path/to/feedfiles";
|
|
};
|
|
|
|
title = mkOption {
|
|
type = str;
|
|
description = "Title of given feed.";
|
|
example = "My Fancy Feed";
|
|
};
|
|
|
|
path = mkOption {
|
|
type = str;
|
|
description = "Path to Gemini files making up the feed.";
|
|
example = "/path/to/feed";
|
|
};
|
|
};
|
|
};
|
|
|
|
register-feed = name: opts: ''
|
|
(cl-gemini:register-feed :name "${name}" :title "${opts.title}" :path "${opts.path}" :base-uri "${opts.url}")'';
|
|
|
|
register-feeds = feeds:
|
|
concatStringsSep "\n"
|
|
(mapAttrsToList register-feed feeds);
|
|
|
|
|
|
in {
|
|
options.informis.cl-gemini = with types; {
|
|
enable = mkEnableOption "Enable the cl-gemini server.";
|
|
|
|
port = mkOption {
|
|
type = port;
|
|
description = "Port on which to serve Gemini traffic.";
|
|
default = 1965;
|
|
};
|
|
|
|
server-ip = mkOption {
|
|
type = str;
|
|
description = "IP on which to serve Gemini traffic.";
|
|
example = "1.2.3.4";
|
|
};
|
|
|
|
document-root = mkOption {
|
|
type = str;
|
|
description = "Root at which to look for gemini files.";
|
|
example = "/my/gemini/root";
|
|
};
|
|
|
|
user-public = mkOption {
|
|
type = str;
|
|
description = "Subdirectory of user homes to check for gemini files.";
|
|
default = "gemini-public";
|
|
};
|
|
|
|
ssl-private-key = mkOption {
|
|
type = path;
|
|
description = "Path to the pem-encoded server private key.";
|
|
example = /path/to/secret/key.pem;
|
|
};
|
|
|
|
ssl-certificate = mkOption {
|
|
type = path;
|
|
description = "Path to the pem-encoded server public certificate.";
|
|
example = /path/to/cert.pem;
|
|
};
|
|
|
|
slynk-port = mkOption {
|
|
type = nullOr port;
|
|
description = "Port on which to open a slynk server, if any.";
|
|
default = null;
|
|
};
|
|
|
|
feeds = mkOption {
|
|
type = loaOf (submodule feedOpts);
|
|
description = "Feeds to generate and make available (as eg. /feed/name.xml).";
|
|
example = {
|
|
diary = {
|
|
title = "My Diary";
|
|
path = "/path/to/my/gemfiles/";
|
|
url = "gemini://my.host/blog-path/";
|
|
};
|
|
};
|
|
default = {};
|
|
};
|
|
|
|
textfiles-archive = mkOption {
|
|
type = str;
|
|
description = "A path containing only gemini & text files.";
|
|
example = "/path/to/textfiles/";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
environment.systemPackages = with pkgs; [
|
|
cl-gemini
|
|
];
|
|
|
|
users.users = {
|
|
cl-gemini = {
|
|
isSystemUser = true;
|
|
group = "nogroup";
|
|
createHome = true;
|
|
home = "/var/lib/cl-gemini";
|
|
};
|
|
};
|
|
|
|
environment.etc = {
|
|
"cl-gemini/key.pem" = {
|
|
mode = "0400";
|
|
user = "cl-gemini";
|
|
source = cfg.ssl-private-key;
|
|
};
|
|
|
|
"cl-gemini/cert.pem" = {
|
|
mode = "0444";
|
|
user = "cl-gemini";
|
|
source = cfg.ssl-certificate;
|
|
};
|
|
};
|
|
|
|
systemd.services.cl-gemini = {
|
|
description = "cl-gemini Gemini server (https://gemini.circumlunar.space/)";
|
|
|
|
serviceConfig = let
|
|
feed-registrations = register-feeds cfg.feeds;
|
|
in {
|
|
ExecStartPre = "${pkgs.lispPackages.quicklisp}/bin/quicklisp init";
|
|
ExecStart = "${sbcl-with-ssl}/bin/sbcl --load ${
|
|
launchServer
|
|
cfg.server-ip
|
|
cfg.port
|
|
cfg.document-root
|
|
cfg.user-public
|
|
"/etc/cl-gemini/key.pem"
|
|
"/etc/cl-gemini/cert.pem"
|
|
cfg.slynk-port
|
|
feed-registrations
|
|
cfg.textfiles-archive
|
|
}";
|
|
Restart = "on-failure";
|
|
PIDFile = "/run/cl-gemini.$USERNAME.uid";
|
|
User = "cl-gemini";
|
|
};
|
|
|
|
environment = {
|
|
LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
|
|
CL_SOURCE_REGISTRY = concatStringsSep ":"
|
|
(["${config.users.users.cl-gemini.home}/quicklisp/quicklisp"] ++
|
|
(map
|
|
(pkg: "${pkg}//")
|
|
(lisp-libs ++ [pkgs.cl-gemini])));
|
|
};
|
|
|
|
path = with pkgs; [
|
|
gcc
|
|
file
|
|
getent
|
|
];
|
|
|
|
wantedBy = [ "default.target" ];
|
|
};
|
|
};
|
|
}
|