- 创建 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
351 lines
8.4 KiB
Markdown
351 lines
8.4 KiB
Markdown
# 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 了。
|