+
+
+ Unable to load this profile
+
+
+ This may be a temporary problem. Please try again.
+
+
+
+)
+
const UserHero = ({ user }: { user: UserProfile }) => {
const subscriptions = useUserSubscriptionsQuery(user.id)
diff --git a/apps/ssr/client/query/users.ts b/apps/ssr/client/query/users.ts
index f351e4cb8..a3f0318cb 100644
--- a/apps/ssr/client/query/users.ts
+++ b/apps/ssr/client/query/users.ts
@@ -2,7 +2,7 @@ import { followClient } from "@client/lib/api-fetch"
import { getProviders } from "@client/lib/auth"
import { getHydrateData } from "@client/lib/helper"
import type { LoginHydrateData } from "@client/pages/(login)/login/metadata"
-import { isBizId, sortByAlphabet } from "@follow/utils/utils"
+import { sortByAlphabet } from "@follow/utils/utils"
import type {
InboxSubscriptionResponse,
ListSubscriptionResponse,
@@ -10,6 +10,8 @@ import type {
} from "@follow-app/client-sdk"
import { useQuery } from "@tanstack/react-query"
+import { getUserProfile } from "../../src/lib/user-profile-params"
+
type GetUserSubscriptionsResponse = (
SubscriptionWithFeed | ListSubscriptionResponse | InboxSubscriptionResponse
)[]
@@ -69,13 +71,7 @@ export const useUserSubscriptionsQuery = (userId: string | undefined) => {
}
export const fetchUser = async (handleOrId: string | undefined) => {
- const handle = isBizId(handleOrId || "")
- ? handleOrId
- : `${handleOrId}`.startsWith("@")
- ? `${handleOrId}`.slice(1)
- : handleOrId
-
- const res = await followClient.api.profiles.getProfile({ id: handleOrId, handle })
+ const res = await getUserProfile(followClient, handleOrId)
return res.data
}
diff --git a/apps/ssr/src/lib/user-profile-params.test.ts b/apps/ssr/src/lib/user-profile-params.test.ts
new file mode 100644
index 000000000..1cbef864b
--- /dev/null
+++ b/apps/ssr/src/lib/user-profile-params.test.ts
@@ -0,0 +1,50 @@
+import type { FollowClient } from "@follow-app/client-sdk"
+import { describe, expect, it, vi } from "vitest"
+
+import { getUserProfile, resolveUserProfileParams } from "./user-profile-params"
+
+vi.mock("@follow/utils/utils", () => ({
+ isBizId: (value: string | undefined) => value === "41125409313095680",
+}))
+
+describe("resolveUserProfileParams", () => {
+ it("uses a handle without also sending it as an id", () => {
+ expect(resolveUserProfileParams("DIYgod")).toEqual({
+ id: undefined,
+ handle: "DIYgod",
+ })
+ })
+
+ it("removes a leading at sign from handles", () => {
+ expect(resolveUserProfileParams("@DIYgod")).toEqual({
+ id: undefined,
+ handle: "DIYgod",
+ })
+ })
+
+ it("uses a business id without also sending it as a handle", () => {
+ expect(resolveUserProfileParams("41125409313095680")).toEqual({
+ id: "41125409313095680",
+ handle: undefined,
+ })
+ })
+
+ it("uses the resolved parameters for the profile request", async () => {
+ const getProfile = vi.fn().mockResolvedValue({ data: { id: "profile-id" } })
+ const apiClient = {
+ api: {
+ profiles: {
+ getProfile,
+ },
+ },
+ } as unknown as FollowClient
+
+ await getUserProfile(apiClient, "DIYgod")
+
+ expect(getProfile).toHaveBeenCalledOnce()
+ expect(getProfile).toHaveBeenCalledWith({
+ id: undefined,
+ handle: "DIYgod",
+ })
+ })
+})
diff --git a/apps/ssr/src/lib/user-profile-params.ts b/apps/ssr/src/lib/user-profile-params.ts
new file mode 100644
index 000000000..6c8598be9
--- /dev/null
+++ b/apps/ssr/src/lib/user-profile-params.ts
@@ -0,0 +1,19 @@
+import { isBizId } from "@follow/utils/utils"
+import type { FollowClient } from "@follow-app/client-sdk"
+
+export const resolveUserProfileParams = (handleOrId: string | undefined) => {
+ if (isBizId(handleOrId || "")) {
+ return {
+ id: handleOrId,
+ handle: undefined,
+ }
+ }
+
+ return {
+ id: undefined,
+ handle: handleOrId?.startsWith("@") ? handleOrId.slice(1) : handleOrId,
+ }
+}
+
+export const getUserProfile = (apiClient: FollowClient, handleOrId: string | undefined) =>
+ apiClient.api.profiles.getProfile(resolveUserProfileParams(handleOrId))
diff --git a/apps/ssr/src/router/og/user.tsx b/apps/ssr/src/router/og/user.tsx
index d2d65ec9b..047dab559 100644
--- a/apps/ssr/src/router/og/user.tsx
+++ b/apps/ssr/src/router/og/user.tsx
@@ -1,17 +1,12 @@
-import { isBizId } from "@follow/utils/utils"
import type { FollowClient } from "@follow-app/client-sdk"
import * as React from "react"
import { renderToImage } from "../../lib/og/render-to-image"
+import { getUserProfile } from "../../lib/user-profile-params"
import { getImageBase64, OGAvatar, OGCanvas } from "./__base"
-export const renderUserOG = async (apiClient: FollowClient, id: string) => {
- const handle = isBizId(id || "") ? id : `${id}`.startsWith("@") ? `${id}`.slice(1) : id
-
- const user = await apiClient.api.profiles.getProfile({
- id,
- handle,
- })
+export const renderUserOG = async (apiClient: FollowClient, handleOrId: string) => {
+ const user = await getUserProfile(apiClient, handleOrId)
if (!user) {
throw 404
diff --git a/apps/ssr/wrangler.jsonc b/apps/ssr/wrangler.jsonc
index 97a7676b9..1e3a59f77 100644
--- a/apps/ssr/wrangler.jsonc
+++ b/apps/ssr/wrangler.jsonc
@@ -3,7 +3,11 @@
"name": "folo-ssr",
"main": "dist/worker/worker-entry.mjs",
"compatibility_date": "2026-02-01",
- "compatibility_flags": ["nodejs_compat"],
+ "compatibility_flags": [
+ "nodejs_compat",
+ // The SSR Worker fetches api.folo.is, another Worker Route in the same zone.
+ "global_fetch_strictly_public",
+ ],
"observability": {
"logs": {
"enabled": true,