Files
2026-06-19 18:45:55 +08:00

314 lines
9.1 KiB
JavaScript

const express = require('express')
const cors = require('cors')
const sqlite3 = require('sqlite3').verbose()
const path = require('path')
const app = express()
app.use(cors())
app.use(express.json())
app.use(express.static(path.join(__dirname, 'public')))
// SQLite 数据库初始化
const db = new sqlite3.Database(path.join(__dirname, 'activation.db'), (err) => {
if (err) console.error('❌ 数据库连接失败:', err)
else console.log('✅ SQLite 数据库已连接')
})
// 创建表
db.serialize(() => {
db.run(`CREATE TABLE IF NOT EXISTS activation_codes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT UNIQUE NOT NULL,
type TEXT NOT NULL,
duration INTEGER NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME,
is_used BOOLEAN DEFAULT 0,
is_active BOOLEAN DEFAULT 1,
activated_at DATETIME,
device_fingerprint TEXT,
last_checkin DATETIME,
last_checkin_device TEXT
)`)
db.run(`CREATE TABLE IF NOT EXISTS activation_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL,
action TEXT NOT NULL,
message TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`)
// 尝试添加新的列(如果表已存在且列不存在)
db.run(`ALTER TABLE activation_codes ADD COLUMN last_checkin DATETIME`, (err) => {
if (err && !err.message.includes('duplicate column name')) {
console.warn('⚠️ 添加 last_checkin 列失败:', err.message)
}
})
db.run(`ALTER TABLE activation_codes ADD COLUMN last_checkin_device TEXT`, (err) => {
if (err && !err.message.includes('duplicate column name')) {
console.warn('⚠️ 添加 last_checkin_device 列失败:', err.message)
}
})
})
// 生成激活码
function generateCode() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let code = ''
for (let i = 0; i < 16; i++) {
if (i > 0 && i % 4 === 0) code += '-'
code += chars.charAt(Math.floor(Math.random() * chars.length))
}
return code
}
// 计算过期时间
function calculateExpiresAt(type, duration) {
const now = new Date()
let expiresAt = new Date(now)
if (type === 'minute') {
expiresAt.setMinutes(expiresAt.getMinutes() + duration)
} else if (type === 'hour') {
expiresAt.setHours(expiresAt.getHours() + duration)
} else if (type === 'day') {
expiresAt.setDate(expiresAt.getDate() + duration)
} else if (type === 'month') {
expiresAt.setMonth(expiresAt.getMonth() + duration)
} else if (type === 'year') {
expiresAt.setFullYear(expiresAt.getFullYear() + duration)
}
return expiresAt.toISOString()
}
// API: 生成激活码
app.post('/api/codes/generate', (req, res) => {
const { type = 'day', duration = 7 } = req.body
const code = generateCode()
const expiresAt = calculateExpiresAt(type, duration)
db.run(
'INSERT INTO activation_codes (code, type, duration, expires_at) VALUES (?, ?, ?, ?)',
[code, type, duration, expiresAt],
(err) => {
if (err) {
return res.json({ success: false, error: err.message })
}
res.json({
success: true,
code: code,
type: type,
duration: duration,
expiresAt: expiresAt
})
}
)
})
// API: 激活激活码
app.post('/api/activate', (req, res) => {
const { code, device_fingerprint } = req.body
db.get('SELECT * FROM activation_codes WHERE code = ?', [code], (err, row) => {
if (err || !row) {
db.run('INSERT INTO activation_logs (code, action, message) VALUES (?, ?, ?)',
[code, 'activate_failed', '激活码不存在'])
return res.json({ success: false, message: '激活码不存在' })
}
if (!row.is_active) {
return res.json({ success: false, message: '激活码已停用' })
}
const now = new Date()
const expiresAt = new Date(row.expires_at)
if (now > expiresAt) {
return res.json({ success: false, message: '激活码已过期' })
}
if (row.is_used) {
return res.json({ success: false, message: '激活码已被使用' })
}
// 标记为已使用
db.run(
'UPDATE activation_codes SET is_used = 1, activated_at = ?, device_fingerprint = ? WHERE code = ?',
[new Date().toISOString(), device_fingerprint, code],
(err) => {
if (err) {
return res.json({ success: false, error: err.message })
}
db.run('INSERT INTO activation_logs (code, action, message) VALUES (?, ?, ?)',
[code, 'activate_success', '激活成功'])
res.json({
success: true,
code: code,
type: row.type,
duration: row.duration,
activatedAt: new Date().toISOString(),
expiresAt: row.expires_at
})
}
)
})
})
// API: 验证激活码
app.post('/api/validate', (req, res) => {
const { code, device_fingerprint } = req.body
db.get('SELECT * FROM activation_codes WHERE code = ?', [code], (err, row) => {
if (err || !row) {
return res.json({ valid: false, message: '激活码不存在' })
}
if (!row.is_active) {
return res.json({ valid: false, message: '激活码已停用' })
}
const now = new Date()
const expiresAt = new Date(row.expires_at)
if (now > expiresAt) {
return res.json({
valid: false,
message: '激活码已过期',
isExpired: true
})
}
if (!row.is_used) {
return res.json({ valid: false, message: '激活码未被激活' })
}
const remainingMs = expiresAt - now
const remainingDays = Math.ceil(remainingMs / (1000 * 60 * 60 * 24))
const remainingHours = Math.ceil(remainingMs / (1000 * 60 * 60))
const remainingMinutes = Math.ceil(remainingMs / (1000 * 60))
res.json({
valid: true,
code: code,
type: row.type,
duration: row.duration,
activatedAt: row.activated_at,
expiresAt: row.expires_at,
remainingMs: remainingMs,
remainingDays: remainingDays,
remainingHours: remainingHours,
remainingMinutes: remainingMinutes,
isExpired: false
})
})
})
// API: 获取所有激活码
app.get('/api/codes/list', (req, res) => {
db.all('SELECT * FROM activation_codes ORDER BY created_at DESC', [], (err, rows) => {
if (err) {
return res.json({ success: false, error: err.message })
}
res.json({ success: true, codes: rows })
})
})
// API: 停用激活码
app.post('/api/codes/disable', (req, res) => {
const { code } = req.body
db.run('UPDATE activation_codes SET is_active = 0 WHERE code = ?', [code], (err) => {
if (err) {
return res.json({ success: false, error: err.message })
}
db.run('INSERT INTO activation_logs (code, action, message) VALUES (?, ?, ?)',
[code, 'disable', '激活码已停用'])
res.json({ success: true, message: '激活码已停用' })
})
})
// API: 启用激活码
app.post('/api/codes/enable', (req, res) => {
const { code } = req.body
db.run('UPDATE activation_codes SET is_active = 1 WHERE code = ?', [code], (err) => {
if (err) {
return res.json({ success: false, error: err.message })
}
db.run('INSERT INTO activation_logs (code, action, message) VALUES (?, ?, ?)',
[code, 'enable', '激活码已启用'])
res.json({ success: true, message: '激活码已启用' })
})
})
// API: 删除激活码
app.post('/api/codes/delete', (req, res) => {
const { code } = req.body
db.run('DELETE FROM activation_codes WHERE code = ?', [code], (err) => {
if (err) {
return res.json({ success: false, error: err.message })
}
res.json({ success: true, message: '激活码已删除' })
})
})
// API: 设备签到(记录最后启动时间)
app.post('/api/device/checkin', (req, res) => {
const { code, device_fingerprint } = req.body
db.run(
'UPDATE activation_codes SET last_checkin = ?, last_checkin_device = ? WHERE code = ?',
[new Date().toISOString(), device_fingerprint || 'unknown', code],
(err) => {
if (err) {
console.error('❌ 设备签到失败:', err)
return res.json({ success: false, error: err.message })
}
db.run('INSERT INTO activation_logs (code, action, message) VALUES (?, ?, ?)',
[code, 'device_checkin', `设备 ${device_fingerprint} 启动`])
res.json({ success: true, message: '设备签到成功' })
}
)
})
// API: 获取统计信息
app.get('/api/codes/stats', (req, res) => {
db.all(`
SELECT
COUNT(*) as total,
SUM(CASE WHEN is_used = 0 THEN 1 ELSE 0 END) as unused,
SUM(CASE WHEN is_used = 1 THEN 1 ELSE 0 END) as used,
SUM(CASE WHEN is_active = 0 THEN 1 ELSE 0 END) as disabled
FROM activation_codes
`, [], (err, rows) => {
if (err) {
return res.json({ success: false, error: err.message })
}
res.json({ success: true, stats: rows[0] })
})
})
// Web 管理后台路由
app.get('/admin', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'admin.html'))
})
const PORT = 3002
app.listen(PORT, () => {
console.log(`\n✅ 激活码服务器已启动`)
console.log(`📋 管理后台: http://localhost:${PORT}/admin`)
console.log(`📡 API 地址: http://localhost:${PORT}/api\n`)
})