diff options
Diffstat (limited to 'overlays/spm/server/Spm/Server')
-rw-r--r-- | overlays/spm/server/Spm/Server/Ctx.hs | 24 | ||||
-rw-r--r-- | overlays/spm/server/Spm/Server/Database.hs | 73 | ||||
-rw-r--r-- | overlays/spm/server/Spm/Server/UI.hs | 23 | ||||
-rw-r--r-- | overlays/spm/server/Spm/Server/Wordlist.hs | 31 |
4 files changed, 151 insertions, 0 deletions
diff --git a/overlays/spm/server/Spm/Server/Ctx.hs b/overlays/spm/server/Spm/Server/Ctx.hs new file mode 100644 index 00000000..7b84e3d5 --- /dev/null +++ b/overlays/spm/server/Spm/Server/Ctx.hs | |||
@@ -0,0 +1,24 @@ | |||
1 | {-# LANGUAGE TemplateHaskell #-} | ||
2 | |||
3 | module Spm.Server.Ctx | ||
4 | ( ServerCtx(..) | ||
5 | , sctxSqlPool, sctxInstanceId, sctxJwkSet | ||
6 | ) where | ||
7 | |||
8 | import Data.UUID (UUID) | ||
9 | import Crypto.JOSE.JWK (JWKSet) | ||
10 | import Database.Persist.Postgresql | ||
11 | import Data.Pool | ||
12 | import Control.Lens.TH | ||
13 | |||
14 | import Type.Reflection (Typeable) | ||
15 | import GHC.Generics (Generic) | ||
16 | |||
17 | |||
18 | data ServerCtx = ServerCtx | ||
19 | { _sctxSqlPool :: Pool SqlBackend | ||
20 | , _sctxInstanceId :: UUID | ||
21 | , _sctxJwkSet :: JWKSet | ||
22 | } deriving (Generic, Typeable) | ||
23 | makeLenses ''ServerCtx | ||
24 | |||
diff --git a/overlays/spm/server/Spm/Server/Database.hs b/overlays/spm/server/Spm/Server/Database.hs new file mode 100644 index 00000000..cc133e06 --- /dev/null +++ b/overlays/spm/server/Spm/Server/Database.hs | |||
@@ -0,0 +1,73 @@ | |||
1 | {-# LANGUAGE OverloadedStrings, TemplateHaskell, QuasiQuotes, UndecidableInstances #-} | ||
2 | |||
3 | module Spm.Server.Database | ||
4 | ( MailMailbox, MailLocal, MailExtension, MailDomain | ||
5 | , Mailbox(..), MailboxMapping(..) | ||
6 | , Key(..), EntityField(..), Unique(..) | ||
7 | ) where | ||
8 | |||
9 | import Prelude | ||
10 | |||
11 | import Database.Persist | ||
12 | import Database.Persist.Sql | ||
13 | import Database.Persist.TH | ||
14 | |||
15 | import GHC.Generics (Generic) | ||
16 | import Type.Reflection (Typeable) | ||
17 | |||
18 | import Data.Text (Text) | ||
19 | |||
20 | import Data.CaseInsensitive (CI) | ||
21 | import qualified Data.CaseInsensitive as CI | ||
22 | import Data.CaseInsensitive.Instances () | ||
23 | |||
24 | import Data.UUID (UUID) | ||
25 | import Data.UUID.Instances () | ||
26 | |||
27 | import Data.Int (Int64) | ||
28 | |||
29 | import Control.Lens | ||
30 | |||
31 | import Web.HttpApiData | ||
32 | |||
33 | |||
34 | newtype MailMailbox = MailMailbox | ||
35 | { unMailMailbox :: CI Text | ||
36 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) | ||
37 | deriving newtype (PersistField, PersistFieldSql) | ||
38 | makeWrapped ''MailMailbox | ||
39 | newtype MailLocal = MailLocal | ||
40 | { unMailLocal :: CI Text | ||
41 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) | ||
42 | deriving newtype (PersistField, PersistFieldSql) | ||
43 | makeWrapped ''MailLocal | ||
44 | newtype MailExtension = MailExtension | ||
45 | { unMailExtension :: CI Text | ||
46 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) | ||
47 | deriving newtype (PersistField, PersistFieldSql) | ||
48 | makeWrapped ''MailExtension | ||
49 | newtype MailDomain = MailDomain | ||
50 | { unMailDomain :: CI Text | ||
51 | } deriving stock (Eq, Ord, Read, Show, Generic, Typeable) | ||
52 | deriving newtype (PersistField, PersistFieldSql) | ||
53 | makeWrapped ''MailDomain | ||
54 | |||
55 | instance FromHttpApiData MailDomain where | ||
56 | parseUrlPiece = fmap (review _Wrapped . CI.mk) . parseUrlPiece | ||
57 | |||
58 | |||
59 | share [mkPersist sqlSettings] [persistLowerCase| | ||
60 | Mailbox | ||
61 | Id UUID sqltype=uuid default=gen_random_uuid() | ||
62 | ident MailMailbox sql=mailbox | ||
63 | quota Int64 Maybe sql=quota_bytes MigrationOnly | ||
64 | UniqueMailbox ident | ||
65 | deriving Show | ||
66 | MailboxMapping | ||
67 | Id UUID sqltype=uuid default=gen_random_uuid() | ||
68 | local MailLocal Maybe | ||
69 | extension MailExtension Maybe | ||
70 | domain MailDomain | ||
71 | mailbox MailboxId | ||
72 | UniqueLocalDomain local domain sql=local_domain_unique !force | ||
73 | |] | ||
diff --git a/overlays/spm/server/Spm/Server/UI.hs b/overlays/spm/server/Spm/Server/UI.hs new file mode 100644 index 00000000..544e4f54 --- /dev/null +++ b/overlays/spm/server/Spm/Server/UI.hs | |||
@@ -0,0 +1,23 @@ | |||
1 | {-# LANGUAGE OverloadedStrings, TemplateHaskell #-} | ||
2 | |||
3 | module Spm.Server.UI | ||
4 | ( uiServer | ||
5 | ) where | ||
6 | |||
7 | import Prelude | ||
8 | |||
9 | import Network.Wai | ||
10 | |||
11 | import Network.Wai.Application.Static | ||
12 | import WaiAppStatic.Types | ||
13 | |||
14 | import Data.FileEmbed | ||
15 | |||
16 | |||
17 | uiServer :: Application | ||
18 | uiServer = staticApp settings | ||
19 | { ssLookupFile = \pcs -> ssLookupFile settings pcs >>= \case | ||
20 | LRNotFound -> ssLookupFile settings [unsafeToPiece "index.html"] | ||
21 | o -> return o | ||
22 | } | ||
23 | where settings = embeddedSettings $(embedDir "frontend") | ||
diff --git a/overlays/spm/server/Spm/Server/Wordlist.hs b/overlays/spm/server/Spm/Server/Wordlist.hs new file mode 100644 index 00000000..8109f07b --- /dev/null +++ b/overlays/spm/server/Spm/Server/Wordlist.hs | |||
@@ -0,0 +1,31 @@ | |||
1 | {-# LANGUAGE TemplateHaskell #-} | ||
2 | |||
3 | module Spm.Server.Wordlist | ||
4 | ( wordlist, consonants | ||
5 | ) where | ||
6 | |||
7 | import Prelude | ||
8 | import Language.Haskell.TH.Syntax | ||
9 | import Control.Monad.IO.Class | ||
10 | |||
11 | import Data.Text (Text) | ||
12 | import qualified Data.Text as Text | ||
13 | import qualified Data.Text.IO as Text | ||
14 | |||
15 | import Data.Vector (Vector) | ||
16 | import qualified Data.Vector as Vector | ||
17 | |||
18 | import Data.FileEmbed (makeRelativeToProject) | ||
19 | |||
20 | import Instances.TH.Lift () | ||
21 | |||
22 | |||
23 | {-# NOINLINE wordlist #-} | ||
24 | {-# NOINLINE consonants #-} | ||
25 | wordlist, consonants :: Vector Text | ||
26 | wordlist = $( do | ||
27 | fPath <- makeRelativeToProject "wordlist.txt" | ||
28 | addDependentFile fPath | ||
29 | lift . Vector.fromList =<< liftIO (filter (not . Text.null) . Text.words <$> Text.readFile fPath) | ||
30 | ) | ||
31 | consonants = Vector.fromList $ map Text.singleton "bcdfghjklmnpqrstvwxz" | ||