Compare commits
3 Commits
773ae2e3fb
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 020ad65e8c | |||
| cd9fb924d5 | |||
| 2c0d6ba2eb |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ dist/
|
||||
`n__pycache__/
|
||||
test_output*.json
|
||||
results_v*.json
|
||||
`n__pycache__/`noriget/ test_output_*.json`noriget/results_v*.json`n*.pyc
|
||||
|
||||
42
docker-compose.dev.yml
Normal file
42
docker-compose.dev.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
# DEV 部署 — 与 main 共享 postgres,独立端口
|
||||
# 用法:
|
||||
# 启动 dev: docker compose -f docker-compose.dev.yml up -d --build
|
||||
# 停止 dev: docker compose -f docker-compose.dev.yml down
|
||||
# 切换回 main: docker compose up -d --build
|
||||
# 两者可同时运行
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: maqt-postgres
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-maqt}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-maqt123456}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-maqt}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-maqt}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
mqsrv:
|
||||
build: .
|
||||
container_name: maqt-backend-dev
|
||||
ports:
|
||||
- "127.0.0.1:3003:3001"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
1252
origet/ test_output_202605240119.json
Normal file
1252
origet/ test_output_202605240119.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
origet/__pycache__/api_spec.cpython-313.pyc
Normal file
BIN
origet/__pycache__/api_spec.cpython-313.pyc
Normal file
Binary file not shown.
BIN
origet/__pycache__/client.cpython-312.pyc
Normal file
BIN
origet/__pycache__/client.cpython-312.pyc
Normal file
Binary file not shown.
BIN
origet/__pycache__/client.cpython-313.pyc
Normal file
BIN
origet/__pycache__/client.cpython-313.pyc
Normal file
Binary file not shown.
BIN
origet/__pycache__/decrypt.cpython-313.pyc
Normal file
BIN
origet/__pycache__/decrypt.cpython-313.pyc
Normal file
Binary file not shown.
@@ -280,3 +280,31 @@ model DeviceBinding {
|
||||
@@unique([userId, installId])
|
||||
@@map("device_bindings")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 地图密码批次表
|
||||
// ============================================
|
||||
model MapPassword {
|
||||
id String @id @default(uuid())
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
maps MapPasswordItem[]
|
||||
|
||||
@@map("map_passwords")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 地图密码明细表
|
||||
// ============================================
|
||||
model MapPasswordItem {
|
||||
id String @id @default(uuid())
|
||||
batchId String @map("batch_id")
|
||||
mapName String @map("map_name") @db.VarChar(50)
|
||||
password String @db.VarChar(20)
|
||||
location String @db.Text
|
||||
image String @db.VarChar(500)
|
||||
|
||||
batch MapPassword @relation(fields: [batchId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("map_password_items")
|
||||
}
|
||||
|
||||
58
src/index.ts
58
src/index.ts
@@ -158,9 +158,30 @@ app.get('/api/software-version-ad', (req, res) => {
|
||||
res.json({ success: true, data: { items: [] } });
|
||||
});
|
||||
|
||||
// 游戏地图密码缓存
|
||||
app.get('/api/game/map-password/cached', (req, res) => {
|
||||
res.json({ success: true, data: [] });
|
||||
// 游戏地图密码缓存(对齐 maqt.top 格式)
|
||||
app.get('/api/game/map-password/cached', async (_req, res) => {
|
||||
try {
|
||||
// 从 DB 取最新一批地图密码
|
||||
const latest = await prisma.mapPassword.findFirst({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
include: { maps: { select: { mapName: true, password: true, location: true, image: true } } },
|
||||
});
|
||||
if (latest) {
|
||||
const d = new Date(latest.createdAt);
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
maps: latest.maps,
|
||||
updateTime: `${String(d.getDate()).padStart(2,'0')}月${String(d.getMonth()+1).padStart(2,'0')}日`,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// 默认空数据
|
||||
res.json({ success: true, data: { maps: [], updateTime: '' } });
|
||||
}
|
||||
} catch {
|
||||
res.json({ success: true, data: { maps: [], updateTime: '' } });
|
||||
}
|
||||
});
|
||||
|
||||
// 筛选分类
|
||||
@@ -360,6 +381,37 @@ app.post('/api/admin/set-vip', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// 管理员更新地图密码
|
||||
app.post('/api/admin/map-passwords', async (req, res) => {
|
||||
const secret = process.env.ADMIN_SECRET;
|
||||
if (!secret || req.headers['x-admin-secret'] !== secret) {
|
||||
return res.status(403).json({ success: false, message: '禁止访问' });
|
||||
}
|
||||
try {
|
||||
const { maps } = req.body;
|
||||
if (!Array.isArray(maps) || maps.length === 0) {
|
||||
return res.status(400).json({ success: false, message: '缺少 maps 数组' });
|
||||
}
|
||||
const batch = await prisma.mapPassword.create({
|
||||
data: {
|
||||
maps: {
|
||||
create: maps.map((m: any) => ({
|
||||
mapName: m.mapName,
|
||||
password: m.password,
|
||||
location: m.location || '',
|
||||
image: m.image || '',
|
||||
})),
|
||||
},
|
||||
},
|
||||
include: { maps: true },
|
||||
});
|
||||
res.json({ success: true, data: batch });
|
||||
} catch (e: any) {
|
||||
console.error('Admin map-password error:', e);
|
||||
res.status(500).json({ success: false, message: '更新失败: ' + e.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 错误处理
|
||||
// ============================================
|
||||
|
||||
@@ -16,7 +16,7 @@ function sourceToType(source?: string): string {
|
||||
// 格式化方案卡片(对齐顶层 API)
|
||||
function formatScheme(s: any) {
|
||||
return {
|
||||
id: parseInt(s.id, 36) || String(s.id).split('-')[0] || s.id,
|
||||
id: s.id,
|
||||
user_id: s.userId,
|
||||
description: s.description || '',
|
||||
scheme_content: s.schemeContent || '',
|
||||
|
||||
@@ -10,7 +10,7 @@ const router = Router();
|
||||
|
||||
function formatScheme(s: any) {
|
||||
return {
|
||||
id: parseInt(s.id, 36) || String(s.id).split('-')[0] || s.id,
|
||||
id: s.id,
|
||||
user_id: s.userId,
|
||||
description: s.description || '',
|
||||
scheme_content: s.schemeContent || '',
|
||||
@@ -190,6 +190,9 @@ router.post('/', authMiddleware, async (req: Request, res: Response) => {
|
||||
price: priceNum,
|
||||
status: 'PUBLISHED',
|
||||
},
|
||||
include: {
|
||||
user: { select: { id: true, username: true, avatar: true } },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.user.update({
|
||||
|
||||
@@ -8,7 +8,7 @@ const router = Router();
|
||||
|
||||
function formatScheme(s: any) {
|
||||
return {
|
||||
id: parseInt(s.id, 36) || String(s.id).split('-')[0] || s.id,
|
||||
id: s.id,
|
||||
user_id: s.userId,
|
||||
description: s.description || '',
|
||||
scheme_content: s.schemeContent || '',
|
||||
@@ -151,6 +151,9 @@ router.post('/', authMiddleware, async (req: Request, res: Response) => {
|
||||
schemeContent: body.scheme,
|
||||
status: 'PUBLISHED',
|
||||
},
|
||||
include: {
|
||||
user: { select: { id: true, username: true, avatar: true } },
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.user.update({
|
||||
|
||||
Reference in New Issue
Block a user