| 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
 | module Persistence.User
  ( list
  , get
  , checkPassword
  , createSignInToken
  ) where
import qualified Data.Maybe             as Maybe
import           Data.Text              (Text)
import           Database.SQLite.Simple (FromRow (fromRow), NamedParam ((:=)))
import qualified Database.SQLite.Simple as SQLite
import           Common.Model           (Email (..), Password (..), User (..))
import           Model.HashedPassword   (HashedPassword (..))
import qualified Model.HashedPassword   as HashedPassword
import           Model.Query            (Query (Query))
import qualified Model.UUID             as UUID
newtype Row = Row User
instance FromRow Row where
  fromRow = Row <$> (User <$>
    SQLite.field <*>
    SQLite.field <*>
    SQLite.field <*>
    SQLite.field)
list :: Query [User]
list =
  Query (\conn -> do
    map (\(Row u) -> u) <$>
      SQLite.query_ conn "SELECT id, creation, email, name from user ORDER BY creation DESC"
  )
get :: Text -> Query (Maybe User)
get token =
  Query (\conn -> do
    fmap (\(Row u) -> u) . Maybe.listToMaybe <$>
      SQLite.queryNamed
        conn
        "SELECT id, creation, email, name FROM user WHERE sign_in_token = :sign_in_token LIMIT 1"
        [ ":sign_in_token" := token ]
  )
data HashedPasswordRow = HashedPasswordRow HashedPassword
instance FromRow HashedPasswordRow where
  fromRow = HashedPasswordRow <$> (HashedPassword <$> SQLite.field)
checkPassword :: Email -> Password -> Query Bool
checkPassword (Email email) password =
  Query (\conn -> do
    hashedPassword <- fmap (\(HashedPasswordRow p) -> p) . Maybe.listToMaybe <$>
      SQLite.queryNamed
        conn
        "SELECT password FROM user WHERE email = :email LIMIT 1"
        [ ":email" := email ]
    case hashedPassword of
      Just h ->
        return (HashedPassword.check password h)
      Nothing ->
        return False
  )
createSignInToken :: Email -> Query Text
createSignInToken (Email email) =
  Query (\conn -> do
    token <- UUID.generateUUID
    SQLite.executeNamed
      conn
      "UPDATE user SET sign_in_token = :sign_in_token WHERE email = :email"
      [ ":sign_in_token" := token
      , ":email" := email
      ]
    return token
  )
 |