chipburners_web/app/Main.hs

174 lines
5.8 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString.Lazy qualified as BL
import Data.Either (lefts, rights)
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Data.Text.Lazy qualified as TL
import Data.Time (MonthOfYear, Year, addDays, addUTCTime, fromGregorian, getCurrentTime, secondsToNominalDiffTime, toGregorian)
import Data.String (fromString)
import Data.Time.LocalTime
import Ical
import Interested qualified
import Lucid (renderText)
import Network.HTTP.Simple
import Network.HTTP.Types (status400)
import Network.Wai.Middleware.Static (staticPolicy, addBase, noDots, (>->))
import Network.Wai.Handler.Warp (setPort, setHost)
import Options.Applicative
( Parser,
auto,
execParser,
fullDesc,
help,
helper,
info,
long,
metavar,
option,
progDesc,
short,
showDefault,
strOption,
value,
(<**>),
)
import Render
import Text.Printf (printf)
import Translation (Lang (EN, ES), getLangAsString)
import Web.Scotty
import Web.Scotty.Cookie (defaultSetCookie, getCookie, setCookie, setCookieExpires, setCookieName, setCookieValue)
getFromUrl :: String -> IO T.Text
getFromUrl url = do
r <- parseRequest url
response <- httpLBS r
let rBody = getResponseBody response
let rText = TE.decodeUtf8 (BL.toStrict rBody)
pure $ T.replace "\n " " " $ T.replace "\r\n" "\n" rText
getLanguageHeader :: Maybe TL.Text -> Lang
getLanguageHeader (Just h)
| pre "es" h = ES
| pre "en" h = EN
| otherwise = ES
where
pre = TL.isPrefixOf
getLanguageHeader Nothing = ES
data Config = Config
{ configHost :: String,
configPort :: Int
}
configParser :: Parser Config
configParser =
Config
<$> strOption
( long "bind"
<> short 'b'
<> metavar "HOST"
<> help "Interface to bind to"
<> value "127.0.0.1"
<> showDefault
)
<*> option
auto
( long "port"
<> short 'p'
<> metavar "PORT"
<> help "Port to listen on"
<> value 3000
<> showDefault
)
main :: IO ()
main = do
cfg <- execParser $ info (configParser <**> helper)
( fullDesc <> progDesc "A Scotty web server with custom bind/port" )
let opts = defaultOptions
{ settings = setPort (configPort cfg)
$ setHost (fromString (configHost cfg))
$ settings defaultOptions
}
scottyOpts opts $ do
middleware $ staticPolicy (noDots >-> addBase "static")
get "/" $ do
langHeader <- header "Accept-Language"
let lang = getLanguageHeader langHeader
linesArray <- liftIO $ getFromUrl "https://cdav.chipburners.club/public/main/"
timeNow <- liftIO $ getCurrentTime
tmz <- liftIO $ getCurrentTimeZone
let today = utcToLocalTime tmz timeNow
dayToday = localDay today
endD = addDays 30 dayToday
events = readCalendar linesArray
validEvents = take 5 $ getEventsFromTo (rights events) dayToday endD
errors = lefts events
when (not $ null errors) $ do
liftIO $ print errors
html $ renderText $ renderFrontpage lang validEvents
get "/wiki" $ do
langHeader <- header "Accept-Language"
let lang = getLanguageHeader langHeader
langString = getLangAsString lang
redirect $ TL.pack $ printf "/%s/" langString
get "/monthView/" $ do
timeNow <- liftIO $ getCurrentTime
tmz <- liftIO $ getCurrentTimeZone
let today = utcToLocalTime tmz timeNow
dayToday = localDay today
(y, m, _) = toGregorian dayToday
redirect $ TL.pack $ printf "/monthView/%s/%s" (show y) (show m)
get "/monthView/:year/:month" $ do
langHeader <- header "Accept-Language"
let lang = getLanguageHeader langHeader
year :: Year <- pathParam "year"
month :: MonthOfYear <- pathParam "month"
if (year <= 2000 || year >= 3000 || month < 1 || month > 12)
then do
status status400
text "Invalid date"
else do
linesArray <- liftIO $ getFromUrl "https://cdav.chipburners.club/public/main/"
timeNow <- liftIO $ getCurrentTime
tmz <- liftIO $ getCurrentTimeZone
let firstOfMonth = fromGregorian year month 1
start = addDays (-7) firstOfMonth
end = addDays (37) firstOfMonth
today = utcToLocalTime tmz timeNow
dayToday = localDay today
events = readCalendar linesArray
validEvents = getEventsFromTo (rights events) start end
groupedEvents = groupEvents validEvents
errors = lefts events
when (not $ null errors) $ do
liftIO $ print errors
html $ renderText $ renderMonthview lang (year, month) dayToday groupedEvents
get "/api/interested/register/:event" $ do
event <- pathParam "event"
let cookieName = "interested-" <> event
returnUri <- queryParam "return"
cookie <- getCookie cookieName
newCookie <- liftIO $ Interested.register event cookie
currentTime <- (liftIO $ getCurrentTime)
setCookie
( defaultSetCookie
{ setCookieName = TE.encodeUtf8 cookieName,
setCookieValue = TE.encodeUtf8 newCookie,
-- TODO: just adding one month of expire which is kind of weird
setCookieExpires = Just (addUTCTime (secondsToNominalDiffTime 60 * 60 * 24 * 30) currentTime)
}
)
redirect returnUri