Update select license

This commit is contained in:
nguyentrungthat 2026-01-12 16:33:07 +07:00
parent 5586f8f930
commit a9dce43ab2
5 changed files with 263 additions and 33 deletions

View File

@ -1225,18 +1225,18 @@ export default class LineConnection {
const timeZone = process.env.TIME_ZONE || 'Australia/Sydney' const timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
const body = ` const body = `
Load IOS Report<br/> Load License Report<br/>
<br/> <br/>
Station : <b>${this.config.stationName}</b><br/> Station : <b>${this.config.stationName}</b><br/>
Line : <b>${this.config.lineNumber}</b><br/> Line : <b>${this.config.lineNumber}</b><br/>
License : <b>${nameLicense}</b> <br/> License : <b>${nameLicense}</b> <br/>
Started At : ${startTime}<br/> Started At : ${startTime}<br/>
Finished At : ${dataFormat}<br/> Finished At : ${dataFormat}<br/>
<br/> <br/>
`.trim() `.trim()
await sendMessageToMail( 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 body
) )
} }
@ -1370,7 +1370,7 @@ export default class LineConnection {
* Handle load License for switch * Handle load License for switch
* Assumes traditional licensing (PAK/file-based) via TFTP * 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) const station = await Station.find(this.config.stationId)
if (!station) return if (!station) return
@ -1384,21 +1384,22 @@ export default class LineConnection {
const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
const body = buildBody( const body = buildBody(
'ROUTER_IOS', 'SWITCH_LICENSE',
tftpIp, tftpIp,
licenseFileName, licenseFileName,
`${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, `${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`,
`${station?.gateway ? station?.gateway : '0.0.0.0'}`, `${station?.gateway ? station?.gateway : '0.0.0.0'}`,
this.listDeviceIos this.listDeviceIos,
portName
) )
const script = { const script = {
id: 0, // Hoặc ID khác tuỳ logic DB id: 0,
isReboot: true, // License thường cần reboot isReboot: false,
sendResult: false, sendResult: false,
send_result: false, send_result: false,
title: 'Load License Switch', 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), body: JSON.stringify(body),
} }
@ -1409,7 +1410,7 @@ export default class LineConnection {
/** /**
* Handle load License for Router * 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) const station = await Station.find(this.config.stationId)
if (!station) return if (!station) return
@ -1417,24 +1418,22 @@ export default class LineConnection {
const tftpIp = station?.tftp_ip || '172.16.7.69' const tftpIp = station?.tftp_ip || '172.16.7.69'
const [a, b] = network.split('.').map(Number) 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 timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss') const startTime = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
const body = buildBody( const body = buildBody(
'ROUTER_IOS', 'ROUTER_LICENSE',
tftpIp, tftpIp,
licenseFileName, licenseFileName,
`${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`, `${a}.${b}.100.${this.config.id < 254 ? this.config.id : 254 - this.config.id}`,
`${station?.gateway ? station?.gateway : '0.0.0.0'}`, `${station?.gateway ? station?.gateway : '0.0.0.0'}`,
this.listDeviceIos this.listDeviceIos,
portName
) )
const script = { const script = {
id: 0, id: 0,
isReboot: true, isReboot: false,
sendResult: false, sendResult: false,
send_result: false, send_result: false,
title: 'Load License Router', title: 'Load License Router',

View File

@ -684,7 +684,8 @@ export function buildBody(
fileName: string, fileName: string,
address: string, address: string,
gateway: string, gateway: string,
listDeviceIos: string[] listDeviceIos: string[],
portName?: string
) { ) {
switch (type) { switch (type) {
/* ================= ROUTER LOAD IOS ================= */ /* ================= ROUTER LOAD IOS ================= */
@ -769,7 +770,14 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `show inventory`, send: `show license`,
delay: '1',
repeat: '1',
note: 'Verify license status',
},
{
expect: '#',
send: ` show inventory`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: '', note: '',
@ -989,7 +997,7 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `show version`, send: ` show version`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Verify version info', note: 'Verify version info',
@ -1022,7 +1030,7 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `interface vlan 1`, send: `interface ${portName ? portName : 'vlan 1'}`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Select Interface Vlan 1', note: 'Select Interface Vlan 1',
@ -1062,6 +1070,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'End config', note: 'End config',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `license install tftp://${tftpIp}/License/${fileName}`, send: `license install tftp://${tftpIp}/License/${fileName}`,
@ -1069,6 +1084,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'Install license', note: 'Install license',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `write memory`, send: `write memory`,
@ -1076,6 +1098,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'Save config', note: 'Save config',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `reload`, send: `reload`,
@ -1129,7 +1158,7 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `show version`, send: ` show version`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Verify version info', note: 'Verify version info',
@ -1162,7 +1191,7 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `interface GigabitEthernet0/0`, send: `interface ${portName ? portName : 'GigabitEthernet0/0'}`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Select management interface', note: 'Select management interface',
@ -1202,6 +1231,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'End config', note: 'End config',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `license install tftp://${tftpIp}/License/${fileName}`, send: `license install tftp://${tftpIp}/License/${fileName}`,
@ -1209,6 +1245,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'Install license', note: 'Install license',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `write memory`, send: `write memory`,
@ -1216,6 +1259,13 @@ export function buildBody(
repeat: '1', repeat: '1',
note: 'Save config', note: 'Save config',
}, },
{
expect: '',
send: ``,
delay: '1',
repeat: '1',
note: '',
},
{ {
expect: '#', expect: '#',
send: `reload`, send: `reload`,
@ -1224,8 +1274,8 @@ export function buildBody(
note: 'Reload router', note: 'Reload router',
}, },
{ {
expect: '', // Router thường hỏi câu này expect: '',
send: ``, // Enter confirm send: ``,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Confirm reload', note: 'Confirm reload',
@ -1269,7 +1319,7 @@ export function buildBody(
}, },
{ {
expect: '#', expect: '#',
send: `show version`, send: ` show version`,
delay: '1', delay: '1',
repeat: '1', repeat: '1',
note: 'Verify version info', note: 'Verify version info',

View File

@ -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, () => { socketServer.listen(SOCKET_IO_PORT, () => {

View File

@ -82,6 +82,7 @@ const StationSetting = ({
form.setFieldValue("apc_2_password", dataStation.apc_2_password); form.setFieldValue("apc_2_password", dataStation.apc_2_password);
form.setFieldValue("switch_control_ip", dataStation.switch_control_ip); form.setFieldValue("switch_control_ip", dataStation.switch_control_ip);
form.setFieldValue("send_wiki", dataStation?.send_wiki); form.setFieldValue("send_wiki", dataStation?.send_wiki);
// form.setFieldValue("is_active", dataStation?.is_active);
form.setFieldValue( form.setFieldValue(
"switch_control_port", "switch_control_port",
dataStation.switch_control_port dataStation.switch_control_port
@ -402,6 +403,16 @@ const StationSetting = ({
form.setFieldValue("send_wiki", event.currentTarget.checked) form.setFieldValue("send_wiki", event.currentTarget.checked)
} }
/> />
{/* <Checkbox
c={"yellow"}
color="yellow"
ml={"12px"}
label="Active Station"
checked={form.values.is_active}
onChange={(event) =>
form.setFieldValue("is_active", event.currentTarget.checked)
}
/> */}
</div> </div>
} }
size={"90%"} size={"90%"}

View File

@ -4,6 +4,7 @@ import {
Modal, Modal,
ScrollArea, ScrollArea,
Table, Table,
Tabs,
Text, Text,
TextInput, TextInput,
} from "@mantine/core"; } from "@mantine/core";
@ -28,6 +29,9 @@ const ModalSelectLicense = ({
line: TLine | undefined; line: TLine | undefined;
}) => { }) => {
const [inputSearch, setInputSearch] = useState<string>(""); const [inputSearch, setInputSearch] = useState<string>("");
const [inputPort, setInputPort] = useState<string>("GigabitEthernet0/0");
const [licenseName, setLicenseName] = useState<string>("");
const [modalConfirm, setModalConfirm] = useState<boolean>(false);
const filterLicense = () => { const filterLicense = () => {
return listLicense.filter((ios) => return listLicense.filter((ios) =>
@ -103,9 +107,9 @@ const ModalSelectLicense = ({
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{filterLicense()?.map((ios, i) => ( {filterLicense()?.map((lic, i) => (
<Table.Tr key={i}> <Table.Tr key={i}>
<Table.Td>{ios || ""}</Table.Td> <Table.Td>{lic || ""}</Table.Td>
<Table.Td <Table.Td
style={{ style={{
textAlign: "center", textAlign: "center",
@ -118,12 +122,14 @@ const ModalSelectLicense = ({
size="sm" size="sm"
leftSection={<IconPlayerPlay size={16} />} leftSection={<IconPlayerPlay size={16} />}
onClick={() => { onClick={() => {
socket?.emit("load_ios_switch", { // socket?.emit("load_ios_switch", {
stationId: Number(station?.id), // stationId: Number(station?.id),
lineId: Number(line?.id), // lineId: Number(line?.id),
iosName: ios, // iosName: ios,
}); // });
close(); // close();
setLicenseName(lic);
setModalConfirm(true);
}} }}
> >
Run Run
@ -134,6 +140,144 @@ const ModalSelectLicense = ({
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</ScrollArea> </ScrollArea>
<Modal
style={{ position: "absolute", left: 0 }}
centered
opened={modalConfirm}
onClose={() => {
setModalConfirm(false);
setLicenseName("");
setInputPort("GigabitEthernet0/0");
}}
title={
<Text fz={"lg"} fw={"bolder"}>
Confirm
</Text>
}
size="lg"
>
<Flex
align={"center"}
justify={"center"}
pb={"xs"}
style={{ borderBottom: "1px solid #ccc" }}
>
<Text fw={"bold"} fz={"h4"}>
{licenseName}
</Text>
</Flex>
<Tabs defaultValue="router" h={"100%"}>
<Tabs.List>
<Tabs.Tab
w={"50%"}
value="router"
onClick={() => {
setInputPort("GigabitEthernet0/0");
}}
>
Router
</Tabs.Tab>
<Tabs.Tab
w={"50%"}
value="switch"
onClick={() => {
setInputPort("vlan 1");
}}
>
Switch
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="router" w={"100%"}>
<Flex align={"center"} justify={"center"} p={"md"}>
<TextInput
style={{ width: "350px" }}
placeholder="Port name"
value={inputPort}
onChange={(event) => setInputPort(event.currentTarget.value)}
rightSection={
inputPort ? (
<IconX
size={14}
style={{ cursor: "pointer" }}
onClick={() => setInputPort("")}
/>
) : null
}
rightSectionPointerEvents="auto"
size="md"
/>
</Flex>
<Flex align={"center"} justify={"center"}>
<Button
style={{ width: "100px" }}
variant="filled"
color="green"
size="sm"
leftSection={<IconPlayerPlay size={16} />}
onClick={() => {
socket?.emit("load_license_router", {
stationId: Number(station?.id),
lineId: Number(line?.id),
licenseName: licenseName,
portName: inputPort,
});
setModalConfirm(false);
setLicenseName("");
setInputPort("GigabitEthernet0/0");
close();
}}
>
Run
</Button>
</Flex>
</Tabs.Panel>
<Tabs.Panel value="switch" w={"100%"}>
<Flex align={"center"} justify={"center"} p={"md"}>
<TextInput
style={{ width: "350px" }}
placeholder="Port name"
value={inputPort}
onChange={(event) => setInputPort(event.currentTarget.value)}
rightSection={
inputPort ? (
<IconX
size={14}
style={{ cursor: "pointer" }}
onClick={() => setInputPort("")}
/>
) : null
}
rightSectionPointerEvents="auto"
size="md"
/>
</Flex>
<Flex align={"center"} justify={"center"}>
<Button
style={{ width: "100px" }}
variant="filled"
color="green"
size="sm"
leftSection={<IconPlayerPlay size={16} />}
onClick={() => {
socket?.emit("load_license_switch", {
stationId: Number(station?.id),
lineId: Number(line?.id),
licenseName: licenseName,
portName: inputPort,
});
setModalConfirm(false);
setLicenseName("");
setInputPort("GigabitEthernet0/0");
close();
}}
>
Run
</Button>
</Flex>
</Tabs.Panel>
</Tabs>
</Modal>
</Modal> </Modal>
); );
}; };