223 lines
7.4 KiB
Python
223 lines
7.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Mega Water - 离线授权发卡器 (开发者专用)
|
|
生成绑定特定机器码的 .lic 授权文件
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
# 确保 src.auth 在 path 中
|
|
_current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
_project_root = os.path.abspath(os.path.join(_current_dir, "..", ".."))
|
|
if _project_root not in sys.path:
|
|
sys.path.insert(0, _project_root)
|
|
|
|
from PyQt5.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
|
QPushButton, QFileDialog, QMessageBox, QApplication, QDateEdit, QCheckBox
|
|
)
|
|
from PyQt5.QtCore import Qt, QDate
|
|
|
|
from src.auth.license_manager import generate_license
|
|
|
|
# 永久授权的标识日期
|
|
PERMANENT_EXPIRY = "2099-12-31"
|
|
|
|
|
|
class LicenseKeygenWindow(QWidget):
|
|
"""授权发卡器主窗口"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("Mega Water - 离线授权发卡器 (开发者专用)")
|
|
self.setMinimumSize(640, 360)
|
|
self.move(400, 280)
|
|
|
|
self._default_save_path = os.path.join(_project_root, "license.lic")
|
|
self._setup_ui()
|
|
|
|
def _setup_ui(self):
|
|
# ── 全局字体:无衬线,清晰 ──
|
|
font_family = "Microsoft YaHei" if sys.platform == "win32" else "Segoe UI"
|
|
self.setStyleSheet(f"""
|
|
* {{
|
|
font-family: {font_family}, 'Segoe UI', sans-serif;
|
|
font-size: 11pt;
|
|
}}
|
|
QLabel#titleLabel {{
|
|
font-size: 16pt;
|
|
font-weight: bold;
|
|
color: #2c3e50;
|
|
}}
|
|
QLabel#tipLabel {{
|
|
font-size: 10pt;
|
|
color: #95a5a6;
|
|
}}
|
|
""")
|
|
|
|
main_layout = QVBoxLayout()
|
|
main_layout.setContentsMargins(45, 40, 45, 40)
|
|
main_layout.setSpacing(18)
|
|
|
|
# ── 标题 ──
|
|
title_label = QLabel("离线授权发卡器 (开发者专用)")
|
|
title_label.setObjectName("titleLabel")
|
|
title_label.setAlignment(Qt.AlignCenter)
|
|
main_layout.addWidget(title_label)
|
|
|
|
# ── 机器码输入行 ──
|
|
mc_layout = QHBoxLayout()
|
|
mc_layout.setSpacing(12)
|
|
mc_label = QLabel("机器码:")
|
|
mc_label.setFixedWidth(90)
|
|
self.mc_input = QLineEdit()
|
|
self.mc_input.setPlaceholderText("粘贴用户发来的 32 位机器码")
|
|
self.mc_input.setMinimumHeight(36)
|
|
self.mc_input.setMinimumWidth(400)
|
|
mc_layout.addWidget(mc_label, 0)
|
|
mc_layout.addWidget(self.mc_input, 1)
|
|
main_layout.addLayout(mc_layout)
|
|
|
|
# ── 到期时间选择行 ──
|
|
exp_layout = QHBoxLayout()
|
|
exp_layout.setSpacing(14)
|
|
exp_label = QLabel("到期时间:")
|
|
exp_label.setFixedWidth(90)
|
|
self.exp_edit = QDateEdit()
|
|
self.exp_edit.setCalendarPopup(True)
|
|
self.exp_edit.setMinimumHeight(36)
|
|
self.exp_edit.setMinimumWidth(160)
|
|
self.exp_edit.setDate(QDate.currentDate().addYears(1))
|
|
|
|
self.perm_check = QCheckBox("永久授权 (不限时)")
|
|
self.perm_check.setMinimumHeight(36)
|
|
self.perm_check.stateChanged.connect(self._on_perm_changed)
|
|
|
|
exp_layout.addWidget(exp_label, 0)
|
|
exp_layout.addWidget(self.exp_edit, 0)
|
|
exp_layout.addWidget(self.perm_check, 0)
|
|
exp_layout.addStretch(1)
|
|
main_layout.addLayout(exp_layout)
|
|
|
|
# ── 保存路径行 ──
|
|
path_layout = QHBoxLayout()
|
|
path_layout.setSpacing(12)
|
|
path_label = QLabel("保存路径:")
|
|
path_label.setFixedWidth(90)
|
|
self.path_input = QLineEdit()
|
|
self.path_input.setReadOnly(True)
|
|
self.path_input.setMinimumHeight(36)
|
|
self.browse_btn = QPushButton("浏览...")
|
|
self.browse_btn.setMinimumHeight(36)
|
|
self.browse_btn.setFixedWidth(80)
|
|
self.browse_btn.clicked.connect(self._on_browse)
|
|
path_layout.addWidget(path_label, 0)
|
|
path_layout.addWidget(self.path_input, 1)
|
|
path_layout.addWidget(self.browse_btn, 0)
|
|
main_layout.addLayout(path_layout)
|
|
|
|
# ── 弹性空间 ──
|
|
main_layout.addSpacing(10)
|
|
|
|
# ── 生成按钮 ──
|
|
self.gen_btn = QPushButton("生成授权文件 (.lic)")
|
|
self.gen_btn.setMinimumHeight(48)
|
|
self.gen_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #27ae60;
|
|
color: white;
|
|
font-size: 13pt;
|
|
font-weight: bold;
|
|
border: none;
|
|
border-radius: 8px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #2ecc71;
|
|
}
|
|
QPushButton:pressed {
|
|
background-color: #1e8449;
|
|
}
|
|
""")
|
|
self.gen_btn.clicked.connect(self._on_generate)
|
|
main_layout.addWidget(self.gen_btn)
|
|
|
|
# ── 底部提示 ──
|
|
tip_label = QLabel("生成后请将 license.lic 文件发给用户,放置到软件安装目录下即可。")
|
|
tip_label.setObjectName("tipLabel")
|
|
tip_label.setAlignment(Qt.AlignCenter)
|
|
main_layout.addWidget(tip_label)
|
|
|
|
self.setLayout(main_layout)
|
|
|
|
def _on_perm_changed(self, state):
|
|
"""永久授权复选框状态变化时,联动日期选择器"""
|
|
if state == Qt.Checked:
|
|
self.exp_edit.setEnabled(False)
|
|
else:
|
|
self.exp_edit.setEnabled(True)
|
|
|
|
def _on_browse(self):
|
|
"""打开文件对话框选择保存路径"""
|
|
path, _ = QFileDialog.getSaveFileName(
|
|
self,
|
|
"选择授权文件保存位置",
|
|
self._default_save_path,
|
|
"授权文件 (*.lic)"
|
|
)
|
|
if path:
|
|
if not path.lower().endswith(".lic"):
|
|
path += ".lic"
|
|
self.path_input.setText(path)
|
|
|
|
def _on_generate(self):
|
|
"""点击生成按钮,调用授权管理器"""
|
|
machine_code = self.mc_input.text().strip()
|
|
if not machine_code:
|
|
QMessageBox.warning(self, "输入错误", "请输入机器码")
|
|
return
|
|
|
|
output_path = self.path_input.text().strip()
|
|
if not output_path:
|
|
QMessageBox.warning(self, "输入错误", "请设置保存路径")
|
|
return
|
|
|
|
# 根据是否勾选永久授权决定日期
|
|
if self.perm_check.isChecked():
|
|
expiry_date = PERMANENT_EXPIRY
|
|
else:
|
|
expiry_date = self.exp_edit.date().toString("yyyy-MM-dd")
|
|
|
|
ok, msg = generate_license(
|
|
machine_code=machine_code,
|
|
output_path=output_path,
|
|
expiry_date=expiry_date
|
|
)
|
|
|
|
if ok:
|
|
QMessageBox.information(
|
|
self,
|
|
"生成成功",
|
|
f"✅ 授权文件已成功生成!\n\n保存路径:\n{output_path}\n\n请将此文件发给用户即可。",
|
|
QMessageBox.Ok
|
|
)
|
|
else:
|
|
QMessageBox.critical(
|
|
self,
|
|
"生成失败",
|
|
f"❌ {msg}",
|
|
QMessageBox.Ok
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# ── 高 DPI 自适应(必须放在 QApplication 实例化之前)──
|
|
from PyQt5.QtCore import Qt
|
|
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
|
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
|
|
|
|
app = QApplication(sys.argv)
|
|
app.setApplicationName("LicenseKeygen")
|
|
window = LicenseKeygenWindow()
|
|
window.show()
|
|
sys.exit(app.exec_()) |