Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/resolver #2923

Merged
merged 4 commits into from
Mar 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions apps/mobile/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ config.resolver.nodeModulesPaths = [
path.resolve(__dirname, "../../node_modules"),
]

config.resolver.resolveRequest = (context, moduleName, platform) => {
const result = context.resolveRequest(context, moduleName, platform)
if (result.type === "sourceFile") {
const lastDotIndex = result.filePath.lastIndexOf(".")
const mobilePath = `${result.filePath.slice(0, lastDotIndex)}.mobile${result.filePath.slice(lastDotIndex)}`
const file = context.fileSystemLookup(mobilePath)
if (file.exists) {
return {
...result,
filePath: mobilePath,
}
} else {
return result
}
}
return result
}

module.exports = wrapWithReanimatedMetroConfig(
withNativeWind(config, { input: "./src/global.css" }),
)
11 changes: 0 additions & 11 deletions apps/mobile/src/atoms/env.ts

This file was deleted.

14 changes: 0 additions & 14 deletions apps/mobile/src/constants/env.ts

This file was deleted.

6 changes: 3 additions & 3 deletions apps/mobile/src/lib/api-fetch.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/* eslint-disable no-console */
import type { AppType } from "@follow/shared"
import { env } from "@follow/shared/src/env"
import { FetchError, ofetch } from "ofetch"

import { userActions } from "../store/user/store"
import { getCookie } from "./auth"
import { getApiUrl } from "./env"

const { hc } = require("hono/dist/cjs/client/client") as typeof import("hono/client")

