haixunMaster/lib/threads-api/config.ts

97 lines
2.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Setting } from "@prisma/client";
export function normalizeThreadsAppId(value: string | null | undefined): string | undefined {
const trimmed = value?.trim();
if (!trimmed) return undefined;
if (!/^\d+$/.test(trimmed)) return undefined;
return trimmed;
}
/** Meta App 憑證僅從部署層 .env 讀取THREADS_APP_ID / THREADS_APP_SECRET。 */
export function getThreadsAppId(): string | undefined {
return normalizeThreadsAppId(process.env.THREADS_APP_ID);
}
export function getThreadsAppSecret(): string | undefined {
return process.env.THREADS_APP_SECRET?.trim() || undefined;
}
export function isThreadsAppConfigured(): boolean {
return !!(getThreadsAppId() && getThreadsAppSecret());
}
type ThreadsAccountAuth = {
threadsAccessToken: string | null;
threadsUserId: string | null;
};
export function accountHasThreadsToken(account: ThreadsAccountAuth | null): boolean {
return !!(account?.threadsAccessToken?.trim() && account?.threadsUserId?.trim());
}
export function getThreadsCredentials(
settings: Setting,
account: ThreadsAccountAuth | null,
overrideToken?: string | null
) {
const appId = getThreadsAppId();
const appSecret = getThreadsAppSecret();
const accessToken = (overrideToken ?? account?.threadsAccessToken)?.trim();
const userId = account?.threadsUserId?.trim();
if (!accessToken || !userId) return null;
return {
appId,
appSecret,
accessToken,
userId,
};
}
export function maskToken(token?: string | null): string | null {
if (!token) return null;
if (token.length <= 8) return "••••";
return `••••${token.slice(-6)}`;
}
export function normalizeAppUrl(value: string | null | undefined): string | undefined {
const trimmed = value?.trim();
if (!trimmed) return undefined;
try {
const url = new URL(trimmed);
if (!["http:", "https:"].includes(url.protocol)) return undefined;
return `${url.protocol}//${url.host}`.replace(/\/$/, "");
} catch {
return undefined;
}
}
export function getAppBaseUrl(
request?: Request,
appUrlOverride?: string | null
): string {
const fromSettings = normalizeAppUrl(appUrlOverride);
if (fromSettings) return fromSettings;
const fromEnv = normalizeAppUrl(process.env.APP_URL) || normalizeAppUrl(process.env.NEXT_PUBLIC_APP_URL);
if (fromEnv) return fromEnv;
if (request) {
const host = request.headers.get("x-forwarded-host") || request.headers.get("host");
const proto = request.headers.get("x-forwarded-proto") || "http";
if (host) return `${proto}://${host}`;
}
return "http://localhost:3000";
}
export function isPubliclyReachableUrl(url: string): boolean {
try {
const { hostname } = new URL(url);
return !["localhost", "127.0.0.1", "0.0.0.0"].includes(hostname);
} catch {
return false;
}
}