Add API endpoint links to home pages
This commit is contained in:
1 parent
dae5c121b7
commit
5a35fb2100
10 files changed
+153
No files matched your search
@@ -0,0 +1,118 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useSite } from '../context/SiteContext';
|
||||
|
||||
const SHARED_API_ENDPOINTS = [
|
||||
{
|
||||
id: 'world',
|
||||
labelKey: 'home.apiEndpointWorld',
|
||||
url: 'https://world.claudecoder.me',
|
||||
},
|
||||
{
|
||||
id: 'china',
|
||||
labelKey: 'home.apiEndpointChina',
|
||||
url: 'https://china.claudecoder.me',
|
||||
},
|
||||
];
|
||||
|
||||
const normalizeEndpoint = (value = '') => {
|
||||
const raw = String(value || '').trim();
|
||||
if (!raw) return '';
|
||||
if (/^https?:\/\//i.test(raw)) {
|
||||
return raw.replace(/\/+$/, '');
|
||||
}
|
||||
return `https://${raw.replace(/^\/+/, '').replace(/\/+$/, '')}`;
|
||||
};
|
||||
|
||||
const copyToClipboard = async (text) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
} catch (e) {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
ta.style.position = 'fixed';
|
||||
ta.style.opacity = '0';
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
}
|
||||
};
|
||||
|
||||
export default function ApiEndpoints() {
|
||||
const { t } = useTranslation();
|
||||
const { site } = useSite();
|
||||
|
||||
const siteEndpoint = useMemo(() => {
|
||||
const currentOrigin =
|
||||
typeof window !== 'undefined' ? window.location.origin : '';
|
||||
return normalizeEndpoint(site?.domain || currentOrigin);
|
||||
}, [site?.domain]);
|
||||
|
||||
const endpoints = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'site',
|
||||
label: t('home.apiEndpointSite'),
|
||||
url: siteEndpoint,
|
||||
},
|
||||
...SHARED_API_ENDPOINTS.map((endpoint) => ({
|
||||
...endpoint,
|
||||
label: t(endpoint.labelKey),
|
||||
apiOnly: true,
|
||||
})),
|
||||
].filter((endpoint) => endpoint.url),
|
||||
[siteEndpoint, t],
|
||||
);
|
||||
|
||||
const handleCopy = async (url) => {
|
||||
await copyToClipboard(url);
|
||||
toast.success(t('config.apiUrlCopied'));
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="max-w-5xl mx-auto px-6 pb-12">
|
||||
<div className="glass rounded-2xl p-5 md:p-6">
|
||||
<div className="mb-4 flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-page">
|
||||
{t('home.apiEndpointsTitle')}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-page-muted">
|
||||
{t('home.apiEndpointsDesc')}
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-[11px] text-page-muted">
|
||||
{t('home.apiEndpointClickToCopy')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-3 md:grid-cols-3">
|
||||
{endpoints.map((endpoint) => (
|
||||
<button
|
||||
key={endpoint.id}
|
||||
type="button"
|
||||
onClick={() => handleCopy(endpoint.url)}
|
||||
className="rounded-xl border border-page-divider bg-page-inset/40 px-4 py-3 text-left transition-colors hover:bg-page-surface-hover"
|
||||
>
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<span className="text-xs font-medium text-page-label">
|
||||
{endpoint.label}
|
||||
</span>
|
||||
{endpoint.apiOnly && (
|
||||
<span className="rounded-full bg-brand-500/10 px-2 py-0.5 text-[10px] font-medium text-brand-400">
|
||||
{t('home.apiEndpointApiOnly')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<code className="block break-all text-[11px] leading-relaxed text-page-secondary">
|
||||
{endpoint.url}
|
||||
</code>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -38,6 +38,13 @@
|
||||
"home.createFreeAccount": "Create Free Account",
|
||||
"home.signIn": "Sign In",
|
||||
"home.defaultHeroTitle": "AI API Platform",
|
||||
"home.apiEndpointsTitle": "API Endpoints",
|
||||
"home.apiEndpointsDesc": "After creating an API key, use any of these URLs. Pick the endpoint closest to your network.",
|
||||
"home.apiEndpointClickToCopy": "Click an address to copy it",
|
||||
"home.apiEndpointSite": "Sub-site Domain",
|
||||
"home.apiEndpointWorld": "Overseas calls",
|
||||
"home.apiEndpointChina": "China calls",
|
||||
"home.apiEndpointApiOnly": "API only",
|
||||
|
||||
"login.welcomeBack": "Welcome Back",
|
||||
"login.signInTo": "Sign in to {{name}}",
|
||||
|
||||
@@ -38,6 +38,13 @@
|
||||
"home.createFreeAccount": "免费创建账户",
|
||||
"home.signIn": "登录",
|
||||
"home.defaultHeroTitle": "AI API 平台",
|
||||
"home.apiEndpointsTitle": "API 调用地址",
|
||||
"home.apiEndpointsDesc": "创建 API Key 后,可使用以下任一地址调用。优先选择最适合当前网络的位置。",
|
||||
"home.apiEndpointClickToCopy": "点击地址即可复制",
|
||||
"home.apiEndpointSite": "分站域名",
|
||||
"home.apiEndpointWorld": "海外调用",
|
||||
"home.apiEndpointChina": "中国调用",
|
||||
"home.apiEndpointApiOnly": "仅支持 API",
|
||||
|
||||
"login.welcomeBack": "欢迎回来",
|
||||
"login.signInTo": "登录到 {{name}}",
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function ClaudeHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -81,6 +82,8 @@ export default function ClaudeHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="bg-[#F5EEE6]">
|
||||
<div className="max-w-5xl mx-auto px-6 py-20">
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function CleanHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -82,6 +83,8 @@ export default function CleanHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||
<FadeContent blur duration={800} delay={100}>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function CorporateHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -81,6 +82,8 @@ export default function CorporateHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="bg-slate-50 border-y border-slate-200">
|
||||
<div className="max-w-6xl mx-auto px-6 lg:px-8 py-20">
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function DarkHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -86,6 +87,8 @@ export default function DarkHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||
<FadeContent blur duration={800} delay={100}>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function DefaultHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -81,6 +82,8 @@ export default function DefaultHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||
<FadeContent blur duration={800} delay={100}>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function MinimalHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -81,6 +82,8 @@ export default function MinimalHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features — two-column layout */}
|
||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||
<FadeContent blur duration={800} delay={100}>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { calcOfficialEquivList } from '../../utils/officialEquiv';
|
||||
import RotatingEquiv from '../../components/bits/RotatingEquiv';
|
||||
import CountUp from '../../components/bits/CountUp';
|
||||
import FadeContent from '../../components/bits/FadeContent';
|
||||
import ApiEndpoints from '../../components/ApiEndpoints';
|
||||
|
||||
export default function StarterHome() {
|
||||
const { t } = useTranslation();
|
||||
@@ -87,6 +88,8 @@ export default function StarterHome() {
|
||||
</FadeContent>
|
||||
</section>
|
||||
|
||||
<ApiEndpoints />
|
||||
|
||||
{/* Features */}
|
||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||
<FadeContent blur duration={800} delay={100}>
|
||||
|
||||
Reference in new issue
Block a user