Add a new actor type: Factory

- No factories inserted to DB yet
- Admin user list specified in settings
- Admins can create a Factory via C2S
- Creating other actors via C2S the old way still works as well
- Factory S2S handler implementation still blank
This commit is contained in:
Pere Lev 2024-08-02 18:31:06 +03:00
parent a74b24f61a
commit 66870458b7
No known key found for this signature in database
GPG key ID: 5252C5C863E5E57D
34 changed files with 1213 additions and 143 deletions

View file

@ -107,6 +107,9 @@ max-accounts: 3
# development, and to verify otherwise. # development, and to verify otherwise.
#email-verification: true #email-verification: true
# Person usernames who are allowed to create Factory actors
can-create-factories: []
############################################################################### ###############################################################################
# Mail # Mail
############################################################################### ###############################################################################

View file

@ -0,0 +1,6 @@
Factory
resource ResourceId
create OutboxItemId
UniqueFactory resource
UniqueFactoryCreate create

View file

@ -1742,6 +1742,7 @@ actorOutboxItem (LocalActorRepo r) = RepoOutboxItemR r
actorOutboxItem (LocalActorDeck d) = DeckOutboxItemR d actorOutboxItem (LocalActorDeck d) = DeckOutboxItemR d
actorOutboxItem (LocalActorLoom l) = LoomOutboxItemR l actorOutboxItem (LocalActorLoom l) = LoomOutboxItemR l
actorOutboxItem (LocalActorProject l) = ProjectOutboxItemR l actorOutboxItem (LocalActorProject l) = ProjectOutboxItemR l
actorOutboxItem (LocalActorFactory l) = FactoryOutboxItemR l
offerDepC offerDepC
:: Entity Person :: Entity Person

View file

