From a9dce43ab27140053121457aa3a999ad1b02ff5c Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:33:07 +0700 Subject: [PATCH] Update select license --- BACKEND/app/services/line_connection.ts | 31 ++-- BACKEND/app/ultils/helper.ts | 68 +++++++- BACKEND/providers/socket_io_provider.ts | 26 +++ FRONTEND/src/components/FormAddEdit.tsx | 11 ++ .../components/Modal/ModalSelectLicense.tsx | 160 +++++++++++++++++- 5 files changed, 263 insertions(+), 33 deletions(-) diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index 8b98131..bf0b1de 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -1225,18 +1225,18 @@ export default class LineConnection { const timeZone = process.env.TIME_ZONE || 'Australia/Sydney' const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const body = ` - Load IOS Report
+ Load License Report
────────────────────────────────
Station : ${this.config.stationName}
Line : ${this.config.lineNumber}
- License : ${nameLicense}
+ License : ${nameLicense}
Started At : ${startTime}
Finished At : ${dataFormat}
────────────────────────────────
`.trim() await sendMessageToMail( - `[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load IOS Report`, + `[ATC] - [${this.config.stationName} - Line: ${this.config.lineNumber}] - Load License Report`, body ) } @@ -1370,7 +1370,7 @@ export default class LineConnection { * Handle load License for switch * Assumes traditional licensing (PAK/file-based) via TFTP */ - async loadLicenseSwitch(licenseFileName: string, userName: string) { + async loadLicenseSwitch(licenseFileName: string, userName: string, portName: string) { const station = await Station.find(this.config.stationId) if (!station) return @@ -1384,21 +1384,22 @@ export default class LineConnection { const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const body = buildBody( - 'ROUTER_IOS', + 'SWITCH_LICENSE', tftpIp, licenseFileName, `${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, `${station?.gateway ? station?.gateway : '0.0.0.0'}`, - this.listDeviceIos + this.listDeviceIos, + portName ) const script = { - id: 0, // Hoặc ID khác tuỳ logic DB - isReboot: true, // License thường cần reboot + id: 0, + isReboot: false, sendResult: false, send_result: false, title: 'Load License Switch', - timeout: 1000, // Tăng timeout nếu cần vì lệnh install có thể lâu + timeout: 1000, body: JSON.stringify(body), } @@ -1409,7 +1410,7 @@ export default class LineConnection { /** * Handle load License for Router */ - async loadLicenseRouter(licenseFileName: string, userName: string) { + async loadLicenseRouter(licenseFileName: string, userName: string, portName: string) { const station = await Station.find(this.config.stationId) if (!station) return @@ -1417,24 +1418,22 @@ export default class LineConnection { const tftpIp = station?.tftp_ip || '172.16.7.69' const [a, b] = network.split('.').map(Number) - // Tên Interface dùng để load (Cần sửa nếu router dùng 0/0/0 hoặc tên khác) - const mgmtInterface = 'GigabitEthernet0/0' - const timeZone = process.env.TIME_ZONE || 'Australia/Sydney' const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const body = buildBody( - 'ROUTER_IOS', + 'ROUTER_LICENSE', tftpIp, licenseFileName, `${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, `${station?.gateway ? station?.gateway : '0.0.0.0'}`, - this.listDeviceIos + this.listDeviceIos, + portName ) const script = { id: 0, - isReboot: true, + isReboot: false, sendResult: false, send_result: false, title: 'Load License Router', diff --git a/BACKEND/app/ultils/helper.ts b/BACKEND/app/ultils/helper.ts index 9aee0c6..fc56b93 100644 --- a/BACKEND/app/ultils/helper.ts +++ b/BACKEND/app/ultils/helper.ts @@ -684,7 +684,8 @@ export function buildBody( fileName: string, address: string, gateway: string, - listDeviceIos: string[] + listDeviceIos: string[], + portName?: string ) { switch (type) { /* ================= ROUTER LOAD IOS ================= */ @@ -769,7 +770,14 @@ export function buildBody( }, { expect: '#', - send: `show inventory`, + send: `show license`, + delay: '1', + repeat: '1', + note: 'Verify license status', + }, + { + expect: '#', + send: ` show inventory`, delay: '1', repeat: '1', note: '', @@ -989,7 +997,7 @@ export function buildBody( }, { expect: '#', - send: `show version`, + send: ` show version`, delay: '1', repeat: '1', note: 'Verify version info', @@ -1022,7 +1030,7 @@ export function buildBody( }, { expect: '#', - send: `interface vlan 1`, + send: `interface ${portName ? portName : 'vlan 1'}`, delay: '1', repeat: '1', note: 'Select Interface Vlan 1', @@ -1062,6 +1070,13 @@ export function buildBody( repeat: '1', note: 'End config', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `license install tftp://${tftpIp}/License/${fileName}`, @@ -1069,6 +1084,13 @@ export function buildBody( repeat: '1', note: 'Install license', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `write memory`, @@ -1076,6 +1098,13 @@ export function buildBody( repeat: '1', note: 'Save config', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `reload`, @@ -1129,7 +1158,7 @@ export function buildBody( }, { expect: '#', - send: `show version`, + send: ` show version`, delay: '1', repeat: '1', note: 'Verify version info', @@ -1162,7 +1191,7 @@ export function buildBody( }, { expect: '#', - send: `interface GigabitEthernet0/0`, + send: `interface ${portName ? portName : 'GigabitEthernet0/0'}`, delay: '1', repeat: '1', note: 'Select management interface', @@ -1202,6 +1231,13 @@ export function buildBody( repeat: '1', note: 'End config', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `license install tftp://${tftpIp}/License/${fileName}`, @@ -1209,6 +1245,13 @@ export function buildBody( repeat: '1', note: 'Install license', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `write memory`, @@ -1216,6 +1259,13 @@ export function buildBody( repeat: '1', note: 'Save config', }, + { + expect: '', + send: ``, + delay: '1', + repeat: '1', + note: '', + }, { expect: '#', send: `reload`, @@ -1224,8 +1274,8 @@ export function buildBody( note: 'Reload router', }, { - expect: '', // Router thường hỏi câu này - send: ``, // Enter confirm + expect: '', + send: ``, delay: '1', repeat: '1', note: 'Confirm reload', @@ -1269,7 +1319,7 @@ export function buildBody( }, { expect: '#', - send: `show version`, + send: ` show version`, delay: '1', repeat: '1', note: 'Verify version info', diff --git a/BACKEND/providers/socket_io_provider.ts b/BACKEND/providers/socket_io_provider.ts index 1da7b13..0d8f71a 100644 --- a/BACKEND/providers/socket_io_provider.ts +++ b/BACKEND/providers/socket_io_provider.ts @@ -705,6 +705,32 @@ export class WebSocketIo { {} ) }) + + socket.on('load_license_router', async (data) => { + const { stationId, lineId, licenseName, portName } = data + await this.handleLineOperation( + io, + stationId, + [lineId], + async (lineCon) => { + lineCon.loadLicenseRouter(licenseName, userName, portName) + }, + {} + ) + }) + + socket.on('load_license_switch', async (data) => { + const { stationId, lineId, licenseName, portName } = data + await this.handleLineOperation( + io, + stationId, + [lineId], + async (lineCon) => { + lineCon.loadLicenseSwitch(licenseName, userName, portName) + }, + {} + ) + }) }) socketServer.listen(SOCKET_IO_PORT, () => { diff --git a/FRONTEND/src/components/FormAddEdit.tsx b/FRONTEND/src/components/FormAddEdit.tsx index 757e0c3..e6ac87f 100644 --- a/FRONTEND/src/components/FormAddEdit.tsx +++ b/FRONTEND/src/components/FormAddEdit.tsx @@ -82,6 +82,7 @@ const StationSetting = ({ form.setFieldValue("apc_2_password", dataStation.apc_2_password); form.setFieldValue("switch_control_ip", dataStation.switch_control_ip); form.setFieldValue("send_wiki", dataStation?.send_wiki); + // form.setFieldValue("is_active", dataStation?.is_active); form.setFieldValue( "switch_control_port", dataStation.switch_control_port @@ -402,6 +403,16 @@ const StationSetting = ({ form.setFieldValue("send_wiki", event.currentTarget.checked) } /> + {/* + form.setFieldValue("is_active", event.currentTarget.checked) + } + /> */} } size={"90%"} diff --git a/FRONTEND/src/components/Modal/ModalSelectLicense.tsx b/FRONTEND/src/components/Modal/ModalSelectLicense.tsx index 00b7283..05d7866 100644 --- a/FRONTEND/src/components/Modal/ModalSelectLicense.tsx +++ b/FRONTEND/src/components/Modal/ModalSelectLicense.tsx @@ -4,6 +4,7 @@ import { Modal, ScrollArea, Table, + Tabs, Text, TextInput, } from "@mantine/core"; @@ -28,6 +29,9 @@ const ModalSelectLicense = ({ line: TLine | undefined; }) => { const [inputSearch, setInputSearch] = useState(""); + const [inputPort, setInputPort] = useState("GigabitEthernet0/0"); + const [licenseName, setLicenseName] = useState(""); + const [modalConfirm, setModalConfirm] = useState(false); const filterLicense = () => { return listLicense.filter((ios) => @@ -103,9 +107,9 @@ const ModalSelectLicense = ({ - {filterLicense()?.map((ios, i) => ( + {filterLicense()?.map((lic, i) => ( - {ios || ""} + {lic || ""} } onClick={() => { - socket?.emit("load_ios_switch", { - stationId: Number(station?.id), - lineId: Number(line?.id), - iosName: ios, - }); - close(); + // socket?.emit("load_ios_switch", { + // stationId: Number(station?.id), + // lineId: Number(line?.id), + // iosName: ios, + // }); + // close(); + setLicenseName(lic); + setModalConfirm(true); }} > Run @@ -134,6 +140,144 @@ const ModalSelectLicense = ({ + + { + setModalConfirm(false); + setLicenseName(""); + setInputPort("GigabitEthernet0/0"); + }} + title={ + + Confirm + + } + size="lg" + > + + + {licenseName} + + + + + { + setInputPort("GigabitEthernet0/0"); + }} + > + Router + + { + setInputPort("vlan 1"); + }} + > + Switch + + + + + setInputPort(event.currentTarget.value)} + rightSection={ + inputPort ? ( + setInputPort("")} + /> + ) : null + } + rightSectionPointerEvents="auto" + size="md" + /> + + + + + + + + setInputPort(event.currentTarget.value)} + rightSection={ + inputPort ? ( + setInputPort("")} + /> + ) : null + } + rightSectionPointerEvents="auto" + size="md" + /> + + + + + + + ); };