This commit is contained in:
2025-10-29 22:20:21 +08:00
commit 32b3b7b29a
111 changed files with 344425 additions and 0 deletions

View File

@@ -0,0 +1 @@
from .exceptions import getCode

202
app/core/utils/config.py Normal file
View File

@@ -0,0 +1,202 @@
# coding:utf-8
import sys
from datetime import datetime
from PyQt6.QtGui import QPixmap
from qfluentwidgets import (
BoolValidator,
ConfigItem,
ConfigSerializer,
OptionsConfigItem,
OptionsValidator,
qconfig,
QConfig,
FolderValidator,
Theme,
setThemeColor,
)
from .encryption import encrypt
from .setting import CONFIG_FILE, DOWNLOAD_FOLDER
def isWin11():
return sys.platform == "win32" and sys.getwindowsversion().build >= 22000
class EncrpytionSerializer(ConfigSerializer):
"""QColor serializer"""
def serialize(self, value):
return encrypt.encrypt(value)
def deserialize(self, value):
return encrypt.decrypt(value)
class Config(QConfig):
"""Config of application"""
# TODO: ADD YOUR CONFIG GROUP HERE
# register
rememberMe = ConfigItem(
"UmVnaXN0ZXI=", "UmVtZW1iZXJNZQ==", True, serializer=EncrpytionSerializer()
)
email = ConfigItem(
"UmVnaXN0ZXI=", "RW1haWw=", "", serializer=EncrpytionSerializer()
)
activationCode = ConfigItem(
"UmVnaXN0ZXI=", "QWN0aXZhdGlvbkNvZGU=", "", serializer=EncrpytionSerializer()
)
# main window
micaEnabled = ConfigItem("MainWindow", "MicaEnabled", isWin11(), BoolValidator())
dpiScale = OptionsConfigItem(
"MainWindow",
"DpiScale",
"Auto",
OptionsValidator([1, 1.25, 1.5, 1.75, 2, "Auto"]),
restart=True,
)
# software update
checkUpdateAtStartUp = ConfigItem(
"Update", "CheckUpdateAtStartUp", True, BoolValidator()
)
# bg
customBackground = ConfigItem(
"Background",
"CustomBackground",
"app\\resource\\images\\bg0.png",
)
customOpactity = ConfigItem("Background", "Opactity", 0.2)
downloadSavePath = ConfigItem(
"Download", "SavePath", DOWNLOAD_FOLDER, validator=FolderValidator()
)
# language
language = OptionsConfigItem(
"General", "Language", "zh", OptionsValidator(["zh", "en"]), restart=False
)
class UserConfig:
def __init__(self, userData):
self.userData = userData
self.token = None
self.avaterPixmap = None
@property
def userId(self):
if self.userData:
return self.userData["data"].get("id", "")
else:
return None
@property
def userAvatarURL(self):
if self.userData and "avatar" in self.userData["data"]:
return self.userData["data"].get("avatar", "")
else:
return ""
@property
def userName(self):
if self.userData:
return self.userData["data"].get("nickname", "")
else:
return ""
@property
def userEmail(self):
if self.userData:
return self.userData["data"].get("user_name", "")
else:
return ""
@property
def userGroup(self):
if self.userData:
return self.userData.get("data", {}).get("group", {}).get("name", "")
else:
return ""
@property
def userScore(self):
if self.userData:
return str(self.userData["data"].get("score", 0))
@property
def userCreatedTime(self):
if self.userData:
return self.format_date(self.userData["data"].get("created_at", ""))
def setUserAvatarPixmap(self, avaterPixmap):
self.avaterPixmap: QPixmap = avaterPixmap
def returnAvatarPixmap(self):
return self.avaterPixmap
def setToken(self, token):
"""设置JWT token"""
self.token = token
def getToken(self):
"""获取JWT token"""
return self.token
def format_date(self, date_str):
"""格式化日期时间"""
try:
# 处理带小数秒的情况
if "." in date_str:
# 分割日期和小数秒部分
date_part, fractional_part = date_str.split(".", 1)
# 去除末尾的"Z"并截取前6位小数
fractional_sec = fractional_part.rstrip("Z")[:6]
# 重新组合日期字符串
normalized_date_str = f"{date_part}.{fractional_sec}Z"
date_time = datetime.strptime(
normalized_date_str, "%Y-%m-%dT%H:%M:%S.%fZ"
)
else:
# 处理没有小数秒的情况
date_time = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")
except ValueError:
# 如果所有格式都失败,返回原始字符串
return date_str
return date_time.strftime("%Y-%m-%d %H:%M:%S")
class PolicyConfig:
def __init__(self):
self.currentPolicy = {}
self.currentPath = "/"
def returnPolicy(self):
return self.currentPolicy
def setPolicy(self, policy):
self.currentPolicy = policy
def setCurrentPath(self, path):
self.currentPath = path
def returnCurrentPath(self):
return self.currentPath
cfg = Config()
cfg.themeMode.value = Theme.AUTO
# 设置默认主题色为蓝色 (使用RGB值)
setThemeColor('#2F80ED') # 这是一个标准的蓝色RGB值
qconfig.load(str(CONFIG_FILE.absolute()), cfg)
userConfig = UserConfig(None)
policyConfig = PolicyConfig()

