fix(remote-backup): 移除调试日志并改进协议类型验证

refactor(migrations): 添加设备字段options配置迁移
This commit is contained in:
zhang1106
2026-03-23 11:09:36 +08:00
parent 66807b1660
commit f51c39284b
3 changed files with 81 additions and 36 deletions
+4 -13
View File
@@ -802,7 +802,6 @@ router.delete('/remote/targets/:id', (req, res) => {
router.post('/remote/test', async (req, res) => { router.post('/remote/test', async (req, res) => {
try { try {
const config = req.body; const config = req.body;
console.log(`[RemoteBackup] 收到连接测试请求:`, JSON.stringify(config));
if (!config.host || !config.port || !config.username) { if (!config.host || !config.port || !config.username) {
return res.status(400).json({ return res.status(400).json({
@@ -812,21 +811,13 @@ router.post('/remote/test', async (req, res) => {
} }
if (!config.protocol) { if (!config.protocol) {
const port = parseInt(config.port); return res.status(400).json({
if (port === 22 || port === 2222) { success: false,
config.protocol = 'sftp'; message: '请提供协议类型(protocol',
} 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})`);
} }
const result = await testRemoteConnection(config); const result = await testRemoteConnection(config);
console.log(`[RemoteBackup] 连接测试结果:`, JSON.stringify(result));
if (result.success) { if (result.success) {
res.json({ res.json({
+77 -22
View File
@@ -94,6 +94,11 @@ const migrations = [
name: '设备字段系统标记', name: '设备字段系统标记',
description: '为 deviceFields 表添加 isSystem 字段,标记系统字段不可删除', description: '为 deviceFields 表添加 isSystem 字段,标记系统字段不可删除',
migrate: migrateDeviceFieldsIsSystem migrate: migrateDeviceFieldsIsSystem
},
{
name: '设备字段Options配置',
description: '确保 deviceFields 表的 type 和 status 字段有正确的 options 配置',
migrate: migrateDeviceFieldsOptions
} }
]; ];
@@ -149,7 +154,7 @@ async function runMigrations() {
async function getTableColumns(tableName) { async function getTableColumns(tableName) {
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
const tableInfo = await sequelize.query( const tableInfo = await sequelize.query(
`PRAGMA table_info(${tableName})`, `PRAGMA table_info(${tableName})`,
@@ -167,7 +172,7 @@ async function getTableColumns(tableName) {
async function tableExists(tableName) { async function tableExists(tableName) {
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
const tables = await sequelize.query( const tables = await sequelize.query(
"SELECT name FROM sqlite_master WHERE type='table' AND name=?", "SELECT name FROM sqlite_master WHERE type='table' AND name=?",
@@ -185,10 +190,10 @@ async function tableExists(tableName) {
async function addColumnIfNotExists(tableName, columnName, columnDef) { async function addColumnIfNotExists(tableName, columnName, columnDef) {
const columns = await getTableColumns(tableName); const columns = await getTableColumns(tableName);
if (!columns.includes(columnName)) { if (!columns.includes(columnName)) {
const dialect = sequelize.getDialect(); 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}`
: `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`; : `ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDef}`;
await sequelize.query(sql); await sequelize.query(sql);
@@ -276,7 +281,7 @@ async function migrateConsumableLogDecouple() {
async function removeConsumableLogFK() { async function removeConsumableLogFK() {
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
const fks = await sequelize.query( const fks = await sequelize.query(
`PRAGMA foreign_key_list(consumable_logs);`, `PRAGMA foreign_key_list(consumable_logs);`,
@@ -411,7 +416,7 @@ async function migrateConsumableLogArchive() {
async function migrateSnList() { async function migrateSnList() {
const tables = ['consumables', 'consumable_records', 'consumable_logs']; const tables = ['consumables', 'consumable_records', 'consumable_logs'];
for (const table of tables) { for (const table of tables) {
if (await tableExists(table)) { if (await tableExists(table)) {
const columnDef = dbDialect === 'sqlite' ? "TEXT DEFAULT '[]'" : "JSON"; const columnDef = dbDialect === 'sqlite' ? "TEXT DEFAULT '[]'" : "JSON";
@@ -429,7 +434,7 @@ async function migrateDeviceModelField() {
} }
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
if (dialect === 'mysql') { if (dialect === 'mysql') {
await sequelize.query( await sequelize.query(
'ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL' 'ALTER TABLE devices MODIFY COLUMN model VARCHAR(255) NULL'
@@ -441,7 +446,7 @@ async function migrateDeviceModelField() {
console.log(' model_old 字段已存在,跳过迁移'); console.log(' model_old 字段已存在,跳过迁移');
return; return;
} }
await sequelize.query('ALTER TABLE devices RENAME COLUMN model TO model_old'); 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('ALTER TABLE devices ADD COLUMN model VARCHAR(255)');
await sequelize.query('UPDATE devices SET model = model_old'); await sequelize.query('UPDATE devices SET model = model_old');
@@ -452,14 +457,14 @@ async function migrateDeviceModelField() {
async function migrateDeviceFieldsConfig() { async function migrateDeviceFieldsConfig() {
const DeviceField = require('../models/DeviceField'); const DeviceField = require('../models/DeviceField');
const updates = [ const updates = [
{ fieldName: 'model', required: false }, { fieldName: 'model', required: false },
{ fieldName: 'powerConsumption', required: true }, { fieldName: 'powerConsumption', required: true },
{ fieldName: 'purchaseDate', required: false }, { fieldName: 'purchaseDate', required: false },
{ fieldName: 'warrantyExpiry', required: false }, { fieldName: 'warrantyExpiry', required: false },
]; ];
for (const update of updates) { for (const update of updates) {
const field = await DeviceField.findOne({ where: { fieldName: update.fieldName } }); const field = await DeviceField.findOne({ where: { fieldName: update.fieldName } });
if (field && field.required !== update.required) { if (field && field.required !== update.required) {
@@ -475,7 +480,7 @@ async function migrateDeviceFieldsConfig() {
async function migrateDeviceFieldsNullable() { async function migrateDeviceFieldsNullable() {
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
if (dialect === 'mysql') { if (dialect === 'mysql') {
const alterCommands = [ const alterCommands = [
"ALTER TABLE devices MODIFY COLUMN name VARCHAR(255) NULL", "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 powerConsumption FLOAT NULL",
"ALTER TABLE devices MODIFY COLUMN customFields JSON NULL" "ALTER TABLE devices MODIFY COLUMN customFields JSON NULL"
]; ];
for (const sql of alterCommands) { for (const sql of alterCommands) {
try { try {
await sequelize.query(sql); await sequelize.query(sql);
@@ -499,21 +504,21 @@ async function migrateDeviceFieldsNullable() {
} }
} }
console.log(' devices 表字段已改为可空'); console.log(' devices 表字段已改为可空');
} else if (dialect === 'sqlite') { } else if (dialect === 'sqlite') {
const columns = await getTableColumns('devices'); const columns = await getTableColumns('devices');
const hasNullableFlag = columns.includes('_nullable_migration_done'); const hasNullableFlag = columns.includes('_nullable_migration_done');
if (hasNullableFlag) { if (hasNullableFlag) {
console.log(' 已完成可空迁移,跳过'); console.log(' 已完成可空迁移,跳过');
return; return;
} }
await sequelize.query('PRAGMA foreign_keys = OFF'); await sequelize.query('PRAGMA foreign_keys = OFF');
try { try {
await sequelize.query('DROP TABLE IF EXISTS devices_new'); await sequelize.query('DROP TABLE IF EXISTS devices_new');
await sequelize.query(` await sequelize.query(`
CREATE TABLE devices_new ( CREATE TABLE devices_new (
deviceId VARCHAR(255) PRIMARY KEY NOT NULL UNIQUE, deviceId VARCHAR(255) PRIMARY KEY NOT NULL UNIQUE,
@@ -536,28 +541,28 @@ async function migrateDeviceFieldsNullable() {
_nullable_migration_done INTEGER DEFAULT 1 _nullable_migration_done INTEGER DEFAULT 1
) )
`); `);
await sequelize.query(` await sequelize.query(`
INSERT INTO devices_new ( INSERT INTO devices_new (
deviceId, name, type, model, serialNumber, rackId, position, height, deviceId, name, type, model, serialNumber, rackId, position, height,
powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress, powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress,
description, customFields, createdAt, updatedAt description, customFields, createdAt, updatedAt
) )
SELECT SELECT
deviceId, name, type, model, serialNumber, rackId, position, height, deviceId, name, type, model, serialNumber, rackId, position, height,
powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress, powerConsumption, status, purchaseDate, warrantyExpiry, ipAddress,
description, customFields, createdAt, updatedAt description, customFields, createdAt, updatedAt
FROM devices FROM devices
`); `);
await sequelize.query('DROP TABLE devices'); await sequelize.query('DROP TABLE devices');
await sequelize.query('ALTER TABLE devices_new RENAME TO 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_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_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_rackId ON devices(rackId)');
await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_name ON devices(name)'); await sequelize.query('CREATE INDEX IF NOT EXISTS idx_devices_name ON devices(name)');
console.log(' devices 表字段已改为可空'); console.log(' devices 表字段已改为可空');
} finally { } finally {
await sequelize.query('PRAGMA foreign_keys = ON'); await sequelize.query('PRAGMA foreign_keys = ON');
@@ -632,6 +637,56 @@ async function migrateDeviceFieldsIsSystem() {
console.log(' 设备字段系统标记迁移完成'); 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() { async function migrateIdleDeviceAndBusiness() {
const queryInterface = sequelize.getQueryInterface(); const queryInterface = sequelize.getQueryInterface();
const dialect = sequelize.getDialect(); const dialect = sequelize.getDialect();
@@ -296,7 +296,6 @@ const RemoteBackupSettings = () => {
await form.validateFields(); await form.validateFields();
const values = form.getFieldsValue(true); const values = form.getFieldsValue(true);
console.log('[TestConnection] getFieldsValue(true):', JSON.stringify(values));
const response = await api.post('/backup/remote/test', values); const response = await api.post('/backup/remote/test', values);
if (response?.success) { if (response?.success) {