chore: 清除多余的依赖
This commit is contained in:
@@ -1,73 +0,0 @@
|
|||||||
from Crypto.Cipher import AES
|
|
||||||
import json
|
|
||||||
import binascii
|
|
||||||
|
|
||||||
|
|
||||||
class ResponseDecryptor:
|
|
||||||
def __init__(self, encryption_key):
|
|
||||||
"""
|
|
||||||
初始化解密器
|
|
||||||
:param encryption_key: 十六进制格式的密钥字符串
|
|
||||||
"""
|
|
||||||
# 将十六进制字符串转换为字节
|
|
||||||
self.key = binascii.unhexlify(encryption_key)
|
|
||||||
|
|
||||||
def decrypt_response(self, encrypted_data):
|
|
||||||
"""
|
|
||||||
解密服务器响应的数据
|
|
||||||
:param encrypted_data: 加密的数据字符串 (格式: "iv:encrypted")
|
|
||||||
:return: 解密后的 JSON 数据
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 分离 IV 和加密数据
|
|
||||||
iv_hex, encrypted_text = encrypted_data.split(":")
|
|
||||||
|
|
||||||
# 将十六进制转换为字节
|
|
||||||
iv = binascii.unhexlify(iv_hex)
|
|
||||||
encrypted_bytes = binascii.unhexlify(encrypted_text)
|
|
||||||
|
|
||||||
# 创建解密器
|
|
||||||
cipher = AES.new(self.key, AES.MODE_CBC, iv)
|
|
||||||
|
|
||||||
# 解密数据
|
|
||||||
decrypted_bytes = cipher.decrypt(encrypted_bytes)
|
|
||||||
|
|
||||||
# 移除填充
|
|
||||||
padding_length = decrypted_bytes[-1]
|
|
||||||
decrypted_data = decrypted_bytes[:-padding_length]
|
|
||||||
|
|
||||||
# 转换为字符串并解析 JSON
|
|
||||||
decrypted_str = decrypted_data.decode("utf-8")
|
|
||||||
return json.loads(decrypted_str)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception(f"解密失败: {str(e)}")
|
|
||||||
|
|
||||||
|
|
||||||
# 使用示例
|
|
||||||
def main():
|
|
||||||
# 这里填入与服务器相同的加密密钥(十六进制格式)
|
|
||||||
ENCRYPTION_KEY = "f1e2d3c4b5a6978899aabbccddeeff00112233445566778899aabbccddeeff00" # 替换为实际的密钥
|
|
||||||
|
|
||||||
# 创建解密器实例
|
|
||||||
decryptor = ResponseDecryptor(ENCRYPTION_KEY)
|
|
||||||
|
|
||||||
# 模拟服务器响应
|
|
||||||
server_response = {
|
|
||||||
"encrypted_data": "iv_hex:encrypted_data_hex" # 这里是服务器返回的加密数据
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 解密数据
|
|
||||||
decrypted_data = decryptor.decrypt_response(server_response["encrypted_data"])
|
|
||||||
print("解密后的数据:", decrypted_data)
|
|
||||||
|
|
||||||
# 现在可以访问解密后的数据
|
|
||||||
if decrypted_data.get("success"):
|
|
||||||
print("操作成功!")
|
|
||||||
# 处理其他数据...
|
|
||||||
else:
|
|
||||||
print("操作失败:", decrypted_data.get("message"))
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print("错误:", str(e))
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from license_manager import LicenseManager
|
|
||||||
|
|
||||||
os.environ["PYTHONVERBOSE"] = "0"
|
os.environ["PYTHONVERBOSE"] = "0"
|
||||||
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||||
|
|
||||||
@@ -234,20 +232,6 @@ class EmailGenerator:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
browser_manager = None
|
browser_manager = None
|
||||||
try:
|
try:
|
||||||
license_manager = LicenseManager()
|
|
||||||
|
|
||||||
# 验证许可证
|
|
||||||
is_valid, message = license_manager.verify_license()
|
|
||||||
if not is_valid:
|
|
||||||
print(f"许可证验证失败: {message}")
|
|
||||||
# 提示用户激活
|
|
||||||
license_key = input("请输入激活码: ")
|
|
||||||
success, activate_message = license_manager.activate_license(license_key)
|
|
||||||
if not success:
|
|
||||||
print(f"激活失败: {activate_message}")
|
|
||||||
sys.exit(1)
|
|
||||||
print("激活成功!")
|
|
||||||
|
|
||||||
# 初始化浏览器
|
# 初始化浏览器
|
||||||
browser_manager = BrowserManager()
|
browser_manager = BrowserManager()
|
||||||
browser = browser_manager.init_browser()
|
browser = browser_manager.init_browser()
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
from DrissionPage import ChromiumOptions, Chromium
|
|
||||||
import random
|
|
||||||
import time
|
|
||||||
|
|
||||||
def handle_turnstile(tab):
|
|
||||||
"""处理 Turnstile 验证"""
|
|
||||||
print("准备处理验证")
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
if tab.ele('@id=email-input', timeout=2):
|
|
||||||
print("无需验证 - 邮箱输入框已加载")
|
|
||||||
return True
|
|
||||||
|
|
||||||
if tab.ele('@id=password', timeout=2):
|
|
||||||
print("无需验证 - 密码输入框已加载")
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
|
||||||
challenge_element = (tab.ele("@name=cf-turnstile-response", timeout=2)
|
|
||||||
.parent()
|
|
||||||
.shadow_root
|
|
||||||
.ele("tag:iframe")
|
|
||||||
.ele("tag:body")
|
|
||||||
.sr("tag:input"))
|
|
||||||
|
|
||||||
if challenge_element:
|
|
||||||
print("验证框加载完成")
|
|
||||||
time.sleep(random.uniform(1, 3))
|
|
||||||
challenge_element.click()
|
|
||||||
print("验证按钮已点击,等待验证完成...")
|
|
||||||
time.sleep(2)
|
|
||||||
return True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"验证处理出错: {str(e)}")
|
|
||||||
print('跳过验证')
|
|
||||||
return False
|
|
||||||
|
|
||||||
account = 'your_chatgpt_account'
|
|
||||||
password = 'your_chatgpt_password'
|
|
||||||
|
|
||||||
co = ChromiumOptions()
|
|
||||||
co.add_extension("turnstilePatch")
|
|
||||||
# co.headless()
|
|
||||||
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36')
|
|
||||||
co.set_pref('credentials_enable_service', False)
|
|
||||||
co.set_argument('--hide-crash-restore-bubble')
|
|
||||||
co.auto_port()
|
|
||||||
|
|
||||||
browser = Chromium(co)
|
|
||||||
tab = browser.latest_tab
|
|
||||||
tab.run_js("try { turnstile.reset() } catch(e) { }")
|
|
||||||
|
|
||||||
print("\n步骤1: 开始访问网站...")
|
|
||||||
|
|
||||||
tab.get('https://chatgpt.com')
|
|
||||||
print('等待页面加载...')
|
|
||||||
|
|
||||||
print("\n步骤2: 开始登录...")
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
if tab.ele('xpath:/html/body/div[1]/div[1]/main/div[1]/div[1]/div/div[1]/div/div[3]/div/button[1]'):
|
|
||||||
signin_btn = tab.ele('xpath:/html/body/div[1]/div[1]/main/div[1]/div[1]/div/div[1]/div/div[3]/div/button[1]')
|
|
||||||
print("找到黑色登录按钮:", signin_btn.text)
|
|
||||||
break
|
|
||||||
if tab.ele('@data-testid=login-button'):
|
|
||||||
signin_btn = tab.ele('@data-testid=login-button')
|
|
||||||
print("找到蓝色登录按钮:", signin_btn.text)
|
|
||||||
break
|
|
||||||
if tab.ele("@name=cf-turnstile-response"):
|
|
||||||
print('加载页面时出现CF验证, IP 质量太差, 请更换 IP 重新尝试!')
|
|
||||||
browser.quit()
|
|
||||||
exit()
|
|
||||||
time.sleep(3)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"处理登录按钮时出错: {str(e)}")
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
if signin_btn:
|
|
||||||
signin_btn.click()
|
|
||||||
print("点击登录按钮")
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
print(f"处理登录按钮时出错: {str(e)}")
|
|
||||||
time.sleep(3)
|
|
||||||
else:
|
|
||||||
print("尝试点击登录按钮失败,程序退出")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
handle_turnstile(tab)
|
|
||||||
|
|
||||||
print("\n步骤3: 输入邮箱...")
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
if tab.ele('@id=email-input'):
|
|
||||||
tab.actions.click('@id=email-input').type(account)
|
|
||||||
time.sleep(0.5)
|
|
||||||
tab.ele('@class=continue-btn').click()
|
|
||||||
print("输入邮箱并点击继续")
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
print(f"加载邮箱输入框时出错: {str(e)}")
|
|
||||||
time.sleep(3)
|
|
||||||
else:
|
|
||||||
print("尝试加载邮箱输入框失败,程序退出")
|
|
||||||
browser.quit()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
handle_turnstile(tab)
|
|
||||||
|
|
||||||
print("\n步骤4: 输入密码...")
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
if tab.ele('@id=password'):
|
|
||||||
print("密码输入框加载完成")
|
|
||||||
tab.actions.click('@id=password').input(password)
|
|
||||||
time.sleep(2)
|
|
||||||
tab.ele('@type=submit').click('js')
|
|
||||||
# tab.actions.click('@type=submit')
|
|
||||||
print("输入密码并JS点击登录")
|
|
||||||
break
|
|
||||||
except Exception as e:
|
|
||||||
print(f"输入密码时出错: {str(e)}")
|
|
||||||
time.sleep(3)
|
|
||||||
else:
|
|
||||||
print("尝试加载密码输入框失败,程序退出")
|
|
||||||
browser.quit()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
try:
|
|
||||||
if tab.ele('有什么可以帮忙的?'):
|
|
||||||
print('登录成功!')
|
|
||||||
break
|
|
||||||
if tab.ele('重新发送电子邮件'):
|
|
||||||
print('提示需要邮箱验证码,脚本终止,请手动获取')
|
|
||||||
exit()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"登录可能遇到问题: {str(e)}")
|
|
||||||
time.sleep(3)
|
|
||||||
else:
|
|
||||||
print("登录失败,程序退出")
|
|
||||||
browser.quit()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
time.sleep(random.uniform(1,2))
|
|
||||||
print('\n',"步骤5: 获取access_token...")
|
|
||||||
browser.new_tab('https://chatgpt.com/api/auth/session')
|
|
||||||
tab = browser.latest_tab
|
|
||||||
time.sleep(1)
|
|
||||||
response_json = tab.json
|
|
||||||
if response_json and 'accessToken' in response_json:
|
|
||||||
access_token = response_json['accessToken']
|
|
||||||
print('\n',"请复制保存你的access_token:",'\n')
|
|
||||||
print(access_token)
|
|
||||||
else:
|
|
||||||
print("错误:未找到access token")
|
|
||||||
|
|
||||||
# input("\n按Enter键关闭浏览器...")
|
|
||||||
browser.quit()
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
node_modules
|
|
||||||
npm-debug.log
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
README.md
|
|
||||||
.dockerignore
|
|
||||||
Dockerfile
|
|
||||||
docker-compose.yml
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
MONGODB_URI=mongodb://mongo_3y6JyM:mongo_iNySJ5@119.8.35.41:27017/license-manager?authSource=admin&retryWrites=true&w=majority
|
|
||||||
PORT=3000
|
|
||||||
ENCRYPTION_KEY=f1e2d3c4b5a6978899aabbccddeeff00112233445566778899aabbccddeeff00
|
|
||||||
MAX_USAGE_COUNT=10
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 使用 Node.js 18 作为基础镜像
|
|
||||||
FROM node:18-alpine
|
|
||||||
|
|
||||||
# 设置工作目录
|
|
||||||
WORKDIR /usr/src/app
|
|
||||||
|
|
||||||
# 复制 package.json 和 package-lock.json(如果存在)
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
# 复制源代码
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# 暴露端口
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
# 启动命令
|
|
||||||
CMD ["npm", "start"]
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
docker build --platform linux/amd64 -t ccz2/cursor-auth-server:latest .
|
|
||||||
docker push ccz2/cursor-auth-server:latest
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
|
||||||
# Node.js 应用服务
|
|
||||||
app:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
environment:
|
|
||||||
- MONGODB_URI=${MONGODB_URI}
|
|
||||||
- PORT=3000
|
|
||||||
- ENCRYPTION_KEY=f1e2d3c4b5a6978899aabbccddeeff00112233445566778899aabbccddeeff00
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
depends_on:
|
|
||||||
- mongo
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
# MongoDB 服务
|
|
||||||
mongo:
|
|
||||||
image: mongo:latest
|
|
||||||
ports:
|
|
||||||
- "27017:27017"
|
|
||||||
environment:
|
|
||||||
- MONGO_INITDB_ROOT_USERNAME=mongo_3y6JyM
|
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=mongo_iNySJ5
|
|
||||||
- MONGO_INITDB_DATABASE=license-manager
|
|
||||||
volumes:
|
|
||||||
- mongodb_data:/data/db
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
mongodb_data:
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
const mongoose = require('mongoose');
|
|
||||||
const { getNowChinaTimeString } = require('../utils/date');
|
|
||||||
const licenseSchema = new mongoose.Schema({
|
|
||||||
licenseKey: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
machineCode: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
activationDate: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
default: getNowChinaTimeString
|
|
||||||
},
|
|
||||||
expiryDate: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
maxUsageCount: {
|
|
||||||
type: Number,
|
|
||||||
required: true,
|
|
||||||
default: process.env.MAX_USAGE_COUNT || 10 // 默认允许使用100次
|
|
||||||
},
|
|
||||||
currentUsageCount: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model('License', licenseSchema);
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
const mongoose = require('mongoose');
|
|
||||||
const { getNowChinaTimeString } = require('../utils/date');
|
|
||||||
|
|
||||||
const licenseKeySchema = new mongoose.Schema({
|
|
||||||
licenseKey: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
isUsed: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
generatedAt: {
|
|
||||||
type: String,
|
|
||||||
default() {
|
|
||||||
return getNowChinaTimeString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model('LicenseKey', licenseKeySchema);
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
const mongoose = require('mongoose');
|
|
||||||
const { getNowChinaTimeString } = require('../utils/date');
|
|
||||||
|
|
||||||
const userGenerationSchema = new mongoose.Schema({
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
unique: true,
|
|
||||||
index: true
|
|
||||||
},
|
|
||||||
lastGenerationTime: {
|
|
||||||
type: String,
|
|
||||||
default() {
|
|
||||||
return getNowChinaTimeString();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
generationCount: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
isDisabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
|
|
||||||
}, {
|
|
||||||
timestamps: true // 添加 createdAt 和 updatedAt 字段
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建索引以优化查询性能
|
|
||||||
userGenerationSchema.index({ username: 1 });
|
|
||||||
|
|
||||||
const UserGeneration = mongoose.model('UserGeneration', userGenerationSchema);
|
|
||||||
|
|
||||||
module.exports = UserGeneration;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "license-server",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "License activation server for Cursor Pro",
|
|
||||||
"main": "server.js",
|
|
||||||
"scripts": {
|
|
||||||
"start": "NODE_ENV=production node server.js",
|
|
||||||
"dev": "NODE_ENV=development nodemon server.js"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"crypto": "^1.0.1",
|
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"moment": "^2.29.4",
|
|
||||||
"moment-timezone": "^0.5.46",
|
|
||||||
"mongoose": "^8.0.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"nodemon": "^3.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
305
server/server.js
305
server/server.js
@@ -1,305 +0,0 @@
|
|||||||
require('dotenv').config();
|
|
||||||
const express = require('express');
|
|
||||||
const mongoose = require('mongoose');
|
|
||||||
const cors = require('cors');
|
|
||||||
const License = require('./models/License');
|
|
||||||
const LicenseKey = require('./models/LicenseKey');
|
|
||||||
const { formatChinaTime, getNowChinaTime, getNowChinaTimeString, moment } = require('./utils/date');
|
|
||||||
const { encryptLicenseKey, decryptLicenseKey, generateLicenseKey, encryptResponse } = require('./utils/encryption');
|
|
||||||
const { validateStar } = require('./utils/validateStar');
|
|
||||||
const UserGeneration = require('./models/UserGeneration');
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
// Middleware
|
|
||||||
app.use(cors());
|
|
||||||
app.use(express.json());
|
|
||||||
|
|
||||||
// 添加访问日志中间件
|
|
||||||
app.use((req, res, next) => {
|
|
||||||
const start = Date.now();
|
|
||||||
res.on('finish', () => {
|
|
||||||
const duration = Date.now() - start;
|
|
||||||
console.log(`[${getNowChinaTimeString()}] ${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`);
|
|
||||||
});
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Connect to MongoDB
|
|
||||||
mongoose.connect(process.env.MONGODB_URI, {
|
|
||||||
useNewUrlParser: true,
|
|
||||||
useUnifiedTopology: true,
|
|
||||||
serverSelectionTimeoutMS: 5000, // 超时时间
|
|
||||||
socketTimeoutMS: 45000, // Socket 超时
|
|
||||||
family: 4, // 强制使用 IPv4
|
|
||||||
})
|
|
||||||
.then(() => console.log('Connected to MongoDB'))
|
|
||||||
.catch(err => {
|
|
||||||
console.error('MongoDB connection error:', err);
|
|
||||||
process.exit(1); // 如果连接失败,终止程序
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加连接错误处理
|
|
||||||
mongoose.connection.on('error', err => {
|
|
||||||
console.error('MongoDB connection error:', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
mongoose.connection.on('disconnected', () => {
|
|
||||||
console.log('MongoDB disconnected');
|
|
||||||
});
|
|
||||||
|
|
||||||
// 定义一个复杂的路径,可以放在环境变量中
|
|
||||||
const GENERATE_PATH = process.env.GENERATE_PATH || 'xx-zz-yy-dd';
|
|
||||||
|
|
||||||
// 在应用启动时输出生成路径(仅在控制台显示一次)
|
|
||||||
console.log('License generation path:', GENERATE_PATH);
|
|
||||||
|
|
||||||
// Generate license key endpoint
|
|
||||||
app.get(`/${GENERATE_PATH}`, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { username } = req.query;
|
|
||||||
if (username !== 'ccj') {
|
|
||||||
const starResult = await validateStar(username);
|
|
||||||
if (starResult.code === -1 || starResult.hasStarred === false) {
|
|
||||||
return res.status(200).json({
|
|
||||||
code: -1,
|
|
||||||
message: '不好意思,您还没有star我的项目,无法生成许可证'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const userGeneration = await UserGeneration.findOne({ username: username });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!userGeneration) {
|
|
||||||
await UserGeneration.create({
|
|
||||||
username: username,
|
|
||||||
generationCount: 1
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 如果这个账号已经禁用
|
|
||||||
if (userGeneration.isDisabled) {
|
|
||||||
return res.status(200).json({
|
|
||||||
code: -1,
|
|
||||||
message: '不好意思,您的账号已被禁用,无法生成许可证'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果这个月已经生成过许可证,则不能再次生成
|
|
||||||
if (getNowChinaTime().month() === moment(userGeneration.lastGenerationTime).month()) {
|
|
||||||
return res.status(200).json({
|
|
||||||
code: -1,
|
|
||||||
message: '不好意思,这个月您已经生成过许可证,无法再次生成'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
userGeneration.generationCount += 1;
|
|
||||||
userGeneration.lastGenerationTime = getNowChinaTimeString();
|
|
||||||
await userGeneration.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const licenseKey = generateLicenseKey();
|
|
||||||
|
|
||||||
await LicenseKey.create({
|
|
||||||
licenseKey: licenseKey,
|
|
||||||
isUsed: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// 加密响应数据
|
|
||||||
const responseData = {
|
|
||||||
code: 0,
|
|
||||||
data: licenseKey
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.json(responseData);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('生成许可证错误:', error);
|
|
||||||
return res.status(500).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '服务器错误'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Activation endpoint
|
|
||||||
app.post('/activate', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { license_key, machine_code, activation_date } = req.body;
|
|
||||||
|
|
||||||
// Validate input
|
|
||||||
if (!license_key || !machine_code) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证密钥和机器码是必需的'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate license key format
|
|
||||||
try {
|
|
||||||
decryptLicenseKey(license_key);
|
|
||||||
} catch (error) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '无效的许可证密钥'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查许可证是否存在于生成记录中
|
|
||||||
const licenseKeyRecord = await LicenseKey.findOne({ licenseKey: license_key });
|
|
||||||
if (!licenseKeyRecord) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '无效的许可证密钥'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查许可证是否已被使用
|
|
||||||
if (licenseKeyRecord.isUsed) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '此许可证密钥已被使用,不能重复激活'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查许可证激活状态
|
|
||||||
const existingLicense = await License.findOne({ licenseKey: license_key });
|
|
||||||
if (existingLicense) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '此许可证已被激活,不能重复使用'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新过期时间计算,使用中国时区
|
|
||||||
const expiryDate = formatChinaTime(getNowChinaTime().add(1, 'month'), 'YYYY-MM-DD');
|
|
||||||
|
|
||||||
await License.create([{
|
|
||||||
licenseKey: license_key,
|
|
||||||
machineCode: machine_code,
|
|
||||||
activationDate: activation_date ? activation_date : getNowChinaTimeString(),
|
|
||||||
expiryDate: expiryDate,
|
|
||||||
isActive: true,
|
|
||||||
maxUsageCount: process.env.MAX_USAGE_COUNT || 10,
|
|
||||||
currentUsageCount: 1
|
|
||||||
}]);
|
|
||||||
|
|
||||||
|
|
||||||
// 更新许可证密钥状态为已使用
|
|
||||||
licenseKeyRecord.isUsed = true;
|
|
||||||
await licenseKeyRecord.save();
|
|
||||||
|
|
||||||
const responseData = {
|
|
||||||
code: 0,
|
|
||||||
message: '激活成功',
|
|
||||||
data: {
|
|
||||||
expiry_date: expiryDate
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.json(encryptResponse(responseData));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('激活错误:', error);
|
|
||||||
return res.status(500).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '服务器错误'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verification endpoint
|
|
||||||
app.post('/verify', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const { license_key, machine_code } = req.body;
|
|
||||||
|
|
||||||
// Validate input
|
|
||||||
if (!license_key || !machine_code) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证密钥和机器码是必需的'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate license key format
|
|
||||||
try {
|
|
||||||
decryptLicenseKey(license_key);
|
|
||||||
} catch (error) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '无效的许可证密钥'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find license
|
|
||||||
const license = await License.findOne({ licenseKey: license_key });
|
|
||||||
|
|
||||||
if (!license) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证不存在'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check machine code
|
|
||||||
if (license.machineCode !== machine_code) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '硬件信息不匹配'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if license is active
|
|
||||||
if (!license.isActive) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证已被禁用'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用中国时区检查过期时间
|
|
||||||
if (getNowChinaTime().isAfter(license.expiryDate)) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证已过期'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查使用次数
|
|
||||||
if (license.currentUsageCount >= license.maxUsageCount) {
|
|
||||||
return res.status(200).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '许可证使用次数已达到上限'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新使用次数
|
|
||||||
license.currentUsageCount += 1;
|
|
||||||
await license.save();
|
|
||||||
|
|
||||||
const responseData = {
|
|
||||||
code: 0,
|
|
||||||
data: {
|
|
||||||
message: '许可证有效',
|
|
||||||
expiry_date: formatChinaTime(license.expiryDate, 'YYYY-MM-DD'),
|
|
||||||
usage_count: {
|
|
||||||
current: license.currentUsageCount,
|
|
||||||
max: license.maxUsageCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return res.json(encryptResponse(responseData));
|
|
||||||
} catch (error) {
|
|
||||||
console.error('验证错误:', error);
|
|
||||||
return res.status(500).json(encryptResponse({
|
|
||||||
code: -1,
|
|
||||||
message: '服务器错误'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`Server is running on port ${PORT}`);
|
|
||||||
});
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
const moment = require('moment');
|
|
||||||
require('moment-timezone');
|
|
||||||
|
|
||||||
// 设置默认时区为中国时区
|
|
||||||
moment.tz.setDefault('Asia/Shanghai');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取格式化的中国时区时间
|
|
||||||
* @param {Date|String|Number} date - 日期输入
|
|
||||||
* @param {String} format - 格式化模板,默认 'YYYY-MM-DD HH:mm:ss'
|
|
||||||
* @returns {String} 格式化后的时间字符串
|
|
||||||
*/
|
|
||||||
const formatChinaTime = (date, format = 'YYYY-MM-DD HH:mm:ss') => {
|
|
||||||
return moment(date).format(format);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前中国时区的时间
|
|
||||||
* @returns {moment} moment对象
|
|
||||||
*/
|
|
||||||
const getNowChinaTime = () => {
|
|
||||||
return moment();
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNowChinaTimeString = () => {
|
|
||||||
return formatChinaTime(getNowChinaTime());
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
formatChinaTime,
|
|
||||||
getNowChinaTime,
|
|
||||||
getNowChinaTimeString,
|
|
||||||
moment // 导出配置好时区的 moment 实例
|
|
||||||
};
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
const crypto = require('crypto');
|
|
||||||
|
|
||||||
function getIV() {
|
|
||||||
if (process.env.ENCRYPTION_IV) {
|
|
||||||
return Buffer.from(process.env.ENCRYPTION_IV, 'hex');
|
|
||||||
}
|
|
||||||
return crypto.randomBytes(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encryptLicenseKey(text) {
|
|
||||||
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
|
|
||||||
const iv = getIV();
|
|
||||||
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
||||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
||||||
encrypted += cipher.final('hex');
|
|
||||||
return iv.toString('hex') + ':' + encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decryptLicenseKey(encrypted) {
|
|
||||||
const [ivHex, encryptedText] = encrypted.split(':');
|
|
||||||
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
|
|
||||||
const iv = Buffer.from(ivHex, 'hex');
|
|
||||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
||||||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
|
|
||||||
decrypted += decipher.final('utf8');
|
|
||||||
return decrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateLicenseKey() {
|
|
||||||
const randomBytes = crypto.randomBytes(16);
|
|
||||||
const timestamp = Date.now().toString();
|
|
||||||
const combined = randomBytes.toString('hex') + timestamp;
|
|
||||||
const encrypted = encryptLicenseKey(combined);
|
|
||||||
return encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function encryptResponse(data) {
|
|
||||||
// 在开发模式下直接返回原始数据
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生产模式下加密数据
|
|
||||||
const jsonStr = JSON.stringify(data);
|
|
||||||
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
|
|
||||||
const iv = getIV();
|
|
||||||
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
||||||
let encrypted = cipher.update(jsonStr, 'utf8', 'hex');
|
|
||||||
encrypted += cipher.final('hex');
|
|
||||||
return {
|
|
||||||
encrypted_data: iv.toString('hex') + ':' + encrypted
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
encryptLicenseKey,
|
|
||||||
decryptLicenseKey,
|
|
||||||
generateLicenseKey,
|
|
||||||
encryptResponse
|
|
||||||
};
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
async function validateStar(username) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`https://api.github.com/users/${username}/starred`);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const data = await response.json();
|
|
||||||
const hasStarred = data.some(repo => repo.name === 'cursor-auto-free');
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 0,
|
|
||||||
hasStarred
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
error: `验证star失败: ${response.status}`
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
error: `请求出错: ${error.message}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
validateStar
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user