# 部署说明：Scenic Studio

## A. 本地演示

1. 解压 `scenic-website-builder-studio.zip`。
2. 安装 Python 3（若电脑已安装可跳过）。
3. 在解压后的项目目录打开终端，运行：

   ```bash
   python -m http.server 8000 --directory public
   ```

4. 浏览器访问 `http://localhost:8000`。

本地演示无需后端，但项目数据只保存在当前浏览器；换电脑或清理浏览器数据后不会同步。图片较大时可能触发浏览器存储限制。

## B. 团队共享线上后台（Cloudflare）

第一版后端使用 Cloudflare Pages Functions + D1 + R2。以下资源名称与代码默认绑定一致：

- D1 binding：`DB`
- R2 binding：`MEDIA`
- D1 database name：`scenic-website-builder`
- R2 bucket name：`scenic-website-media`

### 1. 创建代码仓库与 Pages 项目

将整个项目目录（包括 `functions/`、`public/` 和 `schema.sql`）放进 Git 仓库。创建 Cloudflare Pages 项目并连接该仓库：

- Build command：留空（纯静态文件，无需构建）
- Build output directory：`public`
- Root directory：项目根目录

确保 `functions/` 目录位于仓库根目录，不要只上传 `public/` 文件夹，否则 API Functions 不会部署。

### 2. 创建 D1 数据库

在 Cloudflare Dashboard 的 Storage & databases → D1 创建数据库 `scenic-website-builder`。

然后在本地安装并登录 Wrangler：

```bash
npm install -g wrangler
wrangler login
```

将 `wrangler.toml.example` 复制为 `wrangler.toml`，把 `database_id` 替换成刚创建的真实 D1 ID。执行：

```bash
wrangler d1 execute scenic-website-builder --file=./schema.sql --remote
```

如果团队选择在 Pages 项目设置中直接配置 D1 binding，也请将 binding 名称设为 `DB`，并确保其指向同一个数据库。

### 3. 创建 R2 图片存储

在 Cloudflare Dashboard 的 R2 创建 bucket：`scenic-website-media`。

在 Pages 项目 Settings → Functions → R2 bucket bindings 中添加：

- Variable name：`MEDIA`
- R2 bucket：`scenic-website-media`

在 D1 bindings 中添加：

- Variable name：`DB`
- D1 database：`scenic-website-builder`

完成资源绑定后重新部署 Pages 项目。

### 3.1 可选：启用整段资料的 AI 语义识别

在 Pages 项目 Settings → Variables and Secrets 中添加 `OPENAI_API_KEY`（Secret），可选添加 `OPENAI_MODEL`（默认 `gpt-4.1-mini`），然后重新部署。配置后，“一键识别景区资料”会优先调用 AI 从非结构化文本中提取字段；未配置密钥或请求失败时会自动回退到本地规则识别。密钥只保存在服务端，不要写进前端文件。

### 4. 设置访问控制（上线前必须完成）

**不要在未设置访问控制的情况下将该管理后台公开给互联网。** 第一版 API 本身没有登录系统，建议使用 Cloudflare Access 创建 Self-hosted application，仅允许指定公司邮箱或团队身份访问整个工具台。若需要细粒度角色、审计日志或对外开放申请入口，应在正式推广前补充应用级鉴权。

### 5. 验证团队模式

1. 打开线上工具台，左下角应显示“团队共享模式”。
2. 创建一个测试项目并上传小图片。
3. 保存后在另一台已授权设备打开同一网址，确认项目记录存在。
4. 生成网站，下载 ZIP，检查其中的 `index.html` 与语言目录。
5. 只有以上验证通过后，再开始批量创建真实景区网站。

## C. 输出网站的部署方式

- The root `index.html` is a fallback redirect. Each selected language has its own directory, including the default language (for example `jp/index.html`, `kr/index.html` and `th/index.html`). The ZIP also contains `_redirects`, `sitemap.xml` and `robots.txt`.
- The ZIP contains every selected language in its own directory, including the default language. The root page redirects to the chosen default language.
- 生成页面的图片已嵌入 Base64；Google Fonts 仍可能通过网络加载，离线时会回退到系统字体。

## D. 当前限制

- 景区专有介绍和产品内容不会自动翻译；需要在各语言表单中填写。可在下一版接入 AI 翻译 API。
- 第一版没有应用级登录与角色权限，请使用 Cloudflare Access。
- 删除项目记录不会自动删除 R2 中已上传的图片对象；后续可加垃圾回收。
- 自动 SEO 检查只覆盖基础标签和页面结构，不代表 Google 一定收录或获得排名。


## Multilingual URL and booking-link behavior (v0.3)

The generated website ZIP contains one folder per selected language, including the default language. For example, selecting Japanese, Korean, Thai and English produces `jp/index.html`, `kr/index.html`, `th/index.html` and `en/index.html`. The root `index.html` is a fallback redirect, and Cloudflare Pages applies the `_redirects` 301 rule to the selected default-language path.

Every language page has its own title, meta description, HTML language tag, self-referencing canonical URL and hreflang alternates. The ZIP also includes `sitemap.xml` and `robots.txt`. Upload the entire generated website ZIP with its directory structure preserved.

For Trip.com booking URLs, a language-specific URL always takes priority over the fallback URL. The language selector on the published site navigates to the matching language directory, so Japanese uses the Japanese Trip.com URL, Thai uses the Thai URL, and so on. If a selected language has no dedicated URL, the configured fallback URL is used.


## v0.4.0 minimal footer update

Generated websites no longer include an Official Partner section, partner logo, copyright notice, or site-operator footer block. The footer retains only the venue identity/address and optional social links.
