147 lines
4.2 KiB
Haskell
147 lines
4.2 KiB
Haskell
module Main where
|
|
|
|
import Control.Logger.Simple qualified as Log
|
|
import DB
|
|
import Data.Aeson qualified as A
|
|
import Data.Functor ((<&>))
|
|
import Data.SecureMem (secureMemFromByteString)
|
|
import Data.Text qualified as T
|
|
import Data.Text.Encoding qualified as T
|
|
import Data.Text.IO qualified as T
|
|
import Database.Sqlite.Easy qualified as Sqlite
|
|
import Network.Wai.Handler.Warp (Port, run)
|
|
import Network.Wai.Middleware.HttpAuth (basicAuth)
|
|
import Network.Wai.Middleware.RequestLogger qualified as Logger
|
|
import Network.Wai.Middleware.RequestSizeLimit qualified as Limit
|
|
import Network.Wai.Middleware.Routed qualified as Wai
|
|
import Routes
|
|
import Fedi qualified as Fedi
|
|
import System.Environment (getArgs, lookupEnv)
|
|
import Web.Twain qualified as Twain
|
|
|
|
data Command
|
|
= Serve
|
|
| Insert FilePath
|
|
|
|
main :: IO ()
|
|
main = do
|
|
command <-
|
|
getArgs >>= \case
|
|
["insert", file] -> pure (Insert file)
|
|
["serve"] -> pure Serve
|
|
_ -> usageError
|
|
|
|
case command of
|
|
Insert file -> do
|
|
insertNoteFromFile file
|
|
Serve ->
|
|
serve
|
|
|
|
insertNoteFromFile :: FilePath -> IO ()
|
|
insertNoteFromFile file = do
|
|
connStr <- maybe "/tmp/fediserve_sqlite_db.sqlite" fromString <$> lookupEnv "FEDI_CONN_STRING"
|
|
content <- T.readFile file
|
|
|
|
detailsFile <-
|
|
lookupEnv "FEDI_DETAILS"
|
|
<&> maybe (error "missing FEDI_DETAILS") id
|
|
|
|
details <-
|
|
A.eitherDecodeFileStrict detailsFile
|
|
<&> either (\err -> error $ "could not read file '" <> detailsFile <> "'.\n" <> err) id
|
|
|
|
db <- mkDB connStr details
|
|
|
|
note <-
|
|
db.insertNote
|
|
NoteEntry
|
|
{ content = content
|
|
, inReplyTo = Nothing
|
|
, name = Nothing
|
|
, url = Nothing
|
|
}
|
|
|
|
putStrLn "Inserted."
|
|
print note
|
|
|
|
serve :: IO ()
|
|
serve = Log.withGlobalLogging (Log.LogConfig (Just "fedi.log") True) do
|
|
logLevel <- maybe Log.LogDebug read <$> lookupEnv "FEDI_LOG_LEVEL"
|
|
Log.setLogLevel logLevel
|
|
|
|
auth <- fmap (T.splitOn "," . T.pack) <$> lookupEnv "FEDI_AUTH"
|
|
authMiddleware <-
|
|
case auth of
|
|
Nothing -> do
|
|
Log.logInfo "Starting server with authentication disabled."
|
|
pure id
|
|
Just [user, pass] -> do
|
|
Log.logInfo "Starting server with authentication enabled,"
|
|
let
|
|
username = secureMemFromByteString $ T.encodeUtf8 user
|
|
password = secureMemFromByteString $ T.encodeUtf8 pass
|
|
pure $
|
|
basicAuth
|
|
( \u p ->
|
|
pure $
|
|
secureMemFromByteString u == username
|
|
&& secureMemFromByteString p == password
|
|
)
|
|
"My Fediserve"
|
|
Just {} -> usageError
|
|
|
|
fediPort <- maybe 3001 read <$> lookupEnv "FEDI_PORT"
|
|
conn <- maybe "/tmp/fediserve_sqlite_db.sqlite" fromString <$> lookupEnv "FEDI_CONN_STRING"
|
|
Log.logInfo $ "and with connection string " <> Sqlite.unConnectionString conn <> "."
|
|
|
|
runServer fediPort authMiddleware =<< mkFediApp conn
|
|
|
|
usageError :: err
|
|
usageError =
|
|
errorWithoutStackTrace $
|
|
unlines
|
|
[ "Usage: fedi [ insert <FILE> | serve ]"
|
|
, "Env vars:"
|
|
, " - FEDI_PORT=<PORT>"
|
|
, " - FEDI_DETAILS=<FILE>"
|
|
, " - FEDI_CONN_STRING=<SQLITE_CONN_STR>"
|
|
, " - FEDI_AUTH=<user>,<password>"
|
|
, " - FEDI_LOG=[ LogTrace | LogDebug | LogInfo | LogNote | LogWarn | LogError ]"
|
|
]
|
|
|
|
-- | Run server at at specific port.
|
|
runServer :: Port -> Twain.Middleware -> Twain.Application -> IO ()
|
|
runServer port authMiddleware app = do
|
|
Log.logInfo $
|
|
T.unwords
|
|
[ "Running fedi at"
|
|
, "http://localhost:" <> T.pack (show port)
|
|
, "(ctrl-c to quit)"
|
|
]
|
|
auth <- Wai.routedMiddleware matchAdmin <$> pure authMiddleware
|
|
run port $
|
|
( Logger.logStdoutDev
|
|
. Limit.requestSizeLimitMiddleware Limit.defaultRequestSizeLimitSettings
|
|
. auth
|
|
)
|
|
app
|
|
|
|
matchAdmin :: [T.Text] -> Bool
|
|
matchAdmin = any (== "admin")
|
|
|
|
-- | Application description.
|
|
mkFediApp :: Sqlite.ConnectionString -> IO Twain.Application
|
|
mkFediApp connStr = do
|
|
detailsFile <-
|
|
lookupEnv "FEDI_DETAILS"
|
|
<&> maybe (error "missing FEDI_DETAILS") id
|
|
|
|
details <- Fedi.readUserDetailsFile detailsFile
|
|
|
|
db <- mkDB connStr details
|
|
|
|
pure $
|
|
foldr
|
|
($)
|
|
(Twain.notFound $ Twain.send $ Twain.text "Error: not found.")
|
|
(routes db detailsFile)
|