# 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)