feat(supabase): 整理 Storage 文档和示例代码

- 创建 docs/ 目录存放所有文档
  - QUICK_START.md: 快速入门指南
  - OPERATIONS_GUIDE.md: 完整运维指南
  - VUE_API_INTEGRATION.md: Vue 集成文档
  - DEPLOYMENT_INFO.md: 部署配置信息
  - versions.md: 版本信息

- 创建 examples/ 目录存放示例代码
  - storage_client.py: Python 完整客户端
  - storage_client.js: JavaScript 完整客户端
  - test_https_storage.py: 功能测试脚本

- 新增 README_STORAGE.md 作为 Storage 使用指南

- 修复签名 URL 生成问题(需要 /storage/v1 前缀)
- 测试脚本支持资源已存在的情况
- 所有客户端代码已验证可用

功能特性:
✓ 公网 HTTPS 访问
✓ 文件上传/下载
✓ 生成临时下载链接
✓ 完整的 REST API 客户端
✓ 支持 Python 和 JavaScript
This commit is contained in:
zly
2025-11-22 21:03:00 +08:00
parent 8b068d8171
commit 9fa602f21b
9 changed files with 3261 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
# Supabase 部署信息汇总
## 🌐 访问地址
### HTTPS 域名访问(推荐用于 API
```
API Base URL: https://amiap.hzau.edu.cn/supa
```
### 内网访问(用于 Dashboard
```
Dashboard: http://100.64.0.2:18000
API Base: http://100.64.0.2:18000
```
---
## 🔑 认证信息
### Dashboard 登录
```
URL: http://100.64.0.2:18000
用户名: lab-admin
密码: 017b7076cfb25bd18410d1e5f4f7ec5a
```
### API Keys用于前端/后端开发)
```javascript
// ✅ 公开密钥 - 可以在前端使用
ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzYzODAyNjY5LCJleHAiOjIwNzkxNjI2Njl9.ltGXvQKpguLaf8Vzomn310hLgOZbrjqZT-F3rR00ulg"
// ⚠️ 私密密钥 - 仅后端使用,不要暴露
SERVICE_ROLE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3NjM4MDI2NjksImV4cCI6MjA3OTE2MjY2OX0.gQWUaTkZ6mjjlv2TED0cODp2meqqWuCGKZR1ptIbovg"
```
### 数据库连接
```
主机: 100.64.0.2 或 db (容器内)
端口: 5432
数据库: postgres
用户: postgres
密码: a837234b952ad7aa9ab4852f47021660c038209a71fce027cec8bab37ad82ae5
```
---
## 📡 API 端点
### ✅ 可用的服务端点
| 服务 | HTTPS 端点 | 内网端点 | 用途 |
|------|-----------|----------|------|
| REST API | `https://amiap.hzau.edu.cn/supa/rest/v1/` | `http://100.64.0.2:18000/rest/v1/` | 数据库 CRUD 操作 |
| Auth API | `https://amiap.hzau.edu.cn/supa/auth/v1/` | `http://100.64.0.2:18000/auth/v1/` | 用户认证 |
| Storage API | `https://amiap.hzau.edu.cn/supa/storage/v1/` | `http://100.64.0.2:18000/storage/v1/` | 文件存储 |
| Realtime | `wss://amiap.hzau.edu.cn/supa/realtime/v1/websocket` | `ws://100.64.0.2:18000/realtime/v1/websocket` | 实时订阅 |
| Dashboard | ❌ 不可用 | `http://100.64.0.2:18000/` | 管理界面 |
---
## 🐳 Docker 服务状态
### 核心服务
```bash
# 查看所有服务状态
docker compose ps
# 核心服务列表
supabase-db # PostgreSQL 数据库
supabase-kong # API 网关(端口: 18000
supabase-auth # 用户认证服务
supabase-rest # REST API 服务
supabase-storage # 文件存储服务
supabase-realtime # 实时订阅服务
supabase-studio # Dashboard
supabase-meta # 数据库元数据
supabase-analytics # 日志分析
```
### 常用命令
```bash
# 进入 supabase-stack 目录
cd /vol1/1000/docker_server/traefik/supabase-stack
# 启动所有服务
docker compose up -d
# 停止所有服务
docker compose down
# 查看服务日志
docker compose logs -f kong
docker compose logs -f rest
docker compose logs -f auth
# 重启特定服务
docker compose restart kong
docker compose restart rest
```
---
## 🔧 配置文件
### 重要文件位置
```
/vol1/1000/docker_server/traefik/supabase-stack/
├── .env # 环境变量配置
├── docker-compose.yml # Docker Compose 配置
├── VUE_API_INTEGRATION.md # Vue 集成文档(已创建)
├── DEPLOYMENT_INFO.md # 本文件
├── volumes/
│ ├── api/kong.yml # Kong 网关配置
│ ├── db/ # 数据库数据目录
│ └── storage/ # 文件存储目录
```
### .env 关键配置
```bash
# 数据库
POSTGRES_PASSWORD=a837234b952ad7aa9ab4852f47021660c038209a71fce027cec8bab37ad82ae5
POSTGRES_HOST=db
POSTGRES_PORT=5432
# JWT
JWT_SECRET=7a264228051ad10724934342ce62dce584161c248841061de0d0a56e92d9bb1a
# API Keys
ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Dashboard
DASHBOARD_USERNAME=lab-admin
DASHBOARD_PASSWORD=017b7076cfb25bd18410d1e5f4f7ec5a
# 域名配置
SITE_URL=https://amiap.hzau.edu.cn
API_EXTERNAL_URL=https://amiap.hzau.edu.cn/supa
SUPABASE_PUBLIC_URL=https://amiap.hzau.edu.cn/supa
```
---
## 🚀 快速开始(前端集成)
### 1. 安装依赖
```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. 使用示例
```javascript
// 查询数据
const { data, error } = await supabase
.from('users')
.select('*')
// 插入数据
const { data, error } = await supabase
.from('users')
.insert([{ name: 'John', email: 'john@example.com' }])
// 用户登录
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password'
})
```
---
## 🔍 测试 API
### 快速测试脚本
```bash
cd /vol1/1000/docker_server/traefik/supabase-stack
# 测试 REST API
curl -H "apikey: ANON_KEY" \
-H "Authorization: Bearer ANON_KEY" \
https://amiap.hzau.edu.cn/supa/rest/v1/
# 测试 Auth API
curl https://amiap.hzau.edu.cn/supa/auth/v1/health
# 测试 Storage API
curl https://amiap.hzau.edu.cn/supa/storage/v1/version
```
### 在浏览器中测试
```
1. 访问 Dashboard: http://100.64.0.2:18000
2. 使用用户名/密码登录
3. 创建测试表
4. 在 Vue 应用中调用 API
```
---
## ⚠️ 已知问题
### 1. Dashboard 子路径问题
- **问题**: `https://amiap.hzau.edu.cn/supa` 无法访问 Dashboard
- **原因**: Supabase Studio 是 SPA不支持子路径部署
- **解决**: 使用内网地址 `http://100.64.0.2:18000`
### 2. Pooler 服务重启
- **问题**: `supabase-pooler` 一直重启
- **影响**: 无影响,这是连接池优化组件,非必需
- **解决**: 可忽略或停止该服务 `docker stop supabase-pooler`
### 3. REST API 缓存重试
- **问题**: 首次访问可能看到 "schema cache retrying"
- **原因**: PostgreSQL schema 缓存加载
- **解决**: 等待几秒自动恢复,或重启 `docker compose restart rest`
---
## 📚 开发文档
| 文档 | 位置 | 说明 |
|------|------|------|
| Vue 集成指南 | `VUE_API_INTEGRATION.md` | 详细的前端集成教程 |
| 部署信息 | `DEPLOYMENT_INFO.md` | 本文件 |
| 官方文档 | https://supabase.com/docs | Supabase 官方文档 |
---
## 🛠️ 故障排查
### 服务无法启动
```bash
# 查看日志
docker compose logs kong
docker compose logs rest
# 重启服务
docker compose restart
# 完全重建
docker compose down
docker compose up -d
```
### API 无法访问
```bash
# 检查 Kong 状态
docker compose ps kong
# 检查端口
netstat -tlnp | grep 18000
# 测试内网连接
curl http://100.64.0.2:18000/rest/v1/
```
### 数据库连接问题
```bash
# 进入数据库容器
docker exec -it supabase-db psql -U postgres
# 查看数据库
\l
# 查看 schema
\dn
```
---
## 📞 支持
如有问题,请检查:
1. 所有容器是否正常运行: `docker compose ps`
2. 查看服务日志: `docker compose logs [service]`
3. 参考 `VUE_API_INTEGRATION.md` 了解详细用法
4. 访问 Dashboard 管理数据库: `http://100.64.0.2:18000`
---
**最后更新**: 2025-11-22
**版本**: Supabase Self-Hosted Stack

