Update
This commit is contained in:
parent
0a0dd559f0
commit
cbc8397ea8
|
|
@ -23,3 +23,5 @@ yarn-error.log
|
||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
storage/system_logs
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,20 @@ import User from '../models/user.js'
|
||||||
export default class AuthController {
|
export default class AuthController {
|
||||||
// Đăng ký
|
// Đăng ký
|
||||||
async register({ request, response }: HttpContext) {
|
async register({ request, response }: HttpContext) {
|
||||||
const data = request.only(['email', 'password', 'full_name'])
|
try {
|
||||||
const user = await User.create(data)
|
const data = request.only(['email', 'password', 'full_name'])
|
||||||
return response.json({ message: 'User created', user })
|
|
||||||
|
const user = await User.query().where('email', data.email).first()
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return response.status(401).json({ status: false, message: 'Email is exist' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = await User.create(data)
|
||||||
|
return response.json({ status: true, message: 'User created', user: newUser })
|
||||||
|
} catch (error) {
|
||||||
|
return response.status(401).json({ status: false, message: 'Invalid credentials' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Đăng nhập
|
// Đăng nhập
|
||||||
|
|
@ -24,11 +35,9 @@ export default class AuthController {
|
||||||
return response.status(401).json({ message: 'Invalid email or password' })
|
return response.status(401).json({ message: 'Invalid email or password' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Nếu dùng token thủ công:
|
|
||||||
const token = Math.random().toString(36).substring(2) // hoặc JWT nếu bạn cài auth
|
|
||||||
return response.json({
|
return response.json({
|
||||||
message: 'Login successful',
|
message: 'Login successful',
|
||||||
user: { id: user.id, email: user.email, token },
|
user: { id: user.id, email: user.email, fullName: user.fullName },
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
return response.status(401).json({ message: 'Invalid credentials' })
|
return response.status(401).json({ message: 'Invalid credentials' })
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import Scenario from '#models/scenario'
|
import Scenario from '#models/scenario'
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
import { searchRequest } from '../utils/hasPaginationRequest.js'
|
|
||||||
import db from '@adonisjs/lucid/services/db'
|
import db from '@adonisjs/lucid/services/db'
|
||||||
import UserScenarios from '#models/user_scenario'
|
|
||||||
|
|
||||||
export default class ScenariosController {
|
export default class ScenariosController {
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,7 +34,6 @@ export default class ScenariosController {
|
||||||
async create({ request, response, auth }: HttpContext) {
|
async create({ request, response, auth }: HttpContext) {
|
||||||
try {
|
try {
|
||||||
const payload = await request.all()
|
const payload = await request.all()
|
||||||
|
|
||||||
const trx = await db.transaction()
|
const trx = await db.transaction()
|
||||||
try {
|
try {
|
||||||
const scenario = await Scenario.create(
|
const scenario = await Scenario.create(
|
||||||
|
|
@ -44,7 +41,7 @@ export default class ScenariosController {
|
||||||
title: payload.title.trim(),
|
title: payload.title.trim(),
|
||||||
body: JSON.stringify(payload.body),
|
body: JSON.stringify(payload.body),
|
||||||
timeout: payload.timeout,
|
timeout: payload.timeout,
|
||||||
isReboot: payload.is_reboot,
|
isReboot: payload.isReboot,
|
||||||
},
|
},
|
||||||
{ client: trx }
|
{ client: trx }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import net from 'node:net'
|
import net from 'node:net'
|
||||||
import { cleanData, sleep } from '../ultils/helper.js'
|
import { appendLog, cleanData, sleep } from '../ultils/helper.js'
|
||||||
import Scenario from '#models/scenario'
|
import Scenario from '#models/scenario'
|
||||||
|
|
||||||
interface LineConfig {
|
interface LineConfig {
|
||||||
|
|
@ -11,6 +11,14 @@ interface LineConfig {
|
||||||
apcName?: string
|
apcName?: string
|
||||||
output: string
|
output: string
|
||||||
status: string
|
status: string
|
||||||
|
openCLI: boolean
|
||||||
|
userEmailOpenCLI: string
|
||||||
|
userOpenCLI: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
userEmail: string
|
||||||
|
userName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LineConnection {
|
export default class LineConnection {
|
||||||
|
|
@ -77,6 +85,7 @@ export default class LineConnection {
|
||||||
lineId: id,
|
lineId: id,
|
||||||
data: message,
|
data: message,
|
||||||
})
|
})
|
||||||
|
appendLog(cleanData(message), this.config.stationId, this.config.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.client.on('error', (err) => {
|
this.client.on('error', (err) => {
|
||||||
|
|
@ -149,6 +158,11 @@ export default class LineConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isRunningScript = true
|
this.isRunningScript = true
|
||||||
|
appendLog(
|
||||||
|
`\n\n---start-scenarios---${Date.now()}---\n---scenario---${script?.title}---${Date.now()}---\n`,
|
||||||
|
this.config.stationId,
|
||||||
|
this.config.id
|
||||||
|
)
|
||||||
const steps = typeof script?.body === 'string' ? JSON.parse(script?.body) : []
|
const steps = typeof script?.body === 'string' ? JSON.parse(script?.body) : []
|
||||||
let stepIndex = 0
|
let stepIndex = 0
|
||||||
|
|
||||||
|
|
@ -156,6 +170,13 @@ export default class LineConnection {
|
||||||
const timeoutTimer = setTimeout(() => {
|
const timeoutTimer = setTimeout(() => {
|
||||||
this.isRunningScript = false
|
this.isRunningScript = false
|
||||||
this.outputBuffer = ''
|
this.outputBuffer = ''
|
||||||
|
this.config.output += 'Timeout run scenario'
|
||||||
|
this.socketIO.emit('line_output', {
|
||||||
|
stationId: this.config.stationId,
|
||||||
|
lineId: this.config.id,
|
||||||
|
data: 'Timeout run scenario',
|
||||||
|
})
|
||||||
|
appendLog(`\n---end-scenarios---${Date.now()}---\n`, this.config.stationId, this.config.id)
|
||||||
// reject(new Error('Script timeout'))
|
// reject(new Error('Script timeout'))
|
||||||
}, script.timeout || 300000)
|
}, script.timeout || 300000)
|
||||||
|
|
||||||
|
|
@ -164,11 +185,21 @@ export default class LineConnection {
|
||||||
clearTimeout(timeoutTimer)
|
clearTimeout(timeoutTimer)
|
||||||
this.isRunningScript = false
|
this.isRunningScript = false
|
||||||
this.outputBuffer = ''
|
this.outputBuffer = ''
|
||||||
|
appendLog(
|
||||||
|
`\n---end-scenarios---${Date.now()}---\n`,
|
||||||
|
this.config.stationId,
|
||||||
|
this.config.id
|
||||||
|
)
|
||||||
resolve(true)
|
resolve(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const step = steps[index]
|
const step = steps[index]
|
||||||
|
appendLog(
|
||||||
|
`\n---send-command---"${step?.send ?? ''}"---${Date.now()}---\n`,
|
||||||
|
this.config.stationId,
|
||||||
|
this.config.id
|
||||||
|
)
|
||||||
let repeatCount = Number(step.repeat) || 1
|
let repeatCount = Number(step.repeat) || 1
|
||||||
const sendCommand = () => {
|
const sendCommand = () => {
|
||||||
if (repeatCount <= 0) {
|
if (repeatCount <= 0) {
|
||||||
|
|
@ -203,4 +234,27 @@ export default class LineConnection {
|
||||||
runStep(stepIndex)
|
runStep(stepIndex)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userOpenCLI(user: User) {
|
||||||
|
this.config.openCLI = true
|
||||||
|
this.config.userEmailOpenCLI = user.userEmail
|
||||||
|
this.config.userOpenCLI = user.userName
|
||||||
|
this.socketIO.emit('user_open_cli', {
|
||||||
|
stationId: this.config.stationId,
|
||||||
|
lineId: this.config.id,
|
||||||
|
userEmailOpenCLI: user.userEmail,
|
||||||
|
userOpenCLI: user.userName,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
userCloseCLI() {
|
||||||
|
this.config.openCLI = false
|
||||||
|
this.config.userEmailOpenCLI = ''
|
||||||
|
this.config.userOpenCLI = ''
|
||||||
|
this.socketIO.emit('user_close_cli', {
|
||||||
|
stationId: this.config.stationId,
|
||||||
|
lineId: this.config.id,
|
||||||
|
userEmailOpenCLI: '',
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to clean up unwanted characters from the output data.
|
* Function to clean up unwanted characters from the output data.
|
||||||
* @param {string} data - The raw data to be cleaned.
|
* @param {string} data - The raw data to be cleaned.
|
||||||
|
|
@ -16,3 +19,20 @@ export const cleanData = (data: string) => {
|
||||||
export function sleep(ms: number) {
|
export function sleep(ms: number) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function appendLog(output: string, stationId: number, lineId: number) {
|
||||||
|
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '') // YYYYMMDD
|
||||||
|
const logDir = path.join('storage', 'system_logs')
|
||||||
|
const logFile = path.join(logDir, `${date}-Station_${stationId}-Line_${lineId}.log`)
|
||||||
|
|
||||||
|
// Ensure folder exists
|
||||||
|
if (!fs.existsSync(logDir)) {
|
||||||
|
fs.mkdirSync(logDir, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.appendFile(logFile, output, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('❌ Failed to write log:', err.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default class extends BaseSchema {
|
||||||
table.string('title').notNullable()
|
table.string('title').notNullable()
|
||||||
table.text('body').notNullable()
|
table.text('body').notNullable()
|
||||||
table.integer('timeout').notNullable()
|
table.integer('timeout').notNullable()
|
||||||
table.boolean('isReboot').defaultTo(false)
|
table.boolean('is_reboot').defaultTo(false)
|
||||||
table.timestamps()
|
table.timestamps()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ export class WebSocketIo {
|
||||||
stationMap: Map<number, Station> = new Map()
|
stationMap: Map<number, Station> = new Map()
|
||||||
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
lineMap: Map<number, LineConnection> = new Map() // key = lineId
|
||||||
lineConnecting: number[] = [] // key = lineId
|
lineConnecting: number[] = [] // key = lineId
|
||||||
|
userConnecting: Map<number, { userId: number; userName: string }> = new Map()
|
||||||
|
|
||||||
constructor(protected app: ApplicationService) {}
|
constructor(protected app: ApplicationService) {}
|
||||||
|
|
||||||
|
|
@ -70,8 +71,14 @@ export class WebSocketIo {
|
||||||
})
|
})
|
||||||
|
|
||||||
io.on('connection', (socket: CustomSocket) => {
|
io.on('connection', (socket: CustomSocket) => {
|
||||||
|
const { userId, userName } = socket.handshake.auth
|
||||||
console.log('Socket connected:', socket.id)
|
console.log('Socket connected:', socket.id)
|
||||||
socket.connectionTime = new Date()
|
socket.connectionTime = new Date()
|
||||||
|
this.userConnecting.set(userId, { userId, userName })
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
io.emit('user_connecting', Array.from(this.userConnecting.values()))
|
||||||
|
}, 200)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
io.to(socket.id).emit(
|
io.to(socket.id).emit(
|
||||||
|
|
@ -82,6 +89,10 @@ export class WebSocketIo {
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log(`FE disconnected: ${socket.id}`)
|
console.log(`FE disconnected: ${socket.id}`)
|
||||||
|
this.userConnecting.delete(userId)
|
||||||
|
setTimeout(() => {
|
||||||
|
io.emit('user_connecting', Array.from(this.userConnecting.values()))
|
||||||
|
}, 200)
|
||||||
})
|
})
|
||||||
|
|
||||||
// FE gửi yêu cầu connect lines
|
// FE gửi yêu cầu connect lines
|
||||||
|
|
@ -94,8 +105,8 @@ export class WebSocketIo {
|
||||||
const { lineIds, stationId, command } = data
|
const { lineIds, stationId, command } = data
|
||||||
for (const lineId of lineIds) {
|
for (const lineId of lineIds) {
|
||||||
const line = this.lineMap.get(lineId)
|
const line = this.lineMap.get(lineId)
|
||||||
if (line) {
|
if (line && line.config.status === 'connected') {
|
||||||
this.lineConnecting.filter((el) => el !== lineId)
|
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)
|
||||||
this.setTimeoutConnect(lineId, line)
|
this.setTimeoutConnect(lineId, line)
|
||||||
line.writeCommand(command)
|
line.writeCommand(command)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -107,7 +118,7 @@ export class WebSocketIo {
|
||||||
await this.connectLine(io, [linesData], stationData)
|
await this.connectLine(io, [linesData], stationData)
|
||||||
const lineReconnect = this.lineMap.get(lineId)
|
const lineReconnect = this.lineMap.get(lineId)
|
||||||
if (lineReconnect) {
|
if (lineReconnect) {
|
||||||
this.lineConnecting.filter((el) => el !== lineId)
|
this.lineConnecting = this.lineConnecting.filter((el) => el !== lineId)
|
||||||
this.setTimeoutConnect(lineId, lineReconnect)
|
this.setTimeoutConnect(lineId, lineReconnect)
|
||||||
lineReconnect.writeCommand(command)
|
lineReconnect.writeCommand(command)
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +138,7 @@ export class WebSocketIo {
|
||||||
const lineId = data.id
|
const lineId = data.id
|
||||||
const scenario = data.scenario
|
const scenario = data.scenario
|
||||||
const line = this.lineMap.get(lineId)
|
const line = this.lineMap.get(lineId)
|
||||||
if (line) {
|
if (line && line.config.status === 'connected') {
|
||||||
this.setTimeoutConnect(
|
this.setTimeoutConnect(
|
||||||
lineId,
|
lineId,
|
||||||
line,
|
line,
|
||||||
|
|
@ -155,9 +166,44 @@ export class WebSocketIo {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// FE yêu cầu ngắt kết nối 1 station
|
socket.on('open_cli', async (data) => {
|
||||||
socket.on('disconnect_station', (stationId) => {
|
const { lineId, userEmail, userName: name, stationId } = data
|
||||||
this.disconnectStation(stationId)
|
const line = this.lineMap.get(lineId)
|
||||||
|
if (line) {
|
||||||
|
line.userOpenCLI({ userEmail, userName: name })
|
||||||
|
} else {
|
||||||
|
if (this.lineConnecting.includes(lineId)) return
|
||||||
|
const linesData = await Line.findBy('id', lineId)
|
||||||
|
const stationData = await Station.findBy('id', stationId)
|
||||||
|
if (linesData && stationData) {
|
||||||
|
this.lineConnecting.push(lineId)
|
||||||
|
await this.connectLine(io, [linesData], stationData)
|
||||||
|
const lineReconnect = this.lineMap.get(lineId)
|
||||||
|
if (lineReconnect) {
|
||||||
|
lineReconnect.userOpenCLI({ userEmail, userName: name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('close_cli', async (data) => {
|
||||||
|
const { lineId, stationId } = data
|
||||||
|
const line = this.lineMap.get(lineId)
|
||||||
|
if (line) {
|
||||||
|
line.userCloseCLI()
|
||||||
|
} else {
|
||||||
|
if (this.lineConnecting.includes(lineId)) return
|
||||||
|
const linesData = await Line.findBy('id', lineId)
|
||||||
|
const stationData = await Station.findBy('id', stationId)
|
||||||
|
if (linesData && stationData) {
|
||||||
|
this.lineConnecting.push(lineId)
|
||||||
|
await this.connectLine(io, [linesData], stationData)
|
||||||
|
const lineReconnect = this.lineMap.get(lineId)
|
||||||
|
if (lineReconnect) {
|
||||||
|
lineReconnect.userCloseCLI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -182,6 +228,9 @@ export class WebSocketIo {
|
||||||
apcName: line.apcName,
|
apcName: line.apcName,
|
||||||
output: '',
|
output: '',
|
||||||
status: '',
|
status: '',
|
||||||
|
openCLI: false,
|
||||||
|
userEmailOpenCLI: '',
|
||||||
|
userOpenCLI: '',
|
||||||
},
|
},
|
||||||
socket
|
socket
|
||||||
)
|
)
|
||||||
|
|
@ -195,22 +244,6 @@ export class WebSocketIo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private disconnectStation(stationId: number) {
|
|
||||||
const station = this.stationMap.get(stationId)
|
|
||||||
if (!station) return
|
|
||||||
|
|
||||||
for (const line of station.lines) {
|
|
||||||
const conn = this.lineMap.get(line.id)
|
|
||||||
if (conn) {
|
|
||||||
conn.disconnect()
|
|
||||||
this.lineMap.delete(line.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stationMap.delete(stationId)
|
|
||||||
console.log(`🔻 Station ${station.name} disconnected`)
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTimeoutConnect = (lineId: number, lineConn: LineConnection, timeout = 120000) => {
|
private setTimeoutConnect = (lineId: number, lineConn: LineConnection, timeout = 120000) => {
|
||||||
if (this.intervalMap[`${lineId}`]) {
|
if (this.intervalMap[`${lineId}`]) {
|
||||||
clearInterval(this.intervalMap[`${lineId}`])
|
clearInterval(this.intervalMap[`${lineId}`])
|
||||||
|
|
@ -218,7 +251,7 @@ export class WebSocketIo {
|
||||||
}
|
}
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
lineConn.disconnect()
|
lineConn.disconnect()
|
||||||
this.lineMap.delete(lineId)
|
// this.lineMap.delete(lineId)
|
||||||
if (this.intervalMap[`${lineId}`]) {
|
if (this.intervalMap[`${lineId}`]) {
|
||||||
clearInterval(this.intervalMap[`${lineId}`])
|
clearInterval(this.intervalMap[`${lineId}`])
|
||||||
delete this.intervalMap[`${lineId}`]
|
delete this.intervalMap[`${lineId}`]
|
||||||
|
|
|
||||||
|
|
@ -67,5 +67,6 @@ router
|
||||||
router
|
router
|
||||||
.group(() => {
|
.group(() => {
|
||||||
router.post('/login', '#controllers/auth_controller.login')
|
router.post('/login', '#controllers/auth_controller.login')
|
||||||
|
router.post('/register', '#controllers/auth_controller.register')
|
||||||
})
|
})
|
||||||
.prefix('api/auth')
|
.prefix('api/auth')
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue