- 新增图像生成接口,支持试用、积分和自定义API Key模式 - 实现生成图片结果异步上传至MinIO存储,带重试机制 - 优化积分预扣除和异常退还逻辑,保障用户积分准确 - 添加获取生成历史记录接口,支持时间范围和分页 - 提供本地字典配置接口,支持模型、比例、提示模板和尺寸 - 实现图片批量上传接口,支持S3兼容对象存储 feat(admin): 增加管理员角色管理与权限分配接口 - 实现角色列表查询、角色创建、更新及删除功能 - 增加权限列表查询接口 - 实现用户角色分配接口,便于统一管理用户权限 - 增加系统字典增删查改接口,支持分类过滤和排序 - 权限控制全面覆盖管理接口,保证安全访问 feat(auth): 完善用户登录注册及权限相关接口与页面 - 实现手机号验证码发送及校验功能,保障注册安全 - 支持手机号注册、登录及退出接口,集成日志记录 - 增加修改密码功能,验证原密码后更新 - 提供动态导航菜单接口,基于权限展示不同菜单 - 实现管理界面路由及日志、角色、字典管理页面访问权限控制 - 添加系统日志查询接口,支持关键词和等级筛选 feat(app): 初始化Flask应用并配置蓝图与数据库 - 创建应用程序工厂,加载配置,初始化数据库和Redis客户端 - 注册认证、API及管理员蓝图,整合路由 - 根路由渲染主页模板 - 应用上下文中自动创建数据库表,保证运行环境准备完毕 feat(database): 提供数据库创建与迁移支持脚本 - 新增数据库创建脚本,支持自动检测是否已存在 - 添加数据库表初始化脚本,支持创建和删除所有表 - 实现RBAC权限初始化,包含基础权限和角色创建 - 新增字段手动修复脚本,添加用户API Key和积分字段 - 强制迁移脚本支持清理连接和修复表结构,初始化默认数据及角色分配 feat(config): 新增系统配置参数 - 配置数据库、Redis、Session和MinIO相关参数 - 添加AI接口地址及试用Key配置 - 集成阿里云短信服务配置及开发模式相关参数 feat(extensions): 初始化数据库、Redis和MinIO客户端 - 创建全局SQLAlchemy数据库实例和Redis客户端 - 配置基于boto3的MinIO兼容S3客户端 chore(logs): 添加示例系统日志文件 - 记录用户请求、验证码发送成功与失败的日志信息
218 lines
7.1 KiB
Python
218 lines
7.1 KiB
Python
# Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
# may not use this file except in compliance with the License. A copy of
|
|
# the License is located at
|
|
#
|
|
# https://aws.amazon.com/apache2.0/
|
|
#
|
|
# or in the "license" file accompanying this file. This file is
|
|
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
# ANY KIND, either express or implied. See the License for the specific
|
|
# language governing permissions and limitations under the License.
|
|
"""
|
|
This file contains private functionality for interacting with the AWS
|
|
Common Runtime library (awscrt) in boto3.
|
|
|
|
All code contained within this file is for internal usage within this
|
|
project and is not intended for external consumption. All interfaces
|
|
contained within are subject to abrupt breaking changes.
|
|
"""
|
|
|
|
import logging
|
|
import threading
|
|
|
|
import botocore.exceptions
|
|
from botocore.session import Session
|
|
from s3transfer.crt import (
|
|
BotocoreCRTCredentialsWrapper,
|
|
BotocoreCRTRequestSerializer,
|
|
CRTTransferManager,
|
|
acquire_crt_s3_process_lock,
|
|
create_s3_crt_client,
|
|
)
|
|
|
|
from boto3.compat import TRANSFER_CONFIG_SUPPORTS_CRT
|
|
from boto3.exceptions import InvalidCrtTransferConfigError
|
|
from boto3.s3.constants import CRT_TRANSFER_CLIENT
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Singletons for CRT-backed transfers
|
|
CRT_S3_CLIENT = None
|
|
BOTOCORE_CRT_SERIALIZER = None
|
|
|
|
CLIENT_CREATION_LOCK = threading.Lock()
|
|
PROCESS_LOCK_NAME = 'boto3'
|
|
|
|
|
|
_ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS = {
|
|
'multipart_threshold',
|
|
'max_concurrency',
|
|
'max_request_concurrency',
|
|
'multipart_chunksize',
|
|
'preferred_transfer_client',
|
|
}
|
|
|
|
|
|
def _create_crt_client(session, config, region_name, cred_provider):
|
|
"""Create a CRT S3 Client for file transfer.
|
|
|
|
Instantiating many of these may lead to degraded performance or
|
|
system resource exhaustion.
|
|
"""
|
|
create_crt_client_kwargs = {
|
|
'region': region_name,
|
|
'use_ssl': True,
|
|
'crt_credentials_provider': cred_provider,
|
|
}
|
|
return create_s3_crt_client(**create_crt_client_kwargs)
|
|
|
|
|
|
def _create_crt_request_serializer(session, region_name):
|
|
return BotocoreCRTRequestSerializer(
|
|
session, {'region_name': region_name, 'endpoint_url': None}
|
|
)
|
|
|
|
|
|
def _create_crt_s3_client(
|
|
session, config, region_name, credentials, lock, **kwargs
|
|
):
|
|
"""Create boto3 wrapper class to manage crt lock reference and S3 client."""
|
|
cred_wrapper = BotocoreCRTCredentialsWrapper(credentials)
|
|
cred_provider = cred_wrapper.to_crt_credentials_provider()
|
|
return CRTS3Client(
|
|
_create_crt_client(session, config, region_name, cred_provider),
|
|
lock,
|
|
region_name,
|
|
cred_wrapper,
|
|
)
|
|
|
|
|
|
def _initialize_crt_transfer_primatives(client, config):
|
|
lock = acquire_crt_s3_process_lock(PROCESS_LOCK_NAME)
|
|
if lock is None:
|
|
# If we're unable to acquire the lock, we cannot
|
|
# use the CRT in this process and should default to
|
|
# the classic s3transfer manager.
|
|
return None, None
|
|
|
|
session = Session()
|
|
region_name = client.meta.region_name
|
|
credentials = client._get_credentials()
|
|
|
|
serializer = _create_crt_request_serializer(session, region_name)
|
|
s3_client = _create_crt_s3_client(
|
|
session, config, region_name, credentials, lock
|
|
)
|
|
return serializer, s3_client
|
|
|
|
|
|
def get_crt_s3_client(client, config):
|
|
global CRT_S3_CLIENT
|
|
global BOTOCORE_CRT_SERIALIZER
|
|
|
|
with CLIENT_CREATION_LOCK:
|
|
if CRT_S3_CLIENT is None:
|
|
serializer, s3_client = _initialize_crt_transfer_primatives(
|
|
client, config
|
|
)
|
|
BOTOCORE_CRT_SERIALIZER = serializer
|
|
CRT_S3_CLIENT = s3_client
|
|
|
|
return CRT_S3_CLIENT
|
|
|
|
|
|
class CRTS3Client:
|
|
"""
|
|
This wrapper keeps track of our underlying CRT client, the lock used to
|
|
acquire it and the region we've used to instantiate the client.
|
|
|
|
Due to limitations in the existing CRT interfaces, we can only make calls
|
|
in a single region and does not support redirects. We track the region to
|
|
ensure we don't use the CRT client when a successful request cannot be made.
|
|
"""
|
|
|
|
def __init__(self, crt_client, process_lock, region, cred_provider):
|
|
self.crt_client = crt_client
|
|
self.process_lock = process_lock
|
|
self.region = region
|
|
self.cred_provider = cred_provider
|
|
|
|
|
|
def is_crt_compatible_request(client, crt_s3_client):
|
|
"""
|
|
Boto3 client must use same signing region and credentials
|
|
as the CRT_S3_CLIENT singleton. Otherwise fallback to classic.
|
|
"""
|
|
if crt_s3_client is None:
|
|
return False
|
|
|
|
boto3_creds = client._get_credentials()
|
|
if boto3_creds is None:
|
|
return False
|
|
|
|
is_same_identity = compare_identity(
|
|
boto3_creds.get_frozen_credentials(), crt_s3_client.cred_provider
|
|
)
|
|
is_same_region = client.meta.region_name == crt_s3_client.region
|
|
return is_same_region and is_same_identity
|
|
|
|
|
|
def compare_identity(boto3_creds, crt_s3_creds):
|
|
try:
|
|
crt_creds = crt_s3_creds()
|
|
except botocore.exceptions.NoCredentialsError:
|
|
return False
|
|
|
|
is_matching_identity = (
|
|
boto3_creds.access_key == crt_creds.access_key_id
|
|
and boto3_creds.secret_key == crt_creds.secret_access_key
|
|
and boto3_creds.token == crt_creds.session_token
|
|
)
|
|
return is_matching_identity
|
|
|
|
|
|
def _validate_crt_transfer_config(config):
|
|
if config is None:
|
|
return
|
|
# CRT client can also be configured via `AUTO_RESOLVE_TRANSFER_CLIENT`
|
|
# but it predates this validation. We only validate against CRT client
|
|
# configured via `CRT_TRANSFER_CLIENT` to preserve compatibility.
|
|
if config.preferred_transfer_client != CRT_TRANSFER_CLIENT:
|
|
return
|
|
invalid_crt_args = []
|
|
for param in config.DEFAULTS.keys():
|
|
val = config.get_deep_attr(param)
|
|
if (
|
|
param not in _ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS
|
|
and val is not config.UNSET_DEFAULT
|
|
):
|
|
invalid_crt_args.append(param)
|
|
if len(invalid_crt_args) > 0:
|
|
raise InvalidCrtTransferConfigError(
|
|
"The following transfer config options are invalid "
|
|
"when preferred_transfer_client is set to crt: "
|
|
f"{', '.join(invalid_crt_args)}`"
|
|
)
|
|
|
|
|
|
def create_crt_transfer_manager(client, config):
|
|
"""Create a CRTTransferManager for optimized data transfer."""
|
|
crt_s3_client = get_crt_s3_client(client, config)
|
|
if is_crt_compatible_request(client, crt_s3_client):
|
|
crt_transfer_manager_kwargs = {
|
|
'crt_s3_client': crt_s3_client.crt_client,
|
|
'crt_request_serializer': BOTOCORE_CRT_SERIALIZER,
|
|
}
|
|
if TRANSFER_CONFIG_SUPPORTS_CRT:
|
|
_validate_crt_transfer_config(config)
|
|
crt_transfer_manager_kwargs['config'] = config
|
|
if not TRANSFER_CONFIG_SUPPORTS_CRT and config:
|
|
logger.warning(
|
|
'Using TransferConfig with CRT client requires '
|
|
's3transfer >= 0.16.0, configured values will be ignored.'
|
|
)
|
|
return CRTTransferManager(**crt_transfer_manager_kwargs)
|
|
return None
|