ai_v/venv/Lib/site-packages/alibabacloud_openapi_util/client.py

432 lines
13 KiB
Python
Raw Normal View History

feat(api): 实现图像生成及后台同步功能 - 新增图像生成接口,支持试用、积分和自定义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): 添加示例系统日志文件 - 记录用户请求、验证码发送成功与失败的日志信息
2026-01-12 00:53:31 +08:00
# -*- coding: utf-8 -*-
import binascii
import datetime
import hashlib
import hmac
import base64
import copy
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from urllib.parse import quote_plus, quote
from .sm3 import hash_sm3, Sm3
from alibabacloud_tea_util.client import Client as Util
from Tea.stream import STREAM_CLASS
from Tea.model import TeaModel
def to_str(val):
if val is None:
return val
if isinstance(val, bytes):
return str(val, encoding='utf-8')
else:
return str(val)
def rsa_sign(plaintext, secret):
if not secret.startswith(b'-----BEGIN RSA PRIVATE KEY-----'):
secret = b'-----BEGIN RSA PRIVATE KEY-----\n%s' % secret
if not secret.endswith(b'-----END RSA PRIVATE KEY-----'):
secret = b'%s\n-----END RSA PRIVATE KEY-----' % secret
key = load_pem_private_key(secret, password=None, backend=default_backend())
return key.sign(plaintext, padding.PKCS1v15(), hashes.SHA256())
def signature_method(secret, source, sign_type):
source = source.encode('utf-8')
secret = secret.encode('utf-8')
if sign_type == 'ACS3-HMAC-SHA256':
return hmac.new(secret, source, hashlib.sha256).digest()
elif sign_type == 'ACS3-HMAC-SM3':
return hmac.new(secret, source, Sm3).digest()
elif sign_type == 'ACS3-RSA-SHA256':
return rsa_sign(source, secret)
def get_canonical_query_string(query):
if query is None or len(query) <= 0:
return ''
canon_keys = []
for k, v in query.items():
if v is not None:
canon_keys.append(k)
canon_keys.sort()
query_string = ''
for key in canon_keys:
value = quote(query[key], safe='~', encoding='utf-8')
if value is None:
s = f'{key}&'
else:
s = f'{key}={value}&'
query_string += s
return query_string[:-1]
def get_canonicalized_headers(headers):
canon_keys = []
tmp_headers = {}
for k, v in headers.items():
if v is not None:
if k.lower() not in canon_keys:
canon_keys.append(k.lower())
tmp_headers[k.lower()] = [to_str(v).strip()]
else:
tmp_headers[k.lower()].append(to_str(v).strip())
canon_keys.sort()
canonical_headers = ''
for key in canon_keys:
header_entry = ','.join(sorted(tmp_headers[key]))
s = f'{key}:{header_entry}\n'
canonical_headers += s
return canonical_headers, ';'.join(canon_keys)
class Client(object):
"""
This is for OpenApi Util
"""
@staticmethod
def convert(body, content):
"""
Convert all params of body other than type of readable into content
@param body: source Model
@param content: target Model
@return: void
"""
body_map = Client._except_stream(body.to_map())
content.from_map(body_map)
@staticmethod
def _except_stream(val):
if isinstance(val, dict):
result = {}
for k, v in val.items():
result[k] = Client._except_stream(v)
return result
elif isinstance(val, list):
result = []
for i in val:
if i is not None:
item = Client._except_stream(i)
if item is not None:
result.append(item)
else:
result.append(Client._except_stream(i))
return result
elif isinstance(val, STREAM_CLASS):
return None
return val
@staticmethod
def _get_canonicalized_headers(headers):
canon_keys = []
for k in headers:
if k.startswith('x-acs-'):
canon_keys.append(k)
canon_keys = sorted(canon_keys)
canon_header = ''
for k in canon_keys:
canon_header += '%s:%s\n' % (k, headers[k])
return canon_header
@staticmethod
def _get_canonicalized_resource(pathname, query):
if len(query) <= 0:
return pathname
resource = '%s?' % pathname
query_list = sorted(list(query))
for key in query_list:
if query[key] is not None:
if query[key] == '':
s = '%s&' % key
else:
s = '%s=%s&' % (key, query[key])
resource += s
return resource[:-1]
@staticmethod
def get_string_to_sign(request):
"""
Get the string to be signed according to request
@param request: which contains signed messages
@return: the signed string
"""
method, pathname, headers, query = request.method, request.pathname, request.headers, request.query
accept = '' if headers.get('accept') is None else headers.get('accept')
content_md5 = '' if headers.get('content-md5') is None else headers.get('content-md5')
content_type = '' if headers.get('content-type') is None else headers.get('content-type')
date = '' if headers.get('date') is None else headers.get('date')
header = '%s\n%s\n%s\n%s\n%s\n' % (method, accept, content_md5, content_type, date)
canon_headers = Client._get_canonicalized_headers(headers)
canon_resource = Client._get_canonicalized_resource(pathname, query)
sign_str = header + canon_headers + canon_resource
return sign_str
@staticmethod
def get_roasignature(string_to_sign, secret):
"""
Get signature according to stringToSign, secret
@type string_to_sign: str
@param string_to_sign: the signed string
@type secret: str
@param secret: accesskey secret
@return: the signature
"""
hash_val = hmac.new(secret.encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha1).digest()
signature = base64.b64encode(hash_val).decode('utf-8')
return signature
@staticmethod
def _object_handler(key, value, out):
if value is None:
return
if isinstance(value, dict):
for k, v in value.items():
Client._object_handler('%s.%s' % (key, k), v, out)
elif isinstance(value, TeaModel):
for k, v in value.to_map().items():
Client._object_handler('%s.%s' % (key, k), v, out)
elif isinstance(value, (list, tuple)):
for index, val in enumerate(value):
Client._object_handler('%s.%s' % (key, index + 1), val, out)
else:
if key.startswith('.'):
key = key[1:]
if isinstance(value, bytes):
out[key] = str(value, encoding='utf-8')
elif not isinstance(value, STREAM_CLASS):
out[key] = str(value)
@staticmethod
def to_form(filter):
"""
Parse filter into a form string
@type filter: dict
@param filter: object
@return: the string
"""
result = {}
if filter:
Client._object_handler('', filter, result)
return Util.to_form_string(
Util.anyify_map_value(result)
)
@staticmethod
def get_timestamp():
"""
Get timestamp
@return: the timestamp string
"""
return datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
@staticmethod
def query(filter):
"""
Parse filter into a object which's type is map[string]string
@type filter: dict
@param filter: query param
@return: the object
"""
out_dict = {}
if filter:
Client._object_handler('', filter, out_dict)
return out_dict
@staticmethod
def get_rpcsignature(signed_params, method, secret):
"""
Get signature according to signedParams, method and secret
@type signed_params: dict
@param signed_params: params which need to be signed
@type method: str
@param method: http method e.g. GET
@type secret: str
@param secret: AccessKeySecret
@return: the signature
"""
queries = signed_params.copy()
keys = list(queries.keys())
keys.sort()
canonicalized_query_string = ""
for k in keys:
if queries[k] is not None:
canonicalized_query_string += f'&{quote(k, safe="~", encoding="utf-8")}=' \
f'{quote(queries[k], safe="~", encoding="utf-8")}'
string_to_sign = f'{method}&%2F&{quote_plus(canonicalized_query_string[1:], safe="~", encoding="utf-8")}'
digest_maker = hmac.new(bytes(secret + '&', encoding="utf-8"),
bytes(string_to_sign, encoding="utf-8"),
digestmod=hashlib.sha1)
hash_bytes = digest_maker.digest()
signed_str = str(base64.b64encode(hash_bytes), encoding="utf-8")
return signed_str
@staticmethod
def array_to_string_with_specified_style(array, prefix, style):
"""
Parse array into a string with specified style
@type array: any
@param array: the array
@type prefix: str
@param prefix: the prefix string
@param style: specified style e.g. repeatList
@return: the string
"""
if array is None:
return ''
if style == 'repeatList':
return Client._flat_repeat_list({prefix: array})
elif style == 'simple':
return ','.join(map(str, array))
elif style == 'spaceDelimited':
return ' '.join(map(str, array))
elif style == 'pipeDelimited':
return '|'.join(map(str, array))
elif style == 'json':
return Util.to_jsonstring(Client._parse_to_dict(array))
else:
return ''
@staticmethod
def _flat_repeat_list(dic):
query = {}
if dic:
Client._object_handler('', dic, query)
l = []
q = sorted(query)
for i in q:
k = quote_plus(i, encoding='utf-8')
v = quote_plus(query[i], encoding='utf-8')
l.append(k + '=' + v)
return '&&'.join(l)
@staticmethod
def parse_to_map(inp):
"""
Transform input as map.
"""
try:
result = Client._parse_to_dict(inp)
return copy.deepcopy(result)
except TypeError:
return
@staticmethod
def _parse_to_dict(val):
if isinstance(val, dict):
result = {}
for k, v in val.items():
if isinstance(v, (list, dict, TeaModel)):
result[k] = Client._parse_to_dict(v)
else:
result[k] = v
return result
elif isinstance(val, list):
result = []
for i in val:
if isinstance(i, (list, dict, TeaModel)):
result.append(Client._parse_to_dict(i))
else:
result.append(i)
return result
elif isinstance(val, TeaModel):
return val.to_map()
@staticmethod
def get_endpoint(endpoint, server_use, endpoint_type):
"""
If endpointType is internal, use internal endpoint
If serverUse is true and endpointType is accelerate, use accelerate endpoint
Default return endpoint
@param server_use whether use accelerate endpoint
@param endpoint_type value must be internal or accelerate
@return the final endpoint
"""
if endpoint_type == "internal":
str_split = endpoint.split('.')
str_split[0] += "-internal"
endpoint = ".".join(str_split)
if server_use and endpoint_type == "accelerate":
return "oss-accelerate.aliyuncs.com"
return endpoint
@staticmethod
def hash(raw, sign_type):
if sign_type == 'ACS3-HMAC-SHA256' or sign_type == 'ACS3-RSA-SHA256':
return hashlib.sha256(raw).digest()
elif sign_type == 'ACS3-HMAC-SM3':
return hash_sm3(raw)
@staticmethod
def hex_encode(raw):
if raw:
return binascii.b2a_hex(raw).decode('utf-8')
@staticmethod
def get_authorization(request, sign_type, payload, ak, secret):
canonical_uri = request.pathname if request.pathname else '/'
canonicalized_query = get_canonical_query_string(request.query)
canonicalized_headers, signed_headers = get_canonicalized_headers(request.headers)
canonical_request = f'{request.method}\n' \
f'{canonical_uri}\n' \
f'{canonicalized_query}\n' \
f'{canonicalized_headers}\n' \
f'{signed_headers}\n' \
f'{payload}'
str_to_sign = f'{sign_type}\n{Client.hex_encode(Client.hash(canonical_request.encode("utf-8"), sign_type))}'
signature = Client.hex_encode(signature_method(secret, str_to_sign, sign_type))
auth = f'{sign_type} Credential={ak},SignedHeaders={signed_headers},Signature={signature}'
return auth
@staticmethod
def get_encode_path(path):
return quote(path, safe='/~', encoding="utf-8")
@staticmethod
def get_encode_param(param):
return quote(param, safe='~', encoding="utf-8")