S2S: Loom: Port Offer{MR} handler from old federation code
This is to allow getting rid of the old C2S offerTicketC and write a C2S Offer handler in the new actor system. And that is to allow ticket opening and closing to work, so that it can use delegated OCAPs too, as a first demo of delegated OCAPs in action.
This commit is contained in:
parent
909ba94b49
commit
a06003c361
7 changed files with 444 additions and 351 deletions
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
module Vervis.API
|
module Vervis.API
|
||||||
( handleViaActor
|
( handleViaActor
|
||||||
, acceptC
|
--, acceptC
|
||||||
--, addBundleC
|
--, addBundleC
|
||||||
, applyC
|
, applyC
|
||||||
--, noteC
|
--, noteC
|
||||||
|
@ -188,6 +188,7 @@ verifyRemoteAddressed remoteRecips u =
|
||||||
lus <- lookup h remoteRecips
|
lus <- lookup h remoteRecips
|
||||||
guard $ lu `elem` lus
|
guard $ lu `elem` lus
|
||||||
|
|
||||||
|
{-
|
||||||
acceptC
|
acceptC
|
||||||
:: Entity Person
|
:: Entity Person
|
||||||
-> Actor
|
-> Actor
|
||||||
|
@ -203,8 +204,6 @@ acceptC
|
||||||
-> AP.Accept URIMode
|
-> AP.Accept URIMode
|
||||||
-> ExceptT Text Handler OutboxItemId
|
-> ExceptT Text Handler OutboxItemId
|
||||||
acceptC (Entity senderPersonID senderPerson) senderActor maybeCap localRecips remoteRecips fwdHosts action accept = do
|
acceptC (Entity senderPersonID senderPerson) senderActor maybeCap localRecips remoteRecips fwdHosts action accept = do
|
||||||
error "acceptC temporarily disabled due to actor refactoring"
|
|
||||||
{-
|
|
||||||
-- Check input
|
-- Check input
|
||||||
verifyNothingE maybeCap "Capability not needed"
|
verifyNothingE maybeCap "Capability not needed"
|
||||||
acceptee <- parseAccept accept
|
acceptee <- parseAccept accept
|
||||||
|
@ -1922,7 +1921,7 @@ offerTicketC (Entity senderPersonID senderPerson) senderActor maybeCap localReci
|
||||||
lift $ forkWorker "offerTicketC: async HTTP Offer delivery" deliverHttpOffer
|
lift $ forkWorker "offerTicketC: async HTTP Offer delivery" deliverHttpOffer
|
||||||
for_ maybeAcceptMaybePull $ \ (deliverHttpAccept, maybePull) -> do
|
for_ maybeAcceptMaybePull $ \ (deliverHttpAccept, maybePull) -> do
|
||||||
lift $ forkWorker "offerTicketC: async HTTP Accept delivery" deliverHttpAccept
|
lift $ forkWorker "offerTicketC: async HTTP Accept delivery" deliverHttpAccept
|
||||||
traverse generatePatches maybePull
|
VA2.runActE $ traverse generatePatches maybePull
|
||||||
|
|
||||||
return offerID
|
return offerID
|
||||||
|
|
||||||
|
|
|
@ -18,23 +18,40 @@ module Vervis.Actor.Loom
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Exception.Base
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
import Control.Monad.Logger.CallStack
|
import Control.Monad.Logger.CallStack
|
||||||
import Control.Monad.Trans.Class
|
import Control.Monad.Trans.Class
|
||||||
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.Align
|
||||||
|
import Data.Bifunctor
|
||||||
|
import Data.Bitraversable
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
|
import Data.List.NonEmpty (NonEmpty (..))
|
||||||
|
import Data.Maybe
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Data.These
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
|
import Data.Traversable
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
|
import Database.Persist.Sql
|
||||||
|
import Optics.Core
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Control.Concurrent.Actor
|
import Control.Concurrent.Actor
|
||||||
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
|
import Web.Actor
|
||||||
|
import Web.Actor.Persist
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import qualified Web.ActivityPub as AP
|
import qualified Web.ActivityPub as AP
|
||||||
|
@ -42,19 +59,360 @@ import qualified Web.ActivityPub as AP
|
||||||
import Control.Monad.Trans.Except.Local
|
import Control.Monad.Trans.Except.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.Access
|
||||||
|
import Vervis.ActivityPub
|
||||||
import Vervis.Actor
|
import Vervis.Actor
|
||||||
|
import Vervis.Actor.Common
|
||||||
|
import Vervis.Actor2
|
||||||
import Vervis.Cloth
|
import Vervis.Cloth
|
||||||
|
import Vervis.Data.Actor
|
||||||
|
import Vervis.Data.Collab
|
||||||
import Vervis.Data.Discussion
|
import Vervis.Data.Discussion
|
||||||
|
import Vervis.Data.Ticket
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Federation.Util
|
import Vervis.Federation.Util
|
||||||
|
import Vervis.Fetch
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model hiding (deckCreate)
|
||||||
|
import Vervis.Recipient (makeRecipientSet, LocalStageBy (..), Aud (..), collectAudience, localActorFollowers)
|
||||||
|
import Vervis.Persist.Actor
|
||||||
|
import Vervis.Persist.Collab
|
||||||
import Vervis.Persist.Discussion
|
import Vervis.Persist.Discussion
|
||||||
|
import Vervis.RemoteActorStore
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
import Vervis.Web.Repo
|
||||||
|
|
||||||
|
-- Meaning: An actor A is offering a ticket or a ticket dependency
|
||||||
|
-- Behavior:
|
||||||
|
-- * Verify I'm the target
|
||||||
|
-- * Insert the Offer to my inbox
|
||||||
|
-- * Create the new ticket in my DB
|
||||||
|
-- * Forward the Offer to my followers
|
||||||
|
-- * Publish an Accept to:
|
||||||
|
-- - My followers
|
||||||
|
-- - Offer sender+followers
|
||||||
|
loomOffer
|
||||||
|
:: UTCTime
|
||||||
|
-> LoomId
|
||||||
|
-> Verse
|
||||||
|
-> AP.Offer URIMode
|
||||||
|
-> ActE (Text, Act (), Next)
|
||||||
|
loomOffer now loomID (Verse authorIdMsig body) (AP.Offer object uTarget) = do
|
||||||
|
|
||||||
|
-- Check input
|
||||||
|
(title, desc, source, originTipOrBundle, targetRepoID, maybeTargetBranch) <- do
|
||||||
|
ticket <-
|
||||||
|
case object of
|
||||||
|
AP.OfferTicket t -> pure t
|
||||||
|
_ -> throwE "Unsupported Offer.object type"
|
||||||
|
ObjURI hAuthor _ <- lift $ getActorURI authorIdMsig
|
||||||
|
let author = bimap (view _1) (remoteAuthorURI . view _1) authorIdMsig
|
||||||
|
WorkItemOffer {..} <- checkOfferTicket hAuthor ticket uTarget
|
||||||
|
unless (bimap LocalActorPerson id wioAuthor == author) $
|
||||||
|
throwE "Offering a Ticket attributed to someone else"
|
||||||
|
Merge maybeOriginTip maybeBundle targetTip <- case wioRest of
|
||||||
|
TAM_Task _ ->
|
||||||
|
throwE
|
||||||
|
"Offer target is some local deck, so I have no use for \
|
||||||
|
\this Offer. Was I supposed to receive it?"
|
||||||
|
TAM_Merge loomID' merge ->
|
||||||
|
if loomID' == loomID
|
||||||
|
then return merge
|
||||||
|
else throwE
|
||||||
|
"Offer target is some other local loom, so I have \
|
||||||
|
\no use for this Offer. Was I supposed to receive \
|
||||||
|
\it?"
|
||||||
|
TAM_Remote _ _ ->
|
||||||
|
throwE
|
||||||
|
"Offer target is some remote tracker, so I have no use \
|
||||||
|
\for this Offer. Was I supposed to receive it?"
|
||||||
|
originTipOrBundle <-
|
||||||
|
fromMaybeE
|
||||||
|
(align maybeOriginTip maybeBundle)
|
||||||
|
"MR provides neither origin nor patches"
|
||||||
|
(targetRepoID, maybeTargetBranch) <-
|
||||||
|
case targetTip of
|
||||||
|
TipLocalRepo repoID -> pure (repoID, Nothing)
|
||||||
|
TipLocalBranch repoID branch -> pure (repoID, Just branch)
|
||||||
|
_ -> throwE "MR target is a remote repo (this tracker serves only local repos)"
|
||||||
|
return (wioTitle, wioDesc, wioSource, originTipOrBundle, targetRepoID, maybeTargetBranch)
|
||||||
|
|
||||||
|
-- Verify the capability URI, if provided, is one of:
|
||||||
|
-- * Outbox item URI of a local actor, i.e. a local activity
|
||||||
|
-- * A remote URI
|
||||||
|
maybeCapability <-
|
||||||
|
for (AP.activityCapability $ actbActivity body) $ \ uCap ->
|
||||||
|
nameExceptT "Offer.capability" $
|
||||||
|
first (\ (actor, _, item) -> (actor, item)) <$>
|
||||||
|
parseActivityURI' uCap
|
||||||
|
|
||||||
|
-- If origin repo is remote, HTTP GET its AP representation and
|
||||||
|
-- remember it in our DB
|
||||||
|
--
|
||||||
|
-- Why do we need to HTTP GET it? Because:
|
||||||
|
-- * No support for providing a signed repo object directly in the
|
||||||
|
-- Offer activity
|
||||||
|
-- * It may be nice to make sure a remote origin repo's VCS type
|
||||||
|
-- matches the target repo's VCS, even if patches are provided too
|
||||||
|
-- + However there's no support for caching VCS type when
|
||||||
|
-- remembering remote repo in our DB, so we'd have to check this
|
||||||
|
-- every time
|
||||||
|
-- * If origin is remote and no patches are provided, we'll need to
|
||||||
|
-- know the clone URL to generate the patches ourselves
|
||||||
|
-- + However the code here, for some simplicity, doesn't have a
|
||||||
|
-- way to skip that and do the whole handler synchronously in
|
||||||
|
-- case patches are provided or the origin is a local repo
|
||||||
|
-- + And no support for caching the clone URI in DB when
|
||||||
|
-- remembering the remote repo, so we'd need to do this every
|
||||||
|
-- time
|
||||||
|
let originTipOrBundle' =
|
||||||
|
bimap
|
||||||
|
(\case
|
||||||
|
TipLocalRepo repoID -> Left (repoID, Nothing)
|
||||||
|
TipLocalBranch repoID branch -> Left (repoID, Just branch)
|
||||||
|
TipRemote uOrigin -> Right (uOrigin, Nothing)
|
||||||
|
TipRemoteBranch uRepo branch -> Right (uRepo, Just branch)
|
||||||
|
)
|
||||||
|
id
|
||||||
|
originTipOrBundle
|
||||||
|
originTipOrBundle'' <-
|
||||||
|
bitraverse
|
||||||
|
(bitraverse
|
||||||
|
pure
|
||||||
|
(\ (uOrigin, maybeOriginBranch) -> do
|
||||||
|
case maybeOriginBranch of
|
||||||
|
Nothing -> do
|
||||||
|
(vcs, raid, uClone, mb) <- withExceptT (T.pack . show) $ httpGetRemoteTip' uOrigin
|
||||||
|
return (vcs, (raid, uClone, first Just <$> mb))
|
||||||
|
Just branch -> do
|
||||||
|
(vcs, raid, uClone) <- withExceptT (T.pack . show) $ httpGetRemoteRepo' uOrigin
|
||||||
|
return (vcs, (raid, uClone, Just (Nothing, branch)))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pure
|
||||||
|
originTipOrBundle'
|
||||||
|
|
||||||
|
maybeNew <- withDBExcept $ do
|
||||||
|
|
||||||
|
-- Grab me from DB
|
||||||
|
(loomRecip, actorRecip) <- lift $ do
|
||||||
|
d <- getJust loomID
|
||||||
|
(d,) <$> getJust (loomActor d)
|
||||||
|
|
||||||
|
-- Grab loom's repo from DB and verify that it consents to be served by
|
||||||
|
-- the loom, otherwise this loom doesn't accept tickets
|
||||||
|
let recipLoomRepoID = loomRepo loomRecip
|
||||||
|
unless (targetRepoID == recipLoomRepoID) $
|
||||||
|
throwE "MR target repo isn't the one served by the Offer target loom"
|
||||||
|
targetRepo <- lift $ getJust targetRepoID
|
||||||
|
unless (repoLoom targetRepo == Just loomID) $
|
||||||
|
throwE "Offer target loom doesn't have repo's consent to serve it"
|
||||||
|
|
||||||
|
-- Verify VCS type match between patch bundle and target repo
|
||||||
|
let targetRepoVCS = repoVcs targetRepo
|
||||||
|
for_ (justThere originTipOrBundle) $ \ (Material typ diffs) -> do
|
||||||
|
unless (targetRepoVCS == patchMediaTypeVCS typ) $
|
||||||
|
throwE "Patch type and local target repo VCS mismatch"
|
||||||
|
case (typ, diffs) of
|
||||||
|
(PatchMediaTypeDarcs, _ :| _ : _) ->
|
||||||
|
throwE "More than one Darcs dpatch file provided"
|
||||||
|
_ -> pure ()
|
||||||
|
|
||||||
|
-- If origin repo is local, find it in our DB.
|
||||||
|
--
|
||||||
|
-- Verify the (local or remote) origin repo's VCS type matches the
|
||||||
|
-- target repo.
|
||||||
|
originOrBundle' <-
|
||||||
|
bitraverse
|
||||||
|
(bitraverse
|
||||||
|
(\ origin@(repoID, maybeBranch) -> do
|
||||||
|
repo <- getE repoID "MR origin local repo not found in DB"
|
||||||
|
unless (repoVcs repo == targetRepoVCS) $
|
||||||
|
throwE "Local origin repo VCS differs from target repo VCS"
|
||||||
|
return origin
|
||||||
|
)
|
||||||
|
(\ (vcs, origin) -> do
|
||||||
|
unless (vcs == targetRepoVCS) $
|
||||||
|
throwE "Remote origin repo VCS differs from target repo VCS"
|
||||||
|
return origin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pure
|
||||||
|
originTipOrBundle''
|
||||||
|
|
||||||
|
-- Verify that branches are specified for Git and aren't specified for
|
||||||
|
-- Darcs
|
||||||
|
-- Also, produce a data structure separating by VCS rather than by
|
||||||
|
-- local/remote origin, which we'll need for generating patches
|
||||||
|
tipInfo <- case targetRepoVCS of
|
||||||
|
VCSGit -> do
|
||||||
|
targetBranch <- fromMaybeE maybeTargetBranch "Local target repo is Git but no target branch specified"
|
||||||
|
maybeOrigin <- for (justHere originOrBundle') $ \case
|
||||||
|
Left (originRepoID, maybeOriginBranch) -> do
|
||||||
|
originBranch <- fromMaybeE maybeOriginBranch "Local origin repo is Git but no origin branch specified"
|
||||||
|
return (Left originRepoID, originBranch)
|
||||||
|
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
|
||||||
|
(_maybeURI, originBranch) <- fromMaybeE maybeOriginBranch "Remote origin repo is Git but no origin branch specified"
|
||||||
|
return (Right uClone, originBranch)
|
||||||
|
return $ Left (targetBranch, maybeOrigin)
|
||||||
|
VCSDarcs -> do
|
||||||
|
verifyNothingE maybeTargetBranch "Local target repo is Darcs but target branch specified"
|
||||||
|
maybeOriginRepo <- for (justHere originOrBundle') $ \case
|
||||||
|
Left (originRepoID, maybeOriginBranch) -> do
|
||||||
|
verifyNothingE maybeOriginBranch "Local origin repo is Darcs but origin branch specified"
|
||||||
|
return $ Left originRepoID
|
||||||
|
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
|
||||||
|
verifyNothingE maybeOriginBranch "Remote origin repo is Darcs but origin branch specified"
|
||||||
|
return $ Right uClone
|
||||||
|
return $ Right $ maybeOriginRepo
|
||||||
|
|
||||||
|
-- Insert the Offer to my inbox
|
||||||
|
mractid <- lift $ insertToInbox now authorIdMsig body (actorInbox actorRecip) False
|
||||||
|
for mractid $ \ offerDB -> do
|
||||||
|
|
||||||
|
-- If a capability is provided, check it
|
||||||
|
for_ maybeCapability $ \ cap -> do
|
||||||
|
lcap <-
|
||||||
|
case cap of
|
||||||
|
Left c -> pure c
|
||||||
|
Right _ -> throwE "Capability is a remote URI, i.e. not authored by me"
|
||||||
|
verifyCapability'
|
||||||
|
lcap
|
||||||
|
authorIdMsig
|
||||||
|
(GrantResourceLoom loomID)
|
||||||
|
AP.RoleReport
|
||||||
|
|
||||||
|
-- Prepare forwarding the Offer to my followers
|
||||||
|
let recipByID = grantResourceLocalActor $ GrantResourceLoom loomID
|
||||||
|
recipByHash <- hashLocalActor recipByID
|
||||||
|
let sieve = makeRecipientSet [] [localActorFollowers recipByHash]
|
||||||
|
|
||||||
|
-- Insert the new ticket to our DB
|
||||||
|
acceptID <- lift $ insertEmptyOutboxItem' (actorOutbox actorRecip) now
|
||||||
|
offerDB' <-
|
||||||
|
bitraverse
|
||||||
|
(traverseOf _1 $ \case
|
||||||
|
LocalActorPerson personID -> pure personID
|
||||||
|
_ -> throwE "Local non-Person ticket authors not allowed"
|
||||||
|
)
|
||||||
|
pure
|
||||||
|
offerDB
|
||||||
|
ticketID <- lift $ insertTask title desc source offerDB' acceptID
|
||||||
|
clothID <- lift $ insertMerge loomID ticketID maybeTargetBranch originOrBundle'
|
||||||
|
let maybePull =
|
||||||
|
let maybeTipInfo =
|
||||||
|
case tipInfo of
|
||||||
|
Left (b, mo) -> Left . (b,) <$> mo
|
||||||
|
Right mo -> Right <$> mo
|
||||||
|
hasBundle = isJust $ justThere originOrBundle'
|
||||||
|
in (clothID, targetRepoID, hasBundle,) <$> maybeTipInfo
|
||||||
|
|
||||||
|
-- Prepare an Accept activity and insert to my outbox
|
||||||
|
accept@(actionAccept, _, _, _) <- lift $ prepareAccept clothID
|
||||||
|
let recipByKey = LocalActorLoom loomID
|
||||||
|
_luAccept <- lift $ updateOutboxItem' recipByKey acceptID actionAccept
|
||||||
|
|
||||||
|
return (loomActor loomRecip, sieve, acceptID, accept, maybePull)
|
||||||
|
|
||||||
|
case maybeNew of
|
||||||
|
Nothing -> done "I already have this activity in my inbox"
|
||||||
|
Just (loomActorID, sieve, acceptID, (actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept), maybePull) -> do
|
||||||
|
traverse_ generatePatches maybePull
|
||||||
|
forwardActivity
|
||||||
|
authorIdMsig body (LocalActorLoom loomID) loomActorID sieve
|
||||||
|
lift $ sendActivity
|
||||||
|
(LocalActorLoom loomID) loomActorID localRecipsAccept
|
||||||
|
remoteRecipsAccept fwdHostsAccept acceptID actionAccept
|
||||||
|
done "Opened a MR and forwarded the Offer"
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
insertTask title desc source offerDB acceptID = do
|
||||||
|
did <- insert Discussion
|
||||||
|
fsid <- insert FollowerSet
|
||||||
|
tid <- insert Ticket
|
||||||
|
{ ticketNumber = Nothing
|
||||||
|
, ticketCreated = now
|
||||||
|
, ticketTitle = title
|
||||||
|
, ticketSource = source
|
||||||
|
, ticketDescription = desc
|
||||||
|
, ticketDiscuss = did
|
||||||
|
, ticketFollowers = fsid
|
||||||
|
, ticketAccept = acceptID
|
||||||
|
}
|
||||||
|
case offerDB of
|
||||||
|
Left (personID, _, offerID) ->
|
||||||
|
insert_ TicketAuthorLocal
|
||||||
|
{ ticketAuthorLocalTicket = tid
|
||||||
|
, ticketAuthorLocalAuthor = personID
|
||||||
|
, ticketAuthorLocalOpen = offerID
|
||||||
|
}
|
||||||
|
Right (author, _, offerID) ->
|
||||||
|
insert_ TicketAuthorRemote
|
||||||
|
{ ticketAuthorRemoteTicket = tid
|
||||||
|
, ticketAuthorRemoteAuthor = remoteAuthorId author
|
||||||
|
, ticketAuthorRemoteOpen = offerID
|
||||||
|
}
|
||||||
|
return tid
|
||||||
|
|
||||||
|
insertMerge
|
||||||
|
:: LoomId
|
||||||
|
-> TicketId
|
||||||
|
-> Maybe Text
|
||||||
|
-> These
|
||||||
|
(Either
|
||||||
|
(RepoId, Maybe Text)
|
||||||
|
(RemoteActorId, FedURI, Maybe (Maybe LocalURI, Text))
|
||||||
|
)
|
||||||
|
Material
|
||||||
|
-> ActDB TicketLoomId
|
||||||
|
insertMerge loomID ticketID maybeTargetBranch originOrBundle = do
|
||||||
|
clothID <- insert $ TicketLoom ticketID loomID maybeTargetBranch
|
||||||
|
for_ (justHere originOrBundle) $ \case
|
||||||
|
Left (repoID, maybeOriginBranch) ->
|
||||||
|
insert_ $ MergeOriginLocal clothID repoID maybeOriginBranch
|
||||||
|
Right (remoteActorID, _uClone, maybeOriginBranch) -> do
|
||||||
|
originID <- insert $ MergeOriginRemote clothID remoteActorID
|
||||||
|
for_ maybeOriginBranch $ \ (mlu, b) ->
|
||||||
|
insert_ $ MergeOriginRemoteBranch originID mlu b
|
||||||
|
for_ (justThere originOrBundle) $ \ (Material typ diffs) -> do
|
||||||
|
bundleID <- insert $ Bundle clothID False
|
||||||
|
insertMany_ $ NE.toList $ NE.reverse $
|
||||||
|
NE.map (Patch bundleID now typ) diffs
|
||||||
|
return clothID
|
||||||
|
|
||||||
|
prepareAccept clothID = do
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
|
||||||
|
audSender <- makeAudSenderWithFollowers authorIdMsig
|
||||||
|
loomHash <- encodeKeyHashid loomID
|
||||||
|
clothHash <- encodeKeyHashid clothID
|
||||||
|
let audLoom = AudLocal [] [LocalStageLoomFollowers loomHash]
|
||||||
|
uOffer <- lift $ getActivityURI authorIdMsig
|
||||||
|
|
||||||
|
let (recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
|
||||||
|
collectAudience [audSender, audLoom]
|
||||||
|
|
||||||
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
|
action = AP.Action
|
||||||
|
{ AP.actionCapability = Nothing
|
||||||
|
, AP.actionSummary = Nothing
|
||||||
|
, AP.actionAudience = AP.Audience recips [] [] [] [] []
|
||||||
|
, AP.actionFulfills = [uOffer]
|
||||||
|
, AP.actionSpecific = AP.AcceptActivity AP.Accept
|
||||||
|
{ AP.acceptObject = uOffer
|
||||||
|
, AP.acceptResult =
|
||||||
|
Just $ encodeRouteLocal $ ClothR loomHash clothHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (action, recipientSet, remoteActors, fwdHosts)
|
||||||
|
|
||||||
loomBehavior :: UTCTime -> LoomId -> VerseExt -> ActE (Text, Act (), Next)
|
loomBehavior :: UTCTime -> LoomId -> VerseExt -> ActE (Text, Act (), Next)
|
||||||
loomBehavior now loomID (Left _verse@(Verse _authorIdMsig body)) =
|
loomBehavior now loomID (Left verse@(Verse _authorIdMsig body)) =
|
||||||
case AP.activitySpecific $ actbActivity body of
|
case AP.activitySpecific $ actbActivity body of
|
||||||
|
AP.OfferActivity offer -> loomOffer now loomID verse offer
|
||||||
_ -> throwE "Unsupported activity type for Loom"
|
_ -> throwE "Unsupported activity type for Loom"
|
||||||
loomBehavior _ _ (Right _) = throwE "ClientMsgs aren't supported for Loom"
|
loomBehavior _ _ (Right _) = throwE "ClientMsgs aren't supported for Loom"
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,10 @@
|
||||||
|
|
||||||
module Vervis.Federation.Ticket
|
module Vervis.Federation.Ticket
|
||||||
( --personOfferTicketF
|
( --personOfferTicketF
|
||||||
loomOfferTicketF
|
|
||||||
|
|
||||||
--, repoAddBundleF
|
--, repoAddBundleF
|
||||||
|
|
||||||
, loomApplyF
|
loomApplyF
|
||||||
|
|
||||||
--, deckOfferDepF
|
--, deckOfferDepF
|
||||||
--, repoOfferDepF
|
--, repoOfferDepF
|
||||||
|
@ -328,336 +327,6 @@ activityAlreadyInInbox hAct luAct inboxID = fmap isJust . runMaybeT $ do
|
||||||
remoteActivityID <- MaybeT $ getKeyBy $ UniqueRemoteActivity remoteObjectID
|
remoteActivityID <- MaybeT $ getKeyBy $ UniqueRemoteActivity remoteObjectID
|
||||||
MaybeT $ getBy $ UniqueInboxItemRemote inboxID remoteActivityID
|
MaybeT $ getBy $ UniqueInboxItemRemote inboxID remoteActivityID
|
||||||
|
|
||||||
loomOfferTicketF
|
|
||||||
:: UTCTime
|
|
||||||
-> KeyHashid Loom
|
|
||||||
-> RemoteAuthor
|
|
||||||
-> ActivityBody
|
|
||||||
-> Maybe (RecipientRoutes, ByteString)
|
|
||||||
-> LocalURI
|
|
||||||
-> AP.Ticket URIMode
|
|
||||||
-> FedURI
|
|
||||||
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
|
|
||||||
loomOfferTicketF now recipLoomHash author body mfwd luOffer ticket uTarget = do
|
|
||||||
error "loomOfferTicketF disabled for refactoring"
|
|
||||||
{-
|
|
||||||
-- Check input
|
|
||||||
recipLoomID <- decodeKeyHashid404 recipLoomHash
|
|
||||||
(title, desc, source, originTipOrBundle, targetRepoID, maybeTargetBranch) <- do
|
|
||||||
let uAuthor@(ObjURI hAuthor _) = remoteAuthorURI author
|
|
||||||
WorkItemOffer {..} <- checkOfferTicket hAuthor ticket uTarget
|
|
||||||
unless (wioAuthor == Right (remoteAuthorURI author)) $
|
|
||||||
throwE "Offering a Ticket attributed to someone else"
|
|
||||||
Merge maybeOriginTip maybeBundle targetTip <- case wioRest of
|
|
||||||
TAM_Task _ ->
|
|
||||||
throwE
|
|
||||||
"Offer target is some local deck, so I have no use for \
|
|
||||||
\this Offer. Was I supposed to receive it?"
|
|
||||||
TAM_Merge loomID merge ->
|
|
||||||
if loomID == recipLoomID
|
|
||||||
then return merge
|
|
||||||
else throwE
|
|
||||||
"Offer target is some other local loom, so I have \
|
|
||||||
\no use for this Offer. Was I supposed to receive \
|
|
||||||
\it?"
|
|
||||||
TAM_Remote _ _ ->
|
|
||||||
throwE
|
|
||||||
"Offer target is some remote tracker, so I have no use \
|
|
||||||
\for this Offer. Was I supposed to receive it?"
|
|
||||||
originTipOrBundle <-
|
|
||||||
fromMaybeE
|
|
||||||
(align maybeOriginTip maybeBundle)
|
|
||||||
"MR provides neither origin nor patches"
|
|
||||||
(targetRepoID, maybeTargetBranch) <-
|
|
||||||
case targetTip of
|
|
||||||
TipLocalRepo repoID -> pure (repoID, Nothing)
|
|
||||||
TipLocalBranch repoID branch -> pure (repoID, Just branch)
|
|
||||||
_ -> throwE "MR target is a remote repo (this tracker serves only local repos)"
|
|
||||||
return (wioTitle, wioDesc, wioSource, originTipOrBundle, targetRepoID, maybeTargetBranch)
|
|
||||||
|
|
||||||
-- Soon we're going to proceed asynchronously to be able to HTTP GET the
|
|
||||||
-- origin repo AP object, because:
|
|
||||||
--
|
|
||||||
-- * No support for providing a signed repo object directly in the
|
|
||||||
-- Offer activity
|
|
||||||
-- * It may be nice to make sure a remote origin repo's VCS type
|
|
||||||
-- matches the target repo's VCS, even if patches are provided too
|
|
||||||
-- + However there's no support for caching VCS type when
|
|
||||||
-- remembering remote repo in our DB, so we'd have to check this
|
|
||||||
-- every time
|
|
||||||
-- * If origin is remote and no patches are provided, we'll need to
|
|
||||||
-- know the clone URL to generate the patches ourselves
|
|
||||||
-- + However the code here, for some simplicity, doesn't have a
|
|
||||||
-- way to skip that and do the whole handler synchronously in
|
|
||||||
-- case patches are provided or the origin is a local repo
|
|
||||||
-- + And no support for caching the clone URI in DB when
|
|
||||||
-- remembering the remote repo, so we'd need to do this every
|
|
||||||
-- time
|
|
||||||
--
|
|
||||||
-- So first let's do some checks using the DB, on the loom, on the target
|
|
||||||
-- repo (which is always local), and on the origin repo if it's local
|
|
||||||
(recipLoomRepoID, Entity recipLoomActorID recipLoomActor, alreadyInInbox) <- lift $ runDB $ do
|
|
||||||
|
|
||||||
-- Find recipient loom in DB, returning 404 if doesn't exist because
|
|
||||||
-- we're in the loom's inbox post handler
|
|
||||||
(recipLoomRepoID, recipLoomActor@(Entity _ actor)) <- do
|
|
||||||
loom <- get404 recipLoomID
|
|
||||||
let actorID = loomActor loom
|
|
||||||
(loomRepo loom,) . Entity actorID <$> getJust actorID
|
|
||||||
|
|
||||||
-- Has the loom already received this activity to its inbox? If yes, we
|
|
||||||
-- won't process it again
|
|
||||||
alreadyInInbox <- do
|
|
||||||
let hOffer = objUriAuthority $ remoteAuthorURI author
|
|
||||||
activityAlreadyInInbox hOffer luOffer (actorInbox actor)
|
|
||||||
|
|
||||||
return (recipLoomRepoID, recipLoomActor, alreadyInInbox)
|
|
||||||
|
|
||||||
if alreadyInInbox
|
|
||||||
then return ("I already have this activity in my inbox, ignoring", Nothing)
|
|
||||||
else do
|
|
||||||
(targetRepoVCS, originOrBundle) <- runDBExcept $ do
|
|
||||||
|
|
||||||
-- Grab loom's repo from DB and verify that it consents to be served by
|
|
||||||
-- the loom, otherwise this loom doesn't accept tickets
|
|
||||||
unless (targetRepoID == recipLoomRepoID) $
|
|
||||||
throwE "MR target repo isn't the one served by the Offer target loom"
|
|
||||||
targetRepo <- lift $ getJust targetRepoID
|
|
||||||
unless (repoLoom targetRepo == Just recipLoomID) $
|
|
||||||
throwE "Offer target loom doesn't have repo's consent to serve it"
|
|
||||||
|
|
||||||
-- Verify VCS type match between patch bundle and target repo
|
|
||||||
for_ (justThere originTipOrBundle) $ \ (Material typ diffs) -> do
|
|
||||||
unless (repoVcs targetRepo == patchMediaTypeVCS typ) $
|
|
||||||
throwE "Patch type and local target repo VCS mismatch"
|
|
||||||
case (typ, diffs) of
|
|
||||||
(PatchMediaTypeDarcs, _ :| _ : _) ->
|
|
||||||
throwE "More than one Darcs dpatch file provided"
|
|
||||||
_ -> pure ()
|
|
||||||
|
|
||||||
-- If origin repo is local, find it in our DB and verify its VCS type
|
|
||||||
-- matches the target repo
|
|
||||||
originOrBundle <- flip (bifor originTipOrBundle) pure $ \ originTip -> do
|
|
||||||
let origin =
|
|
||||||
case originTip of
|
|
||||||
TipLocalRepo repoID -> Left (repoID, Nothing)
|
|
||||||
TipLocalBranch repoID branch -> Left (repoID, Just branch)
|
|
||||||
TipRemote uOrigin -> Right (uOrigin, Nothing)
|
|
||||||
TipRemoteBranch uRepo branch -> Right (uRepo, Just branch)
|
|
||||||
bitraverse_
|
|
||||||
(\ (repoID, maybeBranch) -> do
|
|
||||||
repo <- getE repoID "MR origin local repo not found in DB"
|
|
||||||
unless (repoVcs repo == repoVcs targetRepo) $
|
|
||||||
throwE "Local origin repo VCS differs from target repo VCS"
|
|
||||||
)
|
|
||||||
pure
|
|
||||||
origin
|
|
||||||
return origin
|
|
||||||
|
|
||||||
return (repoVcs targetRepo, originOrBundle)
|
|
||||||
|
|
||||||
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
|
|
||||||
|
|
||||||
-- If origin repo is remote, HTTP GET its AP representation and
|
|
||||||
-- remember it in our DB
|
|
||||||
originOrBundle' <-
|
|
||||||
bitraverse
|
|
||||||
(bitraverse
|
|
||||||
pure
|
|
||||||
(\ (uOrigin, maybeOriginBranch) -> do
|
|
||||||
(vcs, remoteOrigin) <-
|
|
||||||
case maybeOriginBranch of
|
|
||||||
Nothing -> do
|
|
||||||
(vcs, raid, uClone, mb) <- withExceptT (T.pack . show) $ httpGetRemoteTip uOrigin
|
|
||||||
return (vcs, (raid, uClone, first Just <$> mb))
|
|
||||||
Just branch -> do
|
|
||||||
(vcs, raid, uClone) <- withExceptT (T.pack . show) $ httpGetRemoteRepo uOrigin
|
|
||||||
return (vcs, (raid, uClone, Just (Nothing, branch)))
|
|
||||||
unless (vcs == targetRepoVCS) $
|
|
||||||
throwE "Remote origin repo VCS differs from target repo VCS"
|
|
||||||
return remoteOrigin
|
|
||||||
)
|
|
||||||
)
|
|
||||||
pure
|
|
||||||
originOrBundle
|
|
||||||
|
|
||||||
-- Verify that branches are specified for Git and aren't specified for
|
|
||||||
-- Darcs
|
|
||||||
-- Also, produce a data structure separating by VCS rather than by
|
|
||||||
-- local/remote origin, which we'll need for generating patches
|
|
||||||
tipInfo <- case targetRepoVCS of
|
|
||||||
VCSGit -> do
|
|
||||||
targetBranch <- fromMaybeE maybeTargetBranch "Local target repo is Git but no target branch specified"
|
|
||||||
maybeOrigin <- for (justHere originOrBundle') $ \case
|
|
||||||
Left (originRepoID, maybeOriginBranch) -> do
|
|
||||||
originBranch <- fromMaybeE maybeOriginBranch "Local origin repo is Git but no origin branch specified"
|
|
||||||
return (Left originRepoID, originBranch)
|
|
||||||
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
|
|
||||||
(_maybeURI, originBranch) <- fromMaybeE maybeOriginBranch "Remote origin repo is Git but no origin branch specified"
|
|
||||||
return (Right uClone, originBranch)
|
|
||||||
return $ Left (targetBranch, maybeOrigin)
|
|
||||||
VCSDarcs -> do
|
|
||||||
verifyNothingE maybeTargetBranch "Local target repo is Darcs but target branch specified"
|
|
||||||
maybeOriginRepo <- for (justHere originOrBundle') $ \case
|
|
||||||
Left (originRepoID, maybeOriginBranch) -> do
|
|
||||||
verifyNothingE maybeOriginBranch "Local origin repo is Darcs but origin branch specified"
|
|
||||||
return $ Left originRepoID
|
|
||||||
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
|
|
||||||
verifyNothingE maybeOriginBranch "Remote origin repo is Darcs but origin branch specified"
|
|
||||||
return $ Right uClone
|
|
||||||
return $ Right $ maybeOriginRepo
|
|
||||||
|
|
||||||
maybeHttp <- runSiteDBExcept $ do
|
|
||||||
|
|
||||||
-- Insert the Offer to loom's inbox
|
|
||||||
mractid <- lift $ insertToInbox now author body (actorInbox recipLoomActor) luOffer False
|
|
||||||
for mractid $ \ offerID -> do
|
|
||||||
|
|
||||||
-- Forward the Offer activity to relevant local stages, and
|
|
||||||
-- schedule delivery for unavailable remote members of them
|
|
||||||
maybeHttpFwdOffer <- lift $ for mfwd $ \ (localRecips, sig) -> do
|
|
||||||
let sieve =
|
|
||||||
makeRecipientSet
|
|
||||||
[]
|
|
||||||
[LocalStageLoomFollowers recipLoomHash]
|
|
||||||
forwardActivityDB
|
|
||||||
(actbBL body) localRecips sig
|
|
||||||
recipLoomActorID (LocalActorLoom recipLoomHash)
|
|
||||||
sieve offerID
|
|
||||||
|
|
||||||
-- Insert the new ticket to our DB
|
|
||||||
acceptID <- lift $ insertEmptyOutboxItem (actorOutbox recipLoomActor) now
|
|
||||||
ticketID <- lift $ insertTicket now title desc source offerID acceptID
|
|
||||||
clothID <- lift $ insertMerge recipLoomID ticketID maybeTargetBranch originOrBundle'
|
|
||||||
let maybePull =
|
|
||||||
let maybeTipInfo =
|
|
||||||
case tipInfo of
|
|
||||||
Left (b, mo) -> Left . (b,) <$> mo
|
|
||||||
Right mo -> Right <$> mo
|
|
||||||
hasBundle = isJust $ justThere originOrBundle'
|
|
||||||
in (clothID, targetRepoID, hasBundle,) <$> maybeTipInfo
|
|
||||||
|
|
||||||
-- Prepare an Accept activity and insert to loom's outbox
|
|
||||||
(actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
|
|
||||||
lift $ prepareAccept clothID
|
|
||||||
_luAccept <- lift $ updateOutboxItem (LocalActorLoom recipLoomID) acceptID actionAccept
|
|
||||||
|
|
||||||
-- Deliver the Accept to local recipients, and schedule delivery
|
|
||||||
-- for unavailable remote recipients
|
|
||||||
deliverHttpAccept <-
|
|
||||||
deliverActivityDB
|
|
||||||
(LocalActorLoom recipLoomHash) recipLoomActorID
|
|
||||||
localRecipsAccept remoteRecipsAccept
|
|
||||||
fwdHostsAccept acceptID actionAccept
|
|
||||||
|
|
||||||
-- Return instructions for HTTP inbox-forwarding of the Offer
|
|
||||||
-- activity, and for HTTP delivery of the Accept activity to
|
|
||||||
-- remote recipients, and for generating patches from
|
|
||||||
-- the origin repo
|
|
||||||
return
|
|
||||||
(maybeHttpFwdOffer, deliverHttpAccept, maybePull)
|
|
||||||
|
|
||||||
-- Launch asynchronous HTTP forwarding of the Offer activity and HTTP
|
|
||||||
-- delivery of the Accept activity, and generate patches if we opened
|
|
||||||
-- a local MR that mentions just an origin
|
|
||||||
case maybeHttp of
|
|
||||||
Nothing ->
|
|
||||||
return
|
|
||||||
"When I started serving this activity, I didn't have it in my inbox, \
|
|
||||||
\but now suddenly it seems I already do, so ignoring"
|
|
||||||
Just (maybeHttpFwdOffer, deliverHttpAccept, maybePull) -> do
|
|
||||||
forkWorker "loomOfferTicketF Accept HTTP delivery" deliverHttpAccept
|
|
||||||
traverse generatePatches maybePull
|
|
||||||
case maybeHttpFwdOffer of
|
|
||||||
Nothing -> return "Opened a merge request, no inbox-forwarding to do"
|
|
||||||
Just forwardHttpOffer -> do
|
|
||||||
forkWorker "loomOfferTicketF inbox-forwarding" forwardHttpOffer
|
|
||||||
return "Opened a merge request and ran inbox-forwarding of the Offer"
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
insertTicket now title desc source offerID acceptID = do
|
|
||||||
did <- insert Discussion
|
|
||||||
fsid <- insert FollowerSet
|
|
||||||
tid <- insert Ticket
|
|
||||||
{ ticketNumber = Nothing
|
|
||||||
, ticketCreated = now
|
|
||||||
, ticketTitle = title
|
|
||||||
, ticketSource = source
|
|
||||||
, ticketDescription = desc
|
|
||||||
, ticketDiscuss = did
|
|
||||||
, ticketFollowers = fsid
|
|
||||||
, ticketAccept = acceptID
|
|
||||||
}
|
|
||||||
insert_ TicketAuthorRemote
|
|
||||||
{ ticketAuthorRemoteTicket = tid
|
|
||||||
, ticketAuthorRemoteAuthor = remoteAuthorId author
|
|
||||||
, ticketAuthorRemoteOpen = offerID
|
|
||||||
}
|
|
||||||
return tid
|
|
||||||
|
|
||||||
insertMerge
|
|
||||||
:: LoomId
|
|
||||||
-> TicketId
|
|
||||||
-> Maybe Text
|
|
||||||
-> These
|
|
||||||
(Either
|
|
||||||
(RepoId, Maybe Text)
|
|
||||||
(RemoteActorId, FedURI, Maybe (Maybe LocalURI, Text))
|
|
||||||
)
|
|
||||||
Material
|
|
||||||
-> WorkerDB TicketLoomId
|
|
||||||
insertMerge loomID ticketID maybeTargetBranch originOrBundle = do
|
|
||||||
clothID <- insert $ TicketLoom ticketID loomID maybeTargetBranch
|
|
||||||
for_ (justHere originOrBundle) $ \case
|
|
||||||
Left (repoID, maybeOriginBranch) ->
|
|
||||||
insert_ $ MergeOriginLocal clothID repoID maybeOriginBranch
|
|
||||||
Right (remoteActorID, _uClone, maybeOriginBranch) -> do
|
|
||||||
originID <- insert $ MergeOriginRemote clothID remoteActorID
|
|
||||||
for_ maybeOriginBranch $ \ (mlu, b) ->
|
|
||||||
insert_ $ MergeOriginRemoteBranch originID mlu b
|
|
||||||
for_ (justThere originOrBundle) $ \ (Material typ diffs) -> do
|
|
||||||
bundleID <- insert $ Bundle clothID False
|
|
||||||
insertMany_ $ NE.toList $ NE.reverse $
|
|
||||||
NE.map (Patch bundleID now typ) diffs
|
|
||||||
return clothID
|
|
||||||
|
|
||||||
prepareAccept clothID = do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
|
|
||||||
clothHash <- encodeKeyHashid clothID
|
|
||||||
|
|
||||||
ra <- getJust $ remoteAuthorId author
|
|
||||||
|
|
||||||
let ObjURI hAuthor luAuthor = remoteAuthorURI author
|
|
||||||
|
|
||||||
audSender =
|
|
||||||
AudRemote hAuthor
|
|
||||||
[luAuthor]
|
|
||||||
(maybeToList $ remoteActorFollowers ra)
|
|
||||||
audTracker = AudLocal [] [LocalStageLoomFollowers recipLoomHash]
|
|
||||||
|
|
||||||
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
|
|
||||||
collectAudience [audSender, audTracker]
|
|
||||||
|
|
||||||
recips = map encodeRouteHome audLocal ++ audRemote
|
|
||||||
action = AP.Action
|
|
||||||
{ AP.actionCapability = Nothing
|
|
||||||
, AP.actionSummary = Nothing
|
|
||||||
, AP.actionAudience = AP.Audience recips [] [] [] [] []
|
|
||||||
, AP.actionFulfills = []
|
|
||||||
, AP.actionSpecific = AP.AcceptActivity AP.Accept
|
|
||||||
{ AP.acceptObject = ObjURI hAuthor luOffer
|
|
||||||
, AP.acceptResult =
|
|
||||||
Just $ encodeRouteLocal $
|
|
||||||
ClothR recipLoomHash clothHash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (action, recipientSet, remoteActors, fwdHosts)
|
|
||||||
-}
|
|
||||||
|
|
||||||
repoOfferTicketF
|
repoOfferTicketF
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> KeyHashid Repo
|
-> KeyHashid Repo
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2022 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2022, 2023 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -16,7 +16,9 @@
|
||||||
module Vervis.Fetch
|
module Vervis.Fetch
|
||||||
( Result (..)
|
( Result (..)
|
||||||
, httpGetRemoteTip
|
, httpGetRemoteTip
|
||||||
|
, httpGetRemoteTip'
|
||||||
, httpGetRemoteRepo
|
, httpGetRemoteRepo
|
||||||
|
, httpGetRemoteRepo'
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -60,6 +62,7 @@ import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
import qualified Data.Text.Lazy as TL
|
import qualified Data.Text.Lazy as TL
|
||||||
|
|
||||||
|
import Control.Concurrent.Actor
|
||||||
import Database.Persist.JSON
|
import Database.Persist.JSON
|
||||||
import Development.PatchMediaType
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
|
@ -81,6 +84,7 @@ import qualified Data.Text.UTF8.Local as TU
|
||||||
import qualified Darcs.Local.Repository as D (createRepo)
|
import qualified Darcs.Local.Repository as D (createRepo)
|
||||||
|
|
||||||
--import Vervis.Access
|
--import Vervis.Access
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
import Vervis.Cloth
|
import Vervis.Cloth
|
||||||
import Vervis.Data.Actor
|
import Vervis.Data.Actor
|
||||||
|
@ -116,6 +120,13 @@ fetchRepoE h lu = do
|
||||||
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
||||||
fetchAPID' manager apRepoId h lu
|
fetchAPID' manager apRepoId h lu
|
||||||
|
|
||||||
|
fetchRepoE' :: Host -> LocalURI -> ExceptT Result Act (AP.Repo URIMode)
|
||||||
|
fetchRepoE' h lu = do
|
||||||
|
manager <- asksEnv envHttpManager
|
||||||
|
let apRepoId = AP.actorId . AP.actorLocal . AP.repoActor
|
||||||
|
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
||||||
|
fetchAPID' manager apRepoId h lu
|
||||||
|
|
||||||
insertRemoteActor
|
insertRemoteActor
|
||||||
:: MonadIO m
|
:: MonadIO m
|
||||||
=> Host
|
=> Host
|
||||||
|
@ -167,6 +178,36 @@ httpGetRemoteTip (ObjURI host localURI) = do
|
||||||
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
||||||
fetchTip manager h lu
|
fetchTip manager h lu
|
||||||
|
|
||||||
|
httpGetRemoteTip'
|
||||||
|
:: FedURI
|
||||||
|
-> ExceptT Result Act
|
||||||
|
( VersionControlSystem
|
||||||
|
, RemoteActorId
|
||||||
|
, FedURI
|
||||||
|
, Maybe (LocalURI, Text)
|
||||||
|
)
|
||||||
|
httpGetRemoteTip' (ObjURI host localURI) = do
|
||||||
|
repoOrBranch <- fetchTipE host localURI
|
||||||
|
case repoOrBranch of
|
||||||
|
Left repo -> do
|
||||||
|
remoteActorID <-
|
||||||
|
lift $ withDB $
|
||||||
|
insertRemoteActor host localURI $ AP.repoActor repo
|
||||||
|
let uClone = ObjURI host $ NE.head $ AP.repoClone repo
|
||||||
|
return (AP.repoVcs repo, remoteActorID, uClone, Nothing)
|
||||||
|
Right (AP.Branch name _ luRepo) -> do
|
||||||
|
repo <- fetchRepoE' host luRepo
|
||||||
|
remoteActorID <-
|
||||||
|
lift $ withDB $
|
||||||
|
insertRemoteActor host luRepo $ AP.repoActor repo
|
||||||
|
let uClone = ObjURI host $ NE.head $ AP.repoClone repo
|
||||||
|
return (AP.repoVcs repo, remoteActorID, uClone, Just (localURI, name))
|
||||||
|
where
|
||||||
|
fetchTipE h lu = do
|
||||||
|
manager <- asksEnv envHttpManager
|
||||||
|
ExceptT $ first (maybe ResultIdMismatch ResultGetError) <$>
|
||||||
|
fetchTip manager h lu
|
||||||
|
|
||||||
httpGetRemoteRepo
|
httpGetRemoteRepo
|
||||||
:: (MonadUnliftIO m, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadSite m, SiteEnv m ~ App)
|
||||||
=> FedURI
|
=> FedURI
|
||||||
|
@ -178,3 +219,14 @@ httpGetRemoteRepo (ObjURI host localURI) = do
|
||||||
insertRemoteActor host localURI $ AP.repoActor repo
|
insertRemoteActor host localURI $ AP.repoActor repo
|
||||||
let uClone = ObjURI host $ NE.head $ AP.repoClone repo
|
let uClone = ObjURI host $ NE.head $ AP.repoClone repo
|
||||||
return (AP.repoVcs repo, remoteActorID, uClone)
|
return (AP.repoVcs repo, remoteActorID, uClone)
|
||||||
|
|
||||||
|
httpGetRemoteRepo'
|
||||||
|
:: FedURI
|
||||||
|
-> ExceptT Result Act (VersionControlSystem, RemoteActorId, FedURI)
|
||||||
|
httpGetRemoteRepo' (ObjURI host localURI) = do
|
||||||
|
repo <- fetchRepoE' host localURI
|
||||||
|
remoteActorID <-
|
||||||
|
lift $ withDB $
|
||||||
|
insertRemoteActor host localURI $ AP.repoActor repo
|
||||||
|
let uClone = ObjURI host $ NE.head $ AP.repoClone repo
|
||||||
|
return (AP.repoVcs repo, remoteActorID, uClone)
|
||||||
|
|
|
@ -222,7 +222,6 @@ postPersonOutboxR personHash = do
|
||||||
-> t
|
-> t
|
||||||
run f = f eperson actorDB maybeCap localRecips remoteRecips fwdHosts action
|
run f = f eperson actorDB maybeCap localRecips remoteRecips fwdHosts action
|
||||||
case specific of
|
case specific of
|
||||||
AP.AcceptActivity accept -> run acceptC accept
|
|
||||||
AP.ApplyActivity apply -> run applyC apply
|
AP.ApplyActivity apply -> run applyC apply
|
||||||
AP.CreateActivity (AP.Create obj mtarget) ->
|
AP.CreateActivity (AP.Create obj mtarget) ->
|
||||||
case obj of
|
case obj of
|
||||||
|
@ -246,7 +245,10 @@ postPersonOutboxR personHash = do
|
||||||
AP.FollowActivity follow -> run followC follow
|
AP.FollowActivity follow -> run followC follow
|
||||||
AP.OfferActivity (AP.Offer obj target) ->
|
AP.OfferActivity (AP.Offer obj target) ->
|
||||||
case obj of
|
case obj of
|
||||||
AP.OfferTicket ticket -> run offerTicketC ticket target
|
AP.OfferTicket _ ->
|
||||||
|
handleViaActor
|
||||||
|
(entityKey eperson) maybeCap localRecips remoteRecips
|
||||||
|
fwdHosts action
|
||||||
{-
|
{-
|
||||||
OfferDep dep ->
|
OfferDep dep ->
|
||||||
offerDepC eperson sharer summary audience dep target
|
offerDepC eperson sharer summary audience dep target
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019, 2022 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022, 2023 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -17,6 +17,7 @@ module Vervis.Path
|
||||||
( askRepoRootDir
|
( askRepoRootDir
|
||||||
, repoDir
|
, repoDir
|
||||||
, askRepoDir
|
, askRepoDir
|
||||||
|
, askRepoDir'
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -26,9 +27,11 @@ import System.FilePath ((</>))
|
||||||
import qualified Data.CaseInsensitive as CI (foldedCase)
|
import qualified Data.CaseInsensitive as CI (foldedCase)
|
||||||
import qualified Data.Text as T (unpack)
|
import qualified Data.Text as T (unpack)
|
||||||
|
|
||||||
|
import Control.Concurrent.Actor
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
@ -36,6 +39,9 @@ import Vervis.Settings
|
||||||
askRepoRootDir :: (MonadSite m, SiteEnv m ~ App) => m FilePath
|
askRepoRootDir :: (MonadSite m, SiteEnv m ~ App) => m FilePath
|
||||||
askRepoRootDir = asksSite $ appRepoDir . appSettings
|
askRepoRootDir = asksSite $ appRepoDir . appSettings
|
||||||
|
|
||||||
|
askRepoRootDir' :: Act FilePath
|
||||||
|
askRepoRootDir' = asksEnv $ appRepoDir . envSettings
|
||||||
|
|
||||||
repoDir :: FilePath -> KeyHashid Repo -> FilePath
|
repoDir :: FilePath -> KeyHashid Repo -> FilePath
|
||||||
repoDir root repo = root </> (T.unpack $ keyHashidText repo)
|
repoDir root repo = root </> (T.unpack $ keyHashidText repo)
|
||||||
|
|
||||||
|
@ -44,3 +50,8 @@ askRepoDir
|
||||||
askRepoDir repo = do
|
askRepoDir repo = do
|
||||||
root <- askRepoRootDir
|
root <- askRepoRootDir
|
||||||
return $ repoDir root repo
|
return $ repoDir root repo
|
||||||
|
|
||||||
|
askRepoDir' :: KeyHashid Repo -> Act FilePath
|
||||||
|
askRepoDir' repo = do
|
||||||
|
root <- askRepoRootDir'
|
||||||
|
return $ repoDir root repo
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2019, 2020, 2021, 2022 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2019, 2020, 2021, 2022, 2023
|
||||||
|
- by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -45,11 +46,13 @@ import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import qualified Web.ActivityPub as AP
|
import qualified Web.ActivityPub as AP
|
||||||
|
import qualified Web.Actor.Persist as WAP
|
||||||
|
|
||||||
import Data.Patch.Local hiding (Patch)
|
import Data.Patch.Local hiding (Patch)
|
||||||
|
|
||||||
import qualified Data.Patch.Local as P
|
import qualified Data.Patch.Local as P
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.Darcs
|
import Vervis.Darcs
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
|
@ -110,36 +113,35 @@ serveCommit repoHash ref patch parents = do
|
||||||
Right $ encodeRouteHome $ PersonR $ hashPerson personID
|
Right $ encodeRouteHome $ PersonR $ hashPerson personID
|
||||||
|
|
||||||
generatePatches
|
generatePatches
|
||||||
:: (MonadUnliftIO m, MonadSite m, SiteEnv m ~ App)
|
:: ( TicketLoomId
|
||||||
=> ( TicketLoomId
|
|
||||||
, RepoId
|
, RepoId
|
||||||
, Bool
|
, Bool
|
||||||
, Either
|
, Either
|
||||||
(Text, (Either RepoId FedURI, Text))
|
(Text, (Either RepoId FedURI, Text))
|
||||||
(Either RepoId FedURI)
|
(Either RepoId FedURI)
|
||||||
)
|
)
|
||||||
-> ExceptT Text m ()
|
-> ActE ()
|
||||||
generatePatches (clothID, targetRepoID, hasBundle, tipInfo) = unless hasBundle $ do
|
generatePatches (clothID, targetRepoID, hasBundle, tipInfo) = unless hasBundle $ do
|
||||||
patches <-
|
patches <-
|
||||||
case tipInfo of
|
case tipInfo of
|
||||||
Right _ -> error "Auto-pulling from Darcs remote origin not supported yet"
|
Right _ -> error "Auto-pulling from Darcs remote origin not supported yet"
|
||||||
Left (targetBranch, (originRepo, originBranch)) -> do
|
Left (targetBranch, (originRepo, originBranch)) -> do
|
||||||
targetPath <- do
|
targetPath <- do
|
||||||
repoHash <- encodeKeyHashid targetRepoID
|
repoHash <- WAP.encodeKeyHashid targetRepoID
|
||||||
repoDir <- askRepoDir repoHash
|
repoDir <- lift $ askRepoDir' repoHash
|
||||||
liftIO $ makeAbsolute repoDir
|
liftIO $ makeAbsolute repoDir
|
||||||
originURI <-
|
originURI <-
|
||||||
case originRepo of
|
case originRepo of
|
||||||
Left repoID -> do
|
Left repoID -> do
|
||||||
repoHash <- encodeKeyHashid repoID
|
repoHash <- WAP.encodeKeyHashid repoID
|
||||||
repoDir <- askRepoDir repoHash
|
repoDir <- lift $ askRepoDir' repoHash
|
||||||
liftIO $ makeAbsolute repoDir
|
liftIO $ makeAbsolute repoDir
|
||||||
Right uClone -> pure $ T.unpack $ renderObjURI uClone
|
Right uClone -> pure $ T.unpack $ renderObjURI uClone
|
||||||
ExceptT $ liftIO $ runExceptT $
|
ExceptT $ liftIO $ runExceptT $
|
||||||
withSystemTempDirectory "vervis-generatePatches" $
|
withSystemTempDirectory "vervis-generatePatches" $
|
||||||
generateGitPatches targetPath (T.unpack targetBranch) originURI (T.unpack originBranch)
|
generateGitPatches targetPath (T.unpack targetBranch) originURI (T.unpack originBranch)
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
lift $ runSiteDB $ do
|
lift $ withDB $ do
|
||||||
bundleID <- insert $ Bundle clothID True
|
bundleID <- insert $ Bundle clothID True
|
||||||
insertMany_ $ NE.toList $ NE.map (Patch bundleID now PatchMediaTypeGit) $ NE.reverse patches
|
insertMany_ $ NE.toList $ NE.map (Patch bundleID now PatchMediaTypeGit) $ NE.reverse patches
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue