Files
AIclinicalresearch/backend/scripts/test-legacy-auth.ts
HaHafeng c3f7d54fdf feat(platform): Implement legacy system integration with Wrapper Bridge architecture
Complete integration of the old clinical research platform (www.xunzhengyixue.com)
into the new AI platform via Token injection + iframe embedding:

Backend:
- Add legacy-bridge module (MySQL pool, auth service, routes)
- POST /api/v1/legacy/auth: JWT -> phone lookup -> Token injection into old MySQL
- Auto-create user in old system if not found (matched by phone number)

Frontend:
- LegacySystemPage: iframe container with Bridge URL construction
- ResearchManagement + StatisticalTools entry components
- Module registry updated from external links to iframe embed mode

ECS (token-bridge.html deployed to www.xunzhengyixue.com):
- Wrapper Bridge: sets cookies within same-origin context
- Storage Access API for cross-site dev environments
- CSS injection: hide old system nav/footer, remove padding gaps
- Inner iframe loads target page with full DOM access (same-origin)

Key technical decisions:
- Token injection (direct MySQL write) instead of calling login API
- Wrapper Bridge instead of parent-page cookie setting (cross-origin fix)
- Storage Access API + SameSite=None;Secure for third-party cookie handling
- User isolation guaranteed by phone number matching

Documentation:
- Integration plan v4.0 with full implementation record
- Implementation summary with 6 pitfalls documented
- System status guide updated (ST module now integrated)

Tested: Local E2E verified - auto login, research management, 126 statistical
tools, report generation, download, UI layout all working correctly

Made-with: Cursor
2026-02-27 21:54:38 +08:00

121 lines
4.3 KiB
TypeScript

/**
* Legacy Auth Token Injection Test
*
* Tests the full flow:
* 1. Connect to old system MySQL (xzyx_online)
* 2. Find user by phone
* 3. Generate MD5 token (same formula as old Java system)
* 4. Insert token into u_user_token
* 5. Print cookie-setting instructions for manual browser verification
*
* Usage:
* npx tsx scripts/test-legacy-auth.ts <phone>
* npx tsx scripts/test-legacy-auth.ts 18611348738
*/
import mysql from 'mysql2/promise';
import crypto from 'crypto';
const MYSQL_CONFIG = {
host: process.env.LEGACY_MYSQL_HOST || '8.154.22.149',
port: parseInt(process.env.LEGACY_MYSQL_PORT || '3306'),
user: process.env.LEGACY_MYSQL_USER || 'xzyx_rw',
password: process.env.LEGACY_MYSQL_PASSWORD || 'SKJfdwalkd',
database: process.env.LEGACY_MYSQL_DATABASE || 'xzyx_online',
};
function generateToken(userId: number): string {
const timestamp = Date.now();
const raw = `KyKz1.0:${userId}:${timestamp}`;
return crypto.createHash('md5').update(raw).digest('hex');
}
async function main() {
const phone = process.argv[2];
if (!phone) {
console.error('Usage: npx tsx scripts/test-legacy-auth.ts <phone>');
console.error('Example: npx tsx scripts/test-legacy-auth.ts 18611348738');
process.exit(1);
}
console.log('='.repeat(60));
console.log('Legacy Auth Token Injection Test');
console.log('='.repeat(60));
// Step 1: Connect
console.log('\n[1/5] Connecting to MySQL...');
const conn = await mysql.createConnection(MYSQL_CONFIG);
console.log(` OK - connected to ${MYSQL_CONFIG.host}:${MYSQL_CONFIG.port}/${MYSQL_CONFIG.database}`);
// Step 2: Find user
console.log(`\n[2/5] Looking up user by phone: ${phone}`);
const [rows] = await conn.execute<any[]>(
'SELECT id, phone, nickname, real_name, user_role FROM u_user_info WHERE phone = ?',
[phone],
);
if (rows.length === 0) {
console.log(' User NOT found. To test, pick an existing user:');
const [sample] = await conn.execute<any[]>(
'SELECT id, phone, nickname, real_name FROM u_user_info LIMIT 5',
);
console.table(sample);
await conn.end();
process.exit(1);
}
const user = rows[0];
console.log(` Found: id=${user.id}, nickname="${user.nickname}", role=${user.user_role}`);
// Step 3: Generate token
console.log('\n[3/5] Generating MD5 token...');
const token = generateToken(user.id);
console.log(` Token: ${token}`);
// Step 4: Delete old tokens + insert new one
// gen_time must be milliseconds since epoch (Java System.currentTimeMillis()),
// NOT MySQL NOW(). The old system's isTimeout() does millisecond arithmetic.
console.log('\n[4/5] Deleting old tokens and inserting new one...');
await conn.execute('DELETE FROM u_user_token WHERE user_id = ?', [user.id]);
const genTime = Date.now();
await conn.execute(
'INSERT INTO u_user_token (user_id, token, gen_time, user_role) VALUES (?, ?, ?, ?)',
[user.id, token, genTime, user.user_role],
);
console.log(` OK - token inserted (gen_time=${genTime}, which is ${new Date(genTime).toISOString()})`);
// Step 5: Verify
console.log('\n[5/5] Verifying token in database...');
const [verify] = await conn.execute<any[]>(
'SELECT id, user_id, token, gen_time, user_role FROM u_user_token WHERE token = ?',
[token],
);
console.log(' Verified:');
console.table(verify);
await conn.end();
// Print manual test instructions
console.log('\n' + '='.repeat(60));
console.log('TOKEN INJECTION SUCCESSFUL!');
console.log('='.repeat(60));
console.log('\nManual browser test:');
console.log('1. Open browser, go to https://www.xunzhengyixue.com');
console.log('2. Open DevTools (F12) -> Console');
console.log('3. Paste the following 3 lines:\n');
const nickname = user.nickname || user.real_name || phone;
console.log(`document.cookie = "token=${token}; domain=.xunzhengyixue.com; path=/";`);
console.log(`document.cookie = "nickname=${encodeURIComponent(nickname)}; domain=.xunzhengyixue.com; path=/";`);
console.log(`document.cookie = "id=${user.id}; domain=.xunzhengyixue.com; path=/";`);
console.log('\n4. Refresh the page (F5)');
console.log('5. You should see the user\'s projects without logging in manually');
console.log('\n' + '='.repeat(60));
}
main().catch((err) => {
console.error('Test failed:', err);
process.exit(1);
});