feat: support runtime frontend configuration (#1135)

This commit is contained in:
Dream Hunter
2026-09-05 00:15:38 +08:00
committed by GitHub
parent 806ec1aeab
commit fc363cc9c5
22 changed files with 364 additions and 72 deletions
+1
View File
@@ -10,6 +10,7 @@
### Features
- feat: |Frontend| 新增 `VITE_DEFAULT_LANG` 构建变量,并支持通过 `index.html` 运行时配置覆盖前端设置
- feat: |邮件| 新增可选的已读/未读状态,支持点击邮件自动已读和手动切换状态
- feat: |Admin| 数据库页面新增 D1 存储容量展示,支持选择并保存 Free 或 Workers Paid 套餐,对比当前数据库大小和容量上限
- feat: |Admin| 创建邮箱页面支持一键生成随机邮箱名称(issue #1126
+1
View File
@@ -10,6 +10,7 @@
### Features
- feat: |Frontend| Add the `VITE_DEFAULT_LANG` build variable and support overriding frontend settings through runtime configuration in `index.html`
- feat: |Mail| Add optional read/unread status with click-to-read and manual status switching
- feat: |Admin| Add D1 storage capacity details to the database page, with persistent Free and Workers Paid plan selection and a comparison between the current database size and capacity limit
- feat: |Admin| Add one-click random email-name generation to the address creation page (issue #1126)
+1
View File
@@ -1,3 +1,4 @@
VITE_API_BASE=https://temp-email-api.xxx.xxx
VITE_DEFAULT_LANG=zh
VITE_CF_WEB_ANALY_TOKEN=
VITE_IS_TELEGRAM=false
+1
View File
@@ -14,6 +14,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="icon" href="/logo.png" sizes="any">
<link rel="apple-touch-icon" href="/logo.png">
<script id="app-config" type="application/json">{}</script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit"></script>
</head>
+5 -4
View File
@@ -12,12 +12,13 @@ import Footer from './views/Footer.vue';
import { api } from './api'
import { getNaiveLocaleConfig } from './i18n/naive-locale'
import { DEFAULT_LOCALE, isSupportedLocale } from './i18n/utils'
import { APP_CONFIG } from './config'
const {
isDark, loading, useSideMargin, telegramApp, isTelegram
} = useGlobalState()
const adClient = import.meta.env.VITE_GOOGLE_AD_CLIENT;
const adSlot = import.meta.env.VITE_GOOGLE_AD_SLOT;
const adClient = APP_CONFIG.GOOGLE_AD_CLIENT;
const adSlot = APP_CONFIG.GOOGLE_AD_SLOT;
const { locale } = useI18n({ useScope: 'global' });
const theme = computed(() => isDark.value ? darkTheme : null)
const localeConfig = computed(() => getNaiveLocaleConfig(isSupportedLocale(locale.value) ? locale.value : DEFAULT_LOCALE))
@@ -47,7 +48,7 @@ onMounted(async () => {
console.error(error);
}
const token = import.meta.env.VITE_CF_WEB_ANALY_TOKEN;
const token = APP_CONFIG.CF_WEB_ANALY_TOKEN;
const exist = document.querySelector('script[src="https://static.cloudflareinsights.com/beacon.min.js"]') !== null
if (token && !exist) {
@@ -66,7 +67,7 @@ onMounted(async () => {
// check if telegram is enabled
const enableTelegram = import.meta.env.VITE_IS_TELEGRAM;
const enableTelegram = APP_CONFIG.IS_TELEGRAM;
if (
(typeof enableTelegram === 'boolean' && enableTelegram === true)
||
+71
View File
@@ -0,0 +1,71 @@
// @vitest-environment jsdom
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const setRuntimeConfig = (config) => {
const element = document.createElement('script')
element.id = 'app-config'
element.type = 'application/json'
element.textContent = JSON.stringify(config)
document.head.appendChild(element)
}
describe('APP_CONFIG', () => {
beforeEach(() => {
vi.resetModules()
vi.stubEnv('VITE_API_BASE', 'https://build.example.com')
vi.stubEnv('VITE_DEFAULT_LANG', 'zh')
vi.stubEnv('VITE_IS_TELEGRAM', 'false')
})
afterEach(() => {
document.querySelector('#app-config')?.remove()
vi.unstubAllEnvs()
})
it('uses build settings when runtime settings are absent', async () => {
const { APP_CONFIG } = await import('../config')
expect(APP_CONFIG.API_BASE).toBe('https://build.example.com')
expect(APP_CONFIG.DEFAULT_LANG).toBe('zh')
})
it('overrides only settings provided by index.html', async () => {
setRuntimeConfig({ API_BASE: 'https://runtime.example.com', DEFAULT_LANG: 'en' })
const { APP_CONFIG } = await import('../config')
expect(APP_CONFIG.API_BASE).toBe('https://runtime.example.com')
expect(APP_CONFIG.DEFAULT_LANG).toBe('en')
})
it('allows an explicit empty runtime value', async () => {
setRuntimeConfig({ API_BASE: '' })
const { APP_CONFIG } = await import('../config')
expect(APP_CONFIG.API_BASE).toBe('')
expect(APP_CONFIG.DEFAULT_LANG).toBe('zh')
})
it('falls back to build settings for invalid runtime value types', async () => {
setRuntimeConfig({ API_BASE: {}, DEFAULT_LANG: 1, IS_TELEGRAM: [] })
const { APP_CONFIG } = await import('../config')
expect(APP_CONFIG.API_BASE).toBe('https://build.example.com')
expect(APP_CONFIG.DEFAULT_LANG).toBe('zh')
expect(APP_CONFIG.IS_TELEGRAM).toBe('false')
})
it('reads runtime settings only once', async () => {
setRuntimeConfig({ DEFAULT_LANG: 'en' })
const firstImport = await import('../config')
document.querySelector('#app-config').textContent = JSON.stringify({ DEFAULT_LANG: 'de' })
const secondImport = await import('../config')
expect(secondImport.APP_CONFIG).toBe(firstImport.APP_CONFIG)
expect(secondImport.APP_CONFIG.DEFAULT_LANG).toBe('en')
})
})
+2 -1
View File
@@ -6,8 +6,9 @@ import i18n from '../i18n'
import { getFingerprint } from '../utils/fingerprint'
import { safeBearerHeader, safeHeaderValue } from '../utils/headers'
import { sanitizeHtml } from '../utils/sanitize-html'
import { APP_CONFIG } from '../config'
const API_BASE = import.meta.env.VITE_API_BASE || "";
const API_BASE = APP_CONFIG.API_BASE || "";
const {
loading, auth, jwt, settings, openSettings,
userOpenSettings, userSettings, announcement,
@@ -1,6 +1,7 @@
<script setup>
import { computed } from 'vue'
import { useScopedI18n } from '@/i18n/app'
import { APP_CONFIG } from '@/config'
import { useGlobalState } from '../store'
@@ -34,7 +35,7 @@ const modalShow = computed({
set: (value) => emit('update:show', value),
})
const configuredApiBaseUrl = import.meta.env.VITE_API_BASE || ''
const configuredApiBaseUrl = APP_CONFIG.API_BASE || ''
const frontendBaseUrl = computed(() => window.location.origin)
const apiBaseUrl = computed(() => (configuredApiBaseUrl || frontendBaseUrl.value).replace(/\/$/, ''))
const docLocale = computed(() => locale.value === 'zh' ? 'zh' : 'en')
+39
View File
@@ -0,0 +1,39 @@
type RuntimeConfig = Record<string, unknown>
const getRuntimeConfig = (): RuntimeConfig => {
if (typeof document === 'undefined') return {}
const content = document.querySelector('#app-config')?.textContent
if (!content?.trim()) return {}
try {
const config = JSON.parse(content)
return config && typeof config === 'object' && !Array.isArray(config) ? config : {}
} catch (error) {
console.error('Failed to parse app config', error)
return {}
}
}
const runtimeConfig = getRuntimeConfig()
const getStringConfigValue = (key: string, buildValue: string): string => {
const runtimeValue = runtimeConfig[key]
return typeof runtimeValue === 'string' ? runtimeValue : buildValue
}
const getTelegramConfigValue = (buildValue: string): string | boolean => {
const runtimeValue = runtimeConfig.IS_TELEGRAM
return typeof runtimeValue === 'string' || typeof runtimeValue === 'boolean'
? runtimeValue
: buildValue
}
export const APP_CONFIG = {
API_BASE: getStringConfigValue('API_BASE', import.meta.env.VITE_API_BASE || ''),
DEFAULT_LANG: getStringConfigValue('DEFAULT_LANG', import.meta.env.VITE_DEFAULT_LANG || ''),
CF_WEB_ANALY_TOKEN: getStringConfigValue('CF_WEB_ANALY_TOKEN', import.meta.env.VITE_CF_WEB_ANALY_TOKEN || ''),
IS_TELEGRAM: getTelegramConfigValue(import.meta.env.VITE_IS_TELEGRAM || ''),
GOOGLE_AD_CLIENT: getStringConfigValue('GOOGLE_AD_CLIENT', import.meta.env.VITE_GOOGLE_AD_CLIENT || ''),
GOOGLE_AD_SLOT: getStringConfigValue('GOOGLE_AD_SLOT', import.meta.env.VITE_GOOGLE_AD_SLOT || ''),
} as const
+5 -2
View File
@@ -1,11 +1,11 @@
import { LOCALE_REGISTRY, SUPPORTED_LOCALES } from './locale-registry'
import { APP_CONFIG } from '../config'
export { SUPPORTED_LOCALES } from './locale-registry'
export type { SupportedLocale } from './locale-registry'
import type { SupportedLocale } from './locale-registry'
export const DEFAULT_LOCALE: SupportedLocale = 'zh'
export const FALLBACK_LOCALE: SupportedLocale = 'zh'
export const PREFERRED_LOCALE_STORAGE_KEY = 'preferredLocale'
export const EMPTY_LOCALE_MESSAGES = Object.fromEntries(
@@ -29,6 +29,9 @@ export const resolveSupportedLocale = (locale: string | null | undefined): Suppo
return null
}
export const DEFAULT_LOCALE: SupportedLocale = resolveSupportedLocale(APP_CONFIG.DEFAULT_LANG)
|| FALLBACK_LOCALE
export const matchSupportedLocale = (locale: string | null | undefined): SupportedLocale | null => {
if (!locale) return null
const normalizedLocale = locale.trim().toLowerCase()
@@ -135,4 +138,4 @@ const getLocaleAliasPath = (path: string, locale: SupportedLocale): string => {
}
return getPathWithLocale(basePath, locale)
}
}
+1
View File
@@ -140,6 +140,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
collapsed: false,
items: [
{ text: 'Worker Variables', link: 'worker-vars' },
{ text: 'Frontend Variables', link: 'frontend-vars' },
{ text: 'Configure Email Sending', link: 'config-send-mail' },
]
},
+1
View File
@@ -140,6 +140,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
collapsed: false,
items: [
{ text: 'Worker 变量说明', link: 'worker-vars' },
{ text: '前端变量说明', link: 'frontend-vars' },
{ text: '配置发送邮件', link: 'config-send-mail' },
]
},
@@ -44,7 +44,7 @@ Then go to the repository page `Settings` -> `Secrets and variables` -> `Actions
| Name | Description |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FRONTEND_ENV` | Frontend configuration file used by the `Deploy Frontend` workflow. Copy the content from `frontend/.env.example`, [and modify it according to this guide](/en/guide/cli/pages.html). For separate frontend/backend deployment that talks to Worker directly, `VITE_API_BASE` should be the backend Worker API root URL, must start with `https://`, and must not include a trailing `/`. When this address is configured incorrectly, common symptoms are the `map` error or `405` API responses |
| `FRONTEND_ENV` | Frontend configuration file used by the `Deploy Frontend` workflow. Copy the content from `frontend/.env.example` and modify it according to [Frontend Variables](/en/guide/frontend-vars) |
| `FRONTEND_NAME` | The project name you created in Cloudflare Pages, can be created via [UI](https://temp-mail-docs.awsl.uk/en/guide/ui/pages.html) or [Command Line](https://temp-mail-docs.awsl.uk/en/guide/cli/pages.html) |
| `FRONTEND_BRANCH` | (Optional) Branch for pages deployment, can be left unconfigured, defaults to `production` |
| `PAGE_TOML` | (Optional) Used only by the `Deploy Frontend with page function` workflow. Required when using page functions to forward backend requests. Please copy the content from `pages/wrangler.toml` and modify the `service` field to your worker backend name according to actual situation. This workflow builds the frontend in Pages mode and uses same-origin requests, so it does not read `FRONTEND_ENV` |
@@ -29,6 +29,12 @@ Change `VITE_API_BASE` to the `worker` `url` created in the previous step. Do no
For example: `VITE_API_BASE=https://xxx.xxx.workers.dev`
Set the frontend default language with `VITE_DEFAULT_LANG`. Supported values are `zh`, `en`, `es`, `pt-BR`, `ja`, and `de`; an unset or invalid value falls back to `zh`.
For example: `VITE_DEFAULT_LANG=en`
See [Frontend Variables](/en/guide/frontend-vars) for other settings.
```bash
pnpm build --emptyOutDir
# The first deployment will prompt you to create a project, for production branch enter production
@@ -47,6 +53,8 @@ The first deployment will prompt you to create a project. For the `production` b
If your worker backend name is not `cloudflare_temp_email`, please modify `pages/wrangler.toml`.
To set the default language, add `VITE_DEFAULT_LANG=en` to `frontend/.env.pages.local`. Same-origin requests do not require `VITE_API_BASE`; see [Frontend Variables](/en/guide/frontend-vars) for other settings.
```bash
cd frontend
pnpm install
@@ -1,17 +1,17 @@
# Adding Google Ads to Your Website
## Command Line Deployment
Google Ads uses the following two variables. See [Google AdSense](https://www.google.com/adsense/start/) for the values:
Modify the `.env.prod` file
Add the following two variables, refer to [Google AdSense](https://www.google.com/adsense/start/) for specific values
```txt
```ini
VITE_GOOGLE_AD_CLIENT=ca-pub-123456
VITE_GOOGLE_AD_SLOT=123456
```
Then execute the following commands to redeploy pages.
Ads are not loaded when either variable is empty. See [Frontend Variables](/en/guide/frontend-vars) for their types and defaults.
## CLI Deployment
Add both variables to `frontend/.env.prod`, then rebuild and deploy:
```bash
pnpm build --emptyOutDir
@@ -19,11 +19,25 @@ pnpm build --emptyOutDir
pnpm run deploy
```
## GitHub Action Deployment
See [CLI Frontend Deployment](/en/guide/cli/pages) for the complete steps.
Modify `FRONTEND_ENV`, add the following two variables, refer to [Google AdSense](https://www.google.com/adsense/start/) for specific values, then redeploy pages.
## GitHub Actions Deployment
```txt
VITE_GOOGLE_AD_CLIENT=ca-pub-123456
VITE_GOOGLE_AD_SLOT=123456
Add both variables to the existing `FRONTEND_ENV` secret, then run the `Deploy Frontend` workflow again.
See [GitHub Actions Deployment](/en/guide/actions/github-action) for the complete steps.
## Manual ZIP Deployment
Edit `app-config` in the archive's `index.html` and use the field names without the `VITE_` prefix:
```html
<script id="app-config" type="application/json">
{
"GOOGLE_AD_CLIENT": "ca-pub-123456",
"GOOGLE_AD_SLOT": "123456"
}
</script>
```
See [Manual ZIP Deployment](/en/guide/ui/pages) for the complete steps.
@@ -0,0 +1,44 @@
# Frontend Variables
Frontend configuration is public in the browser. Do not put passwords, API keys, or other secrets in these values.
## Configuration Methods
### ENV
For CLI deployment, add the variables to `frontend/.env.prod`. For Worker Assets or Page Functions deployment, use `frontend/.env.pages.local`. For the GitHub Actions `Deploy Frontend` workflow, put the same content in the `FRONTEND_ENV` repository secret. The `Deploy Frontend with page function` workflow does not read this secret.
```ini
VITE_API_BASE=https://temp-email-api.example.com
VITE_DEFAULT_LANG=en
```
See [CLI Deployment](/en/guide/cli/pages) and [GitHub Actions Deployment](/en/guide/actions/github-action) for complete steps.
### index.html
When using a prebuilt frontend ZIP, edit `app-config` in `index.html`. Field names do not use the `VITE_` prefix:
```html
<script id="app-config" type="application/json">
{
"API_BASE": "https://temp-email-api.example.com",
"DEFAULT_LANG": "en"
}
</script>
```
Fields set in `app-config` override ENV build values. Omitted fields, or a missing `app-config` tag, continue to use ENV build values. See [Manual ZIP Deployment](/en/guide/ui/pages) for complete steps.
## Variable Reference
No frontend variable is required for every deployment method. `VITE_API_BASE` is required only when the frontend and backend are deployed separately and the browser must request the Worker domain directly. Leave it empty for same-origin deployments such as Worker Assets or Page Functions.
| ENV Variable | `app-config` Field | Required | Type | Default | Description |
| --- | --- | --- | --- | --- | --- |
| `VITE_API_BASE` | `API_BASE` | Depends on deployment | Text | Empty | Backend API root URL beginning with `https://` and without a trailing `/`; an empty value uses the same-origin API |
| `VITE_DEFAULT_LANG` | `DEFAULT_LANG` | No | Text | `zh` | Default language: `zh`, `en`, `es`, `pt-BR`, `ja`, or `de` |
| `VITE_CF_WEB_ANALY_TOKEN` | `CF_WEB_ANALY_TOKEN` | No | Text | Empty | Cloudflare Web Analytics Token |
| `VITE_IS_TELEGRAM` | `IS_TELEGRAM` | No | Boolean | `false` | Whether to enable Telegram Mini App; see [Telegram Configuration](/en/guide/feature/telegram) |
| `VITE_GOOGLE_AD_CLIENT` | `GOOGLE_AD_CLIENT` | No | Text | Empty | Google AdSense Client ID; see [Google Ads Configuration](/en/guide/feature/google-ads) |
| `VITE_GOOGLE_AD_SLOT` | `GOOGLE_AD_SLOT` | No | Text | Empty | Google AdSense Slot ID; see [Google Ads Configuration](/en/guide/feature/google-ads) |
+38 -19
View File
@@ -68,24 +68,34 @@ const generate = async () => {
const arrayBuffer = await response.arrayBuffer();
var zip = new JSZip();
await zip.loadAsync(arrayBuffer);
let target_path = ""
const directory = zip.folder("assets");
if (directory) {
for (const [relativePath, zipEntry] of Object.entries(directory.files)) {
console.log(relativePath);
if (relativePath.startsWith("assets/index-") && relativePath.endsWith(".js")){
let content = await zipEntry.async("string");
content = content.replaceAll("https://temp-email-api.xxx.xxx", normalizedDomain);
target_path = relativePath;
zip.file(relativePath, content);
break;
}
}
}
if (!target_path) {
errorMessage.value = "Could not find the frontend entry file. Generation failed"
const indexEntry = zip.file("index.html");
if (!indexEntry) {
errorMessage.value = "Could not find index.html. Generation failed"
return
}
const content = await indexEntry.async("string");
const configPattern = /(<script id="app-config" type="application\/json">)([\s\S]*?)(<\/script>)/;
const configMatch = content.match(configPattern);
if (!configMatch) {
errorMessage.value = "Could not find app-config. Generation failed"
return
}
let appConfig = {};
try {
appConfig = JSON.parse(configMatch[2]);
} catch {
errorMessage.value = "Invalid app-config. Generation failed"
return
}
if (!appConfig || typeof appConfig !== "object" || Array.isArray(appConfig)) {
errorMessage.value = "Invalid app-config. Generation failed"
return
}
appConfig.API_BASE = normalizedDomain;
zip.file("index.html", content.replace(
configPattern,
(_, openTag, _config, closeTag) => `${openTag}${JSON.stringify(appConfig)}${closeTag}`
));
const blob = await zip.generateAsync({ type: "blob" });
const url = window.URL.createObjectURL(blob);
errorMessage.value = ""
@@ -133,12 +143,21 @@ const generate = async () => {
> [!NOTE]
> You can also deploy manually. Download the zip from here: [frontend.zip](https://github.com/dreamhunter2333/cloudflare_temp_email/releases/latest/download/frontend.zip)
>
> Modify the index-xxx.js file in the archive, where xx is a random string
>
> Search for `https://temp-email-api.xxx.xxx` and replace it with your worker's backend API root URL, then deploy the new zip file. If you replace it with the frontend Pages domain, common symptoms are the `map` error or `405` responses from API requests
> Extract the archive and edit `app-config` in `index.html`. When finished, compress all files again and upload the archive. Do not include the outer directory in the archive.
>
> If you entered the wrong address the first time and still see errors after redeploying, test in an incognito window or clear browser cache so the browser stops using the old frontend assets.
```html
<script id="app-config" type="application/json">
{
"API_BASE": "https://temp-email-api.example.com",
"DEFAULT_LANG": "en"
}
</script>
```
`API_BASE` is the backend API root URL without a trailing `/`; `DEFAULT_LANG` supports `zh`, `en`, `es`, `pt-BR`, `ja`, and `de`. You can also set `CF_WEB_ANALY_TOKEN`, `IS_TELEGRAM`, `GOOGLE_AD_CLIENT`, and `GOOGLE_AD_SLOT`; see [Frontend Variables](/en/guide/frontend-vars) for their purpose and values. Fields present in `app-config` override the corresponding settings built into JavaScript, while omitted fields keep their existing settings.
4. Select `Pages`, click `Create Pages`, modify the name, upload the downloaded zip package
> [!warning] Important: SPA Mode
@@ -44,7 +44,7 @@
| 名称 | 说明 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FRONTEND_ENV` | `Deploy Frontend` workflow 使用的前端配置文件,请复制 `frontend/.env.example` 的内容,[并参考此处修改](/zh/guide/cli/pages.html)。如果是前后端分离直连 Worker,`VITE_API_BASE` 应填写后端 Worker API 根地址,并且以 `https://` 开头、末尾不要带 `/`。地址配置错误时,常见现象是前端报 `map` 错误或接口返回 `405` |
| `FRONTEND_ENV` | `Deploy Frontend` workflow 使用的前端配置文件,请复制 `frontend/.env.example` 的内容,并参考 [前端变量说明](/zh/guide/frontend-vars) 修改 |
| `FRONTEND_NAME` | 你在 Cloudflare Pages 创建的项目名称,可通过 [用户界面](https://temp-mail-docs.awsl.uk/zh/guide/ui/pages.html) 或者 [命令行](https://temp-mail-docs.awsl.uk/zh/guide/cli/pages.html) 创建 |
| `FRONTEND_BRANCH` | (可选) pages 部署的分支,可不配置,默认 `production` |
| `PAGE_TOML` | (可选) 仅供 `Deploy Frontend with page function` workflow 使用。通过 page functions 转发后端请求时需要配置,请复制 `pages/wrangler.toml` 的内容,并根据实际情况修改 `service` 字段为你的 worker 后端名称。这个 workflow 会以 Pages 模式构建前端并走同域请求,因此不会读取 `FRONTEND_ENV` |
@@ -29,6 +29,12 @@ cp .env.example .env.prod
例如: `VITE_API_BASE=https://xxx.xxx.workers.dev`
可通过 `VITE_DEFAULT_LANG` 设置前端默认语言,支持 `zh``en``es``pt-BR``ja``de`,未配置或配置无效时使用 `zh`
例如: `VITE_DEFAULT_LANG=en`
其他配置项请查看 [前端变量说明](/zh/guide/frontend-vars)。
```bash
pnpm build --emptyOutDir
# 第一次部署会提示创建项目, production 分支请填写 production
@@ -47,6 +53,8 @@ pnpm run deploy
如果你的 worker 后端 名称不为 `cloudflare_temp_email` 请修改 `pages/wrangler.toml`
如需设置默认语言,在 `frontend/.env.pages.local` 中添加 `VITE_DEFAULT_LANG=en`。同域请求不需要设置 `VITE_API_BASE`,其他配置项请查看 [前端变量说明](/zh/guide/frontend-vars)。
```bash
cd frontend
pnpm install
@@ -1,17 +1,17 @@
# 给网页增加 Google Ads
## 命令行部署
Google Ads 使用以下两个变量,具体值请参考 [Google AdSense](https://www.google.com/adsense/start/)
修改 `.env.prod` 文件
增加下列两个变量, 具体的值请参考 [Google AdSense](https://www.google.com/adsense/start/) 的说明
```txt
```ini
VITE_GOOGLE_AD_CLIENT=ca-pub-123456
VITE_GOOGLE_AD_SLOT=123456
```
然后执行下列命令, 重新部署 pages 即可.
任意一个变量为空时都不会加载广告。变量类型和默认值请查看 [前端变量说明](/zh/guide/frontend-vars)。
## CLI 部署
`frontend/.env.prod` 中添加上述两个变量,然后重新构建并部署:
```bash
pnpm build --emptyOutDir
@@ -19,11 +19,25 @@ pnpm build --emptyOutDir
pnpm run deploy
```
## GitHub Action 部署
完整步骤请查看 [CLI 部署前端](/zh/guide/cli/pages)。
修改 `FRONTEND_ENV`, 增加下列两个变量, 具体的值请参考 [Google AdSense](https://www.google.com/adsense/start/) 的说明, 重新部署 pages 即可.
## GitHub Actions 部署
```txt
VITE_GOOGLE_AD_CLIENT=ca-pub-123456
VITE_GOOGLE_AD_SLOT=123456
在已有的 `FRONTEND_ENV` Secret 中添加上述两个变量,然后重新运行 `Deploy Frontend` workflow。
完整步骤请查看 [GitHub Actions 部署](/zh/guide/actions/github-action)。
## 手动 ZIP 部署
编辑压缩包中 `index.html``app-config`,使用不带 `VITE_` 前缀的字段名:
```html
<script id="app-config" type="application/json">
{
"GOOGLE_AD_CLIENT": "ca-pub-123456",
"GOOGLE_AD_SLOT": "123456"
}
</script>
```
完整步骤请查看 [手动 ZIP 部署](/zh/guide/ui/pages)。
@@ -0,0 +1,44 @@
# 前端变量说明
前端配置会公开在浏览器中,请勿填写密码、API 密钥等敏感信息。
## 配置方式
### ENV
通过 CLI 部署时,将变量写入 `frontend/.env.prod`Worker Assets 或 Page Functions 部署写入 `frontend/.env.pages.local`。通过 GitHub Actions 的 `Deploy Frontend` workflow 部署时,将相同内容写入 Repository Secret `FRONTEND_ENV``Deploy Frontend with page function` workflow 不读取该 Secret。
```ini
VITE_API_BASE=https://temp-email-api.example.com
VITE_DEFAULT_LANG=en
```
完整步骤请查看 [CLI 部署](/zh/guide/cli/pages) 和 [GitHub Actions 部署](/zh/guide/actions/github-action)。
### index.html
使用构建好的前端 ZIP 时,可以编辑 `index.html` 中的 `app-config`。字段名不带 `VITE_` 前缀:
```html
<script id="app-config" type="application/json">
{
"API_BASE": "https://temp-email-api.example.com",
"DEFAULT_LANG": "en"
}
</script>
```
`app-config` 中填写的字段会覆盖 ENV 构建值;未填写字段或不存在 `app-config` 标签时继续使用 ENV 构建值。完整步骤请查看 [手动 ZIP 部署](/zh/guide/ui/pages)。
## 变量列表
没有所有部署方式都必须配置的前端变量。`VITE_API_BASE` 仅在前后端分离、前端需要直接请求 Worker 域名时配置;Worker Assets 或 Page Functions 等同域部署应留空。
| ENV 变量 | `app-config` 字段 | 是否必须 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `VITE_API_BASE` | `API_BASE` | 视部署方式 | 文本 | 空 | 以 `https://` 开头的后端 API 根地址,不要带结尾 `/`;空值表示使用同域 API |
| `VITE_DEFAULT_LANG` | `DEFAULT_LANG` | 否 | 文本 | `zh` | 默认语言,支持 `zh``en``es``pt-BR``ja``de` |
| `VITE_CF_WEB_ANALY_TOKEN` | `CF_WEB_ANALY_TOKEN` | 否 | 文本 | 空 | Cloudflare Web Analytics Token |
| `VITE_IS_TELEGRAM` | `IS_TELEGRAM` | 否 | 布尔值 | `false` | 是否启用 Telegram Mini App,详见 [Telegram 配置](/zh/guide/feature/telegram) |
| `VITE_GOOGLE_AD_CLIENT` | `GOOGLE_AD_CLIENT` | 否 | 文本 | 空 | Google AdSense Client ID,详见 [Google Ads 配置](/zh/guide/feature/google-ads) |
| `VITE_GOOGLE_AD_SLOT` | `GOOGLE_AD_SLOT` | 否 | 文本 | 空 | Google AdSense Slot ID,详见 [Google Ads 配置](/zh/guide/feature/google-ads) |
+38 -19
View File
@@ -68,24 +68,34 @@ const generate = async () => {
const arrayBuffer = await response.arrayBuffer();
var zip = new JSZip();
await zip.loadAsync(arrayBuffer);
let target_path = ""
const directory = zip.folder("assets");
if (directory) {
for (const [relativePath, zipEntry] of Object.entries(directory.files)) {
console.log(relativePath);
if (relativePath.startsWith("assets/index-") && relativePath.endsWith(".js")){
let content = await zipEntry.async("string");
content = content.replaceAll("https://temp-email-api.xxx.xxx", normalizedDomain);
target_path = relativePath;
zip.file(relativePath, content);
break;
}
}
}
if (!target_path) {
errorMessage.value = "没有找到前端入口文件,生成失败"
const indexEntry = zip.file("index.html");
if (!indexEntry) {
errorMessage.value = "没有找到 index.html,生成失败"
return
}
const content = await indexEntry.async("string");
const configPattern = /(<script id="app-config" type="application\/json">)([\s\S]*?)(<\/script>)/;
const configMatch = content.match(configPattern);
if (!configMatch) {
errorMessage.value = "没有找到 app-config,生成失败"
return
}
let appConfig = {};
try {
appConfig = JSON.parse(configMatch[2]);
} catch {
errorMessage.value = "app-config 格式错误,生成失败"
return
}
if (!appConfig || typeof appConfig !== "object" || Array.isArray(appConfig)) {
errorMessage.value = "app-config 格式错误,生成失败"
return
}
appConfig.API_BASE = normalizedDomain;
zip.file("index.html", content.replace(
configPattern,
(_, openTag, _config, closeTag) => `${openTag}${JSON.stringify(appConfig)}${closeTag}`
));
const blob = await zip.generateAsync({ type: "blob" });
const url = window.URL.createObjectURL(blob);
errorMessage.value = ""
@@ -133,12 +143,21 @@ const generate = async () => {
> [!NOTE]
> 你也可以手动部署,从这里下载 zip, [frontend.zip](https://github.com/dreamhunter2333/cloudflare_temp_email/releases/latest/download/frontend.zip)
>
> 修改压缩包里面的 index-xxx.js 文件 xx 是随机的字符串
>
> 搜索 `https://temp-email-api.xxx.xxx` ,替换成你 worker 的后端 API 根地址,然后部署新的 zip 文件。如果填成前端 Pages 域名,常见现象就是页面报 `map` 错误或接口返回 `405`
> 解压后编辑 `index.html` 中的 `app-config`,完成后重新压缩全部文件并上传。不要把外层目录一起压缩。
>
> 如果第一次填错后重新部署仍然报错,请用无痕窗口测试或清理浏览器缓存,避免浏览器继续使用旧的前端资源。
```html
<script id="app-config" type="application/json">
{
"API_BASE": "https://temp-email-api.example.com",
"DEFAULT_LANG": "en"
}
</script>
```
`API_BASE` 是后端 API 根地址,不要带结尾 `/``DEFAULT_LANG` 支持 `zh`、`en`、`es`、`pt-BR`、`ja`、`de`。还可以设置 `CF_WEB_ANALY_TOKEN`、`IS_TELEGRAM`、`GOOGLE_AD_CLIENT`、`GOOGLE_AD_SLOT`,用途和取值请查看 [前端变量说明](/zh/guide/frontend-vars)。`app-config` 中存在的字段会覆盖构建进 JS 的对应配置,未填写的字段继续使用原配置。
4. 选择 `Pages`,点击 `Create Pages`, 修改名称,上传下载的 zip 包
> [!warning] 重要:SPA 模式