repair-lobster-api-registry-schema.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /**
  2. * Repair lobster_api_registry schema for tenant DBs (old api_url -> new base_url/provider/auth columns).
  3. * Usage: node scripts/repair-lobster-api-registry-schema.js [dbName|all]
  4. */
  5. const mysql = require('mysql2/promise')
  6. const DB_CFG = {
  7. host: process.env.DB_HOST || 'cq-cdb-8fjmemkb.sql.tencentcdb.com',
  8. port: Number(process.env.DB_PORT || 27220),
  9. user: process.env.DB_USER || 'root',
  10. password: process.env.DB_PASSWORD || 'Ylrz_1q2w3e4r5t6y',
  11. multipleStatements: true,
  12. charset: 'utf8mb4'
  13. }
  14. const MASTER_DB = process.env.DB_NAME || 'ylrz_saas'
  15. const ALTERS = [
  16. "ALTER TABLE lobster_api_registry ADD COLUMN provider VARCHAR(64) DEFAULT NULL COMMENT 'provider' AFTER category",
  17. "ALTER TABLE lobster_api_registry ADD COLUMN base_url VARCHAR(500) DEFAULT NULL COMMENT 'base url' AFTER provider",
  18. "ALTER TABLE lobster_api_registry ADD COLUMN auth_type VARCHAR(32) DEFAULT 'none' COMMENT 'auth type' AFTER base_url",
  19. "ALTER TABLE lobster_api_registry ADD COLUMN auth_config TEXT COMMENT 'auth config' AFTER auth_type",
  20. "ALTER TABLE lobster_api_registry ADD COLUMN timeout INT DEFAULT 5000 COMMENT 'timeout ms' AFTER auth_config",
  21. "ALTER TABLE lobster_api_registry ADD COLUMN priority INT DEFAULT 0 COMMENT 'priority' AFTER enabled",
  22. "ALTER TABLE lobster_api_registry ADD COLUMN is_backup TINYINT DEFAULT 0 COMMENT 'backup' AFTER priority",
  23. "ALTER TABLE lobster_api_registry ADD COLUMN description TEXT COMMENT 'description' AFTER is_backup"
  24. ]
  25. async function columnExists(conn, table, column) {
  26. const [rows] = await conn.query(
  27. 'SELECT COUNT(*) AS c FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?',
  28. [table, column]
  29. )
  30. return Number(rows[0].c) > 0
  31. }
  32. async function tableExists(conn, table) {
  33. const [rows] = await conn.query('SHOW TABLES LIKE ?', [table])
  34. return rows.length > 0
  35. }
  36. async function repairDb(db) {
  37. const conn = await mysql.createConnection({ ...DB_CFG, database: db })
  38. try {
  39. if (!(await tableExists(conn, 'lobster_api_registry'))) {
  40. console.log(`[skip] ${db}: table missing`)
  41. return
  42. }
  43. let added = 0
  44. for (const sql of ALTERS) {
  45. const col = sql.match(/ADD COLUMN (\w+)/)[1]
  46. if (await columnExists(conn, 'lobster_api_registry', col)) continue
  47. await conn.query(sql)
  48. added++
  49. console.log(`[${db}] added column ${col}`)
  50. }
  51. if (await columnExists(conn, 'lobster_api_registry', 'api_url')) {
  52. await conn.query(
  53. "UPDATE lobster_api_registry SET base_url = api_url WHERE (base_url IS NULL OR base_url = '') AND api_url IS NOT NULL AND api_url <> ''"
  54. )
  55. }
  56. if (await columnExists(conn, 'lobster_api_registry', 'api_description')) {
  57. await conn.query(
  58. "UPDATE lobster_api_registry SET description = api_description WHERE (description IS NULL OR description = '') AND api_description IS NOT NULL AND api_description <> ''"
  59. )
  60. }
  61. await conn.query("UPDATE lobster_api_registry SET auth_type = 'none' WHERE auth_type IS NULL OR auth_type = ''")
  62. const [cols] = await conn.query('SHOW COLUMNS FROM lobster_api_registry')
  63. const [cnt] = await conn.query('SELECT COUNT(*) AS c FROM lobster_api_registry')
  64. console.log(`[ok] ${db}: columns=${cols.length}, rows=${cnt[0].c}, newCols=${added}`)
  65. } finally {
  66. await conn.end()
  67. }
  68. }
  69. async function listTenantDbs() {
  70. const conn = await mysql.createConnection({ ...DB_CFG, database: MASTER_DB })
  71. const dbs = new Set()
  72. try {
  73. const [rows] = await conn.query("SELECT db_url FROM tenant_info WHERE db_url IS NOT NULL AND db_url <> ''")
  74. for (const r of rows) {
  75. const m = String(r.db_url).match(/\/([^/?]+)(?:\?|$)/)
  76. if (m) dbs.add(m[1])
  77. }
  78. } finally {
  79. await conn.end()
  80. }
  81. return [...dbs]
  82. }
  83. async function main() {
  84. const arg = process.argv[2] || 'all'
  85. const targets = arg === 'all' ? await listTenantDbs() : [arg]
  86. for (const db of targets) {
  87. try {
  88. await repairDb(db)
  89. } catch (e) {
  90. console.error(`[fail] ${db}: ${e.message}`)
  91. }
  92. }
  93. console.log('Done.')
  94. }
  95. main().catch(e => { console.error(e); process.exit(1) })