skip to content
仙人掌主题

使用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工具的表中)
请你给到我设计方案,并给出对应的sql

3.2 创建数据存储设计方案

• 提示词:

接下来,请你帮我创建对应的数据存储的bucket,我会存放三个数据
1.侧边栏分类的icon的图片
2.工具的logo
3.工具的预览图
请你给到我设计文档

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 表
```sql
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()
);
```
### 创建 AI Tools 表
```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);
```
### 创建 Tool Submissions 表
```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);
```
## 数据流程
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 SELECT
USING (true);
-- AI工具表所有人可读,只有管理员可写
ALTER TABLE ai_tools ENABLE ROW LEVEL SECURITY;
CREATE POLICY "任何人可查看AI工具"
ON ai_tools FOR SELECT
USING (true)
-- 工具提交表用户可查看自己的提交,管理员可查看所有
ALTER TABLE tool_submissions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "用户可查看自己的提交"
ON tool_submissions FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "用户可创建提交"
ON tool_submissions FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
## 备注
1. 此设计使用 Supabase 的认证系统(`auth.users`)
2. 使用 UUID 作为主键以避免暴露记录数量和便于数据迁移
3. 该设计专注于核心功能,移除了与基本需求无关的字段

• 数据存储设计方案(展开查着)

• supabase-storage.mdc:

# Supabase 存储桶设计方案
## 概述
本文档详细说明了AI工具集平台的文件存储设计方案。我们将使用Supabase Storage来存储三类媒体文件:
1. 分类图标
2. 工具logo
3. 工具预览图
## 存储桶结构
我们将创建3个独立的存储桶,分别用于存储不同类型的媒体文件:
1. `category-icons` - 存储侧边栏分类的图标
2. `tool-logos` - 存储AI工具的logo
3. `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.objects
FOR SELECT USING (bucket_id = 'category-icons');
CREATE POLICY "管理员可上传分类图标" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'category-icons' AND
auth.uid() IN (SELECT user_id FROM admin_users)
);
CREATE POLICY "管理员可更新分类图标" ON storage.objects
FOR UPDATE USING (
bucket_id = 'category-icons' AND
auth.uid() IN (SELECT user_id FROM admin_users)
);
CREATE POLICY "管理员可删除分类图标" ON storage.objects
FOR DELETE USING (
bucket_id = 'category-icons' AND
auth.uid() IN (SELECT user_id FROM admin_users)
);
-- 工具logo存储桶 - 公开可读,认证用户可上传,仅所有者和管理员可修改
CREATE POLICY "工具logo公开可读" ON storage.objects
FOR SELECT USING (bucket_id = 'tool-logos');
CREATE POLICY "认证用户可上传工具logo" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'tool-logos' AND
auth.uid() IS NOT NULL
);
CREATE POLICY "所有者和管理员可更新工具logo" ON storage.objects
FOR 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.objects
FOR DELETE USING (
bucket_id = 'tool-logos' AND
(auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users))
);
-- 工具预览图存储桶 - 公开可读,认证用户可上传,仅所有者和管理员可修改
CREATE POLICY "工具预览图公开可读" ON storage.objects
FOR SELECT USING (bucket_id = 'tool-previews');
CREATE POLICY "认证用户可上传工具预览图" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'tool-previews' AND
auth.uid() IS NOT NULL
);
CREATE POLICY "所有者和管理员可更新工具预览图" ON storage.objects
FOR UPDATE USING (
bucket_id = 'tool-previews' AND
(auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users))
);
CREATE POLICY "所有者和管理员可删除工具预览图" ON storage.objects
FOR DELETE USING (
bucket_id = 'tool-previews' AND
(auth.uid() = owner OR auth.uid() IN (SELECT user_id FROM admin_users))
);
```
## 文件类型限制配置
配置存储桶的文件类型限制:
```sql
-- 为分类图标存储桶设置文件类型限制
UPDATE storage.buckets
SET 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.buckets
SET file_size_limit = 2000000, -- 2MB
allowed_mime_types = ARRAY['image/jpeg', 'image/png', 'image/svg+xml', 'image/webp']
WHERE id = 'tool-logos';
-- 为工具预览图存储桶设置文件类型限制
UPDATE storage.buckets
SET 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 }
}
// 上传工具logo
async 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
// 获取分类图标URL
function getCategoryIconUrl(categoryId, fileExt) {
return supabase.storage
.from('category-icons')
.getPublicUrl(`${categoryId}.${fileExt}`).data.publicUrl
}
// 获取工具logoURL
function 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 categories
ADD COLUMN icon_path VARCHAR(255);
-- 更新AI工具表,添加logo_path字段
ALTER TABLE ai_tools
ADD 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 SELECT
USING (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 SELECT
USING (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 SELECT
USING (auth.uid() = user_id);
CREATE POLICY "用户可创建提交"
ON tool_submissions FOR INSERT
WITH CHECK (auth.uid() = user_id);

6.4 让AI接入数据

后来让我的提交页面进入真实的数据

提示词:

/user/submissions 这个页面请获取真实的数据,从tool_submissions这个表里面获取

7. 项目源码

7.1 初始化前端

ai-tools-directory.zip 5.2MB

7.2 最终项目源码

ai-tools-directory-main.zip 5.1MB