- 新增图像生成接口,支持试用、积分和自定义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): 添加示例系统日志文件 - 记录用户请求、验证码发送成功与失败的日志信息
307 lines
11 KiB
Python
307 lines
11 KiB
Python
#
|
|
# DEPRECATED: implementation for ffi.verify()
|
|
#
|
|
import sys, os, binascii, shutil, io
|
|
from . import __version_verifier_modules__
|
|
from . import ffiplatform
|
|
from .error import VerificationError
|
|
|
|
if sys.version_info >= (3, 3):
|
|
import importlib.machinery
|
|
def _extension_suffixes():
|
|
return importlib.machinery.EXTENSION_SUFFIXES[:]
|
|
else:
|
|
import imp
|
|
def _extension_suffixes():
|
|
return [suffix for suffix, _, type in imp.get_suffixes()
|
|
if type == imp.C_EXTENSION]
|
|
|
|
|
|
if sys.version_info >= (3,):
|
|
NativeIO = io.StringIO
|
|
else:
|
|
class NativeIO(io.BytesIO):
|
|
def write(self, s):
|
|
if isinstance(s, unicode):
|
|
s = s.encode('ascii')
|
|
super(NativeIO, self).write(s)
|
|
|
|
|
|
class Verifier(object):
|
|
|
|
def __init__(self, ffi, preamble, tmpdir=None, modulename=None,
|
|
ext_package=None, tag='', force_generic_engine=False,
|
|
source_extension='.c', flags=None, relative_to=None, **kwds):
|
|
if ffi._parser._uses_new_feature:
|
|
raise VerificationError(
|
|
"feature not supported with ffi.verify(), but only "
|
|
"with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))
|
|
self.ffi = ffi
|
|
self.preamble = preamble
|
|
if not modulename:
|
|
flattened_kwds = ffiplatform.flatten(kwds)
|
|
vengine_class = _locate_engine_class(ffi, force_generic_engine)
|
|
self._vengine = vengine_class(self)
|
|
self._vengine.patch_extension_kwds(kwds)
|
|
self.flags = flags
|
|
self.kwds = self.make_relative_to(kwds, relative_to)
|
|
#
|
|
if modulename:
|
|
if tag:
|
|
raise TypeError("can't specify both 'modulename' and 'tag'")
|
|
else:
|
|
key = '\x00'.join(['%d.%d' % sys.version_info[:2],
|
|
__version_verifier_modules__,
|
|
preamble, flattened_kwds] +
|
|
ffi._cdefsources)
|
|
if sys.version_info >= (3,):
|
|
key = key.encode('utf-8')
|
|
k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
|
|
k1 = k1.lstrip('0x').rstrip('L')
|
|
k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
|
|
k2 = k2.lstrip('0').rstrip('L')
|
|
modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,
|
|
k1, k2)
|
|
suffix = _get_so_suffixes()[0]
|
|
self.tmpdir = tmpdir or _caller_dir_pycache()
|
|
self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)
|
|
self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)
|
|
self.ext_package = ext_package
|
|
self._has_source = False
|
|
self._has_module = False
|
|
|
|
def write_source(self, file=None):
|
|
"""Write the C source code. It is produced in 'self.sourcefilename',
|
|
which can be tweaked beforehand."""
|
|
with self.ffi._lock:
|
|
if self._has_source and file is None:
|
|
raise VerificationError(
|
|
"source code already written")
|
|
self._write_source(file)
|
|
|
|
def compile_module(self):
|
|
"""Write the C source code (if not done already) and compile it.
|
|
This produces a dynamic link library in 'self.modulefilename'."""
|
|
with self.ffi._lock:
|
|
if self._has_module:
|
|
raise VerificationError("module already compiled")
|
|
if not self._has_source:
|
|
self._write_source()
|
|
self._compile_module()
|
|
|
|
def load_library(self):
|
|
"""Get a C module from this Verifier instance.
|
|
Returns an instance of a FFILibrary class that behaves like the
|
|
objects returned by ffi.dlopen(), but that delegates all
|
|
operations to the C module. If necessary, the C code is written
|
|
and compiled first.
|
|
"""
|
|
with self.ffi._lock:
|
|
if not self._has_module:
|
|
self._locate_module()
|
|
if not self._has_module:
|
|
if not self._has_source:
|
|
self._write_source()
|
|
self._compile_module()
|
|
return self._load_library()
|
|
|
|
def get_module_name(self):
|
|
basename = os.path.basename(self.modulefilename)
|
|
# kill both the .so extension and the other .'s, as introduced
|
|
# by Python 3: 'basename.cpython-33m.so'
|
|
basename = basename.split('.', 1)[0]
|
|
# and the _d added in Python 2 debug builds --- but try to be
|
|
# conservative and not kill a legitimate _d
|
|
if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):
|
|
basename = basename[:-2]
|
|
return basename
|
|
|
|
def get_extension(self):
|
|
if not self._has_source:
|
|
with self.ffi._lock:
|
|
if not self._has_source:
|
|
self._write_source()
|
|
sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)
|
|
modname = self.get_module_name()
|
|
return ffiplatform.get_extension(sourcename, modname, **self.kwds)
|
|
|
|
def generates_python_module(self):
|
|
return self._vengine._gen_python_module
|
|
|
|
def make_relative_to(self, kwds, relative_to):
|
|
if relative_to and os.path.dirname(relative_to):
|
|
dirname = os.path.dirname(relative_to)
|
|
kwds = kwds.copy()
|
|
for key in ffiplatform.LIST_OF_FILE_NAMES:
|
|
if key in kwds:
|
|
lst = kwds[key]
|
|
if not isinstance(lst, (list, tuple)):
|
|
raise TypeError("keyword '%s' should be a list or tuple"
|
|
% (key,))
|
|
lst = [os.path.join(dirname, fn) for fn in lst]
|
|
kwds[key] = lst
|
|
return kwds
|
|
|
|
# ----------
|
|
|
|
def _locate_module(self):
|
|
if not os.path.isfile(self.modulefilename):
|
|
if self.ext_package:
|
|
try:
|
|
pkg = __import__(self.ext_package, None, None, ['__doc__'])
|
|
except ImportError:
|
|
return # cannot import the package itself, give up
|
|
# (e.g. it might be called differently before installation)
|
|
path = pkg.__path__
|
|
else:
|
|
path = None
|
|
filename = self._vengine.find_module(self.get_module_name(), path,
|
|
_get_so_suffixes())
|
|
if filename is None:
|
|
return
|
|
self.modulefilename = filename
|
|
self._vengine.collect_types()
|
|
self._has_module = True
|
|
|
|
def _write_source_to(self, file):
|
|
self._vengine._f = file
|
|
try:
|
|
self._vengine.write_source_to_f()
|
|
finally:
|
|
del self._vengine._f
|
|
|
|
def _write_source(self, file=None):
|
|
if file is not None:
|
|
self._write_source_to(file)
|
|
else:
|
|
# Write our source file to an in memory file.
|
|
f = NativeIO()
|
|
self._write_source_to(f)
|
|
source_data = f.getvalue()
|
|
|
|
# Determine if this matches the current file
|
|
if os.path.exists(self.sourcefilename):
|
|
with open(self.sourcefilename, "r") as fp:
|
|
needs_written = not (fp.read() == source_data)
|
|
else:
|
|
needs_written = True
|
|
|
|
# Actually write the file out if it doesn't match
|
|
if needs_written:
|
|
_ensure_dir(self.sourcefilename)
|
|
with open(self.sourcefilename, "w") as fp:
|
|
fp.write(source_data)
|
|
|
|
# Set this flag
|
|
self._has_source = True
|
|
|
|
def _compile_module(self):
|
|
# compile this C source
|
|
tmpdir = os.path.dirname(self.sourcefilename)
|
|
outputfilename = ffiplatform.compile(tmpdir, self.get_extension())
|
|
try:
|
|
same = ffiplatform.samefile(outputfilename, self.modulefilename)
|
|
except OSError:
|
|
same = False
|
|
if not same:
|
|
_ensure_dir(self.modulefilename)
|
|
shutil.move(outputfilename, self.modulefilename)
|
|
self._has_module = True
|
|
|
|
def _load_library(self):
|
|
assert self._has_module
|
|
if self.flags is not None:
|
|
return self._vengine.load_library(self.flags)
|
|
else:
|
|
return self._vengine.load_library()
|
|
|
|
# ____________________________________________________________
|
|
|
|
_FORCE_GENERIC_ENGINE = False # for tests
|
|
|
|
def _locate_engine_class(ffi, force_generic_engine):
|
|
if _FORCE_GENERIC_ENGINE:
|
|
force_generic_engine = True
|
|
if not force_generic_engine:
|
|
if '__pypy__' in sys.builtin_module_names:
|
|
force_generic_engine = True
|
|
else:
|
|
try:
|
|
import _cffi_backend
|
|
except ImportError:
|
|
_cffi_backend = '?'
|
|
if ffi._backend is not _cffi_backend:
|
|
force_generic_engine = True
|
|
if force_generic_engine:
|
|
from . import vengine_gen
|
|
return vengine_gen.VGenericEngine
|
|
else:
|
|
from . import vengine_cpy
|
|
return vengine_cpy.VCPythonEngine
|
|
|
|
# ____________________________________________________________
|
|
|
|
_TMPDIR = None
|
|
|
|
def _caller_dir_pycache():
|
|
if _TMPDIR:
|
|
return _TMPDIR
|
|
result = os.environ.get('CFFI_TMPDIR')
|
|
if result:
|
|
return result
|
|
filename = sys._getframe(2).f_code.co_filename
|
|
return os.path.abspath(os.path.join(os.path.dirname(filename),
|
|
'__pycache__'))
|
|
|
|
def set_tmpdir(dirname):
|
|
"""Set the temporary directory to use instead of __pycache__."""
|
|
global _TMPDIR
|
|
_TMPDIR = dirname
|
|
|
|
def cleanup_tmpdir(tmpdir=None, keep_so=False):
|
|
"""Clean up the temporary directory by removing all files in it
|
|
called `_cffi_*.{c,so}` as well as the `build` subdirectory."""
|
|
tmpdir = tmpdir or _caller_dir_pycache()
|
|
try:
|
|
filelist = os.listdir(tmpdir)
|
|
except OSError:
|
|
return
|
|
if keep_so:
|
|
suffix = '.c' # only remove .c files
|
|
else:
|
|
suffix = _get_so_suffixes()[0].lower()
|
|
for fn in filelist:
|
|
if fn.lower().startswith('_cffi_') and (
|
|
fn.lower().endswith(suffix) or fn.lower().endswith('.c')):
|
|
try:
|
|
os.unlink(os.path.join(tmpdir, fn))
|
|
except OSError:
|
|
pass
|
|
clean_dir = [os.path.join(tmpdir, 'build')]
|
|
for dir in clean_dir:
|
|
try:
|
|
for fn in os.listdir(dir):
|
|
fn = os.path.join(dir, fn)
|
|
if os.path.isdir(fn):
|
|
clean_dir.append(fn)
|
|
else:
|
|
os.unlink(fn)
|
|
except OSError:
|
|
pass
|
|
|
|
def _get_so_suffixes():
|
|
suffixes = _extension_suffixes()
|
|
if not suffixes:
|
|
# bah, no C_EXTENSION available. Occurs on pypy without cpyext
|
|
if sys.platform == 'win32':
|
|
suffixes = [".pyd"]
|
|
else:
|
|
suffixes = [".so"]
|
|
|
|
return suffixes
|
|
|
|
def _ensure_dir(filename):
|
|
dirname = os.path.dirname(filename)
|
|
if dirname and not os.path.isdir(dirname):
|
|
os.makedirs(dirname)
|