Compare commits

...

3 Commits

Author SHA1 Message Date
834521efb1
Lisää Guix-järjestelmäpalvelu 2025-04-19 14:03:39 +03:00
b2da3220c9
Alusta Git-tietovaranto automaattisesti 2025-04-16 15:23:23 +03:00
33c2269671
Poista turha --force gitPull funktiosta
Valitsin --force vaikuttaa git fetch -komennon toimintaan vain kun
ylikirjoitetaan paikallisia kehityshaaroja, mitä gitPull komento ei tee.
2025-04-16 14:49:36 +03:00
5 changed files with 137 additions and 12 deletions

View File

@ -0,0 +1,112 @@
(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 (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.")
(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 (tiedote.md-shepherd-service config)
(match-record config <tiedote.md-configuration>
(state-directory socket-path email-address sender-name port sendmail-path
remote-branch remote-url)
(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))
(repo-path (string-append #$state-directory "/git-repo")))
(begin
(mkdir-p socket-directory)
(chown socket-directory uid gid)
(make-forkexec-constructor
`(,#$(file-append tiedote.md "/bin/tiedote.md")
"--address" ,#$email-address
"--sender-name" ,#$sender-name
"--sendmail" ,#$sendmail-path
"--socket" ,#$socket-path
"--repository" ,repo-path
"--port" ,#$(number->string port)
"--remote-url" ,#$remote-url
,@(if #$(maybe-value-set? remote-branch)
(list "--remote-branch" #$remote-branch)
'())
"server")
#:user "tiedote.md"
#:group "tiedote.md"
#:directory #$state-directory))))
(stop #~(make-kill-destructor))))))
(define (tiedote.md-activation config)
(match-record config <tiedote.md-configuration>
(state-directory)
(let* ((receive-exec (list (file-append tiedote.md "/bin/tiedote.md") ; file to exec
(file-append tiedote.md "/bin/tiedote.md") ; arg $0
"--address" email-address
"--sender-name" sender-name
"--sendmail" sendmail-path
"--socket" socket-path))
(receive-script (program-file "tiedote.md-receive"
#~(apply execl #$receive-exec)))
(forward-file (mixed-text-file ".forward" "|" receive-script)))
#~(symlink #$forward-file
(string-append #$state-directory "/.forward")))))
(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

@ -11,6 +11,7 @@ import Control.Exception (throwIO)
import Control.Monad (void, forever) import Control.Monad (void, forever)
import Data.Acid (AcidState(..), openLocalState, createCheckpoint, createArchive) import Data.Acid (AcidState(..), openLocalState, createCheckpoint, createArchive)
import Data.Acid.Remote (skipAuthenticationCheck, acidServerSockAddr, openRemoteStateSockAddr, skipAuthenticationPerform) import Data.Acid.Remote (skipAuthenticationCheck, acidServerSockAddr, openRemoteStateSockAddr, skipAuthenticationPerform)
import Data.FileStore (gitFileStore, initialize)
import Data.IMF (Mailbox(..), parse, mailbox) import Data.IMF (Mailbox(..), parse, mailbox)
import Data.MIME.Charset (defaultCharsets) import Data.MIME.Charset (defaultCharsets)
import Data.Text.Encoding (encodeUtf8) import Data.Text.Encoding (encodeUtf8)
@ -33,15 +34,16 @@ import TiedoteMD.State
import TiedoteMD.Types import TiedoteMD.Types
import TiedoteMD.Debug import TiedoteMD.Debug
serverMain :: SockAddr -> Mailbox -> FilePath -> FilePath -> Int -> Int -> IO () serverMain :: SockAddr -> Mailbox -> FilePath -> GitOrigin -> FilePath -> Int -> Int -> IO ()
serverMain socket mail repoPath sendmailPath checkpointDelay port = do serverMain socket mail repoPath origin sendmailPath checkpointDelay port = do
initialize $ gitFileStore repoPath
acid <- openLocalState $ emptyState acid <- openLocalState $ emptyState
_ <- forkIO $ acidServerSockAddr skipAuthenticationCheck socket acid _ <- forkIO $ acidServerSockAddr skipAuthenticationCheck socket acid
queueMessages acid queueMessages acid
_ <- forkIO $ scotty port $ do _ <- forkIO $ scotty port $ do
defaultHandler $ liftAndCatchIO . LT.putStrLn -- Don't return exceptions to clients defaultHandler $ liftAndCatchIO . LT.putStrLn -- Don't return exceptions to clients
post "/" $ liftAndCatchIO $ void $ forkIO $ updateMessages acid repoPath post "/" $ liftAndCatchIO $ void $ forkIO $ updateMessages acid repoPath origin
updateMessages acid repoPath updateMessages acid repoPath origin
_ <- forkIO $ manageQueueingMessages acid _ <- forkIO $ manageQueueingMessages acid
_ <- forkIO $ managePreviews acid mail sendmailPath _ <- forkIO $ managePreviews acid mail sendmailPath
_ <- forkIO $ manageQueue acid mail sendmailPath _ <- forkIO $ manageQueue acid mail sendmailPath
@ -69,7 +71,7 @@ archiveMain socket = do
createArchive acid createArchive acid
data Config = Config { socketPath :: FilePath, mail :: Mailbox, sendmailCommand :: String, runMode :: RunMode } deriving Show data Config = Config { socketPath :: FilePath, mail :: Mailbox, sendmailCommand :: String, runMode :: RunMode } deriving Show
data RunMode = Server { repoPath :: FilePath, portNumber :: Int, checkpointDelay :: Int } | Receive | Print | Archive deriving Show data RunMode = Server { repoPath :: FilePath, origin :: GitOrigin, portNumber :: Int, checkpointDelay :: Int } | Receive | Print | Archive deriving Show
config :: Maybe Mailbox -> Parser Config config :: Maybe Mailbox -> Parser Config
config mail = Config config mail = Config
@ -84,6 +86,9 @@ config mail = Config
serverCommand :: Parser RunMode serverCommand :: Parser RunMode
serverCommand = Server serverCommand = Server
<$> strOption (long "repository" <> short 'r' <> metavar "DIRECTORY" <> value "/var/lib/tiedote.md/git-repo" <> showDefault <> help "Path to git repository") <$> strOption (long "repository" <> short 'r' <> metavar "DIRECTORY" <> value "/var/lib/tiedote.md/git-repo" <> showDefault <> help "Path to git repository")
<*> (GitOrigin
<$> strOption (long "remote-url" <> short 'u' <> metavar "URL" <> help "Url to fetch updated messages from")
<*> optional (strOption (long "remote-branch" <> short 'b' <> metavar "NAME" <> help "Branch to fetch updated messages from")))
<*> option auto (long "port" <> short 'p' <> metavar "PORT" <> value 3000 <> showDefault <> help "TCP port number to listen on for webhook notifications") <*> option auto (long "port" <> short 'p' <> metavar "PORT" <> value 3000 <> showDefault <> help "TCP port number to listen on for webhook notifications")
<*> option auto (long "checkpoint-delay" <> metavar "MINUTES" <> value (60 * 24 * 7) <> help "Number of minutes to wait between creating snapshots.") <*> option auto (long "checkpoint-delay" <> metavar "MINUTES" <> value (60 * 24 * 7) <> help "Number of minutes to wait between creating snapshots.")
@ -111,4 +116,4 @@ main = do
Config {runMode = Receive, ..} -> clientMain (SockAddrUnix socketPath) mail sendmailPath Config {runMode = Receive, ..} -> clientMain (SockAddrUnix socketPath) mail sendmailPath
Config {runMode = Print, ..} -> printStateMain (SockAddrUnix socketPath) Config {runMode = Print, ..} -> printStateMain (SockAddrUnix socketPath)
Config {runMode = Archive, ..} -> archiveMain (SockAddrUnix socketPath) Config {runMode = Archive, ..} -> archiveMain (SockAddrUnix socketPath)
Config {runMode = Server {..}, ..} -> serverMain (SockAddrUnix socketPath) mail repoPath sendmailPath checkpointDelay portNumber Config {runMode = Server {..}, ..} -> serverMain (SockAddrUnix socketPath) mail repoPath origin sendmailPath checkpointDelay portNumber

View File

@ -1,4 +1,5 @@
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NamedFieldPuns #-}
module TiedoteMD.Git where module TiedoteMD.Git where
@ -9,6 +10,7 @@ import Control.Exception (throwIO)
import Control.Monad (liftM2, unless) import Control.Monad (liftM2, unless)
import Data.ByteString.Lazy.UTF8 (toString) import Data.ByteString.Lazy.UTF8 (toString)
import Data.FileStore.Types (FileStoreError(..)) import Data.FileStore.Types (FileStoreError(..))
import Data.List (singleton)
import System.Directory (getTemporaryDirectory, removeFile) import System.Directory (getTemporaryDirectory, removeFile)
import System.Environment (getEnvironment) import System.Environment (getEnvironment)
import System.Exit (ExitCode(..)) import System.Exit (ExitCode(..))
@ -19,9 +21,11 @@ import qualified Control.Exception as E
import qualified Data.ByteString as S import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy as B
gitPull :: FilePath -> IO () import TiedoteMD.Types (GitOrigin(..))
gitPull repo = do
(exit, err, _) <- runGitCommand repo "fetch" ["--force"] gitPull :: FilePath -> GitOrigin -> IO ()
gitPull repo GitOrigin {url, branch} = do
(exit, err, _) <- runGitCommand repo "fetch" (url : (maybe [] singleton branch))
unless (exit == ExitSuccess) $ throwIO $ UnknownError $ "git-fetch failed: " <> err unless (exit == ExitSuccess) $ throwIO $ UnknownError $ "git-fetch failed: " <> err
(exit', err', _) <- runGitCommand repo "reset" ["--hard","FETCH_HEAD"] (exit', err', _) <- runGitCommand repo "reset" ["--hard","FETCH_HEAD"]
unless (exit' == ExitSuccess) $ throwIO $ UnknownError $ "git-reset failed: " <> err' unless (exit' == ExitSuccess) $ throwIO $ UnknownError $ "git-reset failed: " <> err'

View File

@ -52,9 +52,9 @@ import TiedoteMD.State
import TiedoteMD.Templates import TiedoteMD.Templates
import TiedoteMD.Types import TiedoteMD.Types
updateMessages :: AcidState State -> FilePath -> IO () updateMessages :: AcidState State -> FilePath -> GitOrigin -> IO ()
updateMessages acid repoPath = updateMessages acid repoPath origin =
gitPull repoPath >> readMessageFiles (gitFileStore repoPath) >>= update acid . UpdateState gitPull repoPath origin >> readMessageFiles (gitFileStore repoPath) >>= update acid . UpdateState
readMessageFiles :: FileStore -> IO [Message] readMessageFiles :: FileStore -> IO [Message]
readMessageFiles store = do readMessageFiles store = do

View File

@ -7,6 +7,7 @@
module TiedoteMD.Types module TiedoteMD.Types
( Email(..) ( Email(..)
, Error(..) , Error(..)
, GitOrigin(..)
, MailID , MailID
, MediaPart(..) , MediaPart(..)
, Message(..) , Message(..)
@ -141,6 +142,9 @@ messageIDToMailID = MailID . renderMessageID
renderMailID :: MailID -> ByteString renderMailID :: MailID -> ByteString
renderMailID = renderMessageID . mailIDToMessageID renderMailID = renderMessageID . mailIDToMessageID
data GitOrigin = GitOrigin { url :: String, branch :: Maybe String }
deriving (Show)
data Error = PandocError PandocError data Error = PandocError PandocError
| NoMeta Text | NoMeta Text
| InvalidTime Text | InvalidTime Text