View File

@@ -0,0 +1,77 @@
import json
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from Crypto.Random import get_random_bytes
# from .setting import ENCRYPTKEY
ENCRYPTKEY = b"lunminalinguaai_"
class AESCipher:
def __init__(self, key):
"""
初始化AES加密器
:param key: 加密密钥16/24/32字节可以是字符串或字节
"""
if isinstance(key, str):
key = key.encode("utf-8")
if len(key) not in [16, 24, 32]:
raise ValueError("密钥长度必须为16、24或32字节")
self.key = key
def encrypt(self, data):
"""
加密数据(支持字符串/列表/字典等JSON可序列化类型
:param data: 要加密的数据
:return: 返回Base64编码的加密字符串
"""
# 生成随机初始化向量
iv = get_random_bytes(AES.block_size)
# 创建AES加密器
cipher = AES.new(self.key, AES.MODE_CBC, iv)
# 序列化数据为JSON字符串并编码为字节
json_data = json.dumps(data)
plain_bytes = json_data.encode("utf-8")
# 填充并加密数据
padded_bytes = pad(plain_bytes, AES.block_size)
cipher_bytes = cipher.encrypt(padded_bytes)
# 组合IV和密文并进行Base64编码
encrypted_data = iv + cipher_bytes
return base64.b64encode(encrypted_data).decode("utf-8")
def decrypt(self, enc_data):
"""
解密数据并恢复原始格式
:param enc_data: Base64编码的加密字符串
:return: 原始数据(保持原始格式)
"""
# Base64解码
encrypted_data = base64.b64decode(enc_data)
# 提取初始化向量
iv = encrypted_data[: AES.block_size]
cipher_bytes = encrypted_data[AES.block_size :]
# 创建AES解密器
cipher = AES.new(self.key, AES.MODE_CBC, iv)
# 解密并去除填充
decrypted_bytes = cipher.decrypt(cipher_bytes)
unpadded_bytes = unpad(decrypted_bytes, AES.block_size)
# 解码JSON并恢复原始数据结构
json_data = unpadded_bytes.decode("utf-8")
return json.loads(json_data)
encrypt = AESCipher(ENCRYPTKEY)
if __name__ == '__main__':
data = "sk-3e47a49bf60e49e8ab08bb1f1550aa86"
enc_data = encrypt.encrypt(data)
print(enc_data)

View File

@@ -0,0 +1,14 @@
# API请求code
stateCodeList = {
"0": "成功",
"40026": "验证码错误",
"40001": "读取用户头像数据失败",
"40020": "邮箱或密码不正确",
"40018": "该账号未激活",
"40033": "用户未激活,已重新发送激活邮件",
}
def getCode(code):
return stateCodeList.get(str(code), "未知错误,请联系技术支持")

67
app/core/utils/format.py Normal file
View File

@@ -0,0 +1,67 @@
from datetime import datetime
def formatSize(size):
"""格式化文件大小"""
for unit in ["B", "KB", "MB", "GB", "TB"]:
if size < 1024:
return f"{size:.2f} {unit}"
size /= 1024
return f"{size:.2f} PB"
def formatDate(date_str):
"""格式化日期时间"""
try:
# 处理带小数秒的情况
if "." in date_str:
# 分割日期和小数秒部分
date_part, fractional_part = date_str.split(".", 1)
# 去除末尾的'Z'并截取前6位小数
fractional_sec = fractional_part.rstrip("Z")[:6]
# 重新组合日期字符串
normalized_date_str = f"{date_part}.{fractional_sec}Z"
date_time = datetime.strptime(normalized_date_str, "%Y-%m-%dT%H:%M:%S.%fZ")
else:
# 处理没有小数秒的情况
date_time = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ")
except ValueError:
# 如果所有格式都失败,返回原始字符串
return date_str
return date_time.strftime("%Y-%m-%d %H:%M:%S")
def getFileIcon(fileType, fileName):
if fileType == "file":
suffix = fileName.split(".")[-1].lower()
icon_map = {
"txt": "Txt.svg",
"png": "Image.svg",
"jpg": "Image.svg",
"svg": "Image.svg",
"jpeg": "Image.svg",
"bmp": "Image.svg",
"gif": "Gif.svg",
"xls": "Excel.svg",
"xlsx": "Excel.svg",
"doc": "Word.svg",
"docx": "Word.svg",
"pdf": "Pdf.svg",
"ppt": "PPT.svg",
"mp4": "Video.svg",
"mkv": "Video.svg",
"mp3": "music.svg",
"wav": "music.svg",
"zip": "Zip.svg",
"rar": "Zip.svg",
"csv": "Excel.svg",
"db": "Database.svg",
"py": "Programme.svg",
"c": "Programme.svg",
"cpp": "Programme.svg",
"go": "Programme.svg",
}
return icon_map.get(suffix, "None.svg") # 默认图标
else:
return "Folder.svg"

