on 4a67a77 help sign in
29 files changed+112−1194
since splitsnapshots
unifiedsplit
Procfile+1−1 denis/package.yaml−13 denis/src/Data/Channel/AnonymousChannel.hs+7−13 denis/src/Data/Channel/NamedChannel.hs+11−20 denis/src/Data/Connection.hs+1−6 denis/src/Data/GroupChat.hs+1−10 denis/src/Data/Limits.hs−35 denis/src/Data/Message.hs+1−9 denis/src/Data/Post.hs−4 denis/src/Data/PostElement.hs+6−23 denis/src/Data/Query.hs+37−71 denis/src/Data/Tags/Validation.hs−25 denis/src/Data/Text/Validator.hs−59 denis/src/Data/User.hs+1−7 denis/src/Server/API.hs+1−6 denis/src/Server/API/Channels.hs+13−28 denis/src/Server/API/Completions.hs+1−10 denis/src/Server/API/Faculty.hs+1−11 denis/src/Server/API/Messages.hs+9−35 denis/src/Server/API/Posts.hs+4−16 denis/src/Server/App.hs+2−11 denis/src/Server/Auth.hs+4−22 denis/src/Server/Error.hs+4−35 denis/src/Server/Query/Pagination.hs+1−5 denis/src/Server/Server.hs+5−15 denis/src/test/Orphans.hs−104 denis/src/test/Spec.hs−101 dump.sql−499 stack.yaml+1
Procfile+1−1
@@ -1 +1 @@
1−web: denis-exe +RTS -N
1+web: denis-exe +RTS -N -T
denis/package.yaml−13
@@ -63,10 +63,6 @@ dependencies:
6363- blaze-html
6464- servant-blaze
6565- random
66−- hspec
67−- generic-arbitrary
68−- quickcheck-instances
69−- http-client
7066
7167library:
7268 source-dirs: src
@@ -109,13 +105,4 @@ executables:
109105 - -rtsopts
110106 - -with-rtsopts=-N
111107 dependencies:
112− - denis
113− test:
114− main: Spec.hs
115− source-dirs: src/test
116− ghc-options:
117− - -threaded
118− dependencies:
119− - QuickCheck
120− - servant-quickcheck
121108 - denis
denis/src/Data/Channel/AnonymousChannel.hs+7−13
@@ -5,8 +5,7 @@
55 OverloadedStrings ,
66 TypeApplications ,
77 TypeOperators,
8− OverloadedStrings,
9− RecordWildCards #-}
8+ OverloadedStrings #-}
109
1110module Data.Channel.AnonymousChannel (
1211 AnonymousChannelId,
@@ -20,20 +19,15 @@ import Data.Aeson
2019import Servant.Docs (ToSample, toSamples, samples)
2120import Data.Text (Text)
2221import qualified Data.Vector as V
23−import Data.User
22+import Data.User
2423import qualified Data.Set as Set
25−import Data.Tags.Validation
26−import Data.Text.Validator
24+
2725
2826-- MARK: Documentation
2927
3028instance ToSample AnonymousChannel where
3129 toSamples _ = samples $ AnonymousChannel <$> [V.fromList ["thisIsHashTag", "thisIsAlsoHashTag"]] <*> [[2, 7, 8]]
32−
33−
34−instance HasValidatableText AnonymousChannel where
35− validateText AnonymousChannel{..} = all (validateText . (~= validateTag')) anonymousChannelTags
36−
30+
3731-- MARK: Implementation
3832
3933type AnonymousChannelId = Int64
@@ -52,10 +46,10 @@ instance ToJSON AnonymousChannel where
5246 "people" .= cPeople ]
5347
5448instance FromJSON AnonymousChannel where
55− parseJSON = withObject "anonymous channel" $ \e ->
49+ parseJSON = withObject "anonymous channel" $ \e ->
5650 AnonymousChannel <$> fmap (V.fromList . removeDuplicates) (e .: "tags") <*> fmap removeDuplicates (e .: "people")
57− where
51+ where
5852 removeDuplicates :: Ord a => [a] -> [a]
5953 removeDuplicates = Set.toList . Set.fromList
6054
61−
55+
denis/src/Data/Channel/NamedChannel.hs+11−20
@@ -5,8 +5,7 @@ OverloadedLabels,
55OverloadedStrings ,
66TypeApplications ,
77TypeOperators,
8−OverloadedStrings,
9−RecordWildCards #-}
8+OverloadedStrings #-}
109
1110module Data.Channel.NamedChannel (
1211 NamedChannelId,
@@ -22,26 +21,18 @@ import Servant.Docs (ToSample, toSamples, samples)
2221import Data.Proxy
2322import Data.Text (Text)
2423import qualified Data.Vector as V
25−import Data.User
24+import Data.User
2625import qualified Data.Set as Set
27−import Data.Text.Validator
28−import Data.Tags.Validation
29−import Data.Limits
3026
3127-- MARK: Documentation
3228
3329instance (ToSample u) => ToSample (NamedChannel u) where
3430 toSamples _ = samples $ NamedChannel <$> [8] <*> ["социология", "ФКН"] <*> [V.fromList ["thisIsHashTag", "thisIsAlsoHashTag"]] <*> [V.fromList (map snd $ toSamples Proxy)]
3531
32+
3633instance ToSample NamedChannelCreationRequest where
3734 toSamples _ = samples $ NamedChannelCreationRequest <$> ["социология", "ФКН"] <*> [V.fromList ["thisIsHashTag", "thisIsAlsoHashTag"]] <*> [V.fromList [2, 7, 8]]
38−
39−instance HasValidatableText (NamedChannel u) where
40− validateText NamedChannel{..} = validateText (namedChannelName ~< maxChannelName) && all (validateText . (~= validateTag')) namedChannelTags
41−
42−instance HasValidatableText (NamedChannelCreationRequest) where
43− validateText NamedChannelCreationRequest{..} = validateText (namedChannelCreationRequestName ~< maxChannelName) && all validateText namedChannelCreationRequestTags
44−
35+
4536-- MARK: Implementation
4637
4738type NamedChannelId = Int64
@@ -62,14 +53,14 @@ instance (ToJSON u) => ToJSON (NamedChannel u) where
6253 "people" .= cPeople ]
6354
6455instance (FromJSON u, Ord u) => FromJSON (NamedChannel u) where
65− parseJSON = withObject "named channel" $ \e ->
56+ parseJSON = withObject "named channel" $ \e ->
6657 NamedChannel <$> e .: "id" <*> e .: "name" <*> fmap removeDuplicates (e .: "tags") <*> fmap removeDuplicates (e .: "people")
67− where
58+ where
6859 removeDuplicates :: Ord a => [a] -> V.Vector a
6960 removeDuplicates = V.fromList . Set.toList . Set.fromList
70−
61+
7162-- MARK: NamedChannelCreationRequest
72−
63+
7364data NamedChannelCreationRequest = NamedChannelCreationRequest {
7465 namedChannelCreationRequestName :: Text,
7566 namedChannelCreationRequestTags :: V.Vector Text,
@@ -84,10 +75,10 @@ instance ToJSON NamedChannelCreationRequest where
8475 "name" .= cName,
8576 "tags" .= cTags,
8677 "people" .= cPeople ]
87−
78+
8879instance FromJSON NamedChannelCreationRequest where
89− parseJSON = withObject "named channel" $ \e ->
80+ parseJSON = withObject "named channel" $ \e ->
9081 NamedChannelCreationRequest <$> e .: "name" <*> fmap removeDuplicates (e .: "tags") <*> fmap removeDuplicates (e .: "people")
91− where
82+ where
9283 removeDuplicates :: Ord a => [a] -> V.Vector a
9384 removeDuplicates = V.fromList . Set.toList . Set.fromList
denis/src/Data/Connection.hs+1−6
@@ -32,14 +32,9 @@ import Control.Monad.Trans.Control
3232
3333type StaticPQ = PoolPQ Schema Handler
3434
35−printRed :: (Show a) => a -> IO ()
36−printRed a = do
37− putStr "\a\x001b[31m"
38− print a
39− putStr "\x001b[0m"
4035
4136runQ :: ServantErr -> StaticPQ a -> App a
42−runQ err req = handleSqueal ((>> throwError err) . liftIO . printRed) $ asks getPool >>= lift . runPoolPQ req
37+runQ err req = handleSqueal ((>> throwError err) . liftIO . print) $ asks getPool >>= lift . runPoolPQ req
4338
4439runQ' :: DBConnection -> ServantErr -> StaticPQ a -> Handler a
4540runQ' conn err req = handleSqueal (const $ throwError err) $ runPoolPQ req conn
denis/src/Data/GroupChat.hs+1−10
@@ -7,8 +7,7 @@
77 TypeOperators,
88 OverloadedStrings,
99 TypeSynonymInstances,
10− FlexibleInstances,
11− RecordWildCards #-}
10+ FlexibleInstances #-}
1211{-# OPTIONS_GHC -Wno-orphans #-}
1312
1413
@@ -33,8 +32,6 @@ import Squeal.PostgreSQL.Schema
3332import Data.Function
3433import Data.Proxy
3534import Servant.Docs
36−import Data.Limits
37−import Data.Text.Validator
3835
3936instance ToSample GroupChat where
4037 toSamples _ = samples $ GroupChat <$>
@@ -47,12 +44,6 @@ instance ToSample GroupChatCreation where
4744 (Jsonb <$> map snd (toSamples Proxy)) <*>
4845 ["cocos", "kursach"]
4946
50−instance HasValidatableText GroupChat where
51− validateText GroupChat{..} = validateText $ groupChatName ~< maxGroupChatName
52−
53−instance HasValidatableText GroupChatCreation where
54− validateText GroupChatCreation{..} = validateText $ groupChatCreationName ~< maxGroupChatName
55−
5647instance ToSample UserPermissions where
5748 toSamples _ = singleSample $ M.fromList [(69, GroupChatPermissions True), (5051, GroupChatPermissions False)]
5849
denis/src/Data/Limits.hsdeleted−35
@@ -1,35 +0,0 @@
1−{-# OPTIONS_GHC -Wno-missing-export-lists #-}
2−
3−module Data.Limits where
4−
5−import Data.Int (Int64)
6−
7−maxChannelName :: Int
8−maxChannelName = 60
9−
10−maxGroupChatName :: Int
11−maxGroupChatName = 60
12−
13−maxMessageLimitName :: Int
14−maxMessageLimitName = 60
15−
16−postElementLengthLimit :: Int
17−postElementLengthLimit = 35000
18−
19−userFieldLengthLimit :: Int
20−userFieldLengthLimit = 50
21−
22−maxNumberOfTags :: Int
23−maxNumberOfTags = 20
24−
25−channelCountLimit :: Int64
26−channelCountLimit = 128
27−
28−listLimit :: Int
29−listLimit = 100
30−
31−globalTextLimit :: Int
32−globalTextLimit = 50000
33−
34−groupChatSizeLimit :: Int
35−groupChatSizeLimit = 256
denis/src/Data/Message.hs+1−9
@@ -6,8 +6,7 @@
66 TypeApplications ,
77 TypeOperators,
88 OverloadedStrings,
9− TupleSections,
10− RecordWildCards #-}
9+ TupleSections #-}
1110{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
1211
1312
@@ -34,7 +33,6 @@ import Control.Applicative
3433import Data.Aeson.Types
3534import Data.Proxy
3635import Servant.Docs
37−import Data.Text.Validator
3836
3937instance ToSample Message where
4038 toSamples _ = samples $ Message 8 69 <$>
@@ -45,12 +43,6 @@ instance ToSample Message where
4543instance ToSample MessageDetination where
4644 toSamples _ = samples $ [GroupChatDestination 5, UserChatDestination 69]
4745
48−instance HasValidatableText Message where
49− validateText Message{..} = validateText messageBody
50−
51−instance HasValidatableText MessageCreation where
52− validateText (MessageCreation _ _ (Jsonb mb)) = validateText mb
53−
5446type GroupChatMessageId = Int64
5547
5648data MessageDetination = GroupChatDestination GroupChatId | UserChatDestination UserId
denis/src/Data/Post.hs−4
@@ -29,8 +29,6 @@ import Data.Function (on)
2929import Data.Time.Clock
3030import Data.Time.Calendar
3131import qualified Data.Vector as V
32−import Data.Tags.Validation
33−import Data.Text.Validator
3432
3533-- MARK: Documentation
3634
@@ -41,8 +39,6 @@ instance ToSample Post where
4139instance ToSample PostCreation where
4240 toSamples _ = samples $ PostCreation <$> (map snd $ toSamples Proxy) <*> [(V.fromList ["hse", "cs", "machineLearning"])]
4341
44−instance HasValidatableText PostCreation where
45− validateText (PostCreation body tags) = all validateText body && all (validateText . (~= validateTag')) tags
4642
4743-- MARK: Actual type
4844
denis/src/Data/PostElement.hs+6−23
@@ -15,20 +15,16 @@ module Data.PostElement (
1515 MkElementRow,
1616 PostElementRow(..),
1717 PostElement(..),
18− elementsToRows,
19− PostQuote(..),
20− stripPostElement
18+ elementsToRows
2119) where
2220
2321import Data.Int (Int64)
2422import qualified Generics.SOP as SOP
2523import qualified GHC.Generics as GHC
26−import Data.Text (Text, strip)
24+import Data.Text (Text)
2725import Data.Aeson
2826import Control.Applicative ((<|>))
2927import Servant.Docs (ToSample, toSamples, samples)
30−import Data.Text.Validator
31−import Data.Limits
3228
3329-- MARK: Documentation
3430
@@ -38,27 +34,15 @@ instance ToSample (PostElement p) where
3834 Markdown "## This is a subtitle\n_hello._"
3935 ]
4036
41−instance HasValidatableText (PostElement p) where
42− validateText (Markdown t) = validateText $ t ~< postElementLengthLimit
43− validateText (Latex t) = validateText $ t ~< postElementLengthLimit
44− validateText (Image t) = validateText $ t ~< postElementLengthLimit
45− validateText (Quote _) = False
46− validateText (Attachment t) = validateText $ t ~< postElementLengthLimit
47−
4837
4938-- MARK: PostElement
5039
5140data PostElement a = Markdown Text
5241 | Latex Text
5342 | Image Text
54− | Quote ()
43+ | Quote PostQuote
5544 | Attachment Text
56− deriving (Show, GHC.Generic)
57−
58−stripPostElement :: (PostElement a) -> (PostElement a)
59−stripPostElement (Markdown t) = Markdown (strip t)
60−stripPostElement (Latex t) = Latex (strip t)
61−stripPostElement u = u
45+ deriving (Show)
6246
6347instance ToJSON (PostElement t) where
6448 toJSON (Markdown m) = object ["markdown" .= m]
@@ -85,8 +69,7 @@ elemenToRow f el = case el of
8569 Markdown m -> f (Just m) Nothing Nothing Nothing Nothing
8670 Latex l -> f Nothing (Just l) Nothing Nothing Nothing
8771 Image i -> f Nothing Nothing (Just i) Nothing Nothing
88− -- Quote q -> f Nothing Nothing Nothing (Just $ (quoteId q)) Nothing
89− Quote _ -> f Nothing Nothing Nothing Nothing Nothing
72+ Quote q -> f Nothing Nothing Nothing (Just $ quoteId q) Nothing
9073 Attachment a -> f Nothing Nothing Nothing Nothing (Just a)
9174
9275rowsToElement :: MkElementRow (Maybe (PostElement p))
@@ -122,7 +105,7 @@ data PostQuote = PostQuote {
122105 quoteId :: Int64,
123106 quoteBody :: [PostElement PostQuote],
124107 quotePostId :: Int64
125−} deriving (Show, GHC.Generic)
108+} deriving (Show)
126109
127110instance ToJSON PostQuote where
128111 toJSON (PostQuote qId pb pId) = object [
denis/src/Data/Query.hs+37−71
@@ -9,8 +9,7 @@
99 FlexibleInstances,
1010 KindSignatures,
1111 DeriveAnyClass,
12− RecordWildCards,
13− ScopedTypeVariables #-}
12+ RecordWildCards #-}
1413
1514{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
1615
@@ -64,9 +63,7 @@ module Data.Query (
6463 killAllTokens,
6564 channelCountForUser,
6665 pruneAuth,
67− searchChannels,
68− isValidFaculty,
69− groupChatIsValidForUser
66+ searchChannels
7067 ) where
7168
7269import Squeal.PostgreSQL
@@ -96,7 +93,7 @@ import Data.Text (Text)
9693import qualified Data.Text as T
9794import Data.Channel.NamedChannel
9895import Data.Channel.AnonymousChannel
99−import Data.Maybe (fromMaybe, catMaybes, isJust)
96+import Data.Maybe (fromMaybe, catMaybes)
10097import Squeal.PostgreSQL.Render
10198import Data.GroupChat
10299import Data.Message
@@ -108,8 +105,6 @@ import Data.Foldable (fold)
108105import Data.Char
109106import qualified Data.Vector as V
110107import Server.Auth.Token
111−import Server.Error
112−import Data.Text.Validator
113108
114109-- MARK: Documentation
115110
@@ -318,7 +313,8 @@ getAllChannelsForUserQ = selectStar $
318313searchChannelsForUserQ :: Query Schema (TuplePG (UserId, Text)) (RowPG NamedChannelFull)
319314searchChannelsForUserQ = selectStar $
320315 from (table #channels) &
321− where_ (#namedChannelFullOwner .== param @1) &
316+ where_ (#namedChannelFullOwner .== param @1 .&&
317+ vector @@ query) &
322318 orderBy [
323319 tsRankCd vector1 query & Desc,
324320 tsRankCd vector query & Desc
@@ -790,14 +786,6 @@ pruneGhostUsersQ = deleteFrom_ #users
790786
791787-- MARK: FrontEnd Data Structures
792788
793−instance HasValidatableText UserCreation where
794− validateText UserCreation{..} = all validateText [
795− userCreationFirstName,
796− userCreationMiddleName,
797− userCreationLastName,
798− userCreationUserFaculty,
799− userCreationUserEmail]
800−
801789data UserCreation = UserCreation {
802790 userCreationFirstName :: Text,
803791 userCreationMiddleName :: Text,
@@ -863,13 +851,11 @@ getChannelForUser uId cId = do
863851 Nothing -> lift $ S.throwError S.err404
864852
865853-- TODO: Reject bad requests. Should be binary.
866−verifyUsers :: [UserId] -> StaticPQ ()
867−verifyUsers uIds =
854+verifiedUsers :: [UserId] -> StaticPQ [UserId]
855+verifiedUsers uIds =
868856 case verifiedUserIdsQ uIds of
869− Just uIdsQ -> do
870− (us :: [UserId]) <- fmap fromOnly <$> (runQuery uIdsQ >>= getRows)
871− when (length us /= length uIds) $ lift $ S.throwError impossibleContent
872− Nothing -> return ()
857+ Just uIdsQ -> fmap fromOnly <$> (runQuery uIdsQ >>= getRows)
858+ Nothing -> return []
873859
874860deleteGroupChat :: GroupChatId -> StaticPQ ()
875861deleteGroupChat gId = do
@@ -931,18 +917,17 @@ getAllUsers :: StaticPQ [User Faculty]
931917getAllUsers = fmap rowToUser <$> (runQuery getAllUsersQ >>= getRows)
932918
933919publishPost :: UserId -> PostCreation -> StaticPQ PostId
934−publishPost uId pc = commitedTransactionallyUpdate $ do
920+publishPost uId pc = commitedTransactionallyUpdate $ do
935921 pId' <- manipulateParams createPostRowQ (uId, postCreationTags pc) >>= firstRow
936922 case pId' of
937923 (Just (Only pId)) -> do
938− traversePrepared_ createPostElementRowsQ $ elementsToRows pId (map stripPostElement $ postCreationBody pc)
924+ traversePrepared_ createPostElementRowsQ $ elementsToRows pId (postCreationBody pc)
939925 return pId
940926 _ -> lift $ S.throwError S.err404
941927
942928createNewChannel :: UserId -> NamedChannelCreationRequest -> StaticPQ (Maybe NamedChannelId)
943929createNewChannel uId req = commitedTransactionallyUpdate $ do
944− let uIds = toList $ namedChannelCreationRequestPeopleIds req
945− verifyUsers uIds
930+ uIds <- verifiedUsers . toList $ namedChannelCreationRequestPeopleIds req
946931 cId <- manipulateParams createNamedChannelQ (addUserToChannelCreate uId (req {namedChannelCreationRequestPeopleIds = fromList uIds})) >>= firstRow
947932 return $ fmap fromOnly cId
948933
@@ -950,21 +935,15 @@ channelCountForUser :: UserId -> StaticPQ Int64
950935channelCountForUser uId = (fromMaybe 0 . fmap fromOnly) <$> (runQueryParams countChannelsQ (Only uId) >>= firstRow)
951936
952937updateChannel :: UserId -> NamedChannel UserId -> StaticPQ ()
953−updateChannel uId req = do
954− let uIds = namedChannelPeopleIds req
955− verifyUsers (toList uIds)
938+updateChannel uId req = commitedTransactionallyUpdate $ do
939+ uIds <- fmap fromList . verifiedUsers . toList $ namedChannelPeopleIds req
956940 _ <- getChannelForUser uId $ namedChannelId req
957− _ <- commitedTransactionallyUpdate $ manipulateParams updateNamedChannelQ $ addUserToChannelUpdate uId (req {namedChannelPeopleIds = uIds})
941+ _ <- manipulateParams updateNamedChannelQ $ addUserToChannelUpdate uId (req {namedChannelPeopleIds = uIds})
958942 return ()
959943
960944getFaculty :: Text -> StaticPQ Faculty
961945getFaculty t = runQueryParams getFacultyQ (Only t) >>= firstRow >>= fromMaybe404
962946
963−isValidFaculty :: FacultyUrl -> StaticPQ Bool
964−isValidFaculty url = do
965− (faculty :: Maybe Faculty) <- runQueryParams getFacultyQ (Only url) >>= firstRow
966− return $ isJust faculty
967−
968947updateFaculty :: Faculty -> StaticPQ ()
969948updateFaculty fac = void $ manipulateParams updateFacultyQ fac
970949
@@ -1001,11 +980,12 @@ getAllChannels uId = fmap (map removeUserFromChannel) $
1001980 runQueryParams getAllChannelsForUserQ (Only uId) >>= getRows
1002981
1003982deleteNamedChannel :: UserId -> NamedChannelId -> StaticPQ ()
1004−deleteNamedChannel uId cId = do
983+deleteNamedChannel uId cId = commitedTransactionallyUpdate $ do
1005984 _ <- getChannelForUser uId cId
1006− _ <- commitedTransactionallyUpdate $ manipulateParams deleteNamedChannelQ (Only cId)
985+ _ <- manipulateParams deleteNamedChannelQ (Only cId)
1007986 return ()
1008987
988+-- Returns n most popular
1009989getTags :: Limit -> StaticPQ [String]
1010990getTags lim = map fromOnly <$> (runQuery (getNpopularTagsQ lim) >>= getRows)
1011991
@@ -1043,37 +1023,32 @@ getMessagesInGroupChat selfId gId mId lim dir = do
10431023 return . catMaybes . map restoreMessage $ resp
10441024
10451025sendMessage :: UserId -> MessageCreation -> StaticPQ MessageId
1046−sendMessage selfId (MessageCreation (Just gId) uId@Nothing (Jsonb body)) = do
1026+sendMessage selfId (MessageCreation (Just gId) uId@Nothing body) = do
10471027 _ <- getGroupChatForUser selfId gId
1048− (Only mId) <- manipulateParams createMessageQ (selfId, Just gId, uId, Jsonb $ stripPostElement body)
1028+ (Only mId) <- manipulateParams createMessageQ (selfId, Just gId, uId, body)
10491029 >>= firstRow >>= fromMaybe500
10501030 return mId
1051−sendMessage selfId (MessageCreation gId@Nothing (Just uId) (Jsonb body)) = do
1052− verifyUsers [uId]
1053− (Only mId) <- manipulateParams createMessageQ (selfId, gId, Just uId, Jsonb $ stripPostElement body)
1031+sendMessage selfId (MessageCreation gId@Nothing (Just uId) body) = do
1032+ (Only mId) <- manipulateParams createMessageQ (selfId, gId, Just uId, body)
10541033 >>= firstRow >>= fromMaybe500
10551034 return mId
10561035sendMessage _ _ = lift $ S.throwError S.err400
10571036
10581037createGroupChat :: UserId -> GroupChatCreation -> StaticPQ GroupChatId
1059−createGroupChat uId (GroupChatCreation (Jsonb chatUsers) name) = do
1060− verifyUsers . M.keys $ chatUsers
1061− commitedTransactionallyUpdate $ do
1062− let chatUsers' = M.insert uId maxChatPermissions chatUsers
1063− (Only chatId) <- manipulateParams createGroupChatQ (GroupChatCreation (Jsonb chatUsers') name)
1064− >>= firstRow >>= fromMaybe500
1065− _ <- manipulateParams createPhantomMessageQ (Only chatId)
1066− return chatId
1038+createGroupChat uId (GroupChatCreation (Jsonb chatUsers) name) = commitedTransactionallyUpdate $ do
1039+ let chatUsers' = M.insert uId maxChatPermissions chatUsers
1040+ (Only chatId) <- manipulateParams createGroupChatQ (GroupChatCreation (Jsonb chatUsers') name)
1041+ >>= firstRow >>= fromMaybe500
1042+ _ <- manipulateParams createPhantomMessageQ (Only chatId)
1043+ return chatId
10671044
10681045updateGroupChat :: UserId -> GroupChat -> StaticPQ ()
1069−updateGroupChat uId (GroupChat gId (Jsonb perms) name) = do
1070− verifyUsers . M.keys $ perms
1071− commitedTransactionallyUpdate $ do
1072− (GroupChat _ (Jsonb perms') _) <- getGroupChatForUser uId gId
1073− let userPerm = fromMaybe minChatPermissions $ M.lookup uId perms'
1074− unless (isAdmin userPerm) $ lift $ S.throwError $ S.err401
1075− let newPerms = M.insert uId userPerm perms
1076− void $ manipulateParams updateGroupChatQ (GroupChat gId (Jsonb newPerms) name)
1046+updateGroupChat uId (GroupChat gId (Jsonb perms) name) = commitedTransactionallyUpdate $ do
1047+ (GroupChat _ (Jsonb perms') _) <- getGroupChatForUser uId gId
1048+ let userPerm = fromMaybe minChatPermissions $ M.lookup uId perms'
1049+ unless (isAdmin userPerm) $ lift $ S.throwError $ S.err401
1050+ let newPerms = M.insert uId userPerm perms
1051+ void $ manipulateParams updateGroupChatQ (GroupChat gId (Jsonb newPerms) name)
10771052
10781053leaveGroupChat :: UserId -> GroupChatId -> StaticPQ ()
10791054leaveGroupChat uId gId = commitedTransactionallyUpdate $ do
@@ -1090,24 +1065,15 @@ getGroupChatForUser uId gId = do
10901065 (Just c) -> return c
10911066 Nothing -> lift $ S.throwError S.err404
10921067
1093−groupChatIsValidForUser :: UserId -> GroupChatId -> StaticPQ Bool
1094−groupChatIsValidForUser uId gId = do
1095− (chat :: Maybe GroupChat) <- runQueryParams getGroupChatForUserQ (show uId, gId) >>= firstRow
1096− return $ isJust chat
1097−
10981068getFacultyFromQuery :: T.Text -> StaticPQ [Faculty]
10991069getFacultyFromQuery query =
11001070 runQueryParams getFacultySearchResultQ (Only . queryFromTextAbbr $ query) >>= getRows
11011071
11021072searchUsers :: T.Text -> StaticPQ [User Faculty]
1103−searchUsers query = do
1104− unless (validateText query) $ lift $ throwError lengthExceeded
1105− if (T.null . T.filter isAlpha $ query)
1106− then return []
1107− else map rowToUser <$> (runQueryParams searchUsersQ (Only . queryFromText $ query) >>= getRows)
1073+searchUsers query =
1074+ map rowToUser <$> (runQueryParams searchUsersQ (Only . queryFromText $ query) >>= getRows)
11081075
11091076searchTags :: T.Text -> StaticPQ [Text]
1110−searchTags "" = return []
11111077searchTags query = map fromOnly <$>
11121078 (runQueryParams searchTagsQ
11131079 (Only . (<> ":*") . T.strip $ query) >>= getRows)
@@ -1231,7 +1197,7 @@ queryFromTextAbbr = T.unwords . L.intersperse "&" . map processTerm . filter (no
12311197 ) <> ")"
12321198
12331199queryFromText :: Text -> Text
1234−queryFromText = T.unwords . L.intersperse "&" . map processTerm . filter (not . T.null) . T.split (not . isPrint)
1200+queryFromText = T.unwords . L.intersperse "&" . map processTerm . filter (not . T.null) . T.split (not . isAlphaNum)
12351201 where
12361202 processTerm :: Text -> Text
12371203 processTerm t = t <> ":*"
denis/src/Data/Tags/Validation.hsdeleted−25
@@ -1,25 +0,0 @@
1−module Data.Tags.Validation (
2− validateTag,
3− validateTag'
4− ) where
5−
6−import Data.Text as T
7−import Data.Char as C
8−import qualified Data.Set as S
9−
10−validateTag :: T.Text -> Maybe T.Text
11−validateTag tag =
12− let
13− normalised = T.strip $ tag
14− in if validateTag' normalised
15− then Just normalised
16− else Nothing
17−
18−validateTag' :: T.Text -> Bool
19−validateTag' = T.all validChar
20−
21−validChars :: S.Set Char
22−validChars = S.fromList "йцукенгшщзхъфывапролджэёячсмитьбюqwertyuiopasdfghjklzxcvbnm1234567890_"
23−
24−validChar :: Char -> Bool
25−validChar c = S.member (C.toLower c) validChars
denis/src/Data/Text/Validator.hsdeleted−59
@@ -1,59 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−
3−module Data.Text.Validator (
4− HasValidatableText(..),
5− isValidChar,
6− ExtraPredicate(..),
7− (~=),
8− (~<),
9− (<~>),
10− RangeableText(..)
11− ) where
12−
13−import qualified Data.Text as T
14−import Data.Char
15−import Data.Limits
16−
17−isValidChar :: Char -> Bool
18−isValidChar '\n' = True
19−isValidChar '\r' = True
20−isValidChar c = isPrint c || generalCategory c == ModifierLetter
21−
22−class HasValidatableText t where
23− validateText :: t -> Bool
24−
25−data ExtraPredicate t = ExtraPredicate t (t -> Bool)
26−
27−infixl 5 ~=
28−(~=) :: t -> (t -> Bool) -> ExtraPredicate t
29−(~=) = ExtraPredicate
30−
31−instance (HasValidatableText t) => HasValidatableText (ExtraPredicate t) where
32− validateText (t `ExtraPredicate` p) = validateText t && p t
33−
34−
35−infixl 5 ~<
36−(~<) :: t -> Int -> RangeableText t
37−t ~< n = t <~> (0, n)
38−
39−infixl 5 <~>
40−(<~>) :: t -> (Int, Int) -> RangeableText t
41−(<~>) = RangeableText
42−
43−data RangeableText t = RangeableText t (Int, Int)
44−
45−instance HasValidatableText (RangeableText String) where
46− validateText (t `RangeableText` (lower, upper)) = len >= lower && len <= upper && validateText t
47− where
48− len = length t
49−
50−instance HasValidatableText (RangeableText T.Text) where
51− validateText (t `RangeableText` (lower, upper)) = len >= lower && len <= upper && validateText t
52− where
53− len = T.length t
54−
55−instance HasValidatableText T.Text where
56− validateText t = T.all isValidChar t && (not . T.null) t && T.length t < globalTextLimit
57−
58−instance HasValidatableText String where
59− validateText t = all isValidChar t && length t > 0 && length t < globalTextLimit
denis/src/Data/User.hs+1−7
@@ -6,8 +6,7 @@
66 TypeApplications ,
77 TypeOperators,
88 OverloadedStrings,
9− DeriveAnyClass,
10− RecordWildCards #-}
9+ DeriveAnyClass #-}
1110
1211module Data.User (
1312 User(..),
@@ -24,8 +23,6 @@ import Data.Aeson
2423import Servant.Docs (ToSample, toSamples, samples)
2524import Data.Proxy
2625import Data.Faculty
27−import Data.Limits
28−import Data.Text.Validator
2926
3027-- MARK: Documentation
3128
@@ -35,9 +32,6 @@ instance (ToSample f) => ToSample (User f) where
3532instance ToSample (UserUpdate) where
3633 toSamples _ = samples $ [UserUpdate "Vasya" "Pupkinovuch" "Pupkin", UserUpdate "Seva" "Algebrovich" "Leonidov"] <*> ["cs.hse.ru/dse/"]
3734
38−instance HasValidatableText UserUpdate where
39− validateText UserUpdate{..} = all (validateText . (~< userFieldLengthLimit)) [userUpdateFirstName, userUpdateMiddleName, userUpdateLastName, userUpdateUserFaculty]
40−
4135-- MARK: Implementation
4236
4337type UserId = Int64
denis/src/Server/API.hs+1−6
@@ -35,8 +35,6 @@ import Server.API.Faculty
3535import Data.Text (Text)
3636import Data.Maybe (isJust)
3737import Control.Monad.Except
38−import Server.Error
39−import Data.Text.Validator
4038
4139-- MARK: Documentation
4240
@@ -94,10 +92,7 @@ mkServerAPI l =
9492
9593updateUserApi :: UserId -> UserUpdate -> App NoContent
9694updateUserApi uId UserUpdate{..} = do
97− unless (validateText $ userUpdateUserFaculty ~< 100) $ throwError lengthExceeded
98− facultyIsValid <- runQerror $ isValidFaculty userUpdateUserFaculty
99− unless (facultyIsValid) $ throwError impossibleContent
100− res <- runQ impossibleContent $ updateUser UserUpdateRow {
95+ res <- runQerror $ updateUser UserUpdateRow {
10196 userUpdateRowId = uId,
10297 userUpdateRowFirstName = userUpdateFirstName,
10398 userUpdateRowMiddleName = userUpdateMiddleName,
denis/src/Server/API/Channels.hs+13−28
@@ -4,8 +4,7 @@
44 OverloadedLabels,
55 OverloadedStrings ,
66 TypeApplications ,
7− TypeOperators,
8− RecordWildCards #-}
7+ TypeOperators #-}
98
109module Server.API.Channels (
1110 ChannelsApi,
@@ -22,13 +21,9 @@ import Data.Channel.NamedChannel
2221import Data.Channel.AnonymousChannel
2322import qualified Data.Post as P
2423import Server.Query.Pagination
24+import Data.Int
2525import Server.Error
2626import Data.Text (Text)
27−import Data.Text.Validator
28−import Control.Monad
29−import Data.Limits
30−import qualified Data.Text as T
31−import Data.Char
3227
3328type CreateDescription = Description "Creates a new channel\n\nReturns 406 if the channel limit has been exceeded."
3429
@@ -52,32 +47,22 @@ getChannelsApi :: UserId -> App [NamedChannel UserId]
5247getChannelsApi uId = runQnotFound $ getAllChannels uId
5348
5449getAnonymousChannelsApi :: PaginatingRequest P.PostId (Maybe AnonymousChannel) -> App (ResponseWithUsers [P.Post])
55−getAnonymousChannelsApi req'@(PaginatingRequest pId lim (Just req) dir) = do
56− unless (validateText req && validatePaginationRequest req') $ throwError lengthExceeded
57− runQnotFound $ getAnonymousChannelPosts pId lim dir req
58−getAnonymousChannelsApi req@(PaginatingRequest pId lim Nothing dir) = do
59− unless (validatePaginationRequest req) $ throwError lengthExceeded
60− maybeNotFound . runQnotFound $ getLastPosts pId lim dir
50+getAnonymousChannelsApi (PaginatingRequest pId lim (Just req) dir) = runQnotFound $ getAnonymousChannelPosts pId lim dir req
51+getAnonymousChannelsApi (PaginatingRequest pId lim Nothing dir) = maybeNotFound . runQnotFound $ getLastPosts pId lim dir
6152
6253getChannel :: UserId -> PaginatingRequest P.PostId (Maybe NamedChannelId) -> App (ResponseWithUsers [P.Post])
63−getChannel uId req@(PaginatingRequest pId lim (Just cId) dir) = do
64− unless (validatePaginationRequest req) $ throwError lengthExceeded
65− runQnotFound $ getChannelPosts uId pId lim dir cId
66−getChannel _ req@(PaginatingRequest pId lim Nothing dir) = do
67− unless (validatePaginationRequest req) $ throwError lengthExceeded
68− maybeNotFound . runQnotFound $ getLastPosts pId lim dir
54+getChannel uId (PaginatingRequest pId lim (Just cId) dir) = runQnotFound $ getChannelPosts uId pId lim dir cId
55+getChannel _ (PaginatingRequest pId lim Nothing dir) = maybeNotFound . runQnotFound $ getLastPosts pId lim dir
6956
7057createChannelApi :: UserId -> NamedChannelCreationRequest -> App NamedChannelId
7158createChannelApi uId req = do
7259 cCount <- runQerror $ channelCountForUser uId
73− unless (validateText req) $ throwError lengthExceeded
7460 if cCount >= channelCountLimit
75− then throwError $ amountExceeded
61+ then throwError $ limitExceeded "You have exceeded the number of channels you can create."
7662 else maybeNotFound . runQerror $ createNewChannel uId req
7763
7864updateChannelApi :: UserId -> NamedChannel UserId -> App NoContent
79−updateChannelApi uId req@NamedChannel{..} = do
80− unless (validateText req) $ throwError lengthExceeded
65+updateChannelApi uId req = do
8166 runQnotFound $ updateChannel uId req
8267 return NoContent
8368
@@ -87,8 +72,8 @@ deleteChannelApi uId cId = do
8772 return NoContent
8873
8974searchChannelsApi :: UserId -> Text -> App [NamedChannel UserId]
90−searchChannelsApi uId query = do
91− unless (validateText query) $ throwError lengthExceeded
92− if (T.null . T.filter (not . isSpace) . T.filter isPrint) query
93− then return []
94− else runQerror . searchChannels uId $ query
75+searchChannelsApi uId = runQerror . searchChannels uId
76+
77+-- TODO: Increase
78+channelCountLimit :: Int64
79+channelCountLimit = 5
denis/src/Server/API/Completions.hs+1−10
@@ -19,10 +19,6 @@ import Server.App
1919import Data.Connection
2020import Data.Query
2121import Data.Text (Text)
22−import Data.Tags.Validation
23−import Server.Error
24−import qualified Data.Text as T
25−import Data.Char
2622
2723type CompletionsDescription = Description "\
2824 \Gets tag completion tree\n\n\
@@ -75,9 +71,4 @@ completionHandler :: App CompletionTree
7571completionHandler = runQerror $ mkCompletionTree <$> getTags 1000
7672
7773tagSearch :: Text -> App [Text]
78−tagSearch query' =
79− if T.null . T.filter (not . isSpace) . T.filter isPrint $ query'
80− then return []
81− else do
82− query <- fromMaybeThrow formatError . return $ validateTag query'
83− runQerror . searchTags $ query
74+tagSearch = runQerror . searchTags
denis/src/Server/API/Faculty.hs+1−11
@@ -18,12 +18,7 @@ import Server.App
1818import Data.Connection
1919import Data.Query
2020import Data.Text (Text)
21−import qualified Data.Text as T
2221import Data.Faculty
23−import Data.Text.Validator
24−import Control.Monad
25−import Server.Error
26−import Data.Char
2722
2823type SearchDescription = Description "Returns faculties for given string"
2924
@@ -34,9 +29,4 @@ facultyServer :: ServerT FacultyAPI App
3429facultyServer = search
3530
3631search :: Text -> App [Faculty]
37−search query = do
38− if T.null . T.filter (not . isSpace) . T.filter isPrint $ query
39− then return []
40− else do
41− unless (validateText query) $ throwError lengthExceeded
42− runQerror . getFacultyFromQuery $ query
32+search = runQerror . getFacultyFromQuery
denis/src/Server/API/Messages.hs+9−35
@@ -4,8 +4,7 @@
44 OverloadedLabels,
55 OverloadedStrings ,
66 TypeApplications ,
7− TypeOperators,
8− RecordWildCards #-}
7+ TypeOperators #-}
98
109module Server.API.Messages (
1110 MessagesApi,
@@ -22,12 +21,6 @@ import Server.Query.Pagination
2221import Data.Message
2322import Data.GroupChat
2423import Server.Query.Dialog
25−import Data.Text.Validator
26−import Control.Monad
27−import Server.Error
28−import qualified Data.Map as M
29−import Squeal.PostgreSQL.Schema
30−import Data.Limits
3124
3225type GetAllChatsDescription = Description "Gets chats starting from/ending with the given message id."
3326type GetGroupChatDescription = Description "Gets information about a given group chat."
@@ -65,46 +58,27 @@ messagesServer uId =
6558
6659
6760getChats :: UserId -> PaginatingRequest MessageId (Maybe ()) -> App (ResponseWithUsers [Dialog])
68−getChats uId req@(PaginatingRequest mId lim _ dir) = do
69− unless (validatePaginationRequest req) $ throwError lengthExceeded
70− runQerror $ getDialogs uId mId lim dir
61+getChats uId (PaginatingRequest mId lim _ dir) = runQerror $ getDialogs uId mId lim dir
7162
7263getGroupChat :: UserId -> PaginatingRequest MessageId GroupChatId -> App [Message]
73−getGroupChat uId req@(PaginatingRequest mId lim gId dir) = do
74− unless (validatePaginationRequest req) $ throwError lengthExceeded
64+getGroupChat uId (PaginatingRequest mId lim gId dir) =
7565 runQerror $ getMessagesInGroupChat uId gId mId lim dir
7666
7767getUserChat :: UserId -> PaginatingRequest MessageId UserId -> App [Message]
78−getUserChat uId req@(PaginatingRequest mId lim dId dir) = do
79− unless (validatePaginationRequest req) $ throwError lengthExceeded
68+getUserChat uId (PaginatingRequest mId lim dId dir) =
8069 runQerror $ getMessagesInUserChat uId dId mId lim dir
8170
8271getGroupChatInfo :: UserId -> GroupChatId -> App GroupChat
83−getGroupChatInfo uId gId = do
84− groupIsValid <- runQerror $ groupChatIsValidForUser uId gId
85− unless (groupIsValid) $ throwError impossibleContent
86− runQerror $ getGroupChatForUser uId gId
72+getGroupChatInfo uId gId = runQerror $ getGroupChatForUser uId gId
8773
8874updateGroupChatApi :: UserId -> GroupChat -> App NoContent
89−updateGroupChatApi uId group = do
90− unless (validateText group) $ throwError lengthExceeded
91− groupIsValid <- runQerror $ groupChatIsValidForUser uId (groupChatId group)
92− unless (groupIsValid) $ throwError impossibleContent
93− runQerror (updateGroupChat uId group) >> return NoContent
75+updateGroupChatApi uId group = runQerror (updateGroupChat uId group) >> return NoContent
9476
9577sendMessageApi :: UserId -> MessageCreation -> App MessageId
96−sendMessageApi uId mes = do
97− unless (validateText mes) $ throwError lengthExceeded
98− runQerror $ sendMessage uId mes
78+sendMessageApi uId mes = runQerror $ sendMessage uId mes
9979
10080createGroupChatApi :: UserId -> GroupChatCreation -> App GroupChatId
101−createGroupChatApi uId group@GroupChatCreation{..} = do
102− unless (validateText group) $ throwError lengthExceeded
103− unless ((M.size $ getJsonb groupChatCreationUsers) < groupChatSizeLimit) $ throwError amountExceeded
104− runQerror $ createGroupChat uId group
81+createGroupChatApi uId group = runQerror $ createGroupChat uId group
10582
10683leaveChatAPI :: UserId -> GroupChatId -> App NoContent
107−leaveChatAPI uId gId = do
108− groupIsValid <- runQerror $ groupChatIsValidForUser uId gId
109− unless (groupIsValid) $ throwError impossibleContent
110− runQerror (leaveGroupChat uId gId) >> return NoContent
84+leaveChatAPI uId gIg = runQerror (leaveGroupChat uId gIg) >> return NoContent
denis/src/Server/API/Posts.hs+4−16
@@ -19,10 +19,6 @@ import Data.Query
1919import Data.User
2020import Data.Connection
2121import Server.Query.Pagination
22−import Data.Limits
23−import Server.Error
24−import Control.Monad
25−import Data.Text.Validator
2622
2723-- MARK: Implementation
2824
@@ -35,21 +31,13 @@ postApi :: UserId -> ServerT PostApi App
3531postApi uId = getPostsApi :<|> lastPosts :<|> getPostsForUserApi :<|> publishPostApi uId
3632
3733getPostsApi :: [P.PostId] -> App (ResponseWithUsers [P.Post])
38−getPostsApi req = do
39− unless (listLimit < length req) $ throwError lengthExceeded
40− maybeNotFound . runQnotFound . getPosts $ req
34+getPostsApi = maybeNotFound . runQnotFound . getPosts
4135
4236lastPosts :: PaginatingRequest P.PostId (Maybe ()) -> App (ResponseWithUsers [P.Post])
43−lastPosts req@(PaginatingRequest pId lim _ dir) = do
44− unless (validatePaginationRequest req) $ throwError lengthExceeded
45− maybeNotFound . runQnotFound $ getLastPosts pId lim dir
37+lastPosts (PaginatingRequest pId lim _ dir) = maybeNotFound . runQnotFound $ getLastPosts pId lim dir
4638
4739getPostsForUserApi :: PaginatingRequest P.PostId UserId -> App (ResponseWithUsers [P.Post])
48−getPostsForUserApi req@(PaginatingRequest pId lim uId dir) = do
49− unless (validatePaginationRequest req) $ throwError lengthExceeded
50− maybeNotFound . runQnotFound $ getPostsForUser pId lim dir uId
40+getPostsForUserApi (PaginatingRequest pId lim uId dir) = maybeNotFound . runQnotFound $ getPostsForUser pId lim dir uId
5141
5242publishPostApi :: UserId -> P.PostCreation -> App P.PostId
53−publishPostApi uId creation = do
54− unless (validateText creation) $ throwError lengthExceeded
55− runQerror . publishPost uId $ creation
43+publishPostApi uId = runQerror . publishPost uId
denis/src/Server/App.hs+2−11
@@ -15,8 +15,7 @@ module Server.App (
1515 ask,
1616 asks,
1717 liftIO,
18− MailConfig(..),
19− fromMaybeThrow
18+ MailConfig(..)
2019) where
2120
2221import Servant.Server
@@ -26,7 +25,6 @@ import Data.Schema
2625import Generics.SOP (K)
2726import Squeal.PostgreSQL.PQ (Connection)
2827import Crypto.RNG
29−import Server.Error
3028
3129type App = ReaderT Config Handler
3230
@@ -47,11 +45,4 @@ data MailConfig = MailConfig {
4745}
4846
4947instance CryptoRNG App where
50− randomBytes n = join $ asks (liftIO . randomBytesIO n . cryptoState)
51−
52−fromMaybeThrow :: ServantErr -> App (Maybe a) -> App a
53−fromMaybeThrow err mb = do
54− a <- mb
55− case a of
56− Just b -> return b
57− Nothing -> throwError err
48+ randomBytes n = join $ asks (liftIO . randomBytesIO n . cryptoState)
denis/src/Server/Auth.hs+4−22
@@ -11,7 +11,6 @@
1111 DeriveAnyClass,
1212 RecordWildCards #-}
1313{-# OPTIONS_GHC -Wno-orphans #-}
14−
1514module Server.Auth (
1615 checkToken,
1716 cookieTokenKey,
@@ -20,8 +19,7 @@ module Server.Auth (
2019 logIn,
2120 contextProxy,
2221 AuthenticationCredits(..),
23− authenticationAPI,
24− TokenActivationData
22+ authenticationAPI
2523) where
2624
2725import Server.App
@@ -56,8 +54,6 @@ import Server.Auth.Mail
5654import Web.UAParser
5755import qualified Data.Text.Lazy as TL
5856import Server.Auth.HTML
59−import Data.Char
60−import Data.Text.Validator
6157
6258-- MARK: Documentation
6359
@@ -164,7 +160,7 @@ data AuthenticationCookieData = AuthenticationCookieData {
164160data TokenActivationData = TokenActivationData {
165161 tokenVerificationUserId :: UserId,
166162 tokenVerificationData :: BS.ByteString
167−} deriving (Show, GHC.Generic)
163+} deriving (Show)
168164
169165instance ToJSON TokenActivationData where
170166 toJSON (TokenActivationData uId vData) = object [
@@ -314,9 +310,7 @@ checkToken conn (AuthenticationCookieData uId tokenData) = do
314310register :: UserCreation -> Maybe String -> App (Headers '[Header "Set-Cookie" String] NoContent)
315311register creation' ua' = do
316312 let creation = normalizaUser creation'
317− unless (validateUser creation && validateText creation) $ throwError err400
318− validFaculty <- runQerror $ isValidFaculty (userCreationUserFaculty creation)
319− unless validFaculty $ throwError impossibleContent
313+ unless (validateUser creation) $ throwError err400
320314 genToken <- generateTokenM
321315 let ua = getUserAgent ua'
322316 GeneratedToken token value code activation deactivation <- runQ err409 . commitedTransactionallyUpdate $ do
@@ -386,11 +380,8 @@ validateEmail :: T.Text -> Bool
386380validateEmail "ilyakooo0@gmail.com" = True
387381validateEmail "rednikina.com@yandex.ru" = True
388382validateEmail "admikhaleva@edu.hse.ru" = True
389−validateEmail e = if all validChar email
390− then ((isJust . matchRegex regex $ email) || (isJust . matchRegex falseRegex $ email))
391− else False
383+validateEmail e = (isJust . matchRegex regex . T.unpack $ e) || (isJust . matchRegex falseRegex . T.unpack $ e)
392384 where
393− email = T.unpack $ e
394385 regex = mkRegexWithOpts
395386 "^[A-Z0-9a-z._%-]+@hse\\.ru$"
396387 False
@@ -399,12 +390,3 @@ validateEmail e = if all validChar email
399390 "^[A-Z0-9a-z._%-+]+@edu.hse\\.ru$"
400391 False
401392 False
402−
403−validChar :: Char -> Bool
404−validChar c = isAscii c && isPrint c
405−
406−-- checkString :: String -> App String
407−-- checkString s =
408−-- if any invalidChar s
409−-- then throwError formatError
410−-- else return s
denis/src/Server/Error.hs+4−35
@@ -2,10 +2,7 @@
22
33module Server.Error (
44 invalidToken,
5− amountExceeded,
6− lengthExceeded,
7− formatError,
8− impossibleContent,
5+ limitExceeded,
96 throwError
107) where
118
@@ -20,35 +17,7 @@ invalidToken = ServantErr {
2017 errHeaders = []
2118}
2219
23−amountExceeded :: ServantErr
24−amountExceeded = ServantErr {
25− errHTTPCode = 470,
26− errReasonPhrase = "The amount of elements the user can have has been exceeded.",
27− errBody = "",
28− errHeaders = []
29−}
30−
31−lengthExceeded :: ServantErr
32−lengthExceeded = ServantErr {
33− errHTTPCode = 471,
34− errReasonPhrase = "The length of user content has been exceeded.",
35− errBody = "",
36− errHeaders = []
20+limitExceeded :: String -> ServantErr
21+limitExceeded msg = err406 {
22+ errReasonPhrase = msg
3723}
38−
39−formatError :: ServantErr
40−formatError = ServantErr {
41− errHTTPCode = 472,
42− errReasonPhrase = "The format of the supplied content is invalid.",
43− errBody = "",
44− errHeaders = []
45−}
46−
47−impossibleContent :: ServantErr
48−impossibleContent = ServantErr {
49− errHTTPCode = 473,
50− errReasonPhrase = "The content you have supplied is invalid and/or conflict with the rest of the existing content.",
51− errBody = "",
52− errHeaders = []
53−}
54−
denis/src/Server/Query/Pagination.hs+1−5
@@ -13,8 +13,7 @@
1313
1414module Server.Query.Pagination (
1515 PaginatingRequest(..),
16− PaginationDirection(..),
17− validatePaginationRequest
16+ PaginationDirection(..)
1817 ) where
1918
2019import GHC.Generics
@@ -33,9 +32,6 @@ data PaginatingRequest i r = PaginatingRequest {
3332 direction :: PaginationDirection
3433} deriving (Generic)
3534
36−validatePaginationRequest :: (PaginatingRequest i r) -> Bool
37−validatePaginationRequest PaginatingRequest{limit=lim} = lim > 0 && lim < 100
38−
3935instance (ToJSON r, ToJSON i) => ToJSON (PaginatingRequest i r) where
4036instance (FromJSON r, FromJSON i) => FromJSON (PaginatingRequest i r) where
4137
denis/src/Server/Server.hs+5−15
@@ -1,12 +1,7 @@
1−{-# LANGUAGE
2− OverloadedStrings,
3− TypeOperators,
4− DataKinds #-}
1+{-# LANGUAGE OverloadedStrings #-}
52
63module Server.Server (
7− runServer,
8− hoistedServer,
9− getConfig
4+ runServer
105) where
116
127import Server.API
@@ -54,27 +49,22 @@ getConfig = do
5449 mail <- getMailConfig
5550 return $ Config conn crypto selfRoot mail
5651
57−hoistedServer :: Logger -> Config -> Server API
58−hoistedServer logger cfg =
59− let serverAPI = mkServerAPI logger
60− in hoistServerWithContext serverProxy contextProxy (flip runReaderT cfg) serverAPI
61−
6252runServer :: IO ()
6353runServer = do
6454 port <- getServerPort
6555 cfg <- getConfig
66− updateFaculties cfg
6756 _ <- periodically (1 * hours) $ updateFaculties cfg
6857 _ <- periodically (20 * minutes) $ runDbTask cfg pruneAuth
6958 let ctx = genAuthServerContext $ getPool cfg
7059 (middleLogger, logger) <- mkLogger 500
71− let s = hoistedServer logger cfg
60+ let serverAPI = mkServerAPI logger
61+ let s = hoistServerWithContext serverProxy contextProxy (flip runReaderT cfg) serverAPI
7262 runSettings (settings port) $ cors (const $ Just policy) . middleLogger $ serveWithContext serverProxy ctx s
7363
7464policy :: CorsResourcePolicy
7565policy = CorsResourcePolicy {
7666 corsOrigins = Just (["http://localhost:8080", "http://localhost:4000", "http://127.0.0.1:4000", "http://127.0.0.1:8080", "http://lvh.me:4000", "http://lvh.me:8080", "http://valera-kristina.herokuapp.com", "https://valera-kristina.herokuapp.com"], True), -- Nothing,
77− corsMethods=[methodGet, methodPost, methodDelete],
67+ corsMethods=[methodGet, methodPost],
7868 corsRequestHeaders = ["content-type"],
7969 corsExposedHeaders = Nothing,
8070 corsMaxAge = Nothing,
denis/src/test/Orphans.hsdeleted−104
@@ -1,104 +0,0 @@
1−{-# LANGUAGE
2− OverloadedStrings,
3− TypeOperators,
4− DataKinds,
5− FlexibleInstances,
6− UndecidableInstances #-}
7−
8−module Orphans where
9−
10−import Server.API
11−import Server.Server
12−import Test.QuickCheck
13−import Servant.QuickCheck
14−import Test.Hspec
15−import Server.App
16−import Servant.Server
17−import Data.User
18−import Servant.Server.Experimental.Auth
19−import Network.Wai
20−import Servant.QuickCheck.Internal.HasGenRequest
21−import Servant.API
22−import Server.Query.ComplexQuery
23−import Test.QuickCheck
24−import Server.Query.Pagination
25−import Test.QuickCheck.Arbitrary.Generic
26−import Data.Post
27−import Data.Channel.NamedChannel
28−import Test.QuickCheck.Instances
29−import Server.Auth
30−import Data.Query
31−import Data.User
32−import Data.PostElement
33−import Data.Channel.AnonymousChannel
34−import Data.Channel.NamedChannel
35−import Data.GroupChat
36−import Squeal.PostgreSQL.Schema
37−import Data.Message
38−
39−instance (Arbitrary i, Arbitrary r) => Arbitrary (PaginatingRequest i r) where
40− arbitrary = genericArbitrary
41− shrink = genericShrink
42−
43−instance Arbitrary PaginationDirection where
44− arbitrary = elements [BackPagination, ForwardPagination]
45−
46−instance (Arbitrary u) => Arbitrary (NamedChannel u) where
47− arbitrary = genericArbitrary
48− shrink = genericShrink
49−
50−instance Arbitrary AuthenticationCredits where
51− arbitrary = genericArbitrary
52− shrink = genericShrink
53−
54−instance Arbitrary UserCreation where
55− arbitrary = genericArbitrary
56− shrink = genericShrink
57−
58−instance Arbitrary TokenActivationData where
59− arbitrary = genericArbitrary
60− shrink = genericShrink
61−
62−instance Arbitrary UserUpdate where
63− arbitrary = genericArbitrary
64− shrink = genericShrink
65−
66−instance Arbitrary PostCreation where
67− arbitrary = genericArbitrary
68− shrink = genericShrink
69−
70−instance Arbitrary (PostElement p) where
71− arbitrary = genericArbitrary
72− shrink = genericShrink
73−
74−instance Arbitrary PostQuote where
75− arbitrary = genericArbitrary
76− shrink = genericShrink
77−
78−instance Arbitrary AnonymousChannel where
79− arbitrary = genericArbitrary
80− shrink = genericShrink
81−
82−instance Arbitrary NamedChannelCreationRequest where
83− arbitrary = genericArbitrary
84− shrink = genericShrink
85−
86−instance Arbitrary GroupChat where
87− arbitrary = genericArbitrary
88− shrink = genericShrink
89−
90−instance Arbitrary GroupChatPermissions where
91− arbitrary = genericArbitrary
92− shrink = genericShrink
93−
94−instance Arbitrary GroupChatCreation where
95− arbitrary = genericArbitrary
96− shrink = genericShrink
97−
98−instance Arbitrary MessageCreation where
99− arbitrary = genericArbitrary
100− shrink = genericShrink
101−
102−instance (Arbitrary a) => Arbitrary (Jsonb a) where
103− arbitrary = genericArbitrary
104− shrink = genericShrink
denis/src/test/Spec.hsdeleted−101
@@ -1,101 +0,0 @@
1−{-# LANGUAGE
2− OverloadedStrings,
3− TypeOperators,
4− DataKinds,
5− FlexibleInstances,
6− UndecidableInstances,
7− ScopedTypeVariables #-}
8−
9−import Server.API
10−import Server.Server
11−import Test.QuickCheck
12−import Servant.QuickCheck
13−import Test.Hspec
14−import Server.App
15−import Servant.Server
16−import Data.User
17−import Servant.Server.Experimental.Auth
18−import Network.Wai
19−import Servant.QuickCheck.Internal.HasGenRequest
20−import Servant.API
21−import Server.Query.ComplexQuery
22−import Orphans
23−import qualified Network.HTTP.Client as HTTP
24−import qualified Data.ByteString.Char8 as B
25−import qualified Data.ByteString.Lazy.Char8 as LB
26−import Network.HTTP.Types.Status (statusCode)
27−
28−main :: IO ()
29−main = do
30− cfg <- getCfg
31−
32− hspec $ do
33− let
34− test :: Predicates -> IO ()
35− test predicates = withServantServerAndContext serverProxy mockAuth
36− (return $ hoistedServer (return "") cfg) $ \burl ->
37− serverSatisfies serverProxy burl testArgs
38− (printPredicate <%> predicates)
39−
40− it "Doesn't have errors" $
41− test $ not500 <%> mempty
42−
43− it "Honours accept headers" $
44− test $ honoursAcceptHeader <%> mempty
45−
46− it "Doesn't take longer than 1 second" $
47− test $ notLongerThan (1 * seconds) <%> mempty
48−
49−mockAuth :: Context (AuthHandler Request UserId ': '[])
50−mockAuth = mkAuthHandler (const $ return 3) :. EmptyContext
51−
52−milliseconds :: Integer
53−milliseconds = 1000000
54−
55−seconds :: Integer
56−seconds = 1000 * milliseconds
57−
58−getCfg :: IO Config
59−getCfg = do
60− cfg <- getConfig
61− return cfg {
62− mailConfig = mail
63− }
64−
65−mail :: MailConfig
66−mail = MailConfig {
67− mailHost = "",
68− mailPort = 25,
69− mailUser = "",
70− mailPassword = ""
71−}
72−
73−testArgs :: Args
74−testArgs = Args
75− { replay = Nothing
76− , maxSuccess = 1000
77− , maxDiscardRatio = 10
78− , maxSize = 100
79− , chatty = True
80− , maxShrinks = maxBound
81− }
82−
83−printPredicate = RequestPredicate $ \ req mgr -> do
84− let url = HTTP.path req
85− let (HTTP.RequestBodyLBS bs) = HTTP.requestBody req
86− resp <- HTTP.httpLbs req mgr
87− let code = statusCode $ HTTP.responseStatus resp
88− putStr "\tRequest:\t"
89− putStrLn $ B.unpack url
90− putStrLn . filter (/= '\a') $ LB.unpack bs
91− putStr "\tResponse:\t"
92− print code
93− putStrLn ""
94− return []
95−
96−
97−instance (HasGenRequest a) => HasGenRequest (Authentication :> a) where
98− genRequest (Proxy :: Proxy (Authentication :> a)) = genRequest (Proxy :: Proxy a)
99−
100−instance HasGenRequest api => HasGenRequest (ComplexQuery :> api) where
101− genRequest (Proxy :: Proxy (ComplexQuery :> api)) = genRequest (Proxy :: Proxy api)
dump.sqldeleted−499
@@ -1,499 +0,0 @@
1−
2−
3−--
4−-- Data for Name: groupChats; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
5−--
6−
7−COPY public."groupChats" ("groupChatId", "groupChatUsers", "groupChatName") FROM stdin;
8−12 {"7": {"isAdmin": false}} Untitled1
9−14 {"6": {"isAdmin": false}, "7": {"isAdmin": false}} new chat
10−15 {"7": {"isAdmin": false}} hello
11−19 {"6": {"isAdmin": false}}
12−13 {"6": {"isAdmin": true}, "7": {"isAdmin": false}} Проверка
13−18 {"3": {"isAdmin": true}, "11": {"isAdmin": false}} proverka
14−17 {"6": {"isAdmin": true}, "11": {"isAdmin": false}} Sosatt
15−22 {"6": {"isAdmin": true}} Untitled
16−23 {"6": {"isAdmin": true}} Тест
17−21 {"3": {"isAdmin": true}, "6": {"isAdmin": false}} new name of chat
18−20 {"3": {"isAdmin": true}} Untitled
19−16 {"6": {"isAdmin": true}} Hello
20−\.
21−
22−
23−--
24−-- Data for Name: messages; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
25−--
26−
27−COPY public.messages ("messageStorageId", "messageStorageAuthorId", "messageStorageDestinationGroupId", "messageStorageDestinationUserId", "messageStorageBody", "messageStorageTime") FROM stdin;
28−56 \N 12 \N \N 2019-05-12 19:00:42.217465+00
29−57 \N 13 \N \N 2019-05-12 20:00:15.500388+00
30−60 3 \N 6 {"markdown": "Идёшь завтра на лекцию???"} 2019-05-12 21:16:53.471869+00
31−61 \N 14 \N \N 2019-05-12 21:17:28.177618+00
32−62 3 12 \N {"markdown": "hello"} 2019-05-12 21:17:38.560284+00
33−63 \N 15 \N \N 2019-05-12 21:19:35.607156+00
34−64 6 \N 3 {"markdown": "Da!"} 2019-05-12 22:22:34.636955+00
35−69 6 \N 3 {"markdown": "Проверка"} 2019-05-13 09:00:04.543673+00
36−75 \N 16 \N \N 2019-05-13 09:42:35.621514+00
37−76 3 \N 6 {"markdown": "аааа я пишу с сайта "} 2019-05-13 09:51:45.216052+00
38−77 6 \N 3 {"markdown": ")))"} 2019-05-13 09:59:04.464285+00
39−78 6 \N 3 {"markdown": "Ты еще можешь отправлять сообщения на ентер"} 2019-05-13 09:59:17.664861+00
40−79 \N 17 \N \N 2019-05-13 10:34:51.369108+00
41−80 12 17 \N {"markdown": "New chat!"} 2019-05-13 10:34:59.396363+00
42−81 12 17 \N {"markdown": "Write me something pls"} 2019-05-13 10:38:06.641916+00
43−82 6 17 \N {"markdown": "Hello!"} 2019-05-13 10:39:02.53931+00
44−83 12 16 \N {"markdown": "Hello!"} 2019-05-13 10:49:47.834312+00
45−84 12 17 \N {"markdown": "Кикнула Илью из беседы)"} 2019-05-13 10:50:45.408526+00
46−85 12 17 \N {"markdown": "привет как дела"} 2019-05-13 10:58:54.034185+00
47−86 6 17 \N {"markdown": "Хорошо, а у тебя?"} 2019-05-13 11:00:03.129155+00
48−87 12 \N 3 {"markdown": "Привет!"} 2019-05-13 12:23:21.226991+00
49−88 12 \N 3 {"markdown": "Как у тебя дела?"} 2019-05-13 12:23:28.143832+00
50−89 12 17 \N {"markdown": "Ну так плюс минус жить можно)"} 2019-05-13 12:24:43.553721+00
51−90 12 17 \N {"markdown": "привет"} 2019-05-13 13:16:37.040853+00
52−91 12 17 \N {"markdown": "Всем пис"} 2019-05-13 13:16:50.7915+00
53−92 12 \N 3 {"markdown": "Аняяяя"} 2019-05-13 13:20:25.214522+00
54−93 3 \N 12 {"markdown": "Нормально"} 2019-05-13 14:08:52.660103+00
55−94 3 \N 12 {"markdown": "Ты как?"} 2019-05-13 14:09:00.358669+00
56−95 3 \N 6 {"markdown": "Я так и делала!!!"} 2019-05-13 14:09:48.991844+00
57−96 \N 18 \N \N 2019-05-13 14:18:30.872479+00
58−97 \N 19 \N \N 2019-05-13 15:24:01.710566+00
59−98 3 19 \N {"markdown": "hello"} 2019-05-13 15:27:17.523456+00
60−99 \N 20 \N \N 2019-05-13 15:27:36.736424+00
61−100 3 20 \N {"markdown": "Darya "} 2019-05-13 15:27:53.856998+00
62−101 3 20 \N {"markdown": "pls sent message"} 2019-05-13 15:28:18.065107+00
63−102 \N 21 \N \N 2019-05-13 17:23:22.627985+00
64−103 3 21 \N {"markdown": "hellooooo\\n## markdown"} 2019-05-13 17:27:35.021832+00
65−104 12 18 \N {"markdown": "Help"} 2019-05-13 17:40:57.74548+00
66−105 12 20 \N {"markdown": "Pls"} 2019-05-13 17:41:28.669253+00
67−106 12 20 \N {"markdown": "Send"} 2019-05-13 17:41:33.713239+00
68−107 12 20 \N {"markdown": "Nudes"} 2019-05-13 17:41:37.030689+00
69−108 3 \N 12 {"markdown": "i"} 2019-05-13 18:04:27.566263+00
70−109 3 \N 12 {"markdown": "need"} 2019-05-13 18:04:29.597069+00
71−110 3 \N 12 {"markdown": "a"} 2019-05-13 18:04:30.747952+00
72−111 3 \N 12 {"markdown": "lot"} 2019-05-13 18:04:32.866007+00
73−112 3 \N 12 {"markdown": "of"} 2019-05-13 18:04:34.439089+00
74−113 3 \N 12 {"markdown": "messages"} 2019-05-13 18:04:37.348507+00
75−114 3 \N 12 {"markdown": "lets"} 2019-05-13 18:04:41.274395+00
76−115 3 \N 12 {"markdown": "check "} 2019-05-13 18:04:43.225609+00
77−116 3 \N 12 {"markdown": "pagination"} 2019-05-13 18:04:46.724472+00
78−117 3 \N 12 {"markdown": "))))"} 2019-05-13 18:04:49.117602+00
79−118 3 \N 12 {"markdown": "Darya"} 2019-05-13 18:04:58.079826+00
80−119 3 \N 12 {"markdown": "chto delaesh?"} 2019-05-13 18:05:06.691576+00
81−120 3 \N 12 {"markdown": "native eeeee"} 2019-05-13 18:05:13.633699+00
82−121 3 21 \N {"markdown": "блин мне нравится сидеть с сайта"} 2019-05-13 18:38:41.575588+00
83−122 6 21 \N {"markdown": "Фига"} 2019-05-13 18:59:23.480114+00
84−123 6 21 \N {"markdown": "Неожиданно"} 2019-05-13 18:59:28.098338+00
85−124 6 21 \N {"markdown": "А еще мне тоже не хватает смайликов"} 2019-05-13 18:59:36.924505+00
86−125 6 21 \N {"markdown": "Надо бы прикрутить))"} 2019-05-13 18:59:41.452505+00
87−126 12 \N 3 {"markdown": "Привет"} 2019-05-13 19:30:55.962054+00
88−127 12 \N 3 {"markdown": "У Кости тут время не отображается сообщения, когда ты мне писать то успеваешь!!! _удивлена_ _поражена_ _раздосадована_"} 2019-05-13 19:31:22.510321+00
89−128 12 \N 3 {"markdown": "привет"} 2019-05-13 19:34:50.753794+00
90−129 12 \N 3 {"markdown": "блять ну привет ало"} 2019-05-13 19:35:21.876566+00
91−130 12 \N 3 {"markdown": "Приветттттт!"} 2019-05-13 19:36:01.406531+00
92−131 12 \N 3 {"markdown": ""} 2019-05-13 19:36:02.600456+00
93−132 12 \N 3 {"markdown": "привет"} 2019-05-13 19:36:05.515343+00
94−133 12 \N 3 {"markdown": "где сообщения????"} 2019-05-13 19:36:18.145702+00
95−134 \N 22 \N \N 2019-05-13 19:37:11.909828+00
96−135 \N 23 \N \N 2019-05-13 19:37:28.304101+00
97−136 12 \N 11 {"markdown": "Соси писю"} 2019-05-13 19:56:44.176754+00
98−137 12 \N 3 {"markdown": "Пис"} 2019-05-13 19:58:39.660301+00
99−138 11 \N 12 {"markdown": "Сама I just want people to be happy "} 2019-05-13 19:59:20.391998+00
100−139 12 \N 11 {"markdown": "Починила я твои сэйфэриа"} 2019-05-13 20:19:53.326795+00
101−140 12 \N 11 {"markdown": "Иди и make people happy again сам"} 2019-05-13 20:20:17.66819+00
102−141 6 23 \N {"markdown": "Ну что"} 2019-05-13 20:39:46.038071+00
103−142 6 22 \N {"markdown": "Тесты"} 2019-05-13 20:39:52.353636+00
104−143 6 21 \N {"markdown": "Интеграционные!"} 2019-05-13 20:40:01.990466+00
105−144 6 19 \N {"markdown": "Инновационные!"} 2019-05-13 20:40:08.21061+00
106−145 6 \N 3 {"markdown": "И сюда"} 2019-05-13 20:40:17.047374+00
107−146 6 16 \N {"markdown": "Бадумс"} 2019-05-13 20:40:24.582964+00
108−147 6 17 \N {"markdown": "Все работает? "} 2019-05-13 20:40:33.688546+00
109−148 12 \N 11 {"markdown": "Привет"} 2019-05-13 20:47:15.369318+00
110−149 6 17 \N {"markdown": "Привет!"} 2019-05-13 20:55:12.148039+00
111−150 12 \N 12 {"markdown": "пннггргрг"} 2019-05-13 20:59:00.544809+00
112−154 12 \N 11 {"markdown": "Ильяяяяяяяя"} 2019-05-13 21:02:21.115633+00
113−155 18 \N 12 {"markdown": "Привеееет"} 2019-05-13 21:16:46.458811+00
114−156 12 \N 18 {"markdown": "Привет солнце"} 2019-05-13 21:19:24.020172+00
115−157 11 \N 12 {"markdown": "ильяяяяя"} 2019-05-13 21:36:37.985452+00
116−158 11 \N 12 {"markdown": "ильяяяяя"} 2019-05-13 21:37:04.22538+00
117−159 3 \N 6 {"markdown": "Проверка связи"} 2019-05-13 21:40:41.308023+00
118−160 6 \N 3 {"markdown": "Прием-прием"} 2019-05-13 22:04:37.477219+00
119−161 3 \N 6 {"markdown": "Костя"} 2019-05-13 22:20:58.83048+00
120−162 3 \N 6 {"markdown": "Ты сделал коммит?"} 2019-05-13 22:21:14.089104+00
121−163 3 21 \N {"markdown": "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of ."} 2019-05-13 22:24:06.104804+00
122−164 3 21 \N {"markdown": "### hellllooooo"} 2019-05-13 22:24:16.578672+00
123−\.
124−
125−
126−--
127−-- Data for Name: postElements; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
128−--
129−
130−COPY public."postElements" ("rowElementId", "rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment") FROM stdin;
131−3 0 грустно((((( \n## куда плакать \N \N \N \N
132−4 0 я из **Питера** \n_а вы????_ \N \N \N \N
133−6 0 Я написал длинный пост, а он не отправился, потому что у меня истек токен \N \N \N \N
134−8 0 Я придумал! давайте устроим бунт и уроним сервер Илья! \N \N \N \N
135−10 0 Ну сервер не вопротивился, значит можно))\nТипо для привлечения внимания\nВыглядит хначительно серьезнее)) \N \N \N \N
136−19 0 Тест! \N \N \N \N
137−23 0 Привет \N \N \N \N
138−24 0 Еще раз \N \N \N \N
139−27 0 Новый пост! \N \N \N \N
140−28 0 Всем привет \N \N \N \N
141−29 0 Привет! \N \N \N \N
142−30 0 I am back with information about _UIKit_ !\n\nThe **UIKit** framework provides the required infrastructure for your iOS or tvOS apps. It provides the window and view architecture for implementing your interface, the event handling infrastructure for delivering _Multi-Touch _and other types of input to your app, and the main run loop needed to manage interactions among the user, the system, and your app. Other features offered by the framework include animation support, document support, drawing and printing support, information about the current device, text management and display, search support, accessibility support, app extension support, and resource management.\n \N \N \N \N
143−31 0 IOS is the best!I will publish a lot of posts about it! Follow _iOS_ channel with hashtags about _Apple_, _iOS_, _UIKit_! \N \N \N \N
144−32 0 Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the\n 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.fjedhfdjdhfdjfd \N \N \N \N
145−33 0 San Francisco journalist says police raided his home after he refused to identify source\n\n \N \N \N \N
146−34 0 It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). \N \N \N \N
147−35 0 приветик всем, я пишу с сайтика Кости \n\nэто так прикольноооооооооооо, я хочу смайлики(((( \N \N \N \N
148−36 0 Тоже пишу с сайта Костика! \N \N \N \N
149−37 0 # пост про веб и любовь\n\nЯ люблю веб! А вы? _??????_ Я уверена, что тоже! \N \N \N \N
150−38 0 У меня что нет постов на страничке??? Не может быть!\n \N \N \N \N
151−39 0 Я люблю сосать \N \N \N \N
152−40 0 (очень) \N \N \N \N
153−41 0 _Всем привет_!!! Как дела? _Завтра к МК_ :( \N \N \N \N
154−42 0 Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!!\n\n# не бейте меня это проверка связи\n\n> quote \n> another quote\n\n_help_\n\n\n\n\n\n\nПривет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!! Привет!!!!\n\n# не бейте меня это проверка связи\n\n> quote \n> another quote\n\n_help_ \N \N \N \N
155−43 0 Куку\n \N \N \N \N
156−44 0 ) \N \N \N \N
157−45 0 # New post!\n\nThis is an example post about our _Social network_, **iOS** application! \n\nThis is a demonstration of writing a new _post_\n\n> quote can be made like this!\n\n- list\n- is something 🍀\n- like this \N \N \N \N
158−46 0 )) \N \N \N \N
159−47 0 ###### очень маленький писюн \N \N \N \N
160−48 0 hello, ilya \N \N \N \N
161−49 0 proverka \N \N \N \N
162−\.
163−
164−
165−--
166−-- Data for Name: posts; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
167−--
168−
169−COPY public.posts ("postRowId", "postRowAuthorId", "postRowUpdateTime", "postRowTags") FROM stdin;
170−3 3 2019-05-12 11:06:56.113423+00 {hse,new,tag,hello,я,ничего,не,успеваю}
171−4 3 2019-05-12 11:08:28.56758+00 {Питер,вышка,ура}
172−6 6 2019-05-12 12:41:48.579615+00 {Злюсь}
173−8 6 2019-05-12 13:13:59.958842+00 {Бунт,Бунд,Бунт}
174−10 6 2019-05-12 13:19:59.825686+00 {Бунт,Бунт," Бунт"}
175−19 6 2019-05-13 08:55:38.703923+00 {Apple}
176−23 6 2019-05-13 08:58:18.327297+00 {}
177−24 6 2019-05-13 08:58:57.09688+00 {eb}
178−27 12 2019-05-13 09:56:38.510814+00 {tag}
179−28 12 2019-05-13 10:58:05.757737+00 {ag}
180−29 6 2019-05-13 12:17:40.144256+00 {унт}
181−30 12 2019-05-13 12:31:11.967519+00 {UIKit,mobileDevelopment,iOS}
182−31 12 2019-05-13 12:33:09.981899+00 {mobileDevelopment,Swift,iOS}
183−32 12 2019-05-13 13:27:40.118674+00 {lorem,ipsum}
184−33 3 2019-05-13 18:09:10.681441+00 {привет,новый,пост,Бунт}
185−34 3 2019-05-13 18:10:26.800786+00 {tag,new,lorem}
186−35 3 2019-05-13 18:43:07.731224+00 {obileDevelopment,ышка,ello,Бунт}
187−36 12 2019-05-13 19:27:48.658511+00 {}
188−37 12 2019-05-13 19:29:45.334246+00 {web," js",webDev,kosya,ялюблюсвоюработу,веб}
189−38 12 2019-05-13 19:38:06.900945+00 {}
190−39 11 2019-05-13 20:02:49.600981+00 {}
191−40 11 2019-05-13 20:07:02.280734+00 {злюсь}
192−41 12 2019-05-13 20:52:39.125909+00 {" Бунт"}
193−42 12 2019-05-13 20:54:41.973395+00 {markdown,help}
194−43 12 2019-05-13 21:03:30.724701+00 {}
195−44 6 2019-05-13 21:24:20.968302+00 {orem}
196−45 12 2019-05-13 21:25:21.929693+00 {social_network,tag,markdown}
197−46 6 2019-05-13 21:30:40.207788+00 {lorem}
198−47 11 2019-05-13 21:58:00.669801+00 {}
199−48 3 2019-05-13 22:17:51.138898+00 {писюн,маленький,Илья}
200−49 3 2019-05-13 22:28:48.651112+00 {tags,news,hse,web,js,mobileDevelopment,work,kursach}
201−\.
202−
203−
204−--
205−-- Data for Name: quoteElements; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
206−--
207−
208−COPY public."quoteElements" ("rowElementId", "rowElementOrd", "rowElementMarkdown", "rowElementLatex", "rowElementImage", "rowElementQuote", "rowElementAttachment") FROM stdin;
209−\.
210−
211−
212−--
213−-- Data for Name: quotes; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
214−--
215−
216−COPY public.quotes ("quoteRowId", "quoteRowPostId") FROM stdin;
217−\.
218−
219−
220−--
221−-- Data for Name: tokens; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
222−--
223−
224−COPY public.tokens ("tokenUserId", "tokenValue", "tokenExpiryDate", "tokenVerificationCode", "tokenActivationTriesLeft", "tokenUserAgent", "tokenActivationCode", "tokenDeactivationCode") FROM stdin;
225−17 \\x1a0b89fca490bb64dd1526d2b833079f6954da89e14fb1635623a5800a8c31e25757d75048313d4f07547e228a8971db531faf2be52584307347828ebfc20945 2020-05-12 16:35:45.006489+00 \\xf1fbeea5b543738b22acc2673bbbfa69ea2776f46ba234f85ec251d5dc76287c6351ec553317785c5f23c5bee9c1698a42d27955c5bcaff09305b7ba0ae55310 10 okhttp \\x0b3e68e5974a5e8b29779e78332278d93b58483f5b06584a4821e1c1ea551ce13a28cd7c9e9350f96577843fe82469bc6bc1876c2ee65337025e4d79dfa7fa2d \\x0bf0c404a09f2949e9bc26765bdc0671c79865def476cbb2c39de66bcf7c5d386e26db3687ca0d493443c50fbbf68ba0ffe6864abdd3b08b00400024a7852da1
226−3 \\xd0e9cf3a38ef778783a2d6b4ecc82c30868eed82c62284ed2627eee305c7fbda649e4a40b3e911b7504e18603e8c76ba7291740f3d83200884a3c562256c252a 2020-05-12 18:36:07.869759+00 \N 10 Mac OS X — Chrome \N \\x913b36ffad3a049648705f39fee5ffebc9576ce1c59d4aac0558462325b0ef19c6ea17804cc9097af33792baa9655947ed3c275a9f84a5a5d48ab13effeded1a
227−11 \\x9fb88c39f22896feddaa5e176c6b459701d28f2fa56a83d80b0d8e9fd874618165e934468c2e6932156584daca665b3f41636e4e3dbfd4e9fd70356ede2273d4 2020-05-12 19:57:53.635167+00 \N 10 iOS \N \\xe98830312baaa841f4b7b65c799c852fc12f75dd9edecb42a304cebf2dc6ab149a56d9bdacbd16ffb94aff2c188c5af8639e8ff6b9cd18ef34e31e222c35a0a0
228−12 \\x6c3ea86a478f9f2b3caf61d8bcef018a61b954609e81fb8fdc6dccd9ebce1134e7ef922bbf89c2d274b9cc974b5552b3480042a0980571d79753dcbb143c3d2c 2020-05-12 20:08:01.486261+00 \N 10 iOS \N \\x186d1a9dbcd9e3eb35e69b1cb05309a1635d38b8350cfbfbefdc68bd3f37c11af1d9b5670d6c14827e428bcdedfb8f681d9ac3c982d08d8fb29a62f7ebaf810a
229−6 \\xe10faca3c9403b42d3e5d6ad7dad1bc95be3933b3330b94b55b6f9b6d108cfdac1288e7defbcab6b7e2a94f31756f4f217ed77ae887eac409b268388bc6724cf 2020-05-11 19:57:14.624458+00 \\x5271d7def0733e698a19f9a9378e1b54ba95a8593b2a90a3e404a465f0e8635d56c6894bca0a6eb5c49f5da10ad411e1c316068bb5eed84f4850d71a8a8faf35 10 Windows 10 — Chrome \\xdf4465993fce0f9ea7b628cd78ab65f1513ee2096139a481b35e7cecc18eacdadaa041bbd2fc5cce11a4137787645e1a6054e98da06777361882d9d952011b91 \\x03f547951f4a8eccc92891efa566f4b9f8370ab41bc428a2c78db322da07efde78b4028a2a0e76e2c92a76e29afdafd97411999fa7b57772f3f3b51fc45a4d7a
230−6 \\x6ee17d4e1b23c56f626571c1f7ebdf7f2b5b28973402d4e98e8337f43c3b1e73778580616fead50ae62233df4c9bcdcde47e635f0b62bdebd0ebbeebd1151394 2020-05-12 21:06:00.33602+00 \N 10 Windows 10 — Chrome \N \\x47015bd8699a424bd41f081dde3bd3122cf6b7b2fde87590ba0eb084196c4f693415d50d8f760af208d8313e9f3e8d8f74aa0fba35d58b6ce69a99d8e33574db
231−3 \\x4944451363b0a133b458548113ac5091a58a07de228d81e3e4c6345a2f7dec13e3d0a23336460b54991025e4888ceb721e43ca984b456ad0d7b01e5a5bb90bc0 2020-05-12 21:39:15.57724+00 \N 10 okhttp \N \\xcca681ba42dd011d288178f9034bf55a5ed4299ccbbe97383774b207f25225559f710e6e587bc8467ad6f09446f4cebca9187ea4cdb144872e6c0bf83128d50f
232−6 \\x9d74794077a85bde414f93d51c1c2ea12beb41d68ae7053efb0932a2ee0e8da694ab2ce9b4c18823a44df024b7ab7167e81188a360bd051a5f166ce1f5d34866 2020-05-11 19:57:17.459942+00 \N 10 Windows 10 — Chrome \N \\x4ee19017009b90b1d8f6e813f5fd5234d29bf0316ac7fc5dfb8504228efe3c100d5364600917dd58317a9a18ba796c3b982df4fa1dd67d9663a7960e7e4e88c4
233−6 \\x9e7d4f2869f48112728aa46b8c8b757f612b32a62c4081cfe8920977bb2cb87aa966157670fa02255c04e84c66c13eb5a86af54698ce24a2ff0a91157aaebe5c 2020-05-11 14:48:22.634562+00 \N 10 Windows 10 — Chrome \N \\x1ce4d8b76b9aa4d528eea1aabe0df7497fd156e0760d0829a30c0963ad252507f1c00a8beaed4648470deccc319caf531e98ebce0b0f653f030bc2c882d7959b
234−9 \\x8ec80e74ce3720a8028f4f4bcd9c5598ca0343afd63cff5a8933729b1e2e28ca5e1a761420ae898ca51080b8e431ac1eaa5936dcdfaa4a04eeefe016adcdf86a 2020-05-11 15:36:57.456372+00 \\x2c1efa77bd7c3f09179efa261bb10ada6b6b2bdc2aa52f4f7f3f75de3093c0c80afedb8e09441b6fe683e73cd4e28dcdab78cc3d6d8c667c9ce8abb8c7f062d6 10 iOS \\xf0beaa45dc5b744d1691c0db6389a21e350280c8023a211adb45bcd62328312b89f663fb17452cb1708559eea67895e3d25d55422911053f4b4bb38206f88883 \\x10e95dd6f45681fe8b55e5f43905738a4b2bf49b8b119b559d8aa33090d1141a4071fc67eab7f9426b1932d5674e6690d2b3cd059c12f9b8fdef51f13e708cee
235−12 \\x7f76401afb1167a59d1aadf3b051900498332e56a909927f004073f676253b65fe8c75860ce8635d7d2e72aeb25f405db4c814304f4ac5120d83d51b8de24e3e 2020-05-12 09:44:34.181474+00 \N 10 iOS \N \\xd7132d27c779f16cb32eab613c3716f0436348833fca1ff8ed45a27121a2bd4c6ad4c7cbfaceb588b801708d45bcc578a3dbc19730392fe661d2a37aac086c7e
236−10 \\xfca54c43c5e2a8e6853d48325cb700a7f50ecbe635fd0a5353a36ea7858ba904a9c712c6a7d2d6e6e8d20aa3aae9f326c0fbe7d8c96f6357295012f5898bea84 2020-05-11 15:52:01.741885+00 \\x3f1fcbaba55307ac237249c0a17d1d2747dfccec6338be65fbefee8b01466f9515c376cedb6951b35a0b286d793284d4277e9e556406f26daae0af69d3362ff9 10 curl \\x2005dda2be269b6d23c185e0418200f42a17e0ac5b94e8a2a76753470128b1fa2da58b434fa15c2b68228b3212d775fe7a92bf0cb4d4d7dcf1377ed43eb16429 \\x26633323ef3a3c83ff8f4051ebe3d53778a0372534c8bdccfa0538309ee2b545e4baf9f07dfa1ec1728e4ddb95597d033370633a8fab70fc328b2c3932dbfc00
237−12 \\x3e81a2f178cc25b8280ef5bca9fee6f71e81f00e997a14810d7b6059ed638047aa55c06d9d51aef7eb1732de963ea12a7e12bd3864a3b05f696663f9129c6088 2020-05-12 09:58:42.998106+00 \N 10 iOS \N \\x0dc1c1286c3bc853f69ceb5f03dc0480187bd0acb089be8f1aa61dc51d68198cfc017d82ad3c2713e8c67b30e8cfaa81d87969744e231670fc3118a38d89714c
238−12 \\x25db505e0f4bc136b75ec2ef96132868d31baf8b659ccd89f797ac6d4a6375fa713506a9434d5a4c5f4d6b2663d31dcb6478e3eb92520c9201d6728c315e1424 2020-05-12 10:01:45.081199+00 \\x4c05612fff5c714d46abf6098a308744205ed345003b74d21f9e19622a9544ad5d186f1a84345d2d91dbd1d3a53e0d05461f66439f6b21a7c1c76b483a7b3641 10 iOS \\x7ebe3eb07ebf2ac66cab89a5fa26f8acc53f3ec06df7bad7522ff58ed5d02086c7f48687bea499b7ce47c04c79245cd06c0c56910f38d9f2b1af265b8de4775b \\x0a9df3cdc3b1a21eac72e3d77971ada874c899ac14937cb454a1e5c781b1886cfbb8822fb829273da5580ba2b15db947082463cbb36bb050445ea27b15d366a0
239−12 \\x3f0a881a910c85ea7dfd6ede9f732427bdf08e62b3dd19eb060d4536bf14e153a5804b86a16a61235c22d78dbac959b4fbb8b013d18c656c3d0faa6819727b8c 2020-05-12 10:02:27.63402+00 \N 10 iOS \N \\x9581a372265f20f5f51259f89d471e4e14aefe32989f17548832f3a31ec046ce93cc244885c2b7ea607a39249a14643d45b736ad5e786886c9cc2aaf86f89475
240−12 \\x12e21098059ff5efe4957f54e542fb0d03162f6a1dbdf4881f3d0915afc0e85ac2f29f75a99f706928ba57e77c1ab3b4f6563e4bc740f8dc06589b63774cdc85 2020-05-12 10:56:28.957574+00 \N 10 Mac OS X — Chrome \N \\xa61575d8f89b31a6e6ff558c72befaddea92294797433da8cee0d8238953541bf6fe9603f1d9f08fbf1eb094c34ce51de8c20ceb2a0e4704a57473eb6a2d8047
241−12 \\x239fc16e587f11903a0b983e3e9cc10ce5f9e320b753831190d03f647c55e00ca7f4635211c187a082d827fa5e8c038e40ab0cce47e68ec5a13ab56bc2886259 2020-05-12 19:26:46.204018+00 \\xa0dfb4c9699729277c23f492064763c2dc6496cf8f3c0805b374cffb38eec1f12f3df0c033b832db025a6f5a9deb7d6282fb72526a9e1a55876300848aede8ca 10 Mac OS X — Chrome \\x55224d5b28d6fa16e692740d8639eb033d142ed1a77ca0e50912acb4c952c9dccbc3616bc6454caebed3f1dbc3a5d5d9b939c7b8956a22b9fbefc3763d1916a0 \\x409a3cfd6904ec3c12b4dd33632040e89f4f340fe90e9b75abb76a787b58311f090204fd718b22e5cf9f6438e1f7984f335142624260f5d0248494ca96ba02b7
242−11 \\x41da42b65822526a52dec3f651a6d432fa950ddc795fd0a582b5b722b4c6e4956292b329dd58b4471a270a9784e9542f63142d799af8e6c96679e8bd86171835 2020-05-12 20:06:05.904528+00 \N 10 Mac OS X — Chrome \N \\xd2af034090688a7c03b7492d69c4bfeacbdf0b9e68be7bd3cc45182a689300dd3c5bb34e38d7e6152057e169b99fc6118cd876ed9987d7ae0e7a69f2dbd82c5c
243−12 \\x065e8193c701d5e8600eb30f3632845d8ac76f59b81155f88f520ff179bf13a3867e3363fb2589ca93ef438187c1cd990abd3ddd5e126fa722bbe727c13e9348 2020-05-12 20:45:55.846716+00 \N 10 iOS \N \\x52274795f5999d01a681f51a5e29afe4edd26889ddaaa77d1d2a85a2bfd08be4af9253d356bb360822034a5171819562177c3cea0e7df6e71212f672fee86f53
244−18 \\x897a5aa341b7709c7d9cb58f425723458658a1570a26429976b3c92c0da1613fa7e27fbe4a8429b5ab3d4cf2938caf1d730e57911a8b51b2d4f17257473bbd69 2020-05-12 21:13:54.40466+00 \N 10 Mac OS X — Chrome \N \\xcae63685d7a24031e550c5e49ac1e8ed8bd3b3bfff51051977c0c2cceba8af2a01faa964cebdac5724c8f0be7ce19f8af7902f6710bb8e989ce35c93833fc9fa
245−6 \\x6963dfdbf872528ed1f985e4d788cac5b96a9afc5c9e1aa78073e8fca1505a201b459695d3c5dbf8123b8f80f35b7672990389ce26be1bf6873c3fe59f4757ee 2020-05-12 12:37:07.199628+00 \N 10 Windows 10 — Chrome \N \\x766516d234fb7cabb224fbd991193784a60d62106e5d4b7ceeb7bdf123d7504a35ca9db3c1e14aacd3bd060c98b3e4e5e4f6bc7e0ee61440437c6464a63eb596
246−6 \\xef2cec0fe597a7158618a567da72595f81324e5ab42df9ee6f17024f9937267e1b6da7aada49defa7f2938b9e9584aed098087da4796c966fabeaff348d4e5c6 2020-05-12 12:37:55.467622+00 \N 10 Windows 10 — Chrome \N \\xc0d6f362c33fc9d3d2e61fef09eed321c7845968e4bd1ae17ddb61128c59861f25db12ac030c5f1871725be3d73e5e68f8ae64a29284cd07a8e1aa693beddf22
247−6 \\xf26ebf4f5880219a2fc8ae8624adcf8727a3ecd3521f396c811a90ca6ea7c15788b97145e33d6ee6bad7aefca1d53d51c852a4fd951b7204d6ef50b38b3305c6 2020-05-12 12:46:13.117509+00 \N 10 Windows 10 — Chrome \N \\x8e0519e8f56e7a4bbab46b58ff498e87a733e6371cd647c597551ad151b3a63c3be4e733d2130bf568a01e59f79c01bf9b38ed6a96940746f8f12ea009019ed3
248−6 \\x8b48c76b4d6005bf0c02ba7a7b765b6372246e1a7d503b249e901e6ddbade8e1f660272595de6b876afb945cb93cd9e03ab3b0498016e0285864acdb4d1ed228 2020-05-12 12:47:57.324299+00 \N 10 Windows 10 — Chrome \N \\xd89f6f8f5c3b9d6c66639febb0d2208023f46851bd402fffb2f86a05684bbde9f2964fdf443405fbf637d51257040e46971194f984927b1801635281497f93b6
249−6 \\x89fcb726e07982cd2ad61504ed85d2f6ab390108b501ce9533900cc232434409f7c37d66a64fdc48a3b145f110412290bf440d69b3360b323ba9138358d47432 2020-05-12 12:48:18.89163+00 \N 10 Windows 10 — Chrome \N \\x8d1344a463e9a6f3f5a334e5bccd532e3d42aed478808c46d0ebf711fcb6eb7e40890b04d713c6d5636e0ac1596c887771100e52b9619cb7aba344119b2adf15
250−6 \\x2a4e2f5621af53d8aca32b0e6ef9f784661e0d25e9f3c1a6147da28c8ec582b86c1edfd9ad50672156c5b8956eab0f4039ef9f5194c078ae09b6b908997cb828 2020-05-12 12:58:03.390233+00 \N 10 Windows 10 — Chrome \N \\xa0fa6918589bee447fa3d63548390836d95571cf94e4056e4cd51cab5f5cdad579774c55385173893bb5cf8f7123ea123fa8de7d6c1fd22bb07fae05eb82c470
251−6 \\xe79fbed1a6a85dca69674ce3c8f0d04087e3ca08e61a1e6570402405f56f6d7e1ff405c345027645636799e3b67ff432d3885e8f87fd9f37dcbdd509264ab4c9 2020-05-12 13:02:15.709627+00 \N 10 Windows 10 — Chrome \N \\xf39f55f18841a4c029d2721dd911e07738011cc0043b783c615d6f68238b60ddc3fc9f13e0f93ca918a81b274c86c6c412b0d1d5d283eea26e5d331ab4eceb63
252−6 \\x791f98bdab86f17dc631d8a641308babd1bf6a6d80c8d9a13474847b5bdd403a07ceda5bfda647ceeea52cc15d24dcb84417c3900f7744d548f3c11c4927a636 2020-05-12 13:00:39.90702+00 \N 10 Windows 10 — Chrome \N \\x54277fb3f03893ee8ca550f6cfd7b2cf03f792ee987dae7a36ecb6b052f5bc46d1262eabc34e2ee6d896f3ba50a9c309cba785ed55928866b1f4d1217e5d62fe
253−6 \\x8475047b3f4cf52c58d31b5f40f51fbcb421e889b9545af40685b50f77c32060c08adb7593733d174d0d705e7a11b05352745f997e151383c3df4087b87aa437 2020-05-12 13:08:36.061589+00 \N 10 Windows 10 — Chrome \N \\x32b6441572986f12c92b4cab97231ed416b0c1538589061bc1498af55b12d6abf16ba10842c97f950ace47bd6a34499a458112de353e003336eaefd87232a441
254−6 \\xe50a1cabdbce47cd7384ea7e7b9a4ebd1f70bd4d2270f7fd1449243e3c2a514f6374a9415b838a88d44be91260b1c8e789c02451a5215caa823e85ce9a6c6a35 2020-05-12 13:11:01.163827+00 \N 10 Windows 10 — Chrome \N \\x4cc5ab60a8b31de5109f9f0f86754e5654aecc8a83b5c553854809690a576d94d71f474d776d490828522db88349c607968d120af014bebdfcc984ff11ec97f3
255−6 \\x14ecc52de16df124a0cfdd6fac2af58998d7ef75a4b77dc6ffcbebfcb1a0641933125399ccb83ff162b5a4c526cae43989c73db33f5f4e9cd0d667ffa64b1491 2020-05-12 13:15:09.286079+00 \\xa3c9a1ed5ad4cf49cc0d90f9e0f179975cddab69f2306e205a35f1a4daac65dc75cd5182de269d7b1b4f21e6de154279363c31ff7807ee0998e54cc408c542a4 10 Windows 10 — Opera \\x53b20233e1912eb3e1889f7d62813852a6f00160c923640e0500b3c8056c0e1582cc6ab1f0860c9ea377b7bc057cabc8d1107bfd09a4e1de1a5200facc9e4680 \\x976304336533e75ebd013d336daaec896ea6aac620ba76593cf0f5e9067bbcbd7c22db86c9cf570e555a7d3d38840dea671897363c5de797628db25819cea194
256−12 \\x9a3f1bc8ad7af3256544d16b10af39f170671b37913ad0795b7c83b92c165815d52e04ae41a778ba1987ceb7ec3771a8a96c2117b1a93e53086a7aa72b744101 2020-05-12 13:17:22.323574+00 \\x68614722f437fd805c9bef73e1319a60ab7ae8d1e669afe8a52c7853d19da646dbd72ad8026329c2555c0b5e5ad4f16793b0a0b55e33f5ca97b941b4d28a03ae 10 Mac OS X — Chrome \\x03906cccc155cfe4ef4b237e7663f05616623b7a10c2443248f79f3ff9fd3df9c4052107b11e3426a66427b7a22c1205ef6b6b8eaa366500f4ed775281a44bb9 \\x1d33623701873d27f030afc04b3af27554adde549230e814b1791c9104591ca1751e865c89591b689df1e02ffbdfe279fccfd59335d448313a6c7b0c6db34818
257−12 \\xdb2021edc253bfdf75870708682c0b17c60d9d2552d614da87874341127549d811b49b331f66902e583937282f8b433b64111a28647447bac45457fbc4951397 2020-05-12 13:17:25.803874+00 \N 9 Mac OS X — Chrome \N \\x404618e3db88af5f6f48d97526ba2d813ae4cd98943892a3eb33d606182a4c685d1f251c577894aa2d6f192446e6c377250c0f1c199eaae0164e6908921d56d3
258−6 \\xff236155bcdc965d10d97e37b95498cd48e84f91d84cbf9eeb7149d4488174d0c011ebcec15a708775fc02eb48d987c6a707460cf3f4ad6a5920acfa25e4097d 2020-05-12 13:20:49.70022+00 \N 10 Windows 10 — Chrome \N \\xac51b5e531a71c2b90024e308ded6bcf291327ccf58c2a38a79e93cd21aef908469e3ba731d438df0dd830bd9e91a3b4af8baefcf1481539face9f7bd284dc63
259−6 \\xd8068bbf1c75403f98bc65d5b5023773391eb157e71b1796d5ef8ff6e51e12dfa8eee816dd387a8284e62abf3622bdd62e986ab70d4e7c06eadf0c336c7ddf65 2020-05-12 13:42:52.574211+00 \N 10 Windows 10 — Chrome \N \\xfe79ff74735cb836511460da0b64387353ac5d1328f8c429312e09ad27d7d76344ff0ff760beca020bcc651ce6e2014049836bdf8fd2e0b79c752bb58a44edd2
260−6 \\x4054af8d092a164806b1f5c1a9272fef54d04b14fea1c5c7e1cdbb82541339198bb449f10aeaa6c729b5d60b72201230ccf2cc023d4457cc7990b37b74c53680 2020-05-12 13:43:48.856981+00 \N 10 Windows 10 — Opera \N \\x34cf5f64da39a5f9348f024fe04ca22c0f71b85fe3e1538474d1605aef8e65e0dd6b01a31dbb5a6ca5d957edfc746e395a40832406cf6fe3b4bf6f9d67f6d2b1
261−6 \\xca1eaaff2de79c350e2b452a8adac5d465def68a81b2cae0ce6c6ab06b79fb862594b8557b3d45da17ad2ec2d6cdb19792f6168be7513f30afd709a04bf4a045 2020-05-12 14:12:24.590032+00 \N 10 Windows 10 — Chrome \N \\x3099f74e612c1ef8b65d742748232025bdb7c6e54dbcdca70ff015b3f6ed1ed9b6b8b03e5a9cefe80a81598b40e8603ac8d643a4728904a96f8283b4132a670b
262−12 \\xe9135b7110691c01639db7fe5828c1bbb1fda424d4eb4ede378413493742ef360af3fd97b71f2a99977222725d9e22c8eef62e89331ae2dd269678d8549c44f7 2020-05-12 17:39:51.225664+00 \N 10 iOS \N \\x78ccada9b2d8e9a7aa553ad2d1143eff23b8b59caaf3e573cfb115bd66aa2c2d3735fab6bee8fe0c83bc00148129793b806824c2901adc73665841f88b8f15f0
263−12 \\x4b61df40968e85661eb637ede9aa49bedb12eb9fbaad1250317f102390ae474bc9fc6e49e9f06b61a9b2f79eb9a72c59d0178080d4699abae2b3406bd3702fd0 2020-05-12 19:26:49.449533+00 \N 10 Mac OS X — Chrome \N \\xfc0df82243a1174a1c60f066369a22fb75c516e071061dbc539a61cee7853b081dfdb0e6ef05f85cf6a70d9a6a184daa2f8d46de41ae5aab95c0f0f553875411
264−6 \\x2d1108aa6559686f85b32cb0ed446d42c64021ff43539b4e4462b697dcf2ec1c6c6c306100be9aee0e1321dac27a5dcee76a34abcfda77a7949b01e64a6acdb6 2020-05-12 14:22:04.298934+00 \N 10 Windows 10 — Chrome \N \\x347810d21c6b1c39fc8deb5056df65f5aff393a8b94b4f9fc80064a5d4e3b98d3b6de9ea8e2fcc0625ede95a3cf94f707c490710da086d6dfe645f6bc498d814
265−11 \\xd0c382a30c6508107c4095fbe6713c8b987d51145e5e95b25fe082d00c89af26c9d4f46b5c6a9a63bbc82a63a57c9c0d488cb6787f64465a0b29dbe4b481aec1 2020-05-12 20:06:08.427816+00 \N 10 Mac OS X — Chrome \N \\xc7d039fa0c13356ffd1379a8ec10a97b3bbfafb0a3222aaabff85c5467ffb8224bbf1e450aafd278642aa457db904d4318d05a37a3ad65b6e42055729baa992c
266−6 \\x783d36a882b22f9e35e0e67f16058bb01d07bbaee4f35465f84125868367161aa77a48c24fce9a8587ab5baeaefc7f20d2d1fbe6b9ffc3e7ea079ce4b4fc2065 2020-05-12 21:05:34.945924+00 \\xaed141900570ffbc6027328ebaedc19516014fd15e51f2dac2f63d0ebdcc0b6e42893be3ad9edca85b521412357bb0e2ec74eb359af17b6d7d5bbac9b59912e4 10 Windows 10 — Chrome \\xa17c601d49975527b36bdeccfedca7f66bbc0603ed8456fe99c8592e270f070ab15dd083efc278665c295af2667b345c24f1da9d1d19299bbd001971b4e3f112 \\xe887bda8dc9e076244c88d678885f81b21b93d228fe24cd0f8619203671e40e0db8d4f50168083f1742809cb75338ddc62c08ba017790ea6f15602ef2fa77fac
267−11 \\x467a1e43d01b6d92be00e984d74d2984225cead7380ddba3a3b962b64fa7c99878cfda33c15bc72826fd4d747c05b1905a51fcecd7bdc2c20322c1c9c0cde1b8 2020-05-12 21:35:38.166955+00 \N 10 Mac OS X — Chrome \N \\xab87612391d51e870bce7b33ed618fbc1310cf2bfffe7cb8a887a161a107187924848e0f9aab60fd2719155984a800181a4d1dc7b58680da197b2b1e373df210
268−\.
269−
270−
271−--
272−-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: qsfrneimsrtpjz
273−--
274−
275−COPY public.users ("userRowId", "userRowFirstName", "userRowMiddleName", "userRowLastName", "userRowFacultyUrl", "userRowEmail", "userRowIsValidated") FROM stdin;
276−12 Дарья Юрьевна Редникина strategy.hse.ru/ dyurednikina@edu.hse.ru t
277−18 Darya _ _ cs.hse.ru/dse/ rednikina.com@yandex.ru t
278−6 Костя Игоревич Манежин perm.hse.ru/sgf/hum/ kimanezhin@edu.hse.ru t
279−11 Костюченко Игоревич Илья spb.hse.ru/fmcs/math/ iikostyuchenko@edu.hse.ru t
280−3 Анна Денисовна Михалева spb.hse.ru/humart/design/ admikhaleva@edu.hse.ru t
281−9 Darya Yuryevna Rednikina cs.hse.ru/dse/ dyrednikina@edu.hse.ru f
282−17 anna denisovna mikhaleva scem.spb.hse.ru/man/ hello@edu.hse.ru f
283−10 Seva Algebrovich Leonidov cs.hse.ru/dse/ iikostyuchen@edu.hse.ru f
284−\.
285−
286−
287−--
288−-- Name: channels_namedChannelFullId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
289−--
290−
291−SELECT pg_catalog.setval('public."channels_namedChannelFullId_seq"', 37, true);
292−
293−
294−--
295−-- Name: groupChats_groupChatId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
296−--
297−
298−SELECT pg_catalog.setval('public."groupChats_groupChatId_seq"', 24, true);
299−
300−
301−--
302−-- Name: messages_messageStorageId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
303−--
304−
305−SELECT pg_catalog.setval('public."messages_messageStorageId_seq"', 164, true);
306−
307−
308−--
309−-- Name: posts_postRowId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
310−--
311−
312−SELECT pg_catalog.setval('public."posts_postRowId_seq"', 49, true);
313−
314−
315−--
316−-- Name: quotes_quoteRowId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
317−--
318−
319−SELECT pg_catalog.setval('public."quotes_quoteRowId_seq"', 1, false);
320−
321−
322−--
323−-- Name: users_userRowId_seq; Type: SEQUENCE SET; Schema: public; Owner: qsfrneimsrtpjz
324−--
325−
326−SELECT pg_catalog.setval('public."users_userRowId_seq"', 18, true);
327−
328−
329−--
330−-- Name: channels pk_channels; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
331−--
332−
333−ALTER TABLE ONLY public.channels
334− ADD CONSTRAINT pk_channels PRIMARY KEY ("namedChannelFullId");
335−
336−
337−--
338−-- Name: faculties pk_faculties; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
339−--
340−
341−ALTER TABLE ONLY public.faculties
342− ADD CONSTRAINT pk_faculties PRIMARY KEY ("facultyUrl");
343−
344−
345−--
346−-- Name: groupChats pk_group_chats; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
347−--
348−
349−ALTER TABLE ONLY public."groupChats"
350− ADD CONSTRAINT pk_group_chats PRIMARY KEY ("groupChatId");
351−
352−
353−--
354−-- Name: messages pk_messages; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
355−--
356−
357−ALTER TABLE ONLY public.messages
358− ADD CONSTRAINT pk_messages PRIMARY KEY ("messageStorageId");
359−
360−
361−--
362−-- Name: quotes pk_post_quotes; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
363−--
364−
365−ALTER TABLE ONLY public.quotes
366− ADD CONSTRAINT pk_post_quotes PRIMARY KEY ("quoteRowId");
367−
368−
369−--
370−-- Name: posts pk_posts; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
371−--
372−
373−ALTER TABLE ONLY public.posts
374− ADD CONSTRAINT pk_posts PRIMARY KEY ("postRowId");
375−
376−
377−--
378−-- Name: users pk_users; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
379−--
380−
381−ALTER TABLE ONLY public.users
382− ADD CONSTRAINT pk_users PRIMARY KEY ("userRowId");
383−
384−
385−--
386−-- Name: users user_email; Type: CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
387−--
388−
389−ALTER TABLE ONLY public.users
390− ADD CONSTRAINT user_email UNIQUE ("userRowEmail");
391−
392−
393−--
394−-- Name: posts fk_author_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
395−--
396−
397−ALTER TABLE ONLY public.posts
398− ADD CONSTRAINT fk_author_id FOREIGN KEY ("postRowAuthorId") REFERENCES public.users("userRowId") ON UPDATE CASCADE ON DELETE CASCADE;
399−
400−
401−--
402−-- Name: channels fk_channels_users_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
403−--
404−
405−ALTER TABLE ONLY public.channels
406− ADD CONSTRAINT fk_channels_users_id FOREIGN KEY ("namedChannelFullOwner") REFERENCES public.users("userRowId") ON UPDATE CASCADE ON DELETE CASCADE;
407−
408−
409−--
410−-- Name: messages fk_messages_author; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
411−--
412−
413−ALTER TABLE ONLY public.messages
414− ADD CONSTRAINT fk_messages_author FOREIGN KEY ("messageStorageAuthorId") REFERENCES public.users("userRowId") ON UPDATE CASCADE ON DELETE CASCADE;
415−
416−
417−--
418−-- Name: messages fk_messages_group; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
419−--
420−
421−ALTER TABLE ONLY public.messages
422− ADD CONSTRAINT fk_messages_group FOREIGN KEY ("messageStorageDestinationGroupId") REFERENCES public."groupChats"("groupChatId") ON UPDATE CASCADE ON DELETE CASCADE;
423−
424−
425−--
426−-- Name: messages fk_messages_to_user; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
427−--
428−
429−ALTER TABLE ONLY public.messages
430− ADD CONSTRAINT fk_messages_to_user FOREIGN KEY ("messageStorageDestinationUserId") REFERENCES public.users("userRowId") ON UPDATE CASCADE ON DELETE CASCADE;
431−
432−
433−--
434−-- Name: postElements fk_post_quote_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
435−--
436−
437−ALTER TABLE ONLY public."postElements"
438− ADD CONSTRAINT fk_post_quote_id FOREIGN KEY ("rowElementQuote") REFERENCES public.quotes("quoteRowId") ON UPDATE CASCADE ON DELETE CASCADE;
439−
440−
441−--
442−-- Name: postElements fk_post_quote_self_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
443−--
444−
445−ALTER TABLE ONLY public."postElements"
446− ADD CONSTRAINT fk_post_quote_self_id FOREIGN KEY ("rowElementId") REFERENCES public.posts("postRowId") ON UPDATE CASCADE ON DELETE CASCADE;
447−
448−
449−--
450−-- Name: quotes fk_quote_post_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
451−--
452−
453−ALTER TABLE ONLY public.quotes
454− ADD CONSTRAINT fk_quote_post_id FOREIGN KEY ("quoteRowPostId") REFERENCES public.posts("postRowId") ON UPDATE CASCADE ON DELETE CASCADE;
455−
456−
457−--
458−-- Name: quoteElements fk_quote_quote_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
459−--
460−
461−ALTER TABLE ONLY public."quoteElements"
462− ADD CONSTRAINT fk_quote_quote_id FOREIGN KEY ("rowElementQuote") REFERENCES public.quotes("quoteRowId") ON UPDATE CASCADE ON DELETE CASCADE;
463−
464−
465−--
466−-- Name: quoteElements fk_quotes_quote_self_id; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
467−--
468−
469−ALTER TABLE ONLY public."quoteElements"
470− ADD CONSTRAINT fk_quotes_quote_self_id FOREIGN KEY ("rowElementId") REFERENCES public.quotes("quoteRowId") ON UPDATE CASCADE ON DELETE CASCADE;
471−
472−
473−--
474−-- Name: users fk_user_faculty; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
475−--
476−
477−ALTER TABLE ONLY public.users
478− ADD CONSTRAINT fk_user_faculty FOREIGN KEY ("userRowFacultyUrl") REFERENCES public.faculties("facultyUrl") ON UPDATE CASCADE ON DELETE RESTRICT;
479−
480−
481−--
482−-- Name: tokens fk_user_token; Type: FK CONSTRAINT; Schema: public; Owner: qsfrneimsrtpjz
483−--
484−
485−ALTER TABLE ONLY public.tokens
486− ADD CONSTRAINT fk_user_token FOREIGN KEY ("tokenUserId") REFERENCES public.users("userRowId") ON UPDATE CASCADE ON DELETE CASCADE;
487−
488−
489−--
490−-- Name: LANGUAGE plpgsql; Type: ACL; Schema: -; Owner: postgres
491−--
492−
493−GRANT ALL ON LANGUAGE plpgsql TO qsfrneimsrtpjz;
494−
495−
496−--
497−-- PostgreSQL database dump complete
498−--
499−
stack.yaml+1
@@ -40,6 +40,7 @@ packages:
4040extra-deps:
4141- taggy-0.2.1
4242- crypto-rng-0.1.0.2
43+- periodic-0.1.0.0
4344# - servant-options-0.1.0.0
4445
4546