@ -67,6 +67,7 @@ module Vervis.Actor
, DeckRoutes (..) , DeckRoutes (..)
, LoomRoutes (..) , LoomRoutes (..)
, ProjectRoutes (..) , ProjectRoutes (..)
, FactoryRoutes (..)
, DeckFamilyRoutes (..) , DeckFamilyRoutes (..)
, LoomFamilyRoutes (..) , LoomFamilyRoutes (..)
, RecipientRoutes (..) , RecipientRoutes (..)
@ -169,6 +170,7 @@ data LocalActorBy f
| LocalActorDeck (f Deck) | LocalActorDeck (f Deck)
| LocalActorLoom (f Loom) | LocalActorLoom (f Loom)
| LocalActorProject (f Project) | LocalActorProject (f Project)
| LocalActorFactory (f Factory)
deriving (Generic, FunctorB, ConstraintsB) deriving (Generic, FunctorB, ConstraintsB)
deriving instance AllBF Eq f LocalActorBy => Eq (LocalActorBy f) deriving instance AllBF Eq f LocalActorBy => Eq (LocalActorBy f)
@ -182,6 +184,7 @@ data LocalResourceBy f
| LocalResourceDeck (f Deck) | LocalResourceDeck (f Deck)
| LocalResourceLoom (f Loom) | LocalResourceLoom (f Loom)
| LocalResourceProject (f Project) | LocalResourceProject (f Project)
| LocalResourceFactory (f Factory)
deriving (Generic, FunctorB, ConstraintsB) deriving (Generic, FunctorB, ConstraintsB)
deriving instance AllBF Eq f LocalResourceBy => Eq (LocalResourceBy f) deriving instance AllBF Eq f LocalResourceBy => Eq (LocalResourceBy f)
@ -191,6 +194,7 @@ data LocalResourceNonGroupBy f
| LocalResourceDeck' (f Deck) | LocalResourceDeck' (f Deck)
| LocalResourceLoom' (f Loom) | LocalResourceLoom' (f Loom)
| LocalResourceProject' (f Project) | LocalResourceProject' (f Project)
| LocalResourceFactory' (f Factory)
deriving (Generic, FunctorB, ConstraintsB) deriving (Generic, FunctorB, ConstraintsB)
deriving instance AllBF Eq f LocalResourceNonGroupBy => Eq (LocalResourceNonGroupBy f) deriving instance AllBF Eq f LocalResourceNonGroupBy => Eq (LocalResourceNonGroupBy f)
@ -204,6 +208,7 @@ actorToResource = \case
LocalActorDeck d -> Just $ LocalResourceDeck d LocalActorDeck d -> Just $ LocalResourceDeck d
LocalActorLoom l -> Just $ LocalResourceLoom l LocalActorLoom l -> Just $ LocalResourceLoom l
LocalActorProject j -> Just $ LocalResourceProject j LocalActorProject j -> Just $ LocalResourceProject j
LocalActorFactory f -> Just $ LocalResourceFactory f
resourceToActor = \case resourceToActor = \case
LocalResourceGroup g -> LocalActorGroup g LocalResourceGroup g -> LocalActorGroup g
@ -211,6 +216,7 @@ resourceToActor = \case
LocalResourceDeck d -> LocalActorDeck d LocalResourceDeck d -> LocalActorDeck d
LocalResourceLoom l -> LocalActorLoom l LocalResourceLoom l -> LocalActorLoom l
LocalResourceProject j -> LocalActorProject j LocalResourceProject j -> LocalActorProject j
LocalResourceFactory f -> LocalActorFactory f
resourceToNG = \case resourceToNG = \case
LocalResourceGroup _ -> Nothing LocalResourceGroup _ -> Nothing
@ -218,12 +224,14 @@ resourceToNG = \case
LocalResourceDeck d -> Just $ LocalResourceDeck' d LocalResourceDeck d -> Just $ LocalResourceDeck' d
LocalResourceLoom l -> Just $ LocalResourceLoom' l LocalResourceLoom l -> Just $ LocalResourceLoom' l
LocalResourceProject j -> Just $ LocalResourceProject' j LocalResourceProject j -> Just $ LocalResourceProject' j
LocalResourceFactory f -> Just $ LocalResourceFactory' f
resourceFromNG = \case resourceFromNG = \case
LocalResourceRepo' r -> LocalResourceRepo r LocalResourceRepo' r -> LocalResourceRepo r
LocalResourceDeck' d -> LocalResourceDeck d LocalResourceDeck' d -> LocalResourceDeck d
LocalResourceLoom' l -> LocalResourceLoom l LocalResourceLoom' l -> LocalResourceLoom l
LocalResourceProject' j -> LocalResourceProject j LocalResourceProject' j -> LocalResourceProject j
LocalResourceFactory' f -> LocalResourceFactory f
hashLocalActorPure hashLocalActorPure
:: HashidsContext -> LocalActorBy Key -> LocalActorBy KeyHashid :: HashidsContext -> LocalActorBy Key -> LocalActorBy KeyHashid
@ -235,6 +243,7 @@ hashLocalActorPure ctx = f
f (LocalActorDeck d) = LocalActorDeck $ encodeKeyHashidPure ctx d f (LocalActorDeck d) = LocalActorDeck $ encodeKeyHashidPure ctx d
f (LocalActorLoom l) = LocalActorLoom $ encodeKeyHashidPure ctx l f (LocalActorLoom l) = LocalActorLoom $ encodeKeyHashidPure ctx l
f (LocalActorProject j) = LocalActorProject $ encodeKeyHashidPure ctx j f (LocalActorProject j) = LocalActorProject $ encodeKeyHashidPure ctx j
f (LocalActorFactory f) = LocalActorFactory $ encodeKeyHashidPure ctx f
getHashLocalActor getHashLocalActor
:: (MonadActor m, StageHashids (MonadActorStage m)) :: (MonadActor m, StageHashids (MonadActorStage m))
@ -260,6 +269,7 @@ unhashLocalActorPure ctx = f
f (LocalActorDeck d) = LocalActorDeck <$> decodeKeyHashidPure ctx d f (LocalActorDeck d) = LocalActorDeck <$> decodeKeyHashidPure ctx d
f (LocalActorLoom l) = LocalActorLoom <$> decodeKeyHashidPure ctx l f (LocalActorLoom l) = LocalActorLoom <$> decodeKeyHashidPure ctx l
f (LocalActorProject j) = LocalActorProject <$> decodeKeyHashidPure ctx j f (LocalActorProject j) = LocalActorProject <$> decodeKeyHashidPure ctx j
f (LocalActorFactory f) = LocalActorFactory <$> decodeKeyHashidPure ctx f
unhashLocalActor unhashLocalActor
:: (MonadActor m, StageHashids (MonadActorStage m)) :: (MonadActor m, StageHashids (MonadActorStage m))
@ -307,6 +317,7 @@ hashLocalResourcePure ctx = f
f (LocalResourceDeck d) = LocalResourceDeck $ encodeKeyHashidPure ctx d f (LocalResourceDeck d) = LocalResourceDeck $ encodeKeyHashidPure ctx d
f (LocalResourceLoom l) = LocalResourceLoom $ encodeKeyHashidPure ctx l f (LocalResourceLoom l) = LocalResourceLoom $ encodeKeyHashidPure ctx l
f (LocalResourceProject j) = LocalResourceProject $ encodeKeyHashidPure ctx j f (LocalResourceProject j) = LocalResourceProject $ encodeKeyHashidPure ctx j
f (LocalResourceFactory f) = LocalResourceFactory $ encodeKeyHashidPure ctx f
getHashLocalResource getHashLocalResource
:: (MonadActor m, StageHashids (MonadActorStage m)) :: (MonadActor m, StageHashids (MonadActorStage m))
@ -331,6 +342,7 @@ unhashLocalResourcePure ctx = f
f (LocalResourceDeck d) = LocalResourceDeck <$> decodeKeyHashidPure ctx d f (LocalResourceDeck d) = LocalResourceDeck <$> decodeKeyHashidPure ctx d
f (LocalResourceLoom l) = LocalResourceLoom <$> decodeKeyHashidPure ctx l f (LocalResourceLoom l) = LocalResourceLoom <$> decodeKeyHashidPure ctx l
f (LocalResourceProject j) = LocalResourceProject <$> decodeKeyHashidPure ctx j f (LocalResourceProject j) = LocalResourceProject <$> decodeKeyHashidPure ctx j
f (LocalResourceFactory f) = LocalResourceFactory <$> decodeKeyHashidPure ctx f
unhashLocalResource unhashLocalResource
:: (MonadActor m, StageHashids (MonadActorStage m)) :: (MonadActor m, StageHashids (MonadActorStage m))
@ -415,6 +427,12 @@ data ProjectRoutes = ProjectRoutes
} }
deriving Eq deriving Eq
data FactoryRoutes = FactoryRoutes
{ routeFactory :: Bool
, routeFactoryFollowers :: Bool
}
deriving Eq
data DeckFamilyRoutes = DeckFamilyRoutes data DeckFamilyRoutes = DeckFamilyRoutes
{ familyDeck :: DeckRoutes { familyDeck :: DeckRoutes
, familyTickets :: [(KeyHashid TicketDeck, TicketRoutes)] , familyTickets :: [(KeyHashid TicketDeck, TicketRoutes)]
@ -434,6 +452,7 @@ data RecipientRoutes = RecipientRoutes
, recipDecks :: [(KeyHashid Deck , DeckFamilyRoutes)] , recipDecks :: [(KeyHashid Deck , DeckFamilyRoutes)]
, recipLooms :: [(KeyHashid Loom , LoomFamilyRoutes)] , recipLooms :: [(KeyHashid Loom , LoomFamilyRoutes)]
, recipProjects :: [(KeyHashid Project, ProjectRoutes)] , recipProjects :: [(KeyHashid Project, ProjectRoutes)]
, recipFactories :: [(KeyHashid Factory, FactoryRoutes)]
} }
deriving Eq deriving Eq
@ -513,6 +532,11 @@ instance Actor Group where
type ActorKey Group = GroupId type ActorKey Group = GroupId
type ActorReturn Group = Either Text Text type ActorReturn Group = Either Text Text
data ActorMessage Group = MsgG Verse data ActorMessage Group = MsgG Verse
instance Actor Factory where
type ActorStage Factory = Staje
type ActorKey Factory = FactoryId
type ActorReturn Factory = Either Text Text
data ActorMessage Factory = MsgF Verse
instance VervisActor Person where instance VervisActor Person where
actorVerse = MsgP . Left actorVerse = MsgP . Left
@ -538,6 +562,9 @@ instance VervisActor Repo where
case e of case e of
Left v -> Just v Left v -> Just v
Right _ -> Nothing Right _ -> Nothing
instance VervisActor Factory where
actorVerse = MsgF
toVerse (MsgF v) = Just v
instance Stage Staje where instance Stage Staje where
data StageEnv Staje = forall y. (Typeable y, Yesod y) => Env data StageEnv Staje = forall y. (Typeable y, Yesod y) => Env
@ -564,7 +591,7 @@ instance Stage Staje where
, envFetch :: ActorFetchShare , envFetch :: ActorFetchShare
} }
deriving Typeable deriving Typeable
type StageActors Staje = [Person, Project, Group, Deck, Loom, Repo] type StageActors Staje = [Person, Project, Group, Deck, Loom, Repo, Factory]
instance Message (ActorMessage Person) where instance Message (ActorMessage Person) where
summarize (MsgP (Left verse)) = summarizeVerse verse summarize (MsgP (Left verse)) = summarizeVerse verse
@ -588,6 +615,9 @@ instance Message (ActorMessage Project) where
instance Message (ActorMessage Group) where instance Message (ActorMessage Group) where
summarize (MsgG verse) = summarizeVerse verse summarize (MsgG verse) = summarizeVerse verse
refer (MsgG verse) = referVerse verse refer (MsgG verse) = referVerse verse
instance Message (ActorMessage Factory) where
summarize (MsgF verse) = summarizeVerse verse
refer (MsgF verse) = referVerse verse
type YesodRender y = Route y -> [(Text, Text)] -> Text type YesodRender y = Route y -> [(Text, Text)] -> Text
@ -670,7 +700,8 @@ launchActorIO
TVar (HashMap GroupId (ActorRef Group)), TVar (HashMap GroupId (ActorRef Group)),
TVar (HashMap DeckId (ActorRef Deck)), TVar (HashMap DeckId (ActorRef Deck)),
TVar (HashMap LoomId (ActorRef Loom)), TVar (HashMap LoomId (ActorRef Loom)),
TVar (HashMap RepoId (ActorRef Repo))] TVar (HashMap RepoId (ActorRef Repo)),
TVar (HashMap FactoryId (ActorRef Factory))]
l'1 l'1
, H.HOccurs' , H.HOccurs'
(TVar (HashMap (ActorKey a) (ActorRef a))) (TVar (HashMap (ActorKey a) (ActorRef a)))
@ -680,7 +711,8 @@ launchActorIO
TVar (HashMap GroupId (ActorRef Group)), TVar (HashMap GroupId (ActorRef Group)),
TVar (HashMap DeckId (ActorRef Deck)), TVar (HashMap DeckId (ActorRef Deck)),
TVar (HashMap LoomId (ActorRef Loom)), TVar (HashMap LoomId (ActorRef Loom)),
TVar (HashMap RepoId (ActorRef Repo))] TVar (HashMap RepoId (ActorRef Repo)),
TVar (HashMap FactoryId (ActorRef Factory))]
) )
=> Theater => Theater
-> StageEnv Staje -> StageEnv Staje
@ -705,7 +737,8 @@ launchActor
TVar (HashMap GroupId (ActorRef Group)), TVar (HashMap GroupId (ActorRef Group)),
TVar (HashMap DeckId (ActorRef Deck)), TVar (HashMap DeckId (ActorRef Deck)),
TVar (HashMap LoomId (ActorRef Loom)), TVar (HashMap LoomId (ActorRef Loom)),
TVar (HashMap RepoId (ActorRef Repo))] TVar (HashMap RepoId (ActorRef Repo)),
TVar (HashMap FactoryId (ActorRef Factory))]
l'0 l'0
, H.HOccurs' , H.HOccurs'
(TVar (HashMap (ActorKey a) (ActorRef a))) (TVar (HashMap (ActorKey a) (ActorRef a)))
@ -715,7 +748,8 @@ launchActor
TVar (HashMap GroupId (ActorRef Group)), TVar (HashMap GroupId (ActorRef Group)),
TVar (HashMap DeckId (ActorRef Deck)), TVar (HashMap DeckId (ActorRef Deck)),
TVar (HashMap LoomId (ActorRef Loom)), TVar (HashMap LoomId (ActorRef Loom)),
TVar (HashMap RepoId (ActorRef Repo))] TVar (HashMap RepoId (ActorRef Repo)),
TVar (HashMap FactoryId (ActorRef Factory))]
) )
=> ActorKey a => ActorKey a
-> Act Bool -> Act Bool
@ -771,6 +805,7 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
for looms $ \ (loomID, (LoomFamilyRoutes loom cloths)) -> for looms $ \ (loomID, (LoomFamilyRoutes loom cloths)) ->
(loomID,) . (loom,) <$> unhashKeys cloths (loomID,) . (loom,) <$> unhashKeys cloths
projects <- unhashKeys $ recipProjects recips projects <- unhashKeys $ recipProjects recips
factories <- unhashKeys $ recipFactories recips
-- Grab local actor sets whose stages are allowed for delivery -- Grab local actor sets whose stages are allowed for delivery
let allowStages' let allowStages'
@ -793,6 +828,8 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
filter (allowStages' fst routeLoom LocalActorLoom) loomsAndCloths filter (allowStages' fst routeLoom LocalActorLoom) loomsAndCloths
projectsForStages = projectsForStages =
filter (allowStages' id routeProject LocalActorProject) projects filter (allowStages' id routeProject LocalActorProject) projects
factoriesForStages =
filter (allowStages' id routeFactory LocalActorFactory) factories
-- Grab local actors being addressed -- Grab local actors being addressed
let localActorsForSelf = concat let localActorsForSelf = concat
@ -802,6 +839,7 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
, [ LocalActorDeck key | (key, (routes, _)) <- decksAndTickets, routeDeck routes ] , [ LocalActorDeck key | (key, (routes, _)) <- decksAndTickets, routeDeck routes ]
, [ LocalActorLoom key | (key, (routes, _)) <- loomsAndCloths, routeLoom routes ] , [ LocalActorLoom key | (key, (routes, _)) <- loomsAndCloths, routeLoom routes ]
, [ LocalActorProject key | (key, routes) <- projects, routeProject routes ] , [ LocalActorProject key | (key, routes) <- projects, routeProject routes ]
, [ LocalActorFactory key | (key, routes) <- factories, routeFactory routes ]
] ]
-- Grab local actors whose followers are going to be delivered to -- Grab local actors whose followers are going to be delivered to
@ -817,6 +855,8 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
[ key | (key, (routes, _)) <- loomsAndClothsForStages, routeLoomFollowers routes ] [ key | (key, (routes, _)) <- loomsAndClothsForStages, routeLoomFollowers routes ]
projectIDsForFollowers = projectIDsForFollowers =
[ key | (key, routes) <- projectsForStages, routeProjectFollowers routes ] [ key | (key, routes) <- projectsForStages, routeProjectFollowers routes ]
factoryIDsForFollowers =
[ key | (key, routes) <- factoriesForStages, routeFactoryFollowers routes ]
-- Grab tickets and cloths whose followers are going to be delivered to -- Grab tickets and cloths whose followers are going to be delivered to
let ticketSetsForFollowers = let ticketSetsForFollowers =
@ -848,6 +888,7 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
, selectActorIDs deckActor deckIDsForFollowers , selectActorIDs deckActor deckIDsForFollowers
, selectActorIDs loomActor loomIDsForFollowers , selectActorIDs loomActor loomIDsForFollowers
, selectActorIDs projectActor projectIDsForFollowers , selectActorIDs projectActor projectIDsForFollowers
, selectActorIDs' factoryResource factoryIDsForFollowers
] ]
ticketIDs <- ticketIDs <-
concat <$> concat <$>
@ -875,6 +916,7 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
, selectFollowers LocalActorDeck DeckActor followerSetIDs , selectFollowers LocalActorDeck DeckActor followerSetIDs
, selectFollowers LocalActorLoom LoomActor followerSetIDs , selectFollowers LocalActorLoom LoomActor followerSetIDs
, selectFollowers LocalActorProject ProjectActor followerSetIDs , selectFollowers LocalActorProject ProjectActor followerSetIDs
, selectFollowers' LocalActorFactory FactoryResource followerSetIDs
] ]
remotes <- getRemoteFollowers followerSetIDs remotes <- getRemoteFollowers followerSetIDs
return (locals, remotes) return (locals, remotes)
@ -889,7 +931,7 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
Just a -> HS.delete a s Just a -> HS.delete a s
authorAndId' = authorAndId' =
second (\ (author, luAct) -> (author, luAct, Nothing)) authorAndId second (\ (author, luAct) -> (author, luAct, Nothing)) authorAndId
(liveRecipsP, liveRecipsJ, liveRecipsG, liveRecipsD, liveRecipsL, liveRecipsR) = (liveRecipsP, liveRecipsJ, liveRecipsG, liveRecipsD, liveRecipsL, liveRecipsR, liveRecipsF) =
partitionByActor liveRecips partitionByActor liveRecips
verse = Verse authorAndId' body verse = Verse authorAndId' body
sendMany $ sendMany $
@ -898,7 +940,8 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
(liveRecipsG, actorVerse verse) `H.HCons` (liveRecipsG, actorVerse verse) `H.HCons`
(liveRecipsD, actorVerse verse) `H.HCons` (liveRecipsD, actorVerse verse) `H.HCons`
(liveRecipsL, actorVerse verse) `H.HCons` (liveRecipsL, actorVerse verse) `H.HCons`
(liveRecipsR, actorVerse verse) `H.HCons` H.HNil (liveRecipsR, actorVerse verse) `H.HCons`
(liveRecipsF, actorVerse verse) `H.HCons` H.HNil
-- Return remote followers, to whom we need to deliver via HTTP -- Return remote followers, to whom we need to deliver via HTTP
return remoteFollowers return remoteFollowers
@ -940,6 +983,15 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
selectActorIDs grabActor ids = selectActorIDs grabActor ids =
map (grabActor . entityVal) <$> selectList [persistIdField <-. ids] [] map (grabActor . entityVal) <$> selectList [persistIdField <-. ids] []
selectActorIDs'
:: (MonadIO m, PersistRecordBackend record SqlBackend)
=> (record -> ResourceId)
-> [Key record]
-> ReaderT SqlBackend m [ActorId]
selectActorIDs' grabResource ids = do
resourceIDs <- map (grabResource . entityVal) <$> selectList [persistIdField <-. ids] []
map (resourceActor . entityVal) <$> selectList [ResourceId <-. resourceIDs] []
selectTicketIDs selectTicketIDs
:: ( MonadIO m :: ( MonadIO m
, PersistRecordBackend tracker SqlBackend , PersistRecordBackend tracker SqlBackend
@ -990,6 +1042,14 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
E.where_ $ f E.^. FollowTarget `E.in_` E.valList followerSetIDs E.where_ $ f E.^. FollowTarget `E.in_` E.valList followerSetIDs
return $ p E.^. persistIdField return $ p E.^. persistIdField
selectFollowers' makeLocalActor resourceField followerSetIDs =
fmap (map (makeLocalActor . E.unValue)) $
E.select $ E.from $ \ (f `E.InnerJoin` r `E.InnerJoin` p) -> do
E.on $ r E.^. ResourceId E.==. p E.^. resourceField
E.on $ f E.^. FollowActor E.==. r E.^. ResourceActor
E.where_ $ f E.^. FollowTarget `E.in_` E.valList followerSetIDs
return $ p E.^. persistIdField
partitionByActor partitionByActor
:: HashSet (LocalActorBy Key) :: HashSet (LocalActorBy Key)
-> ( HashSet PersonId -> ( HashSet PersonId
@ -998,21 +1058,24 @@ sendToLocalActors authorAndId body requireOwner mauthor maidAuthor recips = do
, HashSet DeckId , HashSet DeckId
, HashSet LoomId , HashSet LoomId
, HashSet RepoId , HashSet RepoId
, HashSet FactoryId
) )
partitionByActor = foldl' f (HS.empty, HS.empty, HS.empty, HS.empty, HS.empty, HS.empty) partitionByActor = foldl' f (HS.empty, HS.empty, HS.empty, HS.empty, HS.empty, HS.empty, HS.empty)
where where
f (p, j, g, d, l, r) (LocalActorPerson k) = f (p, j, g, d, l, r, f') (LocalActorPerson k) =
(HS.insert k p, j, g, d, l, r) (HS.insert k p, j, g, d, l, r, f')
f (p, j, g, d, l, r) (LocalActorProject k) = f (p, j, g, d, l, r, f') (LocalActorProject k) =
(p, HS.insert k j, g, d, l, r) (p, HS.insert k j, g, d, l, r, f')
f (p, j, g, d, l, r) (LocalActorGroup k) = f (p, j, g, d, l, r, f') (LocalActorGroup k) =
(p, j, HS.insert k g, d, l, r) (p, j, HS.insert k g, d, l, r, f')
f (p, j, g, d, l, r) (LocalActorDeck k) = f (p, j, g, d, l, r, f') (LocalActorDeck k) =
(p, j, g, HS.insert k d, l, r) (p, j, g, HS.insert k d, l, r, f')
f (p, j, g, d, l, r) (LocalActorLoom k) = f (p, j, g, d, l, r, f') (LocalActorLoom k) =
(p, j, g, d, HS.insert k l, r) (p, j, g, d, HS.insert k l, r, f')
f (p, j, g, d, l, r) (LocalActorRepo k) = f (p, j, g, d, l, r, f') (LocalActorRepo k) =
(p, j, g, d, l, HS.insert k r) (p, j, g, d, l, HS.insert k r, f')
f (p, j, g, d, l, r, f') (LocalActorFactory k) =
(p, j, g, d, l, r, HS.insert k f')
actorIsAddressed :: RecipientRoutes -> LocalActor -> Bool actorIsAddressed :: RecipientRoutes -> LocalActor -> Bool
actorIsAddressed recips = isJust . verify actorIsAddressed recips = isJust . verify
@ -1035,6 +1098,9 @@ actorIsAddressed recips = isJust . verify
verify (LocalActorProject j) = do verify (LocalActorProject j) = do
routes <- lookup j $ recipProjects recips routes <- lookup j $ recipProjects recips
guard $ routeProject routes guard $ routeProject routes
verify (LocalActorFactory f) = do
routes <- lookup f $ recipFactories recips
guard $ routeFactory routes
localActorType :: LocalActorBy f -> AP.ActorType localActorType :: LocalActorBy f -> AP.ActorType
localActorType = \case localActorType = \case
@ -1044,3 +1110,4 @@ localActorType = \case
LocalActorLoom _ -> AP.ActorTypePatchTracker LocalActorLoom _ -> AP.ActorTypePatchTracker
LocalActorProject _ -> AP.ActorTypeProject LocalActorProject _ -> AP.ActorTypeProject
LocalActorGroup _ -> AP.ActorTypeTeam LocalActorGroup _ -> AP.ActorTypeTeam
LocalActorFactory _ -> AP.ActorTypeFactory

View file

@ -0,0 +1,65 @@
{- This file is part of Vervis.
-
- Written in 2024 by fr33domlover <fr33domlover@riseup.net>.
-
- Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
module Vervis.Actor.Factory
(
)
where
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Data.ByteString (ByteString)
import Data.Foldable
import Data.Text (Text)
import Data.Time.Clock
import Database.Persist
import Yesod.Persist.Core
import qualified Data.Text as T
import Control.Concurrent.Actor
import Network.FedURI
import Yesod.MonadSite
import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Database.Persist.Local
import Vervis.Actor
import Vervis.Data.Discussion
import Vervis.FedURI
import Vervis.Foundation
import Vervis.Model
import Vervis.Persist.Actor
import Vervis.Persist.Discussion
factoryBehavior :: UTCTime -> FactoryId -> ActorMessage Factory -> ActE (Text, Act (), Next)
factoryBehavior now factoryID (MsgF _verse@(Verse _authorIdMsig body)) =
case AP.activitySpecific $ actbActivity body of
_ -> throwE "Unsupported activity type for Factory"
instance VervisActorLaunch Factory where
actorBehavior' now factoryID ve = do
errboxID <- lift $ withDB $ do
resourceID <- factoryResource <$> getJust factoryID
Resource actorID <- getJust resourceID
actorErrbox <$> getJust actorID
adaptErrbox errboxID False factoryBehavior now factoryID ve

View file

@ -62,6 +62,7 @@ import Vervis.ActivityPub
import Vervis.Actor import Vervis.Actor
import Vervis.Actor2 import Vervis.Actor2
import Vervis.Actor.Deck import Vervis.Actor.Deck
import Vervis.Actor.Factory
import Vervis.Actor.Group import Vervis.Actor.Group
import Vervis.Actor.Project import Vervis.Actor.Project
import Vervis.Cloth import Vervis.Cloth
@ -74,12 +75,14 @@ import Vervis.FedURI
import Vervis.Fetch import Vervis.Fetch
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Model.Ident
import Vervis.Persist.Actor import Vervis.Persist.Actor
import Vervis.Persist.Collab import Vervis.Persist.Collab
import Vervis.Persist.Discussion import Vervis.Persist.Discussion
import Vervis.Persist.Follow import Vervis.Persist.Follow
import Vervis.Recipient (makeRecipientSet, LocalStageBy (..), Aud (..), collectAudience, localRecipSieve, localActorFollowers) import Vervis.Recipient (makeRecipientSet, LocalStageBy (..), Aud (..), collectAudience, localRecipSieve, localActorFollowers)
import Vervis.RemoteActorStore import Vervis.RemoteActorStore
import Vervis.Settings
import Vervis.Ticket import Vervis.Ticket
verifyActorAddressed :: RecipientRoutes -> LocalActorBy Key -> ActE () verifyActorAddressed :: RecipientRoutes -> LocalActorBy Key -> ActE ()
@ -831,6 +834,166 @@ clientCreateTeam now personMeID (ClientMsg maybeCap localRecips remoteRecips fwd
} }
return (action, recipientSet, remoteActors, fwdHosts) return (action, recipientSet, remoteActors, fwdHosts)
-- Meaning: The human wants to create a factory
-- Behavior:
-- * Verify human is allowed to
-- * Create a factory on DB
-- * Create a Permit record in DB
-- * Launch a factory actor
-- * Record a FollowRequest in DB
-- * Create and send Create and Follow to it
clientCreateFactory
:: UTCTime
-> PersonId
-> ClientMsg
-> AP.ActorDetail
-> ActE OutboxItemId
clientCreateFactory now personMeID (ClientMsg maybeCap localRecips remoteRecips fwdHosts action) detail = do
-- Check input
verifyNothingE maybeCap "Capability not needed"
(name, msummary) <- parseDetail detail
(actorMeID, localRecipsFinal, createID, actionCreate, followID, follow, factoryID) <- withDBExcept $ do
-- Grab me from DB
(personMe, actorMe) <- lift $ do
p <- getJust personMeID
(p,) <$> getJust (personActor p)
let actorMeID = personActor personMe
-- Verify I'm allowed to create a Factory
cans <- asksEnv $ appCanCreateFactories . envSettings
unless (personUsername personMe `elem` map text2username cans) $
throwE "Not allowed to create factories"
-- Insert new factory to DB
createID <- lift $ insertEmptyOutboxItem' (actorOutbox actorMe) now
(factoryID, resourceID, factoryFollowerSetID) <-
lift $ insertFactory now name msummary createID actorMeID
-- Insert a Permit record
permitID <- lift $ insert $ Permit personMeID AP.RoleAdmin
topicID <- lift $ insert $ PermitTopicLocal permitID resourceID
lift $ insert_ $ PermitFulfillsTopicCreation permitID
lift $ insert_ $ PermitPersonGesture permitID createID
-- Insert the Create activity to my outbox
factoryHash <- lift $ lift $ encodeKeyHashid factoryID
actionCreate <- lift $ lift $ prepareCreate name msummary factoryHash
luCreate <- lift $ updateOutboxItem' (LocalActorPerson personMeID) createID actionCreate
-- Prepare recipient sieve for sending the Create
personMeHash <- lift $ lift $ encodeKeyHashid personMeID
let sieve =
makeRecipientSet
[LocalActorFactory factoryHash]
[LocalStagePersonFollowers personMeHash]
onlyFactory = FactoryRoutes True False
addMe' factories= (factoryHash, onlyFactory) : factories
addMe rs = rs { recipFactories = addMe' $ recipFactories rs }
-- Insert a follow request, since I'm about to send a Follow
followID <- lift $ insertEmptyOutboxItem' (actorOutbox actorMe) now
lift $ insert_ $ FollowRequest actorMeID factoryFollowerSetID True followID
-- Insert a Follow to my outbox
follow@(actionFollow, _, _, _) <- lift $ lift $ prepareFollow factoryID luCreate
_luFollow <- lift $ updateOutboxItem' (LocalActorPerson personMeID) followID actionFollow
return
( personActor personMe
, localRecipSieve sieve False $ addMe localRecips
, createID
, actionCreate
, followID
, follow
, factoryID
)
-- Spawn new Factory actor
success <- lift $ launchActor factoryID
unless success $
error "Failed to spawn new Factory, somehow ID already in Theater"
-- Send the Create
lift $ sendActivity
(LocalActorPerson personMeID) actorMeID localRecipsFinal remoteRecips
fwdHosts createID actionCreate
-- Send the Follow
let (actionFollow, localRecipsFollow, remoteRecipsFollow, fwdHostsFollow) = follow
lift $ sendActivity
(LocalActorPerson personMeID) actorMeID localRecipsFollow
remoteRecipsFollow fwdHostsFollow followID actionFollow
return createID
where
parseDetail (AP.ActorDetail typ muser mname msummary) = do
unless (typ == AP.ActorTypeFactory) $
error "clientCreateFactory: Create object isn't a Factory"
verifyNothingE muser "Factory can't have a username"
name <- fromMaybeE mname "Factory doesn't specify name"
return (name, msummary)
insertFactory now name msummary obiidCreate actorMeID = do
Entity aid a <- insertActor now name (fromMaybe "" msummary) (Just actorMeID)
rid <- insert $ Resource aid
fid <- insert Factory
{ factoryResource = rid
, factoryCreate = obiidCreate
}
return (fid, rid, actorFollowers a)
prepareCreate name msummary factoryHash = do
encodeRouteLocal <- getEncodeRouteLocal
hLocal <- asksEnv stageInstanceHost
let ttdetail = AP.ActorDetail
{ AP.actorType = AP.ActorTypeFactory
, AP.actorUsername = Nothing
, AP.actorName = Just name
, AP.actorSummary = msummary
}
ttlocal = AP.ActorLocal
{ AP.actorId = encodeRouteLocal $ FactoryR factoryHash
, AP.actorInbox = encodeRouteLocal $ FactoryInboxR factoryHash
, AP.actorOutbox = Nothing
, AP.actorFollowers = Nothing
, AP.actorFollowing = Nothing
, AP.actorPublicKeys = []
, AP.actorSshKeys = []
}
specific = AP.CreateActivity AP.Create
{ AP.createObject = AP.CreateFactory ttdetail (Just (hLocal, ttlocal))
, AP.createTarget = Nothing
}
return action { AP.actionSpecific = specific }
prepareFollow factoryID luCreate = do
encodeRouteHome <- getEncodeRouteHome
h <- asksEnv stageInstanceHost
factoryHash <- encodeKeyHashid factoryID
let audTopic = AudLocal [LocalActorFactory factoryHash] []
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audTopic]
recips = map encodeRouteHome audLocal ++ audRemote
action = AP.Action
{ AP.actionCapability = Nothing
, AP.actionSummary = Nothing
, AP.actionAudience = AP.Audience recips [] [] [] [] []
, AP.actionFulfills = [ObjURI h luCreate]
, AP.actionSpecific = AP.FollowActivity AP.Follow
{ AP.followObject = encodeRouteHome $ FactoryR factoryHash
, AP.followContext = Nothing
, AP.followHide = False
}
}
return (action, recipientSet, remoteActors, fwdHosts)
clientCreate clientCreate
:: UTCTime :: UTCTime
-> PersonId -> PersonId
@ -855,6 +1018,11 @@ clientCreate now personMeID msg (AP.Create object muTarget) =
verifyNothingE muTarget "'target' not supported in Create Team" verifyNothingE muTarget "'target' not supported in Create Team"
clientCreateTeam now personMeID msg detail clientCreateTeam now personMeID msg detail
AP.CreateFactory detail mlocal -> do
verifyNothingE mlocal "Factory id must not be provided"
verifyNothingE muTarget "'target' not supported in Create Factory"
clientCreateFactory now personMeID msg detail
_ -> throwE "Unsupported Create object for C2S" _ -> throwE "Unsupported Create object for C2S"
-- Meaning: The human wants to invite someone A to a resource R -- Meaning: The human wants to invite someone A to a resource R

View file

@ -106,6 +106,7 @@ import Web.Hashids.Local
import Vervis.Actor import Vervis.Actor
import Vervis.Actor.Deck import Vervis.Actor.Deck
import Vervis.Actor.Factory
import Vervis.Actor.Group import Vervis.Actor.Group
import Vervis.Actor.Loom import Vervis.Actor.Loom
import Vervis.Actor.Person import Vervis.Actor.Person
@ -125,6 +126,7 @@ import Vervis.Handler.Client
import Vervis.Handler.Common import Vervis.Handler.Common
import Vervis.Handler.Cloth import Vervis.Handler.Cloth
import Vervis.Handler.Deck import Vervis.Handler.Deck
import Vervis.Handler.Factory
--import Vervis.Handler.Git --import Vervis.Handler.Git
import Vervis.Handler.Group import Vervis.Handler.Group
import Vervis.Handler.Key import Vervis.Handler.Key
@ -348,16 +350,18 @@ makeFoundation appSettings = do
, [(DeckId , StageEnv Staje)] , [(DeckId , StageEnv Staje)]
, [(LoomId , StageEnv Staje)] , [(LoomId , StageEnv Staje)]
, [(RepoId , StageEnv Staje)] , [(RepoId , StageEnv Staje)]
, [(FactoryId, StageEnv Staje)]
] ]
) )
loadTheater env = loadTheater env =
(\ p j g d l r -> p `H.HCons`j `H.HCons` g `H.HCons` d `H.HCons` l `H.HCons` r `H.HCons` H.HNil) (\ p j g d l r f -> p `H.HCons`j `H.HCons` g `H.HCons` d `H.HCons` l `H.HCons` r `H.HCons` f `H.HCons` H.HNil)
<$> (map (,env) <$> selectKeysList [PersonVerified ==. True] []) <$> (map (,env) <$> selectKeysList [PersonVerified ==. True] [])
<*> (map (,env) <$> selectKeysList [] []) <*> (map (,env) <$> selectKeysList [] [])
<*> (map (,env) <$> selectKeysList [] []) <*> (map (,env) <$> selectKeysList [] [])
<*> (map (,env) <$> selectKeysList [] []) <*> (map (,env) <$> selectKeysList [] [])
<*> (map (,env) <$> selectKeysList [] []) <*> (map (,env) <$> selectKeysList [] [])
<*> (map (,env) <$> selectKeysList [] []) <*> (map (,env) <$> selectKeysList [] [])
<*> (map (,env) <$> selectKeysList [] [])
startPersonLauncher :: Theater -> StageEnv Staje -> IO (MVar (PersonId, MVar Bool)) startPersonLauncher :: Theater -> StageEnv Staje -> IO (MVar (PersonId, MVar Bool))
startPersonLauncher theater env = do startPersonLauncher theater env = do

View file

@ -41,6 +41,7 @@ module Vervis.Client
, createRepo , createRepo
, createProject , createProject
, createGroup , createGroup
, createFactory
, invite , invite
, add , add
, remove , remove
@ -557,7 +558,7 @@ unfollow personID uActor = do
meActorID <- lift $ personActor <$> getJust personID meActorID <- lift $ personActor <$> getJust personID
case target of case target of
Left byk -> do Left byk -> do
themActorID <- localActorID <$> getLocalActorEntityE byk "No such local acto in DB" themActorID <- lift . grabLocalActorID =<< getLocalActorEntityE byk "No such local acto in DB"
theirFollowerSetID <- lift $ actorFollowers <$> getJust themActorID theirFollowerSetID <- lift $ actorFollowers <$> getJust themActorID
mf <- lift $ getValBy $ UniqueFollow meActorID theirFollowerSetID mf <- lift $ getValBy $ UniqueFollow meActorID theirFollowerSetID
followFollow <$> followFollow <$>
@ -1110,6 +1111,27 @@ createGroup senderHash name desc = do
return (Nothing, audience, detail) return (Nothing, audience, detail)
createFactory
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> KeyHashid Person
-> Text
-> Text
-> m (Maybe HTML, [Aud URIMode], AP.ActorDetail)
createFactory senderHash name desc = do
let audAuthor =
AudLocal [] [LocalStagePersonFollowers senderHash]
audience = [audAuthor]
detail = AP.ActorDetail
{ AP.actorType = AP.ActorTypeFactory
, AP.actorUsername = Nothing
, AP.actorName = Just name
, AP.actorSummary = Just desc
}
return (Nothing, audience, detail)
invite invite
:: PersonId :: PersonId
-> FedURI -> FedURI

View file

@ -21,7 +21,7 @@ module Vervis.Data.Actor
, activityRoute , activityRoute
, stampRoute , stampRoute
, parseStampRoute , parseStampRoute
, localActorID , grabLocalActorID
, localResourceID , localResourceID
, WA.parseLocalURI , WA.parseLocalURI
, parseFedURIOld , parseFedURIOld
@ -46,6 +46,7 @@ import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import Data.Bitraversable import Data.Bitraversable
import Data.Text (Text) import Data.Text (Text)
import Database.Persist.Sql
import Database.Persist.Types import Database.Persist.Types
import UnliftIO.Exception (try, SomeException, displayException) import UnliftIO.Exception (try, SomeException, displayException)
@ -154,6 +155,7 @@ activityRoute (LocalActorRepo r) = RepoOutboxItemR r
activityRoute (LocalActorDeck d) = DeckOutboxItemR d activityRoute (LocalActorDeck d) = DeckOutboxItemR d
activityRoute (LocalActorLoom l) = LoomOutboxItemR l activityRoute (LocalActorLoom l) = LoomOutboxItemR l
activityRoute (LocalActorProject r) = ProjectOutboxItemR r activityRoute (LocalActorProject r) = ProjectOutboxItemR r
activityRoute (LocalActorFactory f) = FactoryOutboxItemR f
stampRoute :: LocalActorBy KeyHashid -> KeyHashid SigKey -> Route App stampRoute :: LocalActorBy KeyHashid -> KeyHashid SigKey -> Route App
stampRoute (LocalActorPerson p) = PersonStampR p stampRoute (LocalActorPerson p) = PersonStampR p
@ -162,6 +164,7 @@ stampRoute (LocalActorRepo r) = RepoStampR r
stampRoute (LocalActorDeck d) = DeckStampR d stampRoute (LocalActorDeck d) = DeckStampR d
stampRoute (LocalActorLoom l) = LoomStampR l stampRoute (LocalActorLoom l) = LoomStampR l
stampRoute (LocalActorProject r) = ProjectStampR r stampRoute (LocalActorProject r) = ProjectStampR r
stampRoute (LocalActorFactory f) = FactoryStampR f
parseStampRoute parseStampRoute
:: Route App -> Maybe (LocalActorBy KeyHashid, KeyHashid SigKey) :: Route App -> Maybe (LocalActorBy KeyHashid, KeyHashid SigKey)
@ -171,15 +174,17 @@ parseStampRoute (RepoStampR r i) = Just (LocalActorRepo r, i)
parseStampRoute (DeckStampR d i) = Just (LocalActorDeck d, i) parseStampRoute (DeckStampR d i) = Just (LocalActorDeck d, i)
parseStampRoute (LoomStampR l i) = Just (LocalActorLoom l, i) parseStampRoute (LoomStampR l i) = Just (LocalActorLoom l, i)
parseStampRoute (ProjectStampR r i) = Just (LocalActorProject r, i) parseStampRoute (ProjectStampR r i) = Just (LocalActorProject r, i)
parseStampRoute (FactoryStampR f i) = Just (LocalActorFactory f, i)
parseStampRoute _ = Nothing parseStampRoute _ = Nothing
localActorID :: LocalActorBy Entity -> ActorId grabLocalActorID :: MonadIO m => LocalActorBy Entity -> SqlPersistT m ActorId
localActorID (LocalActorPerson (Entity _ p)) = personActor p grabLocalActorID (LocalActorPerson (Entity _ p)) = pure $ personActor p
localActorID (LocalActorGroup (Entity _ g)) = groupActor g grabLocalActorID (LocalActorGroup (Entity _ g)) = pure $ groupActor g
localActorID (LocalActorRepo (Entity _ r)) = repoActor r grabLocalActorID (LocalActorRepo (Entity _ r)) = pure $ repoActor r
localActorID (LocalActorDeck (Entity _ d)) = deckActor d grabLocalActorID (LocalActorDeck (Entity _ d)) = pure $ deckActor d
localActorID (LocalActorLoom (Entity _ l)) = loomActor l grabLocalActorID (LocalActorLoom (Entity _ l)) = pure $ loomActor l
localActorID (LocalActorProject (Entity _ r)) = projectActor r grabLocalActorID (LocalActorProject (Entity _ r)) = pure $ projectActor r
grabLocalActorID (LocalActorFactory (Entity _ f)) = resourceActor <$> getJust (factoryResource f)
localResourceID :: LocalResourceBy Entity -> ResourceId localResourceID :: LocalResourceBy Entity -> ResourceId
localResourceID (LocalResourceGroup (Entity _ g)) = groupResource g localResourceID (LocalResourceGroup (Entity _ g)) = groupResource g
@ -187,6 +192,7 @@ localResourceID (LocalResourceRepo (Entity _ r)) = repoResource r
localResourceID (LocalResourceDeck (Entity _ d)) = deckResource d localResourceID (LocalResourceDeck (Entity _ d)) = deckResource d
localResourceID (LocalResourceLoom (Entity _ l)) = loomResource l localResourceID (LocalResourceLoom (Entity _ l)) = loomResource l
localResourceID (LocalResourceProject (Entity _ r)) = projectResource r localResourceID (LocalResourceProject (Entity _ r)) = projectResource r
localResourceID (LocalResourceFactory (Entity _ f)) = factoryResource f
parseFedURIOld parseFedURIOld
:: ( MonadSite m :: ( MonadSite m

View file

@ -87,6 +87,7 @@ parseGrantResourceCollabs (DeckCollabsR d) = Just $ LocalResourceDeck d
parseGrantResourceCollabs (LoomCollabsR l) = Just $ LocalResourceLoom l parseGrantResourceCollabs (LoomCollabsR l) = Just $ LocalResourceLoom l
parseGrantResourceCollabs (ProjectCollabsR l) = Just $ LocalResourceProject l parseGrantResourceCollabs (ProjectCollabsR l) = Just $ LocalResourceProject l
parseGrantResourceCollabs (GroupMembersR l) = Just $ LocalResourceGroup l parseGrantResourceCollabs (GroupMembersR l) = Just $ LocalResourceGroup l
parseGrantResourceCollabs (FactoryCollabsR f) = Just $ LocalResourceFactory f
parseGrantResourceCollabs _ = Nothing parseGrantResourceCollabs _ = Nothing
data GrantRecipBy f = GrantRecipPerson (f Person) data GrantRecipBy f = GrantRecipPerson (f Person)
@ -390,6 +391,9 @@ parseAddTarget = \case
LoomTeamsR k -> LoomTeamsR k ->
ATLoomTeams <$> ATLoomTeams <$>
WAP.decodeKeyHashidE k "Inavlid hashid" WAP.decodeKeyHashidE k "Inavlid hashid"
FactoryTeamsR k ->
ATFactoryTeams <$>
WAP.decodeKeyHashidE k "Inavlid hashid"
GroupEffortsR k -> GroupEffortsR k ->
ATGroupEfforts <$> ATGroupEfforts <$>
WAP.decodeKeyHashidE k "Inavlid hashid" WAP.decodeKeyHashidE k "Inavlid hashid"
@ -454,6 +458,7 @@ data AddTarget
| ATRepoTeams RepoId | ATRepoTeams RepoId
| ATDeckTeams DeckId | ATDeckTeams DeckId
| ATLoomTeams LoomId | ATLoomTeams LoomId
| ATFactoryTeams FactoryId
| ATGroupEfforts GroupId | ATGroupEfforts GroupId
deriving Eq deriving Eq
@ -471,6 +476,7 @@ addTargetResource = \case
ATRepoTeams r -> LocalResourceRepo r ATRepoTeams r -> LocalResourceRepo r
ATDeckTeams d -> LocalResourceDeck d ATDeckTeams d -> LocalResourceDeck d
ATLoomTeams l -> LocalResourceLoom l ATLoomTeams l -> LocalResourceLoom l
ATFactoryTeams f -> LocalResourceFactory f
ATGroupEfforts g -> LocalResourceGroup g ATGroupEfforts g -> LocalResourceGroup g
addTargetComponentProjects = \case addTargetComponentProjects = \case
@ -563,6 +569,7 @@ resourceToComponent = \case
LocalResourceLoom k -> Just $ ComponentLoom k LocalResourceLoom k -> Just $ ComponentLoom k
LocalResourceProject _ -> Nothing LocalResourceProject _ -> Nothing
LocalResourceGroup _ -> Nothing LocalResourceGroup _ -> Nothing
LocalResourceFactory _ -> Nothing
localComponentID :: ComponentBy Entity -> KomponentId localComponentID :: ComponentBy Entity -> KomponentId
localComponentID (ComponentRepo (Entity _ r)) = repoKomponent r localComponentID (ComponentRepo (Entity _ r)) = repoKomponent r

View file

@ -223,3 +223,4 @@ messageRoute (LocalActorRepo r) = RepoMessageR r
messageRoute (LocalActorDeck d) = DeckMessageR d messageRoute (LocalActorDeck d) = DeckMessageR d
messageRoute (LocalActorLoom l) = LoomMessageR l messageRoute (LocalActorLoom l) = LoomMessageR l
messageRoute (LocalActorProject l) = ProjectMessageR l messageRoute (LocalActorProject l) = ProjectMessageR l
messageRoute (LocalActorFactory f) = FactoryMessageR f

View file

@ -161,6 +161,7 @@ type StemKeyHashid = KeyHashid Stem
type PermitFulfillsInviteKeyHashid = KeyHashid PermitFulfillsInvite type PermitFulfillsInviteKeyHashid = KeyHashid PermitFulfillsInvite
type DestUsStartKeyHashid = KeyHashid DestUsStart type DestUsStartKeyHashid = KeyHashid DestUsStart
type SquadUsStartKeyHashid = KeyHashid SquadUsStart type SquadUsStartKeyHashid = KeyHashid SquadUsStart
type FactoryKeyHashid = KeyHashid Factory
-- This is where we define all of the routes in our application. For a full -- This is where we define all of the routes in our application. For a full
-- explanation of the syntax, please see: -- explanation of the syntax, please see:
@ -238,6 +239,7 @@ instance Yesod App where
Just (DeckInboxR _) -> return False Just (DeckInboxR _) -> return False
Just (LoomInboxR _) -> return False Just (LoomInboxR _) -> return False
Just (ProjectInboxR _) -> return False Just (ProjectInboxR _) -> return False
Just (FactoryInboxR _) -> return False
Just (GitUploadRequestR _) -> return False Just (GitUploadRequestR _) -> return False
Just (DvaraR _) -> return False Just (DvaraR _) -> return False
Just RegisterR -> return False Just RegisterR -> return False
@ -282,7 +284,10 @@ instance Yesod App where
[E.Value i] -> return i [E.Value i] -> return i
_ -> error $ "countUnread returned " ++ show vs _ -> error $ "countUnread returned " ++ show vs
hash <- YH.encodeKeyHashid pid hash <- YH.encodeKeyHashid pid
return (p, hash, verified, unread) canCreateFactories <- do
cans <- asksSite $ appCanCreateFactories . appSettings
return $ personUsername person `elem` map text2username cans
return (p, hash, verified, unread, canCreateFactories)
(title, bcs) <- breadcrumbs (title, bcs) <- breadcrumbs
-- We break up the default layout into two components: -- We break up the default layout into two components:
@ -384,6 +389,8 @@ instance Yesod App where
(LoomInboxR _ , False) -> personAny (LoomInboxR _ , False) -> personAny
(FactoryInboxR _ , False) -> personAny
(FactoryNewR , _ ) -> personAny
@ -1106,6 +1113,7 @@ instance YesodBreadcrumbs App where
RepoErrboxR r -> ("Errbox", Just $ RepoR r) RepoErrboxR r -> ("Errbox", Just $ RepoR r)
DeckErrboxR d -> ("Errbox", Just $ DeckR d) DeckErrboxR d -> ("Errbox", Just $ DeckR d)
LoomErrboxR l -> ("Errbox", Just $ LoomR l) LoomErrboxR l -> ("Errbox", Just $ LoomR l)
FactoryErrboxR f -> ("Errbox", Just $ FactoryR f)
RemoteActorsR -> ("Remote Actors", Just HomeR) RemoteActorsR -> ("Remote Actors", Just HomeR)
RemoteActorR k -> (T.pack $ show $ fromSqlKey k, Just RemoteActorsR) RemoteActorR k -> (T.pack $ show $ fromSqlKey k, Just RemoteActorsR)
@ -1114,3 +1122,27 @@ instance YesodBreadcrumbs App where
FollowRemoteR _ -> ("", Nothing) FollowRemoteR _ -> ("", Nothing)
UnfollowLocalR _ -> ("", Nothing) UnfollowLocalR _ -> ("", Nothing)
UnfollowRemoteR _ -> ("", Nothing) UnfollowRemoteR _ -> ("", Nothing)
FactoryR f -> ("Factory *" <> keyHashidText f, Just HomeR)
FactoryInboxR f -> ("Inbox", Just $ FactoryR f)
FactoryOutboxR f -> ("Outbox", Just $ FactoryR f)
FactoryOutboxItemR f i -> (keyHashidText i, Just $ FactoryOutboxR f)
FactoryFollowersR f -> ("Followers", Just $ FactoryR f)
FactoryMessageR f m -> ("Message #" <> keyHashidText m, Just $ FactoryR f)
FactoryNewR -> ("New Factory", Just HomeR)
FactoryStampR f k -> ("Stamp #" <> keyHashidText k, Just $ FactoryR f)
FactoryCollabsR f -> ("Collaborators", Just $ FactoryR f)
FactoryInviteR _ -> ("", Nothing)
FactoryRemoveR _ _ -> ("", Nothing)
FactoryTeamsR f -> ("Teams", Just $ FactoryR f)
FactoryAddTeamR f -> ("", Nothing)
FactoryApproveTeamR f t -> ("", Nothing)
FactoryRemoveTeamR f t -> ("", Nothing)
FactoryTeamLiveR _ _ -> ("", Nothing)

View file

@ -256,7 +256,7 @@ getHomeR = do
bitraverse bitraverse
(\ byK -> do (\ byK -> do
byE <- getLocalActorEntityE byK "No such local actor in DB" byE <- getLocalActorEntityE byK "No such local actor in DB"
actor <- lift $ getJust $ localActorID byE actor <- lift $ getJust =<< grabLocalActorID byE
return (byK, actor) return (byK, actor)
) )
(\ u -> (\ u ->
@ -271,7 +271,7 @@ getHomeR = do
) )
personalOverview :: Entity Person -> Handler Html personalOverview :: Entity Person -> Handler Html
personalOverview (Entity pid _person) = do personalOverview (Entity pid person) = do
(permits, invites) <- runDB $ do (permits, invites) <- runDB $ do
permits <- do permits <- do
locals <- do locals <- do
@ -418,11 +418,14 @@ getHomeR = do
) )
return $ sortOn (view _1) $ locals ++ remotes return $ sortOn (view _1) $ locals ++ remotes
return (permits, invites) return (permits, invites)
let (people, repos, decks, looms, projects, groups, others) = let (people, repos, decks, looms, projects, groups, factories, others) =
partitionByActorType (view _4) (view _1) permits partitionByActorType (view _4) (view _1) permits
if null people if null people
then pure () then pure ()
else error "Bug: Person as a PermitTopic" else error "Bug: Person as a PermitTopic"
canCreateFactories <- do
cans <- asksSite $ appCanCreateFactories . appSettings
return $ personUsername person `elem` map text2username cans
defaultLayout $(widgetFile "personal-overview") defaultLayout $(widgetFile "personal-overview")
where where
@ -432,7 +435,7 @@ getHomeR = do
=> (a -> AP.ActorType) => (a -> AP.ActorType)
-> (a -> b) -> (a -> b)
-> [a] -> [a]
-> ([a], [a], [a], [a], [a], [a], [a]) -> ([a], [a], [a], [a], [a], [a], [a], [a])
partitionByActorType typ key xs = partitionByActorType typ key xs =
let p = filter ((== AP.ActorTypePerson) . typ) xs let p = filter ((== AP.ActorTypePerson) . typ) xs
r = filter ((== AP.ActorTypeRepo) . typ) xs r = filter ((== AP.ActorTypeRepo) . typ) xs
@ -440,8 +443,9 @@ getHomeR = do
l = filter ((== AP.ActorTypePatchTracker) . typ) xs l = filter ((== AP.ActorTypePatchTracker) . typ) xs
j = filter ((== AP.ActorTypeProject) . typ) xs j = filter ((== AP.ActorTypeProject) . typ) xs
g = filter ((== AP.ActorTypeTeam) . typ) xs g = filter ((== AP.ActorTypeTeam) . typ) xs
x = deleteFirstsBy ((==) `on` key) xs (p ++ r ++ d ++ l ++ j ++ g) f = filter ((== AP.ActorTypeFactory) . typ) xs
in (p, r, d, l, j, g, x) x = deleteFirstsBy ((==) `on` key) xs (p ++ r ++ d ++ l ++ j ++ g ++ f)
in (p, r, d, l, j, g, f, x)
item (_gestureID, role, deleg, _typ, actor, exts) = item (_gestureID, role, deleg, _typ, actor, exts) =
[whamlet| [whamlet|
@ -492,6 +496,9 @@ getHomeR = do
getBrowseR :: Handler Html getBrowseR :: Handler Html
getBrowseR = do getBrowseR = do
canCreateFactories <- do
cans <- asksSite $ appCanCreateFactories . appSettings
return $ \ p -> personUsername p `elem` map text2username cans
(people, groups, repos, decks, looms, projects) <- runDB $ (people, groups, repos, decks, looms, projects) <- runDB $
(,,,,,) (,,,,,)
<$> (E.select $ E.from $ \ (person `E.InnerJoin` actor) -> do <$> (E.select $ E.from $ \ (person `E.InnerJoin` actor) -> do

View file

@ -0,0 +1,418 @@
{- This file is part of Vervis.
-
- Written in 2016, 2019, 2022, 2023, 2024
- by fr33domlover <fr33domlover@riseup.net>.
-
- Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
module Vervis.Handler.Factory
( getFactoryR
, getFactoryInboxR
, getFactoryErrboxR
, postFactoryInboxR
, getFactoryOutboxR
, getFactoryOutboxItemR
, getFactoryFollowersR
, getFactoryMessageR
, getFactoryNewR
, postFactoryNewR
, getFactoryStampR
, getFactoryCollabsR
, postFactoryInviteR
, postFactoryRemoveR
, getFactoryTeamsR
, postFactoryAddTeamR
, postFactoryApproveTeamR
, postFactoryRemoveTeamR
, getFactoryTeamLiveR
)
where
import Control.Applicative
import Control.Arrow ((&&&))
import Control.Monad
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Data.Aeson
import Data.Bifunctor
import Data.Bitraversable
import Data.ByteString (ByteString)
import Data.Default.Class
import Data.Foldable
import Data.Maybe (fromMaybe, isJust)
import Data.Text (Text)
import Data.Time.Clock
import Data.Traversable
import Database.Persist
import Network.HTTP.Types.Method
import Optics.Core
import Text.Blaze.Html (Html)
import Yesod.Auth
import Yesod.Core
import Yesod.Core.Handler (redirect, setMessage, lookupPostParam, notFound)
import Yesod.Form
import Yesod.Form.Functions (runFormPost, runFormGet)
import Yesod.Form.Types (FormResult (..))
import Yesod.Persist.Core (runDB, get404, getBy404)
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Database.Esqueleto as E
import Database.Persist.JSON
import Development.PatchMediaType
import Network.FedURI
import Web.ActivityPub hiding (Project (..), Repo (..), Actor (..), ActorDetail (..), ActorLocal (..), Factory)
import Yesod.ActivityPub
import Yesod.FedURI
import Yesod.Hashids
import Yesod.MonadSite
import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Data.Either.Local
import Data.Paginate.Local
import Database.Persist.Local
import Yesod.Form.Local
import Yesod.Persist.Local
import Vervis.Access
import Vervis.API
import Vervis.Actor.Factory
import Vervis.Data.Actor
import Vervis.Federation.Auth
import Vervis.Federation.Discussion
import Vervis.Federation.Offer
import Vervis.Federation.Ticket
import Vervis.FedURI
import Vervis.Form.Ticket
import Vervis.Form.Tracker
import Vervis.Foundation
import Vervis.Model
import Vervis.Paginate
import Vervis.Persist.Actor
import Vervis.Persist.Collab
import Vervis.Recipient
import Vervis.Serve.Collab
import Vervis.Settings
import Vervis.Ticket
import Vervis.TicketFilter
import Vervis.Time
import Vervis.Web.Actor
import Vervis.Web.Collab
import Vervis.Widget
import Vervis.Widget.Person
import Vervis.Widget.Ticket
import Vervis.Widget.Tracker
import qualified Vervis.Client as C
getFactoryR :: KeyHashid Factory -> Handler TypedContent
getFactoryR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
(factory, actor, sigKeyIDs) <- runDB $ do
f <- get404 factoryID
Resource aid <- getJust $ factoryResource f
a <- getJust aid
sigKeys <- selectKeysList [SigKeyActor ==. aid] [Asc SigKeyId]
return (f, a, sigKeys)
encodeRouteLocal <- getEncodeRouteLocal
hashSigKey <- getEncodeKeyHashid
perActor <- asksSite $ appPerActorKeys . appSettings
let factoryAP = AP.Factory
{ AP.factoryActor = AP.Actor
{ AP.actorLocal = AP.ActorLocal
{ AP.actorId = encodeRouteLocal $ FactoryR factoryHash
, AP.actorInbox = encodeRouteLocal $ FactoryInboxR factoryHash
, AP.actorOutbox =
Just $ encodeRouteLocal $ FactoryOutboxR factoryHash
, AP.actorFollowers =
Just $ encodeRouteLocal $ FactoryFollowersR factoryHash
, AP.actorFollowing = Nothing
, AP.actorPublicKeys =
map (Left . encodeRouteLocal) $
if perActor
then map (FactoryStampR factoryHash . hashSigKey) sigKeyIDs
else [ActorKey1R, ActorKey2R]
, AP.actorSshKeys = []
}
, AP.actorDetail = AP.ActorDetail
{ AP.actorType = AP.ActorTypeFactory
, AP.actorUsername = Nothing
, AP.actorName = Just $ actorName actor
, AP.actorSummary = Just $ actorDesc actor
}
}
, AP.factoryCollabs =
encodeRouteLocal $ FactoryCollabsR factoryHash
, AP.factoryTeams =
encodeRouteLocal $ FactoryTeamsR factoryHash
}
provideHtmlAndAP factoryAP $ redirectToPrettyJSON $ FactoryR factoryHash
grabActorID = fmap resourceActor . getJust . factoryResource
getFactoryInboxR :: KeyHashid Factory -> Handler TypedContent
getFactoryInboxR = getInbox'' actorInbox FactoryInboxR grabActorID
getFactoryErrboxR :: KeyHashid Factory -> Handler TypedContent
getFactoryErrboxR = getInbox'' actorErrbox FactoryErrboxR grabActorID
postFactoryInboxR :: KeyHashid Factory -> Handler ()
postFactoryInboxR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
postInbox LocalActorFactory factoryID
getFactoryOutboxR :: KeyHashid Factory -> Handler TypedContent
getFactoryOutboxR = getOutbox' FactoryOutboxR FactoryOutboxItemR grabActorID
getFactoryOutboxItemR
:: KeyHashid Factory -> KeyHashid OutboxItem -> Handler TypedContent
getFactoryOutboxItemR = getOutboxItem' FactoryOutboxItemR grabActorID
getFactoryFollowersR :: KeyHashid Factory -> Handler TypedContent
getFactoryFollowersR = getActorFollowersCollection' FactoryFollowersR grabActorID
getFactoryMessageR :: KeyHashid Factory -> KeyHashid LocalMessage -> Handler Html
getFactoryMessageR _ _ = notFound
newFactoryForm = renderDivs $ (,)
<$> areq textField "Name*" Nothing
<*> areq textField "Description" Nothing
getFactoryNewR :: Handler Html
getFactoryNewR = do
((_result, widget), enctype) <- runFormPost newFactoryForm
defaultLayout $(widgetFile "factory/new")
postFactoryNewR :: Handler Html
postFactoryNewR = do
(name, desc) <- runFormPostRedirect FactoryNewR newFactoryForm
personEntity@(Entity personID person) <- requireAuth
personHash <- encodeKeyHashid personID
(maybeSummary, audience, detail) <- C.createFactory personHash name desc
(localRecips, remoteRecips, fwdHosts, action) <-
C.makeServerInput Nothing maybeSummary audience $ AP.CreateActivity $ AP.Create (AP.CreateFactory detail Nothing) Nothing
result <-
runExceptT $
handleViaActor personID Nothing localRecips remoteRecips fwdHosts action
case result of
Left e -> do
setMessage $ toHtml e
redirect FactoryNewR
Right createID -> do
maybeFactoryID <- runDB $ getKeyBy $ UniqueFactoryCreate createID
case maybeFactoryID of
Nothing -> error "Can't find the newly created factory"
Just factoryID -> do
factoryHash <- encodeKeyHashid factoryID
setMessage "New factory created"
redirect $ FactoryR factoryHash
getFactoryStampR :: KeyHashid Factory -> KeyHashid SigKey -> Handler TypedContent
getFactoryStampR = servePerActorKey'' grabActorID LocalActorFactory
getFactoryCollabsR :: KeyHashid Factory -> Handler TypedContent
getFactoryCollabsR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
(factory, actor) <- runDB $ do
factory <- get404 factoryID
Resource actorID <- getJust $ factoryResource factory
actor <- getJust actorID
return (factory, actor)
serveCollabs
AP.RelHasCollab
(factoryResource factory)
(FactoryR factoryHash)
(FactoryCollabsR factoryHash)
(FactoryRemoveR factoryHash)
(FactoryInviteR factoryHash)
(Just
( FactoryRemoveTeamR factoryHash
, FactoryAddTeamR factoryHash
, FactoryApproveTeamR factoryHash
)
)
(factoryNavW (Entity factoryID factory) actor)
postFactoryInviteR :: KeyHashid Factory -> Handler Html
postFactoryInviteR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
resourceID <- runDB $ factoryResource <$> get404 factoryID
serveInviteCollab resourceID (FactoryCollabsR factoryHash)
postFactoryRemoveR :: KeyHashid Factory -> CollabId -> Handler Html
postFactoryRemoveR factoryHash collabID = do
factoryID <- decodeKeyHashid404 factoryHash
resourceID <- runDB $ factoryResource <$> get404 factoryID
serveRemoveCollab resourceID (FactoryCollabsR factoryHash) collabID
getFactoryTeamsR :: KeyHashid Factory -> Handler TypedContent
getFactoryTeamsR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
resourceID <- runDB $ factoryResource <$> get404 factoryID
serveTeamsCollection (FactoryR factoryHash) (FactoryTeamsR factoryHash) resourceID
postFactoryAddTeamR :: KeyHashid Factory -> Handler ()
postFactoryAddTeamR factoryHash = do
factoryID <- decodeKeyHashid404 factoryHash
(uTeam, role) <-
runFormPostRedirect (FactoryCollabsR factoryHash) addTeamForm
personEntity@(Entity personID person) <- requireAuth
personHash <- encodeKeyHashid personID
encodeRouteHome <- getEncodeRouteHome
let uCollection = encodeRouteHome $ FactoryTeamsR factoryHash
result <- runExceptT $ do
(maybeSummary, audience, add) <- C.add personID uTeam uCollection role
cap <- do
maybeItem <- lift $ runDB $ do
resourceID <- factoryResource <$> get404 factoryID
getCapability personID (Left resourceID) AP.RoleAdmin
fromMaybeE maybeItem "You need to be have Admin access to the Factory to add teams"
uCap <- lift $ renderActivityURI cap
(localRecips, remoteRecips, fwdHosts, action) <-
C.makeServerInput (Just uCap) maybeSummary audience $ AP.AddActivity add
let cap' = first (\ (la, i) -> (la, error "lah", i)) cap
handleViaActor
personID (Just cap') localRecips remoteRecips fwdHosts action
case result of
Left e -> setMessage $ toHtml e
Right inviteID -> setMessage "Add sent"
redirect $ FactoryCollabsR factoryHash
postFactoryApproveTeamR :: KeyHashid Factory -> SquadId -> Handler Html
postFactoryApproveTeamR factoryHash squadID = do
factoryID <- decodeKeyHashid404 factoryHash
personEntity@(Entity personID person) <- requireAuth
personHash <- encodeKeyHashid personID
encodeRouteHome <- getEncodeRouteHome
result <- runExceptT $ do
mpidOrU <- lift $ runDB $ runMaybeT $ do
factory <- MaybeT $ get factoryID
Squad _ resourceID <- MaybeT $ get squadID
guard $ resourceID == factoryResource factory
uAdd <- lift $ do
add <- getSquadAdd squadID
renderActivityURI add
topic <- lift $ bimap snd snd <$> getSquadTeam squadID
lift $
(factoryResource factory,uAdd,) <$>
bitraverse
pure
(getRemoteActorURI <=< getJust)
topic
(resourceID, uAdd, pidOrU) <- maybe notFound pure mpidOrU
(maybeSummary, audience, accept) <- do
uTeam <-
case pidOrU of
Left g -> encodeRouteHome . GroupR <$> encodeKeyHashid g
Right u -> pure u
let uFactory = encodeRouteHome $ FactoryR factoryHash
C.acceptParentChild personID uAdd uTeam uFactory
cap <- do
maybeItem <- lift $ runDB $ getCapability personID (Left resourceID) AP.RoleAdmin
fromMaybeE maybeItem "You need to be have Admin access to the Factory to approve teams"
uCap <- lift $ renderActivityURI cap
(localRecips, remoteRecips, fwdHosts, action) <-
C.makeServerInput (Just uCap) maybeSummary audience $ AP.AcceptActivity accept
let cap' = first (\ (la, i) -> (la, error "lah", i)) cap
handleViaActor
personID (Just cap') localRecips remoteRecips fwdHosts action
case result of
Left e -> do
setMessage $ toHtml e
Right removeID ->
setMessage "Accept sent"
redirect $ FactoryCollabsR factoryHash
postFactoryRemoveTeamR :: KeyHashid Factory -> SquadId -> Handler Html
postFactoryRemoveTeamR factoryHash squadID = do
factoryID <- decodeKeyHashid404 factoryHash
personEntity@(Entity personID person) <- requireAuth
personHash <- encodeKeyHashid personID
encodeRouteHome <- getEncodeRouteHome
result <- runExceptT $ do
mpidOrU <- lift $ runDB $ runMaybeT $ do
factory <- MaybeT $ get factoryID
Squad _ resourceID <- MaybeT $ get squadID
guard $ resourceID == factoryResource factory
acceptID <- MaybeT $ getKeyBy $ UniqueSquadUsAccept squadID
_ <- MaybeT $ getBy $ UniqueSquadUsStart acceptID
uAdd <- lift $ do
add <- getSquadAdd squadID
renderActivityURI add
topic <- lift $ bimap snd snd <$> getSquadTeam squadID
lift $
(factoryResource factory,uAdd,) <$>
bitraverse
pure
(getRemoteActorURI <=< getJust)
topic
(resourceID, uAdd, pidOrU) <- maybe notFound pure mpidOrU
(maybeSummary, audience, remove) <- do
uTeam <-
case pidOrU of
Left g -> encodeRouteHome . GroupR <$> encodeKeyHashid g
Right u -> pure u
let uCollection = encodeRouteHome $ FactoryTeamsR factoryHash
C.remove personID uTeam uCollection
cap <- do
maybeItem <- lift $ runDB $ getCapability personID (Left resourceID) AP.RoleAdmin
fromMaybeE maybeItem "You need to be have Admin access to the Factory to remove teams"
uCap <- lift $ renderActivityURI cap
(localRecips, remoteRecips, fwdHosts, action) <-
C.makeServerInput (Just uCap) maybeSummary audience $ AP.RemoveActivity remove
let cap' = first (\ (la, i) -> (la, error "lah", i)) cap
handleViaActor
personID (Just cap') localRecips remoteRecips fwdHosts action
case result of
Left e -> do
setMessage $ toHtml e
Right removeID ->
setMessage "Remove sent"
redirect $ FactoryCollabsR factoryHash
getFactoryTeamLiveR :: KeyHashid Factory -> KeyHashid SquadUsStart -> Handler ()
getFactoryTeamLiveR factoryHash startHash = do
factoryID <- decodeKeyHashid404 factoryHash
startID <- decodeKeyHashid404 startHash
runDB $ do
factory <- get404 factoryID
SquadUsStart usAcceptID _ <- get404 startID
SquadUsAccept squadID _ <- getJust usAcceptID
Squad _ resourceID <- getJust squadID
unless (resourceID == factoryResource factory) notFound

View file

@ -3848,6 +3848,8 @@ changes hLocal ctx =
, removeField "Effort" "topic" , removeField "Effort" "topic"
-- 648 -- 648
, addEntities model_648_report , addEntities model_648_report
-- 649
, addEntities model_649_factory
] ]
migrateDB migrateDB

View file

@ -79,6 +79,7 @@ module Vervis.Migration.Entities
, model_638_effort_squad , model_638_effort_squad
, model_639_component_convey , model_639_component_convey
, model_648_report , model_648_report
, model_649_factory
) )
where where
@ -311,3 +312,6 @@ type ListOfByteStrings = [ByteString]
model_648_report :: [Entity SqlBackend] model_648_report :: [Entity SqlBackend]
model_648_report = $(schema "648_2024-07-06_report") model_648_report = $(schema "648_2024-07-06_report")
model_649_factory :: [Entity SqlBackend]
model_649_factory = $(schema "649_2024-07-29_factory")

View file

@ -109,6 +109,10 @@ instance Hashable ProjectId where
hashWithSalt salt = hashWithSalt salt . fromSqlKey hashWithSalt salt = hashWithSalt salt . fromSqlKey
hash = hash . fromSqlKey hash = hash . fromSqlKey
instance Hashable FactoryId where
hashWithSalt salt = hashWithSalt salt . fromSqlKey
hash = hash . fromSqlKey
{- {-
instance PersistEntityGraph Ticket TicketDependency where instance PersistEntityGraph Ticket TicketDependency where
sourceParam = ticketDependencyParent sourceParam = ticketDependencyParent

View file

@ -114,21 +114,28 @@ getLocalComponent = fmap (bmap entityKey) . getLocalComponentEnt
getLocalActorEnt getLocalActorEnt
:: MonadIO m => ActorId -> ReaderT SqlBackend m (LocalActorBy Entity) :: MonadIO m => ActorId -> ReaderT SqlBackend m (LocalActorBy Entity)
getLocalActorEnt actorID = do getLocalActorEnt actorID = do
m <- getKeyBy $ UniqueResource actorID
mp <- getBy $ UniquePersonActor actorID mp <- getBy $ UniquePersonActor actorID
mg <- getBy $ UniqueGroupActor actorID mg <- getBy $ UniqueGroupActor actorID
mr <- getBy $ UniqueRepoActor actorID mr <- getBy $ UniqueRepoActor actorID
md <- getBy $ UniqueDeckActor actorID md <- getBy $ UniqueDeckActor actorID
ml <- getBy $ UniqueLoomActor actorID ml <- getBy $ UniqueLoomActor actorID
mj <- getBy $ UniqueProjectActor actorID mj <- getBy $ UniqueProjectActor actorID
mf <- runMaybeT $ do
resourceID <- hoistMaybe m
MaybeT $ getBy $ UniqueFactory resourceID
return $ return $
case (mp, mg, mr, md, ml, mj) of case (mp, mg, mr, md, ml, mj, mf) of
(Nothing, Nothing, Nothing, Nothing, Nothing, Nothing) -> error "Unused ActorId" (Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing) -> error "Unused ActorId"
(Just p, Nothing, Nothing, Nothing, Nothing, Nothing) -> LocalActorPerson p (Just p, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing) -> LocalActorPerson p
(Nothing, Just g, Nothing, Nothing, Nothing, Nothing) -> LocalActorGroup g (Nothing, Just g, Nothing, Nothing, Nothing, Nothing, Nothing) -> LocalActorGroup g
(Nothing, Nothing, Just r, Nothing, Nothing, Nothing) -> LocalActorRepo r (Nothing, Nothing, Just r, Nothing, Nothing, Nothing, Nothing) -> LocalActorRepo r
(Nothing, Nothing, Nothing, Just d, Nothing, Nothing) -> LocalActorDeck d (Nothing, Nothing, Nothing, Just d, Nothing, Nothing, Nothing) -> LocalActorDeck d
(Nothing, Nothing, Nothing, Nothing, Just l, Nothing) -> LocalActorLoom l (Nothing, Nothing, Nothing, Nothing, Just l, Nothing, Nothing) -> LocalActorLoom l
(Nothing, Nothing, Nothing, Nothing, Nothing, Just j) -> LocalActorProject j (Nothing, Nothing, Nothing, Nothing, Nothing, Just j, Nothing) -> LocalActorProject j
(Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Just f) -> LocalActorFactory f
_ -> error "Multi-usage of an ActorId" _ -> error "Multi-usage of an ActorId"
getLocalResourceEnt getLocalResourceEnt
@ -142,6 +149,7 @@ getLocalResourceEnt resourceID = do
, fmap LocalResourceLoom <$> getBy (UniqueLoomActor actorID) , fmap LocalResourceLoom <$> getBy (UniqueLoomActor actorID)
, fmap LocalResourceProject <$> getBy (UniqueProjectActor actorID) , fmap LocalResourceProject <$> getBy (UniqueProjectActor actorID)
, fmap LocalResourceGroup <$> getBy (UniqueGroupActor actorID) , fmap LocalResourceGroup <$> getBy (UniqueGroupActor actorID)
, fmap LocalResourceFactory <$> getBy (UniqueFactory resourceID)
] ]
exactlyOneJust exactlyOneJust
options options
@ -180,6 +188,8 @@ getLocalActorEntity (LocalActorLoom l) =
fmap (LocalActorLoom . Entity l) <$> get l fmap (LocalActorLoom . Entity l) <$> get l
getLocalActorEntity (LocalActorProject r) = getLocalActorEntity (LocalActorProject r) =
fmap (LocalActorProject . Entity r) <$> get r fmap (LocalActorProject . Entity r) <$> get r
getLocalActorEntity (LocalActorFactory f) =
fmap (LocalActorFactory . Entity f) <$> get f
getLocalActorEntityE a e = do getLocalActorEntityE a e = do
m <- lift $ getLocalActorEntity a m <- lift $ getLocalActorEntity a
@ -203,6 +213,8 @@ getLocalResourceEntity (LocalResourceLoom l) =
fmap (LocalResourceLoom . Entity l) <$> get l fmap (LocalResourceLoom . Entity l) <$> get l
getLocalResourceEntity (LocalResourceProject r) = getLocalResourceEntity (LocalResourceProject r) =
fmap (LocalResourceProject . Entity r) <$> get r fmap (LocalResourceProject . Entity r) <$> get r
getLocalResourceEntity (LocalResourceFactory f) =
fmap (LocalResourceFactory . Entity f) <$> get f
getLocalResourceEntityE a e = do getLocalResourceEntityE a e = do
m <- lift $ getLocalResourceEntity a m <- lift $ getLocalResourceEntity a

View file

@ -96,6 +96,9 @@ getLocalAuthor lmid aid name = do
LocalActorProject projectID -> do LocalActorProject projectID -> do
projectHash <- encodeKeyHashid projectID projectHash <- encodeKeyHashid projectID
return $ "$" <> keyHashidText projectHash return $ "$" <> keyHashidText projectHash
LocalActorFactory factoryID -> do
factoryHash <- encodeKeyHashid factoryID
return $ "*" <> keyHashidText factoryHash
return $ MessageTreeNodeLocal lmid authorByKey code name return $ MessageTreeNodeLocal lmid authorByKey code name
getAllMessages :: AppDB DiscussionId -> Handler [MessageTreeNode] getAllMessages :: AppDB DiscussionId -> Handler [MessageTreeNode]
@ -176,7 +179,7 @@ getMessageFromRoute authorByKey localMsgID = do
authorByEntity <- do authorByEntity <- do
maybeActor <- lift $ getLocalActorEntity authorByKey maybeActor <- lift $ getLocalActorEntity authorByKey
fromMaybeE maybeActor "No such author in DB" fromMaybeE maybeActor "No such author in DB"
let actorID = localActorID authorByEntity actorID <- lift $ grabLocalActorID authorByEntity
actor <- lift $ getJust actorID actor <- lift $ getJust actorID
localMsg <- do localMsg <- do
mlm <- lift $ get localMsgID mlm <- lift $ get localMsgID

View file

@ -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, 2024 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.
- -
@ -76,7 +76,8 @@ getFollowee (FolloweeActor actorByKey) = do
actorByEntity <- do actorByEntity <- do
maybeActor <- lift $ getLocalActorEntity actorByKey maybeActor <- lift $ getLocalActorEntity actorByKey
fromMaybeE maybeActor "Actor not found in DB" fromMaybeE maybeActor "Actor not found in DB"
return (actorByKey, localActorID actorByEntity, Nothing) actorID <- lift $ grabLocalActorID actorByEntity
return (actorByKey, actorID, Nothing)
getFollowee (FolloweeWorkItem wi) = getFollowee (FolloweeWorkItem wi) =
case wi of case wi of
WorkItemTicket deckID taskID -> do WorkItemTicket deckID taskID -> do

View file

@ -215,6 +215,7 @@ parseLocalActor (RepoR rkhid) = Just $ LocalActorRepo rkhid
parseLocalActor (DeckR dkhid) = Just $ LocalActorDeck dkhid parseLocalActor (DeckR dkhid) = Just $ LocalActorDeck dkhid
parseLocalActor (LoomR lkhid) = Just $ LocalActorLoom lkhid parseLocalActor (LoomR lkhid) = Just $ LocalActorLoom lkhid
parseLocalActor (ProjectR jkhid) = Just $ LocalActorProject jkhid parseLocalActor (ProjectR jkhid) = Just $ LocalActorProject jkhid
parseLocalActor (FactoryR fkhid) = Just $ LocalActorFactory fkhid
parseLocalActor _ = Nothing parseLocalActor _ = Nothing
renderLocalActor :: LocalActor -> Route App renderLocalActor :: LocalActor -> Route App
@ -224,6 +225,7 @@ renderLocalActor (LocalActorRepo rkhid) = RepoR rkhid
renderLocalActor (LocalActorDeck dkhid) = DeckR dkhid renderLocalActor (LocalActorDeck dkhid) = DeckR dkhid
renderLocalActor (LocalActorLoom lkhid) = LoomR lkhid renderLocalActor (LocalActorLoom lkhid) = LoomR lkhid
renderLocalActor (LocalActorProject jkhid) = ProjectR jkhid renderLocalActor (LocalActorProject jkhid) = ProjectR jkhid
renderLocalActor (LocalActorFactory fkhid) = FactoryR fkhid
parseLocalResource :: Route App -> Maybe (LocalResourceBy KeyHashid) parseLocalResource :: Route App -> Maybe (LocalResourceBy KeyHashid)
parseLocalResource (GroupR gkhid) = Just $ LocalResourceGroup gkhid parseLocalResource (GroupR gkhid) = Just $ LocalResourceGroup gkhid
@ -231,6 +233,7 @@ parseLocalResource (RepoR rkhid) = Just $ LocalResourceRepo rkhid
parseLocalResource (DeckR dkhid) = Just $ LocalResourceDeck dkhid parseLocalResource (DeckR dkhid) = Just $ LocalResourceDeck dkhid
parseLocalResource (LoomR lkhid) = Just $ LocalResourceLoom lkhid parseLocalResource (LoomR lkhid) = Just $ LocalResourceLoom lkhid
parseLocalResource (ProjectR jkhid) = Just $ LocalResourceProject jkhid parseLocalResource (ProjectR jkhid) = Just $ LocalResourceProject jkhid
parseLocalResource (FactoryR fkhid) = Just $ LocalResourceFactory fkhid
parseLocalResource _ = Nothing parseLocalResource _ = Nothing
renderLocalResource :: LocalResourceBy KeyHashid -> Route App renderLocalResource :: LocalResourceBy KeyHashid -> Route App
@ -239,6 +242,7 @@ renderLocalResource (LocalResourceRepo rkhid) = RepoR rkhid
renderLocalResource (LocalResourceDeck dkhid) = DeckR dkhid renderLocalResource (LocalResourceDeck dkhid) = DeckR dkhid
renderLocalResource (LocalResourceLoom lkhid) = LoomR lkhid renderLocalResource (LocalResourceLoom lkhid) = LoomR lkhid
renderLocalResource (LocalResourceProject jkhid) = ProjectR jkhid renderLocalResource (LocalResourceProject jkhid) = ProjectR jkhid
renderLocalResource (LocalResourceFactory fkhid) = FactoryR fkhid
data LocalStageBy f data LocalStageBy f
= LocalStagePersonFollowers (f Person) = LocalStagePersonFollowers (f Person)
@ -254,6 +258,8 @@ data LocalStageBy f
| LocalStageClothFollowers (f Loom) (f TicketLoom) | LocalStageClothFollowers (f Loom) (f TicketLoom)
| LocalStageProjectFollowers (f Project) | LocalStageProjectFollowers (f Project)
| LocalStageFactoryFollowers (f Factory)
deriving (Generic, FunctorB, ConstraintsB) deriving (Generic, FunctorB, ConstraintsB)
deriving instance AllBF Eq f LocalStageBy => Eq (LocalStageBy f) deriving instance AllBF Eq f LocalStageBy => Eq (LocalStageBy f)
@ -278,6 +284,8 @@ parseLocalStage (ClothFollowersR lkhid ltkhid) =
Just $ LocalStageClothFollowers lkhid ltkhid Just $ LocalStageClothFollowers lkhid ltkhid
parseLocalStage (ProjectFollowersR jkhid) = parseLocalStage (ProjectFollowersR jkhid) =
Just $ LocalStageProjectFollowers jkhid Just $ LocalStageProjectFollowers jkhid
parseLocalStage (FactoryFollowersR fkhid) =
Just $ LocalStageFactoryFollowers fkhid
parseLocalStage _ = Nothing parseLocalStage _ = Nothing
renderLocalStage :: LocalStage -> Route App renderLocalStage :: LocalStage -> Route App
@ -297,6 +305,8 @@ renderLocalStage (LocalStageClothFollowers lkhid ltkhid) =
ClothFollowersR lkhid ltkhid ClothFollowersR lkhid ltkhid
renderLocalStage (LocalStageProjectFollowers jkhid) = renderLocalStage (LocalStageProjectFollowers jkhid) =
ProjectFollowersR jkhid ProjectFollowersR jkhid
renderLocalStage (LocalStageFactoryFollowers fkhid) =
FactoryFollowersR fkhid
parseLocalRecipient :: Route App -> Maybe (Either LocalActor LocalStage) parseLocalRecipient :: Route App -> Maybe (Either LocalActor LocalStage)
parseLocalRecipient r = parseLocalRecipient r =
@ -309,6 +319,7 @@ localActorFollowers (LocalActorRepo r) = LocalStageRepoFollowers r
localActorFollowers (LocalActorDeck d) = LocalStageDeckFollowers d localActorFollowers (LocalActorDeck d) = LocalStageDeckFollowers d
localActorFollowers (LocalActorLoom l) = LocalStageLoomFollowers l localActorFollowers (LocalActorLoom l) = LocalStageLoomFollowers l
localActorFollowers (LocalActorProject j) = LocalStageProjectFollowers j localActorFollowers (LocalActorProject j) = LocalStageProjectFollowers j
localActorFollowers (LocalActorFactory f) = LocalStageFactoryFollowers f
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Converting between KeyHashid, Key, Identity and Entity -- Converting between KeyHashid, Key, Identity and Entity
@ -412,6 +423,8 @@ hashLocalStagePure ctx = f
(encodeKeyHashidPure ctx c) (encodeKeyHashidPure ctx c)
f (LocalStageProjectFollowers j) = f (LocalStageProjectFollowers j) =
LocalStageProjectFollowers $ encodeKeyHashidPure ctx j LocalStageProjectFollowers $ encodeKeyHashidPure ctx j
f (LocalStageFactoryFollowers j) =
LocalStageFactoryFollowers $ encodeKeyHashidPure ctx j
getHashLocalStage getHashLocalStage
:: (MonadSite m, YesodHashids (SiteEnv m)) :: (MonadSite m, YesodHashids (SiteEnv m))
@ -451,6 +464,8 @@ unhashLocalStagePure ctx = f
<*> decodeKeyHashidPure ctx c <*> decodeKeyHashidPure ctx c
f (LocalStageProjectFollowers j) = f (LocalStageProjectFollowers j) =
LocalStageProjectFollowers <$> decodeKeyHashidPure ctx j LocalStageProjectFollowers <$> decodeKeyHashidPure ctx j
f (LocalStageFactoryFollowers j) =
LocalStageFactoryFollowers <$> decodeKeyHashidPure ctx j
unhashLocalStage unhashLocalStage
:: (MonadSite m, YesodHashids (SiteEnv m)) :: (MonadSite m, YesodHashids (SiteEnv m))
@ -493,6 +508,10 @@ getLocalActorID (LocalActorRepo r) = fmap repoActor <$> get r
getLocalActorID (LocalActorDeck d) = fmap deckActor <$> get d getLocalActorID (LocalActorDeck d) = fmap deckActor <$> get d
getLocalActorID (LocalActorLoom l) = fmap loomActor <$> get l getLocalActorID (LocalActorLoom l) = fmap loomActor <$> get l
getLocalActorID (LocalActorProject j) = fmap projectActor <$> get j getLocalActorID (LocalActorProject j) = fmap projectActor <$> get j
getLocalActorID (LocalActorFactory f) = do
maybeResourceID <- fmap factoryResource <$> get f
for maybeResourceID $ \ resourceID ->
resourceActor <$> getJust resourceID
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Intermediate recipient types -- Intermediate recipient types
@ -518,6 +537,8 @@ data LeafLoom = LeafLoom | LeafLoomFollowers deriving (Eq, Ord)
data LeafProject = LeafProject | LeafProjectFollowers deriving (Eq, Ord) data LeafProject = LeafProject | LeafProjectFollowers deriving (Eq, Ord)
data LeafFactory = LeafFactory | LeafFactoryFollowers deriving (Eq, Ord)
data PieceDeck data PieceDeck
= PieceDeck LeafDeck = PieceDeck LeafDeck
| PieceTicket (KeyHashid TicketDeck) LeafTicket | PieceTicket (KeyHashid TicketDeck) LeafTicket
@ -535,6 +556,7 @@ data LocalRecipient
| RecipDeck (KeyHashid Deck) PieceDeck | RecipDeck (KeyHashid Deck) PieceDeck
| RecipLoom (KeyHashid Loom) PieceLoom | RecipLoom (KeyHashid Loom) PieceLoom
| RecipProject (KeyHashid Project) LeafProject | RecipProject (KeyHashid Project) LeafProject
| RecipFactory (KeyHashid Factory) LeafFactory
deriving (Eq, Ord) deriving (Eq, Ord)
recipientFromActor :: LocalActor -> LocalRecipient recipientFromActor :: LocalActor -> LocalRecipient
@ -550,6 +572,8 @@ recipientFromActor (LocalActorLoom lkhid) =
RecipLoom lkhid $ PieceLoom LeafLoom RecipLoom lkhid $ PieceLoom LeafLoom
recipientFromActor (LocalActorProject jkhid) = recipientFromActor (LocalActorProject jkhid) =
RecipProject jkhid LeafProject RecipProject jkhid LeafProject
recipientFromActor (LocalActorFactory fkhid) =
RecipFactory fkhid LeafFactory
recipientFromStage :: LocalStage -> LocalRecipient recipientFromStage :: LocalStage -> LocalRecipient
recipientFromStage (LocalStagePersonFollowers pkhid) = recipientFromStage (LocalStagePersonFollowers pkhid) =
@ -568,6 +592,8 @@ recipientFromStage (LocalStageClothFollowers lkhid ltkhid) =
RecipLoom lkhid $ PieceCloth ltkhid LeafClothFollowers RecipLoom lkhid $ PieceCloth ltkhid LeafClothFollowers
recipientFromStage (LocalStageProjectFollowers jkhid) = recipientFromStage (LocalStageProjectFollowers jkhid) =
RecipProject jkhid LeafProjectFollowers RecipProject jkhid LeafProjectFollowers
recipientFromStage (LocalStageFactoryFollowers fkhid) =
RecipFactory fkhid LeafFactoryFollowers
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Recipient set types -- Recipient set types
@ -589,21 +615,24 @@ groupLocalRecipients = organize . partitionByActor
, [(KeyHashid Deck, PieceDeck)] , [(KeyHashid Deck, PieceDeck)]
, [(KeyHashid Loom, PieceLoom)] , [(KeyHashid Loom, PieceLoom)]
, [(KeyHashid Project, LeafProject)] , [(KeyHashid Project, LeafProject)]
, [(KeyHashid Factory, LeafFactory)]
) )
partitionByActor = foldl' f ([], [], [], [], [], []) partitionByActor = foldl' f ([], [], [], [], [], [], [])
where where
f (p, g, r, d, l, j) (RecipPerson pkhid pleaf) = f (p, g, r, d, l, j, f') (RecipPerson pkhid pleaf) =
((pkhid, pleaf) : p, g, r, d, l, j) ((pkhid, pleaf) : p, g, r, d, l, j, f')
f (p, g, r, d, l, j) (RecipGroup gkhid gleaf) = f (p, g, r, d, l, j, f') (RecipGroup gkhid gleaf) =
(p, (gkhid, gleaf) : g, r, d, l, j) (p, (gkhid, gleaf) : g, r, d, l, j, f')
f (p, g, r, d, l, j) (RecipRepo rkhid rleaf) = f (p, g, r, d, l, j, f') (RecipRepo rkhid rleaf) =
(p, g, (rkhid, rleaf) : r, d, l, j) (p, g, (rkhid, rleaf) : r, d, l, j, f')
f (p, g, r, d, l, j) (RecipDeck dkhid dpiece) = f (p, g, r, d, l, j, f') (RecipDeck dkhid dpiece) =
(p, g, r, (dkhid, dpiece) : d, l, j) (p, g, r, (dkhid, dpiece) : d, l, j, f')
f (p, g, r, d, l, j) (RecipLoom lkhid lpiece) = f (p, g, r, d, l, j, f') (RecipLoom lkhid lpiece) =
(p, g, r, d, (lkhid, lpiece) : l, j) (p, g, r, d, (lkhid, lpiece) : l, j, f')
f (p, g, r, d, l, j) (RecipProject jkhid jleaf) = f (p, g, r, d, l, j, f') (RecipProject jkhid jleaf) =
(p, g, r, d, l, (jkhid, jleaf) : j) (p, g, r, d, l, (jkhid, jleaf) : j, f')
f (p, g, r, d, l, j, f') (RecipFactory fkhid fleaf) =
(p, g, r, d, l, j, (fkhid, fleaf) : f')
organize organize
:: ( [(KeyHashid Person, LeafPerson)] :: ( [(KeyHashid Person, LeafPerson)]
@ -612,9 +641,10 @@ groupLocalRecipients = organize . partitionByActor
, [(KeyHashid Deck, PieceDeck)] , [(KeyHashid Deck, PieceDeck)]
, [(KeyHashid Loom, PieceLoom)] , [(KeyHashid Loom, PieceLoom)]
, [(KeyHashid Project, LeafProject)] , [(KeyHashid Project, LeafProject)]
, [(KeyHashid Factory, LeafFactory)]
) )
-> RecipientRoutes -> RecipientRoutes
organize (p, g, r, d, l, j) = RecipientRoutes organize (p, g, r, d, l, j, f) = RecipientRoutes
{ recipPeople = { recipPeople =
map (second $ foldr orLP $ PersonRoutes False False) $ groupByKeySort p map (second $ foldr orLP $ PersonRoutes False False) $ groupByKeySort p
, recipGroups = , recipGroups =
@ -645,6 +675,8 @@ groupLocalRecipients = organize . partitionByActor
groupByKeySort l groupByKeySort l
, recipProjects = , recipProjects =
map (second $ foldr orLJ $ ProjectRoutes False False) $ groupByKeySort j map (second $ foldr orLJ $ ProjectRoutes False False) $ groupByKeySort j
, recipFactories =
map (second $ foldr orLF $ FactoryRoutes False False) $ groupByKeySort f
} }
where where
groupByKey :: (Foldable f, Eq a) => f (a, b) -> [(a, NonEmpty b)] groupByKey :: (Foldable f, Eq a) => f (a, b) -> [(a, NonEmpty b)]
@ -691,6 +723,11 @@ groupLocalRecipients = organize . partitionByActor
orLJ LeafProject rr@(ProjectRoutes _ _) = rr { routeProject = True } orLJ LeafProject rr@(ProjectRoutes _ _) = rr { routeProject = True }
orLJ LeafProjectFollowers rr@(ProjectRoutes _ _) = rr { routeProjectFollowers = True } orLJ LeafProjectFollowers rr@(ProjectRoutes _ _) = rr { routeProjectFollowers = True }
orLF :: LeafFactory -> FactoryRoutes -> FactoryRoutes
orLF _ rr@(FactoryRoutes True True) = rr
orLF LeafFactory rr@(FactoryRoutes _ _) = rr { routeFactory = True }
orLF LeafFactoryFollowers rr@(FactoryRoutes _ _) = rr { routeFactoryFollowers = True }
pd2either :: PieceDeck -> Either LeafDeck (KeyHashid TicketDeck, LeafTicket) pd2either :: PieceDeck -> Either LeafDeck (KeyHashid TicketDeck, LeafTicket)
pd2either (PieceDeck ld) = Left ld pd2either (PieceDeck ld) = Left ld
pd2either (PieceTicket ltkhid lt) = Right (ltkhid, lt) pd2either (PieceTicket ltkhid lt) = Right (ltkhid, lt)
@ -729,6 +766,7 @@ localRecipSieve' sieve allowPeople allowOthers routes = RecipientRoutes
, recipDecks = applySieve' applyDeck recipDecks , recipDecks = applySieve' applyDeck recipDecks
, recipLooms = applySieve' applyLoom recipLooms , recipLooms = applySieve' applyLoom recipLooms
, recipProjects = applySieve' applyProject recipProjects , recipProjects = applySieve' applyProject recipProjects
, recipFactories = applySieve' applyFactory recipFactories
} }
where where
applySieve applySieve
@ -843,6 +881,17 @@ localRecipSieve' sieve allowPeople allowOthers routes = RecipientRoutes
then Nothing then Nothing
else Just (rkhid, merged) else Just (rkhid, merged)
applyFactory _ (This _) = Nothing
applyFactory rkhid (That r) =
if allowOthers && routeFactory r
then Just (rkhid, FactoryRoutes True False)
else Nothing
applyFactory rkhid (These (FactoryRoutes r' rf') (FactoryRoutes r rf)) =
let merged = FactoryRoutes (r && (r' || allowOthers)) (rf && rf')
in if merged == FactoryRoutes False False
then Nothing
else Just (rkhid, merged)
data ParsedAudience u = ParsedAudience data ParsedAudience u = ParsedAudience
{ paudLocalRecips :: RecipientRoutes { paudLocalRecips :: RecipientRoutes
, paudRemoteActors :: [(Authority u, NonEmpty LocalURI)] , paudRemoteActors :: [(Authority u, NonEmpty LocalURI)]

View file

@ -156,6 +156,8 @@ data AppSettings = AppSettings
-- | SMTP server details for sending email, and other email related -- | SMTP server details for sending email, and other email related
-- details. If set to 'Nothing', no email will be sent. -- details. If set to 'Nothing', no email will be sent.
, appMail :: Maybe MailSettings , appMail :: Maybe MailSettings
-- | People's usernames who are allowed to create Factory actors
, appCanCreateFactories :: [Text]
-- | Whether to support federation. This includes: -- | Whether to support federation. This includes:
-- --
@ -254,6 +256,7 @@ instance FromJSON AppSettings where
appAccounts <- o .: "max-accounts" appAccounts <- o .: "max-accounts"
appEmailVerification <- o .:? "email-verification" .!= not defaultDev appEmailVerification <- o .:? "email-verification" .!= not defaultDev
appMail <- o .:? "mail" appMail <- o .:? "mail"
appCanCreateFactories <- o .:? "can-create-factories" .!= []
appFederation <- o .:? "federation" .!= False appFederation <- o .:? "federation" .!= False
appCapabilitySigningKeyFile <- o .: "capability-signing-key" appCapabilitySigningKeyFile <- o .: "capability-signing-key"

View file

@ -17,15 +17,20 @@
module Vervis.Web.Actor module Vervis.Web.Actor
( getInbox ( getInbox
, getInbox' , getInbox'
, getInbox''
, postInbox , postInbox
, getOutbox , getOutbox
, getOutbox'
, getOutboxItem , getOutboxItem
, getOutboxItem'
, getFollowersCollection , getFollowersCollection
, getActorFollowersCollection , getActorFollowersCollection
, getActorFollowersCollection'
, getFollowingCollection , getFollowingCollection
, handleRobotInbox , handleRobotInbox
, serveInstanceKey , serveInstanceKey
, servePerActorKey , servePerActorKey
, servePerActorKey''
) )
where where
@ -80,7 +85,6 @@ import qualified Database.Esqueleto as E
import Control.Concurrent.Actor hiding (Actor) import Control.Concurrent.Actor hiding (Actor)
import Database.Persist.JSON import Database.Persist.JSON
import Network.FedURI import Network.FedURI
import Web.ActivityPub hiding (Project (..), ActorLocal (..))
import Yesod.ActivityPub import Yesod.ActivityPub
import Yesod.Auth.Unverified import Yesod.Auth.Unverified
import Yesod.FedURI import Yesod.FedURI
@ -90,6 +94,7 @@ import Yesod.RenderSource
import qualified Control.Concurrent.Actor as CCA import qualified Control.Concurrent.Actor as CCA
import qualified Crypto.ActorKey as AK import qualified Crypto.ActorKey as AK
import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local import Control.Monad.Trans.Except.Local
import Data.Aeson.Local import Data.Aeson.Local
@ -143,11 +148,15 @@ objectId o =
getInbox = getInbox' actorInbox getInbox = getInbox' actorInbox
getInbox' grabInbox here actor hash = do getInbox' grabInbox here actor hash =
getInbox'' grabInbox here (pure . actor) hash
getInbox'' grabInbox here getActorID hash = do
key <- decodeKeyHashid404 hash key <- decodeKeyHashid404 hash
(total, pages, mpage) <- runDB $ do (total, pages, mpage) <- runDB $ do
inboxID <- do inboxID <- do
actorID <- actor <$> get404 key rec <- get404 key
actorID <- getActorID rec
grabInbox <$> getJust actorID grabInbox <$> getJust actorID
getPageAndNavCount getPageAndNavCount
(countItems inboxID) (countItems inboxID)
@ -161,37 +170,37 @@ getInbox' grabInbox here actor hash = do
selectRep $ selectRep $
case mpage of case mpage of
Nothing -> do Nothing -> do
provideAP $ pure $ Doc host $ Collection AP.provideAP $ pure $ AP.Doc host $ AP.Collection
{ collectionId = encodeRouteLocal here' { AP.collectionId = encodeRouteLocal here'
, collectionType = CollectionTypeOrdered , AP.collectionType = AP.CollectionTypeOrdered
, collectionTotalItems = Just total , AP.collectionTotalItems = Just total
, collectionCurrent = Nothing , AP.collectionCurrent = Nothing
, collectionFirst = Just $ pageUrl 1 , AP.collectionFirst = Just $ pageUrl 1
, collectionLast = Just $ pageUrl pages , AP.collectionLast = Just $ pageUrl pages
, collectionItems = [] :: [Text] , AP.collectionItems = [] :: [Text]
, collectionContext = Nothing , AP.collectionContext = Nothing
} }
provideRep (redirectFirstPage here' :: Handler Html) provideRep (redirectFirstPage here' :: Handler Html)
Just (items, navModel) -> do Just (items, navModel) -> do
let current = nmCurrent navModel let current = nmCurrent navModel
provideAP $ pure $ Doc host $ CollectionPage AP.provideAP $ pure $ AP.Doc host $ AP.CollectionPage
{ collectionPageId = pageUrl current { AP.collectionPageId = pageUrl current
, collectionPageType = CollectionPageTypeOrdered , AP.collectionPageType = AP.CollectionPageTypeOrdered
, collectionPageTotalItems = Nothing , AP.collectionPageTotalItems = Nothing
, collectionPageCurrent = Just $ pageUrl current , AP.collectionPageCurrent = Just $ pageUrl current
, collectionPageFirst = Just $ pageUrl 1 , AP.collectionPageFirst = Just $ pageUrl 1
, collectionPageLast = Just $ pageUrl pages , AP.collectionPageLast = Just $ pageUrl pages
, collectionPagePartOf = encodeRouteLocal here' , AP.collectionPagePartOf = encodeRouteLocal here'
, collectionPagePrev = , AP.collectionPagePrev =
if current > 1 if current > 1
then Just $ pageUrl $ current - 1 then Just $ pageUrl $ current - 1
else Nothing else Nothing
, collectionPageNext = , AP.collectionPageNext =
if current < pages if current < pages
then Just $ pageUrl $ current + 1 then Just $ pageUrl $ current + 1
else Nothing else Nothing
, collectionPageStartIndex = Nothing , AP.collectionPageStartIndex = Nothing
, collectionPageItems = map (view _1) items , AP.collectionPageItems = map (view _1) items
} }
provideRep $ do provideRep $ do
let pageNav = navWidget navModel let pageNav = navWidget navModel
@ -262,7 +271,8 @@ postInbox
TVar (M.HashMap GroupId (ActorRef Group)), TVar (M.HashMap GroupId (ActorRef Group)),
TVar (M.HashMap DeckId (ActorRef Deck)), TVar (M.HashMap DeckId (ActorRef Deck)),
TVar (M.HashMap LoomId (ActorRef Loom)), TVar (M.HashMap LoomId (ActorRef Loom)),
TVar (M.HashMap RepoId (ActorRef Vervis.Model.Repo))] TVar (M.HashMap RepoId (ActorRef Vervis.Model.Repo)),
TVar (M.HashMap FactoryId (ActorRef Factory))]
l'0 l'0
, H.HOccurs' , H.HOccurs'
(TVar (M.HashMap (Key a) (ActorRef a))) (TVar (M.HashMap (Key a) (ActorRef a)))
@ -272,7 +282,8 @@ postInbox
TVar (M.HashMap GroupId (ActorRef Group)), TVar (M.HashMap GroupId (ActorRef Group)),
TVar (M.HashMap DeckId (ActorRef Deck)), TVar (M.HashMap DeckId (ActorRef Deck)),
TVar (M.HashMap LoomId (ActorRef Loom)), TVar (M.HashMap LoomId (ActorRef Loom)),
TVar (M.HashMap RepoId (ActorRef Vervis.Model.Repo))] TVar (M.HashMap RepoId (ActorRef Vervis.Model.Repo)),
TVar (M.HashMap FactoryId (ActorRef Factory))]
) )
=> (Key a -> LocalActorBy Key) -> Key a -> Handler () => (Key a -> LocalActorBy Key) -> Key a -> Handler ()
postInbox toLA recipID = do postInbox toLA recipID = do
@ -290,11 +301,11 @@ postInbox toLA recipID = do
parseAuthenticatedLocalActivityURI parseAuthenticatedLocalActivityURI
authorByKey authorByKey
(AP.activityId $ actbActivity body) (AP.activityId $ actbActivity body)
actorID <- do actorID <- runDBExcept $ do
ment <- lift $ runDB $ getLocalActorEntity authorByKey ment <- lift $ getLocalActorEntity authorByKey
case ment of case ment of
Nothing -> throwE "Author not found in DB" Nothing -> throwE "Author not found in DB"
Just ent -> return $ localActorID ent Just ent -> lift $ grabLocalActorID ent
return (authorByKey, actorID, outboxItemID) return (authorByKey, actorID, outboxItemID)
ActivityAuthRemote author -> Right <$> do ActivityAuthRemote author -> Right <$> do
luActivity <- luActivity <-
@ -333,11 +344,14 @@ postInbox toLA recipID = do
throwE "'actor' actor and 'id' actor mismatch" throwE "'actor' actor and 'id' actor mismatch"
return outboxItemID return outboxItemID
getOutbox here itemRoute grabActorID hash = do getOutbox here itemRoute grabActorID hash =
getOutbox' here itemRoute (pure . grabActorID) hash
getOutbox' here itemRoute grabActorID hash = do
key <- decodeKeyHashid404 hash key <- decodeKeyHashid404 hash
(total, pages, mpage) <- runDB $ do (total, pages, mpage) <- runDB $ do
outboxID <- do outboxID <- do
actorID <- grabActorID <$> get404 key actorID <- grabActorID =<< get404 key
actorOutbox <$> getJust actorID actorOutbox <$> getJust actorID
let countAllItems = count [OutboxItemOutbox ==. outboxID] let countAllItems = count [OutboxItemOutbox ==. outboxID]
selectItems off lim = selectList [OutboxItemOutbox ==. outboxID] [Desc OutboxItemId, OffsetBy off, LimitTo lim] selectItems off lim = selectList [OutboxItemOutbox ==. outboxID] [Desc OutboxItemId, OffsetBy off, LimitTo lim]
@ -351,37 +365,37 @@ getOutbox here itemRoute grabActorID hash = do
selectRep $ selectRep $
case mpage of case mpage of
Nothing -> do Nothing -> do
provideAP $ pure $ Doc host $ Collection AP.provideAP $ pure $ AP.Doc host $ AP.Collection
{ collectionId = encodeRouteLocal here' { AP.collectionId = encodeRouteLocal here'
, collectionType = CollectionTypeOrdered , AP.collectionType = AP.CollectionTypeOrdered
, collectionTotalItems = Just total , AP.collectionTotalItems = Just total
, collectionCurrent = Nothing , AP.collectionCurrent = Nothing
, collectionFirst = Just $ pageUrl 1 , AP.collectionFirst = Just $ pageUrl 1
, collectionLast = Just $ pageUrl pages , AP.collectionLast = Just $ pageUrl pages
, collectionItems = [] :: [Text] , AP.collectionItems = [] :: [Text]
, collectionContext = Nothing , AP.collectionContext = Nothing
} }
provideRep (redirectFirstPage here' :: Handler Html) provideRep (redirectFirstPage here' :: Handler Html)
Just (items, navModel) -> do Just (items, navModel) -> do
let current = nmCurrent navModel let current = nmCurrent navModel
provideAP $ pure $ Doc host $ CollectionPage AP.provideAP $ pure $ AP.Doc host $ AP.CollectionPage
{ collectionPageId = pageUrl current { AP.collectionPageId = pageUrl current
, collectionPageType = CollectionPageTypeOrdered , AP.collectionPageType = AP.CollectionPageTypeOrdered
, collectionPageTotalItems = Nothing , AP.collectionPageTotalItems = Nothing
, collectionPageCurrent = Just $ pageUrl current , AP.collectionPageCurrent = Just $ pageUrl current
, collectionPageFirst = Just $ pageUrl 1 , AP.collectionPageFirst = Just $ pageUrl 1
, collectionPageLast = Just $ pageUrl pages , AP.collectionPageLast = Just $ pageUrl pages
, collectionPagePartOf = encodeRouteLocal here' , AP.collectionPagePartOf = encodeRouteLocal here'
, collectionPagePrev = , AP.collectionPagePrev =
if current > 1 if current > 1
then Just $ pageUrl $ current - 1 then Just $ pageUrl $ current - 1
else Nothing else Nothing
, collectionPageNext = , AP.collectionPageNext =
if current < pages if current < pages
then Just $ pageUrl $ current + 1 then Just $ pageUrl $ current + 1
else Nothing else Nothing
, collectionPageStartIndex = Nothing , AP.collectionPageStartIndex = Nothing
, collectionPageItems = map (persistJSONObject . outboxItemActivity . entityVal) items , AP.collectionPageItems = map (persistJSONObject . outboxItemActivity . entityVal) items
} }
provideRep $ do provideRep $ do
let pageNav = navWidget navModel let pageNav = navWidget navModel
@ -389,12 +403,15 @@ getOutbox here itemRoute grabActorID hash = do
hashItem <- getEncodeKeyHashid hashItem <- getEncodeKeyHashid
defaultLayout $(widgetFile "person/outbox") defaultLayout $(widgetFile "person/outbox")
getOutboxItem here actor topicHash itemHash = do getOutboxItem here actor topicHash itemHash =
getOutboxItem' here (pure . actor) topicHash itemHash
getOutboxItem' here actor topicHash itemHash = do
topicID <- decodeKeyHashid404 topicHash topicID <- decodeKeyHashid404 topicHash
itemID <- decodeKeyHashid404 itemHash itemID <- decodeKeyHashid404 itemHash
body <- runDB $ do body <- runDB $ do
outboxID <- do outboxID <- do
actorID <- actor <$> get404 topicID actorID <- actor =<< get404 topicID
actorOutbox <$> getJust actorID actorOutbox <$> getJust actorID
item <- get404 itemID item <- get404 itemID
unless (outboxItemOutbox item == outboxID) notFound unless (outboxItemOutbox item == outboxID) notFound
@ -405,6 +422,7 @@ getOutboxItem here actor topicHash itemHash = do
getLocalActors getLocalActors
:: [ActorId] -> ReaderT SqlBackend Handler [LocalActorBy Key] :: [ActorId] -> ReaderT SqlBackend Handler [LocalActorBy Key]
getLocalActors actorIDs = do getLocalActors actorIDs = do
resourceIDs <- selectKeysList [ResourceActor <-. actorIDs] []
localActors <- localActors <-
concat <$> sequenceA concat <$> sequenceA
[ map LocalActorPerson <$> [ map LocalActorPerson <$>
@ -419,6 +437,8 @@ getLocalActors actorIDs = do
selectKeysList [LoomActor <-. actorIDs] [] selectKeysList [LoomActor <-. actorIDs] []
, map LocalActorProject <$> , map LocalActorProject <$>
selectKeysList [ProjectActor <-. actorIDs] [] selectKeysList [ProjectActor <-. actorIDs] []
, map LocalActorFactory <$>
selectKeysList [FactoryResource <-. resourceIDs] []
] ]
case compare (length localActors) (length actorIDs) of case compare (length localActors) (length actorIDs) of
LT -> error "Found actor ID not used by any specific actor" LT -> error "Found actor ID not used by any specific actor"
@ -453,26 +473,29 @@ getFollowersCollection here getFsid = do
encodeRouteLocal <- getEncodeRouteLocal encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome encodeRouteHome <- getEncodeRouteHome
hashActor <- getHashLocalActor hashActor <- getHashLocalActor
let followersAP = Collection let followersAP = AP.Collection
{ collectionId = encodeRouteLocal here { AP.collectionId = encodeRouteLocal here
, collectionType = CollectionTypeUnordered , AP.collectionType = AP.CollectionTypeUnordered
, collectionTotalItems = Just $ l + r , AP.collectionTotalItems = Just $ l + r
, collectionCurrent = Nothing , AP.collectionCurrent = Nothing
, collectionFirst = Nothing , AP.collectionFirst = Nothing
, collectionLast = Nothing , AP.collectionLast = Nothing
, collectionItems = , AP.collectionItems =
map (encodeRouteHome . renderLocalActor . hashActor) locals ++ map (encodeRouteHome . renderLocalActor . hashActor) locals ++
map (uncurry ObjURI . bimap E.unValue E.unValue) remotes map (uncurry ObjURI . bimap E.unValue E.unValue) remotes
, collectionContext = Nothing , AP.collectionContext = Nothing
} }
provideHtmlAndAP followersAP $ redirectToPrettyJSON here provideHtmlAndAP followersAP $ redirectToPrettyJSON here
getActorFollowersCollection here actor hash = do getActorFollowersCollection here actor hash =
getActorFollowersCollection' here (pure . actor) hash
getActorFollowersCollection' here actor hash = do
key <- decodeKeyHashid404 hash key <- decodeKeyHashid404 hash
getFollowersCollection (here hash) (getFsid key) getFollowersCollection (here hash) (getFsid key)
where where
getFsid key = do getFsid key = do
actorID <- actor <$> get404 key actorID <- actor =<< get404 key
actorFollowers <$> getJust actorID actorFollowers <$> getJust actorID
getFollowingCollection here actor hash = do getFollowingCollection here actor hash = do
@ -500,15 +523,15 @@ getFollowingCollection here actor hash = do
encodeRouteLocal <- getEncodeRouteLocal encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome encodeRouteHome <- getEncodeRouteHome
let here' = here hash let here' = here hash
followingAP = Collection followingAP = AP.Collection
{ collectionId = encodeRouteLocal here' { AP.collectionId = encodeRouteLocal here'
, collectionType = CollectionTypeUnordered , AP.collectionType = AP.CollectionTypeUnordered
, collectionTotalItems = Just $ localTotal + length remotes , AP.collectionTotalItems = Just $ localTotal + length remotes
, collectionCurrent = Nothing , AP.collectionCurrent = Nothing
, collectionFirst = Nothing , AP.collectionFirst = Nothing
, collectionLast = Nothing , AP.collectionLast = Nothing
, collectionItems = map encodeRouteHome locals ++ remotes , AP.collectionItems = map encodeRouteHome locals ++ remotes
, collectionContext = Nothing , AP.collectionContext = Nothing
} }
provideHtmlAndAP followingAP $ redirectToPrettyJSON here' provideHtmlAndAP followingAP $ redirectToPrettyJSON here'
where where
@ -531,7 +554,7 @@ handleRobotInbox
-> ActivityBody -> ActivityBody
-> Maybe (RecipientRoutes, ByteString) -> Maybe (RecipientRoutes, ByteString)
-> LocalURI -> LocalURI
-> SpecificActivity URIMode -> AP.SpecificActivity URIMode
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text)) -> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
) )
-> UTCTime -> UTCTime
@ -544,13 +567,13 @@ handleRobotInbox recipByHash handleSpecific now auth body = do
ActivityAuthLocal _ -> throwE "Got a forwarded local activity, I don't need those" ActivityAuthLocal _ -> throwE "Got a forwarded local activity, I don't need those"
ActivityAuthRemote ra -> return ra ActivityAuthRemote ra -> return ra
luActivity <- luActivity <-
fromMaybeE (activityId $ actbActivity body) "Activity without 'id'" fromMaybeE (AP.activityId $ actbActivity body) "Activity without 'id'"
localRecips <- do localRecips <- do
mrecips <- parseAudience $ activityAudience $ actbActivity body mrecips <- parseAudience $ AP.activityAudience $ actbActivity body
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients" paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
msig <- checkForwarding recipByHash msig <- checkForwarding recipByHash
let mfwd = (localRecips,) <$> msig let mfwd = (localRecips,) <$> msig
handleSpecific now remoteAuthor body mfwd luActivity (activitySpecific $ actbActivity body) handleSpecific now remoteAuthor body mfwd luActivity (AP.activitySpecific $ actbActivity body)
actorKeyAP actorKeyAP
:: ( MonadSite m, SiteEnv m ~ site :: ( MonadSite m, SiteEnv m ~ site
@ -602,11 +625,21 @@ servePerActorKey
-> KeyHashid holder -> KeyHashid holder
-> KeyHashid SigKey -> KeyHashid SigKey
-> Handler TypedContent -> Handler TypedContent
servePerActorKey holderActor localActorHolder holderHash keyHash = do servePerActorKey holderActor localActorHolder holderHash keyHash =
servePerActorKey'' (pure . holderActor) localActorHolder holderHash keyHash
servePerActorKey''
:: (PersistRecordBackend holder SqlBackend, ToBackendKey SqlBackend holder)
=> (holder -> AppDB ActorId)
-> (KeyHashid holder -> LocalActorBy KeyHashid)
-> KeyHashid holder
-> KeyHashid SigKey
-> Handler TypedContent
servePerActorKey'' holderActor localActorHolder holderHash keyHash = do
holderID <- decodeKeyHashid404 holderHash holderID <- decodeKeyHashid404 holderHash
keyID <- decodeKeyHashid404 keyHash keyID <- decodeKeyHashid404 keyHash
akey <- runDB $ do akey <- runDB $ do
actorID <- holderActor <$> get404 holderID actorID <- holderActor =<< get404 holderID
SigKey actorID' akey <- get404 keyID SigKey actorID' akey <- get404 keyID
unless (actorID' == actorID) notFound unless (actorID' == actorID) notFound
return akey return akey

View file

@ -75,6 +75,7 @@ personLinkFedW (Right (inztance, object, actor)) =
AP.ActorTypePatchTracker -> '+' AP.ActorTypePatchTracker -> '+'
AP.ActorTypeProject -> '$' AP.ActorTypeProject -> '$'
AP.ActorTypeTeam -> '&' AP.ActorTypeTeam -> '&'
AP.ActorTypeFactory -> '*'
AP.ActorTypeOther _ -> '?' AP.ActorTypeOther _ -> '?'
followW :: Route App -> Route App -> FollowerSetId -> Widget followW :: Route App -> Route App -> FollowerSetId -> Widget

View file

@ -17,6 +17,7 @@ module Vervis.Widget.Tracker
( deckNavW ( deckNavW
, loomNavW , loomNavW
, projectNavW , projectNavW
, factoryNavW
, componentLinkFedW , componentLinkFedW
, projectLinkFedW , projectLinkFedW
, groupLinkFedW , groupLinkFedW
@ -81,6 +82,11 @@ groupNavW (Entity groupID group) actor = do
groupHash <- encodeKeyHashid groupID groupHash <- encodeKeyHashid groupID
$(widgetFile "group/nav") $(widgetFile "group/nav")
factoryNavW :: Entity Factory -> Actor -> Widget
factoryNavW (Entity factoryID factory) actor = do
factoryHash <- encodeKeyHashid factoryID
$(widgetFile "factory/nav")
componentLinkW :: ComponentBy Key -> Actor -> Widget componentLinkW :: ComponentBy Key -> Actor -> Widget
componentLinkW (ComponentRepo k) actor = do componentLinkW (ComponentRepo k) actor = do
h <- encodeKeyHashid k h <- encodeKeyHashid k
@ -155,6 +161,12 @@ actorLinkW (LocalActorGroup k) actor = do
<a href=@{GroupR h}> <a href=@{GroupR h}>
&#{keyHashidText h} #{actorName actor} &#{keyHashidText h} #{actorName actor}
|] |]
actorLinkW (LocalActorFactory k) actor = do
h <- encodeKeyHashid k
[whamlet|
<a href=@{FactoryR h}>
*#{keyHashidText h} #{actorName actor}
|]
actorLinkFedW actorLinkFedW
:: Either (LocalActorBy Key, Actor) (Instance, RemoteObject, RemoteActor) :: Either (LocalActorBy Key, Actor) (Instance, RemoteObject, RemoteActor)
@ -188,6 +200,7 @@ remoteActorLinkW (inztance, object, actor) = do
AP.ActorTypePatchTracker -> '+' AP.ActorTypePatchTracker -> '+'
AP.ActorTypeProject -> '$' AP.ActorTypeProject -> '$'
AP.ActorTypeTeam -> '&' AP.ActorTypeTeam -> '&'
AP.ActorTypeFactory -> '*'
AP.ActorTypeOther _ -> '?' AP.ActorTypeOther _ -> '?'
personPermitsForResourceW personPermitsForResourceW

View file

@ -57,6 +57,7 @@ module Web.ActivityPub
, ResourceWithCollections (..) , ResourceWithCollections (..)
, Project (..) , Project (..)
, Team (..) , Team (..)
, Factory (..)
-- * Content objects -- * Content objects
, Note (..) , Note (..)
@ -493,6 +494,7 @@ data ActorType
| ActorTypePatchTracker | ActorTypePatchTracker
| ActorTypeProject | ActorTypeProject
| ActorTypeTeam | ActorTypeTeam
| ActorTypeFactory
| ActorTypeOther Text | ActorTypeOther Text
deriving Eq deriving Eq
@ -508,6 +510,7 @@ actorTypeIsResource = \case
ActorTypePatchTracker -> True ActorTypePatchTracker -> True
ActorTypeProject -> True ActorTypeProject -> True
ActorTypeTeam -> True ActorTypeTeam -> True
ActorTypeFactory -> True
_ -> False _ -> False
actorTypeIsResourceNT t = actorTypeIsResource t && t /= ActorTypeTeam actorTypeIsResourceNT t = actorTypeIsResource t && t /= ActorTypeTeam
@ -520,6 +523,7 @@ parseActorType t
| t == "PatchTracker" = ActorTypePatchTracker | t == "PatchTracker" = ActorTypePatchTracker
| t == "Project" = ActorTypeProject | t == "Project" = ActorTypeProject
| t == "Team" = ActorTypeTeam | t == "Team" = ActorTypeTeam
| t == "Factory" = ActorTypeFactory
| otherwise = ActorTypeOther t | otherwise = ActorTypeOther t
renderActorType :: ActorType -> Text renderActorType :: ActorType -> Text
@ -530,6 +534,7 @@ renderActorType = \case
ActorTypePatchTracker -> "PatchTracker" ActorTypePatchTracker -> "PatchTracker"
ActorTypeProject -> "Project" ActorTypeProject -> "Project"
ActorTypeTeam -> "Team" ActorTypeTeam -> "Team"
ActorTypeFactory -> "Factory"
ActorTypeOther t -> t ActorTypeOther t -> t
instance FromJSON ActorType where instance FromJSON ActorType where
@ -1093,6 +1098,27 @@ instance ActivityPub Team where
<> "members" .= ObjURI h members <> "members" .= ObjURI h members
<> "teamResources" .= ObjURI h resources <> "teamResources" .= ObjURI h resources
data Factory u = Factory
{ factoryActor :: Actor u
, factoryCollabs :: LocalURI
, factoryTeams :: LocalURI
}
instance ActivityPub Factory where
jsonldContext _ = [as2Context, secContext, forgeContext]
parseObject o = do
(h, a) <- parseObject o
unless (actorType (actorDetail a) == ActorTypeFactory) $
fail "Actor type isn't Factory"
fmap (h,) $
Factory a
<$> withAuthorityO h (o .: "collaborators")
<*> withAuthorityO h (o .: "teams")
toSeries h (Factory actor collabs teams)
= toSeries h actor
<> "collaborators" .= ObjURI h collabs
<> "teams" .= ObjURI h teams
data Audience u = Audience data Audience u = Audience
{ audienceTo :: [ObjURI u] { audienceTo :: [ObjURI u]
, audienceBto :: [ObjURI u] , audienceBto :: [ObjURI u]
@ -1978,6 +2004,7 @@ data CreateObject u
| CreatePatchTracker ActorDetail (NonEmpty (ObjURI u)) (Maybe (Authority u, ActorLocal u)) | CreatePatchTracker ActorDetail (NonEmpty (ObjURI u)) (Maybe (Authority u, ActorLocal u))
| CreateProject ActorDetail (Maybe (Authority u, ActorLocal u)) | CreateProject ActorDetail (Maybe (Authority u, ActorLocal u))
| CreateTeam ActorDetail (Maybe (Authority u, ActorLocal u)) | CreateTeam ActorDetail (Maybe (Authority u, ActorLocal u))
| CreateFactory ActorDetail (Maybe (Authority u, ActorLocal u))
parseCreateObject :: UriMode u => Object -> Parser (CreateObject u) parseCreateObject :: UriMode u => Object -> Parser (CreateObject u)
parseCreateObject o parseCreateObject o
@ -2010,6 +2037,11 @@ parseCreateObject o
fail "type isn't Team" fail "type isn't Team"
ml <- parseActorLocal o ml <- parseActorLocal o
return $ CreateTeam d ml return $ CreateTeam d ml
<|> do f <- parseActorDetail o
unless (actorType f == ActorTypeFactory) $
fail "type isn't Factory"
ml <- parseActorLocal o
return $ CreateFactory f ml
encodeCreateObject :: UriMode u => CreateObject u -> Series encodeCreateObject :: UriMode u => CreateObject u -> Series
encodeCreateObject (CreateNote h note) = toSeries h note encodeCreateObject (CreateNote h note) = toSeries h note
@ -2028,6 +2060,8 @@ encodeCreateObject (CreateProject d ml) =
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
encodeCreateObject (CreateTeam d ml) = encodeCreateObject (CreateTeam d ml) =
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
encodeCreateObject (CreateFactory d ml) =
encodeActorDetail d <> maybe mempty (uncurry encodeActorLocal) ml
data Create u = Create data Create u = Create
{ createObject :: CreateObject u { createObject :: CreateObject u
@ -2049,6 +2083,7 @@ parseCreate o a luActor = do
CreatePatchTracker _ _ _ -> return () CreatePatchTracker _ _ _ -> return ()
CreateProject _ _ -> return () CreateProject _ _ -> return ()
CreateTeam _ _ -> return () CreateTeam _ _ -> return ()
CreateFactory _ _ -> return ()
Create obj <$> o .:? "target" Create obj <$> o .:? "target"
encodeCreate :: UriMode u => Create u -> Series encodeCreate :: UriMode u => Create u -> Series

View file

@ -75,6 +75,8 @@ $# #forgefed @ Libera Chat
<ul> <ul>
$forall (Entity personID person, Entity _ actor) <- people $forall (Entity personID person, Entity _ actor) <- people
<li> <li>
$if canCreateFactories person
<span>👑
<a href=@{PersonR $ hashPerson personID}> <a href=@{PersonR $ hashPerson personID}>
~#{username2text $ personUsername person} #{actorName actor} ~#{username2text $ personUsername person} #{actorName actor}

View file

@ -13,11 +13,13 @@ $# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>. $# <http://creativecommons.org/publicdomain/zero/1.0/>.
<header> <header>
$maybe (Entity _ person, hash, verified, unread) <- mperson $maybe (Entity _ person, hash, verified, unread, can) <- mperson
<div> <div>
$if verified $if verified
<span> <span>
[You are logged in as [You are logged in as
$if can
<span>👑
<span .username>#{personLogin person}</span>] <span .username>#{personLogin person}</span>]
$if unread > 0 $if unread > 0
<span> <span>

View file

@ -0,0 +1,35 @@
$# This file is part of Vervis.
$#
$# Written in 2019, 2022, 2023, 2024 by fr33domlover <fr33domlover@riseup.net>.
$#
$# ♡ Copying is an act of love. Please copy, reuse and share.
$#
$# The author(s) have dedicated all copyright and related and neighboring
$# rights to this software to the public domain worldwide. This software is
$# distributed without any warranty.
$#
$# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
<div>
<span>
[[ 🏗
<a href=@{FactoryR factoryHash}>
*#{keyHashidText factoryHash} #{actorName actor}
]] ::
<span>
<a href=@{FactoryInboxR factoryHash}>
[📥 Inbox]
<span>
<a href=@{FactoryOutboxR factoryHash}>
[📤 Outbox]
<span>
<a href=@{FactoryErrboxR factoryHash}>
[💥 Errbox]
<span>
<a href=@{FactoryFollowersR factoryHash}>
[🐤 Followers]
<span>
<a href=@{FactoryCollabsR factoryHash}>
[🤝 Collaborators]

View file

@ -0,0 +1,18 @@
$# This file is part of Vervis.
$#
$# Written in 2016, 2022, 2024 by fr33domlover <fr33domlover@riseup.net>.
$#
$# ♡ Copying is an act of love. Please copy, reuse and share.
$#
$# The author(s) have dedicated all copyright and related and neighboring
$# rights to this software to the public domain worldwide. This software is
$# distributed without any warranty.
$#
$# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
<form method=POST action=@{FactoryNewR} enctype=#{enctype}>
^{widget}
<div class="submit">
<input type="submit">

View file

@ -45,6 +45,10 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
<li> <li>
<a href=@{LoomNewR}> <a href=@{LoomNewR}>
patch tracker patch tracker
$if canCreateFactories
<li>
<a href=@{FactoryNewR}>
factory
<li> <li>
<a href=@{PublishOfferMergeR}> <a href=@{PublishOfferMergeR}>
Open a merge request Open a merge request
@ -106,6 +110,13 @@ $# Comment on a ticket or merge request
<li> <li>
^{item i} ^{item i}
<h2>Your factories
<ul>
$forall i <- factories
<li>
^{item i}
<h2>Your resources of unrecognized type <h2>Your resources of unrecognized type
<ul> <ul>

View file

@ -167,6 +167,13 @@ Komponent
UniqueKomponent resource UniqueKomponent resource
Factory
resource ResourceId
create OutboxItemId
UniqueFactory resource
UniqueFactoryCreate create
-- ========================================================================= -- -- ========================================================================= --
-- Delivery -- Delivery
-- ========================================================================= -- -- ========================================================================= --

View file

@ -436,3 +436,29 @@
/projects/#ProjectKeyHashid/approve-team/#SquadId ProjectApproveTeamR POST /projects/#ProjectKeyHashid/approve-team/#SquadId ProjectApproveTeamR POST
/projects/#ProjectKeyHashid/remove-team/#SquadId ProjectRemoveTeamR POST /projects/#ProjectKeyHashid/remove-team/#SquadId ProjectRemoveTeamR POST
/projects/#ProjectKeyHashid/teams/#SquadUsStartKeyHashid/live ProjectTeamLiveR GET /projects/#ProjectKeyHashid/teams/#SquadUsStartKeyHashid/live ProjectTeamLiveR GET
---- Factory -----------------------------------------------------------------
/factories/#FactoryKeyHashid FactoryR GET
/factories/#FactoryKeyHashid/inbox FactoryInboxR GET POST
/factories/#FactoryKeyHashid/errbox FactoryErrboxR GET
/factories/#FactoryKeyHashid/outbox FactoryOutboxR GET
/factories/#FactoryKeyHashid/outbox/#OutboxItemKeyHashid FactoryOutboxItemR GET
/factories/#FactoryKeyHashid/followers FactoryFollowersR GET
/factories/#FactoryKeyHashid/messages/#LocalMessageKeyHashid FactoryMessageR GET
/new-factory FactoryNewR GET POST
/factories/#FactoryKeyHashid/stamps/#SigKeyKeyHashid FactoryStampR GET
/factories/#FactoryKeyHashid/collabs FactoryCollabsR GET
/factories/#FactoryKeyHashid/invite FactoryInviteR POST
/factories/#FactoryKeyHashid/remove/#CollabId FactoryRemoveR POST
/factories/#FactoryKeyHashid/teams FactoryTeamsR GET
/factories/#FactoryKeyHashid/add-team FactoryAddTeamR POST
/factories/#FactoryKeyHashid/approve-team/#SquadId FactoryApproveTeamR POST
/factories/#FactoryKeyHashid/remove-team/#SquadId FactoryRemoveTeamR POST
/factories/#FactoryKeyHashid/teams/#SquadUsStartKeyHashid/live FactoryTeamLiveR GET

View file

@ -157,6 +157,7 @@ library
Vervis.Actor2 Vervis.Actor2
Vervis.Actor.Common Vervis.Actor.Common
Vervis.Actor.Deck Vervis.Actor.Deck
Vervis.Actor.Factory
Vervis.Actor.Group Vervis.Actor.Group
Vervis.Actor.Loom Vervis.Actor.Loom
Vervis.Actor.Person Vervis.Actor.Person
@ -213,6 +214,7 @@ library
Vervis.Handler.Cloth Vervis.Handler.Cloth
Vervis.Handler.Common Vervis.Handler.Common
Vervis.Handler.Deck Vervis.Handler.Deck
Vervis.Handler.Factory
-- Vervis.Handler.Git -- Vervis.Handler.Git
Vervis.Handler.Group Vervis.Handler.Group
--Vervis.Handler.Inbox --Vervis.Handler.Inbox