25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

78 lines
6.5 KiB

  1. import fs from 'node:fs'
  2. import path from 'node:path'
  3. import assert from 'node:assert/strict'
  4. import { fileURLToPath } from 'node:url'
  5. import { createRequire } from 'node:module'
  6. import { login, call } from './role-permissions-live.mjs'
  7. const report = fileURLToPath(new URL('../reports/digital-human-simple-auth-20260909/', import.meta.url))
  8. fs.mkdirSync(report, { recursive:true })
  9. const checks = []
  10. function check(name, condition) { assert.ok(condition, name); checks.push({ name, result:'PASS' }); console.log(`PASS: ${name}`) }
  11. const clean = value => !/"(?:appSecret|secretCipher|app_secret|secret_cipher)"\s*:/.test(JSON.stringify(value))
  12. let browser
  13. try {
  14. for (const name of ['integration-secret.key', 'integration-internal.key']) {
  15. check(`${name} 已移除`, !fs.existsSync(fileURLToPath(new URL(`../../unreal_tran_api/config/${name}`, import.meta.url))))
  16. }
  17. const session = await login('root')
  18. const token = session.accessToken
  19. const configs = await call('auth/v1/interfaces?size=100', token)
  20. check('接口配置列表不返回 Secret 字段', configs.status === 200 && clean(configs.data))
  21. const row = configs.data.records.find(item => item.code === 'digital-human' && item.environmentCode === 'default')
  22. assert.ok(row, 'Digital-human configuration exists')
  23. const settings = JSON.parse(row.extraConfig)
  24. check('原有 APPID 和密钥配置保留', settings.appId === 'AP_238179E0A10F97927EA49FA3' && settings.secretConfigured)
  25. const configuration = { ...row, environment:row.environmentCode, version:row.dataVersion }
  26. const saved = await call(`auth/v1/interfaces/${row.id}/digital-human`, token, { configuration, appId:settings.appId, appSecret:'' }, 'PUT')
  27. check('留空 Secret 保存配置成功且不回显密钥', saved.status === 200 && saved.code === 0 && clean(saved.data))
  28. const tested = await call(`auth/v1/interfaces/${row.id}/test`, token, {})
  29. check('移除密钥文件后 APPID + Secret 连接测试成功', tested.status === 200 && tested.data?.success)
  30. const runtimeUrl = 'http://127.0.0.1:6101/internal/v1/interfaces/digital-human/runtime'
  31. const anonymous = await fetch(runtimeUrl, { method:'POST' })
  32. check('内部调用继续要求现有登录身份', anonymous.status === 401)
  33. const runtime = await fetch(runtimeUrl, { method:'POST', headers:{ Authorization:`Bearer ${token}` } })
  34. const runtimeData = await runtime.json()
  35. check('复用登录身份获取运行令牌,无额外服务密钥', runtime.status === 200 && runtimeData.data?.accessToken && clean(runtimeData) && runtime.headers.get('cache-control') === 'no-store')
  36. const capabilities = await call('tran/v1/content/digital-human/capabilities', token)
  37. check('Tran → Auth → 数字人系统能力调用成功', capabilities.status === 200 && capabilities.data?.appId === settings.appId)
  38. for (const kind of ['videos', 'video-folders', 'avatars', 'voices', 'backgrounds']) {
  39. const response = await call(`tran/v1/content/digital-human/resources/${kind}`, token)
  40. check(`授权资源 ${kind} 查询成功`, response.status === 200 && response.code === 0)
  41. }
  42. const { chromium, expect } = createRequire(new URL('../package.json', import.meta.url))('@playwright/test')
  43. browser = await chromium.launch({ channel:'msedge', headless:true })
  44. const context = await browser.newContext({ viewport:{ width:1600, height:1000 } })
  45. await context.addInitScript(({ accessToken, refreshToken }) => {
  46. sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken)
  47. sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken)
  48. }, session)
  49. const page = await context.newPage()
  50. await page.goto('http://127.0.0.1:6180/system/interfaces')
  51. const configRow = page.locator('.el-table__body tr').filter({ hasText:'digital-human' }).first()
  52. await expect(configRow).toBeVisible({ timeout:60000 })
  53. await configRow.getByRole('button', { name:'编辑配置' }).click()
  54. const dialog = page.getByRole('dialog', { name:'编辑服务配置' })
  55. const secret = dialog.locator('input[type="password"]')
  56. await expect(secret).toHaveValue('')
  57. await expect(secret).toHaveAttribute('placeholder', '已配置,留空保留现有密钥')
  58. check('配置弹窗密钥留空,仅显示已配置状态', true)
  59. await page.screenshot({ path:path.join(report, '01-configuration.png'), animations:'disabled' })
  60. const saveResponse = page.waitForResponse(response => response.request().method() === 'PUT' && response.url().endsWith(`/interfaces/${row.id}/digital-human`))
  61. await dialog.getByRole('button', { name:'保存配置' }).click()
  62. check('页面留空密钥保存成功', (await saveResponse).status() === 200)
  63. await expect(dialog).toBeHidden()
  64. const testResponse = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith(`/interfaces/${row.id}/test`))
  65. await configRow.getByRole('button', { name:'连接检查' }).click()
  66. const testedFromPage = await (await testResponse).json()
  67. check('页面连接检查实际响应成功', testedFromPage.data?.success)
  68. await expect(configRow.getByText('连接正常', { exact:true })).toBeVisible({ timeout:30000 })
  69. await expect(page.locator('.el-loading-mask:visible')).toHaveCount(0)
  70. await page.screenshot({ path:path.join(report, '02-connection.png'), animations:'disabled' })
  71. check('页面连接测试成功', true)
  72. } finally {
  73. await browser?.close()
  74. fs.writeFileSync(path.join(report, 'results.json'), JSON.stringify({ time:new Date().toISOString(), checks }, null, 2))
  75. }
  76. fs.writeFileSync(path.join(report, 'index.html'), `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><title>数字人 APPID + Secret 简化验证</title><style>body{font:16px/1.7 system-ui;max-width:1200px;margin:32px auto;color:#163d35}h1{font-size:26px}li{margin:5px 0}img{width:100%;border:1px solid #ccdcd5}section{margin-top:28px}</style><h1>数字人 APPID + Secret 简化验证</h1><p>2026-09-09 · 开发环境真实联调 · ${checks.length} 项通过</p><p>移除两个额外密钥文件及运行依赖;现有 Secret 迁移至接口配置表 app_secret。继续使用现有登录身份调用 Auth,数字人开放服务通过 APPID + Secret 换取短期令牌。</p><ul>${checks.map(x=>`<li>✓ ${x.name}</li>`).join('')}</ul><p>后端 mvn clean package 通过;新增认证、密钥保存和转发测试。039 迁移的密文保护、正常迁移、重复执行已验证。原有应用保持不变;本轮未重新创建视频生成任务。</p><section><h2>配置弹窗</h2><img src="01-configuration.png"></section><section><h2>真实连接测试</h2><img src="02-connection.png"></section></html>`)