export const apiFetch = ofetch.create({
retry: false,

baseURL: getApiUrl(),
baseURL: env.VITE_API_URL,
onRequest: async (ctx) => {
const { options, request } = ctx
if (__DEV__) {
Expand Down Expand Up @@ -47,7 +47,7 @@ export const apiFetch = ofetch.create({
},
})

export const apiClient = hc<AppType>(getApiUrl(), {
export const apiClient = hc<AppType>(env.VITE_API_URL, {
fetch: async (input: any, options = {}) =>
apiFetch(input.toString(), options).catch((err) => {
throw err
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { expoClient } from "@better-auth/expo/client"
import { env } from "@follow/shared/src/env"
import { useQuery } from "@tanstack/react-query"
import { twoFactorClient } from "better-auth/client/plugins"
import { createAuthClient } from "better-auth/react"
import type * as better_call from "better-call"
import * as SecureStore from "expo-secure-store"

import { whoamiQueryKey } from "../store/user/hooks"
import { getApiUrl } from "./env"
import { queryClient } from "./query-client"

const storagePrefix = "follow_auth"
export const cookieKey = `${storagePrefix}_cookie`
export const sessionTokenKey = "__Secure-better-auth.session_token"

const authClient = createAuthClient({
baseURL: `${getApiUrl()}/better-auth`,
baseURL: `${env.VITE_API_URL}/better-auth`,
plugins: [
twoFactorClient(),
{
Expand Down
12 changes: 0 additions & 12 deletions apps/mobile/src/lib/env.ts

This file was deleted.

24 changes: 1 addition & 23 deletions apps/mobile/src/lib/image.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,4 @@
const imageRefererMatches = [
{
url: /^https:\/\/\w+\.sinaimg.cn/,
referer: "https://weibo.com",
},
{
url: /^https:\/\/i\.pximg\.net/,
referer: "https://www.pixiv.net",
},
{
url: /^https:\/\/cdnfile\.sspai\.com/,
referer: "https://sspai.com",
},
{
url: /^https:\/\/(?:\w|-)+\.cdninstagram\.com/,
referer: "https://www.instagram.com",
},
{
url: /^https:\/\/sp1\.piokok\.com/,
referer: "https://www.piokok.com",
force: true,
},
]
import { imageRefererMatches } from "@follow/shared/src/image"

const isValidUrl = (url: string) => {
try {
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/src/modules/context-menu/lists.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { env } from "@follow/shared/src/env"
import type { FC, PropsWithChildren } from "react"
import { useMemo } from "react"
import { Alert, Clipboard } from "react-native"

import { ContextMenu } from "@/src/components/ui/context-menu"
import { getWebUrl } from "@/src/lib/env"
import { toast } from "@/src/lib/toast"
import { getList } from "@/src/store/list/getters"
import { useIsOwnList } from "@/src/store/list/hooks"
Expand Down Expand Up @@ -31,7 +31,7 @@ export const SubscriptionListItemContextMenu: FC<
const list = getList(id)
if (!list) return
toast.info("Link copied to clipboard")
Clipboard.setString(`${getWebUrl()}/share/lists/${list.id}`)
Clipboard.setString(`${env.VITE_WEB_URL}/share/lists/${list.id}`)
},
},
{
Expand Down
5 changes: 2 additions & 3 deletions apps/mobile/src/modules/screen/TimelineSelectorProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { env } from "@follow/shared/src/env"
import { useLocalSearchParams } from "expo-router"
import { useMemo } from "react"
import { Share, useAnimatedValue, View } from "react-native"
Expand All @@ -9,7 +10,6 @@ import { NavigationBlurEffectHeader } from "@/src/components/layouts/views/SafeN
import { UIBarButton } from "@/src/components/ui/button/UIBarButton"
import { TIMELINE_VIEW_SELECTOR_HEIGHT } from "@/src/constants/ui"
import { Share3CuteReIcon } from "@/src/icons/share_3_cute_re"
import { getWebUrl } from "@/src/lib/env"
import {
HomeLeftAction,
HomeSharedRightAction,
Expand Down Expand Up @@ -101,8 +101,7 @@ function FeedShareAction({ params }: { params: any }) {
onPress={() => {
const feed = getFeed(feedId)
if (!feed) return
const webUrl = getWebUrl()
const url = `${webUrl}/share/feeds/${feedId}`
const url = `${env.VITE_WEB_URL}/share/feeds/${feedId}`
Share.share({
message: `Check out ${feed.title} on Follow: ${url}`,
title: feed.title!,
Expand Down
67 changes: 67 additions & 0 deletions packages/shared/src/env.desktop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { createEnv } from "@t3-oss/env-core"
import { z } from "zod"

export const isDev =
"process" in globalThis ? process.env.NODE_ENV === "development" : import.meta.env.DEV
export const env = createEnv({
clientPrefix: "VITE_",
client: {
VITE_WEB_URL: z.string().url().default("https://app.follow.is"),
VITE_API_URL: z.string(),
VITE_DEV_PROXY: z.string().optional(),
VITE_SENTRY_DSN: z.string().optional(),
VITE_INBOXES_EMAIL: z.string().default("@follow.re"),
VITE_FIREBASE_CONFIG: z.string().optional(),

VITE_OPENPANEL_CLIENT_ID: z.string().optional(),
VITE_OPENPANEL_API_URL: z.string().url().optional(),

// For external, use api_url if you don't want to fill it in.
VITE_EXTERNAL_PROD_API_URL: z.string().optional(),
VITE_EXTERNAL_DEV_API_URL: z.string().optional(),
VITE_EXTERNAL_API_URL: z.string().optional(),
VITE_WEB_PROD_URL: z.string().optional(),
VITE_WEB_DEV_URL: z.string().optional(),
},

emptyStringAsUndefined: true,
runtimeEnv: getRuntimeEnv() as any,

skipValidation: "process" in globalThis ? process.env.VITEST === "true" : false,
})

function metaEnvIsEmpty() {
try {
return Object.keys(import.meta.env || {}).length === 0
} catch {
return true
}
}

function getRuntimeEnv() {
try {
if (metaEnvIsEmpty()) {
return process.env
}
return injectExternalEnv(import.meta.env)
} catch {
return process.env
}
}

declare const globalThis: any
function injectExternalEnv<T>(originEnv: T): T {
if (!("document" in globalThis)) {
return originEnv
}
const prefix = "__followEnv"
const env = globalThis[prefix]
if (!env) {
return originEnv
}

for (const key in env) {
originEnv[key as keyof T] = env[key]
}
return originEnv
}
24 changes: 24 additions & 0 deletions packages/shared/src/env.mobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const profile = "prod"

const appEndpointMap = {
prod: {
VITE_API_URL: "https://api.follow.is",
VITE_WEB_URL: "https://app.follow.is",
VITE_INBOXES_EMAIL: "@follow.re",
},
dev: {
VITE_API_URL: "https://api.dev.follow.is",
VITE_WEB_URL: "https://dev.follow.is",
VITE_INBOXES_EMAIL: "[email protected]",
},
staging: {
VITE_API_URL: "https://api.follow.is",
VITE_WEB_URL: "https://staging.follow.is",
VITE_INBOXES_EMAIL: "@follow.re",
},
}

export const env = {
VITE_WEB_URL: appEndpointMap[profile].VITE_WEB_URL,
VITE_API_URL: appEndpointMap[profile].VITE_API_URL,
}
82 changes: 19 additions & 63 deletions packages/shared/src/env.ts
Original file line number Diff line number Diff line change
@@ -1,67 +1,23 @@
import { createEnv } from "@t3-oss/env-core"
import { z } from "zod"

export const isDev =
"process" in globalThis ? process.env.NODE_ENV === "development" : import.meta.env.DEV
export const env = createEnv({
clientPrefix: "VITE_",
client: {
VITE_WEB_URL: z.string().url().default("https://app.follow.is"),
VITE_API_URL: z.string(),
VITE_DEV_PROXY: z.string().optional(),
VITE_SENTRY_DSN: z.string().optional(),
VITE_INBOXES_EMAIL: z.string().default("@follow.re"),
VITE_FIREBASE_CONFIG: z.string().optional(),

VITE_OPENPANEL_CLIENT_ID: z.string().optional(),
VITE_OPENPANEL_API_URL: z.string().url().optional(),

// For external, use api_url if you don't want to fill it in.
VITE_EXTERNAL_PROD_API_URL: z.string().optional(),
VITE_EXTERNAL_DEV_API_URL: z.string().optional(),
VITE_EXTERNAL_API_URL: z.string().optional(),
VITE_WEB_PROD_URL: z.string().optional(),
VITE_WEB_DEV_URL: z.string().optional(),
},

emptyStringAsUndefined: true,
runtimeEnv: getRuntimeEnv() as any,

skipValidation: "process" in globalThis ? process.env.VITEST === "true" : false,
})

function metaEnvIsEmpty() {
try {
return Object.keys(import.meta.env || {}).length === 0
} catch {
return true
}
export const envSchema = {
VITE_WEB_URL: z.string().url().default("https://app.follow.is"),
VITE_API_URL: z.string().default("https://api.follow.is"),
VITE_DEV_PROXY: z.string().optional(),
VITE_SENTRY_DSN: z.string().optional(),
VITE_INBOXES_EMAIL: z.string().default("@follow.re"),
VITE_FIREBASE_CONFIG: z.string().optional(),

VITE_OPENPANEL_CLIENT_ID: z.string().optional(),
VITE_OPENPANEL_API_URL: z.string().url().optional(),

// For external, use api_url if you don't want to fill it in.
VITE_EXTERNAL_PROD_API_URL: z.string().optional(),
VITE_EXTERNAL_DEV_API_URL: z.string().optional(),
VITE_EXTERNAL_API_URL: z.string().optional(),
VITE_WEB_PROD_URL: z.string().optional(),
VITE_WEB_DEV_URL: z.string().optional(),
}

function getRuntimeEnv() {
try {
if (metaEnvIsEmpty()) {
return process.env
}
return injectExternalEnv(import.meta.env)
} catch {
return process.env
}
}

declare const globalThis: any
function injectExternalEnv<T>(originEnv: T): T {
if (!("document" in globalThis)) {
return originEnv
}
const prefix = "__followEnv"
const env = globalThis[prefix]
if (!env) {
return originEnv
}

for (const key in env) {
originEnv[key as keyof T] = env[key]
}
return originEnv
}
export const isDev = false
export const env = z.object(envSchema).parse({})
4 changes: 2 additions & 2 deletions packages/shared/src/image.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { env } from "@follow/shared/env"
import { env } from "./env"

export const IMAGE_PROXY_URL = "https://webp.follow.is"

Expand All @@ -8,7 +8,7 @@ export const selfRefererMatches = [env.VITE_OPENPANEL_API_URL, IMAGE_PROXY_URL].

export const imageRefererMatches = [
{
url: /^https:\/\/\w+\.sinaimg.cn/,
url: /^https:\/\/\w+\.sinaimg\.cn/,
referer: "https://weibo.com",
},
{
Expand Down
Loading
Loading