# Supabase + 阿里云 SMTP · 邮件验证码登录配置

来访者 **个人空间**（`portal.html`）与 **咨询师工作台**（`portal-counselor.html`）共用同一 Supabase 项目，通过邮箱 **8 位 OTP** 登录；发信在 **Supabase 控制台** 里配置 **阿里云 SMTP**（无需在本仓库部署发信服务器）。

---

## 一、Supabase 项目

1. 登录 [Supabase Dashboard](https://supabase.com/dashboard)，新建项目（Region 建议选离用户近的，如新加坡）。
2. 进入 **Project Settings → API**，记下：
   - **Project URL** → `https://xxxxx.supabase.co`
   - **anon public** key
3. 在本项目根目录：
   ```bash
   cp supabase-config.example.js supabase-config.js
   ```
4. 编辑 `supabase-config.js`，填入 `url`、`anonKey`，并设 `enabled: true`。

> `supabase-config.js` 已加入 `.gitignore`，勿提交到公开仓库。

---

## 二、开启邮箱 OTP（必做）

1. **Authentication → Providers → Email**
   - 开启 Email
   - 开启 **Confirm email**（按需；若希望免确认即可登录，可关闭，以你合规要求为准）
   - 开启 **Email OTP**（8 位验证码，与页面输入一致）
2. **Authentication → Email Templates**
   - 可编辑「Magic Link / OTP」模板，正文中需包含 `{{ .Token }}`（8 位码）

---

## 三、阿里云 SMTP（在 Supabase 里填，不是写在 HTML 里）

### 1. 阿里云控制台

1. 开通 [阿里云邮件推送](https://www.aliyun.com/product/directmail)（Direct Mail）。
2. 配置 **发信域名** 并完成 DNS 验证（SPF、DKIM 等按控制台指引）。
3. 创建 **发信地址**（例如 `noreply@mail.yourdomain.com`）。
4. 在 **SMTP 设置** 中创建 SMTP 密码，记下：
   - SMTP 服务器（常见：`smtpdm.aliyun.com`，以控制台显示为准）
   - 端口：**465**（SSL）或 **587**（STARTTLS）
   - SMTP 用户名（一般为发信地址或控制台给出的账号）
   - SMTP 密码

### 2. Supabase 控制台

1. **Project Settings → Authentication → SMTP Settings**
2. 开启 **Enable Custom SMTP**
3. 填写示例：

   | 字段 | 示例 |
   |------|------|
   | Sender email | `noreply@mail.yourdomain.com`（须为已验证发信地址） |
   | Sender name | `心马力 Chevali` |
   | Host | `smtpdm.aliyun.com` |
   | Port | `465` |
   | Username | 阿里云 SMTP 用户名 |
   | Password | 阿里云 SMTP 密码 |

4. 保存后，在 Authentication 页面用 **Send test email** 自测一封。

---

## 四、角色区分（来访者 / 咨询师）

代码在注册/发码时写入用户元数据 `app_role`：

| 门户 | 页面 | `app_role` |
|------|------|------------|
| 来访者 | `portal.html` | `visitor` |
| 咨询师 | `portal-counselor.html` | `counselor` |

登录时若 `app_role` 与当前门户不一致，会提示去对应入口登录，并自动登出 Supabase 会话。

首次在某一门户 **注册** 时会创建 Supabase 用户；另一门户需使用对应页面注册，或由管理员在 Supabase **Authentication → Users** 中修改 `user_metadata.app_role`。

---

## 四（补充）、咨询师内部工号（必做）

咨询师工作台除邮箱 OTP 外，还须校验 **内部工号**（防止任意邮箱进入咨询师页）。

### 1. 在 Supabase 执行 SQL

1. 打开 **SQL Editor**，将仓库内 [`supabase/schema.sql`](../supabase/schema.sql) **整段粘贴并 Run**。
2. 脚本会创建：
   - 表 `counselor_staff_registry`（工号白名单 + 首次注册后绑定的邮箱）
   - RPC `check_counselor_staff_access`（发码前校验）
   - RPC `bind_counselor_staff_email`（注册成功并完成邮箱 OTP 后，以当前登录用户身份写入绑定邮箱）
3. 脚本末尾含示例工号 `GH001`、`GH002`，**请改成你们真实工号**或删除示例。

### 2. 日常维护工号

在 **Table Editor → counselor_staff_registry**：

| 字段 | 说明 |
|------|------|
| `staff_id` | 主键，如 `GH001`（登录时自动转大写、去空格） |
| `display_name` | 可选展示名 |
| `is_active` | `false` 即停用，无法登录 |
| `bound_email` | **留空**直到该工号在 `portal-counselor.html` 完成首次「注册」；之后由系统自动写入，一般勿手改 |

新增咨询师：插入一行（只填 `staff_id`、`display_name`，`bound_email` 为空），通知对方用该工号 + 工作邮箱在 **注册** 页完成绑定。

### 3. 登录流程说明

| 步骤 | 注册 | 登录 |
|------|------|------|
| 工号 | 须在白名单且未绑定其他邮箱 | 须与白名单及已绑定邮箱一致 |
| 邮箱 OTP | Supabase 发 8 位码 | 同上 |
| 用户元数据 | `app_role: counselor`、`staff_id` | 同上 |

来访者 **个人空间**（`portal.html`）**不需要**工号，仅邮箱 OTP。

安全边界：`bind_counselor_staff_email` 只授予 `authenticated` 角色调用，且会校验 `auth.email()` 必须等于要绑定的邮箱；因此不能只凭工号从匿名请求里抢先绑定邮箱。
咨询师工作台恢复已有会话时，也会重新调用 `check_counselor_staff_access` 校验工号仍启用、绑定邮箱仍匹配；管理员停用工号或改绑后，旧会话会被要求重新登录。

---

## 五、本地预览

静态服务需能加载 `supabase-config.js`：

```bash
cd "/Users/heramaimai/Documents/New project 3"
python3 -m http.server 8899 --bind 127.0.0.1
```

- 来访者：`http://127.0.0.1:8899/portal.html`
- 咨询师：`http://127.0.0.1:8899/portal-counselor.html`

未配置或 `enabled: false` 时，仍为 **本机演示验证码**（页面上显示数字，不真实发信）。

页面已改为加载仓库内的 `vendor/supabase-js-2.49.1.min.js`，不再依赖浏览器实时访问 jsDelivr；部署时请一并上传 `vendor/` 目录。

---

## 六、安全说明

- 浏览器只使用 **anon key**；勿将 `service_role` key 写入前端。
- 生产环境在 Supabase **Authentication → URL Configuration** 配置 **Site URL** 与 **Redirect URLs**（你的正式域名）。
- 建议在 Supabase 设置 **Rate Limits**，防止刷邮件。
- RLS：若后续把留言、档案迁到 Supabase 表，需为 `visitor` / `counselor` 单独写 Row Level Security 策略（当前留言仍为本机 `localStorage` 演示）。

---

## 七、常见问题

| 现象 | 处理 |
|------|------|
| 点击获取验证码无邮件 | 查阿里云发信额度、垃圾箱；Supabase Auth 日志；SMTP 测试邮件是否成功 |
| 验证码错误 | 确认已开启 Email OTP；输入 8 位数字；邮箱与获取时一致 |
| 咨询师页提示来访者账号 | 该邮箱在 `portal.html` 注册为 `visitor`，需换邮箱或在后台改 `app_role` |
| 工号无效 / 工号校验服务未就绪 | 在 SQL Editor 执行 `supabase/schema.sql`；在表里添加对应 `staff_id` |
| 该工号尚未注册 | 首次须用 **注册** 标签完成工号+邮箱绑定 |
| 页面仍显示演示验证码 | 检查 `supabase-config.js` 是否存在、`enabled: true`、URL/Key 是否正确；浏览器控制台是否有 404 |
