|
|
|
@ -9,178 +9,119 @@
|
|
|
|
|
module PEA.Server.API where
|
|
|
|
|
|
|
|
|
|
import Data.Aeson
|
|
|
|
|
import qualified Data.Map.Strict as Map
|
|
|
|
|
import Data.Time.LocalTime
|
|
|
|
|
import Data.UUID
|
|
|
|
|
import Data.Time.Clock
|
|
|
|
|
import Database.Persist
|
|
|
|
|
import GHC.Generics
|
|
|
|
|
import GHC.TypeLits
|
|
|
|
|
import Network.Wai
|
|
|
|
|
import Network.Wai.Handler.Warp
|
|
|
|
|
import Servant
|
|
|
|
|
|
|
|
|
|
import PEA.Server.Data
|
|
|
|
|
import PEA.Server.Types
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
server :: CookieSettings -> JWTSettings -> Server (API auths)
|
|
|
|
|
server cs jwts = protected :<|> unprotected cs jwts
|
|
|
|
|
type CRUD (name :: Symbol) a = name :>
|
|
|
|
|
( Get '[JSON] (Normal a)
|
|
|
|
|
:<|> ReqBody '[JSON] a :> Post '[JSON] (Key a)
|
|
|
|
|
:<|> Capture "id" (Key a) :>
|
|
|
|
|
( Get '[JSON] a
|
|
|
|
|
:<|> ReqBody '[JSON] a :> Put '[JSON] ()
|
|
|
|
|
:<|> Delete '[JSON] ()
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type API auths = (Auth auths User :> Protected) :<|> Unprotected
|
|
|
|
|
type Tags = CRUD "tags" String
|
|
|
|
|
|
|
|
|
|
protected :: AuthResult User -> Server Protected
|
|
|
|
|
protected (Authenticated user) = return adminServer user :<|> return myServer user
|
|
|
|
|
protected _ = throwAll err401
|
|
|
|
|
tags :: User -> Server Tags
|
|
|
|
|
tags user = readAllTags user
|
|
|
|
|
:<|> createTag user
|
|
|
|
|
:<|> (\i -> readOneTag i user :<|> updateTag i user :<|> deleteTag i user)
|
|
|
|
|
|
|
|
|
|
type Protected = Admin :<|> My
|
|
|
|
|
|
|
|
|
|
data User = User
|
|
|
|
|
{ name :: String
|
|
|
|
|
, email :: String
|
|
|
|
|
} deriving (Eq, Show, Read, Generic)
|
|
|
|
|
createTag
|
|
|
|
|
|
|
|
|
|
instance ToJSON User
|
|
|
|
|
instance ToJWT User
|
|
|
|
|
instance FromJSON User
|
|
|
|
|
instance FromJWT User
|
|
|
|
|
readAllTags
|
|
|
|
|
|
|
|
|
|
unprotected :: CookieSettings -> JWTSettings -> Server Unprotected
|
|
|
|
|
unprotected cs jwts = checkCreds cs jwts
|
|
|
|
|
type Tasks = CRUD "tasks" Task
|
|
|
|
|
|
|
|
|
|
type Unprotected =
|
|
|
|
|
"login"
|
|
|
|
|
:> ReqBody '[JSON] Login
|
|
|
|
|
:> Verb 'POST 204 '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
|
|
|
|
, Header "Set-Cookie" SetCookie]
|
|
|
|
|
NoContent)
|
|
|
|
|
tasks :: User -> Server Tasks
|
|
|
|
|
tasks user = readAllTasks
|
|
|
|
|
:<|> createTask user
|
|
|
|
|
:<|> (\i -> readOneTask i user :<|> updateTask i user :<|> deleteTask i user)
|
|
|
|
|
|
|
|
|
|
data Login = Login
|
|
|
|
|
|
|
|
|
|
users :: User -> Server Users
|
|
|
|
|
users user = readAllUsers
|
|
|
|
|
:<|> createUser user
|
|
|
|
|
:<|> (\i -> readOneUser i user :<|> updateUser i user :<|> deleteUser i user)
|
|
|
|
|
|
|
|
|
|
type Users = CRUD "users" UserData
|
|
|
|
|
|
|
|
|
|
data Credentials = Credentials
|
|
|
|
|
{ username :: String
|
|
|
|
|
, password :: String
|
|
|
|
|
} deriving (Eq, Show, Read, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON Login
|
|
|
|
|
instance ToJWT Login
|
|
|
|
|
instance FromJSON Login
|
|
|
|
|
instance FromJWT Login
|
|
|
|
|
instance ToJSON Credentials
|
|
|
|
|
instance ToJWT Credentials
|
|
|
|
|
instance FromJSON Credentials
|
|
|
|
|
instance FromJWT Credentials
|
|
|
|
|
|
|
|
|
|
adminServer :: User -> Server Admin
|
|
|
|
|
adminServer user = return $ tagServer user :<|> return $ taskServer user :<|> return $ userServer user
|
|
|
|
|
|
|
|
|
|
type Data = "data" :> Get '[JSON] UserData
|
|
|
|
|
|
|
|
|
|
type Admin = "admin" :> (Tags :<|> Tasks :<|> Users)
|
|
|
|
|
|
|
|
|
|
myServer :: User -> Server My
|
|
|
|
|
my user = return $ dataServer user :<|> return $ tagServer user :<|> return $ taskServer user
|
|
|
|
|
admin :: Server Admin
|
|
|
|
|
admin = return tags :<|> return tasks :<|> return users
|
|
|
|
|
|
|
|
|
|
type My = "my" :> (Data :<|> Tags :<|> Tasks)
|
|
|
|
|
|
|
|
|
|
my :: Server My
|
|
|
|
|
my = return dataServer :<|> return tags :<|> return tasks
|
|
|
|
|
|
|
|
|
|
dataServer :: User -> Server UserData
|
|
|
|
|
dataServer user = return $ userData user
|
|
|
|
|
where
|
|
|
|
|
userData :: User -> Handler UserData
|
|
|
|
|
userData user = return fetchUserData user
|
|
|
|
|
|
|
|
|
|
type Data = "data" :> Get '[JSON] UserData
|
|
|
|
|
type Protected = Admin :<|> My
|
|
|
|
|
|
|
|
|
|
data UserData = UserData
|
|
|
|
|
{ user :: User
|
|
|
|
|
, preferences :: UserPreferences
|
|
|
|
|
, properties :: UserProperties
|
|
|
|
|
, tags :: Normal Tag
|
|
|
|
|
, tasks :: Normal Task
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON UserData
|
|
|
|
|
instance FromJSON UserData
|
|
|
|
|
|
|
|
|
|
data UserPreferences = UserPreferences
|
|
|
|
|
{ effortM :: Map.Map Int String
|
|
|
|
|
, priorityM :: Map.Map Int String
|
|
|
|
|
, theme :: String
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON UserPreferences
|
|
|
|
|
instance FromJSON UserPreferences
|
|
|
|
|
|
|
|
|
|
data UserProperties = UserProperties
|
|
|
|
|
{ active :: Bool
|
|
|
|
|
, registration :: UTCTime
|
|
|
|
|
, superuser :: Bool
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON UserProperties
|
|
|
|
|
instance FromJSON UserProperties
|
|
|
|
|
|
|
|
|
|
data Normal a = Normal
|
|
|
|
|
{ ids :: [UUID]
|
|
|
|
|
, entities :: Map.Map UUID a
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON Normal
|
|
|
|
|
instance FromJSON Normal
|
|
|
|
|
|
|
|
|
|
normalize :: [(UUID, a)] -> Normal a
|
|
|
|
|
normalize pairs = foldl normalize' empty' pairs
|
|
|
|
|
where
|
|
|
|
|
empty' = Normal [] Map.empty
|
|
|
|
|
normalize' acc (uuid, ent) =
|
|
|
|
|
let
|
|
|
|
|
prevIds = ids acc
|
|
|
|
|
prevEnts = entities acc
|
|
|
|
|
in Normal
|
|
|
|
|
{ ids = uuid : prevIds
|
|
|
|
|
, entities = Map.insert uuid ent prevEnts
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tagServer :: User -> Server Tags
|
|
|
|
|
tagServer user = readAllTags user
|
|
|
|
|
:<|> createTag user
|
|
|
|
|
:<|> (\uuid -> readOneTag uuid user :<|> updateTag uuid user :<|> deleteTag uuid user)
|
|
|
|
|
protected :: AuthResult Credentials -> Server Protected
|
|
|
|
|
protected (Authenticated creds) = return admin :<|> return my
|
|
|
|
|
protected _ = throwAll err401
|
|
|
|
|
|
|
|
|
|
type Tags = CRUD "tags" Tag
|
|
|
|
|
data Login = Login
|
|
|
|
|
{ username :: String
|
|
|
|
|
, password :: String
|
|
|
|
|
} deriving (Eq, Show, Read, Generic)
|
|
|
|
|
|
|
|
|
|
type Tag = String
|
|
|
|
|
instance ToJSON Login
|
|
|
|
|
instance ToJWT Login
|
|
|
|
|
instance FromJSON Login
|
|
|
|
|
instance FromJWT Login
|
|
|
|
|
|
|
|
|
|
type CRUD (name :: Symbol) a = name :>
|
|
|
|
|
( Get '[JSON] (Normal a)
|
|
|
|
|
:<|> ReqBody '[JSON] a :> PostNoContent
|
|
|
|
|
:<|> Capture "uuid" UUID :>
|
|
|
|
|
( Get '[JSON] a
|
|
|
|
|
:<|> ReqBody '[JSON] a :> PutNoContent
|
|
|
|
|
:<|> DeleteNoContent
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
unprotected :: CookieSettings -> JWTSettings -> Server Unprotected
|
|
|
|
|
unprotected cs jwts = checkCreds cs jwts
|
|
|
|
|
|
|
|
|
|
type Unprotected =
|
|
|
|
|
"login"
|
|
|
|
|
:> ReqBody '[JSON] Login
|
|
|
|
|
:> Verb 'POST 204 '[JSON] (Headers '[ Header "Set-Cookie" SetCookie
|
|
|
|
|
, Header "Set-Cookie" SetCookie]
|
|
|
|
|
NoContent)
|
|
|
|
|
|
|
|
|
|
taskServer :: Server Tasks
|
|
|
|
|
taskServer = readAllTasks
|
|
|
|
|
:<|> createTask
|
|
|
|
|
:<|> (\uuid -> readOneTask uuid :<|> updateTask uuid :<|> deleteTask uuid)
|
|
|
|
|
type API auths = (Auth auths Credentials :> Protected) :<|> Unprotected
|
|
|
|
|
|
|
|
|
|
type Tasks = CRUD "tasks" Task
|
|
|
|
|
server :: CookieSettings -> JWTSettings -> Server (API auths)
|
|
|
|
|
server cs jwts = protected :<|> unprotected cs jwts
|
|
|
|
|
|
|
|
|
|
data Task = Task
|
|
|
|
|
{ parents :: [UUID]
|
|
|
|
|
, children :: [UUID]
|
|
|
|
|
, taskProperties :: TaskProperties
|
|
|
|
|
, tagsIds :: [UUID]
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON Task
|
|
|
|
|
instance FromJSON Task
|
|
|
|
|
|
|
|
|
|
data TaskProperties = TaskProperties
|
|
|
|
|
{ created :: UTCTime
|
|
|
|
|
, description :: String
|
|
|
|
|
, due :: UTCTime
|
|
|
|
|
, duration :: Int
|
|
|
|
|
, effort :: Int
|
|
|
|
|
, priority :: Int
|
|
|
|
|
, progress :: Rational
|
|
|
|
|
, title :: String
|
|
|
|
|
} deriving (Eq, Show, Generic)
|
|
|
|
|
|
|
|
|
|
instance ToJSON TaskProperties
|
|
|
|
|
instance FromJSON TaskProperties
|
|
|
|
|
|
|
|
|
|
userServer :: Server Users
|
|
|
|
|
userServer = readAllUsers
|
|
|
|
|
:<|> createUser
|
|
|
|
|
:<|> (\uuid -> readOneUser uuid :<|> updateUser uuid :<|> deleteUser uuid)
|
|
|
|
|
api :: Proxy All
|
|
|
|
|
api = Proxy
|
|
|
|
|
|
|
|
|
|
type Users = CRUD "users" UserData
|
|
|
|
|
app :: Application
|
|
|
|
|
app = serve api server
|
|
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
|
main = run 8081 app
|
|
|
|
|