File size: 8,967 Bytes
29482b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d19dce9
29482b8
 
 
 
78f2303
 
 
 
 
 
 
 
 
 
 
 
 
29482b8
 
 
 
 
 
 
 
d19dce9
29482b8
 
 
 
cb45be3
 
 
29482b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb45be3
 
 
 
 
 
29482b8
78f2303
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29482b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d19dce9
29482b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d19dce9
29482b8
 
 
 
 
77d2c41
29482b8
 
 
 
 
 
77d2c41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import os
import json
import uuid
from typing import List, Dict
import logging

logger = logging.getLogger("sora-api.config")

class Config:
    # API服务配置
    HOST = os.getenv("API_HOST", "0.0.0.0")
    PORT = int(os.getenv("API_PORT", "8890"))
    
    # 基础URL配置
    BASE_URL = os.getenv("BASE_URL", f"http://0.0.0.0:{PORT}")
    
    # 静态文件路径前缀,用于处理应用部署在子路径的情况
    # 例如: /sora-api 表示应用部署在 /sora-api 下
    STATIC_PATH_PREFIX = os.getenv("STATIC_PATH_PREFIX", "")
    
    # 代理配置
    PROXY_HOST = os.getenv("PROXY_HOST", "")
    PROXY_PORT = os.getenv("PROXY_PORT", "")
    PROXY_USER = os.getenv("PROXY_USER", "")
    PROXY_PASS = os.getenv("PROXY_PASS", "")
    
    # 目录配置
    ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    BASE_DIR = ROOT_DIR
    STATIC_DIR = os.getenv("STATIC_DIR", os.path.join(BASE_DIR, "src/static"))
    
    # 图片保存目录 - 用户只需设置这一项
    IMAGE_SAVE_DIR = os.getenv("IMAGE_SAVE_DIR", "/tmp/images")
    
    # 图片本地化配置
    IMAGE_LOCALIZATION = os.getenv("IMAGE_LOCALIZATION", "False").lower() in ("true", "1", "yes")
    
    # PicGo配置
    ENABLE_PICGO = os.getenv("ENABLE_PICGO", "False").lower() in ("true", "1", "yes")
    PICGO_URL = os.getenv("PICGO_URL", "http://127.0.0.1:36677")
    PICGO_API_KEY = os.getenv("PICGO_API_KEY", "")
    
    # WebDAV配置
    ENABLE_WEBDAV = os.getenv("ENABLE_WEBDAV", "False").lower() in ("true", "1", "yes")
    WEBDAV_URL = os.getenv("WEBDAV_URL", "")
    WEBDAV_USERNAME = os.getenv("WEBDAV_USERNAME", "")
    WEBDAV_PASSWORD = os.getenv("WEBDAV_PASSWORD", "")
    WEBDAV_BASE_PATH = os.getenv("WEBDAV_BASE_PATH", "/sora-images")
    WEBDAV_PUBLIC_URL = os.getenv("WEBDAV_PUBLIC_URL", "")
    
    # 当外部访问地址与服务器地址不同时,可通过BASE_URL覆盖图片访问地址
    # 例如:当服务器在内网,但通过反向代理从外网访问时
    
    # API Keys配置
    API_KEYS = []
    
    # 管理员配置
    ADMIN_KEY = os.getenv("ADMIN_KEY", "sk-123456")
    KEYS_STORAGE_FILE = os.getenv("KEYS_STORAGE_FILE", "/tmp/api_keys.json")
    
    # API认证令牌
    API_AUTH_TOKEN = os.getenv("API_AUTH_TOKEN", "")
    
    # API访问令牌(用于认证API请求,与API_AUTH_TOKEN区分)
    API_ACCESS_TOKEN = os.getenv("API_ACCESS_TOKEN", "")
    
    # 日志配置
    VERBOSE_LOGGING = os.getenv("VERBOSE_LOGGING", "False").lower() in ("true", "1", "yes")
    
    @classmethod
    def print_config(cls):
        """打印当前配置信息"""
        print("\n==== Sora API 配置信息 ====")
        print(f"基础目录: {cls.BASE_DIR}")
        print(f"API服务地址: {cls.HOST}:{cls.PORT}")
        print(f"基础URL: {cls.BASE_URL}")
        
        # API认证信息
        if cls.API_AUTH_TOKEN:
            print(f"API认证令牌: 已设置 (长度: {len(cls.API_AUTH_TOKEN)})")
        else:
            print(f"API认证令牌: 未设置 (将使用管理面板的key)")
            
        # API访问令牌信息
        if cls.API_ACCESS_TOKEN:
            print(f"API访问令牌: 已设置 (长度: {len(cls.API_ACCESS_TOKEN)})")
        else:
            print(f"API访问令牌: 未设置 (将使用key_manager中的密钥)")
        
        # 图片存储配置
        print(f"图片保存目录: {cls.IMAGE_SAVE_DIR}")
        
        # 图片URL生成模式
        if cls.IMAGE_LOCALIZATION:
            image_url_base = f"{cls.BASE_URL}"
            if cls.STATIC_PATH_PREFIX:
                prefix = cls.STATIC_PATH_PREFIX
                if not prefix.startswith('/'):
                    prefix = f"/{prefix}"
                image_url_base = f"{image_url_base}{prefix}"
            print(f"图片将通过 {image_url_base}/images/<filename> 访问")
        
        # 图片上传服务配置
        if cls.ENABLE_PICGO:
            print(f"PicGo上传: 已启用 ({cls.PICGO_URL})")
        
        if cls.ENABLE_WEBDAV:
            webdav_info = cls.WEBDAV_URL
            if cls.WEBDAV_PUBLIC_URL:
                webdav_info += f" -> {cls.WEBDAV_PUBLIC_URL}"
            print(f"WebDAV上传: 已启用 ({webdav_info})")
        
        # 详细日志
        if cls.VERBOSE_LOGGING:
            print(f"静态文件目录: {cls.STATIC_DIR}")
            print(f"图片本地化: {'启用' if cls.IMAGE_LOCALIZATION else '禁用'}")
            
            # 代理配置信息
            if cls.PROXY_HOST:
                proxy_info = f"{cls.PROXY_HOST}:{cls.PROXY_PORT}"
                if cls.PROXY_USER:
                    proxy_info = f"{cls.PROXY_USER}:****@{proxy_info}"
                print(f"代理配置: {proxy_info}")
            else:
                print(f"代理配置: (未配置)")
        
        # 确保必要目录存在
        cls._ensure_directories()
        
    @classmethod
    def _ensure_directories(cls):
        """确保必要的目录存在"""
        # 检查静态文件目录
        if not os.path.exists(cls.STATIC_DIR):
            print(f"⚠️ 警告: 静态文件目录不存在: {cls.STATIC_DIR}")
        elif cls.VERBOSE_LOGGING:
            print(f"✅ 静态文件目录存在")
            
        # 检查并创建图片保存目录
        if not os.path.exists(cls.IMAGE_SAVE_DIR):
            try:
                os.makedirs(cls.IMAGE_SAVE_DIR, exist_ok=True)
                if cls.VERBOSE_LOGGING:
                    print(f"✅ 已创建图片保存目录: {cls.IMAGE_SAVE_DIR}")
            except Exception as e:
                print(f"❌ 创建图片保存目录失败: {str(e)}")
        elif cls.VERBOSE_LOGGING:
            print(f"✅ 图片保存目录存在")
            
            # 测试写入权限
            try:
                test_file = os.path.join(cls.IMAGE_SAVE_DIR, '.test_write')
                with open(test_file, 'w') as f:
                    f.write('test')
                os.remove(test_file)
                print(f"✅ 图片保存目录有写入权限")
            except Exception as e:
                print(f"❌ 图片保存目录没有写入权限: {str(e)}")
    
    @classmethod
    def load_api_keys(cls):
        """加载API密钥"""
        # 先从环境变量加载
        api_keys_str = os.getenv("API_KEYS", "")
        if api_keys_str:
            try:
                cls.API_KEYS = json.loads(api_keys_str)
                if cls.VERBOSE_LOGGING:
                    logger.info(f"已从环境变量加载 {len(cls.API_KEYS)} 个API keys")
                return
            except json.JSONDecodeError as e:
                logger.error(f"解析环境变量API keys失败: {e}")
                
        # 再从文件加载
        try:
            if os.path.exists(cls.KEYS_STORAGE_FILE):
                with open(cls.KEYS_STORAGE_FILE, "r", encoding="utf-8") as f:
                    keys_data = json.load(f)
                    if isinstance(keys_data, dict) and "keys" in keys_data:
                        cls.API_KEYS = [k for k in keys_data["keys"] if k.get("key")]
                    else:
                        cls.API_KEYS = keys_data
                    
                    if cls.VERBOSE_LOGGING:
                        logger.info(f"已从文件加载 {len(cls.API_KEYS)} 个API keys")
        except Exception as e:
            logger.error(f"从文件加载API keys失败: {e}") 
    
    @classmethod
    def save_api_keys(cls, keys_data):
        """保存API密钥到文件"""
        try:
            keys_storage_file = cls.KEYS_STORAGE_FILE
            
            with open(keys_storage_file, "w", encoding="utf-8") as f:
                if isinstance(keys_data, list):
                    json.dump({"keys": keys_data}, f, ensure_ascii=False, indent=2)
                else:
                    json.dump(keys_data, f, ensure_ascii=False, indent=2)
            
            # 更新内存中的keys
            if isinstance(keys_data, dict) and "keys" in keys_data:
                cls.API_KEYS = [k for k in keys_data["keys"] if k.get("key")]
            else:
                cls.API_KEYS = keys_data
                
            if cls.VERBOSE_LOGGING:
                logger.info(f"API keys已保存至 {keys_storage_file}")
        except Exception as e:
            logger.error(f"保存API keys失败: {str(e)}")
    
    @classmethod
    def save_admin_key(cls):
        """保存管理员密钥到文件"""
        try:
            admin_config_file = os.path.join("/tmp", "admin_config.json")
            with open(admin_config_file, "w", encoding="utf-8") as f:
                json.dump({"admin_key": cls.ADMIN_KEY}, f, indent=2)
            
            if cls.VERBOSE_LOGGING:
                logger.info(f"管理员密钥已保存至 {admin_config_file}")
        except Exception as e:
            logger.error(f"保存管理员密钥失败: {str(e)}")