diff --git a/db/federation/actor.go b/db/federation/actor.go
index ea9dddc4..6138ce37 100644
--- a/db/federation/actor.go
+++ b/db/federation/actor.go
@@ -247,8 +247,9 @@ func fetchRemoteActor(actor *core.Record, iri string, includeFollows bool) (*pub
req.Header.Add(k, v)
}
- dbPrivateKey := actor.GetString("private_key")
- if dbPrivateKey != "" {
+ if actor != nil && actor.GetString("private_key") != "" {
+ dbPrivateKey := actor.GetString("private_key")
+
algs := []httpsig.Algorithm{httpsig.RSA_SHA256}
postHeaders := []string{"(request-target)", "Date", "Digest", "Content-Type", "Host"}
expiresIn := 60
@@ -311,7 +312,6 @@ func FetchCollection(actor *core.Record, url string) (*pub.OrderedCollection, er
if len(encryptionKey) == 0 {
return nil, fmt.Errorf("POCKETBASE_ENCRYPTION_KEY not set")
}
-
req, _ := http.NewRequest("GET", url, nil)
headers := map[string]string{
@@ -354,9 +354,15 @@ func FetchCollection(actor *core.Record, url string) (*pub.OrderedCollection, er
}
resp, err := http.DefaultClient.Do(req)
- if err != nil || resp.StatusCode != http.StatusOK {
+ if err != nil {
return nil, fmt.Errorf("collection fetch failed for %s: %v", url, err)
}
+ if resp.StatusCode != http.StatusOK {
+ if resp.StatusCode == http.StatusNotFound {
+ return nil, fmt.Errorf("profile is private")
+ }
+ return nil, fmt.Errorf("collection fetch %s returned: %v", url, resp.StatusCode)
+ }
defer resp.Body.Close()
var collection pub.OrderedCollection
diff --git a/db/main.go b/db/main.go
index 43d0b041..6683a868 100644
--- a/db/main.go
+++ b/db/main.go
@@ -1069,9 +1069,13 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
iri := e.Request.URL.Query().Get("iri")
follows := e.Request.URL.Query().Get("follows") == "true"
- userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
- if err != nil {
- return err
+ var userActor *core.Record
+ var err error
+ if e.Auth != nil {
+ userActor, err = e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
+ if err != nil {
+ return err
+ }
}
var actor *core.Record
@@ -1088,7 +1092,7 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
} else if err != nil && actor != nil {
if err.Error() == "profile is private" {
// this is our own profile
- if actor.GetString("user") == e.Auth.Id {
+ if e.Auth != nil && actor.GetString("user") == e.Auth.Id {
return e.JSON(http.StatusOK, map[string]any{"actor": actor, "error": nil})
} else {
return e.JSON(http.StatusNotFound, map[string]any{"error": "profile is private"})
@@ -1119,9 +1123,12 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
return err
}
- userActor, err := e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
- if err != nil {
- return err
+ var userActor *core.Record
+ if e.Auth != nil {
+ userActor, err = e.App.FindFirstRecordByData("activitypub_actors", "user", e.Auth.Id)
+ if err != nil {
+ return err
+ }
}
url := actor.GetString(followType)
@@ -1131,6 +1138,9 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) {
}
collection, err := federation.FetchCollection(userActor, fmt.Sprintf("%s?page=%d", url, intPage))
if err != nil {
+ if err.Error() == "profile is private" {
+ return e.JSON(http.StatusNotFound, map[string]any{"error": "profile is private"})
+ }
return err
}
return e.JSON(http.StatusOK, collection)
diff --git a/web/src/lib/components/list/list_panel.svelte b/web/src/lib/components/list/list_panel.svelte
index c048876d..e95b3178 100644
--- a/web/src/lib/components/list/list_panel.svelte
+++ b/web/src/lib/components/list/list_panel.svelte
@@ -205,7 +205,7 @@
onmouseenter={() => handleTrailMouseEnter(trail, i)}
onmouseleave={() => handleTrailMouseLeave(trail, i)}
>
-
+
{/each}
diff --git a/web/src/routes/api/v1/activitypub/user/[handle]/+server.ts b/web/src/routes/api/v1/activitypub/user/[handle]/+server.ts
index 256c44f1..6fb8852a 100644
--- a/web/src/routes/api/v1/activitypub/user/[handle]/+server.ts
+++ b/web/src/routes/api/v1/activitypub/user/[handle]/+server.ts
@@ -20,7 +20,7 @@ export async function GET(event: RequestEvent) {
const [username, domain] = splitUsername(fullUsername, env.ORIGIN)
- const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`username:lower='${username?.toLowerCase()}'&&isLocal=true`)
+ const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`preferred_username:lower='${username?.toLowerCase()}'&&isLocal=true`)
const user: UserAnonymous = await event.locals.pb.collection("users_anonymous").getOne(actor.user!)
diff --git a/web/src/routes/api/v1/activitypub/user/[handle]/followers/+server.ts b/web/src/routes/api/v1/activitypub/user/[handle]/followers/+server.ts
index 49172de1..84f6eaec 100644
--- a/web/src/routes/api/v1/activitypub/user/[handle]/followers/+server.ts
+++ b/web/src/routes/api/v1/activitypub/user/[handle]/followers/+server.ts
@@ -25,7 +25,7 @@ export async function GET(event: RequestEvent) {
const [username, domain] = splitUsername(fullUsername, env.ORIGIN)
- const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`username:lower='${username?.toLowerCase()}'&&isLocal=true`)
+ const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`preferred_username:lower='${username?.toLowerCase()}'&&isLocal=true`)
const followers: ListResult = await event.locals.pb.collection("follows").getList(intPage, 10, { sort: "-created", filter: `followee='${actor.id}'&&status='accepted'`, expand: "follower" })
@@ -43,7 +43,7 @@ export async function GET(event: RequestEvent) {
...(hasNextPage ? { next: `${id}/outbox?page=${intPage + 1}` } : {}),
partOf: id + "/followers",
totalItems: followers.totalItems,
- orderedItems: followers.items.map(f => f.expand!.follower.iri)
+ orderedItems: followers.items.filter(f => f.expand?.follower !== undefined).map(f => f.expand!.follower.iri)
}
const headers = new Headers()
diff --git a/web/src/routes/api/v1/activitypub/user/[handle]/following/+server.ts b/web/src/routes/api/v1/activitypub/user/[handle]/following/+server.ts
index 4848f6a2..e3aa11e7 100644
--- a/web/src/routes/api/v1/activitypub/user/[handle]/following/+server.ts
+++ b/web/src/routes/api/v1/activitypub/user/[handle]/following/+server.ts
@@ -26,7 +26,7 @@ export async function GET(event: RequestEvent) {
const [username, domain] = splitUsername(fullUsername, env.ORIGIN)
- const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`username:lower='${username?.toLowerCase()}'&&isLocal=true`)
+ const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`preferred_username:lower='${username?.toLowerCase()}'&&isLocal=true`)
const followers: ListResult = await event.locals.pb.collection("follows").getList(intPage, 10, { sort: "-created", filter: `follower='${actor.id}'&&status='accepted'`, expand: "followee" })
@@ -44,7 +44,7 @@ export async function GET(event: RequestEvent) {
...(hasNextPage ? { next: `${id}/following?page=${intPage + 1}` } : {}),
partOf: id + "/following",
totalItems: followers.totalItems,
- orderedItems: followers.items.map(f => f.expand!.followee.iri)
+ orderedItems: followers.items.filter(f => f.expand?.followee !== undefined).map(f => f.expand!.followee.iri)
}
const headers = new Headers()
diff --git a/web/src/routes/api/v1/activitypub/user/[handle]/outbox/+server.ts b/web/src/routes/api/v1/activitypub/user/[handle]/outbox/+server.ts
index c0d40700..715f82c5 100644
--- a/web/src/routes/api/v1/activitypub/user/[handle]/outbox/+server.ts
+++ b/web/src/routes/api/v1/activitypub/user/[handle]/outbox/+server.ts
@@ -28,7 +28,7 @@ export async function GET(event: RequestEvent) {
const [username, domain] = splitUsername(fullUsername, env.ORIGIN)
- const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`username:lower='${username?.toLowerCase()}'&&isLocal=true`)
+ const actor: Actor = await event.locals.pb.collection("activitypub_actors").getFirstListItem(`preferred_username:lower='${username?.toLowerCase()}'&&isLocal=true`)
const filter = `actor='${actor.iri}'&&type='Create'${safeSearchParams.filter ? '&&' + safeSearchParams.filter : ''}`
const activities: ListResult = await event.locals.pb.collection("activitypub_activities").getList(page, perPage, { sort: safeSearchParams.sort ?? "-created", filter })
diff --git a/web/src/routes/api/v1/follow/+server.ts b/web/src/routes/api/v1/follow/+server.ts
index 69eaa2ab..5ccc771f 100644
--- a/web/src/routes/api/v1/follow/+server.ts
+++ b/web/src/routes/api/v1/follow/+server.ts
@@ -22,8 +22,21 @@ export async function GET(event: RequestEvent) {
const { actor }: { actor: Actor } = await event.locals.pb.send(`/activitypub/actor?resource=acct:${handle}`, { method: "GET", fetch: event.fetch, });
const page = event.url.searchParams.get("page") ?? "1"
-
- const followers: APOrderedCollectionPage = await event.locals.pb.send(`/activitypub/actor/${actor.id}/${type}?page=${page}`, { method: "GET", fetch: event.fetch, });
+
+ let followers: APOrderedCollectionPage;
+
+ // fetch followers locally to not run into auth issues with private profiles
+ if (actor.id === event.locals.user.actor) {
+ const r = await event.fetch(actor[type as "followers" | "following"]! + '?' + new URLSearchParams({ page }))
+
+ if (!r.ok) {
+ const errorResponse = await r.json()
+ throw new ClientResponseError({ status: r.status, response: errorResponse });
+ }
+ followers = await r.json()
+ } else {
+ followers = await event.locals.pb.send(`/activitypub/actor/${actor.id}/${type}?page=${page}`, { method: "GET", fetch: event.fetch, });
+ }
const followerActors: Actor[] = []
for (const f of followers.orderedItems ?? []) {
@@ -37,6 +50,7 @@ export async function GET(event: RequestEvent) {
}
+
const result: ListResult = {
items: followerActors,
page: parseInt(page),
diff --git a/web/src/routes/api/v1/profile/[handle]/trails/+server.ts b/web/src/routes/api/v1/profile/[handle]/trails/+server.ts
index 40b1492e..cf6e569d 100644
--- a/web/src/routes/api/v1/profile/[handle]/trails/+server.ts
+++ b/web/src/routes/api/v1/profile/[handle]/trails/+server.ts
@@ -32,6 +32,9 @@ export async function POST(event: RequestEvent) {
r.hits.forEach(h => {
h.thumbnail = `${origin}/api/v1/files/trails/${h.id}/${h.thumbnail}`;
h.domain = actor.domain
+ if(h.iri == '') {
+ h.iri = `${origin}/api/v1/trails/${h.id}`
+ }
})
}
diff --git a/web/src/routes/lists/[[handle]]/[[id]]/+page.svelte b/web/src/routes/lists/[[handle]]/[[id]]/+page.svelte
index aa69d86b..7b209e7a 100644
--- a/web/src/routes/lists/[[handle]]/[[id]]/+page.svelte
+++ b/web/src/routes/lists/[[handle]]/[[id]]/+page.svelte
@@ -373,7 +373,7 @@
{/if}
-