170 lines
5.6 KiB
Haskell
170 lines
5.6 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))
|
|
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
|
|
}
|
|
|
|
-- Build the parser
|
|
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 "/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
|