mirror of
https://github.com/ddCat-main/cursor-auto-register.git
synced 2025-12-24 13:38:01 +08:00
add:新增账号使用记录
This commit is contained in:
73
api.py
73
api.py
@@ -1,9 +1,9 @@
|
||||
from fastapi import FastAPI, HTTPException, status, UploadFile
|
||||
from fastapi import FastAPI, HTTPException, status, UploadFile, Request
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import select, func, delete, desc
|
||||
from pathlib import Path
|
||||
from database import get_session, AccountModel, init_db
|
||||
from database import get_session, AccountModel, AccountUsageRecordModel, init_db
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from datetime import datetime
|
||||
import uvicorn
|
||||
@@ -1103,7 +1103,7 @@ async def import_accounts(file: UploadFile):
|
||||
|
||||
# 添加"使用Token"功能
|
||||
@app.post("/account/use-token/{id}", tags=["Accounts"])
|
||||
async def use_account_token(id: int):
|
||||
async def use_account_token(id: int, request: Request):
|
||||
"""使用指定账号的Token更新Cursor认证"""
|
||||
try:
|
||||
async with get_session() as session:
|
||||
@@ -1130,6 +1130,25 @@ async def use_account_token(id: int):
|
||||
|
||||
resetter = CursorShadowPatcher()
|
||||
patch_success = resetter.reset_machine_ids()
|
||||
|
||||
# 记录使用记录
|
||||
if success:
|
||||
# 获取请求客户端IP
|
||||
client_ip = request.client.host
|
||||
# 获取用户代理
|
||||
user_agent = request.headers.get("User-Agent", "")
|
||||
|
||||
# 创建使用记录
|
||||
usage_record = AccountUsageRecordModel(
|
||||
id=int(time.time() * 1000), # 使用毫秒级时间戳作为ID
|
||||
account_id=id,
|
||||
email=account.email,
|
||||
ip=client_ip,
|
||||
user_agent=user_agent,
|
||||
created_at=datetime.now().isoformat()
|
||||
)
|
||||
session.add(usage_record)
|
||||
await session.commit()
|
||||
|
||||
if success and patch_success:
|
||||
return {
|
||||
@@ -1149,6 +1168,52 @@ async def use_account_token(id: int):
|
||||
error(traceback.format_exc())
|
||||
return {"success": False, "message": f"使用Token失败: {str(e)}"}
|
||||
|
||||
# 添加获取账号使用记录接口
|
||||
@app.get("/account/{id}/usage-records", tags=["Accounts"])
|
||||
async def get_account_usage_records(id: int):
|
||||
"""获取指定账号的使用记录"""
|
||||
try:
|
||||
async with get_session() as session:
|
||||
# 通过ID查询账号
|
||||
result = await session.execute(
|
||||
select(AccountModel).where(AccountModel.id == id)
|
||||
)
|
||||
account = result.scalar_one_or_none()
|
||||
|
||||
if not account:
|
||||
return {"success": False, "message": f"ID为 {id} 的账号不存在"}
|
||||
|
||||
# 查询使用记录
|
||||
result = await session.execute(
|
||||
select(AccountUsageRecordModel)
|
||||
.where(AccountUsageRecordModel.account_id == id)
|
||||
.order_by(desc(AccountUsageRecordModel.created_at))
|
||||
)
|
||||
|
||||
records = result.scalars().all()
|
||||
|
||||
# 转换记录为字典列表
|
||||
records_list = []
|
||||
for record in records:
|
||||
records_list.append({
|
||||
"id": record.id,
|
||||
"account_id": record.account_id,
|
||||
"email": record.email,
|
||||
"ip": record.ip,
|
||||
"user_agent": record.user_agent,
|
||||
"created_at": record.created_at
|
||||
})
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"records": records_list
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
error(f"获取账号使用记录失败: {str(e)}")
|
||||
error(traceback.format_exc())
|
||||
return {"success": False, "message": f"获取账号使用记录失败: {str(e)}"}
|
||||
|
||||
# 添加"重置设备id"功能
|
||||
@app.get("/reset-machine", tags=["System"])
|
||||
async def reset_machine():
|
||||
|
||||
13
database.py
13
database.py
@@ -1,6 +1,6 @@
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
from sqlalchemy import Column, String, Text, text, BigInteger
|
||||
from sqlalchemy import Column, String, Text, text, BigInteger, ForeignKey
|
||||
from contextlib import asynccontextmanager
|
||||
from logger import info, error
|
||||
from config import DATABASE_URL
|
||||
@@ -24,6 +24,17 @@ class AccountModel(Base):
|
||||
id = Column(BigInteger, nullable=False, index=True) # 添加毫秒时间戳列并创建索引
|
||||
|
||||
|
||||
# 账号使用记录模型
|
||||
class AccountUsageRecordModel(Base):
|
||||
__tablename__ = "account_usage_records"
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
account_id = Column(BigInteger, nullable=False, index=True) # 账号ID
|
||||
email = Column(String, nullable=False, index=True) # 账号邮箱
|
||||
ip = Column(String, nullable=True) # 使用者IP
|
||||
user_agent = Column(Text, nullable=True) # 使用者UA
|
||||
created_at = Column(Text, nullable=False) # 创建时间
|
||||
|
||||
|
||||
def create_engine():
|
||||
"""创建数据库引擎"""
|
||||
# 直接使用配置文件中的数据库URL
|
||||
|
||||
38
index.html
38
index.html
@@ -589,6 +589,44 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 账号使用记录模态框 -->
|
||||
<div class="modal fade" id="usageRecordModal" tabindex="-1" aria-labelledby="usageRecordModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="usageRecordModalLabel">账号使用记录</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<strong>账号邮箱:</strong> <span id="recordEmail"></span>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-sm align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>使用时间</th>
|
||||
<th>使用者IP</th>
|
||||
<th>使用者UA</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="usageRecordBody">
|
||||
<!-- 记录将被动态填充 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="no-records" class="text-center p-4" style="display: none;">
|
||||
<i class="fas fa-history fa-2x text-muted mb-2"></i>
|
||||
<p>暂无使用记录</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 自动刷新指示器 -->
|
||||
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 5">
|
||||
<div class="d-flex align-items-center small text-muted">
|
||||
|
||||
@@ -397,15 +397,17 @@ function updateAccountsTable(accounts) {
|
||||
<i class="fas fa-eye toggle-password" data-password="${account.password}" title="显示/隐藏密码"></i>
|
||||
<i class="fas fa-copy copy-btn ms-1" data-copy="${account.password}" title="复制密码"></i>
|
||||
</td>
|
||||
${renderTokenColumn(account.token, account.id)}
|
||||
${renderTokenColumn(account.token, account.id, account.email)}
|
||||
${renderUsageProgress(account.usage_limit)}
|
||||
<td class="d-none d-lg-table-cell">
|
||||
${account.created_at || '未知'}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary get-usage-btn" data-email="${account.email}" title="查询使用量">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline-primary get-usage-btn" data-email="${account.email}" title="查询使用量">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="operation-column">
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
@@ -493,9 +495,11 @@ function renderAccountsTable() {
|
||||
${account.created_at || '未知'}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary get-usage-btn" data-email="${account.email}" title="查询使用量">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline-primary get-usage-btn" data-email="${account.email}" title="查询使用量">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="operation-column">
|
||||
<div class="d-flex flex-wrap gap-1">
|
||||
@@ -960,6 +964,13 @@ function bindTableEvents() {
|
||||
const email = $(this).data('email');
|
||||
getAccountUsage(email);
|
||||
});
|
||||
|
||||
// 查看使用记录按钮
|
||||
$('.view-records-btn').off('click').on('click', function() {
|
||||
const email = $(this).data('email');
|
||||
const id = $(this).data('id');
|
||||
getAccountUsageRecords(email, id);
|
||||
});
|
||||
|
||||
// 删除按钮
|
||||
$('.delete-account-btn').off('click').on('click', function() {
|
||||
@@ -1137,12 +1148,15 @@ function renderUsageProgress(usageLimit) {
|
||||
}
|
||||
|
||||
// 修改Token列的渲染方式
|
||||
function renderTokenColumn(token, accountId) {
|
||||
function renderTokenColumn(token, accountId, email) {
|
||||
return `
|
||||
<td class="token-column">
|
||||
<button class="btn btn-sm btn-outline-info view-token-btn" data-token="${token}" data-account-id="${accountId}">
|
||||
<i class="fas fa-eye"></i> 查看Token
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info view-records-btn" data-email="${email}" data-id="${accountId}" title="查看使用记录">
|
||||
<i class="fas fa-history"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
}
|
||||
@@ -1690,4 +1704,58 @@ function importAccounts(file) {
|
||||
showAlert('danger', '导入账号失败: ' + (xhr.responseJSON?.detail || xhr.statusText));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取账号使用记录
|
||||
function getAccountUsageRecords(email, id) {
|
||||
showLoading();
|
||||
|
||||
// 设置模态框中的账号邮箱
|
||||
$('#recordEmail').text(email);
|
||||
|
||||
// 清空记录列表
|
||||
$('#usageRecordBody').empty();
|
||||
|
||||
fetch(`/account/${id}/usage-records`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
hideLoading();
|
||||
|
||||
if (data.success) {
|
||||
const records = data.records;
|
||||
|
||||
if (records && records.length > 0) {
|
||||
// 隐藏无记录提示
|
||||
$('#no-records').hide();
|
||||
|
||||
// 显示记录
|
||||
records.forEach(record => {
|
||||
const row = `
|
||||
<tr>
|
||||
<td>${formatDateTime(record.created_at)}</td>
|
||||
<td>${record.ip || '-'}</td>
|
||||
<td class="small text-truncate" style="max-width: 300px;" title="${record.user_agent || ''}">
|
||||
${record.user_agent || '-'}
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
$('#usageRecordBody').append(row);
|
||||
});
|
||||
} else {
|
||||
// 显示无记录提示
|
||||
$('#usageRecordBody').empty();
|
||||
$('#no-records').show();
|
||||
}
|
||||
|
||||
// 显示模态框
|
||||
new bootstrap.Modal(document.getElementById('usageRecordModal')).show();
|
||||
} else {
|
||||
showAlert(`获取使用记录失败: ${data.message || '未知错误'}`, 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取使用记录时发生错误:', error);
|
||||
hideLoading();
|
||||
showAlert('获取使用记录失败,请稍后重试', 'danger');
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user