summaryrefslogtreecommitdiff
path: root/fig-web/src/Fig/Web/Module/Puzzle.hs
blob: ff441eb4d50c40933c9cb2eb3198e37544bffdb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module Fig.Web.Module.Puzzle
  ( secure
  ) where

import Fig.Prelude

import qualified Data.Map.Strict as Map
import qualified Data.Aeson as Aeson
import qualified Data.UUID as UUID
import qualified Data.UUID.V4 as UUID

import Fig.Web.Utils
import Fig.Web.Types
import Fig.Web.Auth
import Fig.Web.DB

data Puzzle = Puzzle
  { pid :: Text
  , name :: Text
  , author :: Text
  , authorid :: Text
  , body :: Text
  , checker :: Text
  } deriving (Show, Generic)
instance Aeson.ToJSON Puzzle

newPuzzlePid :: forall m. (MonadIO m, MonadCatch m) => m Text
newPuzzlePid = do
  uuid <- liftIO UUID.nextRandom
  pure $ UUID.toText uuid

puzzlePidKey :: Text -> ByteString
puzzlePidKey pid = "puzzle:" <> encodeUtf8 pid

puzzleKey :: Puzzle -> ByteString
puzzleKey p = puzzlePidKey p.pid

savePuzzle :: MonadIO m => DB -> Puzzle -> m ()
savePuzzle db p = do
  sadd db "puzzleids" [encodeUtf8 p.pid]
  hmset db (puzzleKey p)
    [ ("pid", encodeUtf8 p.pid)
    , ("name", encodeUtf8 p.name)
    , ("author", encodeUtf8 p.author)
    , ("authorid", encodeUtf8 p.authorid)
    , ("body", encodeUtf8 p.body)
    , ("checker", encodeUtf8 p.checker)
    ]

loadPuzzle :: MonadIO m => DB -> Text -> m (Maybe Puzzle)
loadPuzzle db p = do
  m <- hgetall db $ puzzlePidKey p
  pure $ do
    let field nm = decodeUtf8 <$> Map.lookup nm m
    pid <- field "pid"
    if pid == p
      then do
      name <- field "name"
      author <- field "author"
      authorid <- field "authorid"
      body <- field "body"
      checker <- field "checker"
      Just Puzzle {..}
      else Nothing

deletePuzzle :: MonadIO m => DB -> Text -> m ()
deletePuzzle db p = do
  srem db "puzzleids" [encodeUtf8 p]
  del db [puzzlePidKey p]

allPuzzles :: MonadIO m => DB -> m [Text]
allPuzzles db = smembers db "puzzleids" >>= \case
  Nothing -> pure []
  Just xs -> pure $ decodeUtf8 <$> xs

secure :: SecureModule
secure a = do
  onGet "/api/puzzle" do
    puzzles <- allPuzzles a.db
    respondJSON puzzles
  onGet "/api/puzzle/:pid" do
    pid <- pathParam "pid"
    loadPuzzle a.db pid >>= \case
      Nothing -> do
        status status404
        respondText "no such puzzle"
      Just p -> respondJSON p
  onDelete "/api/puzzle/:pid" do
    pid <- pathParam "pid"
    deletePuzzle a.db pid
  onPost "/api/puzzle" $ authed a \creds -> do
    log "Creating puzzle"
    let author = creds.user
    let authorid = creds.twitchId
    pid <- newPuzzlePid
    log $ "pid: " <> tshow pid
    name <- formParam "name"
    body <- formParam "body"
    checker <- formParam "checker"
    savePuzzle a.db Puzzle {..}
    respondText pid