242 lines
7.9 KiB
Common Lisp
242 lines
7.9 KiB
Common Lisp
;; -*-lisp-*-
|
|
;; Configuration management for Asteroid Radio
|
|
;; This file centralizes all configuration parameters and provides
|
|
;; mechanisms to load them from environment variables or config files
|
|
|
|
(in-package :asteroid)
|
|
|
|
;;; Configuration Structure
|
|
;;; All configuration is loaded from environment variables with sensible defaults
|
|
|
|
(defclass asteroid-config ()
|
|
((server-port
|
|
:initarg :server-port
|
|
:accessor config-server-port
|
|
:initform 8080
|
|
:documentation "HTTP server port")
|
|
|
|
(music-library-path
|
|
:initarg :music-library-path
|
|
:accessor config-music-library-path
|
|
:initform nil
|
|
:documentation "Path to music library directory")
|
|
|
|
(stream-base-url
|
|
:initarg :stream-base-url
|
|
:accessor config-stream-base-url
|
|
:initform "http://localhost:8000"
|
|
:documentation "Base URL for Icecast stream server")
|
|
|
|
(icecast-admin-user
|
|
:initarg :icecast-admin-user
|
|
:accessor config-icecast-admin-user
|
|
:initform "admin"
|
|
:documentation "Icecast admin username")
|
|
|
|
(icecast-admin-password
|
|
:initarg :icecast-admin-password
|
|
:accessor config-icecast-admin-password
|
|
:initform nil
|
|
:documentation "Icecast admin password (MUST be set via environment)")
|
|
|
|
(supported-formats
|
|
:initarg :supported-formats
|
|
:accessor config-supported-formats
|
|
:initform '("mp3" "flac" "ogg" "wav")
|
|
:documentation "List of supported audio formats")
|
|
|
|
(max-history-size
|
|
:initarg :max-history-size
|
|
:accessor config-max-history-size
|
|
:initform 50
|
|
:documentation "Maximum number of tracks in stream history")
|
|
|
|
(database-backend
|
|
:initarg :database-backend
|
|
:accessor config-database-backend
|
|
:initform :i-lambdalite
|
|
:documentation "Database backend to use (:i-lambdalite or :postgresql)")
|
|
|
|
(postgres-host
|
|
:initarg :postgres-host
|
|
:accessor config-postgres-host
|
|
:initform "localhost"
|
|
:documentation "PostgreSQL host")
|
|
|
|
(postgres-port
|
|
:initarg :postgres-port
|
|
:accessor config-postgres-port
|
|
:initform 5432
|
|
:documentation "PostgreSQL port")
|
|
|
|
(postgres-database
|
|
:initarg :postgres-database
|
|
:accessor config-postgres-database
|
|
:initform "asteroid"
|
|
:documentation "PostgreSQL database name")
|
|
|
|
(postgres-user
|
|
:initarg :postgres-user
|
|
:accessor config-postgres-user
|
|
:initform "asteroid"
|
|
:documentation "PostgreSQL username")
|
|
|
|
(postgres-password
|
|
:initarg :postgres-password
|
|
:accessor config-postgres-password
|
|
:initform nil
|
|
:documentation "PostgreSQL password (MUST be set via environment)")
|
|
|
|
(tls-enabled
|
|
:initarg :tls-enabled
|
|
:accessor config-tls-enabled
|
|
:initform nil
|
|
:documentation "Whether TLS/HTTPS is enabled")
|
|
|
|
(tls-certificate-path
|
|
:initarg :tls-certificate-path
|
|
:accessor config-tls-certificate-path
|
|
:initform nil
|
|
:documentation "Path to TLS certificate file")
|
|
|
|
(tls-key-path
|
|
:initarg :tls-key-path
|
|
:accessor config-tls-key-path
|
|
:initform nil
|
|
:documentation "Path to TLS private key file"))
|
|
(:documentation "Configuration object for Asteroid Radio"))
|
|
|
|
;;; Global configuration instance
|
|
(defvar *config* nil
|
|
"Global configuration instance")
|
|
|
|
;;; Environment variable helpers
|
|
(defun getenv (name &optional default)
|
|
"Get environment variable NAME, or DEFAULT if not set"
|
|
(let ((value (uiop:getenv name)))
|
|
(if (and value (not (string= value "")))
|
|
value
|
|
default)))
|
|
|
|
(defun parse-integer-safe (string &optional default)
|
|
"Parse STRING as integer, return DEFAULT on error"
|
|
(handler-case
|
|
(parse-integer string)
|
|
(error () default)))
|
|
|
|
(defun parse-boolean (string)
|
|
"Parse STRING as boolean (true/false, yes/no, 1/0)"
|
|
(when string
|
|
(member (string-downcase string)
|
|
'("true" "yes" "1" "t" "y")
|
|
:test #'string=)))
|
|
|
|
;;; Configuration loading
|
|
(defun load-config-from-env ()
|
|
"Load configuration from environment variables"
|
|
(make-instance 'asteroid-config
|
|
:server-port (parse-integer-safe
|
|
(getenv "ASTEROID_SERVER_PORT")
|
|
8080)
|
|
|
|
:music-library-path (or (getenv "ASTEROID_MUSIC_PATH")
|
|
(merge-pathnames "music/library/"
|
|
(asdf:system-source-directory :asteroid)))
|
|
|
|
:stream-base-url (getenv "ASTEROID_STREAM_URL" "http://localhost:8000")
|
|
|
|
:icecast-admin-user (getenv "ICECAST_ADMIN_USER" "admin")
|
|
|
|
:icecast-admin-password (getenv "ICECAST_ADMIN_PASSWORD")
|
|
|
|
:supported-formats '("mp3" "flac" "ogg" "wav")
|
|
|
|
:max-history-size (parse-integer-safe
|
|
(getenv "ASTEROID_MAX_HISTORY")
|
|
50)
|
|
|
|
:database-backend (let ((backend (getenv "ASTEROID_DB_BACKEND")))
|
|
(cond
|
|
((string-equal backend "postgresql") :postgresql)
|
|
((string-equal backend "postgres") :postgresql)
|
|
(t :i-lambdalite)))
|
|
|
|
:postgres-host (getenv "POSTGRES_HOST" "localhost")
|
|
|
|
:postgres-port (parse-integer-safe
|
|
(getenv "POSTGRES_PORT")
|
|
5432)
|
|
|
|
:postgres-database (getenv "POSTGRES_DB" "asteroid")
|
|
|
|
:postgres-user (getenv "POSTGRES_USER" "asteroid")
|
|
|
|
:postgres-password (getenv "POSTGRES_PASSWORD")
|
|
|
|
:tls-enabled (parse-boolean (getenv "ASTEROID_TLS_ENABLED"))
|
|
|
|
:tls-certificate-path (getenv "ASTEROID_TLS_CERT")
|
|
|
|
:tls-key-path (getenv "ASTEROID_TLS_KEY")))
|
|
|
|
(defun init-config ()
|
|
"Initialize global configuration"
|
|
(unless *config*
|
|
(setf *config* (load-config-from-env)))
|
|
|
|
;; Validate critical configuration
|
|
(validate-config *config*)
|
|
|
|
*config*)
|
|
|
|
(defun validate-config (config)
|
|
"Validate configuration and warn about missing critical values"
|
|
(unless (config-icecast-admin-password config)
|
|
(warn "ICECAST_ADMIN_PASSWORD not set! Icecast status checks will fail."))
|
|
|
|
(when (eq (config-database-backend config) :postgresql)
|
|
(unless (config-postgres-password config)
|
|
(warn "POSTGRES_PASSWORD not set! PostgreSQL connection will fail.")))
|
|
|
|
(when (config-tls-enabled config)
|
|
(unless (and (config-tls-certificate-path config)
|
|
(config-tls-key-path config))
|
|
(error "TLS enabled but certificate or key path not configured!")))
|
|
|
|
t)
|
|
|
|
;;; Convenience accessors for backward compatibility
|
|
(defun get-config-value (key)
|
|
"Get configuration value by key (for backward compatibility)"
|
|
(unless *config*
|
|
(init-config))
|
|
|
|
(case key
|
|
(:server-port (config-server-port *config*))
|
|
(:music-library-path (config-music-library-path *config*))
|
|
(:stream-base-url (config-stream-base-url *config*))
|
|
(:icecast-admin-user (config-icecast-admin-user *config*))
|
|
(:icecast-admin-password (config-icecast-admin-password *config*))
|
|
(:supported-formats (config-supported-formats *config*))
|
|
(otherwise (error "Unknown configuration key: ~a" key))))
|
|
|
|
;;; Export configuration for display (without sensitive data)
|
|
(defun config-summary ()
|
|
"Return configuration summary (without passwords)"
|
|
(unless *config*
|
|
(init-config))
|
|
|
|
`(("Server Port" . ,(config-server-port *config*))
|
|
("Music Library" . ,(namestring (config-music-library-path *config*)))
|
|
("Stream URL" . ,(config-stream-base-url *config*))
|
|
("Icecast Admin User" . ,(config-icecast-admin-user *config*))
|
|
("Icecast Password Set" . ,(if (config-icecast-admin-password *config*) "Yes" "No"))
|
|
("Supported Formats" . ,(format nil "~{~a~^, ~}" (config-supported-formats *config*)))
|
|
("Database Backend" . ,(config-database-backend *config*))
|
|
("PostgreSQL Host" . ,(config-postgres-host *config*))
|
|
("PostgreSQL Port" . ,(config-postgres-port *config*))
|
|
("PostgreSQL Database" . ,(config-postgres-database *config*))
|
|
("PostgreSQL User" . ,(config-postgres-user *config*))
|
|
("PostgreSQL Password Set" . ,(if (config-postgres-password *config*) "Yes" "No"))
|
|
("TLS Enabled" . ,(if (config-tls-enabled *config*) "Yes" "No"))))
|