refactor(website): 域名/邮箱/渠道收口到 src/config/site.ts 单一真相源
新增 SITE 配置(url/email/telegram/line/store),Footer、Pricing、astro.config (canonical 域名)均改为引用它;改联系方式/域名只动一处。 astro.config.mjs 直接 import 该 TS(Astro 配置加载支持)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
|
import { SITE } from './src/config/site.ts';
|
||||||
|
|
||||||
// 纯静态站点 (SSG),零 SSR。
|
// 纯静态站点 (SSG),零 SSR。
|
||||||
// `site` 仅用于生成绝对 URL(sitemap / canonical),镜像部署时可被任意域名覆盖,
|
// `site` 仅用于生成绝对 URL(sitemap / canonical),镜像部署时可被任意域名覆盖,
|
||||||
// 不影响「整站可秒级复制到任意备用域名」:产物为纯相对路径静态文件。
|
// 不影响「整站可秒级复制到任意备用域名」:产物为纯相对路径静态文件。
|
||||||
// 主站 canonical 域名。镜像构建务必用「同一个」SITE_URL,使 canonical 始终指向主站
|
// 主站 canonical 域名。镜像构建务必用「同一个」SITE_URL,使 canonical 始终指向主站
|
||||||
// (避免镜像与主站 SEO 竞争),且主站/镜像产物 hash 完全一致(验收:内容一致性)。
|
// (避免镜像与主站 SEO 竞争),且主站/镜像产物 hash 完全一致(验收:内容一致性)。
|
||||||
const SITE_URL = process.env.SITE_URL || 'https://pangolin.yanmeiai.com';
|
const SITE_URL = process.env.SITE_URL || SITE.url;
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: SITE_URL,
|
site: SITE_URL,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
import Brand from './Brand.astro';
|
import Brand from './Brand.astro';
|
||||||
import type { T } from '../i18n/strings';
|
import type { T } from '../i18n/strings';
|
||||||
|
import { SITE } from '../config/site';
|
||||||
|
|
||||||
interface Props { t: T }
|
interface Props { t: T }
|
||||||
const { t } = Astro.props;
|
const { t } = Astro.props;
|
||||||
@@ -34,10 +35,10 @@ const { t } = Astro.props;
|
|||||||
<div>
|
<div>
|
||||||
<h4>{t('ft.contact')}</h4>
|
<h4>{t('ft.contact')}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a class="mono">shop.pangolin.vpn</a></li>
|
<li><a class="mono">{SITE.store.label}</a></li>
|
||||||
<li><a class="mono" href="https://t.me/pangolin_app" target="_blank" rel="noopener">Telegram @pangolin_app</a></li>
|
<li><a class="mono" href={SITE.telegram.url} target="_blank" rel="noopener">Telegram {SITE.telegram.handle}</a></li>
|
||||||
<li><a class="mono">LINE @pangolinvpn</a></li>
|
<li><a class="mono">LINE {SITE.line.handle}</a></li>
|
||||||
<li><a class="mono" href="mailto:pangolin@yanmeiai.com">pangolin@yanmeiai.com</a></li>
|
<li><a class="mono" href={`mailto:${SITE.email}`}>{SITE.email}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import Icon from './Icon.astro';
|
import Icon from './Icon.astro';
|
||||||
import PricingPlans from './PricingPlans.jsx';
|
import PricingPlans from './PricingPlans.jsx';
|
||||||
import { PRICES, type T, type Lang } from '../i18n/strings';
|
import { PRICES, type T, type Lang } from '../i18n/strings';
|
||||||
|
import { SITE } from '../config/site';
|
||||||
|
|
||||||
interface Props { t: T; lang: Lang }
|
interface Props { t: T; lang: Lang }
|
||||||
const { t, lang } = Astro.props;
|
const { t, lang } = Astro.props;
|
||||||
@@ -82,9 +83,9 @@ function cell(v: string) {
|
|||||||
<div class="chips">
|
<div class="chips">
|
||||||
<span class="chip"><Icon name="shopping-bag" /><span>{t('pay.store')}</span></span>
|
<span class="chip"><Icon name="shopping-bag" /><span>{t('pay.store')}</span></span>
|
||||||
<span class="chip"><Icon name="credit-card" /><span>{t('pay.usdt')}</span></span>
|
<span class="chip"><Icon name="credit-card" /><span>{t('pay.usdt')}</span></span>
|
||||||
<a class="chip" href="https://t.me/pangolin_app" target="_blank" rel="noopener"><Icon name="send" />Telegram @pangolin_app</a>
|
<a class="chip" href={SITE.telegram.url} target="_blank" rel="noopener"><Icon name="send" />Telegram {SITE.telegram.handle}</a>
|
||||||
<span class="chip"><Icon name="message-circle" />LINE @pangolinvpn</span>
|
<span class="chip"><Icon name="message-circle" />LINE {SITE.line.handle}</span>
|
||||||
<a class="chip" href="mailto:pangolin@yanmeiai.com"><Icon name="mail" />pangolin@yanmeiai.com</a>
|
<a class="chip" href={`mailto:${SITE.email}`}><Icon name="mail" />{SITE.email}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* site.ts — 站点级配置「单一真相源」:域名、联系方式、对外渠道。
|
||||||
|
*
|
||||||
|
* 改这一处即可:组件(Footer / Pricing)与 astro.config 的 canonical 域名都从这里读。
|
||||||
|
* 此处只承载「事实值」(URL / 邮箱 / handle);可翻译的展示文案仍走 i18n/strings.ts。
|
||||||
|
*/
|
||||||
|
export const SITE = {
|
||||||
|
/** canonical 主域名。镜像部署可用 SITE_URL 环境变量覆盖(见 astro.config.mjs)。 */
|
||||||
|
url: 'https://pangolin.yanmeiai.com',
|
||||||
|
|
||||||
|
/** 联系邮箱。Cloudflare Email Routing 转发到个人 Gmail。 */
|
||||||
|
email: 'pangolin@yanmeiai.com',
|
||||||
|
|
||||||
|
/** Telegram 公开频道(群为私有,公开页不放)。 */
|
||||||
|
telegram: { handle: '@pangolin_app', url: 'https://t.me/pangolin_app' },
|
||||||
|
|
||||||
|
/** LINE 官方账号 —— 待确认(#24)。 */
|
||||||
|
line: { handle: '@pangolinvpn' },
|
||||||
|
|
||||||
|
/** 自助发卡商店 —— 占位待定(#24)。 */
|
||||||
|
store: { label: 'shop.pangolin.vpn' },
|
||||||
|
} as const;
|
||||||
Reference in New Issue
Block a user