使用v0+ supabase + Cursor创建一个导航站(上)
/ 28 min read
本期提示:
1. 本期教程我们会使用v0+ MemfiredDb + Cursor开发一个全栈导航站
2. 请务必学习完Next.js和Supabase的课程,再来学习本期课程
如果大家v0的额度不够,可以直接使用下方的源码开始项目。
ai-tools-directory.zip 5.2MB
1. 使用v0创建前端页面
我们来模仿这个https://ai-bot.cn/Al导航站的前端页面,鼠标右键点击检查,然后删除掉页面的一些额外的元素,复制这个截图。
1.2 让V0生成项目
• 然后点击进入V0,把上面的的截图提示词丢给V0,让其生成对应的页面。
提示词:
我正在创建一个 AI 工具导航网站,页面布局与功能如下:
### 一、导航页面结构
- 左侧为固定的分类侧边栏,点击某个分类后,会自动滚动定位到当前页面中该分类所在的位置。
- 右侧区域包含以下模块:
1. 页面顶部展示:
- 热门工具
- 最新收录
2. 分类展示区:展示当前页面各分类下的工具列表(与左侧分类联动)
3. 页面右上角包含:
- 站内搜索功能,可按关键词或分类筛选工具
- 登录按钮(未登录状态)或个人中心按钮(登录后状态)
4. 左侧分类栏底部添加“提交站点”按钮:
- 若用户已登录,点击跳转到个人中心中的“提交表单”Tab
- 若用户未登录,点击自动跳转登录页面,登录成功后进入表单页面
- 用户点击某个 AI 工具卡片后,新标签页打开工具详情页,包含:
- 顶部封面图(参考图2)
- 工具标题、所属分类
- 打开网站按钮(跳转至官网)
- 下方内容区包括工具简要介绍(content)和常见问题(QA)
---
### 二、用户系统与个人中心功能
- 登录后,用户可进入“个人中心”,包含三个 Tab:
1. **提交历史**
- 展示用户提交的工具列表
- 包括工具名、提交时间、审核状态(审核中 / 已发布)
2. **提交表单**
- 用于添加新的 AI 工具,字段包括:
- 网站名称、网站的slug、网址
- 网站 Logo(上传组件)
- 网站预览图(上传组件)
- 工具概要(简短描述)
- 工具详细介绍文档(Markdown/富文本)
- 用户点击“提交”后,该工具状态将为“审核中”,等待管理员审核
3. **个人信息**
- 展示用户基本资料
- 提供“退出登录”按钮• 等待生成的效果
1.3 优化对应的前端
•优化1:
•提示词
有几个问题:1. 首页的header应该固定2. 进入到工具详情页面后,工具介绍和常见问题的tab给移除掉,在下方只显示工具介绍的文案,场景问题会在文案中。相关工具卡片也给移除掉。3.进入到user页面后的3个tab在左侧实现,左右布局的方方式• 优化2:
以下修改:1.我的提交处,如果是审核中,不运行编辑。操作变为--2. 个人信息页面的样式优化一下不要用卡片显示,退出登陆的按钮也不要红色。• 优化3:
首页的工具部分有两处修改:1.移除掉查看全部,因为工具就是全部显示。2. 卡片移除掉tag,以及工具的文案介绍只一行显示,如果超出就.1.4 下载到Cursor
• 点击右上角的下载按钮,下载Zip
• 用Cursor打开项目,分别执行 npm install 和 npm run dev, 跑起来这个项目
• 访问 localhost:3000,查看本地的项目
2. 初始化MemfireDB(Supabase)+ 登陆注册逻辑
2.1 进入到MemfireDB
本次我们使用MemfireDB开发实际的项目需求,MemfireDB是一个国内的supabase提供商
MemfireDB对于supabase的优势在于部署国内,访问速度更快更便宜。但是它目前没有免费的应用。需要用户自己购买一个19.9元的版本。
2.2. 让AI完成登录注册的逻辑
• 在Cursor中输入以下提示词,让Cursor帮我们编写完成登录注册逻辑
@https://github.com/vercel/next.js/tree/canary/examples/with-supabase 请你参考这个项目的登录注册,实现我们项目的登录注册功能,使用邮箱密码登录。• Al生成完毕后,在项目的根目录创建.env.local 文件:
NEXT_PUBLIC_SUPABASE_URL=NEXT_PUBLIC_SUPABASE_ANON_KEY=•在MemfireDB的【设置】-【API】-【API设置】中,找到对应的应用URL和公钥,填入到.env.local中
• 测试并修改登录注册逻辑
3. 让AI设计表结构和数据存储方案
3.1 创建数据库设计方案
• 提示词:
请你根据项目,帮我设计3张表,并给到我完整的一个markdown的设计文档,我要求:1.首页左侧分类的数据,设计为一张表2.AI工具设计一张表3.用户的提交工具记录(当审核完成之后,会放到AI工具的表中)
请你给到我设计方案,并给出对应的sql3.2 创建数据存储设计方案
• 提示词:
接下来,请你帮我创建对应的数据存储的bucket,我会存放三个数据1.侧边栏分类的icon的图片2.工具的logo3.工具的预览图
请你给到我设计文档3.3 文档参考
数据库设计方案(展开查看)
- supabase-sqls.mdc:
# AI工具集数据库设计方案
## 概述
本文档详细说明了AI工具集平台的简化数据库设计方案。该设计包含三个主要表:1. `categories` - 存储首页左侧分类信息2. `ai_tools` - 存储已审核通过的AI工具信息3. `tool_submissions` - 存储用户提交的工具记录(待审核)
## 表结构设计
### 1. Categories 分类表
存储工具分类信息,用于首页左侧分类导航。
#### 表结构
| 字段名 | 类型 | 约束 | 描述 ||--------|------|------|------|| id | UUID | PRIMARY KEY | 分类唯一标识符 || name | VARCHAR(100) | NOT NULL | 分类名称 || icon | VARCHAR(255) | NOT NULL | 分类图标代码 || created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 创建时间 |
#### 索引- PRIMARY KEY (`id`)
### 2. AI Tools 工具表
存储已审核通过的AI工具信息。
#### 表结构
| 字段名 | 类型 | 约束 | 描述 ||--------|------|------|------|| id | UUID | PRIMARY KEY | 工具唯一标识符 || name | VARCHAR(200) | NOT NULL | 工具名称 || slug | VARCHAR(200) | NOT NULL, UNIQUE | 工具URL友好标识 || description | TEXT | NOT NULL | 工具简短描述 || full_description | TEXT | | 工具详细描述 || website_url | VARCHAR(255) | | 工具官网URL || image_url | VARCHAR(255) | | 工具图片URL || category_id | UUID | NOT NULL, REFERENCES categories(id) | 所属分类ID || created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 创建时间 || updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 更新时间 || user_id | UUID | REFERENCES auth.users(id) | 提交者ID(如果适用) || is_approved | BOOLEAN | NOT NULL, DEFAULT true | 是否已审核通过 |
#### 索引- PRIMARY KEY (`id`)- UNIQUE (`slug`)- INDEX (`category_id`)- INDEX (`user_id`)
### 3. Tool Submissions 工具提交记录表
存储用户提交的工具记录,待管理员审核。
#### 表结构
| 字段名 | 类型 | 约束 | 描述 ||--------|------|------|------|| id | UUID | PRIMARY KEY | 提交记录唯一标识符 || name | VARCHAR(200) | NOT NULL | 工具名称 || description | TEXT | NOT NULL | 工具简短描述 || full_description | TEXT | | 工具详细描述 || website_url | VARCHAR(255) | NOT NULL | 工具官网URL || image_url | VARCHAR(255) | | 工具图片URL || category_id | UUID | NOT NULL, REFERENCES categories(id) | 所属分类ID || user_id | UUID | NOT NULL, REFERENCES auth.users(id) | 提交者ID || status | VARCHAR(20) | NOT NULL, DEFAULT 'pending' | 状态(pending、approved、rejected) || admin_comments | TEXT | | 管理员审核意见 || created_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 创建时间 || updated_at | TIMESTAMP | NOT NULL, DEFAULT NOW() | 更新时间 || processed_at | TIMESTAMP | | 处理时间 || ai_tool_id | UUID | REFERENCES ai_tools(id) | 关联的已审核工具ID(如果已通过审核) |
#### 索引- PRIMARY KEY (`id`)- INDEX (`user_id`)- INDEX (`status`)- INDEX (`category_id`)- INDEX (`created_at`)
## 关系图
```categories ^ | | (一对多) |ai_tools ----(一对一)----> tool_submissions ^ | | (一对多) |auth.users```
## SQL 创建语句
### 创建 Categories 表
```sqlCREATE TABLE categories ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL, icon VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW());```
### 创建 AI Tools 表
```sqlCREATE TABLE ai_tools ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(200) NOT NULL, slug VARCHAR(200) NOT NULL UNIQUE, description TEXT NOT NULL, full_description TEXT, website_url VARCHAR(255), image_url VARCHAR(255), category_id UUID NOT NULL REFERENCES categories(id), created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), user_id UUID REFERENCES auth.users(id), is_approved BOOLEAN NOT NULL DEFAULT true);
CREATE INDEX idx_ai_tools_category_id ON ai_tools(category_id);CREATE INDEX idx_ai_tools_user_id ON ai_tools(user_id);```
### 创建 Tool Submissions 表
```sqlCREATE TABLE tool_submissions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(200) NOT NULL, description TEXT NOT NULL, full_description TEXT, website_url VARCHAR(255) NOT NULL, image_url VARCHAR(255), category_id UUID NOT NULL REFERENCES categories(id), user_id UUID NOT NULL REFERENCES auth.users(id), status VARCHAR(20) NOT NULL DEFAULT 'pending', admin_comments TEXT, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), processed_at TIMESTAMP, ai_tool_id UUID REFERENCES ai_tools(id));
CREATE INDEX idx_tool_submissions_user_id ON tool_submissions(user_id);CREATE INDEX idx_tool_submissions_status ON tool_submissions(status);CREATE INDEX idx_tool_submissions_category_id ON tool_submissions(category_id);CREATE INDEX idx_tool_submissions_created_at ON tool_submissions(created_at);```
## 数据流程
1. **用户提交工具**: - 用户提交工具信息,记录存入 `tool_submissions` 表 - 状态设为 "pending"
2. **管理员审核**: - 管理员审核提交的工具 - 根据审核结果更新 `tool_submissions` 表中的状态
3. **审核通过后**: - 系统将 `tool_submissions` 中的数据转移到 `ai_tools` 表 - 更新 `tool_submissions` 表中的 `ai_tool_id` 字段,建立对应关系 - 更新 `tool_submissions` 表中的 `processed_at` 字段
4. **用户浏览**: - 用户可以按分类浏览 `ai_tools` 表中的工具
## 权限控制
使用 Supabase 的 Row Level Security (RLS) 策略控制表访问权限:
```sql-- 分类表所有人可读,只有管理员可写ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
-- 查看策略CREATE POLICY "任何人可查看分类"ON categories FOR SELECTUSING (true);
-- AI工具表所有人可读,只有管理员可写ALTER TABLE ai_tools ENABLE ROW LEVEL SECURITY;
CREATE POLICY "任何人可查看AI工具"ON ai_tools FOR SELECTUSING (true)
-- 工具提交表用户可查看自己的提交,管理员可查看所有ALTER TABLE tool_submissions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "用户可查看自己的提交"ON tool_submissions FOR SELECTUSING (auth.uid() = user_id);
CREATE POLICY "用户可创建提交"ON tool_submissions FOR INSERTWITH CHECK (auth.uid() = user_id);```
## 备注
1. 此设计使用 Supabase 的认证系统(`auth.users`)2. 使用 UUID 作为主键以避免暴露记录数量和便于数据迁移3. 该设计专注于核心功能,移除了与基本需求无关的字段• 数据存储设计方案(展开查着)
• supabase-storage.mdc:
# Supabase 存储桶设计方案
## 概述
本文档详细说明了AI工具集平台的文件存储设计方案。我们将使用Supabase Storage来存储三类媒体文件:1. 分类图标2. 工具logo3. 工具预览图
## 存储桶结构
我们将创建3个独立的存储桶,分别用于存储不同类型的媒体文件:
1. `category-icons` - 存储侧边栏分类的图标2. `tool-logos` - 存储AI工具的logo3. `tool-previews` - 存储AI工具的预览图
## 存储桶配置详情
### 1. Category Icons (`category-icons`)
- **用途**: 存储分类的图标图片- **访问权限**: 公开可读,仅管理员可写- **文件类型限制**: 仅图片文件(jpg, jpeg, png, svg, webp)- **文件大小限制**: 最大1MB- **命名规则**: `{category_id}.{extension}`
### 2. Tool Logos (`tool-logos`)
- **用途**: 存储工具的logo图片- **访问权限**: 公开可读,认证用户可上传,仅所有者和管理员可修改- **文件类型限制**: 仅图片文件(jpg, jpeg, png, svg, webp)- **文件大小限制**: 最大2MB- **命名规则**: `{tool_id}.{extension}`
### 3. Tool Previews (`tool-previews`)
- **用途**: 存储工具的预览图片/截图- **访问权限**: 公开可读,认证用户可上传,仅所有者和管理员可修改- **文件类型限制**: 仅图片文件(jpg, jpeg, png, webp)- **文件大小限制**: 最大5MB- **命名规则**: `{tool_id}/{timestamp}_{index}.{extension}`(允许多张预览图)
## 创建存储桶SQL命令
使用Supabase SQL编辑器或API,执行以下SQL命令创建存储桶:
```sql-- 确保启用storage扩展CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 创建分类图标存储桶INSERT INTO storage.buckets (id, name, public)VALUES ('category-icons', 'Category Icons', true);
-- 创建工具logo存储桶INSERT INTO storage.buckets (id, name, public)VALUES ('tool-logos', 'Tool Logos', true);
-- 创建工具预览图存储桶INSERT INTO storage.buckets (id, name, public)VALUES ('tool-previews', 'Tool Previews', true);```
## 存储桶安全策略
配置存储桶的安全策略,确保适当的访问权限:
```sql-- 分类图标存储桶 - 公开可读,仅管理员可写CREATE POLICY "分类图标公开可读" ON storage.objectsFOR SELECT USING (bucket_id = 'category-icons');
CREATE POLICY "管理员可上传分类图标" ON storage.objectsFOR INSERT WITH CHECK ( bucket_id = 'category-icons' AND auth.uid() IN (SELECT user_id FROM admin_users));
CREATE POLICY "管理员可更新分类图标" ON storage.objectsFOR UPDATE USING ( bucket_id = 'category-icons' AND auth.uid() IN (SELECT user_id FROM admin_users));
CREATE POLICY "管理员可删除分类图标" ON storage.objectsFOR DELETE USING ( bucket_id = 'category-icons' AND auth.uid() IN (SELECT user_id FROM admin_users));
-- 工具logo存储桶 - 公开可读,认证用户可上传,仅所有者和管理员可修改CREATE POLICY "工具logo公开可读" ON storage.objectsFOR SELECT USING (bucket_id = 'tool-logos');
CREATE POLICY "认证用户可上传工具logo" ON storage.objectsFOR INSERT WITH CHECK ( bucket_id = 'tool-logos' AND auth.uid() IS NOT NULL);
CREATE POLICY "所有者和管理员可更新工具logo" ON storage.objectsFOR UPDATE USING ( bucket_id = 'tool-logos' AND (auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users)));
CREATE POLICY "所有者和管理员可删除工具logo" ON storage.objectsFOR DELETE USING ( bucket_id = 'tool-logos' AND (auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users)));
-- 工具预览图存储桶 - 公开可读,认证用户可上传,仅所有者和管理员可修改CREATE POLICY "工具预览图公开可读" ON storage.objectsFOR SELECT USING (bucket_id = 'tool-previews');
CREATE POLICY "认证用户可上传工具预览图" ON storage.objectsFOR INSERT WITH CHECK ( bucket_id = 'tool-previews' AND auth.uid() IS NOT NULL);
CREATE POLICY "所有者和管理员可更新工具预览图" ON storage.objectsFOR UPDATE USING ( bucket_id = 'tool-previews' AND (auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users)));
CREATE POLICY "所有者和管理员可删除工具预览图" ON storage.objectsFOR DELETE USING ( bucket_id = 'tool-previews' AND (auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users)));```
## 文件类型限制配置
配置存储桶的文件类型限制:
```sql-- 为分类图标存储桶设置文件类型限制UPDATE storage.bucketsSET file_size_limit = 1000000, -- 1MB allowed_mime_types = ARRAY['image/jpeg', 'image/png', 'image/svg+xml', 'image/webp']WHERE id = 'category-icons';
-- 为工具logo存储桶设置文件类型限制UPDATE storage.bucketsSET file_size_limit = 2000000, -- 2MB allowed_mime_types = ARRAY['image/jpeg', 'image/png', 'image/svg+xml', 'image/webp']WHERE id = 'tool-logos';
-- 为工具预览图存储桶设置文件类型限制UPDATE storage.bucketsSET file_size_limit = 5000000, -- 5MB allowed_mime_types = ARRAY['image/jpeg', 'image/png', 'image/webp']WHERE id = 'tool-previews';```
## 存储桶使用指南
### 上传文件
在前端应用中,使用Supabase客户端上传文件到相应的存储桶:
```javascript// 上传分类图标async function uploadCategoryIcon(categoryId, file) { const fileExt = file.name.split('.').pop() const fileName = `${categoryId}.${fileExt}`
const { data, error } = await supabase.storage .from('category-icons') .upload(fileName, file, { upsert: true })
return { data, error }}
// 上传工具logoasync function uploadToolLogo(toolId, file) { const fileExt = file.name.split('.').pop() const fileName = `${toolId}.${fileExt}`
const { data, error } = await supabase.storage .from('tool-logos') .upload(fileName, file, { upsert: true })
return { data, error }}
// 上传工具预览图async function uploadToolPreview(toolId, file, index = 0) { const fileExt = file.name.split('.').pop() const timestamp = Date.now() const fileName = `${toolId}/${timestamp}_${index}.${fileExt}`
const { data, error } = await supabase.storage .from('tool-previews') .upload(fileName, file)
return { data, error }}```
### 获取文件URL
获取存储在存储桶中的文件公共URL:
```javascript// 获取分类图标URLfunction getCategoryIconUrl(categoryId, fileExt) { return supabase.storage .from('category-icons') .getPublicUrl(`${categoryId}.${fileExt}`).data.publicUrl}
// 获取工具logoURLfunction getToolLogoUrl(toolId, fileExt) { return supabase.storage .from('tool-logos') .getPublicUrl(`${toolId}.${fileExt}`).data.publicUrl}
// 获取工具预览图URL列表async function getToolPreviewUrls(toolId) { const { data, error } = await supabase.storage .from('tool-previews') .list(toolId, { sortBy: { column: 'name', order: 'asc' }, })
if (error || !data) return []
return data.map(file => supabase.storage .from('tool-previews') .getPublicUrl(`${toolId}/${file.name}`).data.publicUrl)}```
## 数据库与存储的集成
更新数据库表结构,添加对文件引用的支持:
```sql-- 更新分类表,添加icon_path字段ALTER TABLE categoriesADD COLUMN icon_path VARCHAR(255);
-- 更新AI工具表,添加logo_path字段ALTER TABLE ai_toolsADD COLUMN logo_path VARCHAR(255);
-- 创建工具预览图表CREATE TABLE tool_previews ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tool_id UUID NOT NULL REFERENCES ai_tools(id) ON DELETE CASCADE, preview_path VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW());
CREATE INDEX idx_tool_previews_tool_id ON tool_previews(tool_id);```
## 维护与监控
为了确保存储桶的有效管理:
1. 定期清理未被引用的文件2. 监控存储使用量3. 设置基于使用情况的警报4. 考虑实施文件缓存策略以提高性能
## 备注
1. 在实际部署中,根据应用需求调整文件大小限制2. 考虑实施图片处理逻辑(如调整大小和压缩)以优化存储和加载性能3. 定期备份重要文件4. 对于高流量场景,考虑使用CDN进一步优化全球访问速度4. 完成分类的数据逻辑
4.1 创建表
•进入MemfireDB项目的后台,点击进入项目-【SQL执行器】-【新查询】,然后运行下面的SOL:
CREATE TABLE categories ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100) NOT NULL, icon VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW());
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
-- 查看策略CREATE POLICY "任何人可查看分类"ON categories FOR SELECTUSING (true);4.2 创建存储桶
• 点击【对象存储】-【新增存储桶】,创一个catetory-icons 的存储桶,用于存放我们分类的icon图标
• 进入到https://lucide.dev/icons,选择合适的icon,并保存到我们刚才创建的存储桶中
• 到 categories 这个数据表中,增加我们的数据
4.3 让Al接入数据
• 输入以下提示词,让Al接入数据:
请你帮我将侧边栏接入真实的数据,@supabase-slqs.mdc 中的 categories 这张表的数据• 修改bug,输入以下提示词:
现在侧边栏渲染有问题,icon保存的是图片的链接地址,请你使用图片渲染这个链接• 等待AI修改完成之后,我们的侧边栏就接入真实的数据了
5. 完成AI工具的数据逻辑
5.1 创建表
• 进入MemfireDB项目的后台,点击进入项目-【SQL执行器】一【新查询】,然后运行下面的SQL
• 提示词:
CREATE TABLE ai_tools ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(200) NOT NULL, slug VARCHAR(200) NOT NULL UNIQUE, description TEXT NOT NULL, full_description TEXT, website_url VARCHAR(255), image_url VARCHAR(255), category_id UUID NOT NULL REFERENCES categories(id), created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), user_id UUID REFERENCES auth.users(id), is_approved BOOLEAN NOT NULL DEFAULT true);
CREATE INDEX idx_ai_tools_category_id ON ai_tools(category_id);CREATE INDEX idx_ai_tools_user_id ON ai_tools(user_id);
-- AI工具表所有人可读,只有管理员可写ALTER TABLE ai_tools ENABLE ROW LEVEL SECURITY;
CREATE POLICY "任何人可查看AI工具"ON ai_tools FOR SELECTUSING (true)• 修复对应的表中缺失的字段,分别添加 logo_url和type 宇段
5.2 创建存储桶
• 点击【对象存储】-【新增存储桶】,分别创建 tool-1ogos 和 tool-previews 这两个存储桶。
5.3 让AI接入数据
create 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;
1. 请你帮我在 @page.tsx 引入真实的ai工具的数据,使用的是ai_tools表(表结构参考上面给你的sql)2. 进入到ai工具页面后,细节描述full_description这个字段的渲染请使用markdown渲染3. 热门工具和最新使用,需要判断type字段,如果type=hot,则是热门工具。如果type是new,则是最新使用。如果没有值,则是普通的工具5.4 测试
• 在ai-tools 中增加对应的数据,进行测试
• 测试成功
6. 用户提交的数据逻辑
6.1 修改提交表单的前端bug
• 我们现在发现了这个提交的表单,前端显示有些问题
• 然后我们让al进行一下修改:
@page.tsx 目前这个表单存在几处问题,请你修改:1. 缺少slug字段2. 工具类型不要显示,这个是管理员设置的3. 缺少logo上传功能4. 工具图片URL和logo上传的两个图片地址,都是应该通过我们上传到我们的bucket里面实现。请修改成为上传组件,工具图片URL地址对应的 ai_tools下的image_url字段,那么logo对应的是 ai_tools表下的logo_url这个字段• 修改完成后显示效果如下:
6.2 创建存储策略
我们需要给 tool-logos和 tool-previews这两个存储桶添加对应的存储策略,勾选所有的允许的操作,并在目标角色处点击【authenticated】。然后点击【回看】-【确认】
6.3 创建数据表
• 进入MemfireDB项目的后台,点击进入项目-【SQL执行器】-【新查询】,然后运行下面的SQL
CREATE TABLE tool_submissions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(200) NOT NULL, description TEXT NOT NULL, full_description TEXT, website_url VARCHAR(255) NOT NULL, image_url VARCHAR(255), category_id UUID NOT NULL REFERENCES categories(id), user_id UUID NOT NULL REFERENCES auth.users(id), status VARCHAR(20) NOT NULL DEFAULT 'pending', admin_comments TEXT, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), processed_at TIMESTAMP, ai_tool_id UUID REFERENCES ai_tools(id));
CREATE INDEX idx_tool_submissions_user_id ON tool_submissions(user_id);CREATE INDEX idx_tool_submissions_status ON tool_submissions(status);CREATE INDEX idx_tool_submissions_category_id ON tool_submissions(category_id);CREATE INDEX idx_tool_submissions_created_at ON tool_submissions(created_at);
ALTER TABLE tool_submissions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "用户可查看自己的提交"ON tool_submissions FOR SELECTUSING (auth.uid() = user_id);
CREATE POLICY "用户可创建提交"ON tool_submissions FOR INSERTWITH CHECK (auth.uid() = user_id);6.4 让AI接入数据
后来让我的提交页面进入真实的数据
提示词:
/user/submissions 这个页面请获取真实的数据,从tool_submissions这个表里面获取7. 项目源码
7.1 初始化前端
ai-tools-directory.zip 5.2MB
7.2 最终项目源码