mirror of
https://code.sup39.dev/repos/Wqawg
synced 2024-12-27 18:24:51 +09:00
Implement outbox remote delivery, in handler and periodic, not used yet
This commit is contained in:
parent
7946fe441d
commit
d5eefd1553
11 changed files with 490 additions and 394 deletions
|
@ -54,6 +54,20 @@ InboxItemLocal
|
||||||
|
|
||||||
UniqueInboxItemLocal person activity
|
UniqueInboxItemLocal person activity
|
||||||
|
|
||||||
|
UnlinkedDelivery
|
||||||
|
recipient UnfetchedRemoteActorId
|
||||||
|
activity OutboxItemId
|
||||||
|
running Bool
|
||||||
|
|
||||||
|
UniqueUnlinkedDelivery recipient activity
|
||||||
|
|
||||||
|
Delivery
|
||||||
|
recipient RemoteActorId
|
||||||
|
activity OutboxItemId
|
||||||
|
running Bool
|
||||||
|
|
||||||
|
UniqueDelivery recipient activity
|
||||||
|
|
||||||
VerifKey
|
VerifKey
|
||||||
ident LocalURI
|
ident LocalURI
|
||||||
instance InstanceId
|
instance InstanceId
|
||||||
|
@ -69,6 +83,13 @@ VerifKeySharedUsage
|
||||||
|
|
||||||
UniqueVerifKeySharedUsage key user
|
UniqueVerifKeySharedUsage key user
|
||||||
|
|
||||||
|
UnfetchedRemoteActor
|
||||||
|
instance InstanceId
|
||||||
|
ident LocalURI
|
||||||
|
since UTCTime Maybe
|
||||||
|
|
||||||
|
UniqueUnfetchedRemoteActor instance ident
|
||||||
|
|
||||||
RemoteActor
|
RemoteActor
|
||||||
ident LocalURI
|
ident LocalURI
|
||||||
instance InstanceId
|
instance InstanceId
|
||||||
|
|
20
migrations/2019_04_12.model
Normal file
20
migrations/2019_04_12.model
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
UnfetchedRemoteActor
|
||||||
|
instance InstanceId
|
||||||
|
ident Text
|
||||||
|
since UTCTime Maybe
|
||||||
|
|
||||||
|
UniqueUnfetchedRemoteActor instance ident
|
||||||
|
|
||||||
|
UnlinkedDelivery
|
||||||
|
recipient UnfetchedRemoteActorId
|
||||||
|
activity OutboxItemId
|
||||||
|
running Bool
|
||||||
|
|
||||||
|
UniqueUnlinkedDelivery recipient activity
|
||||||
|
|
||||||
|
Delivery
|
||||||
|
recipient RemoteActorId
|
||||||
|
activity OutboxItemId
|
||||||
|
running Bool
|
||||||
|
|
||||||
|
UniqueDelivery recipient activity
|
|
@ -63,7 +63,7 @@ data ResultShare m k v a = ResultShare
|
||||||
}
|
}
|
||||||
|
|
||||||
newResultShare
|
newResultShare
|
||||||
:: MonadIO m => ResultShareSettings m k v a -> m (ResultShare m k v a)
|
:: MonadIO n => ResultShareSettings m k v a -> n (ResultShare m k v a)
|
||||||
newResultShare (ResultShareSettings fork action) = do
|
newResultShare (ResultShareSettings fork action) = do
|
||||||
tvar <- liftIO $ newTVarIO M.empty
|
tvar <- liftIO $ newTVarIO M.empty
|
||||||
return $ ResultShare tvar fork action
|
return $ ResultShare tvar fork action
|
||||||
|
|
|
@ -60,12 +60,15 @@ import Control.Concurrent.Local (forkCheck)
|
||||||
|
|
||||||
import Database.Persist.Schema.PostgreSQL (schemaBackend)
|
import Database.Persist.Schema.PostgreSQL (schemaBackend)
|
||||||
|
|
||||||
|
import Control.Concurrent.ResultShare
|
||||||
import Data.KeyFile
|
import Data.KeyFile
|
||||||
|
|
||||||
import Web.Hashids.Local
|
import Web.Hashids.Local
|
||||||
|
|
||||||
import Vervis.ActorKey (generateActorKey, actorKeyRotator)
|
import Vervis.ActorKey (generateActorKey, actorKeyRotator)
|
||||||
|
import Vervis.Federation
|
||||||
import Vervis.KeyFile (isInitialSetup)
|
import Vervis.KeyFile (isInitialSetup)
|
||||||
import Vervis.RemoteActorStore (newInstanceMutex)
|
import Vervis.RemoteActorStore
|
||||||
|
|
||||||
-- Import all relevant handler modules here.
|
-- Import all relevant handler modules here.
|
||||||
-- Don't forget to add new modules to your cabal file!
|
-- Don't forget to add new modules to your cabal file!
|
||||||
|
@ -125,6 +128,8 @@ makeFoundation appSettings = do
|
||||||
|
|
||||||
appInstanceMutex <- newInstanceMutex
|
appInstanceMutex <- newInstanceMutex
|
||||||
|
|
||||||
|
appActorFetchShare <- newResultShare actorFetchShareSettings
|
||||||
|
|
||||||
appActivities <- newTVarIO mempty
|
appActivities <- newTVarIO mempty
|
||||||
|
|
||||||
-- We need a log function to create a connection pool. We need a connection
|
-- We need a log function to create a connection pool. We need a connection
|
||||||
|
@ -169,7 +174,10 @@ makeFoundation appSettings = do
|
||||||
let msg = "DB migration failed: " <> err
|
let msg = "DB migration failed: " <> err
|
||||||
$logError msg
|
$logError msg
|
||||||
error $ T.unpack msg
|
error $ T.unpack msg
|
||||||
Right (_from, _to) -> $logInfo "DB migration success"
|
Right (_from, _to) -> do
|
||||||
|
$logInfo "DB migration success"
|
||||||
|
fixRunningDeliveries
|
||||||
|
deleteUnusedURAs
|
||||||
|
|
||||||
-- Return the foundation
|
-- Return the foundation
|
||||||
return $ mkFoundation pool capSignKey hashidsCtx
|
return $ mkFoundation pool capSignKey hashidsCtx
|
||||||
|
|
|
@ -15,18 +15,23 @@
|
||||||
|
|
||||||
module Vervis.Federation
|
module Vervis.Federation
|
||||||
( handleInboxActivity
|
( handleInboxActivity
|
||||||
|
, fixRunningDeliveries
|
||||||
, handleOutboxNote
|
, handleOutboxNote
|
||||||
|
, retryOutboxDelivery
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Concurrent.MVar
|
||||||
import Control.Concurrent.STM.TVar
|
import Control.Concurrent.STM.TVar
|
||||||
import Control.Exception hiding (Handler, try)
|
import Control.Exception hiding (Handler, try)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Logger.CallStack
|
import Control.Monad.Logger.CallStack
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
import Control.Monad.Trans.Maybe
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Control.Monad.Trans.Reader
|
||||||
import Data.Aeson (Object)
|
import Data.Aeson (Object)
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.Either
|
import Data.Either
|
||||||
|
@ -43,8 +48,10 @@ import Data.Traversable
|
||||||
import Data.Tuple
|
import Data.Tuple
|
||||||
import Database.Persist hiding (deleteBy)
|
import Database.Persist hiding (deleteBy)
|
||||||
import Database.Persist.Sql hiding (deleteBy)
|
import Database.Persist.Sql hiding (deleteBy)
|
||||||
|
import Network.HTTP.Client
|
||||||
import Network.HTTP.Types.Header
|
import Network.HTTP.Types.Header
|
||||||
import Network.HTTP.Types.URI
|
import Network.HTTP.Types.URI
|
||||||
|
import Network.TLS
|
||||||
import UnliftIO.Exception (try)
|
import UnliftIO.Exception (try)
|
||||||
import Yesod.Core hiding (logError, logWarn, logInfo)
|
import Yesod.Core hiding (logError, logWarn, logInfo)
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
@ -66,6 +73,7 @@ import Yesod.Hashids
|
||||||
import Data.Either.Local
|
import Data.Either.Local
|
||||||
import Data.List.Local
|
import Data.List.Local
|
||||||
import Data.List.NonEmpty.Local
|
import Data.List.NonEmpty.Local
|
||||||
|
import Data.Maybe.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActorKey
|
import Vervis.ActorKey
|
||||||
|
@ -296,329 +304,20 @@ handleInboxActivity raw hActor iidActor rsidActor (Activity _id _luActor audienc
|
||||||
]
|
]
|
||||||
return (uNote, luContext)
|
return (uNote, luContext)
|
||||||
|
|
||||||
{-
|
fixRunningDeliveries :: (MonadIO m, MonadLogger m, IsSqlBackend backend) => ReaderT backend m ()
|
||||||
-- | Handle a Note submitted by a local user to their outbox. It can be either
|
fixRunningDeliveries = do
|
||||||
-- a comment on a local ticket, or a comment on some remote context. Return an
|
c <- updateWhereCount [UnlinkedDeliveryRunning ==. True] [UnlinkedDeliveryRunning =. False]
|
||||||
-- error message if the Note is rejected, otherwise the new 'LocalMessageId'.
|
unless (c == 0) $ logWarn $ T.concat
|
||||||
handleOutboxNote :: Text -> Note -> Handler (Either Text LocalMessageId)
|
[ "fixRunningDeliveries fixed "
|
||||||
handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished content) = runExceptT $ do
|
, T.pack (show c)
|
||||||
verifyHostLocal host "Attributed to non-local actor"
|
, " linked deliveries"
|
||||||
verifyNothing mluNote "Note specifies an id"
|
]
|
||||||
verifyNothing mpublished "Note specifies published"
|
c' <- updateWhereCount [DeliveryRunning ==. True] [DeliveryRunning =. False]
|
||||||
uContext <- fromMaybeE muContext "Note without context"
|
unless (c' == 0) $ logWarn $ T.concat
|
||||||
uRecip <- parseAudience aud "Note has not-just-single-to audience"
|
[ "fixRunningDeliveries fixed "
|
||||||
recipContextParent <- parseRecipContextParent uRecip uContext muParent
|
, T.pack (show c)
|
||||||
|
, " unlinked deliveries"
|
||||||
(lmid, mdeliver) <- ExceptT $ runDB $ runExceptT $ do
|
|
||||||
(pid, shrUser) <- verifyIsLoggedInUser luAttrib "Note attributed to different actor"
|
|
||||||
case recipContextParent of
|
|
||||||
(mparent, Left (shr, prj, num)) -> do
|
|
||||||
mdid <- lift $ runMaybeT $ do
|
|
||||||
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
|
||||||
jid <- MaybeT $ getKeyBy $ UniqueProject prj sid
|
|
||||||
t <- MaybeT $ getValBy $ UniqueTicket jid num
|
|
||||||
return $ ticketDiscuss t
|
|
||||||
did <- fromMaybeE mdid "Context: No such local ticket"
|
|
||||||
mmidParent <- for mparent $ \ parent ->
|
|
||||||
case parent of
|
|
||||||
Left (shrParent, lmidParent) -> getLocalParentMessageId did shrParent lmidParent
|
|
||||||
Right (hParent, luParent) -> do
|
|
||||||
mrm <- lift $ runMaybeT $ do
|
|
||||||
iid <- MaybeT $ getKeyBy $ UniqueInstance hParent
|
|
||||||
MaybeT $ getValBy $ UniqueRemoteMessageIdent iid luParent
|
|
||||||
rm <- fromMaybeE mrm "Remote parent unknown locally"
|
|
||||||
let mid = remoteMessageRest rm
|
|
||||||
m <- lift $ getJust mid
|
|
||||||
unless (messageRoot m == did) $
|
|
||||||
throwE "Remote parent belongs to a different discussion"
|
|
||||||
return mid
|
|
||||||
let meparent = Left <$> mmidParent
|
|
||||||
(lmid, _doc) <- lift $ insertMessage luAttrib shrUser pid uContext did muParent meparent content
|
|
||||||
return (lmid, Nothing)
|
|
||||||
(mparent, Right (hRecip, luRecip, luContext)) -> do
|
|
||||||
(did, rdid, rdnew, mluInbox) <- do
|
|
||||||
miid <- lift $ getKeyBy $ UniqueInstance hRecip
|
|
||||||
erd <-
|
|
||||||
case miid of
|
|
||||||
Just iid -> findExistingRemoteDiscussion iid hRecip luRecip luContext
|
|
||||||
Nothing -> return Nothing
|
|
||||||
case erd of
|
|
||||||
Just (d, rd, minb) -> return (d, rd, False, minb)
|
|
||||||
Nothing -> ExceptT $ withHostLock hRecip $ runExceptT $ storeRemoteDiscussion miid hRecip luRecip luContext
|
|
||||||
meparent <- for mparent $ \ parent ->
|
|
||||||
case parent of
|
|
||||||
Left (shrParent, lmidParent) -> do
|
|
||||||
when rdnew $ throwE "Local parent inexistent, RemoteDiscussion is new"
|
|
||||||
Left <$> getLocalParentMessageId did shrParent lmidParent
|
|
||||||
Right (hParent, luParent) -> do
|
|
||||||
mrm <- lift $ runMaybeT $ do
|
|
||||||
iid <- MaybeT $ getKeyBy $ UniqueInstance hParent
|
|
||||||
MaybeT $ getValBy $ UniqueRemoteMessageIdent iid luParent
|
|
||||||
case mrm of
|
|
||||||
Nothing -> return $ Right $ l2f hParent luParent
|
|
||||||
Just rm -> Left <$> do
|
|
||||||
let mid = remoteMessageRest rm
|
|
||||||
m <- lift $ getJust mid
|
|
||||||
unless (messageRoot m == did) $
|
|
||||||
throwE "Remote parent belongs to a different discussion"
|
|
||||||
return mid
|
|
||||||
(lmid, doc) <- lift $ insertMessage luAttrib shrUser pid uContext did muParent meparent content
|
|
||||||
return (lmid, Just (doc, hRecip, maybe (Right (luRecip, rdid)) Left mluInbox))
|
|
||||||
let handleDeliverError e = logError $ "Outbox POST handler: delivery failed! " <> T.pack (displayException e)
|
|
||||||
lift $ for_ mdeliver $ \ (doc, hRecip, einb) -> forkHandler handleDeliverError $ do
|
|
||||||
uInbox <-
|
|
||||||
case einb of
|
|
||||||
Left luInbox -> return $ l2f hRecip luInbox
|
|
||||||
Right (luRecip, rdid) -> do
|
|
||||||
mluInbox <- runDB $ runMaybeT $ do
|
|
||||||
iid <- MaybeT $ getKeyBy $ UniqueInstance hRecip
|
|
||||||
rs <- MaybeT $ getValBy $ UniqueRemoteActor iid luRecip
|
|
||||||
return $ remoteActorInbox rs
|
|
||||||
case mluInbox of
|
|
||||||
Just luInbox -> return $ l2f hRecip luInbox
|
|
||||||
Nothing -> do
|
|
||||||
manager <- getsYesod appHttpManager
|
|
||||||
eactor <- fetchAPID manager actorId hRecip luRecip
|
|
||||||
case eactor of
|
|
||||||
Left s -> fail $ "Fetched recipient actor: " ++ s
|
|
||||||
Right actor -> withHostLock hRecip $ runDB $ do
|
|
||||||
iid <- either entityKey id <$> insertBy (Instance hRecip)
|
|
||||||
let luInbox = actorInbox actor
|
|
||||||
rsid <- either entityKey id <$> insertBy (RemoteActor luRecip iid luInbox Nothing)
|
|
||||||
update rdid [RemoteDiscussionActor =. Just rsid, RemoteDiscussionUnlinkedActor =. Nothing]
|
|
||||||
return $ l2f hRecip luInbox
|
|
||||||
-- TODO based on the httpPostAP usage in postOutboxR
|
|
||||||
manager <- getsYesod appHttpManager
|
|
||||||
(akey1, akey2, new1) <- liftIO . readTVarIO =<< getsYesod appActorKeys
|
|
||||||
renderUrl <- getUrlRender
|
|
||||||
let (keyID, akey) =
|
|
||||||
if new1
|
|
||||||
then (renderUrl ActorKey1R, akey1)
|
|
||||||
else (renderUrl ActorKey2R, akey2)
|
|
||||||
sign b = (KeyId $ encodeUtf8 keyID, actorKeySign akey b)
|
|
||||||
actorID = renderFedURI $ l2f host luAttrib
|
|
||||||
eres <- httpPostAP manager uInbox (hRequestTarget :| [hHost, hDate, hActivityPubActor]) sign actorID doc
|
|
||||||
case eres of
|
|
||||||
Left e -> logError $ "Failed to POST to recipient's inbox: " <> T.pack (displayException e)
|
|
||||||
Right _ -> logInfo $ T.concat
|
|
||||||
[ "Successful delivery of <"
|
|
||||||
, renderFedURI $ l2f (docHost doc) (activityId $ docValue doc)
|
|
||||||
, " to <"
|
|
||||||
, renderFedURI uRecip
|
|
||||||
, ">"
|
|
||||||
]
|
]
|
||||||
return lmid
|
|
||||||
where
|
|
||||||
verifyNothing :: Monad m => Maybe a -> Text -> ExceptT Text m ()
|
|
||||||
verifyNothing Nothing _ = return ()
|
|
||||||
verifyNothing (Just _) t = throwE t
|
|
||||||
|
|
||||||
verifySameHost
|
|
||||||
:: Monad m => Text -> FedURI -> Text -> ExceptT Text m LocalURI
|
|
||||||
verifySameHost h fu t = do
|
|
||||||
let (h', lu) = f2l fu
|
|
||||||
if h == h'
|
|
||||||
then return lu
|
|
||||||
else throwE t
|
|
||||||
|
|
||||||
parseRecipContextParent
|
|
||||||
:: FedURI
|
|
||||||
-> FedURI
|
|
||||||
-> Maybe FedURI
|
|
||||||
-> ExceptT
|
|
||||||
Text
|
|
||||||
Handler
|
|
||||||
( Maybe (Either (ShrIdent, LocalMessageId) (Text, LocalURI))
|
|
||||||
, Either
|
|
||||||
(ShrIdent, PrjIdent, Int)
|
|
||||||
(Text, LocalURI, LocalURI)
|
|
||||||
)
|
|
||||||
parseRecipContextParent uRecip uContext muParent = do
|
|
||||||
let r@(hRecip, luRecip) = f2l uRecip
|
|
||||||
luContext <- verifySameHost hRecip uContext "Recipient and context on different hosts"
|
|
||||||
meparent <-
|
|
||||||
case muParent of
|
|
||||||
Nothing -> return Nothing
|
|
||||||
Just uParent ->
|
|
||||||
if uParent == uContext
|
|
||||||
then return Nothing
|
|
||||||
else Just <$> do
|
|
||||||
let (hParent, luParent) = f2l uParent
|
|
||||||
parentLocal <- hostIsLocal hParent
|
|
||||||
if parentLocal
|
|
||||||
then Left <$> parseComment luParent
|
|
||||||
else return $ Right (hParent, luParent)
|
|
||||||
local <- hostIsLocal hRecip
|
|
||||||
if local
|
|
||||||
then do
|
|
||||||
(shr, prj) <- parseProject luRecip
|
|
||||||
num <- parseTicket (shr, prj) luContext
|
|
||||||
return (meparent, Left (shr, prj, num))
|
|
||||||
else do
|
|
||||||
when (luRecip == luContext) $
|
|
||||||
throwE "Identical recipient and context"
|
|
||||||
{-
|
|
||||||
mrs <- lift $ runDB $ runMaybeT $ do
|
|
||||||
iid <- MaybeT $ getKeyBy $ UniqueInstance hRecip
|
|
||||||
MaybeT $ getBy $ UniqueRemoteActor iid luRecip
|
|
||||||
erecip <-
|
|
||||||
case mrs of
|
|
||||||
Just ers -> return $ Left ers
|
|
||||||
Nothing -> do
|
|
||||||
manager <- getsYesod appHttpManager
|
|
||||||
eactor <- fetchAPID manager actorId hRecip luRecip
|
|
||||||
case eactor of
|
|
||||||
Left s -> throwE $ "Fetched recipient actor: " <> T.pack s
|
|
||||||
Right actor -> return $ Right actor
|
|
||||||
-}
|
|
||||||
return (meparent, Right (hRecip, luRecip, luContext))
|
|
||||||
|
|
||||||
verifyIsLoggedInUser
|
|
||||||
:: LocalURI -> Text -> ExceptT Text AppDB (PersonId, ShrIdent)
|
|
||||||
verifyIsLoggedInUser lu t = do
|
|
||||||
Entity pid p <- requireVerifiedAuth
|
|
||||||
s <- lift $ getJust $ personIdent p
|
|
||||||
route2local <- getEncodeRouteLocal
|
|
||||||
let shr = sharerIdent s
|
|
||||||
if route2local (SharerR shr) == lu
|
|
||||||
then return (pid, shr)
|
|
||||||
else throwE t
|
|
||||||
|
|
||||||
findExistingRemoteDiscussion
|
|
||||||
:: InstanceId
|
|
||||||
-> Text
|
|
||||||
-> LocalURI
|
|
||||||
-> LocalURI
|
|
||||||
-> ExceptT Text AppDB
|
|
||||||
(Maybe (DiscussionId, RemoteDiscussionId, Maybe LocalURI))
|
|
||||||
findExistingRemoteDiscussion iid hRecip luRecip luContext = do
|
|
||||||
merd <- lift $ getBy $ UniqueRemoteDiscussionIdent iid luContext
|
|
||||||
for merd $ \ (Entity rdid rd) -> do
|
|
||||||
eactor <-
|
|
||||||
requireEitherM
|
|
||||||
(remoteDiscussionActor rd)
|
|
||||||
(remoteDiscussionUnlinkedActor rd)
|
|
||||||
"RemoteDiscussion actor and unlinkedActor both unset"
|
|
||||||
"RemoteDiscussion actor and unlinkedActor both set"
|
|
||||||
minb <- case eactor of
|
|
||||||
Left rsid -> do
|
|
||||||
rs <- lift $ getJust rsid
|
|
||||||
unless (remoteActorInstance rs == iid && remoteActorIdent rs == luRecip) $
|
|
||||||
throwE "Known remote context, but its actor doesn't match the new Note's recipient"
|
|
||||||
return $ Just $ remoteActorInbox rs
|
|
||||||
Right uActor -> do
|
|
||||||
unless (uActor == l2f hRecip luRecip) $
|
|
||||||
throwE "Known remote context, but its unlinked actor doesn't match the new Note's recipient"
|
|
||||||
return Nothing
|
|
||||||
return (remoteDiscussionDiscuss rd, rdid, minb)
|
|
||||||
|
|
||||||
insertRemoteDiscussion
|
|
||||||
:: InstanceId
|
|
||||||
-> Bool
|
|
||||||
-> Text
|
|
||||||
-> LocalURI
|
|
||||||
-> LocalURI
|
|
||||||
-> AppDB (DiscussionId, RemoteDiscussionId, Maybe LocalURI)
|
|
||||||
insertRemoteDiscussion iid inew hRecip luRecip luContext = do
|
|
||||||
mrs <-
|
|
||||||
if inew
|
|
||||||
then return Nothing
|
|
||||||
else getBy $ UniqueRemoteActor iid luRecip
|
|
||||||
did <- insert Discussion
|
|
||||||
rdid <- insert RemoteDiscussion
|
|
||||||
{ remoteDiscussionActor = entityKey <$> mrs
|
|
||||||
, remoteDiscussionInstance = iid
|
|
||||||
, remoteDiscussionIdent = luContext
|
|
||||||
, remoteDiscussionDiscuss = did
|
|
||||||
, remoteDiscussionUnlinkedActor =
|
|
||||||
case mrs of
|
|
||||||
Nothing -> Just $ l2f hRecip luRecip
|
|
||||||
Just _ -> Nothing
|
|
||||||
}
|
|
||||||
return (did, rdid, remoteActorInbox . entityVal <$> mrs)
|
|
||||||
|
|
||||||
storeRemoteDiscussion
|
|
||||||
:: Maybe InstanceId
|
|
||||||
-> Text
|
|
||||||
-> LocalURI
|
|
||||||
-> LocalURI
|
|
||||||
-> ExceptT Text AppDB
|
|
||||||
(DiscussionId, RemoteDiscussionId, Bool, Maybe LocalURI)
|
|
||||||
storeRemoteDiscussion miid hRecip luRecip luContext = do
|
|
||||||
(iid, inew) <-
|
|
||||||
case miid of
|
|
||||||
Just i -> return (i, False)
|
|
||||||
Nothing -> lift $ idAndNew <$> insertBy (Instance hRecip)
|
|
||||||
if inew
|
|
||||||
then do
|
|
||||||
(did, rdid, minb) <- lift $ insertRemoteDiscussion iid True hRecip luRecip luContext
|
|
||||||
return (did, rdid, True, minb)
|
|
||||||
else do
|
|
||||||
erd <- findExistingRemoteDiscussion iid hRecip luRecip luContext
|
|
||||||
case erd of
|
|
||||||
Just (did, rdid, minb) -> return (did, rdid, False, minb)
|
|
||||||
Nothing -> do
|
|
||||||
(did, rdid, minb) <- lift $ insertRemoteDiscussion iid False hRecip luRecip luContext
|
|
||||||
return (did, rdid, True, minb)
|
|
||||||
|
|
||||||
insertMessage
|
|
||||||
:: LocalURI
|
|
||||||
-> ShrIdent
|
|
||||||
-> PersonId
|
|
||||||
-> FedURI
|
|
||||||
-> DiscussionId
|
|
||||||
-> Maybe FedURI
|
|
||||||
-> Maybe (Either MessageId FedURI)
|
|
||||||
-> Text
|
|
||||||
-> AppDB (LocalMessageId, Doc Activity)
|
|
||||||
insertMessage luAttrib shrUser pid uContext did muParent meparent content = do
|
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
mid <- insert Message
|
|
||||||
{ messageCreated = now
|
|
||||||
, messageContent = content
|
|
||||||
, messageParent =
|
|
||||||
case meparent of
|
|
||||||
Just (Left midParent) -> Just midParent
|
|
||||||
_ -> Nothing
|
|
||||||
, messageRoot = did
|
|
||||||
}
|
|
||||||
lmid <- insert LocalMessage
|
|
||||||
{ localMessageAuthor = pid
|
|
||||||
, localMessageRest = mid
|
|
||||||
, localMessageUnlinkedParent =
|
|
||||||
case meparent of
|
|
||||||
Just (Right uParent) -> Just uParent
|
|
||||||
_ -> Nothing
|
|
||||||
}
|
|
||||||
route2local <- getEncodeRouteLocal
|
|
||||||
lmhid <- encodeKeyHashid lmid
|
|
||||||
let activity luAct = Doc host Activity
|
|
||||||
{ activityId = luAct
|
|
||||||
, activityActor = luAttrib
|
|
||||||
, activityAudience = aud
|
|
||||||
, activitySpecific = CreateActivity Create
|
|
||||||
{ createObject = Note
|
|
||||||
{ noteId = Just $ route2local $ MessageR shrUser lmhid
|
|
||||||
, noteAttrib = luAttrib
|
|
||||||
, noteAudience = aud
|
|
||||||
, noteReplyTo = Just $ fromMaybe uContext muParent
|
|
||||||
, noteContext = Just uContext
|
|
||||||
, notePublished = Just now
|
|
||||||
, noteContent = content
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obid <- insert OutboxItem
|
|
||||||
{ outboxItemPerson = pid
|
|
||||||
, outboxItemActivity = PersistJSON $ activity $ LocalURI "" ""
|
|
||||||
, outboxItemPublished = now
|
|
||||||
}
|
|
||||||
obhid <- encodeKeyHashid obid
|
|
||||||
let luAct = route2local $ OutboxItemR shrUser obhid
|
|
||||||
doc = activity luAct
|
|
||||||
update obid [OutboxItemActivity =. PersistJSON doc]
|
|
||||||
return (lmid, doc)
|
|
||||||
-}
|
|
||||||
|
|
||||||
data LocalTicketRecipient = LocalTicketParticipants | LocalTicketTeam
|
data LocalTicketRecipient = LocalTicketParticipants | LocalTicketTeam
|
||||||
deriving (Eq, Ord)
|
deriving (Eq, Ord)
|
||||||
|
@ -657,6 +356,44 @@ newtype FedError = FedError Text deriving Show
|
||||||
|
|
||||||
instance Exception FedError
|
instance Exception FedError
|
||||||
|
|
||||||
|
getHttpSign = do
|
||||||
|
(akey1, akey2, new1) <- liftIO . readTVarIO =<< getsYesod appActorKeys
|
||||||
|
renderUrl <- getUrlRender
|
||||||
|
let (keyID, akey) =
|
||||||
|
if new1
|
||||||
|
then (renderUrl ActorKey1R, akey1)
|
||||||
|
else (renderUrl ActorKey2R, akey2)
|
||||||
|
return $ \ b -> (KeyId $ encodeUtf8 keyID, actorKeySign akey b)
|
||||||
|
|
||||||
|
deliverHttp sign doc h luInbox = do
|
||||||
|
manager <- getsYesod appHttpManager
|
||||||
|
let inbox = l2f h luInbox
|
||||||
|
headers = hRequestTarget :| [hHost, hDate, hActivityPubActor]
|
||||||
|
httpPostAP manager inbox headers sign docActor doc
|
||||||
|
where
|
||||||
|
docActor = renderFedURI $ l2f (docHost doc) (activityActor $ docValue doc)
|
||||||
|
|
||||||
|
isInstanceErrorHttp (InvalidUrlException _ _) = False
|
||||||
|
isInstanceErrorHttp (HttpExceptionRequest _ hec) =
|
||||||
|
case hec of
|
||||||
|
ResponseTimeout -> True
|
||||||
|
ConnectionTimeout -> True
|
||||||
|
InternalException se ->
|
||||||
|
case fromException se of
|
||||||
|
Just (HandshakeFailed _) -> True
|
||||||
|
_ -> False
|
||||||
|
_ -> False
|
||||||
|
|
||||||
|
isInstanceErrorP (APPostErrorSig _) = False
|
||||||
|
isInstanceErrorP (APPostErrorHTTP he) = isInstanceErrorHttp he
|
||||||
|
|
||||||
|
isInstanceErrorG Nothing = False
|
||||||
|
isInstanceErrorG (Just e) =
|
||||||
|
case e of
|
||||||
|
APGetErrorHTTP he -> isInstanceErrorHttp he
|
||||||
|
APGetErrorJSON _ -> False
|
||||||
|
APGetErrorContentType _ -> False
|
||||||
|
|
||||||
-- | Handle a Note submitted by a local user to their outbox. It can be either
|
-- | Handle a Note submitted by a local user to their outbox. It can be either
|
||||||
-- a comment on a local ticket, or a comment on some remote context. Return an
|
-- a comment on a local ticket, or a comment on some remote context. Return an
|
||||||
-- error message if the Note is rejected, otherwise the new 'LocalMessageId'.
|
-- error message if the Note is rejected, otherwise the new 'LocalMessageId'.
|
||||||
|
@ -742,15 +479,13 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
return (did, meparent, Nothing)
|
return (did, meparent, Nothing)
|
||||||
(lmid, obid, doc) <- lift $ insertMessage luAttrib shrUser pid uContext did muParent meparent content
|
(lmid, obid, doc) <- lift $ insertMessage luAttrib shrUser pid uContext did muParent meparent content
|
||||||
moreRemotes <- deliverLocal obid localRecips mcollections
|
moreRemotes <- deliverLocal obid localRecips mcollections
|
||||||
return (lmid, doc, moreRemotes)
|
remotesHttp <- lift $ deliverRemoteDB obid remoteRecips moreRemotes
|
||||||
(lmid, doc, moreRemotes) <- case result of
|
return (lmid, obid, doc, remotesHttp)
|
||||||
|
(lmid, obid, doc, remotesHttp) <- case result of
|
||||||
Left (FedError t) -> throwE t
|
Left (FedError t) -> throwE t
|
||||||
Right r -> return r
|
Right r -> return r
|
||||||
-- TODO deliver *async* to remote sharers: remoteRecips and moreRemotes
|
let handleDeliveryError e = logError $ "Outbox POST handler: delivery failed! " <> T.pack (displayException e)
|
||||||
--
|
lift $ forkHandler handleDeliveryError $ deliverRemoteHttp obid doc remotesHttp
|
||||||
-- doc :: Doc Activity
|
|
||||||
-- remoteRecips :: [FedURI]
|
|
||||||
-- moreRemotes :: [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))]
|
|
||||||
return lmid
|
return lmid
|
||||||
where
|
where
|
||||||
verifyNothing :: Monad m => Maybe a -> e -> ExceptT e m ()
|
verifyNothing :: Monad m => Maybe a -> e -> ExceptT e m ()
|
||||||
|
@ -992,6 +727,12 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
mergeConcat :: (Ord a, Semigroup b) => [(a, b)] -> [(a, b)] -> [(a, b)]
|
mergeConcat :: (Ord a, Semigroup b) => [(a, b)] -> [(a, b)] -> [(a, b)]
|
||||||
mergeConcat xs ys = map (second sconcat) $ groupWithExtract fst snd $ LO.mergeBy (compare `on` fst) xs ys
|
mergeConcat xs ys = map (second sconcat) $ groupWithExtract fst snd $ LO.mergeBy (compare `on` fst) xs ys
|
||||||
|
|
||||||
|
fst3 :: (a, b, c) -> a
|
||||||
|
fst3 (x, _, _) = x
|
||||||
|
|
||||||
|
thd3 :: (a, b, c) -> c
|
||||||
|
thd3 (_, _, z) = z
|
||||||
|
|
||||||
-- Deliver to local recipients. For local users, find in DB and deliver.
|
-- Deliver to local recipients. For local users, find in DB and deliver.
|
||||||
-- For local collections, expand them, deliver to local users, and return a
|
-- For local collections, expand them, deliver to local users, and return a
|
||||||
-- list of remote actors found in them.
|
-- list of remote actors found in them.
|
||||||
|
@ -999,7 +740,7 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
:: OutboxItemId
|
:: OutboxItemId
|
||||||
-> [ShrIdent]
|
-> [ShrIdent]
|
||||||
-> Maybe (SharerId, FollowerSetId)
|
-> Maybe (SharerId, FollowerSetId)
|
||||||
-> ExceptT Text AppDB [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))]
|
-> ExceptT Text AppDB [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, Maybe UTCTime))]
|
||||||
deliverLocal obid recips mticket = do
|
deliverLocal obid recips mticket = do
|
||||||
recipPids <- traverse getPersonId $ nub recips
|
recipPids <- traverse getPersonId $ nub recips
|
||||||
(morePids, remotes) <-
|
(morePids, remotes) <-
|
||||||
|
@ -1048,7 +789,7 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
-- instances aren't repeated. Use a custom merge
|
-- instances aren't repeated. Use a custom merge
|
||||||
-- where we can unionBy or LO.unionBy whenever both
|
-- where we can unionBy or LO.unionBy whenever both
|
||||||
-- lists have the same instance.
|
-- lists have the same instance.
|
||||||
, map (second $ NE.nubBy ((==) `on` fst)) $ mergeConcat teamRemotes fsRemotes
|
, map (second $ NE.nubBy ((==) `on` fst3)) $ mergeConcat teamRemotes fsRemotes
|
||||||
)
|
)
|
||||||
lift $ for_ (union recipPids morePids) $ \ pid -> insert_ $ InboxItemLocal pid obid
|
lift $ for_ (union recipPids morePids) $ \ pid -> insert_ $ InboxItemLocal pid obid
|
||||||
return remotes
|
return remotes
|
||||||
|
@ -1068,11 +809,11 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
case id_ of
|
case id_ of
|
||||||
Left pid -> return pid
|
Left pid -> return pid
|
||||||
Right _gid -> throwE "Local Note addresses a local group"
|
Right _gid -> throwE "Local Note addresses a local group"
|
||||||
groupRemotes :: [(InstanceId, Text, RemoteActorId, LocalURI)] -> [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))]
|
groupRemotes :: [(InstanceId, Text, RemoteActorId, LocalURI, Maybe UTCTime)] -> [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, Maybe UTCTime))]
|
||||||
groupRemotes = groupWithExtractBy ((==) `on` fst) fst snd . map toPairs
|
groupRemotes = groupWithExtractBy ((==) `on` fst) fst snd . map toTuples
|
||||||
where
|
where
|
||||||
toPairs (iid, h, rsid, lu) = ((iid, h), (rsid, lu))
|
toTuples (iid, h, rsid, lu, ms) = ((iid, h), (rsid, lu, ms))
|
||||||
getTicketTeam :: SharerId -> AppDB ([PersonId], [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))])
|
getTicketTeam :: SharerId -> AppDB ([PersonId], [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, Maybe UTCTime))])
|
||||||
getTicketTeam sid = do
|
getTicketTeam sid = do
|
||||||
id_ <- getPersonOrGroupId sid
|
id_ <- getPersonOrGroupId sid
|
||||||
(,[]) <$> case id_ of
|
(,[]) <$> case id_ of
|
||||||
|
@ -1080,7 +821,7 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
Right gid ->
|
Right gid ->
|
||||||
map (groupMemberPerson . entityVal) <$>
|
map (groupMemberPerson . entityVal) <$>
|
||||||
selectList [GroupMemberGroup ==. gid] []
|
selectList [GroupMemberGroup ==. gid] []
|
||||||
getFollowers :: FollowerSetId -> AppDB ([PersonId], [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))])
|
getFollowers :: FollowerSetId -> AppDB ([PersonId], [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, Maybe UTCTime))])
|
||||||
getFollowers fsid = do
|
getFollowers fsid = do
|
||||||
local <- selectList [FollowTarget ==. fsid] []
|
local <- selectList [FollowTarget ==. fsid] []
|
||||||
remote <- E.select $ E.from $ \ (rf `E.InnerJoin` rs `E.InnerJoin` i) -> do
|
remote <- E.select $ E.from $ \ (rf `E.InnerJoin` rs `E.InnerJoin` i) -> do
|
||||||
|
@ -1093,12 +834,13 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
, i E.^. InstanceHost
|
, i E.^. InstanceHost
|
||||||
, rs E.^. RemoteActorId
|
, rs E.^. RemoteActorId
|
||||||
, rs E.^. RemoteActorInbox
|
, rs E.^. RemoteActorInbox
|
||||||
|
, rs E.^. RemoteActorErrorSince
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
( map (followPerson . entityVal) local
|
( map (followPerson . entityVal) local
|
||||||
, groupRemotes $
|
, groupRemotes $
|
||||||
map (\ (E.Value iid, E.Value h, E.Value rsid, E.Value luInbox) ->
|
map (\ (E.Value iid, E.Value h, E.Value rsid, E.Value luInbox, E.Value msince) ->
|
||||||
(iid, h, rsid, luInbox)
|
(iid, h, rsid, luInbox, msince)
|
||||||
)
|
)
|
||||||
remote
|
remote
|
||||||
)
|
)
|
||||||
|
@ -1118,56 +860,328 @@ handleOutboxNote host (Note mluNote luAttrib aud muParent muContext mpublished c
|
||||||
Left pid -> lift $ insert_ $ InboxItemLocal pid obid
|
Left pid -> lift $ insert_ $ InboxItemLocal pid obid
|
||||||
Right _gid -> throwE "Local Note addresses a local group"
|
Right _gid -> throwE "Local Note addresses a local group"
|
||||||
|
|
||||||
-- TODO NEXT: So far, we have 2 groups of remote actors to handle,
|
deliverRemoteDB
|
||||||
-- 'allKnown' and 'stillUnknown'. We could be done with DB and proceed to
|
:: OutboxItemId
|
||||||
-- launch HTTP requests, but we haven't considered something: Some actors
|
-> [FedURI]
|
||||||
-- are known to be unreachable:
|
-> [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, Maybe UTCTime))]
|
||||||
--
|
-> AppDB
|
||||||
-- (1) There are actors we've never reached, for whom there are pending
|
( [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, DeliveryId))]
|
||||||
-- deliveries
|
, [((InstanceId, Text), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
-- (2) There are actors we already fetched, but for whom there are
|
, [((InstanceId, Text), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
-- pending deliveries because lately their inboxes are unreachable
|
)
|
||||||
--
|
deliverRemoteDB obid recips known = do
|
||||||
-- And this brings us to 2 potential things to do:
|
|
||||||
--
|
|
||||||
-- (1) Skip the request for some actors, and instead insert a delivery to
|
|
||||||
-- the DB
|
|
||||||
-- (2) Insert/update reachability records for actors we try to reach but
|
|
||||||
-- fail
|
|
||||||
-- (3) Insert/update reachability records for actors we suddenly succeed
|
|
||||||
-- to reach
|
|
||||||
--
|
|
||||||
-- So, for each RemoteActor, we're going to add a field 'errorSince'.
|
|
||||||
-- Its type will be Maybe UTCTime, and the meaning is:
|
|
||||||
--
|
|
||||||
-- - Nothing: We haven't observed the inbox being down
|
|
||||||
-- - Just t: The time t denotes a time we couldn't reach the inbox, and
|
|
||||||
-- since that time all our following attempts failed too
|
|
||||||
--
|
|
||||||
-- In this context, inbox error means any result that isn't a 2xx status.
|
|
||||||
deliverRemote :: Doc Activity -> [FedURI] -> [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI))] -> Handler ()
|
|
||||||
deliverRemote doc recips known = runDB $ do
|
|
||||||
recips' <- for (groupByHost recips) $ \ (h, lus) -> do
|
recips' <- for (groupByHost recips) $ \ (h, lus) -> do
|
||||||
let lus' = NE.nub lus
|
let lus' = NE.nub lus
|
||||||
(iid, inew) <- idAndNew <$> insertBy' (Instance h)
|
(iid, inew) <- idAndNew <$> insertBy' (Instance h)
|
||||||
if inew
|
if inew
|
||||||
then return ((iid, h), (Nothing, Just lus'))
|
then return ((iid, h), (Nothing, Nothing, Just lus'))
|
||||||
else do
|
else do
|
||||||
es <- for lus' $ \ lu -> do
|
es <- for lus' $ \ lu -> do
|
||||||
mers <- getBy $ UniqueRemoteActor iid lu
|
ma <- runMaybeT
|
||||||
|
$ Left <$> MaybeT (getBy $ UniqueRemoteActor iid lu)
|
||||||
|
<|> Right <$> MaybeT (getBy $ UniqueUnfetchedRemoteActor iid lu)
|
||||||
return $
|
return $
|
||||||
case mers of
|
case ma of
|
||||||
Just (Entity rsid rs) -> Left (rsid, remoteActorInbox rs)
|
Nothing -> Left lu
|
||||||
Nothing -> Right lu
|
Just e ->
|
||||||
let (newKnown, unknown) = partitionEithers $ NE.toList es
|
Right $ case e of
|
||||||
return ((iid, h), (nonEmpty newKnown, nonEmpty unknown))
|
Left (Entity raid ra) -> Left (raid, remoteActorInbox ra, remoteActorErrorSince ra)
|
||||||
let moreKnown = mapMaybe (\ (i, (k, _)) -> (i,) <$> k) recips'
|
Right (Entity uraid ura) -> Right (uraid, unfetchedRemoteActorIdent ura, unfetchedRemoteActorSince ura)
|
||||||
stillUnknown = mapMaybe (\ (i, (_, u)) -> (i,) <$> u) recips'
|
let (unknown, newKnown) = partitionEithers $ NE.toList es
|
||||||
-- ^ [ ( (iid, h) , NonEmpty luActor ) ]
|
(fetched, unfetched) = partitionEithers newKnown
|
||||||
|
return ((iid, h), (nonEmpty fetched, nonEmpty unfetched, nonEmpty unknown))
|
||||||
|
let moreKnown = mapMaybe (\ (i, (f, _, _)) -> (i,) <$> f) recips'
|
||||||
|
unfetched = mapMaybe (\ (i, (_, uf, _)) -> (i,) <$> uf) recips'
|
||||||
|
stillUnknown = mapMaybe (\ (i, (_, _, uk)) -> (i,) <$> uk) recips'
|
||||||
-- TODO see the earlier TODO about merge, it applies here too
|
-- TODO see the earlier TODO about merge, it applies here too
|
||||||
allKnown = map (second $ NE.nubBy ((==) `on` fst)) $ mergeConcat known moreKnown
|
allFetched = map (second $ NE.nubBy ((==) `on` fst3)) $ mergeConcat known moreKnown
|
||||||
-- ^ [ ( (iid, h) , NonEmpty (rsid, inb) ) ]
|
fetchedDeliv <- for allFetched $ \ (i, rs) ->
|
||||||
error "TODO CONTINUE"
|
(i,) <$> insertMany' (\ (raid, _, msince) -> Delivery raid obid $ isNothing msince) rs
|
||||||
|
unfetchedDeliv <- for unfetched $ \ (i, rs) ->
|
||||||
|
(i,) <$> insertMany' (\ (uraid, _, msince) -> UnlinkedDelivery uraid obid $ isNothing msince) rs
|
||||||
|
unknownDeliv <- for stillUnknown $ \ (i, lus) -> do
|
||||||
|
-- TODO maybe for URA insertion we should do insertUnique?
|
||||||
|
rs <- insertMany' (\ lu -> UnfetchedRemoteActor (fst i) lu Nothing) lus
|
||||||
|
(i,) <$> insertMany' (\ (_, uraid) -> UnlinkedDelivery uraid obid True) rs
|
||||||
|
return
|
||||||
|
( takeNoError fetchedDeliv
|
||||||
|
, takeNoError unfetchedDeliv
|
||||||
|
, map
|
||||||
|
(second $ NE.map $ \ ((lu, ak), dlk) -> (ak, lu, dlk))
|
||||||
|
unknownDeliv
|
||||||
|
)
|
||||||
where
|
where
|
||||||
groupByHost :: [FedURI] -> [(Text, NonEmpty LocalURI)]
|
groupByHost :: [FedURI] -> [(Text, NonEmpty LocalURI)]
|
||||||
groupByHost = groupAllExtract furiHost (snd . f2l)
|
groupByHost = groupAllExtract furiHost (snd . f2l)
|
||||||
|
|
||||||
|
insertMany' mk xs = zip' xs <$> insertMany (NE.toList $ mk <$> xs)
|
||||||
|
where
|
||||||
|
zip' x y =
|
||||||
|
case nonEmpty y of
|
||||||
|
Just y' | length x == length y' -> NE.zip x y'
|
||||||
|
_ -> error "insertMany' returned different length!"
|
||||||
|
|
||||||
|
takeNoError = mapMaybe $ \ (i, rs) -> (i,) <$> nonEmpty (mapMaybe noError $ NE.toList rs)
|
||||||
|
where
|
||||||
|
noError ((ak, lu, Nothing), dlk) = Just (ak, lu, dlk)
|
||||||
|
noError ((_ , _ , Just _ ), _ ) = Nothing
|
||||||
|
|
||||||
|
deliverRemoteHttp
|
||||||
|
:: OutboxItemId
|
||||||
|
-> Doc Activity
|
||||||
|
-> ( [((InstanceId, Text), NonEmpty (RemoteActorId, LocalURI, DeliveryId))]
|
||||||
|
, [((InstanceId, Text), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
, [((InstanceId, Text), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
)
|
||||||
|
-> Handler ()
|
||||||
|
deliverRemoteHttp obid doc (fetched, unfetched, unknown) = do
|
||||||
|
sign <- getHttpSign
|
||||||
|
let deliver = deliverHttp sign doc
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
traverse_ (fork . deliverFetched deliver now) fetched
|
||||||
|
traverse_ (fork . deliverUnfetched deliver now) unfetched
|
||||||
|
traverse_ (fork . deliverUnfetched deliver now) unknown
|
||||||
|
where
|
||||||
|
fork = forkHandler $ \ e -> logError $ "Outbox POST handler: delivery failed! " <> T.pack (displayException e)
|
||||||
|
deliverFetched deliver now ((_, h), recips@(r :| rs)) = do
|
||||||
|
let (raid, luInbox, dlid) = r
|
||||||
|
e <- deliver h luInbox
|
||||||
|
let e' = case e of
|
||||||
|
Left err ->
|
||||||
|
if isInstanceErrorP err
|
||||||
|
then Nothing
|
||||||
|
else Just False
|
||||||
|
Right _resp -> Just True
|
||||||
|
case e' of
|
||||||
|
Nothing -> runDB $ do
|
||||||
|
let recips' = NE.toList recips
|
||||||
|
updateWhere [RemoteActorId <-. map fst3 recips', RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
updateWhere [DeliveryId <-. map thd3 recips'] [DeliveryRunning =. False]
|
||||||
|
Just success -> do
|
||||||
|
runDB $
|
||||||
|
if success
|
||||||
|
then delete dlid
|
||||||
|
else do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update dlid [DeliveryRunning =. False]
|
||||||
|
for_ rs $ \ (raid, luInbox, dlid) ->
|
||||||
|
fork $ do
|
||||||
|
e <- deliver h luInbox
|
||||||
|
runDB $
|
||||||
|
case e of
|
||||||
|
Left _err -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update dlid [DeliveryRunning =. False]
|
||||||
|
Right _resp -> delete dlid
|
||||||
|
deliverUnfetched deliver now ((iid, h), recips@(r :| rs)) = do
|
||||||
|
let (uraid, luActor, udlid) = r
|
||||||
|
e <- fetchRemoteActor iid h luActor
|
||||||
|
let e' = case e of
|
||||||
|
Left err ->
|
||||||
|
if isInstanceErrorG err
|
||||||
|
then Nothing
|
||||||
|
else Just Nothing
|
||||||
|
Right era -> Just $ Just era
|
||||||
|
case e' of
|
||||||
|
Nothing -> runDB $ do
|
||||||
|
let recips' = NE.toList recips
|
||||||
|
updateWhere [UnfetchedRemoteActorId <-. map fst3 recips', UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
updateWhere [UnlinkedDeliveryId <-. map thd3 recips'] [UnlinkedDeliveryRunning =. False]
|
||||||
|
Just mera -> do
|
||||||
|
for_ rs $ \ (uraid, luActor, udlid) ->
|
||||||
|
fork $ do
|
||||||
|
e <- fetchRemoteActor iid h luActor
|
||||||
|
case e of
|
||||||
|
Left _ -> runDB $ do
|
||||||
|
updateWhere [UnfetchedRemoteActorId ==. uraid, UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
update udlid [UnlinkedDeliveryRunning =. False]
|
||||||
|
Right (Entity raid ra) -> do
|
||||||
|
e' <- deliver h $ remoteActorInbox ra
|
||||||
|
runDB $
|
||||||
|
case e' of
|
||||||
|
Left _ -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
delete udlid
|
||||||
|
insert_ $ Delivery raid obid False
|
||||||
|
Right _ -> delete udlid
|
||||||
|
case mera of
|
||||||
|
Nothing -> runDB $ do
|
||||||
|
updateWhere [UnfetchedRemoteActorId ==. uraid, UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
update udlid [UnlinkedDeliveryRunning =. False]
|
||||||
|
Just (Entity raid ra) -> do
|
||||||
|
e'' <- deliver h $ remoteActorInbox ra
|
||||||
|
runDB $
|
||||||
|
case e'' of
|
||||||
|
Left _ -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
delete udlid
|
||||||
|
insert_ $ Delivery raid obid False
|
||||||
|
Right _ -> delete udlid
|
||||||
|
|
||||||
|
retryOutboxDelivery :: Handler ()
|
||||||
|
retryOutboxDelivery = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
(udls, dls) <- runDB $ do
|
||||||
|
-- Get all unlinked deliveries which aren't running already in outbox
|
||||||
|
-- post handlers
|
||||||
|
unlinked' <- E.select $ E.from $ \ (udl `E.InnerJoin` ob `E.InnerJoin` ura `E.InnerJoin` i `E.LeftOuterJoin` ra) -> do
|
||||||
|
E.on $ E.just (ura E.^. UnfetchedRemoteActorInstance) E.==. ra E.?. RemoteActorInstance
|
||||||
|
E.&&. E.just (ura E.^. UnfetchedRemoteActorIdent) E.==. ra E.?. RemoteActorIdent
|
||||||
|
E.on $ ura E.^. UnfetchedRemoteActorInstance E.==. i E.^. InstanceId
|
||||||
|
E.on $ udl E.^. UnlinkedDeliveryRecipient E.==. ura E.^. UnfetchedRemoteActorId
|
||||||
|
E.on $ udl E.^. UnlinkedDeliveryActivity E.==. ob E.^. OutboxItemId
|
||||||
|
E.where_ $ udl E.^. UnlinkedDeliveryRunning E.==. E.val False
|
||||||
|
E.orderBy [E.asc $ ura E.^. UnfetchedRemoteActorInstance, E.asc $ ura E.^. UnfetchedRemoteActorId]
|
||||||
|
return
|
||||||
|
( i E.^. InstanceId
|
||||||
|
, i E.^. InstanceHost
|
||||||
|
, ura E.^. UnfetchedRemoteActorId
|
||||||
|
, ura E.^. UnfetchedRemoteActorIdent
|
||||||
|
, ura E.^. UnfetchedRemoteActorSince
|
||||||
|
, udl E.^. UnlinkedDeliveryId
|
||||||
|
, udl E.^. UnlinkedDeliveryActivity
|
||||||
|
, ob E.^. OutboxItemActivity
|
||||||
|
, ra E.?. RemoteActorId
|
||||||
|
)
|
||||||
|
-- Strip the E.Value wrappers and organize the records for the
|
||||||
|
-- filtering and grouping we'll need to do
|
||||||
|
let unlinked = map adaptUnlinked unlinked'
|
||||||
|
-- Split into found (recipient has been reached) and lonely (recipient
|
||||||
|
-- hasn't been reached
|
||||||
|
(found, lonely) = partitionMaybes unlinked
|
||||||
|
-- Turn the found ones into linked deliveries
|
||||||
|
deleteWhere [UnlinkedDeliveryId <-. map (unlinkedID . snd) found]
|
||||||
|
insertMany_ $ map toLinked found
|
||||||
|
-- We're left with the lonely ones. We'll check which actors have been
|
||||||
|
-- unreachable for too long, and we'll delete deliveries for them. The
|
||||||
|
-- rest of the actors we'll try to reach by HTTP.
|
||||||
|
dropAfter <- getsYesod $ appDropDeliveryAfter . appSettings
|
||||||
|
let (lonelyOld, lonelyNew) = partitionEithers $ map (decideBySinceUDL dropAfter now) lonely
|
||||||
|
deleteWhere [UnlinkedDeliveryId <-. lonelyOld]
|
||||||
|
-- Now let's grab the linked deliveries, and similarly delete old ones
|
||||||
|
-- and return the rest for HTTP delivery.
|
||||||
|
linked <- E.select $ E.from $ \ (dl `E.InnerJoin` ra `E.InnerJoin` i `E.InnerJoin` ob) -> do
|
||||||
|
E.on $ dl E.^. DeliveryActivity E.==. ob E.^. OutboxItemId
|
||||||
|
E.on $ ra E.^. RemoteActorInstance E.==. i E.^. InstanceId
|
||||||
|
E.on $ dl E.^. DeliveryRecipient E.==. ra E.^. RemoteActorId
|
||||||
|
E.where_ $ dl E.^. DeliveryRunning E.==. E.val False
|
||||||
|
E.orderBy [E.asc $ ra E.^. RemoteActorInstance, E.asc $ ra E.^. RemoteActorId]
|
||||||
|
return
|
||||||
|
( i E.^. InstanceId
|
||||||
|
, i E.^. InstanceHost
|
||||||
|
, ra E.^. RemoteActorId
|
||||||
|
, ra E.^. RemoteActorInbox
|
||||||
|
, ra E.^. RemoteActorErrorSince
|
||||||
|
, dl E.^. DeliveryId
|
||||||
|
, ob E.^. OutboxItemActivity
|
||||||
|
)
|
||||||
|
let (linkedOld, linkedNew) = partitionEithers $ map (decideBySinceDL dropAfter now . adaptLinked) linked
|
||||||
|
deleteWhere [DeliveryId <-. linkedOld]
|
||||||
|
return (groupUnlinked lonelyNew, groupLinked linkedNew)
|
||||||
|
sign <- getHttpSign
|
||||||
|
let deliver = deliverHttp sign
|
||||||
|
waitsDL <- traverse (fork . deliverLinked deliver now) dls
|
||||||
|
waitsUDL <- traverse (fork . deliverUnlinked deliver now) udls
|
||||||
|
resultsDL <- sequence waitsDL
|
||||||
|
unless (and resultsDL) $ logError "Periodic delivery DL error"
|
||||||
|
resultsUDL <- sequence waitsUDL
|
||||||
|
unless (and resultsUDL) $ logError "Periodic delivery UDL error"
|
||||||
|
where
|
||||||
|
adaptUnlinked
|
||||||
|
(E.Value iid, E.Value h, E.Value uraid, E.Value luRecip, E.Value since, E.Value udlid, E.Value obid, E.Value act, E.Value mraid) =
|
||||||
|
( mraid
|
||||||
|
, ( ( (iid, h)
|
||||||
|
, ((uraid, luRecip), (udlid, obid, persistJSONValue act))
|
||||||
|
)
|
||||||
|
, since
|
||||||
|
)
|
||||||
|
)
|
||||||
|
unlinkedID ((_, (_, (udlid, _, _))), _) = udlid
|
||||||
|
toLinked (raid, ((_, (_, (_, obid, _))), _)) = Delivery raid obid False
|
||||||
|
relevant dropAfter now since = addUTCTime dropAfter since > now
|
||||||
|
decideBySinceUDL dropAfter now (udl@(_, (_, (udlid, _, _))), msince) =
|
||||||
|
case msince of
|
||||||
|
Nothing -> Right udl
|
||||||
|
Just since ->
|
||||||
|
if relevant dropAfter now since
|
||||||
|
then Right udl
|
||||||
|
else Left udlid
|
||||||
|
groupUnlinked
|
||||||
|
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
||||||
|
. groupWithExtractBy ((==) `on` fst) fst snd
|
||||||
|
adaptLinked
|
||||||
|
(E.Value iid, E.Value h, E.Value raid, E.Value inbox, E.Value since, E.Value dlid, E.Value act) =
|
||||||
|
( ( (iid, h)
|
||||||
|
, ((raid, inbox), (dlid, persistJSONValue act))
|
||||||
|
)
|
||||||
|
, since
|
||||||
|
)
|
||||||
|
decideBySinceDL dropAfter now (dl@(_, (_, (dlid, _))), msince) =
|
||||||
|
case msince of
|
||||||
|
Nothing -> Right dl
|
||||||
|
Just since ->
|
||||||
|
if relevant dropAfter now since
|
||||||
|
then Right dl
|
||||||
|
else Left dlid
|
||||||
|
groupLinked
|
||||||
|
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
||||||
|
. groupWithExtractBy ((==) `on` fst) fst snd
|
||||||
|
fork action = do
|
||||||
|
mvar <- liftIO newEmptyMVar
|
||||||
|
let handle e = do
|
||||||
|
liftIO $ putMVar mvar False
|
||||||
|
logError $ "Periodic delivery error! " <> T.pack (displayException e)
|
||||||
|
forkHandler handle $ do
|
||||||
|
success <- action
|
||||||
|
liftIO $ putMVar mvar success
|
||||||
|
return $ liftIO $ readMVar mvar
|
||||||
|
deliverLinked deliver now ((_, h), recips) = do
|
||||||
|
waitsR <- for recips $ \ ((raid, inbox), delivs) -> fork $ do
|
||||||
|
waitsD <- for delivs $ \ (dlid, doc) -> fork $ do
|
||||||
|
e <- deliver doc h inbox
|
||||||
|
case e of
|
||||||
|
Left _err -> return False
|
||||||
|
Right _resp -> do
|
||||||
|
runDB $ delete dlid
|
||||||
|
return True
|
||||||
|
results <- sequence waitsD
|
||||||
|
runDB $
|
||||||
|
if and results
|
||||||
|
then update raid [RemoteActorErrorSince =. Nothing]
|
||||||
|
else if or results
|
||||||
|
then update raid [RemoteActorErrorSince =. Just now]
|
||||||
|
else updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
return True
|
||||||
|
results <- sequence waitsR
|
||||||
|
unless (and results) $
|
||||||
|
logError $ "Periodic DL delivery error for host " <> h
|
||||||
|
return True
|
||||||
|
deliverUnlinked deliver now ((iid, h), recips) = do
|
||||||
|
waitsR <- for recips $ \ ((uraid, luRecip), delivs) -> fork $ do
|
||||||
|
e <- fetchRemoteActor iid h luRecip
|
||||||
|
case e of
|
||||||
|
Left _ -> runDB $ updateWhere [UnfetchedRemoteActorId ==. uraid, UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
Right (Entity raid ra) -> do
|
||||||
|
waitsD <- for delivs $ \ (udlid, obid, doc) -> fork $ do
|
||||||
|
e' <- deliver doc h $ remoteActorInbox ra
|
||||||
|
case e' of
|
||||||
|
Left _err -> do
|
||||||
|
runDB $ do
|
||||||
|
delete udlid
|
||||||
|
insert_ $ Delivery raid obid False
|
||||||
|
return False
|
||||||
|
Right _resp -> do
|
||||||
|
runDB $ delete udlid
|
||||||
|
return True
|
||||||
|
results <- sequence waitsD
|
||||||
|
runDB $
|
||||||
|
if and results
|
||||||
|
then update raid [RemoteActorErrorSince =. Nothing]
|
||||||
|
else if or results
|
||||||
|
then update raid [RemoteActorErrorSince =. Just now]
|
||||||
|
else updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
return True
|
||||||
|
results <- sequence waitsR
|
||||||
|
unless (and results) $
|
||||||
|
logError $ "Periodic UDL delivery error for host " <> h
|
||||||
|
return True
|
||||||
|
|
|
@ -64,6 +64,7 @@ import Yesod.Mail.Send
|
||||||
|
|
||||||
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
import qualified Network.HTTP.Signature as S (Algorithm (..))
|
||||||
|
|
||||||
|
import Control.Concurrent.ResultShare
|
||||||
import Crypto.PublicVerifKey
|
import Crypto.PublicVerifKey
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityAccess
|
import Web.ActivityAccess
|
||||||
|
@ -104,6 +105,7 @@ data App = App
|
||||||
, appInstanceMutex :: InstanceMutex
|
, appInstanceMutex :: InstanceMutex
|
||||||
, appCapSignKey :: AccessTokenSecretKey
|
, appCapSignKey :: AccessTokenSecretKey
|
||||||
, appHashidsContext :: HashidsContext
|
, appHashidsContext :: HashidsContext
|
||||||
|
, appActorFetchShare :: ResultShare (HandlerFor App) FedURI (Either (Maybe APGetError) (Entity RemoteActor)) InstanceId
|
||||||
|
|
||||||
, appActivities :: TVar (Vector (UTCTime, ActivityReport))
|
, appActivities :: TVar (Vector (UTCTime, ActivityReport))
|
||||||
}
|
}
|
||||||
|
@ -625,6 +627,7 @@ instance YesodRemoteActorStore App where
|
||||||
siteInstanceRoomMode = appMaxInstanceKeys . appSettings
|
siteInstanceRoomMode = appMaxInstanceKeys . appSettings
|
||||||
siteActorRoomMode = appMaxActorKeys . appSettings
|
siteActorRoomMode = appMaxActorKeys . appSettings
|
||||||
siteRejectOnMaxKeys = appRejectOnMaxKeys . appSettings
|
siteRejectOnMaxKeys = appRejectOnMaxKeys . appSettings
|
||||||
|
siteActorFetchShare = appActorFetchShare
|
||||||
|
|
||||||
data ActorDetail = ActorDetail
|
data ActorDetail = ActorDetail
|
||||||
{ actorDetailId :: FedURI
|
{ actorDetailId :: FedURI
|
||||||
|
|
|
@ -243,6 +243,8 @@ changes =
|
||||||
"RemoteActor"
|
"RemoteActor"
|
||||||
(Nothing :: Maybe UTCTime)
|
(Nothing :: Maybe UTCTime)
|
||||||
"errorSince"
|
"errorSince"
|
||||||
|
-- 59
|
||||||
|
, addEntities model_2019_04_12
|
||||||
]
|
]
|
||||||
|
|
||||||
migrateDB :: MonadIO m => ReaderT SqlBackend m (Either Text (Int, Int))
|
migrateDB :: MonadIO m => ReaderT SqlBackend m (Either Text (Int, Int))
|
||||||
|
|
|
@ -36,6 +36,7 @@ module Vervis.Migration.Model
|
||||||
, FollowerSet2019Generic (..)
|
, FollowerSet2019Generic (..)
|
||||||
, Ticket2019
|
, Ticket2019
|
||||||
, model_2019_04_11
|
, model_2019_04_11
|
||||||
|
, model_2019_04_12
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -94,3 +95,6 @@ makeEntitiesMigration "2019"
|
||||||
|
|
||||||
model_2019_04_11 :: [Entity SqlBackend]
|
model_2019_04_11 :: [Entity SqlBackend]
|
||||||
model_2019_04_11 = $(schema "2019_04_11")
|
model_2019_04_11 = $(schema "2019_04_11")
|
||||||
|
|
||||||
|
model_2019_04_12 :: [Entity SqlBackend]
|
||||||
|
model_2019_04_12 = $(schema "2019_04_12")
|
||||||
|
|
|
@ -23,6 +23,9 @@ module Vervis.RemoteActorStore
|
||||||
, keyListedByActorShared
|
, keyListedByActorShared
|
||||||
, VerifKeyDetail (..)
|
, VerifKeyDetail (..)
|
||||||
, addVerifKey
|
, addVerifKey
|
||||||
|
, actorFetchShareSettings
|
||||||
|
, fetchRemoteActor
|
||||||
|
, deleteUnusedURAs
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -46,11 +49,12 @@ import Database.Persist
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
import UnliftIO.MVar (withMVar)
|
import UnliftIO.MVar (withMVar)
|
||||||
import Yesod.Core hiding (logError)
|
import Yesod.Core hiding (logWarn, logError)
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
import qualified Data.HashMap.Strict as M
|
import qualified Data.HashMap.Strict as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Crypto.PublicVerifKey
|
import Crypto.PublicVerifKey
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
@ -78,7 +82,7 @@ class Yesod site => YesodRemoteActorStore site where
|
||||||
siteActorRoomMode :: site -> Maybe Int
|
siteActorRoomMode :: site -> Maybe Int
|
||||||
siteRejectOnMaxKeys :: site -> Bool
|
siteRejectOnMaxKeys :: site -> Bool
|
||||||
|
|
||||||
siteActorFetchShare :: site -> ResultShare (HandlerFor site) FedURI (Either String (Entity RemoteActor)) InstanceId
|
siteActorFetchShare :: site -> ResultShare (HandlerFor site) FedURI (Either (Maybe APGetError) (Entity RemoteActor)) InstanceId
|
||||||
|
|
||||||
-- TODO this is copied from stm-2.5, remove when we upgrade LTS
|
-- TODO this is copied from stm-2.5, remove when we upgrade LTS
|
||||||
stateTVar :: TVar s -> (s -> (a, s)) -> STM a
|
stateTVar :: TVar s -> (s -> (a, s)) -> STM a
|
||||||
|
@ -452,7 +456,7 @@ actorFetchShareSettings
|
||||||
, BaseBackend (YesodPersistBackend site) ~ SqlBackend
|
, BaseBackend (YesodPersistBackend site) ~ SqlBackend
|
||||||
, HasHttpManager site
|
, HasHttpManager site
|
||||||
)
|
)
|
||||||
=> ResultShareSettings (HandlerFor site) FedURI (Either String (Entity RemoteActor)) InstanceId
|
=> ResultShareSettings (HandlerFor site) FedURI (Either (Maybe APGetError) (Entity RemoteActor)) InstanceId
|
||||||
actorFetchShareSettings = ResultShareSettings
|
actorFetchShareSettings = ResultShareSettings
|
||||||
{ resultShareFork = forkHandler $ \ e -> logError $ "ActorFetchShare action failed! " <> T.pack (displayException e)
|
{ resultShareFork = forkHandler $ \ e -> logError $ "ActorFetchShare action failed! " <> T.pack (displayException e)
|
||||||
, resultShareAction = \ u iid -> do
|
, resultShareAction = \ u iid -> do
|
||||||
|
@ -462,7 +466,7 @@ actorFetchShareSettings = ResultShareSettings
|
||||||
Just ers -> return $ Right ers
|
Just ers -> return $ Right ers
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
manager <- getsYesod getHttpManager
|
manager <- getsYesod getHttpManager
|
||||||
eactor <- fetchAPID manager actorId h lu
|
eactor <- fetchAPID' manager actorId h lu
|
||||||
for eactor $ \ actor -> runDB $
|
for eactor $ \ actor -> runDB $
|
||||||
insertEntity $ RemoteActor lu iid (actorInbox actor) Nothing
|
insertEntity $ RemoteActor lu iid (actorInbox actor) Nothing
|
||||||
}
|
}
|
||||||
|
@ -473,7 +477,7 @@ fetchRemoteActor
|
||||||
, BaseBackend (YesodPersistBackend site) ~ SqlBackend
|
, BaseBackend (YesodPersistBackend site) ~ SqlBackend
|
||||||
, YesodRemoteActorStore site
|
, YesodRemoteActorStore site
|
||||||
)
|
)
|
||||||
=> InstanceId -> Text -> LocalURI -> HandlerFor site (Either String (Entity RemoteActor))
|
=> InstanceId -> Text -> LocalURI -> HandlerFor site (Either (Maybe APGetError) (Entity RemoteActor))
|
||||||
fetchRemoteActor iid host luActor = do
|
fetchRemoteActor iid host luActor = do
|
||||||
mers <- runDB $ getBy $ UniqueRemoteActor iid luActor
|
mers <- runDB $ getBy $ UniqueRemoteActor iid luActor
|
||||||
case mers of
|
case mers of
|
||||||
|
@ -481,3 +485,12 @@ fetchRemoteActor iid host luActor = do
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
afs <- getsYesod siteActorFetchShare
|
afs <- getsYesod siteActorFetchShare
|
||||||
runShared afs (l2f host luActor) iid
|
runShared afs (l2f host luActor) iid
|
||||||
|
|
||||||
|
deleteUnusedURAs = do
|
||||||
|
uraids <- E.select $ E.from $ \ ura -> do
|
||||||
|
E.where_ $ E.notExists $ E.from $ \ udl ->
|
||||||
|
E.where_ $ ura E.^. UnfetchedRemoteActorId E.==. udl E.^. UnlinkedDeliveryRecipient
|
||||||
|
return $ ura E.^. UnfetchedRemoteActorId
|
||||||
|
unless (null uraids) $ do
|
||||||
|
deleteWhere [UnfetchedRemoteActorId <-. map E.unValue uraids]
|
||||||
|
logWarn $ T.pack (show $ length uraids) <> " unused URAs deleted"
|
||||||
|
|
|
@ -53,6 +53,7 @@ module Web.ActivityPub
|
||||||
, httpPostAP
|
, httpPostAP
|
||||||
, Fetched (..)
|
, Fetched (..)
|
||||||
, fetchAPID
|
, fetchAPID
|
||||||
|
, fetchAPID'
|
||||||
, keyListedByActor
|
, keyListedByActor
|
||||||
, fetchUnknownKey
|
, fetchUnknownKey
|
||||||
, fetchKnownPersonalKey
|
, fetchKnownPersonalKey
|
||||||
|
@ -680,8 +681,11 @@ data Fetched = Fetched
|
||||||
-- we received.
|
-- we received.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fetchAP' :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT APGetError m a
|
||||||
|
fetchAP' m u = ExceptT $ second responseBody <$> httpGetAP m u
|
||||||
|
|
||||||
fetchAP :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT String m a
|
fetchAP :: (MonadIO m, FromJSON a) => Manager -> FedURI -> ExceptT String m a
|
||||||
fetchAP m u = ExceptT $ bimap displayException responseBody <$> httpGetAP m u
|
fetchAP m u = withExceptT displayException $ fetchAP' m u
|
||||||
|
|
||||||
{-
|
{-
|
||||||
fetchAPH :: (MonadIO m, ActivityPub a) => Manager -> Text -> LocalURI -> ExceptT String m a
|
fetchAPH :: (MonadIO m, ActivityPub a) => Manager -> Text -> LocalURI -> ExceptT String m a
|
||||||
|
@ -692,12 +696,18 @@ fetchAPH m h lu = do
|
||||||
else throwE "Object @id URI's host doesn't match the URI we fetched"
|
else throwE "Object @id URI's host doesn't match the URI we fetched"
|
||||||
-}
|
-}
|
||||||
|
|
||||||
fetchAPID :: (MonadIO m, ActivityPub a) => Manager -> (a -> LocalURI) -> Text -> LocalURI -> m (Either String a)
|
fetchAPID' :: (MonadIO m, ActivityPub a) => Manager -> (a -> LocalURI) -> Text -> LocalURI -> m (Either (Maybe APGetError) a)
|
||||||
fetchAPID m getId h lu = runExceptT $ do
|
fetchAPID' m getId h lu = runExceptT $ do
|
||||||
Doc h' v <- fetchAP m $ l2f h lu
|
Doc h' v <- withExceptT Just $ fetchAP' m $ l2f h lu
|
||||||
if h == h' && getId v == lu
|
if h == h' && getId v == lu
|
||||||
then return v
|
then return v
|
||||||
else throwE "Object @id doesn't match the URI we fetched"
|
else throwE Nothing
|
||||||
|
|
||||||
|
fetchAPID :: (MonadIO m, ActivityPub a) => Manager -> (a -> LocalURI) -> Text -> LocalURI -> m (Either String a)
|
||||||
|
fetchAPID m getId h lu = first showError <$> fetchAPID' m getId h lu
|
||||||
|
where
|
||||||
|
showError Nothing = "Object @id doesn't match the URI we fetched"
|
||||||
|
showError (Just e) = displayException e
|
||||||
|
|
||||||
fetchAPIDOrH
|
fetchAPIDOrH
|
||||||
:: (MonadIO m, ActivityPub a, ActivityPub b)
|
:: (MonadIO m, ActivityPub a, ActivityPub b)
|
||||||
|
|
|
@ -337,6 +337,7 @@ library
|
||||||
, time-interval
|
, time-interval
|
||||||
, time-interval-aeson
|
, time-interval-aeson
|
||||||
, time-units
|
, time-units
|
||||||
|
, tls
|
||||||
, transformers
|
, transformers
|
||||||
-- probably should be replaced with lenses once I learn
|
-- probably should be replaced with lenses once I learn
|
||||||
, tuple
|
, tuple
|
||||||
|
|
Loading…
Reference in a new issue