> ## Documentation Index
> Fetch the complete documentation index at: https://help.helloazhenweb.top/llms.txt
> Use this file to discover all available pages before exploring further.

# 为 Birthday Wall 祝福墙配置 Supabase

> 创建 Supabase 项目、运行数据库迁移以创建所需的数据表，并获取部署所需的 API 密钥。

Birthday Wall 把祝福墙留言、页面可见性设置、管理员用户和注册码存储在 Supabase Postgres 数据库中。本页将带你完成创建项目、运行迁移 SQL、并找到需要写入环境变量的凭据。

<Steps>
  <Step title="创建一个 Supabase 项目">
    1. 前往 [supabase.com](https://supabase.com) 并登录（或免费注册账号）。
    2. 在组织面板中点击 **New project**。
    3. 给项目起名（如 `birthday-wall`），选择数据库密码，并选择离用户最近的区域。
    4. 点击 **Create new project** 并等待项目创建完成 — 通常大约 30 秒。
  </Step>

  <Step title="运行数据库迁移">
    所有所需的数据表都由一份 SQL 脚本一次性创建。在你的 Supabase 项目中：

    1. 点击左侧栏的 **SQL Editor**。
    2. 点击 **New query**。
    3. 粘贴下面的完整迁移脚本，然后点击 **Run**。

    ```sql theme={null}
    -- ========================================
    -- Table: messages
    -- Stores birthday wish wall submissions
    -- ========================================

    create table messages (
      id uuid default gen_random_uuid() primary key,
      content text not null,
      ip_hash text not null,
      color text not null,
      created_at timestamp with time zone default timezone('utc'::text, now()) not null
    );

    -- Allow anyone to read messages (displayed on the home page)
    alter table messages enable row level security;
    create policy "Allow public read messages" on messages
      for select using (true);

    -- Only the service role can insert or delete messages
    create policy "Service role full access messages" on messages
      for all using (auth.role() = 'service_role');


    -- ========================================
    -- Table: page_configs
    -- Controls which pages appear in the bottom navigation bar
    -- ========================================

    create table page_configs (
      id text primary key,
      name text not null,
      path text not null,
      icon text,
      visible boolean not null default true,
      "order" integer not null default 0
    );

    -- Insert default page configuration
    insert into page_configs (id, name, path, icon, visible, "order") values
      ('home', '首页', '/', '🏠', true, 1),
      ('write', '写祝福', '/write', '✏️', true, 2),
      ('report', '报告', '/report', '📊', true, 3),
      ('games', '游戏室', '/games', '🎮', true, 4);

    -- Allow anyone to read page configs
    create policy "Allow public read page_configs" on page_configs
      for select using (true);

    -- Only the service role can modify page configs
    create policy "Service role full access page_configs" on page_configs
      for all using (auth.role() = 'service_role');


    -- ========================================
    -- Table: users
    -- Stores admin user records linked to Supabase Auth
    -- ========================================

    create table users (
      id uuid primary key references auth.users(id) on delete cascade,
      email text,
      role text not null default 'user' check (role in ('admin', 'user')),
      created_at timestamp with time zone default timezone('utc'::text, now()) not null
    );

    -- Only the service role can access user records
    create policy "Service role full access users" on users
      for all using (auth.role() = 'service_role');


    -- ========================================
    -- Table: registration_codes
    -- Used for invitation-code-gated admin registration
    -- ========================================

    create table registration_codes (
      id uuid default gen_random_uuid() primary key,
      code text not null unique,
      used boolean not null default false,
      created_at timestamp with time zone default timezone('utc'::text, now()) not null
    );

    -- Allow anyone to read codes (needed for client-side verification)
    create policy "Allow public read registration_codes" on registration_codes
      for select using (true);

    -- Only the service role can modify registration codes
    create policy "Service role full access registration_codes" on registration_codes
      for all using (auth.role() = 'service_role');


    -- ========================================
    -- Optional: seed an initial admin registration code
    -- Replace 'ADMIN123' with a secret value before running
    -- ========================================

    insert into registration_codes (code) values ('ADMIN123');
    ```

    <Warning>
      运行之前，请将 `ADMIN123` 改成一个难以猜测的唯一值。任何拿到这个码的人都可以注册管理员账号。
    </Warning>

    SQL 执行成功后，你应当能在 **Table Editor** 中看到全部四张表 — `messages`、`page_configs`、`users` 和 `registration_codes`。
  </Step>

  <Step title="获取 API 凭据">
    Next.js 应用需要从 Supabase 获取三个值。请在 Supabase 仪表板中进入 **Project Settings → API**：

    | 凭据                    | 在哪里找到                                     | 用于                              |
    | --------------------- | ----------------------------------------- | ------------------------------- |
    | **Project URL**       | "Project URL" 字段                          | `NEXT_PUBLIC_SUPABASE_URL`      |
    | **anon / public key** | "Project API keys" → `anon public`        | `NEXT_PUBLIC_SUPABASE_ANON_KEY` |
    | **service\_role key** | "Project API keys" → `service_role`（点击显示） | `SUPABASE_SERVICE_ROLE_KEY`     |

    把三个值都复制下来 — 下一步会用到。
  </Step>

  <Step title="把凭据添加为环境变量">
    将三个 Supabase 值添加到你的环境中。完整的必填变量清单和一份可直接粘贴的 `.env.local` 模板，见 [环境变量页面](/zh/self-hosting/environment)。
  </Step>
</Steps>

## 行级安全（Row Level Security）

迁移 SQL 会自动在所有表上启用行级安全（RLS）策略。每条策略的作用如下：

| 表                    | 策略                         | 效果                        |
| -------------------- | -------------------------- | ------------------------- |
| `messages`           | `Allow public read`        | 任何人都可读取在首页展示的祝福           |
| `messages`           | `Service role full access` | 只有服务端 API 路由可以插入或删除祝福     |
| `page_configs`       | `Allow public read`        | 任何人都可读取页面可见性设置            |
| `page_configs`       | `Service role full access` | 只有服务端 API 路由可以切换页面可见性     |
| `users`              | `Service role full access` | 只有服务端 API 路由可以读写用户记录      |
| `registration_codes` | `Allow public read`        | 客户端代码可在提交前校验注册码           |
| `registration_codes` | `Service role full access` | 只有服务端 API 路由可以创建或标记注册码已使用 |

<Warning>
  `service_role` 密钥绕过所有 RLS 策略，对整个数据库拥有不受限的访问权限。绝不要在浏览器代码中暴露它，也不要把它提交到仓库。Birthday Wall 只会在服务端 API 路由中使用这个密钥 — 它永远不会被发送到浏览器。
</Warning>

相对地，`anon` 密钥是有意公开的。它通过 `NEXT_PUBLIC_` 前缀嵌入到你的前端包中，可以安全暴露 — 它能做什么、不能做什么由 RLS 策略来保障。

## 创建第一个管理员用户

建表完成后，你需要一个初始管理员账号来访问 `/admin`：

1. 在 Supabase 仪表板中前往 **Authentication → Users → Add user**。
2. 输入邮箱和强密码，然后点击 **Create user**。
3. 复制用户列表中显示的 UUID。
4. 打开 **SQL Editor** 并运行：

```sql theme={null}
INSERT INTO users (id, email, role)
VALUES ('<paste-uuid-here>', 'your@email.com', 'admin');
```

或者，你也可以使用前面在 SQL 中插入的注册码，通过 `/admin/login` 界面进行注册 — 任何使用有效注册码创建的账号都会被自动赋予 `admin` 角色。
