migrate_to_resource.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #!/usr/bin/env python3
  2. """
  3. 安全迁移脚本:contents → resources + knowledge.resource_ids
  4. 在服务器上执行此脚本,然后再拉取新代码
  5. 变更内容:
  6. 1. contents表 → resources表
  7. 2. 为resources表添加新字段(secure_body, content_type, metadata, updated_at)
  8. 3. 为knowledge表添加resource_ids字段
  9. """
  10. import sqlite3
  11. import sys
  12. from pathlib import Path
  13. def migrate_resources_table(conn, cursor):
  14. """迁移resources表(原contents表)"""
  15. print("\n" + "="*60)
  16. print("步骤1: 迁移 resources 表")
  17. print("="*60)
  18. # 检查contents表是否存在
  19. cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='contents'")
  20. if not cursor.fetchone():
  21. cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='resources'")
  22. if cursor.fetchone():
  23. print("✅ resources表已存在,跳过此步骤")
  24. return True
  25. else:
  26. print("⚠️ 既没有contents表也没有resources表,将在首次启动时创建")
  27. return False
  28. # 检查contents表中的数据
  29. cursor.execute("SELECT COUNT(*) FROM contents")
  30. count = cursor.fetchone()[0]
  31. print(f"contents表中有 {count} 条记录")
  32. # 检查是否已有新字段
  33. cursor.execute("PRAGMA table_info(contents)")
  34. columns = {row[1] for row in cursor.fetchall()}
  35. print(f"现有字段: {columns}")
  36. # 添加新字段(如果不存在)
  37. migrations = []
  38. if "secure_body" not in columns:
  39. migrations.append("ALTER TABLE contents ADD COLUMN secure_body TEXT DEFAULT ''")
  40. if "content_type" not in columns:
  41. migrations.append("ALTER TABLE contents ADD COLUMN content_type TEXT DEFAULT 'text'")
  42. if "metadata" not in columns:
  43. migrations.append("ALTER TABLE contents ADD COLUMN metadata TEXT DEFAULT '{}'")
  44. if "updated_at" not in columns:
  45. migrations.append("ALTER TABLE contents ADD COLUMN updated_at TEXT DEFAULT ''")
  46. if migrations:
  47. print(f"\n添加 {len(migrations)} 个新字段...")
  48. for sql in migrations:
  49. print(f" {sql}")
  50. cursor.execute(sql)
  51. conn.commit()
  52. print("✅ 字段添加完成")
  53. # 重命名表
  54. print("\n重命名表: contents → resources")
  55. try:
  56. cursor.execute("ALTER TABLE contents RENAME TO resources")
  57. conn.commit()
  58. print("✅ 表重命名完成")
  59. except Exception as e:
  60. print(f"❌ 重命名失败: {e}")
  61. return False
  62. # 验证
  63. cursor.execute("SELECT COUNT(*) FROM resources")
  64. new_count = cursor.fetchone()[0]
  65. print(f"resources表中有 {new_count} 条记录")
  66. if new_count == count:
  67. print(f"✅ 数据完整,{count} 条记录全部保留")
  68. return True
  69. else:
  70. print(f"⚠️ 数据不一致: 原 {count} 条 → 现 {new_count} 条")
  71. return False
  72. def migrate_knowledge_table(conn, cursor):
  73. """为knowledge表添加resource_ids字段"""
  74. print("\n" + "="*60)
  75. print("步骤2: 更新 knowledge 表")
  76. print("="*60)
  77. # 检查knowledge表是否存在
  78. cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='knowledge'")
  79. if not cursor.fetchone():
  80. print("⚠️ knowledge表不存在,跳过此步骤")
  81. return True
  82. # 检查是否已有resource_ids字段
  83. cursor.execute("PRAGMA table_info(knowledge)")
  84. columns = {row[1] for row in cursor.fetchall()}
  85. print(f"现有字段: {columns}")
  86. if "resource_ids" in columns:
  87. print("✅ resource_ids字段已存在,跳过此步骤")
  88. return True
  89. # 添加resource_ids字段
  90. print("\n添加 resource_ids 字段...")
  91. try:
  92. cursor.execute("ALTER TABLE knowledge ADD COLUMN resource_ids TEXT DEFAULT '[]'")
  93. conn.commit()
  94. print("✅ resource_ids字段添加完成")
  95. return True
  96. except Exception as e:
  97. print(f"❌ 添加字段失败: {e}")
  98. return False
  99. def migrate():
  100. """主迁移函数"""
  101. def migrate():
  102. """主迁移函数"""
  103. # 查找数据库文件
  104. db_path = Path("knowhub.db")
  105. if not db_path.exists():
  106. print("❌ 找不到 knowhub.db")
  107. print("请在包含数据库的目录中运行此脚本")
  108. sys.exit(1)
  109. print(f"数据库路径: {db_path.absolute()}")
  110. conn = sqlite3.connect(str(db_path))
  111. cursor = conn.cursor()
  112. # 显示当前表状态
  113. cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
  114. tables = {row[0] for row in cursor.fetchall()}
  115. print(f"\n当前表: {tables}")
  116. # 执行迁移
  117. success = True
  118. # 步骤1: 迁移resources表
  119. if not migrate_resources_table(conn, cursor):
  120. success = False
  121. # 步骤2: 更新knowledge表
  122. if not migrate_knowledge_table(conn, cursor):
  123. success = False
  124. # 最终验证
  125. print("\n" + "="*60)
  126. print("迁移总结")
  127. print("="*60)
  128. cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
  129. tables = {row[0] for row in cursor.fetchall()}
  130. print(f"迁移后的表: {tables}")
  131. # 检查resources表
  132. if "resources" in tables:
  133. cursor.execute("SELECT COUNT(*) FROM resources")
  134. count = cursor.fetchone()[0]
  135. print(f"✅ resources表: {count} 条记录")
  136. else:
  137. print("⚠️ resources表不存在")
  138. # 检查knowledge表
  139. if "knowledge" in tables:
  140. cursor.execute("SELECT COUNT(*) FROM knowledge")
  141. count = cursor.fetchone()[0]
  142. cursor.execute("PRAGMA table_info(knowledge)")
  143. columns = {row[1] for row in cursor.fetchall()}
  144. has_resource_ids = "resource_ids" in columns
  145. print(f"✅ knowledge表: {count} 条记录, resource_ids字段: {'存在' if has_resource_ids else '不存在'}")
  146. else:
  147. print("⚠️ knowledge表不存在")
  148. conn.close()
  149. if success:
  150. print("\n✅ 迁移完成!现在可以拉取新代码并重启服务")
  151. else:
  152. print("\n⚠️ 迁移过程中有警告,请检查上述输出")
  153. return success
  154. if __name__ == "__main__":
  155. print("=" * 60)
  156. print("KnowHub 数据库迁移")
  157. print("变更内容:")
  158. print(" 1. contents表 → resources表")
  159. print(" 2. resources表添加新字段")
  160. print(" 3. knowledge表添加resource_ids字段")
  161. print("=" * 60)
  162. try:
  163. success = migrate()
  164. sys.exit(0 if success else 1)
  165. except Exception as e:
  166. print(f"\n❌ 迁移失败: {e}")
  167. import traceback
  168. traceback.print_exc()
  169. sys.exit(1)