(define-module (services vaultwarden)
  #:use-module (gnu services)
  #:use-module (gnu services shepherd)
  #:use-module (gnu system shadow)
  #:use-module (gnu packages admin)
  #:use-module (vup vaultwarden)
  #:use-module (misc vault)
  #:use-module (guix records)
  #:use-module (guix modules)
  #:use-module (guix gexp)
  #:use-module (ice-9 match)
  #:use-module (ice-9 textual-ports)
  #:export (vaultwarden-configuration
            vaultwarden-service-type))

(define-record-type* <vaultwarden-configuration>
  vaultwarden-configuration make-vaultwarden-configuration
  vaultwarden-configuration?
  ;; <package>
  (vaultwarden            vaultwarden-conf-vaultwarden
                          (default vaultwarden))

  ;; string
  (data-folder            vaultwarden-conf-data-folder
                          (default "/var/vaultwarden"))

  ;; string
  (user                   vaultwarden-conf-user
                          (default "vaultwarden"))

  ;; number
  (port                   vaultwarden-conf-port)

  ;; string
  (database-url           vaultwarden-conf-database-url)

  ;; <package>
  (web-vault              vaultwarden-conf-web-vault
                          (default web-vault))

  ;; bool
  (websocket?             vaultwarden-conf-websocket?
                          (default #t))

  ;; string
  (log-level              vaultwarden-conf-loglevel
                          (default "info"))

  ;; bool
  (signups-allowed?       vaultwarden-conf-signups-allowed?
                          (default #f))

  ;; string
  (admin-token-file       vaultwarden-conf-admin-token-file)

  ;; string
  (domain                 vaultwarden-conf-domain))


(define* (vaultwarden-accounts config)
  (let* ((name (vaultwarden-conf-user config))
         (home-dir (vaultwarden-conf-data-folder config)))
   (list
     (user-group (name name) (system? #t))
     (user-account
      (name name)
      (group name)
      (system? #t)
      (comment "vaultwarden user")
      (home-directory home-dir)
      (shell (file-append shadow "/sbin/nologin"))))))

(define (vaultwarden-shepherd-service config)
  "Return a <shepherd-service> for vaultwarden with CONFIG."

  (define vault-config-file
    (computed-file
     "env"
     #~(begin
         (use-modules (ice-9 match))
         (use-modules (guix build utils))
         (call-with-output-file #$output
            (lambda (port)
              (display "# Generated by 'vault-service'.\n" port)
              (format port "DATA_FOLDER=~a\n" #$(vaultwarden-conf-data-folder config))
              (format port "DATABASE_URL=~a\n" #$(vaultwarden-conf-database-url config))
              (format port "WEB_VAULT_FOLDER=~a\n" #$(vaultwarden-conf-web-vault config))
              (format port "WEB_VAULT_ENABLED=true\n")
              (format port "WEBSOCKET_ENABLED=~a\n" (if #$(vaultwarden-conf-websocket? config) "true" "false"))
; default to stdout -> use shepherd logging
;              (format port "LOG_FILE=/var/log/vaultwarden.log\n")
              (format port "LOG_LEVEL=~a\n" #$(vaultwarden-conf-loglevel config))
              (format port "ROCKET_PORT=~a\n" #$(vaultwarden-conf-port config))
              (format port "USE_SYSLOG=false\n")
              (format port "SIGNUPS_ALLOWED=~a\n" (if #$(vaultwarden-conf-signups-allowed? config) "true" "false"))
              (format port "DOMAIN=~a\n" #$(vaultwarden-conf-domain config))
              #t)))
     #:options '(#:local-build? #t
                 #:modules ((guix build utils)))))

  (list (shepherd-service
         (documentation "vaultwarden server")
         (requirement '(networking))
         (provision '(vaultwarden))
         (start #~ (lambda _
                     (let* ((env-vars (list
                                       (string-append
                                        "ADMIN_TOKEN="
                                        (call-with-input-file
                                            #$(vaultwarden-conf-admin-token-file config)
                                            (@ (ice-9 textual-ports) get-string-all)))
                                       (string-append
                                         "ENV_FILE=" #$vault-config-file)))
                             (ctor (make-forkexec-constructor
                                    (list #$(file-append (vaultwarden-conf-vaultwarden config) "/bin/vaultwarden"))
                                    #:directory "/var/vaultwarden"
                                    #:user #$(vaultwarden-conf-user config)
                                    #:environment-variables env-vars
                                    #:log-file "/var/log/vaultwarden.log")))
                       (ctor))))
         (stop #~(make-kill-destructor))
         (auto-start? #t))))


(define (vaultwarden-activation config)
  #~(begin
      (use-modules (guix build utils))
      (let* ((data-dir #$(vaultwarden-conf-data-folder config))
             (username #$(vaultwarden-conf-user config))
             (user (getpwnam username)))
        (mkdir-p data-dir)
        (chown data-dir (passwd:uid user) (passwd:gid user)))))

(define vaultwarden-service-type
  (service-type (name 'vaultwarden)
                (description
                 "run vaultwarden server")
                (extensions
                 (list (service-extension shepherd-root-service-type
                                          vaultwarden-shepherd-service)
                       (service-extension account-service-type
                                          vaultwarden-accounts)
                       (service-extension activation-service-type
                                          vaultwarden-activation)))))
