api-server/prisma/seed.ts
wangdl ed2dcb02f6
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 50s
feat: add AdminApiKey permanent token for automated testing
- Add AdminApiKey model (keyHash, expiresAt nullable for permanent)
- Extend AdminAuthGuard to accept x-api-key header as fallback auth
- Seed creates test-admin@zhixi.com with permanent SUPER_ADMIN API key
- Key format: zxat_<64 hex chars>, stored as SHA-256 hash

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 18:52:04 +08:00

117 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcryptjs';
import * as crypto from 'crypto';
const prisma = new PrismaClient();
function sha256(input: string): string {
return crypto.createHash('sha256').update(input).digest('hex');
}
async function main() {
const email = process.env.SUPER_ADMIN_EMAIL;
const password = process.env.SUPER_ADMIN_PASSWORD;
const testEmail = process.env.TEST_ADMIN_EMAIL || 'test-admin@zhixi.com';
const testPassword = process.env.TEST_ADMIN_PASSWORD || 'test-zhixi-admin-2026';
if (!email || !password) {
console.error('❌ 请设置环境变量 SUPER_ADMIN_EMAIL 和 SUPER_ADMIN_PASSWORD');
process.exit(1);
}
if (password.length < 8) {
console.error('❌ SUPER_ADMIN_PASSWORD 长度不能少于 8 位');
process.exit(1);
}
const passwordHash = await bcrypt.hash(password, 12);
const adminUser = await prisma.adminUser.upsert({
where: { email },
update: {
passwordHash,
role: 'SUPER_ADMIN',
status: 'ACTIVE',
displayName: '超级管理员',
},
create: {
email,
passwordHash,
displayName: '超级管理员',
role: 'SUPER_ADMIN',
status: 'ACTIVE',
},
});
console.log(`✅ 超级管理员已创建/更新: ${adminUser.email} (id: ${adminUser.id})`);
// ── 测试专用管理员 + 永久 API Key ──
const testPasswordHash = await bcrypt.hash(testPassword, 12);
const testAdmin = await prisma.adminUser.upsert({
where: { email: testEmail },
update: {
passwordHash: testPasswordHash,
role: 'SUPER_ADMIN',
status: 'ACTIVE',
displayName: '自动化测试',
},
create: {
email: testEmail,
passwordHash: testPasswordHash,
displayName: '自动化测试',
role: 'SUPER_ADMIN',
status: 'ACTIVE',
},
});
console.log(`✅ 测试管理员已创建/更新: ${testAdmin.email} (id: ${testAdmin.id})`);
// Generate permanent API key (only if --new-key flag or first time)
const args = process.argv.slice(2);
const forceNew = args.includes('--new-key');
const existingKey = !forceNew
? await prisma.adminApiKey.findFirst({
where: { adminUserId: testAdmin.id, name: 'auto-test-key', expiresAt: null },
})
: null;
if (existingKey) {
console.log(` 永久 API Key 已存在 (prefix: ${existingKey.prefix}...),跳过生成。使用 --new-key 强制重新生成`);
} else {
const rawKey = `zxat_${crypto.randomBytes(32).toString('hex')}`;
const keyHash = sha256(rawKey);
const prefix = rawKey.slice(0, 8);
await prisma.adminApiKey.create({
data: {
adminUserId: testAdmin.id,
name: 'auto-test-key',
keyHash,
prefix,
expiresAt: null, // 永不过期
createdBy: 'seed',
},
});
console.log('');
console.log('═══════════════════════════════════════════════════════');
console.log('🔑 测试管理员永久 API Key请妥善保存:');
console.log(` ${rawKey}`);
console.log('═══════════════════════════════════════════════════════');
console.log(` Email: ${testEmail}`);
console.log(` Password: ${testPassword}`);
console.log(' 使用方式: x-api-key header');
console.log('═══════════════════════════════════════════════════════');
}
}
main()
.catch((e) => {
console.error('❌ Seed 失败:', e);
process.exit(1);
})
.finally(() => prisma.$disconnect());