init-system.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /**
  2. * 系统初始化脚本 - 首次运行时执行
  3. */
  4. const fs = require('fs').promises;
  5. const path = require('path');
  6. const bcrypt = require('bcrypt');
  7. const { execSync } = require('child_process');
  8. const logger = require('../logger');
  9. const { ensureDirectoriesExist } = require('../init-dirs');
  10. const { downloadImages } = require('../download-images');
  11. const configService = require('../services/configService');
  12. // 用户文件路径
  13. const USERS_FILE = path.join(__dirname, '..', 'users.json');
  14. /**
  15. * 创建管理员用户
  16. * @param {string} username 用户名
  17. * @param {string} password 密码
  18. */
  19. async function createAdminUser(username = 'root', password = 'admin') {
  20. try {
  21. // 检查用户文件是否已存在
  22. try {
  23. await fs.access(USERS_FILE);
  24. logger.info('用户文件已存在,跳过创建管理员用户');
  25. return;
  26. } catch (err) {
  27. if (err.code !== 'ENOENT') throw err;
  28. }
  29. // 创建默认管理员用户
  30. const defaultUser = {
  31. username,
  32. password: bcrypt.hashSync(password, 10),
  33. createdAt: new Date().toISOString(),
  34. loginCount: 0,
  35. lastLogin: null
  36. };
  37. await fs.writeFile(USERS_FILE, JSON.stringify({ users: [defaultUser] }, null, 2));
  38. logger.success(`创建默认管理员用户: ${username}/${password}`);
  39. logger.warn('请在首次登录后立即修改默认密码');
  40. } catch (error) {
  41. logger.error('创建管理员用户失败:', error);
  42. throw error;
  43. }
  44. }
  45. /**
  46. * 创建默认配置
  47. */
  48. async function createDefaultConfig() {
  49. try {
  50. // 检查配置是否已存在
  51. const config = await configService.getConfig();
  52. // 如果菜单项为空,添加默认菜单项
  53. if (!config.menuItems || config.menuItems.length === 0) {
  54. config.menuItems = [
  55. {
  56. text: "控制台",
  57. link: "/admin",
  58. newTab: false
  59. },
  60. {
  61. text: "镜像搜索",
  62. link: "/",
  63. newTab: false
  64. },
  65. {
  66. text: "文档",
  67. link: "/docs",
  68. newTab: false
  69. },
  70. {
  71. text: "GitHub",
  72. link: "https://github.com/dqzboy/hubcmdui",
  73. newTab: true
  74. }
  75. ];
  76. await configService.saveConfig(config);
  77. logger.success('创建默认菜单配置');
  78. }
  79. return config;
  80. } catch (error) {
  81. logger.error('初始化配置失败:', error);
  82. throw error;
  83. }
  84. }
  85. /**
  86. * 创建示例文档 - 现已禁用
  87. */
  88. async function createSampleDocumentation() {
  89. logger.info('示例文档创建功能已禁用');
  90. return; // 不再创建默认文档
  91. /* 旧代码保留注释,已禁用
  92. const docService = require('../services/documentationService');
  93. try {
  94. await docService.ensureDocumentationDir();
  95. // 检查是否有现有文档
  96. const docs = await docService.getDocumentationList();
  97. if (docs && docs.length > 0) {
  98. logger.info('文档已存在,跳过创建示例文档');
  99. return;
  100. }
  101. // 创建示例文档
  102. const welcomeDoc = {
  103. title: "欢迎使用 Docker 镜像代理加速系统",
  104. content: `# 欢迎使用 Docker 镜像代理加速系统
  105. ## 系统简介
  106. Docker 镜像代理加速系统是一个帮助用户快速搜索、拉取 Docker 镜像的工具。本系统提供了以下功能:
  107. - 快速搜索 Docker Hub 上的镜像
  108. - 查看镜像的详细信息和标签
  109. - 管理本地 Docker 容器
  110. - 监控容器状态并发送通知
  111. ## 快速开始
  112. 1. 在首页搜索框中输入要查找的镜像名称
  113. 2. 点击搜索结果查看详细信息
  114. 3. 使用提供的命令拉取镜像
  115. ## 管理功能
  116. 管理员可以通过控制面板管理系统:
  117. - 查看所有容器状态
  118. - 启动/停止/重启容器
  119. - 更新容器镜像
  120. - 配置监控告警
  121. 祝您使用愉快!
  122. `,
  123. published: true
  124. };
  125. const aboutDoc = {
  126. title: "关于系统",
  127. content: `# 关于 Docker 镜像代理加速系统
  128. ## 系统版本
  129. 当前版本: v1.0.0
  130. ## 技术栈
  131. - 前端: HTML, CSS, JavaScript
  132. - 后端: Node.js, Express
  133. - 容器: Docker, Dockerode
  134. - 数据存储: 文件系统
  135. ## 联系方式
  136. 如有问题,请通过以下方式联系我们:
  137. - GitHub Issues
  138. - 电子邮件: [email protected]
  139. ## 许可证
  140. 本项目采用 MIT 许可证
  141. `,
  142. published: true
  143. };
  144. await docService.saveDocument(Date.now().toString(), welcomeDoc.title, welcomeDoc.content);
  145. await docService.saveDocument((Date.now() + 1000).toString(), aboutDoc.title, aboutDoc.content);
  146. logger.success('创建示例文档成功');
  147. } catch (error) {
  148. logger.error('创建示例文档失败:', error);
  149. }
  150. */
  151. }
  152. /**
  153. * 检查必要依赖
  154. */
  155. async function checkDependencies() {
  156. try {
  157. logger.info('正在检查系统依赖...');
  158. // 检查 Node.js 版本
  159. const nodeVersion = process.version;
  160. const minNodeVersion = 'v14.0.0';
  161. if (compareVersions(nodeVersion, minNodeVersion) < 0) {
  162. logger.warn(`当前 Node.js 版本 ${nodeVersion} 低于推荐的最低版本 ${minNodeVersion}`);
  163. } else {
  164. logger.success(`Node.js 版本 ${nodeVersion} 满足要求`);
  165. }
  166. // 检查必要的 npm 包
  167. try {
  168. const packageJson = require('../package.json');
  169. const requiredDeps = Object.keys(packageJson.dependencies);
  170. logger.info(`系统依赖共 ${requiredDeps.length} 个包`);
  171. // 检查是否有 node_modules 目录
  172. try {
  173. await fs.access(path.join(__dirname, '..', 'node_modules'));
  174. } catch (err) {
  175. if (err.code === 'ENOENT') {
  176. logger.warn('未找到 node_modules 目录,请运行 npm install 安装依赖');
  177. return false;
  178. }
  179. }
  180. } catch (err) {
  181. logger.warn('无法读取 package.json:', err.message);
  182. }
  183. // 检查 Docker
  184. try {
  185. execSync('docker --version', { stdio: ['ignore', 'ignore', 'ignore'] });
  186. logger.success('Docker 已安装');
  187. } catch (err) {
  188. logger.warn('未检测到 Docker,部分功能可能无法正常使用');
  189. }
  190. return true;
  191. } catch (error) {
  192. logger.error('依赖检查失败:', error);
  193. return false;
  194. }
  195. }
  196. /**
  197. * 比较版本号
  198. */
  199. function compareVersions(v1, v2) {
  200. const v1parts = v1.replace('v', '').split('.');
  201. const v2parts = v2.replace('v', '').split('.');
  202. for (let i = 0; i < Math.max(v1parts.length, v2parts.length); i++) {
  203. const v1part = parseInt(v1parts[i] || 0);
  204. const v2part = parseInt(v2parts[i] || 0);
  205. if (v1part > v2part) return 1;
  206. if (v1part < v2part) return -1;
  207. }
  208. return 0;
  209. }
  210. /**
  211. * 主初始化函数
  212. */
  213. async function initialize() {
  214. logger.info('开始系统初始化...');
  215. try {
  216. // 1. 检查系统依赖
  217. await checkDependencies();
  218. // 2. 确保目录结构存在
  219. await ensureDirectoriesExist();
  220. logger.success('目录结构初始化完成');
  221. // 3. 下载必要图片
  222. await downloadImages();
  223. // 4. 创建默认用户
  224. await createAdminUser();
  225. // 5. 创建默认配置
  226. await createDefaultConfig();
  227. // 6. 创建示例文档
  228. await createSampleDocumentation();
  229. logger.success('系统初始化完成!');
  230. // 移除敏感的账户信息日志
  231. logger.warn('首次登录后请立即修改默认密码!');
  232. return { success: true };
  233. } catch (error) {
  234. logger.error('系统初始化失败:', error);
  235. return { success: false, error: error.message };
  236. }
  237. }
  238. // 如果直接运行此脚本
  239. if (require.main === module) {
  240. initialize()
  241. .then((result) => {
  242. if (result.success) {
  243. process.exit(0);
  244. } else {
  245. process.exit(1);
  246. }
  247. })
  248. .catch((error) => {
  249. logger.fatal('初始化过程中发生错误:', error);
  250. process.exit(1);
  251. });
  252. }
  253. module.exports = {
  254. initialize,
  255. createAdminUser,
  256. createDefaultConfig,
  257. createSampleDocumentation,
  258. checkDependencies
  259. };