(define-module (kulupu services gotosocial) #:use-module (guix gexp) #:use-module ((guix licenses) #:prefix license:) #:use-module (guix records) #:use-module (guix packages) #:use-module (gnu services) #:use-module (gnu services certbot) #:use-module (gnu services configuration) #:use-module (gnu services databases) #:use-module (gnu services base) #:use-module (gnu services shepherd) #:use-module (gnu services web) #:use-module (gnu system shadow) #:use-module (gnu packages admin) #:use-module (gnu packages bash) #:use-module (gnu packages base) #:use-module (kulupu packages gotosocial) #:use-module (ice-9 match) #:export (gotosocial-service-type gotosocial-configuration)) (define-maybe string) (define-configuration gotosocial-configuration (host (string "localhost") "Name to use as gotosocial host") (gotosocial (package gotosocial) "The gotosocial package to use") (frontend (package gotosocial-frontend) "The gotosocial web asset package to use") (config-file (maybe-string) "Path to configuration file, defaults to no configuration file") (port (integer 8080) "Port to listen on, default 8080") (work-dir (string "/var/lib/gotosocial") "GTS work directory") (run-dir (string "/var/run/gotosocial") "GTS runtime directory") (postgres? (boolean #t) "Set up postgres DB, use sqlite if false") (nginx? (boolean #t) "Set up reverse proxy via nginx") (https? (boolean #t) "Set up HTTPS via certbot") (database-address (string "/var/run/gotosocial/gts.db") "Address of the database, default /var/run/gotosocial/gts.db") (no-serialization)) (define (gotosocial-shepherd-service config) (match-record config (gotosocial frontend config-file port work-dir run-dir host postgres? database-address) (let* ((gts (file-append gotosocial "/bin/gotosocial"))) (list (shepherd-service (documentation "Run GoToSocial") (requirement `(networking ,@(if postgres? '(postgres) '()))) (provision '(gotosocial)) (start #~(make-forkexec-constructor (list (#$(file-append gotosocial "/bin/gotosocial") "server" "start" "--syslog-enabled" "--port=" (number->string #$port) $#(when (not postgres?) "--db-type=sqlite") "--db-address=" #$database-address "--letsencrypt-cert-dir=" #$work-dir "/storage/certs" "--storage-local-base-path=" #$work-dir "/storage" "--web-asset-base-dir=" #$(file-append frontend "/gotosocial/web/assets") "--web-template-base-dir=" #$(file-append frontend "/gotosocial/web/template") "--host=" host)) #:user "gotosocial" #:group "gotosocial" #:directory #$work-dir)) (stop #~(make-kill-destructor))))))) (define (gotosocial-postgresql-roles config) (match-record config (postgres?) (if postgres? (list (postgresql-role (name "gotosocial") (create-database? #t))) '()))) (define (gotosocial-certbot config) (match-record config (https? host nginx?) (if (not https?) '() (list (certificate-configuration (domains (list host)) (deploy-hook (if nginx? %nginx-cert-deploy-hook %gotosocial-cert-deploy-hook))))))) (define %nginx-cert-deploy-hook (program-file "nginx-cert-deploy-hook" #~(let ((pid (call-with-input-file "/var/run/nginx/pid" read))) (kill pid SIGHUP)))) (define %gotosocial-cert-deploy-hook (program-file "gotosocial-cert-deploy-hook" (with-imported-modules '((guix build utils)) #~(begin (use-module (guix build utils)) (invoke "herd" "restart" "gotosocial"))))) (define (gotosocial-nginx config) (match-record config (nginx? https? host run-dir) (if (not nginx?) '() (list (nginx-server-configuration (listen (if https? '("443 ssl") '("80"))) (server-name (list host)) (ssl-certificate (if https? (string-append "/etc/letsencrypt/live/" host "/fullchain.pem") #f)) (ssl-certificate-key (if https? (string-append "/etc/letsencrypt/live/" host "/privkey.pem") #f)) (locations (list (nginx-location-configuration (uri "/") (body `(,(string-append "proxy_pass http://unix:" run-dir "/gotosocial.socket;") "proxy_set_header Host $host;" "proxy_set_header X-Real_IP $remote_addr;" "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;" "proxy_set_header X-Forwarded-Proto $scheme;")))))))))) (define (gotosocial-activation config) (match-record config (work-dir run-dir) #~(begin (use-modules (guix build utils) (ice-9 string-fun)) (let* ((user (getpw "gotosocial")) (user-id (passwd:uid user)) (group-id (passwd:gid user))) (mkdir-p #$work-dir) (mkdir-p #$run-dir) (chown #$work-dir user-id group-id) (chown #$run-dir user-id group-id))))) (define (gotosocial-accounts config) (match-record config (work-dir) (list (user-group (name "gotosocial") (system? #t)) (user-account (name "gotosocial") (system? #t) (group "gotosocial") (comment "GoToSocial server user") (home-directory work-dir) (shell (file-append bash-minimal "/bin/bash")))))) (define-public gotosocial-service-type (service-type (name 'gotosocial) (extensions (list (service-extension shepherd-root-service-type gotosocial-shepherd-service) (service-extension account-service-type gotosocial-accounts) (service-extension activation-service-type gotosocial-activation) (service-extension nginx-service-type gotosocial-nginx) (service-extension certbot-service-type gotosocial-certbot) (service-extension postgresql-role-service-type gotosocial-postgresql-roles))) (description "Runs GoToSocial") (default-value (gotosocial-configuration))))