{-# LANGUAGE OverloadedStrings, FlexibleContexts,DeriveGeneric #-} module Villefort.Server (villefort) where import Web.Scotty (scotty ,get ,html ,post ,body ,redirect ,html ,json ,param ,file) import Control.Monad.Reader (liftIO,runReaderT) import Control.Concurrent (forkIO) import Data.Text.Lazy (pack) import Villefort.Database import Villefort.Todo (deleteTodo,getTodos,updateTodos,qetTasks',Task(..),daysUntil) import Villefort.Log (genStats) import Villefort.Definitions (VConfig(..)) import Villefort.Weekly (weeklyStats) import Villefort.Daily (dailyCheck) import Villefort.New (makeNewPage) import Paths_Villefort (getDataFileName,getDataDir) import Villefort.Today (getSummary) import Data.List.Split (splitOn) import System.Environment (getArgs) import System.Process (createProcess,proc,waitForProcess) import System.Directory (getAppUserDataDirectory,doesFileExist) import System.Posix.Process (executeFile) import Data.String.Utils (replace) import Data.Aeson (Value(..),object,(.=),toJSON) import Data.Aeson.Types (toJSONList) import Network.URI.Encode (decode) import Data.String (fromString) import GHC.Exts (fromList) import GHC.Generics import Data.Aeson -- | parses value from raw html post form getIndex :: [[Char]] -> Int -> [Char] getIndex str i = (Data.List.Split.splitOn "=" (str !! i)) !! 1 -- | Converts date from Javascript to sqlite date fromat convDate :: String -> String convDate date = newDate where splitDate = Data.List.Split.splitOn "%2F" date newDate = (splitDate !! 2) ++ "-" ++ (splitDate !! 0) ++ "-" ++ (splitDate !! 1) -- | Entry point for server attempts to recompile if needed villefort :: VConfig -> IO () villefort conf = do -- | update table if necessary let path = database conf args <- getArgs case args of ["--custom",_] -> putStrLn "launched custom build" >> launch conf ["--recompile"] -> re ["-r"] -> re ["-h"] -> putStrLn "usage: Villefort [options] " >> putStrLn " options:" >> putStrLn " -r, --recompile recompiles Villefort using a custom config file found in ~/.villefort/villefort.hs" >> putStrLn " -h, prints this help manual" _ -> do if noCustom conf then launch conf else checkCustomBuild >> launch conf where re = putStrLn "recompiling" >> recompile -- | recompiles villefort by calling ghc expects .villefort/villefort.hs in home directory recompile :: IO () recompile = do dir <- getAppUserDataDirectory "villefort" let execPath = dir ++ "/villefort" sourcePath = dir ++"/villefort.hs" (_,_,_,pid) <- createProcess (proc "ghc" ["-o",execPath,sourcePath]) _ <- waitForProcess pid return () -- | checks for executable in villefort home folder if so it executes it checkCustomBuild :: IO () checkCustomBuild = do dir <- getAppUserDataDirectory "villefort" let path = dir ++ "/villefort" isBuild <- doesFileExist path dataDir <- getDataDir if isBuild then putStrLn "custom build detected" >> executeFile path True ["--custom",dataDir] Nothing else putStrLn "no custom build detected" -- | actually launches the scotty server launch :: VConfig -> IO () launch conf = do _ <- forkIO $ dailyCheck conf scotty ( port conf) $ do get "/" $ do todos <- liftIO $ runReaderT getTodos conf html $ pack $ todos get "/new" $ do page <- liftIO $ runReaderT makeNewPage conf html $ pack page post "/delete" $ do rawHtml <- body runReaderT (deleteTodo rawHtml) conf redirect "/" post "/update" $ do rawHtml <- body let da = Data.List.Split.splitOn "&" (show rawHtml) let rawid = Data.List.Split.splitOn "=" $ (Prelude.init (da !! 1)) let sqlId = read (rawid!! 1) :: Int let rawtime = Data.List.Split.splitOn "=" $ (da !! 0) let insertTime = read (rawtime !! 1) :: Int liftIO $ runReaderT (updateTodos sqlId insertTime) conf redirect "/" post "/add" $ do rawBody <-body let parse = Data.List.Split.splitOn "&" (show rawBody) let clean = replace "+" " " let summary = clean . Network.URI.Encode.decode $ getIndex parse 0 let date = convDate $ getIndex parse 3 let todoTitle = clean . Network.URI.Encode.decode $ getIndex parse 1 let todoSubject = clean . Network.URI.Encode.decode $ getIndex parse 2 liftIO $ runReaderT (addTask todoTitle summary date todoSubject) conf redirect "/" get "/today" $ do dat <-liftIO $ runReaderT getSummary conf html $ pack dat get "/templates/:asset" $ do asset <- param "asset" path <- liftIO $ getDataFileName $ "templates/" ++ asset file path get "/weekly" $ do to <- liftIO $ runReaderT weeklyStats conf html $ pack to get "/log" $ do page <- liftIO $runReaderT genStats conf html $ pack page -- | get json active tasks get "/tasks" $ do tasks <- liftIO $ runReaderT qetTasks' conf Web.Scotty.json $ toJSON tasks -- | get time done today get "/done" $ do dat <- liftIO $ runReaderT getDone conf let encoded = map (\x -> Done (x !! 0) (read (x !! 1) :: Integer) (x !! 2)) dat :: [Done] Web.Scotty.json $ toJSON encoded data Done = Done {title :: String, time :: Integer, subject :: String} deriving (Show,Eq,Generic) instance FromJSON Done instance ToJSON Done