View File

@@ -0,0 +1,90 @@
# coding: utf-8
import json
import os
from loguru import logger
from pathlib import Path
# 导入配置
from .config import cfg, qconfig
# 当前语言设置,默认为中文
_current_language = "zh"
# 翻译词典
_translations = {}
# 语言文件目录
_LANG_DIR = Path("app/resource/lang").absolute()
# print(Path("app/resource/lang").absolute())
def load_language(lang_code="zh"):
"""
加载指定语言的翻译文件
Args:
lang_code: 语言代码,如 "zh""en"
"""
global _current_language, _translations
try:
# 构建语言文件路径
lang_file = os.path.join(_LANG_DIR, f"{lang_code}.json")
# 检查文件是否存在
if not os.path.exists(lang_file):
logger.warning(f"语言文件不存在: {lang_file},使用默认语言")
lang_code = "zh"
lang_file = os.path.join(_LANG_DIR, "zh.json")
# 读取语言文件
with open(lang_file, "r", encoding="utf-8") as f:
_translations = json.load(f)
_current_language = lang_code
logger.info(f"已加载语言: {lang_code}")
except Exception as e:
logger.error(f"加载语言文件失败: {e}")
# 如果加载失败,使用空字典
_translations = {}
def lang(text):
"""
翻译文本
Args:
text: 要翻译的原文
Returns:
翻译后的文本,如果没有找到翻译,则返回原文
"""
# 如果翻译词典为空,尝试加载默认语言
if not _translations:
load_language(_current_language)
# 返回翻译后的文本,找不到则返回原文
return _translations.get(text, text)
def get_current_language():
"""
获取当前语言代码
Returns:
当前语言代码
"""
return _current_language
# 初始化时从配置加载语言
try:
if hasattr(cfg, "language"):
initial_lang = qconfig.get(cfg.language)
load_language(initial_lang)
else:
# 如果配置中没有语言设置,使用默认值
load_language()
except Exception as e:
logger.error(f"加载语言配置时出错: {e}")
# 出错时使用默认语言
load_language("zh")

16
app/core/utils/setting.py Normal file
View File

@@ -0,0 +1,16 @@
# coding: utf-8
from pathlib import Path
# change DEBUG to False if you want to compile the code to exe
DEBUG = "__compiled__" not in globals()
YEAR = 2025
AUTHOR = "Miao"
VERSION = "v0.0.1"
APP_NAME = "miaostarspan"
CONFIG_FOLDER = Path("config").absolute()
CONFIG_FILE = CONFIG_FOLDER / "config.json"
DOWNLOAD_FOLDER = Path("download").absolute()
# 23

View File

@@ -0,0 +1,34 @@
# coding: utf-8
from PyQt6.QtCore import QObject, pyqtSignal
from PyQt6.QtGui import QPixmap
class SignalBus(QObject):
"""Signal bus"""
checkUpdateSig = pyqtSignal()
micaEnableChanged = pyqtSignal(bool)
dirOpenSignal = pyqtSignal(str)
shareDirOpenSignal = pyqtSignal(str)
avatarUpdated = pyqtSignal(QPixmap) # 头像更新信号
imagePreviewSignal = pyqtSignal(str)
txtPreviewSignal = pyqtSignal(str)
opacityChanged = pyqtSignal() # 透明度变化信号
backgroundChanged = pyqtSignal() # 背景颜色变化信号
refreshFolderListSignal = pyqtSignal()
addUploadFileTask = pyqtSignal(str) # 添加upload任务信号
addDownloadFileTask = pyqtSignal(str, str, str) # 添加download任务信号
shareFolderViewSignal = pyqtSignal(str) # 分享文件夹
shareFileDownloadSignal = pyqtSignal() # 分享文件下载
languageChanged = pyqtSignal() # 语言变更信号
loginSuccessSignal = pyqtSignal() # 登录成功信号
signalBus = SignalBus()

View File

@@ -0,0 +1,6 @@
# coding: utf-8
"""
Version information for the application
"""
version = "0.0.2"