Skip to content

Commit

Permalink
feat: add supabase object storage (langgenius#9229)
Browse files Browse the repository at this point in the history
  • Loading branch information
hwzhuhao authored and lau-td committed Oct 23, 2024
1 parent e0f50a0 commit cbc6fd6
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 105 deletions.
7 changes: 6 additions & 1 deletion api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ DB_DATABASE=dify

# Storage configuration
# use for store upload files, private keys...
# storage type: local, s3, azure-blob, google-storage, tencent-cos, huawei-obs, volcengine-tos, baidu-obs
# storage type: local, s3, azure-blob, google-storage, tencent-cos, huawei-obs, volcengine-tos, baidu-obs, supabase
STORAGE_TYPE=local
STORAGE_LOCAL_PATH=storage
S3_USE_AWS_MANAGED_IAM=false
Expand Down Expand Up @@ -99,6 +99,11 @@ VOLCENGINE_TOS_ACCESS_KEY=your-access-key
VOLCENGINE_TOS_SECRET_KEY=your-secret-key
VOLCENGINE_TOS_REGION=your-region

# Supabase Storage Configuration
SUPABASE_BUCKET_NAME=your-bucket-name
SUPABASE_API_KEY=your-access-key
SUPABASE_URL=your-server-url

# CORS configuration
WEB_API_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
CONSOLE_CORS_ALLOW_ORIGINS=http://127.0.0.1:3000,*
Expand Down
2 changes: 2 additions & 0 deletions api/configs/middleware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from configs.middleware.storage.google_cloud_storage_config import GoogleCloudStorageConfig
from configs.middleware.storage.huawei_obs_storage_config import HuaweiCloudOBSStorageConfig
from configs.middleware.storage.oci_storage_config import OCIStorageConfig
from configs.middleware.storage.supabase_storage_config import SupabaseStorageConfig
from configs.middleware.storage.tencent_cos_storage_config import TencentCloudCOSStorageConfig
from configs.middleware.storage.volcengine_tos_storage_config import VolcengineTOSStorageConfig
from configs.middleware.vdb.analyticdb_config import AnalyticdbConfig
Expand Down Expand Up @@ -222,6 +223,7 @@ class MiddlewareConfig(
HuaweiCloudOBSStorageConfig,
OCIStorageConfig,
S3StorageConfig,
SupabaseStorageConfig,
TencentCloudCOSStorageConfig,
VolcengineTOSStorageConfig,
# configs of vdb and vdb providers
Expand Down
24 changes: 24 additions & 0 deletions api/configs/middleware/storage/supabase_storage_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import Optional

from pydantic import BaseModel, Field


class SupabaseStorageConfig(BaseModel):
"""
Configuration settings for Supabase Object Storage Service
"""

SUPABASE_BUCKET_NAME: Optional[str] = Field(
description="Name of the Supabase bucket to store and retrieve objects (e.g., 'dify-bucket')",
default=None,
)

SUPABASE_API_KEY: Optional[str] = Field(
description="API KEY for authenticating with Supabase",
default=None,
)

SUPABASE_URL: Optional[str] = Field(
description="URL of the Supabase",
default=None,
)
4 changes: 4 additions & 0 deletions api/extensions/ext_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def get_storage_factory(storage_type: str) -> type[BaseStorage]:
from extensions.storage.volcengine_tos_storage import VolcengineTosStorage

return VolcengineTosStorage
case StorageType.SUPBASE:
from extensions.storage.supabase_storage import SupabaseStorage

return SupabaseStorage
case StorageType.LOCAL | _:
from extensions.storage.local_fs_storage import LocalFsStorage

Expand Down
1 change: 1 addition & 0 deletions api/extensions/storage/storage_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ class StorageType(str, Enum):
S3 = "s3"
TENCENT_COS = "tencent-cos"
VOLCENGINE_TOS = "volcengine-tos"
SUPBASE = "supabase"
60 changes: 60 additions & 0 deletions api/extensions/storage/supabase_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import io
from collections.abc import Generator
from pathlib import Path

from flask import Flask
from supabase import Client

from extensions.storage.base_storage import BaseStorage


class SupabaseStorage(BaseStorage):
"""Implementation for supabase obs storage."""

def __init__(self, app: Flask):
super().__init__(app)
app_config = self.app.config
self.bucket_name = app_config.get("SUPABASE_BUCKET_NAME")
self.client = Client(
supabase_url=app_config.get("SUPABASE_URL"), supabase_key=app_config.get("SUPABASE_API_KEY")
)
self.create_bucket(
id=app_config.get("SUPABASE_BUCKET_NAME"), bucket_name=app_config.get("SUPABASE_BUCKET_NAME")
)

def create_bucket(self, id, bucket_name):
if not self.bucket_exists():
self.client.storage.create_bucket(id=id, name=bucket_name)

def save(self, filename, data):
self.client.storage.from_(self.bucket_name).upload(filename, data)

def load_once(self, filename: str) -> bytes:
content = self.client.storage.from_(self.bucket_name).download(filename)
return content

def load_stream(self, filename: str) -> Generator:
def generate(filename: str = filename) -> Generator:
result = self.client.storage.from_(self.bucket_name).download(filename)
byte_stream = io.BytesIO(result)
while chunk := byte_stream.read(4096): # Read in chunks of 4KB
yield chunk

return generate()

def download(self, filename, target_filepath):
result = self.client.storage.from_(self.bucket_name).download(filename)
Path(result).write_bytes(result)

def exists(self, filename):
result = self.client.storage.from_(self.bucket_name).list(filename)
if result.count() > 0:
return True
return False

def delete(self, filename):
self.client.storage.from_(self.bucket_name).remove(filename)

def bucket_exists(self):
buckets = self.client.storage.list_buckets()
return any(bucket.name == self.bucket_name for bucket in buckets)
Loading

0 comments on commit cbc6fd6

Please sign in to comment.