Merge branch '423-uneingeloste-einladungen-garbage-collecten' into 'master'
Resolve "Uneingelöste Einladungen garbage-collecten" Closes #423 See merge request !264
This commit is contained in:
commit
533b21fc9d
@ -2,4 +2,5 @@ Invitation
|
|||||||
email UserEmail
|
email UserEmail
|
||||||
for Value
|
for Value
|
||||||
data Value
|
data Value
|
||||||
|
expiresAt UTCTime Maybe
|
||||||
UniqueInvitation email for
|
UniqueInvitation email for
|
||||||
@ -175,7 +175,7 @@ sinkInvitations :: forall junction.
|
|||||||
-- updated, instead.
|
-- updated, instead.
|
||||||
--
|
--
|
||||||
-- For new junctions an invitation is sent by e-mail.
|
-- For new junctions an invitation is sent by e-mail.
|
||||||
sinkInvitations InvitationConfig{..} = determineExists .| C.foldMap pure >>= lift . sinkInvitations'
|
sinkInvitations InvitationConfig{..} = determineExists .| sinkInvitations'
|
||||||
where
|
where
|
||||||
determineExists :: Conduit (Invitation' junction)
|
determineExists :: Conduit (Invitation' junction)
|
||||||
(YesodJobDB UniWorX)
|
(YesodJobDB UniWorX)
|
||||||
@ -201,13 +201,9 @@ sinkInvitations InvitationConfig{..} = determineExists .| C.foldMap pure >>= lif
|
|||||||
JSON.Success dbData -> return dbData
|
JSON.Success dbData -> return dbData
|
||||||
JSON.Error str -> fail $ "Could not decode invitationData: " <> str
|
JSON.Error str -> fail $ "Could not decode invitationData: " <> str
|
||||||
|
|
||||||
sinkInvitations' :: [Invitation' junction]
|
sinkInvitations' :: Sink (Invitation' junction) (YesodJobDB UniWorX) ()
|
||||||
-> YesodJobDB UniWorX ()
|
sinkInvitations' = do
|
||||||
sinkInvitations' new = do
|
C.mapM_ $ \(jInvitee, fid, dat) -> do
|
||||||
when (is _Nothing (ephemeralInvitation @junction)) $ do
|
|
||||||
insertMany_ $ map (\(email, fid, dat) -> Invitation email (invRef @junction fid) (toJSON $ dat ^. _invitationDBData)) new
|
|
||||||
-- forM_ existing $ \(iid, oldDat) -> update iid [ InvitationData =. toJSON (dat ^. _invitationDBData) ]
|
|
||||||
forM_ new $ \(jInvitee, fid, dat) -> do
|
|
||||||
app <- getYesod
|
app <- getYesod
|
||||||
let mr = renderMessage app $ NonEmpty.toList appLanguages
|
let mr = renderMessage app $ NonEmpty.toList appLanguages
|
||||||
ur <- getUrlRenderParams
|
ur <- getUrlRenderParams
|
||||||
@ -224,6 +220,13 @@ sinkInvitations InvitationConfig{..} = determineExists .| C.foldMap pure >>= lif
|
|||||||
jInvitationSubject <- fmap mr . mapReaderT liftHandlerT $ invitationSubject fEnt dat
|
jInvitationSubject <- fmap mr . mapReaderT liftHandlerT $ invitationSubject fEnt dat
|
||||||
let jInvitationExplanation = invitationExplanation fEnt dat (toHtml . mr) ur
|
let jInvitationExplanation = invitationExplanation fEnt dat (toHtml . mr) ur
|
||||||
|
|
||||||
|
when (is _Nothing (ephemeralInvitation @junction)) $ insert_ $ Invitation
|
||||||
|
{ invitationEmail = jInvitee
|
||||||
|
, invitationFor = invRef @junction fid
|
||||||
|
, invitationData = toJSON $ dat ^. _invitationDBData
|
||||||
|
, invitationExpiresAt = tokenExpiresAt token
|
||||||
|
}
|
||||||
|
|
||||||
queueDBJob JobInvitation{..}
|
queueDBJob JobInvitation{..}
|
||||||
|
|
||||||
sinkInvitationsF :: forall junction mono.
|
sinkInvitationsF :: forall junction mono.
|
||||||
@ -245,9 +248,9 @@ sourceInvitations :: forall junction.
|
|||||||
-> Source (YesodDB UniWorX) (UserEmail, InvitationDBData junction)
|
-> Source (YesodDB UniWorX) (UserEmail, InvitationDBData junction)
|
||||||
sourceInvitations forKey = selectSource [InvitationFor ==. invRef @junction forKey] [] .| C.mapM decode
|
sourceInvitations forKey = selectSource [InvitationFor ==. invRef @junction forKey] [] .| C.mapM decode
|
||||||
where
|
where
|
||||||
decode (Entity _ (Invitation email _ invitationData))
|
decode (Entity _ (Invitation{invitationEmail, invitationData}))
|
||||||
= case fromJSON invitationData of
|
= case fromJSON invitationData of
|
||||||
JSON.Success dbData -> return (email, dbData)
|
JSON.Success dbData -> return (invitationEmail, dbData)
|
||||||
JSON.Error str -> fail $ "Could not decode invitationData: " <> str
|
JSON.Error str -> fail $ "Could not decode invitationData: " <> str
|
||||||
|
|
||||||
sourceInvitationsList :: forall junction.
|
sourceInvitationsList :: forall junction.
|
||||||
|
|||||||
@ -66,6 +66,7 @@ import Jobs.Handler.SendCourseCommunication
|
|||||||
import Jobs.Handler.Invitation
|
import Jobs.Handler.Invitation
|
||||||
import Jobs.Handler.SendPasswordReset
|
import Jobs.Handler.SendPasswordReset
|
||||||
import Jobs.Handler.TransactionLog
|
import Jobs.Handler.TransactionLog
|
||||||
|
import Jobs.Handler.PruneInvitations
|
||||||
|
|
||||||
import Jobs.HealthReport
|
import Jobs.HealthReport
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import Data.Semigroup (Max(..))
|
|||||||
|
|
||||||
import Data.Time.Zones
|
import Data.Time.Zones
|
||||||
|
|
||||||
|
import Handler.Utils.DateTime
|
||||||
|
|
||||||
import Control.Monad.Trans.Writer (WriterT, execWriterT)
|
import Control.Monad.Trans.Writer (WriterT, execWriterT)
|
||||||
import Control.Monad.Writer.Class (MonadWriter(..))
|
import Control.Monad.Writer.Class (MonadWriter(..))
|
||||||
|
|
||||||
@ -44,6 +46,16 @@ determineCrontab = execWriterT $ do
|
|||||||
, cronRateLimit = appJobCronInterval
|
, cronRateLimit = appJobCronInterval
|
||||||
, cronNotAfter = Right CronNotScheduled
|
, cronNotAfter = Right CronNotScheduled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldestInvitationMUTC <- lift $ preview (_head . _entityVal . _invitationExpiresAt . _Just) <$> selectList [InvitationExpiresAt !=. Nothing] [Asc InvitationExpiresAt, LimitTo 1]
|
||||||
|
whenIsJust oldestInvitationMUTC $ \oldestInvUTC -> tell $ HashMap.singleton
|
||||||
|
(JobCtlQueue JobPruneInvitations)
|
||||||
|
Cron
|
||||||
|
{ cronInitial = CronTimestamp $ utcToLocalTime oldestInvUTC
|
||||||
|
, cronRepeat = CronRepeatOnChange
|
||||||
|
, cronRateLimit = nominalDay
|
||||||
|
, cronNotAfter = Right CronNotScheduled
|
||||||
|
}
|
||||||
|
|
||||||
tell . flip foldMap universeF $ \kind ->
|
tell . flip foldMap universeF $ \kind ->
|
||||||
case appHealthCheckInterval kind of
|
case appHealthCheckInterval kind of
|
||||||
|
|||||||
13
src/Jobs/Handler/PruneInvitations.hs
Normal file
13
src/Jobs/Handler/PruneInvitations.hs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module Jobs.Handler.PruneInvitations
|
||||||
|
( dispatchJobPruneInvitations
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Database.Persist.Sql (deleteWhereCount)
|
||||||
|
|
||||||
|
dispatchJobPruneInvitations :: Handler ()
|
||||||
|
dispatchJobPruneInvitations = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
n <- runDB $ deleteWhereCount [ InvitationExpiresAt <. Just now ]
|
||||||
|
$logInfoS "PruneInvitations" [st|Deleted #{n} expired invitations|]
|
||||||
@ -49,6 +49,7 @@ data Job = JobSendNotification { jRecipient :: UserId, jNotification :: Notifica
|
|||||||
| JobSendPasswordReset { jRecipient :: UserId
|
| JobSendPasswordReset { jRecipient :: UserId
|
||||||
}
|
}
|
||||||
| JobTruncateTransactionLog
|
| JobTruncateTransactionLog
|
||||||
|
| JobPruneInvitations
|
||||||
| JobDeleteTransactionLogIPs
|
| JobDeleteTransactionLogIPs
|
||||||
deriving (Eq, Ord, Show, Read, Generic, Typeable)
|
deriving (Eq, Ord, Show, Read, Generic, Typeable)
|
||||||
data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId }
|
data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId }
|
||||||
|
|||||||
@ -149,6 +149,8 @@ makeLenses_ ''Occurrences
|
|||||||
|
|
||||||
makeLenses_ ''PredDNF
|
makeLenses_ ''PredDNF
|
||||||
|
|
||||||
|
makeLenses_ ''Invitation
|
||||||
|
|
||||||
makeLenses_ ''ExamBonusRule
|
makeLenses_ ''ExamBonusRule
|
||||||
makeLenses_ ''ExamGradingRule
|
makeLenses_ ''ExamGradingRule
|
||||||
makeLenses_ ''ExamResult
|
makeLenses_ ''ExamResult
|
||||||
|
|||||||
Reference in New Issue
Block a user