# Supabase API 集成文档 - Vue 前端对接指南
## 📋 目录
- [基本信息](#基本信息)
- [快速开始](#快速开始)
- [API 端点](#api-端点)
- [认证配置](#认证配置)
- [REST API 使用](#rest-api-使用)
- [Auth API 使用](#auth-api-使用)
- [Storage API 使用](#storage-api-使用)
- [Realtime 使用](#realtime-使用)
- [完整示例](#完整示例)
- [错误处理](#错误处理)
---
## 基本信息
### 🌐 服务地址
```
Base URL: https://amiap.hzau.edu.cn/supa
内网地址: http://100.64.0.2:18000
Dashboard: http://100.64.0.2:18000 (内网访问)
```
### 🔑 认证密钥
```javascript
// 从 .env 文件获取
const SUPABASE_URL = 'https://amiap.hzau.edu.cn/supa'
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzYzODAyNjY5LCJleHAiOjIwNzkxNjI2Njl9.ltGXvQKpguLaf8Vzomn310hLgOZbrjqZT-F3rR00ulg'
// ⚠️ 仅后端使用!不要暴露给前端
const SUPABASE_SERVICE_ROLE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3NjM4MDI2NjksImV4cCI6MjA3OTE2MjY2OX0.gQWUaTkZ6mjjlv2TED0cODp2meqqWuCGKZR1ptIbovg'
```
---
## 快速开始
### 1. 安装 Supabase 客户端
```bash
npm install @supabase/supabase-js
```
### 2. 创建 Supabase 客户端
```javascript
// src/lib/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = 'https://amiap.hzau.edu.cn/supa'
const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzYzODAyNjY5LCJleHAiOjIwNzkxNjI2Njl9.ltGXvQKpguLaf8Vzomn310hLgOZbrjqZT-F3rR00ulg'
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
```
### 3. 在 Vue 组件中使用
```vue
```
---
## API 端点
### ✅ 可用的 API 端点
| 服务 | 端点 | 用途 | 状态 |
|------|------|------|------|
| REST API | `https://amiap.hzau.edu.cn/supa/rest/v1/` | 数据库操作 | ✅ 可用 |
| Auth API | `https://amiap.hzau.edu.cn/supa/auth/v1/` | 用户认证 | ✅ 可用 |
| Storage API | `https://amiap.hzau.edu.cn/supa/storage/v1/` | 文件存储 | ✅ 可用 |
| Realtime | `wss://amiap.hzau.edu.cn/supa/realtime/v1/` | 实时订阅 | ✅ 可用 |
| Edge Functions | `https://amiap.hzau.edu.cn/supa/functions/v1/` | 云函数 | ⚠️ 需配置 |
---
## 认证配置
### 环境变量配置
创建 `.env` 文件:
```env
VITE_SUPABASE_URL=https://amiap.hzau.edu.cn/supa
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzYzODAyNjY5LCJleHAiOjIwNzkxNjI2Njl9.ltGXvQKpguLaf8Vzomn310hLgOZbrjqZT-F3rR00ulg
```
### 使用环境变量
```javascript
// src/lib/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
```
---
## REST API 使用
### 查询数据
```javascript
// 查询所有记录
const { data, error } = await supabase
.from('users')
.select('*')
// 条件查询
const { data, error } = await supabase
.from('users')
.select('id, name, email')
.eq('status', 'active')
.order('created_at', { ascending: false })
.limit(10)
// 关联查询
const { data, error } = await supabase
.from('posts')
.select(`
*,
author:users(name, email),
comments(*)
`)
```
### 插入数据
```javascript
const { data, error } = await supabase
.from('users')
.insert([
{ name: 'John Doe', email: 'john@example.com' }
])
.select()
```
### 更新数据
```javascript
const { data, error } = await supabase
.from('users')
.update({ status: 'inactive' })
.eq('id', userId)
.select()
```
### 删除数据
```javascript
const { data, error } = await supabase
.from('users')
.delete()
.eq('id', userId)
```
---
## Auth API 使用
### 用户注册
```javascript
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'password123',
options: {
data: {
first_name: 'John',
last_name: 'Doe'
}
}
})
```
### 用户登录
```javascript
// 邮箱密码登录
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password123'
})
// OAuth 登录(需要配置)
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google'
})
```
### 获取当前用户
```javascript
const { data: { user } } = await supabase.auth.getUser()
```
### 退出登录
```javascript
const { error } = await supabase.auth.signOut()
```
### 监听认证状态
```javascript
supabase.auth.onAuthStateChange((event, session) => {
console.log(event, session)
if (event === 'SIGNED_IN') {
console.log('用户已登录:', session.user)
}
if (event === 'SIGNED_OUT') {
console.log('用户已退出')
}
})
```
### Vue 3 Composition API 示例
```vue
```
---
## Storage API 使用
### 上传文件
```javascript
const file = event.target.files[0]
const fileName = `${Date.now()}-${file.name}`
const { data, error } = await supabase.storage
.from('avatars')
.upload(fileName, file)
if (error) {
console.error('上传失败:', error)
} else {
console.log('上传成功:', data)
}
```
### 下载文件
```javascript
const { data, error } = await supabase.storage
.from('avatars')
.download('path/to/file.jpg')
```
### 获取公开 URL
```javascript
const { data } = supabase.storage
.from('avatars')
.getPublicUrl('path/to/file.jpg')
console.log('公开URL:', data.publicUrl)
```
### 删除文件
```javascript
const { data, error } = await supabase.storage
.from('avatars')
.remove(['path/to/file.jpg'])
```
### Vue 文件上传组件示例
```vue
```
---
## Realtime 使用
### 订阅数据变化
```javascript
// 订阅表的所有变化
const channel = supabase
.channel('public:users')
.on('postgres_changes',
{ event: '*', schema: 'public', table: 'users' },
(payload) => {
console.log('数据变化:', payload)
}
)
.subscribe()
// 订阅特定操作
const channel = supabase
.channel('public:posts')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'posts' },
(payload) => {
console.log('新增记录:', payload.new)
}
)
.on('postgres_changes',
{ event: 'UPDATE', schema: 'public', table: 'posts' },
(payload) => {
console.log('更新记录:', payload.new)
}
)
.on('postgres_changes',
{ event: 'DELETE', schema: 'public', table: 'posts' },
(payload) => {
console.log('删除记录:', payload.old)
}
)
.subscribe()
```
### 取消订阅
```javascript
const removeChannel = async () => {
await supabase.removeChannel(channel)
}
```
### Vue 实时数据组件示例
```vue
```
---
## 完整示例
### 完整的 CRUD 示例
```vue
```
---
## 错误处理
### 统一错误处理
```javascript
// src/lib/errorHandler.js
export const handleSupabaseError = (error) => {
if (!error) return null
// 认证错误
if (error.status === 401) {
console.error('认证失败,请重新登录')
// 跳转到登录页
router.push('/login')
}
// 权限错误
if (error.status === 403) {
console.error('权限不足')
}
// 网络错误
if (error.message && error.message.includes('fetch')) {
console.error('网络连接失败')
}
return error.message || '操作失败'
}
```
### 使用错误处理
```javascript
import { handleSupabaseError } from '@/lib/errorHandler'
const fetchData = async () => {
const { data, error } = await supabase
.from('users')
.select('*')
if (error) {
const errorMsg = handleSupabaseError(error)
alert(errorMsg)
return
}
// 处理成功的数据
console.log(data)
}
```
---
## 🔧 数据库 Schema 示例
### 创建表(在 Dashboard 中执行)
```sql
-- 创建用户表
CREATE TABLE users (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
avatar_url TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 创建帖子表
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
content TEXT,
published BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- 启用 RLS (Row Level Security)
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 创建策略
CREATE POLICY "用户可以查看所有用户" ON users
FOR SELECT USING (true);
CREATE POLICY "用户只能更新自己的数据" ON users
FOR UPDATE USING (auth.uid() = id);
CREATE POLICY "所有人可以查看已发布的帖子" ON posts
FOR SELECT USING (published = true);
CREATE POLICY "用户可以创建自己的帖子" ON posts
FOR INSERT WITH CHECK (auth.uid() = user_id);
CREATE POLICY "用户可以更新自己的帖子" ON posts
FOR UPDATE USING (auth.uid() = user_id);
```
---
## 📚 更多资源
- [Supabase 官方文档](https://supabase.com/docs)
- [Supabase JS 客户端文档](https://supabase.com/docs/reference/javascript)
- [Vue 3 官方文档](https://vuejs.org/)
---
## ⚠️ 注意事项
1. **ANON_KEY 是公开的**:可以在前端代码中使用
2. **SERVICE_ROLE_KEY 必须保密**:只能在后端使用
3. **使用 RLS (Row Level Security)**:保护数据安全
4. **Dashboard 访问**:只能通过内网 `http://100.64.0.2:18000` 访问
5. **API 端点**:通过 `https://amiap.hzau.edu.cn/supa` 访问所有服务
---
## 🎯 下一步
1. 在 Dashboard 中创建数据库表
2. 配置 RLS 策略
3. 在 Vue 应用中集成 Supabase
4. 开发认证、CRUD、文件上传等功能
5. 测试实时订阅功能
---
## 📦 S3 对象存储配置与使用
### 概述
Supabase Storage 已配置使用 **MinIO** 作为 S3 兼容的对象存储后端。所有文件都会保存在宿主机的 `/vol1/1000/s3` 目录。
### 🔧 启动 S3 存储服务
```bash
cd /vol1/1000/docker_server/traefik/supabase-stack
docker compose -f docker-compose.yml -f docker-compose.s3.yml up -d
```
**说明**:
- `docker-compose.yml`: 主配置文件(包含所有 Supabase 服务)
- `docker-compose.s3.yml`: MinIO 配置文件(在 `/vol1/1000/docker_server/traefik/supabase-stack/` 目录)
- 所有对象文件实际存储在:`/vol1/1000/s3/stub//...`
---
### 🌐 S3 访问端点
#### 统一 S3 端点(推荐)
```
https://amiap.hzau.edu.cn/supa/storage/v1/s3
```
这是标准的 Supabase Storage S3 协议端点,完全兼容 AWS S3 API。
#### MinIO 管理控制台(可选)
```
http://100.64.0.2:9001
用户名: supa-storage
密码: secret519521
```
---
### 🔑 S3 认证密钥
#### 1. S3 协议访问密钥(推荐用于应用)
用于通过 Supabase Storage 的 S3 端点访问:
```bash
# 从 .env 文件
S3_PROTOCOL_ACCESS_KEY_ID=supa-protocol-key
S3_PROTOCOL_ACCESS_KEY_SECRET=supa-protocol-secret
S3_PROTOCOL_REGION=stub
```
#### 2. MinIO 根密钥(内部使用)
用于 storage 服务连接 MinIO,一般不直接使用:
```bash
MINIO_ROOT_USER=supa-storage
MINIO_ROOT_PASSWORD=secret1234
```
---
### 💻 客户端配置示例
#### Python (boto3)
```python
import boto3
from botocore.client import Config
# 配置 S3 客户端连接到 Supabase Storage
s3_client = boto3.client(
's3',
endpoint_url='https://amiap.hzau.edu.cn/supa/storage/v1/s3',
aws_access_key_id='supa-protocol-key',
aws_secret_access_key='supa-protocol-secret',
region_name='stub',
config=Config(
signature_version='s3v4',
s3={'addressing_style': 'path'}
),
verify=True # HTTPS 验证
)
# 列出所有 buckets
response = s3_client.list_buckets()
for bucket in response['Buckets']:
print(f"Bucket: {bucket['Name']}")
# 上传文件
with open('example.jpg', 'rb') as file:
s3_client.upload_fileobj(
file,
'my-bucket', # bucket 名称
'uploads/example.jpg' # 对象路径
)
# 下载文件
s3_client.download_file(
'my-bucket',
'uploads/example.jpg',
'downloaded.jpg'
)
# 生成预签名 URL(临时访问链接)
url = s3_client.generate_presigned_url(
'get_object',
Params={'Bucket': 'my-bucket', 'Key': 'uploads/example.jpg'},
ExpiresIn=3600 # 1小时过期
)
print(f"Presigned URL: {url}")
```
#### JavaScript/Node.js (AWS SDK v3)
```javascript
import { S3Client, ListBucketsCommand, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import fs from 'fs'
// 配置 S3 客户端
const s3Client = new S3Client({
endpoint: 'https://amiap.hzau.edu.cn/supa/storage/v1/s3',
region: 'stub',
credentials: {
accessKeyId: 'supa-protocol-key',
secretAccessKey: 'supa-protocol-secret',
},
forcePathStyle: true, // 使用路径风格访问
})
// 列出 buckets
const listBuckets = async () => {
const command = new ListBucketsCommand({})
const response = await s3Client.send(command)
console.log('Buckets:', response.Buckets)
}
// 上传文件
const uploadFile = async () => {
const fileContent = fs.readFileSync('example.jpg')
const command = new PutObjectCommand({
Bucket: 'my-bucket',
Key: 'uploads/example.jpg',
Body: fileContent,
ContentType: 'image/jpeg',
})
const response = await s3Client.send(command)
console.log('Upload success:', response)
}
// 生成预签名 URL
const getPresignedUrl = async () => {
const command = new GetObjectCommand({
Bucket: 'my-bucket',
Key: 'uploads/example.jpg',
})
const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 })
console.log('Presigned URL:', url)
return url
}
// 执行
await listBuckets()
await uploadFile()
await getPresignedUrl()
```
#### PostgreSQL Foreign Data Wrapper (FDW)
在 PostgreSQL 中直接查询 S3 对象:
```sql
-- 1. 启用扩展
CREATE EXTENSION IF NOT EXISTS wrappers CASCADE;
-- 2. 创建 S3 服务器连接
CREATE SERVER s3_server
FOREIGN DATA WRAPPER s3_wrapper
OPTIONS (
aws_access_key_id 'supa-protocol-key',
aws_secret_access_key 'supa-protocol-secret',
aws_region 'stub',
endpoint_url 'https://amiap.hzau.edu.cn/supa/storage/v1/s3',
path_style_url 'true'
);
-- 3. 创建外部表映射到 S3 对象(例如 Parquet 文件)
CREATE FOREIGN TABLE my_s3_table (
id bigint,
name text,
created_at timestamp
)
SERVER s3_server
OPTIONS (
uri 's3://my-bucket/data/file.parquet',
format 'parquet'
);
-- 4. 查询 S3 数据
SELECT * FROM my_s3_table WHERE created_at > NOW() - INTERVAL '7 days';
```
#### AWS CLI
```bash
# 配置 AWS CLI
export AWS_ACCESS_KEY_ID=supa-protocol-key
export AWS_SECRET_ACCESS_KEY=supa-protocol-secret
export AWS_DEFAULT_REGION=stub
# 或创建 AWS 配置文件 ~/.aws/config
# [profile supabase]
# region = stub
# output = json
# ~/.aws/credentials
# [supabase]
# aws_access_key_id = supa-protocol-key
# aws_secret_access_key = supa-protocol-secret
# 列出 buckets
aws s3 ls --endpoint-url=https://amiap.hzau.edu.cn/supa/storage/v1/s3
# 列出 bucket 中的对象
aws s3 ls s3://my-bucket/ --endpoint-url=https://amiap.hzau.edu.cn/supa/storage/v1/s3
# 上传文件
aws s3 cp example.jpg s3://my-bucket/uploads/ \
--endpoint-url=https://amiap.hzau.edu.cn/supa/storage/v1/s3
# 下载文件
aws s3 cp s3://my-bucket/uploads/example.jpg downloaded.jpg \
--endpoint-url=https://amiap.hzau.edu.cn/supa/storage/v1/s3
# 同步目录
aws s3 sync ./local-dir s3://my-bucket/backup/ \
--endpoint-url=https://amiap.hzau.edu.cn/supa/storage/v1/s3
```
#### Vue 3 + Supabase Client(推荐)
```vue
```
---
### 🗂️ 数据存储位置
**所有上传的文件实际保存在**:
```
/vol1/1000/s3/stub//
```
示例:
```
/vol1/1000/s3/
└── stub/
├── my-bucket/
│ ├── uploads/
│ │ ├── image1.jpg
│ │ └── image2.png
│ └── documents/
│ └── report.pdf
└── avatars/
└── user123.jpg
```
---
### 🔒 安全建议
1. **生产环境密钥**:修改 `.env` 中的默认密钥
2. **HTTPS 访问**:外部访问使用 HTTPS 端点
3. **Bucket 权限**:合理配置 RLS 策略
4. **MinIO 控制台**:仅内网访问(端口 9001)
---
### ❓ 常见问题
**Q: 文件存在哪里?**
A: 所有文件物理存储在 `/vol1/1000/s3/stub//`
**Q: 如何备份数据?**
A: 直接备份 `/vol1/1000/s3` 目录即可
**Q: 可以直接访问 MinIO 吗?**
A: 可以,但推荐使用 Supabase Storage 的 S3 端点(更安全)
**Q: 如何迁移现有文件到 S3?**
A: 使用 AWS CLI 或 Python 脚本批量上传到对应 bucket
---
### 📝 在 Dashboard 中创建 Bucket
1. 访问 Dashboard: `http://100.64.0.2:18000`
2. 登录后进入 **Storage** 页面
3. 点击 **New Bucket**
4. 输入 bucket 名称(例如:`my-bucket`)
5. 配置访问权限(Public/Private)
6. 创建完成后,文件会保存在 `/vol1/1000/s3/stub/my-bucket/`
---
### ⚙️ 配置文件说明
#### .env 配置
```bash
# MinIO 内部认证
MINIO_ROOT_USER=supa-storage
MINIO_ROOT_PASSWORD=secret1234
# S3 协议访问密钥(用于外部客户端)
S3_PROTOCOL_ACCESS_KEY_ID=supa-protocol-key
S3_PROTOCOL_ACCESS_KEY_SECRET=supa-protocol-secret
S3_PROTOCOL_REGION=stub
# S3 配置
GLOBAL_S3_BUCKET=stub
GLOBAL_S3_ENDPOINT=http://minio:9000
GLOBAL_S3_PROTOCOL=http
GLOBAL_S3_FORCE_PATH_STYLE=true
AWS_DEFAULT_REGION=stub
```
#### docker-compose.s3.yml
```yaml
services:
minio:
image: minio/minio
ports:
- "9000:9000" # S3 API
- "9001:9001" # Web 控制台
environment:
MINIO_ROOT_USER: supa-storage
MINIO_ROOT_PASSWORD: secret1234
command: server --console-address ":9001" /data
volumes:
- /vol1/1000/s3:/data:z # 数据存储位置
minio-createbucket:
image: minio/mc
depends_on:
minio:
condition: service_healthy
entrypoint: >
/bin/sh -c "
/usr/bin/mc alias set supa-minio http://minio:9000 supa-storage secret1234;
/usr/bin/mc mb supa-minio/stub;
exit 0;
"
```
---
### 🔧 验证 S3 配置
#### 1. 检查 MinIO 服务状态
```bash
docker compose ps minio
```
#### 2. 使用 MinIO Client 验证
```bash
# 进入 MinIO 容器
docker exec -it $(docker ps -qf "name=minio") sh
# 配置 mc 别名
mc alias set local http://localhost:9000 supa-storage secret1234
# 列出 buckets
mc ls local/
# 查看 stub bucket
mc ls local/stub/
# 退出
exit
```
#### 3. 测试 S3 API 端点
```bash
# 测试 endpoint 是否可访问
curl -I https://amiap.hzau.edu.cn/supa/storage/v1/s3
```
---
### 📚 相关资源
- [Supabase Storage 文档](https://supabase.com/docs/guides/storage)
- [MinIO 文档](https://min.io/docs/)
- [AWS S3 API 文档](https://docs.aws.amazon.com/s3/)
- [boto3 文档](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)