diff --git a/backend/routes/backup.js b/backend/routes/backup.js index b6e7bfa..258ec45 100644 --- a/backend/routes/backup.js +++ b/backend/routes/backup.js @@ -802,7 +802,6 @@ router.delete('/remote/targets/:id', (req, res) => { router.post('/remote/test', async (req, res) => { try { const config = req.body; - console.log(`[RemoteBackup] 收到连接测试请求:`, JSON.stringify(config)); if (!config.host || !config.port || !config.username) { return res.status(400).json({ @@ -812,21 +811,13 @@ router.post('/remote/test', async (req, res) => { } if (!config.protocol) { - const port = parseInt(config.port); - if (port === 22 || port === 2222) { - config.protocol = 'sftp'; - } else if (port === 445 || port === 139 || port === 443) { - config.protocol = 'smb'; - } else if (port === 80 || port === 443 || config.url) { - config.protocol = 'webdav'; - } else { - config.protocol = 'ftp'; - } - console.log(`[RemoteBackup] 推断协议类型: ${config.protocol} (基于端口 ${config.port})`); + return res.status(400).json({ + success: false, + message: '请提供协议类型(protocol)', + }); } const result = await testRemoteConnection(config); - console.log(`[RemoteBackup] 连接测试结果:`, JSON.stringify(result)); if (result.success) { res.json({ diff --git a/backend/scripts/migrate-all.js b/backend/scripts/migrate-all.js index e5be450..21a563c 100644 --- a/backend/scripts/migrate-all.js +++ b/backend/scripts/migrate-all.js @@ -94,6 +94,11 @@ const migrations = [ name: '设备字段系统标记', description: '为 deviceFields 表添加 isSystem 字段,标记系统字段不可删除', migrate: migrateDeviceFieldsIsSystem + }, + { + name: '设备字段Options配置', + description: '确保 deviceFields 表的 type 和 status 字段有正确的 options 配置', + migrate: migrateDeviceFieldsOptions } ]; @@ -149,7 +154,7 @@ async function runMigrations() { async function getTableColumns(tableName) { const dialect = sequelize.getDialect(); - + if (dialect === 'sqlite') { const tableInfo = await sequelize.query( `PRAGMA table_info(${tableName})`, @@ -167,7 +172,7 @@ async function getTableColumns(tableName) { async function tableExists(tableName) { const dialect = sequelize.getDialect(); - + if (dialect === 'sqlite') { const tables = await sequelize.query( "SELECT name FROM sqlite_master WHERE type='table' AND name=?", @@ -185,10 +190,10 @@ async function tableExists(tableName) { async function addColumnIfNotExists(tableName, columnName, columnDef) { const columns = await getTableColumns(tableName); - + if (!columns.includes(columnName)) { const dialect = sequelize.getDialect(); - const sql = dialect === 'sqlite' + const sql = dialect === 'sqlite' ? `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}` : `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`; await sequelize.query(sql); @@ -276,7 +281,7 @@ async function migrateConsumableLogDecouple() { async function removeConsumableLogFK() { const dialect = sequelize.getDialect(); - + if (dialect === 'sqlite') { const fks = await sequelize.query( `PRAGMA foreign_key_list(consumable_logs);`, @@ -411,7 +416,7 @@ async function migrateConsumableLogArchive() { async function migrateSnList() { const tables = ['consumables', 'consumable_records', 'consumable_logs']; - + for (const table of tables) { if (await tableExists(table)) { const columnDef = dbDialect === 'sqlite' ? "TEXT DEFAULT '[]'" : "JSON"; @@ -429,7 +434,7 @@ async function migrateDeviceModelField() { } const dialect = sequelize.getDialect(); - + if (dialect === 'mysql') { await sequelize.query( 'ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL' @@ -441,7 +446,7 @@ async function migrateDeviceModelField() { console.log(' model_old 字段已存在,跳过迁移'); return; } - + await sequelize.query('ALTER TABLE devices RENAME COLUMN model TO model_old'); await sequelize.query('ALTER TABLE devices ADD COLUMN model VARCHAR(255)'); await sequelize.query('UPDATE devices SET model = model_old'); @@ -452,14 +457,14 @@ async function migrateDeviceModelField() { async function migrateDeviceFieldsConfig() { const DeviceField = require('../models/DeviceField'); - + const updates = [ { fieldName: 'model', required: false }, { fieldName: 'powerConsumption', required: true }, { fieldName: 'purchaseDate', required: false }, { fieldName: 'warrantyExpiry', required: false }, ]; - + for (const update of updates) { const field = await DeviceField.findOne({ where: { fieldName: update.fieldName } }); if (field && field.required !== update.required) { @@ -475,7 +480,7 @@ async function migrateDeviceFieldsConfig() { async function migrateDeviceFieldsNullable() { const dialect = sequelize.getDialect(); - + if (dialect === 'mysql') { const alterCommands = [ "ALTER TABLE devices MODIFY COLUMN name VARCHAR(255) NULL", @@ -488,7 +493,7 @@ async function migrateDeviceFieldsNullable() { "ALTER TABLE devices MODIFY COLUMN powerConsumption FLOAT NULL", "ALTER TABLE devices MODIFY COLUMN customFields JSON NULL" ]; - + for (const sql of alterCommands) { try { await sequelize.query(sql); @@ -499,21 +504,21 @@ async function migrateDeviceFieldsNullable() { } } console.log(' devices 表字段已改为可空'); - + } else if (dialect === 'sqlite') { const columns = await getTableColumns('devices'); const hasNullableFlag = columns.includes('_nullable_migration_done'); - + if (hasNullableFlag) { console.log(' 已完成可空迁移,跳过'); return; } - + await sequelize.query('PRAGMA foreign_keys = OFF'); - + try { await sequelize.query('DROP TABLE IF EXISTS devices_new'); - + await sequelize.query(` CREATE TABLE devices_new ( deviceId VARCHAR(255) PRIMARY KEY NOT NULL UNIQUE, @@ -536,28 +541,28 @@ async function migrateDeviceFieldsNullable() { _nullable_migration_done INTEGER DEFAULT 1 ) `); - + await sequelize.query(` INSERT INTO devices_new ( deviceId, name, type, model, serialNumber, rackId, position, height, powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress, description, customFields, createdAt, updatedAt ) - SELECT + SELECT deviceId, name, type, model, serialNumber, rackId, position, height, powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress, description, customFields, createdAt, updatedAt FROM devices `); - + await sequelize.query('DROP TABLE devices'); await sequelize.query('ALTER TABLE devices_new RENAME TO devices'); - + await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_status ON devices(status)'); await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_type ON devices(type)'); await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_rackId ON devices(rackId)'); await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_name ON devices(name)'); - + console.log(' devices 表字段已改为可空'); } finally { await sequelize.query('PRAGMA foreign_keys = ON'); @@ -632,6 +637,56 @@ async function migrateDeviceFieldsIsSystem() { console.log(' 设备字段系统标记迁移完成'); } +async function migrateDeviceFieldsOptions() { + const DeviceField = require('../models/DeviceField'); + + if (!(await tableExists('deviceFields'))) { + console.log(' deviceFields 表不存在,跳过'); + return; + } + + const deviceTypeOptions = [ + { value: 'server', label: '服务器' }, + { value: 'switch', label: '交换机' }, + { value: 'router', label: '路由器' }, + { value: 'storage', label: '存储设备' }, + { value: 'other', label: '其他设备' } + ]; + + const statusOptions = [ + { value: 'running', label: '运行中' }, + { value: 'maintenance', label: '维护中' }, + { value: 'offline', label: '离线' }, + { value: 'fault', label: '故障' } + ]; + + const typeField = await DeviceField.findOne({ where: { fieldName: 'type' } }); + if (typeField) { + if (!typeField.options || typeField.options.length === 0) { + await typeField.update({ options: deviceTypeOptions }); + console.log(' 已更新 type 字段的 options'); + } else { + console.log(' type 字段 options 已存在,跳过'); + } + } else { + console.log(' type 字段不存在,跳过'); + } + + const statusField = await DeviceField.findOne({ where: { fieldName: 'status' } }); + if (statusField) { + if (!statusField.options || statusField.options.length === 0) { + await statusField.update({ options: statusOptions }); + console.log(' 已更新 status 字段的 options'); + } else { + console.log(' status 字段 options 已存在,跳过'); + } + } else { + console.log(' status 字段不存在,跳过'); + } + + console.log(' 设备字段 Options 配置迁移完成'); +} + async function migrateIdleDeviceAndBusiness() { const queryInterface = sequelize.getQueryInterface(); const dialect = sequelize.getDialect(); diff --git a/frontend/src/pages/RemoteBackupSettings.jsx b/frontend/src/pages/RemoteBackupSettings.jsx index 003ba74..9715e6d 100644 --- a/frontend/src/pages/RemoteBackupSettings.jsx +++ b/frontend/src/pages/RemoteBackupSettings.jsx @@ -296,7 +296,6 @@ const RemoteBackupSettings = () => { await form.validateFields(); const values = form.getFieldsValue(true); - console.log('[TestConnection] getFieldsValue(true):', JSON.stringify(values)); const response = await api.post('/backup/remote/test', values); if (response?.success) {