Compare commits

...

3 Commits

Author SHA1 Message Date
751741c915
Poista READMEstä maininta korjattuun vikaan
ASCII-rajoite --address-valitsimessa korjattiin versiossa
c4c9acc966
2025-05-14 15:58:34 +03:00
b9941268b7
Jatka lähettämistä yksittäisistä lähetysvirheistä huolimatta
Yksi ainoa lähetysvirhe riitti aiemmin kaatamaan tiedotejärjestelmän
postitussilmukan. Tällä muutoksella lähetysvirheet tulostetaan
virhelokiin ja ne aiheuttaneet viestit unohdetaan.
2025-05-14 15:45:19 +03:00
7a7022eba4
Lisää Guix-järjestelmäpalvelu 2025-05-08 21:37:13 +03:00
3 changed files with 154 additions and 9 deletions

View File

@ -0,0 +1,142 @@
(define-module (tiedote-md-service)
#:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu services base)
#:use-module (gnu system shadow)
#:use-module (gnu services)
#:use-module (guix records)
#:use-module (guix gexp)
#:use-module (tiedote-md-package)
#:use-module (gnu packages bash)
#:export (tiedote.md-configuration
tiedote.md-service-type))
(define-maybe string)
(define-configuration/no-serialization tiedote.md-configuration
(email-address
(string)
"Email address to send messages as. Mail to this address must be routed to
the tiedote.md user on this system.")
(sender-name
(string)
"Name to send messages as.")
(remote-url
(string)
"URL of the Git repository to mirror.")
(remote-branch
maybe-string
"Git branch to read messages from.")
(sendmail-path
(string "/run/privileged/bin/sendmail")
"Path to a sendmail compatible mailing executable.")
(webhook-port
(integer 3000)
"Port to listen on for webhook notifications.")
(socket-path
(string "/run/tiedote.md/acid.sock")
"Socket on which tiedote.md should listen on.")
(state-directory
(string "/var/lib/tiedote.md")
"Directory for persistent storage."))
(define (tiedote.md-accounts config)
(match-record config <tiedote.md-configuration>
(state-directory)
(list (user-group
(name "tiedote.md")
(system? #t))
(user-account
(name "tiedote.md")
(system? #t)
(group "tiedote.md")
(comment "Tiedote.md service user")
(home-directory state-directory)
(shell (file-append bash-minimal "/bin/bash"))))))
(define (make-tiedote.md-server-script config)
(match-record config <tiedote.md-configuration>
(socket-path email-address sender-name webhook-port sendmail-path
remote-branch remote-url state-directory)
(program-file "tiedote.md-server"
#~(begin
; use utf8 before starting tiedote.md to be able to pass utf characters as arguments
(setlocale LC_ALL "C.UTF-8")
; use utf8 after exec so that ghc text library doesn't crash from IO
(setenv "LANG" "C.UTF-8")
(let ((tiedote.md #$(file-append tiedote-md "/bin/tiedote.md"))
(repo-path (string-append #$state-directory "/git-repo")))
(apply execl
`(,tiedote.md
,tiedote.md
"--address" #$email-address
"--sender-name" #$sender-name
"--sendmail" #$sendmail-path
"--socket" #$socket-path
"server"
"--repository" ,repo-path
"--port" #$(number->string webhook-port)
"--remote-url" #$remote-url
,@(if #$(maybe-value-set? remote-branch)
(list "--remote-branch" #$remote-branch)
'()))))))))
(define (tiedote.md-shepherd-service config)
(match-record config <tiedote.md-configuration>
(state-directory socket-path)
(list (shepherd-service
(documentation "A simple mass email system")
(requirement '(networking user-processes))
(provision '(tiedote.md))
(start #~(let* ((user (getpw "tiedote.md"))
(uid (passwd:uid user))
(gid (passwd:gid user))
(socket-directory (dirname #$socket-path)))
(begin
(mkdir-p socket-directory)
(chown socket-directory uid gid)
(make-forkexec-constructor (list #$(make-tiedote.md-server-script config))
#:user "tiedote.md"
#:group "tiedote.md"
#:environment-variables
(cons* "GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt"
(default-environment-variables))
#:directory #$state-directory))))
(stop #~(make-kill-destructor))))))
(define (tiedote.md-activation config)
(match-record config <tiedote.md-configuration>
(state-directory email-address sender-name sendmail-path socket-path)
(let* ((tiedote.md (file-append tiedote-md "/bin/tiedote.md"))
(receive-exec (list tiedote.md ; file to exec
tiedote.md ; arg $0
"--address" email-address
"--sender-name" sender-name
"--sendmail" sendmail-path
"--socket" socket-path
"receive"))
(receive-script (program-file "tiedote.md-receive"
#~(begin
(setlocale LC_ALL "C.UTF-8")
(setenv "LANG" "C.UTF-8")
(apply execl '#$receive-exec))))
(forward-file (mixed-text-file "dot-forward" "|" receive-script)))
#~(let* ((.forward (string-append #$state-directory "/.forward"))
(user (getpw "tiedote.md"))
(uid (passwd:uid user))
(gid (passwd:gid user)))
(if (file-exists? .forward)
(delete-file .forward))
; smtpd does not follow symbolic links and requires .forward files to
; be owned by the recipient
(copy-file #$forward-file .forward)
(chown .forward uid gid)))))
(define tiedote.md-service-type
(service-type
(name 'tiedote.md)
(extensions
(list (service-extension account-service-type tiedote.md-accounts)
(service-extension activation-service-type tiedote.md-activation)
(service-extension shepherd-root-service-type tiedote.md-shepherd-service)))
(description "A simple mass email system")))

View File

@ -41,9 +41,7 @@ lähettämisestä ja Git-tietovaraston päivittämisestä, kun se saa muutoksist
ilmoituksen HTTP POST -pyyntönä (esim. Git-palvelun webhookista). ilmoituksen HTTP POST -pyyntönä (esim. Git-palvelun webhookista).
Palvelimen voi käynnistää komennolla `tiedote.md server`. Mahdolliset asetukset Palvelimen voi käynnistää komennolla `tiedote.md server`. Mahdolliset asetukset
saa listattua lisäämällä komentoon `--help` valitsimen. `--address` saa listattua lisäämällä komentoon `--help` valitsimen.
valitsimesta tulee huomata, että osoitteessa voi käyttää vain ASCII-merkkejä
(ks. [#15](https://git.olarinmaensamoojat.fi/OMS/tiedote.md/issues/15)).
Sähköpostin lähettämiseen tarvitaan `sendmail`-yhteensopiva MTA (mail transfer Sähköpostin lähettämiseen tarvitaan `sendmail`-yhteensopiva MTA (mail transfer
agent, sähköpostin välitysohjelma), jollaisen useimmat sähköpostipalvelimet agent, sähköpostin välitysohjelma), jollaisen useimmat sähköpostipalvelimet

View File

@ -4,7 +4,6 @@
module TiedoteMD.Send where module TiedoteMD.Send where
import Control.Concurrent (threadDelay) import Control.Concurrent (threadDelay)
import Control.Exception (throwIO)
import Control.Lens (set, _Just) import Control.Lens (set, _Just)
import Control.Monad (forever, unless) import Control.Monad (forever, unless)
import Data.Acid (AcidState, query, update) import Data.Acid (AcidState, query, update)
@ -15,8 +14,7 @@ import Data.MIME.Charset (defaultCharsets)
import Data.Set (Set) import Data.Set (Set)
import Data.Time (getCurrentTime) import Data.Time (getCurrentTime)
import System.Exit (ExitCode(..)) import System.Exit (ExitCode(..))
import System.Exit.Codes (codeTempFail) import System.IO (hClose, stdout, stderr)
import System.IO (hClose, stdout)
import System.Process (CreateProcess(..), StdStream(..), waitForProcess, createProcess, proc) import System.Process (CreateProcess(..), StdStream(..), waitForProcess, createProcess, proc)
import System.Random (getStdRandom, uniform) import System.Random (getStdRandom, uniform)
@ -25,18 +23,25 @@ import qualified Data.IMF as IMF
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T
import TiedoteMD.State import TiedoteMD.State
import TiedoteMD.Types import TiedoteMD.Types
sendmail :: FilePath -> LBS.ByteString -> IO () sendmail :: FilePath -> LBS.ByteString -> IO ()
sendmail path bs = do sendmail path bs = do
(Just stdin, _, _, processHandle) <- createProcess (proc path ["-t"]) {std_in = CreatePipe} (Just stdin, _, Just stderr, processHandle) <- createProcess
(proc path ["-t"]) {std_in = CreatePipe, std_err = CreatePipe}
LBS.hPut stdin bs LBS.hPut stdin bs
hClose stdin hClose stdin
errors <- T.hGetContents stderr
exitCode <- waitForProcess processHandle exitCode <- waitForProcess processHandle
unless (exitCode `elem` [ExitSuccess, codeTempFail]) $ -- TODO: handle System.Exit.Codes.codeTempFail differently from other failures
throwIO $ ProcessError (T.pack path) exitCode unless (exitCode == ExitSuccess) $ do
T.hPutStr System.IO.stderr $
"Sending mail with " <> (T.pack path) <> " failed with " <> (T.pack $ show exitCode)
unless (T.null errors) $ T.hPutStrLn System.IO.stderr $ ":\n" <> errors
T.hPutStrLn System.IO.stderr ""
manageQueue :: AcidState State -> Mailbox -> FilePath -> IO () manageQueue :: AcidState State -> Mailbox -> FilePath -> IO ()
manageQueue acid sender sendmailPath = forever $ do manageQueue acid sender sendmailPath = forever $ do