View File

@@ -0,0 +1,350 @@
# Supabase Stack 运维指南
完整的 Supabase 对象存储运维手册
---
## 📋 目录
1. [系统架构](#系统架构)
2. [服务启动与管理](#服务启动与管理)
3. [Storage API 使用](#storage-api-使用)
4. [故障排查](#故障排查)
---
## 🏗️ 系统架构
### 组件说明
```
外部用户 (HTTPS)
Traefik (:443)
Kong Gateway (:18000)
┌────────────────────────────────┐
│ Supabase Services │
├────────────────────────────────┤
│ • Auth (GoTrue) - 用户认证 │
│ • REST (PostgREST) - 数据API │
│ • Storage - 对象存储 │
│ • Realtime - 实时订阅 │
└────────────────────────────────┘
PostgreSQL + MinIO
```
### 访问地址
| 服务 | 地址 | 用途 |
|------|------|------|
| 公网 API | https://amiap.hzau.edu.cn/supa | 所有 API 入口 |
| Storage API | https://amiap.hzau.edu.cn/supa/storage/v1 | 对象存储 |
| Dashboard | http://100.64.0.2:18000 | 管理后台(内网) |
### 数据存储
- **PostgreSQL 数据**: `./volumes/db/data/`
- **对象存储数据**: `/vol1/1000/s3/stub/`
---
## 🚀 服务启动与管理
### 启动服务
```bash
cd /vol1/1000/docker_server/traefik/supabase-stack
# 启动所有服务(包含 MinIO 对象存储后端)
docker compose -f docker-compose.yml -f docker-compose.s3.yml up -d
```
**说明**
- `docker-compose.yml` - Supabase 核心服务
- `docker-compose.s3.yml` - MinIO 存储后端(**必需**
### 查看状态
```bash
# 查看所有容器
docker compose ps
# 查看日志
docker compose logs -f storage
docker compose logs -f kong
```
### 停止/重启
```bash
# 停止服务
docker compose stop
# 重启特定服务
docker compose restart storage
docker compose restart minio
# 完全停止并删除容器(数据保留)
docker compose down
```
### 环境变量
关键密钥在 `.env` 文件中:
```bash
# 服务密钥(后端使用)
SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3NjM4MDI2NjksImV4cCI6MjA3OTE2MjY2OX0.gQWUaTkZ6mjjlv2TED0cODp2meqqWuCGKZR1ptIbovg
# 匿名密钥(前端使用)
ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzYzODAyNjY5LCJleHAiOjIwNzkxNjI2Njl9.ltGXvQKpguLaf8Vzomn310hLgOZbrjqZT-F3rR00ulg
```
---
## 📦 Storage API 使用
### API 端点
```
Base URL: https://amiap.hzau.edu.cn/supa
Storage: https://amiap.hzau.edu.cn/supa/storage/v1
```
### 完整 Python 示例
保存为 `storage_client.py`
```python
import requests
from pathlib import Path
class StorageClient:
def __init__(self, base_url, api_key):
self.base_url = base_url
self.headers = {
'apikey': api_key,
'Authorization': f'Bearer {api_key}'
}
def create_bucket(self, name, public=False):
"""创建 bucket"""
response = requests.post(
f'{self.base_url}/storage/v1/bucket',
headers=self.headers,
json={'name': name, 'public': public}
)
return response.ok
def upload_file(self, bucket, file_path, storage_path=None):
"""上传文件"""
if not storage_path:
storage_path = Path(file_path).name
with open(file_path, 'rb') as f:
response = requests.post(
f'{self.base_url}/storage/v1/object/{bucket}/{storage_path}',
headers=self.headers,
files={'file': f}
)
return response.json() if response.ok else None
def download_file(self, bucket, storage_path, local_path):
"""下载文件"""
response = requests.get(
f'{self.base_url}/storage/v1/object/{bucket}/{storage_path}',
headers=self.headers
)
if response.ok:
with open(local_path, 'wb') as f:
f.write(response.content)
return True
return False
def create_signed_url(self, bucket, storage_path, expires_in=3600):
"""生成临时下载链接"""
response = requests.post(
f'{self.base_url}/storage/v1/object/sign/{bucket}/{storage_path}',
headers=self.headers,
json={'expiresIn': expires_in}
)
if response.ok:
return self.base_url + response.json()['signedURL']
return None
# 使用示例
if __name__ == '__main__':
client = StorageClient(
'https://amiap.hzau.edu.cn/supa',
'your-service-role-key'
)
# 创建 bucket
client.create_bucket('my-bucket')
# 上传文件
client.upload_file('my-bucket', 'photo.jpg', 'uploads/photo.jpg')
# 生成临时链接
url = client.create_signed_url('my-bucket', 'uploads/photo.jpg')
print(f'下载链接: {url}')
```
### 完整 JavaScript 示例
```javascript
class StorageClient {
constructor(baseUrl, apiKey) {
this.baseUrl = baseUrl;
this.headers = {
'apikey': apiKey,
'Authorization': `Bearer ${apiKey}`
};
}
async createBucket(name, isPublic = false) {
const response = await fetch(`${this.baseUrl}/storage/v1/bucket`, {
method: 'POST',
headers: {
...this.headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name, public: isPublic })
});
return response.ok;
}
async uploadFile(bucket, file, storagePath) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(
`${this.baseUrl}/storage/v1/object/${bucket}/${storagePath}`,
{
method: 'POST',
headers: this.headers,
body: formData
}
);
return response.ok ? await response.json() : null;
}
async createSignedUrl(bucket, storagePath, expiresIn = 3600) {
const response = await fetch(
`${this.baseUrl}/storage/v1/object/sign/${bucket}/${storagePath}`,
{
method: 'POST',
headers: {
...this.headers,
'Content-Type': 'application/json'
},
body: JSON.stringify({ expiresIn })
}
);
if (response.ok) {
const data = await response.json();
return this.baseUrl + data.signedURL;
}
return null;
}
}
// 使用示例
const client = new StorageClient(
'https://amiap.hzau.edu.cn/supa',
'your-service-role-key'
);
// 上传文件
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
await client.uploadFile('my-bucket', file, `uploads/${file.name}`);
});
```
完整代码示例请查看:`storage_client.py``storage_client.js`
---
## 🔧 故障排查
### 常见问题
**1. 服务无法启动**
```bash
# 检查端口占用
docker compose ps
netstat -tulpn | grep -E ":(8000|5432|9000)"
# 查看日志
docker compose logs storage
```
**2. API 返回 403**
```bash
# 检查密钥是否正确
grep SERVICE_ROLE_KEY .env
# 测试 API
curl -I https://amiap.hzau.edu.cn/supa/storage/v1/bucket \
-H "apikey: YOUR_KEY"
```
**3. 上传失败**
```bash
# 检查 MinIO 是否运行
docker compose ps minio
# 查看 Storage 日志
docker compose logs storage | tail -50
```
### 健康检查
```bash
# 快速测试脚本
python3 test_https_storage.py
# 手动测试
curl https://amiap.hzau.edu.cn/supa/rest/v1/
```
### 备份
```bash
# 备份数据库
docker exec supabase-db pg_dump -U postgres > backup.sql
# 备份对象存储
tar -czf s3_backup.tar.gz /vol1/1000/s3/
```
---
## 📚 相关文档
- `storage_client.py` - Python 完整客户端代码
- `storage_client.js` - JavaScript 完整客户端代码
- `test_https_storage.py` - 测试脚本
- `VUE_API_INTEGRATION.md` - Vue 集成指南
---
## 🎯 快速开始
```bash
# 1. 启动服务
docker compose -f docker-compose.yml -f docker-compose.s3.yml up -d
# 2. 测试
python3 test_https_storage.py
# 3. 查看文档
cat storage_client.py
```
完成!现在可以开始使用 Supabase Storage 了。

View File

@@ -0,0 +1,76 @@
# Supabase Stack 快速参考
## 🚀 启动服务
```bash
cd /vol1/1000/docker_server/traefik/supabase-stack
# 启动所有服务(包含 MinIO 对象存储)
docker compose -f docker-compose.yml -f docker-compose.s3.yml up -d
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f storage
```
## 📦 Storage API 访问
**端点**: `https://amiap.hzau.edu.cn/supa/storage/v1`
**密钥**:
```bash
SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3NjM4MDI2NjksImV4cCI6MjA3OTE2MjY2OX0.gQWUaTkZ6mjjlv2TED0cODp2meqqWuCGKZR1ptIbovg
```
## 💻 快速使用
### Python
```python
from storage_client import SupabaseStorageClient
client = SupabaseStorageClient(
'https://amiap.hzau.edu.cn/supa',
'your-service-role-key'
)
# 上传文件
client.upload_file('bucket', 'photo.jpg', 'uploads/photo.jpg')
# 生成临时链接
url = client.create_signed_url('bucket', 'uploads/photo.jpg', 3600)
```
### JavaScript
```javascript
const client = new SupabaseStorageClient(
'https://amiap.hzau.edu.cn/supa',
'your-service-role-key'
);
// 上传文件
await client.uploadFile('bucket', fileObject, 'uploads/photo.jpg');
// 生成临时链接
const url = await client.createSignedUrl('bucket', 'uploads/photo.jpg', 3600);
```
## 📚 文档
- **OPERATIONS_GUIDE.md** - 完整运维指南(必读)
- **storage_client.py** - Python 完整客户端代码
- **storage_client.js** - JavaScript 完整客户端代码
- **test_https_storage.py** - 测试脚本
## 🧪 测试
```bash
python3 test_https_storage.py
```
## ⚠️ 重要
- **MinIO 必须运行**docker-compose.s3.yml 不能删除
- **数据位置**/vol1/1000/s3/stub/
- **不要直接访问 MinIO**:统一通过 Storage API

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
# Docker Image Versions
## 2025-11-12
- supabase/studio:2025.11.10-sha-5291fe3 (prev 2025.10.27-sha-85b84e0)
- supabase/gotrue:v2.182.1 (prev v2.180.0)
- supabase/realtime:v2.63.0 (prev v2.57.2)
- supabase/storage-api:v1.29.0 (prev v1.28.2)
- supabase/edge-runtime:v1.69.23 (prev v1.69.15)
- supabase/supavisor:2.7.4 (prev 2.7.3)
## 2025-10-28
- supabase/studio:2025.10.27-sha-85b84e0 (prev 2025.10.20-sha-5005fc6)
- supabase/realtime:v2.57.2 (prev v2.56.0)
- supabase/storage-api:v1.28.2 (prev v1.28.1)
- supabase/postgres-meta:v0.93.1 (prev v0.93.0)
- supabase/edge-runtime:v1.69.15 (prev v1.69.14)
## 2025-10-21
- supabase/studio:2025.10.20-sha-5005fc6 (prev 2025.10.01-sha-8460121)
- supabase/realtime:v2.56.0 (prev v2.51.11)
- supabase/storage-api:v1.28.1 (prev v1.28.0)
- supabase/postgres-meta:v0.93.0 (prev v0.91.6)
- supabase/edge-runtime:v1.69.14 (prev v1.69.6)
- supabase/supavisor:2.7.3 (prev 2.7.0)
## 2025-10-13
- supabase/logflare:1.22.6 (prev 1.22.4)
## 2025-10-08
- supabase/studio:2025.10.01-sha-8460121 (prev 2025.06.30-sha-6f5982d)
- supabase/gotrue:v2.180.0 (prev v2.177.0)
- postgrest/postgrest:v13.0.7 (prev v12.2.12)
- supabase/realtime:v2.51.11 (prev v2.34.47)
- supabase/storage-api:v1.28.0 (prev v1.25.7)
- supabase/postgres-meta:v0.91.6 (prev v0.91.0)
- supabase/logflare:1.22.4 (prev 1.14.2)
- supabase/postgres:15.8.1.085 (prev 15.8.1.060)
- supabase/supavisor:2.7.0 (prev 2.5.7)
## 2025-07-15
- supabase/gotrue:v2.177.0 (prev v2.176.1)
- supabase/storage-api:v1.25.7 (prev v1.24.7)
- supabase/postgres-meta:v0.91.0 (prev v0.89.3)
- supabase/supavisor:2.5.7 (prev 2.5.6)
## 2025-07-02
- supabase/studio:2025.06.30-sha-6f5982d (prev 2025.06.02-sha-8f2993d)
- supabase/gotrue:v2.176.1 (prev v2.174.0)
- supabase/storage-api:v1.24.7 (prev v1.23.0)
- supabase/supavisor:2.5.6 (prev 2.5.1)
## 2025-06-03
- supabase/studio:2025.06.02-sha-8f2993d (prev 2025.05.19-sha-3487831)
- supabase/gotrue:v2.174.0 (prev v2.172.1)
- supabase/storage-api:v1.23.0 (prev v1.22.17)
- supabase/postgres-meta:v0.89.3 (prev v0.89.0)