refactor Fedi.Routes
This commit is contained in:
parent
2632c44d0e
commit
cd5d615609
8 changed files with 256 additions and 200 deletions
|
@ -18,10 +18,17 @@ library
|
||||||
exposed-modules:
|
exposed-modules:
|
||||||
Fedi
|
Fedi
|
||||||
Fedi.Helpers
|
Fedi.Helpers
|
||||||
Fedi.Routes
|
|
||||||
Fedi.Types
|
Fedi.Types
|
||||||
Fedi.UserDetails
|
Fedi.UserDetails
|
||||||
Fedi.Webfinger
|
Fedi.Webfinger
|
||||||
|
|
||||||
|
Fedi.Routes
|
||||||
|
Fedi.Routes.Helpers
|
||||||
|
Fedi.Routes.User
|
||||||
|
Fedi.Routes.Inbox
|
||||||
|
Fedi.Routes.Outbox
|
||||||
|
Fedi.Routes.Notes
|
||||||
|
Fedi.Routes.Follow
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends:
|
build-depends:
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
module Fedi.Routes where
|
module Fedi.Routes
|
||||||
|
( module Fedi.Routes
|
||||||
|
, module Export
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
import Data.Aeson qualified as A
|
|
||||||
import Data.ByteString qualified as BS
|
|
||||||
import Data.ByteString.Lazy qualified as BSL
|
|
||||||
import Fedi.Helpers
|
|
||||||
import Fedi.Types
|
|
||||||
import Fedi.UserDetails
|
import Fedi.UserDetails
|
||||||
import Fedi.Webfinger
|
|
||||||
import Web.Twain qualified as Twain
|
import Web.Twain qualified as Twain
|
||||||
import Web.Twain.Types qualified as Twain
|
|
||||||
|
import Fedi.Routes.Helpers as Export
|
||||||
|
import Fedi.Routes.User as Export
|
||||||
|
import Fedi.Routes.Inbox as Export
|
||||||
|
import Fedi.Routes.Outbox as Export
|
||||||
|
import Fedi.Routes.Notes as Export
|
||||||
|
import Fedi.Routes.Follow as Export
|
||||||
|
|
||||||
-- * Routes
|
-- * Routes
|
||||||
|
|
||||||
|
@ -29,194 +33,3 @@ routes details =
|
||||||
, Twain.get matchWebfinger do
|
, Twain.get matchWebfinger do
|
||||||
handleWebfinger details
|
handleWebfinger details
|
||||||
]
|
]
|
||||||
|
|
||||||
jsonLD :: BSL.ByteString -> Twain.Response
|
|
||||||
jsonLD =
|
|
||||||
Twain.raw
|
|
||||||
Twain.status200
|
|
||||||
[(Twain.hContentType, "application/activity+json; charset=utf-8")]
|
|
||||||
|
|
||||||
-- * Create
|
|
||||||
|
|
||||||
matchCreateNote :: UserDetails -> Twain.PathPattern
|
|
||||||
matchCreateNote details = fromString ("/" <> details.username <> "/notes/:note_id/create")
|
|
||||||
|
|
||||||
handleCreateNote :: UserDetails -> [Create] -> Twain.ResponderM a
|
|
||||||
handleCreateNote details items = do
|
|
||||||
noteId <- Twain.param "note_id"
|
|
||||||
let
|
|
||||||
createUrl =
|
|
||||||
"https://"
|
|
||||||
<> details.domain
|
|
||||||
<> "/"
|
|
||||||
<> details.username
|
|
||||||
<> "/notes/"
|
|
||||||
<> noteId
|
|
||||||
<> "/create"
|
|
||||||
let
|
|
||||||
content =
|
|
||||||
find (\create -> create.id == Just (ObjectId createUrl)) items
|
|
||||||
Twain.send $ jsonLD (A.encode content)
|
|
||||||
|
|
||||||
-- * Note
|
|
||||||
|
|
||||||
matchNote :: UserDetails -> Twain.PathPattern
|
|
||||||
matchNote details = fromString ("/" <> details.username <> "/notes/:note_id")
|
|
||||||
|
|
||||||
handleNote :: UserDetails -> [Note] -> Twain.ResponderM a
|
|
||||||
handleNote details items = do
|
|
||||||
noteId <- Twain.param "note_id"
|
|
||||||
let
|
|
||||||
noteUrl =
|
|
||||||
"https://"
|
|
||||||
<> details.domain
|
|
||||||
<> "/"
|
|
||||||
<> details.username
|
|
||||||
<> "/notes/"
|
|
||||||
<> noteId
|
|
||||||
let
|
|
||||||
content =
|
|
||||||
find (\note -> note.id == Just (ObjectId noteUrl)) items
|
|
||||||
Twain.send $ jsonLD (A.encode content)
|
|
||||||
|
|
||||||
-- * User
|
|
||||||
|
|
||||||
matchUser :: UserDetails -> Twain.PathPattern
|
|
||||||
matchUser details = fromString ("/" <> details.username)
|
|
||||||
|
|
||||||
handleUser :: UserDetails -> Twain.ResponderM a
|
|
||||||
handleUser details = do
|
|
||||||
let
|
|
||||||
content = makeActor details
|
|
||||||
Twain.send $ jsonLD (A.encode content)
|
|
||||||
|
|
||||||
-- * Webfinger
|
|
||||||
|
|
||||||
matchWebfinger :: Twain.PathPattern
|
|
||||||
matchWebfinger = "/.well-known/webfinger"
|
|
||||||
|
|
||||||
handleWebfinger :: UserDetails -> Twain.ResponderM b
|
|
||||||
handleWebfinger details = do
|
|
||||||
resource <- Twain.param "resource"
|
|
||||||
let
|
|
||||||
webfinger = makeWebfinger details
|
|
||||||
if resource == ppSubject webfinger.subject
|
|
||||||
then do
|
|
||||||
Twain.send $ jsonLD (A.encode webfinger)
|
|
||||||
else do
|
|
||||||
Twain.next
|
|
||||||
|
|
||||||
-- * Outbox
|
|
||||||
|
|
||||||
matchOutbox :: UserDetails -> Twain.PathPattern
|
|
||||||
matchOutbox details =
|
|
||||||
fromString ("/" <> details.username <> "/outbox")
|
|
||||||
|
|
||||||
handleOutbox :: UserDetails -> [AnyActivity] -> Twain.ResponderM b
|
|
||||||
handleOutbox details items = do
|
|
||||||
isPage <- Twain.queryParamMaybe "page"
|
|
||||||
let
|
|
||||||
outboxUrl =
|
|
||||||
"https://"
|
|
||||||
<> details.domain
|
|
||||||
<> "/"
|
|
||||||
<> details.username
|
|
||||||
<> "/outbox"
|
|
||||||
response =
|
|
||||||
case isPage of
|
|
||||||
Just True ->
|
|
||||||
let
|
|
||||||
empty = emptyOrderedCollectionPage outboxUrl
|
|
||||||
content :: OutboxPage
|
|
||||||
content =
|
|
||||||
empty
|
|
||||||
{ id = Just $ ObjectId $ outboxUrl <> "?page=true"
|
|
||||||
, otype =
|
|
||||||
empty.otype
|
|
||||||
{ ctype =
|
|
||||||
empty.otype.ctype
|
|
||||||
{ partOf = outboxUrl
|
|
||||||
, porderedItems = items
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in
|
|
||||||
A.encode content
|
|
||||||
_ ->
|
|
||||||
let
|
|
||||||
content :: Outbox
|
|
||||||
content =
|
|
||||||
emptyOrderedCollection
|
|
||||||
{ id = Just $ ObjectId outboxUrl
|
|
||||||
, summary = Just $ fromString $ details.username <> "'s notes"
|
|
||||||
, otype =
|
|
||||||
emptyOrderedCollection.otype
|
|
||||||
{ ctype =
|
|
||||||
emptyOrderedCollection.otype.ctype
|
|
||||||
{ orderedItems = items
|
|
||||||
}
|
|
||||||
, first = Just $ outboxUrl <> "?page=true"
|
|
||||||
, last = Just $ outboxUrl <> "?page=true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in
|
|
||||||
A.encode content
|
|
||||||
Twain.send $ jsonLD response
|
|
||||||
|
|
||||||
-- * Inbox
|
|
||||||
|
|
||||||
matchInbox :: UserDetails -> Twain.PathPattern
|
|
||||||
matchInbox details =
|
|
||||||
fromString ("/" <> details.username <> "/inbox")
|
|
||||||
|
|
||||||
handleInbox :: (AnyActivity -> Twain.ResponderM Twain.Response) -> Twain.ResponderM b
|
|
||||||
handleInbox handle = do
|
|
||||||
activity <- Twain.fromBody
|
|
||||||
response <- handle activity
|
|
||||||
Twain.send response
|
|
||||||
|
|
||||||
-- * Followers
|
|
||||||
|
|
||||||
matchFollowers :: UserDetails -> Twain.PathPattern
|
|
||||||
matchFollowers details =
|
|
||||||
fromString ("/" <> details.username <> "/followers")
|
|
||||||
|
|
||||||
handleFollowers :: UserDetails -> Twain.ResponderM b
|
|
||||||
handleFollowers details = do
|
|
||||||
let
|
|
||||||
collection :: Collection ()
|
|
||||||
collection =
|
|
||||||
emptyUnorderedCollection
|
|
||||||
{ id = Just $ ObjectId $ actorUrl details <> "/followers"
|
|
||||||
, summary = Just $ fromString $ details.username <> "'s followers"
|
|
||||||
}
|
|
||||||
Twain.send $ jsonLD (A.encode collection)
|
|
||||||
|
|
||||||
-- * Following
|
|
||||||
|
|
||||||
matchFollowing :: UserDetails -> Twain.PathPattern
|
|
||||||
matchFollowing details =
|
|
||||||
fromString ("/" <> details.username <> "/following")
|
|
||||||
|
|
||||||
handleFollowing :: UserDetails -> Twain.ResponderM b
|
|
||||||
handleFollowing details = do
|
|
||||||
let
|
|
||||||
collection :: Collection ()
|
|
||||||
collection =
|
|
||||||
emptyUnorderedCollection
|
|
||||||
{ id = Just $ ObjectId $ actorUrl details <> "/following"
|
|
||||||
, summary = Just $ fromString $ details.username <> " is following"
|
|
||||||
}
|
|
||||||
Twain.send $ jsonLD (A.encode collection)
|
|
||||||
|
|
||||||
-- * Other stuff
|
|
||||||
|
|
||||||
checkContentTypeAccept :: Twain.Request -> Bool
|
|
||||||
checkContentTypeAccept request =
|
|
||||||
case lookup Twain.hAccept request.requestHeaders of
|
|
||||||
Just bs ->
|
|
||||||
("application/activity+json" `BS.isInfixOf` bs)
|
|
||||||
|| ( ("application/ld+json" `BS.isInfixOf` bs)
|
|
||||||
&& ("profile=\"https://www.w3.org/ns/activitystreams\"" `BS.isInfixOf` bs)
|
|
||||||
)
|
|
||||||
Nothing -> False
|
|
||||||
|
|
43
src/Fedi/Routes/Follow.hs
Normal file
43
src/Fedi/Routes/Follow.hs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
module Fedi.Routes.Follow where
|
||||||
|
|
||||||
|
import Data.Aeson qualified as A
|
||||||
|
import Fedi.Helpers
|
||||||
|
import Fedi.Types
|
||||||
|
import Fedi.UserDetails
|
||||||
|
import Fedi.Routes.Helpers
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
import Web.Twain.Types qualified as Twain
|
||||||
|
|
||||||
|
-- * Followers
|
||||||
|
|
||||||
|
matchFollowers :: UserDetails -> Twain.PathPattern
|
||||||
|
matchFollowers details =
|
||||||
|
fromString ("/" <> details.username <> "/followers")
|
||||||
|
|
||||||
|
handleFollowers :: UserDetails -> Twain.ResponderM b
|
||||||
|
handleFollowers details = do
|
||||||
|
let
|
||||||
|
collection :: Collection ()
|
||||||
|
collection =
|
||||||
|
emptyUnorderedCollection
|
||||||
|
{ id = Just $ ObjectId $ actorUrl details <> "/followers"
|
||||||
|
, summary = Just $ fromString $ details.username <> "'s followers"
|
||||||
|
}
|
||||||
|
Twain.send $ jsonLD (A.encode collection)
|
||||||
|
|
||||||
|
-- * Following
|
||||||
|
|
||||||
|
matchFollowing :: UserDetails -> Twain.PathPattern
|
||||||
|
matchFollowing details =
|
||||||
|
fromString ("/" <> details.username <> "/following")
|
||||||
|
|
||||||
|
handleFollowing :: UserDetails -> Twain.ResponderM b
|
||||||
|
handleFollowing details = do
|
||||||
|
let
|
||||||
|
collection :: Collection ()
|
||||||
|
collection =
|
||||||
|
emptyUnorderedCollection
|
||||||
|
{ id = Just $ ObjectId $ actorUrl details <> "/following"
|
||||||
|
, summary = Just $ fromString $ details.username <> " is following"
|
||||||
|
}
|
||||||
|
Twain.send $ jsonLD (A.encode collection)
|
21
src/Fedi/Routes/Helpers.hs
Normal file
21
src/Fedi/Routes/Helpers.hs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
module Fedi.Routes.Helpers where
|
||||||
|
|
||||||
|
import Data.ByteString qualified as BS
|
||||||
|
import Data.ByteString.Lazy qualified as BSL
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
|
||||||
|
jsonLD :: BSL.ByteString -> Twain.Response
|
||||||
|
jsonLD =
|
||||||
|
Twain.raw
|
||||||
|
Twain.status200
|
||||||
|
[(Twain.hContentType, "application/activity+json; charset=utf-8")]
|
||||||
|
|
||||||
|
checkContentTypeAccept :: Twain.Request -> Bool
|
||||||
|
checkContentTypeAccept request =
|
||||||
|
case lookup Twain.hAccept request.requestHeaders of
|
||||||
|
Just bs ->
|
||||||
|
("application/activity+json" `BS.isInfixOf` bs)
|
||||||
|
|| ( ("application/ld+json" `BS.isInfixOf` bs)
|
||||||
|
&& ("profile=\"https://www.w3.org/ns/activitystreams\"" `BS.isInfixOf` bs)
|
||||||
|
)
|
||||||
|
Nothing -> False
|
19
src/Fedi/Routes/Inbox.hs
Normal file
19
src/Fedi/Routes/Inbox.hs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module Fedi.Routes.Inbox where
|
||||||
|
|
||||||
|
import Fedi.Types
|
||||||
|
import Fedi.UserDetails
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
import Web.Twain.Types qualified as Twain
|
||||||
|
|
||||||
|
-- * Inbox
|
||||||
|
|
||||||
|
matchInbox :: UserDetails -> Twain.PathPattern
|
||||||
|
matchInbox details =
|
||||||
|
fromString ("/" <> details.username <> "/inbox")
|
||||||
|
|
||||||
|
handleInbox :: (AnyActivity -> Twain.ResponderM Twain.Response) -> Twain.ResponderM b
|
||||||
|
handleInbox handle = do
|
||||||
|
activity <- Twain.fromBody
|
||||||
|
-- sig <- Twain.header "Signature"
|
||||||
|
response <- handle activity
|
||||||
|
Twain.send response
|
51
src/Fedi/Routes/Notes.hs
Normal file
51
src/Fedi/Routes/Notes.hs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
module Fedi.Routes.Notes where
|
||||||
|
|
||||||
|
import Data.Aeson qualified as A
|
||||||
|
import Fedi.Types
|
||||||
|
import Fedi.UserDetails
|
||||||
|
import Fedi.Routes.Helpers
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
import Web.Twain.Types qualified as Twain
|
||||||
|
|
||||||
|
-- * Create
|
||||||
|
|
||||||
|
matchCreateNote :: UserDetails -> Twain.PathPattern
|
||||||
|
matchCreateNote details = fromString ("/" <> details.username <> "/notes/:note_id/create")
|
||||||
|
|
||||||
|
handleCreateNote :: UserDetails -> [Create] -> Twain.ResponderM a
|
||||||
|
handleCreateNote details items = do
|
||||||
|
noteId <- Twain.param "note_id"
|
||||||
|
let
|
||||||
|
createUrl =
|
||||||
|
"https://"
|
||||||
|
<> details.domain
|
||||||
|
<> "/"
|
||||||
|
<> details.username
|
||||||
|
<> "/notes/"
|
||||||
|
<> noteId
|
||||||
|
<> "/create"
|
||||||
|
let
|
||||||
|
content =
|
||||||
|
find (\create -> create.id == Just (ObjectId createUrl)) items
|
||||||
|
Twain.send $ jsonLD (A.encode content)
|
||||||
|
|
||||||
|
-- * Note
|
||||||
|
|
||||||
|
matchNote :: UserDetails -> Twain.PathPattern
|
||||||
|
matchNote details = fromString ("/" <> details.username <> "/notes/:note_id")
|
||||||
|
|
||||||
|
handleNote :: UserDetails -> [Note] -> Twain.ResponderM a
|
||||||
|
handleNote details items = do
|
||||||
|
noteId <- Twain.param "note_id"
|
||||||
|
let
|
||||||
|
noteUrl =
|
||||||
|
"https://"
|
||||||
|
<> details.domain
|
||||||
|
<> "/"
|
||||||
|
<> details.username
|
||||||
|
<> "/notes/"
|
||||||
|
<> noteId
|
||||||
|
let
|
||||||
|
content =
|
||||||
|
find (\note -> note.id == Just (ObjectId noteUrl)) items
|
||||||
|
Twain.send $ jsonLD (A.encode content)
|
66
src/Fedi/Routes/Outbox.hs
Normal file
66
src/Fedi/Routes/Outbox.hs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
module Fedi.Routes.Outbox where
|
||||||
|
|
||||||
|
import Data.Aeson qualified as A
|
||||||
|
import Fedi.Helpers
|
||||||
|
import Fedi.Types
|
||||||
|
import Fedi.UserDetails
|
||||||
|
import Fedi.Routes.Helpers
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
import Web.Twain.Types qualified as Twain
|
||||||
|
|
||||||
|
-- * Outbox
|
||||||
|
|
||||||
|
matchOutbox :: UserDetails -> Twain.PathPattern
|
||||||
|
matchOutbox details =
|
||||||
|
fromString ("/" <> details.username <> "/outbox")
|
||||||
|
|
||||||
|
handleOutbox :: UserDetails -> [AnyActivity] -> Twain.ResponderM b
|
||||||
|
handleOutbox details items = do
|
||||||
|
isPage <- Twain.queryParamMaybe "page"
|
||||||
|
let
|
||||||
|
outboxUrl =
|
||||||
|
"https://"
|
||||||
|
<> details.domain
|
||||||
|
<> "/"
|
||||||
|
<> details.username
|
||||||
|
<> "/outbox"
|
||||||
|
response =
|
||||||
|
case isPage of
|
||||||
|
Just True ->
|
||||||
|
let
|
||||||
|
empty = emptyOrderedCollectionPage outboxUrl
|
||||||
|
content :: OutboxPage
|
||||||
|
content =
|
||||||
|
empty
|
||||||
|
{ id = Just $ ObjectId $ outboxUrl <> "?page=true"
|
||||||
|
, otype =
|
||||||
|
empty.otype
|
||||||
|
{ ctype =
|
||||||
|
empty.otype.ctype
|
||||||
|
{ partOf = outboxUrl
|
||||||
|
, porderedItems = items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in
|
||||||
|
A.encode content
|
||||||
|
_ ->
|
||||||
|
let
|
||||||
|
content :: Outbox
|
||||||
|
content =
|
||||||
|
emptyOrderedCollection
|
||||||
|
{ id = Just $ ObjectId outboxUrl
|
||||||
|
, summary = Just $ fromString $ details.username <> "'s notes"
|
||||||
|
, otype =
|
||||||
|
emptyOrderedCollection.otype
|
||||||
|
{ ctype =
|
||||||
|
emptyOrderedCollection.otype.ctype
|
||||||
|
{ orderedItems = items
|
||||||
|
}
|
||||||
|
, first = Just $ outboxUrl <> "?page=true"
|
||||||
|
, last = Just $ outboxUrl <> "?page=true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in
|
||||||
|
A.encode content
|
||||||
|
Twain.send $ jsonLD response
|
36
src/Fedi/Routes/User.hs
Normal file
36
src/Fedi/Routes/User.hs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
module Fedi.Routes.User where
|
||||||
|
|
||||||
|
import Data.Aeson qualified as A
|
||||||
|
import Fedi.Helpers
|
||||||
|
import Fedi.UserDetails
|
||||||
|
import Fedi.Webfinger
|
||||||
|
import Fedi.Routes.Helpers
|
||||||
|
import Web.Twain qualified as Twain
|
||||||
|
import Web.Twain.Types qualified as Twain
|
||||||
|
|
||||||
|
-- * User
|
||||||
|
|
||||||
|
matchUser :: UserDetails -> Twain.PathPattern
|
||||||
|
matchUser details = fromString ("/" <> details.username)
|
||||||
|
|
||||||
|
handleUser :: UserDetails -> Twain.ResponderM a
|
||||||
|
handleUser details = do
|
||||||
|
let
|
||||||
|
content = makeActor details
|
||||||
|
Twain.send $ jsonLD (A.encode content)
|
||||||
|
|
||||||
|
-- * Webfinger
|
||||||
|
|
||||||
|
matchWebfinger :: Twain.PathPattern
|
||||||
|
matchWebfinger = "/.well-known/webfinger"
|
||||||
|
|
||||||
|
handleWebfinger :: UserDetails -> Twain.ResponderM b
|
||||||
|
handleWebfinger details = do
|
||||||
|
resource <- Twain.param "resource"
|
||||||
|
let
|
||||||
|
webfinger = makeWebfinger details
|
||||||
|
if resource == ppSubject webfinger.subject
|
||||||
|
then do
|
||||||
|
Twain.send $ jsonLD (A.encode webfinger)
|
||||||
|
else do
|
||||||
|
Twain.next
|
Loading…
Add table
Reference in a new issue