使用Cursor + supabase + ChatGPT o3开发导航站后台(下)
/ 28 min read
本期我们来开发一个简单的导航站的管理后台功能,你将会完成一个具有登录注册、管理员设置、数据增/删/改/查的管理后台。同时你将会学习到以下技巧和技术栈:
1. 如何基于ChatGPT的o3,来开发一个详细的需求文档
2. 如何让Cursor自动生成默认的Cursor Rules,并且自己创建自定义Rules来限制开发要求
3. 如何创建/docs/*.md,来存放文档让Cursor更好的开发
4. 如何基于supabase来开发后台,并且实现数据库的增/删/改/查功能
注意:本篇教程是《使用v0 + supabase + Cursor创建一个导航站(上)》的下半部分,建议先着完上部分的教程内容再来着这一期的视频。
1. 生成项目文档
1.1 给到AI对应的提示词
• 让ChatGPT的o3,加上画布模式来生成我们的需求文档
现在有一个AI导航站,是基于supabase的,现在这个导航站有3个表:
1. AI工具表,定义如下:
```sqlcreate table public.ai_tools ( id uuid not null default uuid_generate_v4 (), name character varying(200) not null, slug character varying(200) not null, description text not null, full_description text null, website_url character varying(255) null, image_url character varying(255) null, category_id uuid not null, created_at timestamp without time zone not null default now(), updated_at timestamp without time zone not null default now(), user_id uuid null, is_approved boolean not null default true, logo_url text null, type text null, constraint ai_tools_pkey primary key (id), constraint ai_tools_slug_key unique (slug), constraint ai_tools_category_id_fkey foreign key (category_id) references categories (id), constraint ai_tools_user_id_fkey foreign key (user_id) references users (id) ) tablespace pg_default;
create index if not exists idx_ai_tools_category_id on public.ai_tools using btree (category_id) tablespace pg_default;
create index if not exists idx_ai_tools_user_id on public.ai_tools using btree (user_id) tablespace pg_default;```
2. AI工具分类表,定义如下:
```sqlcreate table public.categories ( id uuid not null default uuid_generate_v4 (), name character varying(100) not null, icon character varying(255) not null, created_at timestamp without time zone not null default now(), constraint categories_pkey primary key (id) ) tablespace pg_default;```
3. 公户提交的AI工具记录表,定义如下:
```sqlcreate table public.tool_submissions ( id uuid not null default uuid_generate_v4 (), name character varying(200) not null, description text not null, full_description text null, website_url character varying(255) not null, image_url character varying(255) null, category_id uuid not null, user_id uuid not null, status character varying(20) not null default 'pending'::character varying, admin_comments text null, created_at timestamp without time zone not null default now(), updated_at timestamp without time zone not null default now(), processed_at timestamp without time zone null, ai_tool_id uuid null, slug text not null, constraint tool_submissions_pkey primary key (id), constraint tool_submissions_category_id_fkey foreign key (category_id) references categories (id), constraint tool_submissions_user_id_fkey foreign key (user_id) references users (id), constraint tool_submissions_ai_tool_id_fkey foreign key (ai_tool_id) references ai_tools (id) ) tablespace pg_default;
create index if not exists idx_tool_submissions_user_id on public.tool_submissions using btree (user_id) tablespace pg_default;
create index if not exists idx_tool_submissions_status on public.tool_submissions using btree (status) tablespace pg_default;
create index if not exists idx_tool_submissions_category_id on public.tool_submissions using btree (category_id) tablespace pg_default;
create index if not exists idx_tool_submissions_created_at on public.tool_submissions using btree (created_at) tablespace pg_default;```
我先给的导航站创建一个后台,有3个侧边栏:1. 概览:- 顶部右上角具有添加按钮,支持用户添加AI工具。点击后从右侧弹出Drawer,Drawer内有表单支持用户添加AI工具到ai_tools这张表中。
- 顶部显示4个卡片,包括:AI工具数量(获取ai_tools的数据量),AI分类数量(获取categories的数量),以及当日用户提交(获取今天在ai_tool_submissions中的数据)、未审核的用户提交(获取ai_tool_submissions 中未审核的数据)
- 底部显示所有的AI工具,并且支持查看和编辑功能(获取ai_tools的数据量)
2. 审核:查看当前未审核的用户提交(获取 ai_tool_submissions 中未审核的数据),管理员能够进行审核,将ai_tool_submissions中的status修改成为success。并且审核通过后,将数据添加到ai_tools这张表中
3. 用户管理:管理员能够设置用户是否是管理员,只有是管理员的用户才能登陆进入该后台。否则不允许进入该后台,请你信创建一个admin_users的表来实现这个逻辑。
请你根据我的上述需求,给到我一个完整的需求文档1.2 AI生成的需求文档
下面是ChatGPT 03帮我生成的需求文档,我们可以基于ChatGPT的画布模式优化这个需求文档。删除我们觉得没有必要,或者AI过度设计的内容部分。然后这个需求文档中帮我们新增了 admin_users这一张用户保存管理员用户的表,但是并没有给我们RLS的策略,所以这边我们来让AI帮我们新增一下,输入以下提示词:
•提示词:
请你帮我给admin_users这张表添加RLS开启和相关的策略约束什么是RLS策略?
我们在supabase的课程中有详细的讲到RLS策略,这里我们在来回顾一下。RLS 的作用是:
1. 精细控制访问权限:决定哪些用户可以着到、添加、修改或删除哪些数据行
2. 自动过滤数据:用户查询时,只会看到他们有权限看到的数据
3. 安全在数据库层实现:不依赖前端代码,防止安全漏洞
接下来是我完整的需求文档部分,你可以直接复制粘贴使用:
# AI 导航站后台需求文档
---
## 1. 项目概述
本后台系统用于管理 **AI 导航站** 的核心数据(AI 工具、分类、用户提交记录)并支撑运营人员进行内容审核及用户权限管理。
## 2. 目标与范围
* **目标**:
1. 提供直观可视化的运营数据概览。 2. 支持运营人员在后台快速添加 / 编辑 AI 工具。 3. 支持审核用户提交的 AI 工具申请并一键入库。 4. 支持为用户分配或撤销管理员权限,保障后台安全访问。
* **范围**:仅涵盖后台管理端(前端 Next.js + Supabase *Server Role* API),不包括导航站前台页面。
## 3. 角色与权限
| 角色 | 说明 | 权限 || -------- | ----------------------- | -------------------------- || **管理员** | 通过 `admin_users` 表认定的用户 | 可访问后台全部功能模块;可设定其他用户是否为管理员。 || **普通用户** | 默认注册用户 | 仅能通过前台发起工具提交;无法访问后台管理端。 |
## 4. 数据库表概览(已有)
* **ai\_tools** – 已上架工具主表。* **categories** – 工具分类表。* **tool\_submissions** – 用户提交记录表。
### 4.1 新增:`admin_users`
```sqlcreate table public.admin_users ( user_id uuid primary key references auth.users(id) on delete cascade, created_at timestamp without time zone default now());-- 启用 Row‑Level Securityalter table public.admin_users enable row level security;
-- 1) 仅允许已存在的管理员查看记录(判断当前 uid 是否已在表内)create policy "Admins can read admin_users" on public.admin_users for select using (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));
-- 2) 仅允许已存在的管理员添加新管理员create policy "Admins can add admins" on public.admin_users for insert with check (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));
-- 3) 仅允许已存在的管理员移除管理员create policy "Admins can remove admins" on public.admin_users for delete using (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));```
> **约定**:判定管理员身份时,仅检查当前登录用户 `uid` 是否存在于 `admin_users` 表。
###
## 5. 功能模块
### 5.1 登录与权限校验
* 所有 `/api/admin/**` 路由及后台页面使用中间件校验 `auth.uid()` 是否存在于 `admin_users`。* 无权限访问时重定向至前台首页或展示 403 页面。
### 5.2 概览(Sidebar #1)
| 功能 | 说明 || ------------------ | -------------------------------------------------------------------------------------------------------------------------------- || **顶部操作栏** | 右上角「添加 AI 工具」按钮 -> 右侧 Drawer 表单;表单字段:名称、Slug、描述、分类、网站链接、Logo/图片等。 || **统计卡片 ×4** | 1) AI 工具总数(`ai_tools`) 2) 分类总数(`categories`) 3) 当日提交数(`tool_submissions` 当天) 4) 未审核提交数(`tool_submissions.status = 'pending'`) || **工具列表 DataTable** | 列字段:名称、分类、类型、创建时间、状态;支持搜索、分页、查看、编辑。 || **查看/编辑 Drawer** | 与新增表单复用;提交后更新 `ai_tools` 表并刷新列表。 |
### 5.3 审核(Sidebar #2)
| 功能 | 说明 || --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- || **待审核列表** | 默认筛选 `tool_submissions.status = 'pending'`;字段:名称、用户、分类、提交时间。 || **审核操作** | - **通过**:事务内执行 1) 插入 `ai_tools`(字段映射) 2) 将 `tool_submissions.status` 更新为 `'success'`, `ai_tool_id` 关联。 \|\n- **拒绝**:更新 `status = 'rejected'` 并填写 `admin_comments`。 || **批量操作** | 复选框 + 批量通过 / 拒绝。 |
### 5.4 用户管理(Sidebar #3)
| 功能 | 说明 || -------- | ------------------------------------- || **用户列表** | 来源 `auth.users`;列:Email、注册时间、是否管理员。 || **权限切换** | 开关按钮:写入 / 删除 `admin_users`。操作成功后即刻生效。 |
## 6. UI / 交互规范
* **框架**:Next.js 14, Tailwind CSS, shadcn/ui;暗色 & 亮色自适应。* **布局**:固定侧边栏(宽 240px)+ 顶部导航栏(含用户头像下拉)。* **Drawer**:宽度 480px,从右滑入;ESC 或点击遮罩关闭,保留未提交的表单状态。* **DataTable**:基于 shadcn `DataTable` 组件,支持列排序、搜索、分页(50 / 页)。* **表单校验**:前端使用 `zod` + React Hook Form;后端再次校验必填字段非空。
## 7. API 设计(仅展示核心)
| 方法 | 路径 | 功能 | 备注 | | || ---- | ------------------------------------- | ------------ | -------------------------- | ------- | ---------- || GET | `/api/admin/overview/stats` | 获取四张统计卡的数据 | | | || GET | `/api/admin/ai-tools` | 分页获取 AI 工具列表 | `?page=&pageSize=&search=` | | || POST | `/api/admin/ai-tools` | 新增 AI 工具 | body: 工具信息 | | || PUT | `/api/admin/ai-tools/{id}` | 更新 AI 工具 | | | || GET | `/api/admin/submissions` | 获取提交列表 | \`?status=pending | success | rejected\` || POST | `/api/admin/submissions/{id}/approve` | 审核通过 & 入库 | | | || POST | `/api/admin/submissions/{id}/reject` | 审核拒绝 | body: `admin_comments` | | || GET | `/api/admin/users` | 获取用户列表 | | | || POST | `/api/admin/users/{id}/toggle-admin` | 切换管理员状态 | | | |
> **注意**:所有 `/api/admin/**` 路由需在 Middleware 中验证 `admin_users`,请求上下文使用 Supabase **Service Role** Key 执行以绕过 RLS。>> ##2. 初始化项目
2.1 安装模板
2.1.1 使用next.js + supabase的官方模板
访问next.js的官网,在Templates中搜索supabase提供的官方模板。我们使用这套模板开发我们的这个管理后台项目。
安装这套模板也非常简单,我们在我们的课程中也多次使用到了:
• 如果你使用的npm,直接在命令中输入:
npx create-next-app --example with-supabase dirs-admin• 如果你和我一样,用的pnpm,输入这个命令:
pnpm create next-app --example with-supabase dirs-admindirs-admin 是项目文件夹的名称,你可以修改成为自己想命名的名称。
如果你下载遇到了网络问题,也可以直接使用我们提供的这个代码初始文件包:
dirs-admin.zip 1.1MB
安装成功后,用Cursor打开项目即可,然后在命令行中安装相关的依赖:
• 安装依赖:
pnpm install2.1.2 放入需求文档
接下来我们把AI给到的需求文档放到我们的项目当中:
1. 项目的根目录下创建一个 docs/ 目录
2. 创建一个 /docs/prd.md 文件
3. 复制粘贴刚才1.2中AI给到我们的需求文档
2.2 填写环境变量
• 将项目根目录下的.env.example 修改成为 .env.local,并且添加 SUPABASE_SERVICE_KEY 这个环境变量。
• 在MemfireDB的【设置】-【API】-【API设置】中,找到对应的应用URL和公钥,填入到 .env.local中
对应关系如下:
NEXT_PUBLIC_SUPABASE_URL=your-project-urlNEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-keySUPABASE_SERVICE_KEY=your-service-keymNEXT_PUBLIC_SUPABASE_URL : 应用URL
NEXT_PUBLIC_SUPABASE_ANON_KEY : anon 公钥
SUPABASE_SERVICE_KEY : service_role 私钥
2.3 增加管理角色supabase
• 提示词:
@supabase 创建一个admin.ts,使用@supabase/supabase-js,调用环境变量中SUPABASE_SERVICE_KEY初始化supabase,这是服务端私钥,以确保是service_role注意:你 @supabase 文件夹的操作,需要自己手动@,直接复制提示词没有效果!
等待AI生成完成,这一步非常关键,AI应该会正确的创建 utils/supabase/admin.ts 文件,文件内容如下:
import { createClient } from '@supabase/supabase-js';
// Create a Supabase client with the service role key// This allows full access to the database without RLS restrictions// Should ONLY be used in server-side codeexport const createAdminClient = () => { const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseServiceKey = process.env.SUPABASE_SERVICE_KEY;
if (!supabaseUrl || !supabaseServiceKey) { throw new Error('Missing Supabase environment variables for admin client'); }
return createClient(supabaseUrl, supabaseServiceKey, { auth: { autoRefreshToken: false, persistSession: false } });};为什么要使用服务端私钥?
服务端私钥有最高权限,可以不受数据库中表的RLS策略的限制,这样能让我们的管理员更加方便的操作数据库,对其中的几张表进行增/删/改/查。我们只需要确保在操作前,校验这个用户是管理员用户就行了。
2.4 创建对应的rules
接下来我们来创建一下Cursor Rules,首先我们会通过Cursor 0.49版本引入的 【Generate Cursor Rules】的功能,来自动创建这个项目对应的Cursor Rules。之后我们再来编写一些自定义的Cursor Rules。
2.4.1 Cursor自动生成Cursor Rules
•在Agent模式下,输入反斜杠,点击 Generate Cursor Rules。让AI自动的创建 Cursor Rules
•等待AI生成完成,你会看到在 .cursor/rules 中Cursor会自动的帮我们创建对应的 Cursor Rules.
2.4.2 创建自定义Cursor Rules
• project-rule.mdc:
# 项目开发规则
1. 所有的数据库请求逻辑,都需要新建api实现,不能直接通过supabase-sdk操作
2. api的所有请求,都使用utils/supabase/admin.ts进行操作,避免RLS策略
3. 所有的api请求逻辑,都需要通过中间件校验用户是否是管理员。通过用户是否在 `admin_users`这张表中。如果该用户不是管理员,则接口报错。
4. 所有的文案都使用中文的,这是一个中文网站• 记得把这个设置为Always
2.5 启动项目
到这一步,初始化项目就完成了,在命令行执行 npm run dev,然后开始进如实际的业务开发中吧!
记得用Git提交一下对应的修改哦!有一个好的Git版本管理的习惯~
3. 用户登陆+管理功能
3.1修改页面
• 输入下面的提示词:
根据 @prd.md 需求文档,将 @page.tsx 把首页修改成为后台的简单首页,用户点击可以进行登陆和注册,注册默认为普通用户,登陆用户后需要进行校验,如果不为管理员,不允许登陆。登陆注册采用现在项目中已有的页面,不要自己创建。首页只作为跳转使用测试登陆注册功能,如果遇到问题进行优化,你可能会遇到以下问题,跟着进行修改(注意:你遇到的问题可能会和我遇到的不一致,我们根据具体问题具体修改)
• 首页布局问题
提示词:
@layout.tsx 移除掉默认的布局,只保留子页面• 登录注册页相关的布局问题
• 提示词:
1. 优化 @(auth-pages) 的相关页面布局,要求居中显示2. 同时优化文案,为中文3.2 添加sql
在memfireddb中添加管理员的表
• SQL语句:
create table public.admin_users ( user_id uuid primary key references auth.users(id) on delete cascade, created_at timestamp without time zone default now());-- 启用 Row‑Level Securityalter table public.admin_users enable row level security;
-- 1) 仅允许已存在的管理员查看记录(判断当前 uid 是否已在表内)create policy "Admins can read admin_users" on public.admin_users for select using (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));
-- 2) 仅允许已存在的管理员添加新管理员create policy "Admins can add admins" on public.admin_users for insert with check (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));
-- 3) 仅允许已存在的管理员移除管理员create policy "Admins can remove admins" on public.admin_users for delete using (exists (select 1 from public.admin_users au where au.user_id = auth.uid()));3.3 添加管理员账号
•点击【认证】,选择你想添加进入管理员的用户id,复制用户UID
• 点击【表编辑器】,打开 admin_users这张表,添加插入一条数据。
。user_id 为你刚才复制的用户id
。is_super 选择 TRUE
3.4 测试
测试登陆,目前管理员登录功能是好使的了。但是现在有个bug 就是,如果不是管理员,依然可以登录,但是是跳转了到/protected 页面,这里我们来让AI进行修改:
•提示词:
现在有个bug,我希望如果我不是管理员,登录都不应该登录成功的,也不应该自动跳转到/protected页面,而是报错。如果遇到报错,把明确的报错信息给到Cursor,让Al进行修改修改。
3.5 完成用户管理
接下来我们来完成用户管理页面 /admin/users
• 提示词
输入以下提示词:
创建/admin/users页面,并完成设置管理员和移除管理员的功能如果遇到报错,把明确的报错信息给到Cursor,让AI进行修改修改。我们这里遇到一个登陆的报错,直接在控制台找到报错信息,复制粘贴给Cursor让其修改。
如果正常的话,你就完成了用户管理这个页面的开发。
4. 完成概览+工具管理
4.1 放入数据库文档
• 在 docs/sql.md 下创建一个数据库文档,用于之后的业务逻辑中编写后端逻辑。
# 数据库表结构
1. AI工具表:用于存放项目中的所有AI工具
```sqlcreate table public.ai_tools ( id uuid not null default uuid_generate_v4 (), name character varying(200) not null, slug character varying(200) not null, description text not null, full_description text null, website_url character varying(255) null, image_url character varying(255) null, category_id uuid not null, created_at timestamp without time zone not null default now(), updated_at timestamp without time zone not null default now(), user_id uuid null, is_approved boolean not null default true, logo_url text null, type text null, constraint ai_tools_pkey primary key (id), constraint ai_tools_slug_key unique (slug), constraint ai_tools_category_id_fkey foreign key (category_id) references categories (id), constraint ai_tools_user_id_fkey foreign key (user_id) references users (id) ) tablespace pg_default;
create index if not exists idx_ai_tools_category_id on public.ai_tools using btree (category_id) tablespace pg_default;
create index if not exists idx_ai_tools_user_id on public.ai_tools using btree (user_id) tablespace pg_default;```
2. AI工具分类表:用于存放项目中的所有AI工具分类
```sqlcreate table public.categories ( id uuid not null default uuid_generate_v4 (), name character varying(100) not null, icon character varying(255) not null, created_at timestamp without time zone not null default now(), constraint categories_pkey primary key (id) ) tablespace pg_default;```
3. 用户提交的AI工具记录表:用于存放用户提交的AI工具记录
```sqlcreate table public.tool_submissions ( id uuid not null default uuid_generate_v4 (), name character varying(200) not null, description text not null, full_description text null, website_url character varying(255) not null, image_url character varying(255) null, category_id uuid not null, user_id uuid not null, status character varying(20) not null default 'pending'::character varying, admin_comments text null, created_at timestamp without time zone not null default now(), updated_at timestamp without time zone not null default now(), processed_at timestamp without time zone null, ai_tool_id uuid null, slug text not null, constraint tool_submissions_pkey primary key (id), constraint tool_submissions_category_id_fkey foreign key (category_id) references categories (id), constraint tool_submissions_user_id_fkey foreign key (user_id) references users (id), constraint tool_submissions_ai_tool_id_fkey foreign key (ai_tool_id) references ai_tools (id) ) tablespace pg_default;
create index if not exists idx_tool_submissions_user_id on public.tool_submissions using btree (user_id) tablespace pg_default;
create index if not exists idx_tool_submissions_status on public.tool_submissions using btree (status) tablespace pg_default;
create index if not exists idx_tool_submissions_category_id on public.tool_submissions using btree (category_id) tablespace pg_default;
create index if not exists idx_tool_submissions_created_at on public.tool_submissions using btree (created_at) tablespace pg_default;```
4. 管理员表:用于存放后台管理员信息
```sqlcreate table public.admin_users ( user_id uuid not null, created_at timestamp without time zone null default now(), constraint admin_users_pkey primary key (user_id), constraint admin_users_user_id_fkey foreign key (user_id) references users (id) on delete cascade ) tablespace pg_default;```4.2 卡片数据
首先我们来开发后台【概览】页面的卡片数据,输入以下提示词:
• 提示词:
@page.tsx 完成AI工具导航站的数据卡片数据获取和渲染逻辑,参考@sql.md等Cursor开发成功后,可以测试管理后台的卡片数据,完成
4.3 完成新建工具
表单参考截图如下:
现在我们来完成新增工具的提交功能,输入下面的提示词(记得复制表单参考截图哦~)
提示词:
1. 完成 @page.tsx 添加AI工具的逻辑,跳转到/admin/tools/new页面并完成表单提交的前后端逻辑。参考图片中的字段,改页面提交的录入工具的数据会放到 @sql.md 的ai_tools这一张表
2. 图片需要采用上传组件,上传到supabase的storage中后保存对应的url。图标上传到tool-logos这个bucket中,展示图片上传到tool-previews这个bucket中
3. full_description保存的是markdown语法,并且要支持markdown的预览样式
4. 务必添加成功后,添加到ai_tools这一张表中如果有任何的报错,记得同样给到明确的错误给到Cursor,让AI进行修改就好。
4.4 完成编辑/查看/删除功能现在我们来完成AI工具的编辑、查看和删除功能。
• 提示词:
@page.tsx 请你帮我完成这个页面的列表中编辑、查看和删除功能等待AI修改完成,如果编辑页面404,这个时候我们需要新增加页面,给Cursor对应的提示词:
编辑页面现在是404,请你创建这个页面,并且参考我新建时的表单,能够实现编辑功能5. 完成【审核】
现在我们来完成最后的【审核中心】页面,输入对应的提示词:
• 提示词:
完成审核中心页面/admin/submissions,参考 @sql.md 的逻辑:1. 用户在这个页面能够查看到所有未审核通过的数据,并且点击弹框查看详情2. 管理员能够查看并且通过,如果点击通过之后,将ai_tool_submissions表中该工具的数据插入到ai_tools中。并修改对应的状态3. 管理员也能点击驳回,驳回用户的AI工具提交,并修改对应的状态这一步基本没有太多问题,但是我们之前遗漏了用户提交这张表 ai tool submissions 中AI工具logo的字段创建,导致logo无法正常显示,这个问题不影响核心流程。这个bug修改的点比较多,你可以参考我们的视频内容进行修改~
AI开发完成后,进行实际的测试,如果成功的话,我们就可以去审核用户上传的AI具并且发布。
目前这个产品功能基本就开发完成了,但是【AI分类】还有一个数据库的增/删/改/查我们没有实现,大家可以自己基于Cursor实现。
6. 源码
6.1 初始化项目源码
dirs-admin.zip 1.1MB
6.2 最终项目源码
ai-dirs-admin-main.zip 707.2KB