Update line_connection.ts

This commit is contained in:
nguyentrungthat 2026-01-07 16:47:58 +07:00
parent 8580f3c88a
commit 429c570688
1 changed files with 122 additions and 8 deletions

View File

@ -157,7 +157,9 @@ export default class LineConnection {
this.outputPhysicalTest = ''
this.listDeviceIos = []
}
/**
* Connect to line with socket
*/
connect(timeoutMs = 5000) {
return new Promise<void>((resolve, reject) => {
const { ip, port, lineNumber, id, stationId } = this.config
@ -304,10 +306,16 @@ export default class LineConnection {
})
}
/**
* Waiting with millisecond
*/
private sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
/**
* Write a command with socket.write
*/
async writeCommand(cmd: string | Buffer<ArrayBuffer>, userName = '') {
if (this.client.destroyed) {
console.log(`⚠️ Cannot send, line ${this.config.lineNumber} is closed`)
@ -326,6 +334,9 @@ export default class LineConnection {
this.client.write(cmd)
}
/**
* Disconnect socket with line
*/
async disconnect() {
try {
console.log('[DISCONNECT] Line', this.config.lineNumber)
@ -342,6 +353,9 @@ export default class LineConnection {
}
}
/**
* Run a scenario as DPELP, Breaking password, load ios,...
*/
async runScript(script: Scenario, userName: string) {
if (!this.client || this.client.destroyed) {
console.log('Not connected')
@ -575,7 +589,7 @@ export default class LineConnection {
// }
if (typeof step.send !== 'undefined') {
// console.log(Date.now() - now, (step?.send ?? '[ENTER]').toString())
console.log(Date.now() - now, (step?.send ?? '[ENTER]').toString())
this.outputScenario += `\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`
appendLog(
`\n---send-command---"${(step?.send ?? '[ENTER]').toString().replace(/\r/g, '\\r').replace(/\n/g, '\\n')}"---${now}---\n`,
@ -620,6 +634,9 @@ export default class LineConnection {
})
}
/**
* Reconnect socket with line
*/
public async reconnect(): Promise<boolean> {
try {
this.disconnect()
@ -632,6 +649,9 @@ export default class LineConnection {
}
}
/**
* User open CLI from front-end
*/
userOpenCLI(user: User) {
this.config.openCLI = true
this.config.userEmailOpenCLI = user.userEmail
@ -651,6 +671,9 @@ export default class LineConnection {
)
}
/**
* User close CLI from front-end
*/
userCloseCLI() {
this.config.openCLI = false
this.config.userEmailOpenCLI = ''
@ -662,6 +685,9 @@ export default class LineConnection {
})
}
/**
* Clear output buffer
*/
clearCLI() {
this.config.output = ''
this.socketIO.emit('user_clear_terminal', {
@ -671,6 +697,9 @@ export default class LineConnection {
setTimeout(() => this.writeCommand('\r\n'), 100)
}
/**
* Waiting for a expect with until catch it from output
*/
waitForExpect = async (expect: string, timeout = 60000) => {
const start = Date.now()
// console.log('[EXPECT]', expect, timeout)
@ -684,6 +713,9 @@ export default class LineConnection {
return false
}
/**
* Detect inventory data from output
*/
getInventory = () => {
const data = textfsmResults(this.outputInventory, 'show inventory')
try {
@ -714,7 +746,9 @@ export default class LineConnection {
}
}
// Gửi nhiều ký tự ESC để vào ROMMON
/**
* Gửi nhiều tự ESC đ vào ROMMON
*/
breakSpam() {
console.log('SPAM Break to line:', this.config.lineNumber)
let count = 0
@ -728,6 +762,9 @@ export default class LineConnection {
}, 1)
}
/**
* Set Baud of line
*/
async setBaud(baud: number) {
this.writeCommand('enable\r\n')
await sleep(500)
@ -743,6 +780,9 @@ export default class LineConnection {
this.writeCommand('\r\n')
}
/**
* Get content's log of line with date
*/
async getLog(date: string) {
const logDir = path.join('storage', 'system_logs')
const logFile = path
@ -759,6 +799,9 @@ export default class LineConnection {
return await fs.promises.readFile(logFile, 'utf8')
}
/**
* Detect log by call api gpt, return summary and issues
*/
async detectLogWithAI(log: string) {
try {
const payload = {
@ -812,6 +855,9 @@ export default class LineConnection {
return ''
}
/**
* Add cache to list history devices on this line
*/
async addHistory(stationId: number, lineId: number, item: HistoryItem) {
if (!item.pid || !item.sn) return
const key = `station:${stationId}:line:${lineId}:history`
@ -850,12 +896,18 @@ export default class LineConnection {
return true
}
/**
* Get list history devices
*/
async getHistory(stationId: number, lineId: number) {
const key = `station:${stationId}:line:${lineId}:history`
const items = await redis.zrange(key, 0, -1)
return items.map((i) => JSON.parse(i))
}
/**
* Handle raw log to regex error
*/
handleLogLine = (line: string) => {
try {
const parsed = classifyLog(line)
@ -865,6 +917,9 @@ export default class LineConnection {
}
}
/**
* Check raw log was regex each 5 minutes, if has error will send email report
*/
checkLog() {
const interval = setInterval(async () => {
try {
@ -902,6 +957,9 @@ export default class LineConnection {
}, 300000)
}
/**
* Render table to view error
*/
renderErrorTable(rows: ErrorRow[]): string {
if (!rows.length) {
return `<p style="color: green;">No errors detected</p>`
@ -942,6 +1000,9 @@ export default class LineConnection {
`
}
/**
* Return a body email
*/
buildEmailContent(result: TestResult): string {
const rows = mapErrorsToRows(result.errors)
const table = this.renderErrorTable(rows)
@ -956,6 +1017,9 @@ export default class LineConnection {
`
}
/**
* Update note of SN to ERP after run DPELP
*/
async updateNote(sn: string, data: DataDPELP) {
const licenses = Array.isArray(data.license)
? [...new Set(data.license)]
@ -968,6 +1032,9 @@ export default class LineConnection {
await updateNoteToERP(sn, note)
}
/**
* Starting physical test (PoE ports testing)
*/
async runPhysicalTest() {
if (this.config.runningPhysical) {
console.log('Running physical test')
@ -1002,6 +1069,9 @@ export default class LineConnection {
// }, 10000)
}
/**
* End all testing
*/
endTesting() {
this.physicalTest.done = true
this.physicalTest.resetTestedPorts()
@ -1018,6 +1088,9 @@ export default class LineConnection {
})
}
/**
* Get list PoE ports
*/
async getPorts(): Promise<string[]> {
this.writeCommand(' show power inline\r\n')
this.writeCommand(' \r\n')
@ -1040,6 +1113,9 @@ export default class LineConnection {
return [...new Set(ports)]
}
/**
* Send report after done physical test
*/
async sendReportPhysicalTest() {
const formReport = this.physicalTest.getFormReport()
await sendMessageToMail(
@ -1048,6 +1124,9 @@ export default class LineConnection {
)
}
/**
* Handle load ios for router
*/
async loadIosRouter(nameIos: string, userName: string) {
const station = await Station.find(this.config.stationId)
if (!station) return
@ -1193,6 +1272,9 @@ export default class LineConnection {
await this.sendEmailLoadIos(nameIos, startTime)
}
/**
* Handle load ios for switch
*/
async loadIosSwitch(nameIos: string, userName: string) {
const station = await Station.find(this.config.stationId)
if (!station) return
@ -1402,6 +1484,9 @@ export default class LineConnection {
await this.sendEmailLoadIos(nameIos, startTime)
}
/**
* Send mail report after load ios
*/
async sendEmailLoadIos(nameIos: string, startTime: string) {
const timeZone = process.env.TIME_ZONE || 'Australia/Sydney'
const dataFormat = momentTZ().tz(timeZone).format('YYYY/MM/DD, HH:mm:ss')
@ -1422,20 +1507,38 @@ export default class LineConnection {
)
}
/**
* Check list ios exist on flash
*/
async checkDeviceFlash() {
this.writeCommand(' enable\r\n')
this.writeCommand('show flash:\r\n')
await sleep(2000)
const ios = []
const binRegex = /^\s*\d+\s+-rwx\s+\d+\s+.*?\s+([^\s]+\.bin)\s*$/gim
const output = this.outputBuffer
const ios: string[] = []
let match
while ((match = binRegex.exec(this.outputBuffer)) !== null) {
ios.push(match[1])
const SWITCH_BIN_REGEX = /^\s*\d+\s+-rwx\s+\d+\s+.*?\s+([^\s]+\.bin)\s*$/gim
const ROUTER_BIN_REGEX = /^\s*\d+\s+(\d+)\s+.*?\s+([^\s]+\.bin)\s*$/gim
// 🔍 Detect device type
const isSwitch = output.includes('-rwx')
const regex = isSwitch ? SWITCH_BIN_REGEX : ROUTER_BIN_REGEX
// reset regex state
regex.lastIndex = 0
while ((match = regex.exec(output)) !== null) {
ios.push(isSwitch ? match[1] : match[2])
}
return ios
}
/**
* Delete File on Flash
*/
async deleteFileOnFlash(fileName: string) {
await this.writeCommand(`delete flash:${fileName}\r\n`)
await this.writeCommand(`\r\n`)
@ -1443,6 +1546,9 @@ export default class LineConnection {
await sleep(3000)
}
/**
* Upload file from flash to TFTP server
*/
async uploadFileToServerTFTP(fileName: string, server: string) {
this.config.runningScenario = 'Upload file'
await this.writeCommand(`copy flash: tftp:\r\n`)
@ -1460,7 +1566,9 @@ export default class LineConnection {
}
}
// function get list ios
/**
* Get list ios from TFTP server
*/
async getListIos() {
try {
const controller = new IosLicenseController()
@ -1472,6 +1580,9 @@ export default class LineConnection {
}
}
/**
* Get current boot ios of device
*/
async getCurrentBootIos() {
this.writeCommand('show version | include System image\r\n')
await sleep(2000)
@ -1482,6 +1593,9 @@ export default class LineConnection {
return match ? match[1] : null
}
/**
* Backup ios to TFTP, after that delete it on flash for free space
*/
async backupIos(nameIos: string) {
const station = await Station.find(this.config.stationId)
if (!station) return