mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-07 17:08:29 +08:00
feat: ws链接鉴权增强
This commit is contained in:
+15
-12
@@ -16,25 +16,28 @@ module.exports = (httpServer) => {
|
||||
|
||||
let connectionCount = 0
|
||||
|
||||
serverIo.on('connection', (socket) => {
|
||||
connectionCount++
|
||||
consola.success(`docker websocket 已连接 - 当前连接数: ${ connectionCount }`)
|
||||
let requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
serverIo.on('connection', async (socket) => {
|
||||
// IP白名单检查
|
||||
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
if (!isAllowedIp(requestIP)) {
|
||||
socket.emit('ip_forbidden', 'IP地址不在白名单中')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
consola.success('docker websocket 已连接')
|
||||
// 登录态校验
|
||||
const { token, uid } = socket.handshake.query
|
||||
const { success } = await verifyAuthSync(token, uid)
|
||||
if (!success) {
|
||||
socket.emit('user_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
connectionCount++
|
||||
consola.success(`docker websocket 已连接 - 当前连接数: ${ connectionCount }`)
|
||||
|
||||
let targetSSHClient = null
|
||||
let jumpSshClients = []
|
||||
socket.on('ws_docker', async ({ hostId, token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
socket.on('ws_docker', async ({ hostId }) => {
|
||||
targetSSHClient = new SSHClient()
|
||||
let { jumpSshClients: dockerJumpSshClients } = await createTerminal(hostId, socket, targetSSHClient, false)
|
||||
jumpSshClients.push(...dockerJumpSshClients)
|
||||
|
||||
@@ -64,27 +64,29 @@ module.exports = (httpServer) => {
|
||||
let connectionCount = 0
|
||||
const connectedSockets = new Set() // 跟踪所有连接的socket
|
||||
|
||||
transferIo.on('connection', (socket) => {
|
||||
transferIo.on('connection', async (socket) => {
|
||||
connectionCount++
|
||||
connectedSockets.add(socket)
|
||||
consola.success(`file-transfer websocket 已连接 - 当前连接数: ${ connectionCount }`)
|
||||
|
||||
// IP白名单检查
|
||||
let requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
if (!isAllowedIp(requestIP)) {
|
||||
socket.emit('ip_forbidden', 'IP地址不在白名单中')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
// 登录态校验
|
||||
const { token, uid } = socket.handshake.query
|
||||
const { success } = await verifyAuthSync(token, uid)
|
||||
if (!success) {
|
||||
socket.emit('user_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
// 连接时发送当前所有任务状态
|
||||
socket.on('get_tasks', async ({ token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
socket.on('get_tasks', async () => {
|
||||
try {
|
||||
const sortedTasks = await getSortedTasksList()
|
||||
socket.emit('tasks_list', sortedTasks)
|
||||
@@ -92,7 +94,7 @@ module.exports = (httpServer) => {
|
||||
// 检查是否有运行中的任务,如果有则启动定时推送
|
||||
const runningTasks = sortedTasks.filter(task => task.status === 'running')
|
||||
if (runningTasks.length > 0) {
|
||||
startProgressBroadcast(socket, token, requestIP)
|
||||
startProgressBroadcast(socket)
|
||||
}
|
||||
} catch (error) {
|
||||
socket.emit('error', { message: '获取任务列表失败', error: error.message })
|
||||
@@ -101,13 +103,6 @@ module.exports = (httpServer) => {
|
||||
|
||||
// 启动传输任务
|
||||
socket.on('start_transfer', async (transferConfig) => {
|
||||
const { token } = transferConfig
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const { createTransferTask = null } = (await decryptAndExecuteAsync(path.join(__dirname, 'plus.js'))) || {}
|
||||
if (!createTransferTask) throw new Error('Plus功能解锁失败: createTransferTask')
|
||||
@@ -127,13 +122,7 @@ module.exports = (httpServer) => {
|
||||
})
|
||||
|
||||
// 取消任务
|
||||
socket.on('cancel_task', async ({ taskId, token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
socket.on('cancel_task', async ({ taskId }) => {
|
||||
try {
|
||||
await cancelTransferTask(taskId)
|
||||
socket.emit('task_cancelled', { taskId, message: '任务已取消' })
|
||||
@@ -143,13 +132,7 @@ module.exports = (httpServer) => {
|
||||
})
|
||||
|
||||
// 重试任务
|
||||
socket.on('retry_task', async ({ taskId, token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
socket.on('retry_task', async ({ taskId }) => {
|
||||
try {
|
||||
const task = await fileTransferDB.findOneAsync({ taskId })
|
||||
if (!task) {
|
||||
@@ -179,13 +162,7 @@ module.exports = (httpServer) => {
|
||||
})
|
||||
|
||||
// 删除单个任务
|
||||
socket.on('delete_task', async ({ taskId, token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
socket.on('delete_task', async ({ taskId }) => {
|
||||
try {
|
||||
const task = await fileTransferDB.findOneAsync({ taskId })
|
||||
if (!task) {
|
||||
@@ -211,13 +188,7 @@ module.exports = (httpServer) => {
|
||||
})
|
||||
|
||||
// 清空已完成任务
|
||||
socket.on('clear_completed_tasks', async ({ token }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
return
|
||||
}
|
||||
|
||||
socket.on('clear_completed_tasks', async () => {
|
||||
try {
|
||||
// 删除所有已完成、失败、取消的任务
|
||||
const result = await fileTransferDB.removeAsync(
|
||||
@@ -918,7 +889,7 @@ function cleanupRemoteKeyFile(sshClient, keyFile) {
|
||||
const progressBroadcastTimers = new Map() // socket -> timer
|
||||
|
||||
// 启动进度广播
|
||||
function startProgressBroadcast(socket, token, requestIP) {
|
||||
function startProgressBroadcast(socket) {
|
||||
// 如果该socket已经有定时器,先清除
|
||||
if (progressBroadcastTimers.has(socket)) {
|
||||
clearInterval(progressBroadcastTimers.get(socket))
|
||||
@@ -927,14 +898,6 @@ function startProgressBroadcast(socket, token, requestIP) {
|
||||
// 启动新的定时器,每秒检查并推送运行中任务的进度
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
// 验证token是否仍然有效
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
// token失效,停止广播
|
||||
stopProgressBroadcast(socket)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否还有运行中的任务
|
||||
const runningTasks = Array.from(activeTasks.values()).filter(task => task.status === 'running')
|
||||
if (runningTasks.length === 0) {
|
||||
|
||||
+12
-10
@@ -96,14 +96,22 @@ module.exports = (httpServer) => {
|
||||
origin: '*'
|
||||
}
|
||||
})
|
||||
serverIo.on('connection', (socket) => {
|
||||
// 前者兼容nginx反代, 后者兼容nodejs自身服务
|
||||
let requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
serverIo.on('connection', async (socket) => {
|
||||
// IP白名单检查
|
||||
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
if (!isAllowedIp(requestIP)) {
|
||||
socket.emit('ip_forbidden', 'IP地址不在白名单中')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
// 登录态校验
|
||||
const { token, uid } = socket.handshake.query
|
||||
const { success } = await verifyAuthSync(token, uid)
|
||||
if (!success) {
|
||||
socket.emit('user_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
consola.success('onekey-terminal websocket 已连接')
|
||||
if (isExecuting) {
|
||||
socket.emit('create_fail', '正在执行中, 请稍后再试')
|
||||
@@ -111,13 +119,7 @@ module.exports = (httpServer) => {
|
||||
return
|
||||
}
|
||||
isExecuting = true
|
||||
socket.on('ws_onekey', async ({ hostIds, token, command, timeout }) => {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
socket.on('ws_onekey', async ({ hostIds, command, timeout }) => {
|
||||
console.log('onekey command:', command)
|
||||
const hostList = await hostListDB.findAsync({})
|
||||
const targetHostsInfo = hostList.filter(item => hostIds.some(id => item._id === id)) || {}
|
||||
|
||||
@@ -1327,13 +1327,22 @@ module.exports = (httpServer) => {
|
||||
origin: '*'
|
||||
}
|
||||
})
|
||||
serverIo.on('connection', (socket) => {
|
||||
let requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
serverIo.on('connection', async (socket) => {
|
||||
// IP白名单检查
|
||||
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
if (!isAllowedIp(requestIP)) {
|
||||
socket.emit('ip_forbidden', 'IP地址不在白名单中')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
// 登录态校验
|
||||
const { token, uid } = socket.handshake.query
|
||||
const { success } = await verifyAuthSync(token, uid)
|
||||
if (!success) {
|
||||
socket.emit('user_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
let sftpClient = new SFTPClient()
|
||||
consola.success('sftp-v2 websocket 已连接')
|
||||
let jumpSshClients = []
|
||||
@@ -1343,14 +1352,8 @@ module.exports = (httpServer) => {
|
||||
consola.error('SFTP-v2 Socket连接错误:', err.message)
|
||||
})
|
||||
|
||||
socket.on('ws_sftp', async ({ hostId, token }) => {
|
||||
socket.on('ws_sftp', async ({ hostId }) => {
|
||||
try {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
const targetHostInfo = await hostListDB.findOneAsync({ _id: hostId })
|
||||
if (!targetHostInfo) throw new Error(`Host with ID ${ hostId } not found`)
|
||||
|
||||
|
||||
@@ -261,27 +261,28 @@ async function createTerminal(hostId, socket, targetSSHClient, isInteractiveShel
|
||||
function createServerIo(serverIo) {
|
||||
let connectionCount = 0
|
||||
|
||||
serverIo.on('connection', (socket) => {
|
||||
connectionCount++
|
||||
consola.success(`terminal websocket 已连接 - 当前连接数: ${ connectionCount }`)
|
||||
let requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
serverIo.on('connection', async (socket) => {
|
||||
// IP白名单检查
|
||||
const requestIP = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address
|
||||
if (!isAllowedIp(requestIP)) {
|
||||
socket.emit('ip_forbidden', 'IP地址不在白名单中')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
consola.success('terminal websocket 已连接')
|
||||
// 登录态校验
|
||||
const { token, uid } = socket.handshake.query
|
||||
const { success } = await verifyAuthSync(token, uid)
|
||||
if (!success) {
|
||||
socket.emit('user_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
connectionCount++
|
||||
consola.success(`terminal websocket 已连接 - 当前连接数: ${ connectionCount }`)
|
||||
let targetSSHClient = null
|
||||
let jumpSshClients = []
|
||||
socket.on('ws_terminal', async ({ hostId, token }) => {
|
||||
socket.on('ws_terminal', async ({ hostId }) => {
|
||||
try {
|
||||
const { code } = await verifyAuthSync(token, requestIP)
|
||||
if (code !== 1) {
|
||||
socket.emit('token_verify_fail')
|
||||
socket.disconnect()
|
||||
return
|
||||
}
|
||||
|
||||
targetSSHClient = new SSHClient()
|
||||
let result = await createTerminal(hostId, socket, targetSSHClient, true)
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ const enumLoginCode = {
|
||||
const verifyAuthSync = async (token, userId) => {
|
||||
try {
|
||||
const { commonKey, user, _id: uid } = await keyDB.findOneAsync({ _id: userId })
|
||||
if (uid !== userId) return { code: enumLoginCode.ERROR_UID, msg: '用户id校验失败' }
|
||||
if (uid !== userId) return { code: enumLoginCode.ERROR_UID }
|
||||
token = await AESDecryptAsync(token)
|
||||
const { exp } = jwt.verify(token, `${ user }-${ commonKey }`)
|
||||
if (Date.now() > (exp * 1000)) {
|
||||
return { code: enumLoginCode.EXPIRES } // 过期
|
||||
}
|
||||
return { code: enumLoginCode.SUCCESS } // 验证成功
|
||||
return { code: enumLoginCode.SUCCESS, success: true } // 验证成功
|
||||
} catch (err) {
|
||||
consola.error('用户身份校验失败: ', err.message)
|
||||
return { code: enumLoginCode.ERROR_TOKEN }
|
||||
|
||||
@@ -23,10 +23,4 @@ app.config.globalProperties.$tools = tools
|
||||
app.config.globalProperties.$http = axios
|
||||
app.config.globalProperties.$store = useStore()
|
||||
|
||||
const serviceURI = import.meta.env.DEV ? process.env.serviceURI : location.origin
|
||||
app.config.globalProperties.$serviceURI = serviceURI
|
||||
app.config.globalProperties.$store.$patch({ serviceURI })
|
||||
console.warn('ISDEV: ', import.meta.env.DEV)
|
||||
console.warn('serviceURI: ', serviceURI)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { reactive } from 'vue'
|
||||
import JSRsaEncrypt from 'jsencrypt'
|
||||
import CryptoJS from 'crypto-js'
|
||||
import socketIo from 'socket.io-client'
|
||||
import useStore from '../store'
|
||||
|
||||
export const EventBus = reactive({})
|
||||
|
||||
@@ -175,4 +177,17 @@ export const isValidDate = (dateString) => {
|
||||
if (!dateString) return false
|
||||
const date = new Date(dateString)
|
||||
return !isNaN(date.getTime()) && date instanceof Date
|
||||
}
|
||||
|
||||
const serviceURI = import.meta.env.DEV ? process.env.serviceURI : location.origin
|
||||
export const generateSocketInstance = (path, query = {}, config = { forceNew: false, reconnection: true, reconnectionAttempts: 3 }) => {
|
||||
return socketIo(serviceURI, {
|
||||
path,
|
||||
query: {
|
||||
uid: useStore().uid,
|
||||
token: useStore().token,
|
||||
...query
|
||||
},
|
||||
...config
|
||||
})
|
||||
}
|
||||
@@ -138,15 +138,14 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue'
|
||||
import { List, Setting, ArrowRight, ArrowLeft, Warning } from '@element-plus/icons-vue'
|
||||
import socketIo from 'socket.io-client'
|
||||
import { List, Setting, ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
|
||||
import SftpPanel from '@/components/file-transfer/sftp-panel.vue'
|
||||
import TransferTaskManager from '@/components/file-transfer/transfer-task-manager.vue'
|
||||
import { EventBus } from '@/utils'
|
||||
import { EventBus, generateSocketInstance } from '@/utils'
|
||||
|
||||
defineEmits(['exec-script',])
|
||||
|
||||
const { proxy: { $store, $message, $serviceURI } } = getCurrentInstance()
|
||||
const { proxy: { $message, $router } } = getCurrentInstance()
|
||||
|
||||
// 传输配置(仅支持Rsync)
|
||||
const transferOptions = ref({
|
||||
@@ -174,7 +173,6 @@ const activeTaskCount = computed(() => {
|
||||
|
||||
// Socket连接
|
||||
const socket = ref(null)
|
||||
const token = computed(() => $store.token)
|
||||
|
||||
// 计算属性
|
||||
const canTransferToRight = computed(() => {
|
||||
@@ -207,10 +205,7 @@ onUnmounted(() => {
|
||||
|
||||
// Socket连接
|
||||
const initializeSocket = () => {
|
||||
socket.value = socketIo($serviceURI, {
|
||||
path: '/file-transfer',
|
||||
forceNew: true
|
||||
})
|
||||
socket.value = generateSocketInstance('/file-transfer')
|
||||
|
||||
socket.value.on('connect', () => {
|
||||
console.log('文件传输WebSocket已连接')
|
||||
@@ -249,8 +244,9 @@ const initializeSocket = () => {
|
||||
$message.error(`${ message }: ${ error }`)
|
||||
})
|
||||
|
||||
socket.value.on('token_verify_fail', () => {
|
||||
$message.error('登录已过期,请重新登录')
|
||||
socket.value.on('user_verify_fail', () => {
|
||||
$message.error('登录态校验失败,请重新登录')
|
||||
$router.push('/login')
|
||||
})
|
||||
|
||||
socket.value.on('task_deleted', ({ message }) => {
|
||||
@@ -306,8 +302,7 @@ const performTransfer = (fromSide, toSide) => {
|
||||
})),
|
||||
targetPath,
|
||||
method: 'rsync',
|
||||
options: transferOptions.value,
|
||||
token: token.value
|
||||
options: transferOptions.value
|
||||
}
|
||||
|
||||
isTransferring.value = true
|
||||
@@ -322,29 +317,29 @@ const showTaskManager = () => {
|
||||
|
||||
const refreshTasks = () => {
|
||||
if (socket.value) {
|
||||
socket.value.emit('get_tasks', { token: token.value })
|
||||
socket.value.emit('get_tasks')
|
||||
}
|
||||
}
|
||||
|
||||
const cancelTask = (taskId) => {
|
||||
socket.value.emit('cancel_task', { taskId, token: token.value })
|
||||
socket.value.emit('cancel_task', { taskId })
|
||||
}
|
||||
|
||||
const retryTask = (taskId) => {
|
||||
socket.value.emit('retry_task', { taskId, token: token.value })
|
||||
socket.value.emit('retry_task', { taskId })
|
||||
}
|
||||
|
||||
const clearCompletedTasks = () => {
|
||||
// 发送清空请求到后端
|
||||
if (socket.value) {
|
||||
socket.value.emit('clear_completed_tasks', { token: token.value })
|
||||
socket.value.emit('clear_completed_tasks')
|
||||
}
|
||||
}
|
||||
|
||||
const deleteTask = (taskId) => {
|
||||
// 发送删除请求到后端
|
||||
if (socket.value) {
|
||||
socket.value.emit('delete_task', { taskId, token: token.value })
|
||||
socket.value.emit('delete_task', { taskId })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="login_container">
|
||||
<div class="login_box">
|
||||
<div>
|
||||
<h2>EasyNode</h2>
|
||||
<h2>EasyNode-{{ version }}</h2>
|
||||
</div>
|
||||
<div v-if="notKey">
|
||||
<el-alert title="Error: 用于加密的公钥获取失败,请尝试重新启动或部署服务" type="error" show-icon />
|
||||
@@ -83,8 +83,9 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
|
||||
import { RSAEncrypt } from '@utils/index.js'
|
||||
import { version } from '../../../package.json'
|
||||
|
||||
const { proxy: { $store, $api, $message, $router } } = getCurrentInstance()
|
||||
const { proxy: { $store, $api, $message, $router, $messageBox } } = getCurrentInstance()
|
||||
|
||||
const expireEnum = reactive({
|
||||
ONE_SESSION: 'one_session',
|
||||
@@ -96,6 +97,8 @@ const expireTime = ref(expireEnum.CURRENT_DAY)
|
||||
const loginFormRefs = ref(null)
|
||||
const notKey = ref(false)
|
||||
const loading = ref(false)
|
||||
const isHttps = location.protocol.includes('https')
|
||||
const notHttpsTips = localStorage.getItem('notHttpsTips') === 'true'
|
||||
const loginForm = reactive({
|
||||
loginName: '',
|
||||
pwd: '',
|
||||
@@ -134,7 +137,26 @@ const handleLogin = () => {
|
||||
$store.setJwtToken(token, expireEnum.ONE_SESSION === expireTime.value)
|
||||
$store.setUser(loginName, uid)
|
||||
$message.success({ message: msg || 'success', center: true })
|
||||
$router.push('/')
|
||||
if (isHttps) return $router.push('/')
|
||||
if (notHttpsTips) return $router.push('/')
|
||||
$messageBox.confirm(
|
||||
'http协议下存在数据劫持风险,外网使用请配置https协议',
|
||||
'安全提示',
|
||||
{
|
||||
confirmButtonText: '我知道了',
|
||||
cancelButtonText: '不再提示',
|
||||
type: 'warning',
|
||||
showCancelButton: true,
|
||||
cancelButtonClass: 'el-button--info',
|
||||
confirmButtonClass: 'el-button--warning'
|
||||
}
|
||||
)
|
||||
.catch(() => {
|
||||
localStorage.setItem('notHttpsTips', 'true')
|
||||
})
|
||||
.finally(async () => {
|
||||
$router.push('/')
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -177,6 +199,11 @@ onMounted(async () => {
|
||||
margin: 25px;
|
||||
color: #409eff;
|
||||
font-size: 25px;
|
||||
background: linear-gradient(to right, #ffc021, #e4d1a1);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer_btns {
|
||||
|
||||
@@ -206,13 +206,9 @@
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, watch, nextTick, getCurrentInstance, onActivated } from 'vue'
|
||||
import { ArrowDown } from '@element-plus/icons-vue'
|
||||
import socketIo from 'socket.io-client'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { generateSocketInstance } from '@/utils'
|
||||
|
||||
const { io } = socketIo
|
||||
|
||||
const { proxy: { $api, $notification,$messageBox, $message, $router, $serviceURI, $store, $tools } } = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const { proxy: { $api, $notification,$messageBox, $message, $router, $store, $tools } } = getCurrentInstance()
|
||||
|
||||
const loading = ref(false)
|
||||
const formVisible = ref(false)
|
||||
@@ -272,11 +268,7 @@ const createExecShell = (hostIds = [], command = 'ls', timeout = 60) => {
|
||||
loading.value = true
|
||||
timeRemaining.value = Number(formData.timeout)
|
||||
let timer = null
|
||||
socket.value = io($serviceURI, {
|
||||
path: '/onekey',
|
||||
forceNew: false,
|
||||
reconnectionAttempts: 1
|
||||
})
|
||||
socket.value = generateSocketInstance('/onekey')
|
||||
socket.value.on('connect', () => {
|
||||
timer = setInterval(() => {
|
||||
timeRemaining.value -= 1
|
||||
@@ -312,8 +304,8 @@ const createExecShell = (hostIds = [], command = 'ls', timeout = 60) => {
|
||||
getOnekeyRecord()
|
||||
})
|
||||
|
||||
socket.value.on('token_verify_fail', () => {
|
||||
$message.error('token验证失败,请重新登录')
|
||||
socket.value.on('user_verify_fail', () => {
|
||||
$message.error('登录态校验失败,请重新登录')
|
||||
$router.push('/login')
|
||||
})
|
||||
|
||||
|
||||
@@ -213,11 +213,9 @@ import { ref, onMounted, onUnmounted, computed, watch, getCurrentInstance } from
|
||||
import { VideoPlay, VideoPause, RefreshRight, Delete, MoreFilled } from '@element-plus/icons-vue'
|
||||
import CodeEdit from '@/components/code-edit/index.vue'
|
||||
import PlusLimitTip from '@/components/common/PlusLimitTip.vue'
|
||||
import socketIo from 'socket.io-client'
|
||||
import { EventBus } from '@/utils'
|
||||
import { EventBus, generateSocketInstance } from '@/utils'
|
||||
|
||||
const { io } = socketIo
|
||||
const { proxy: { $store, $serviceURI, $message, $messageBox } } = getCurrentInstance()
|
||||
const { proxy: { $store, $message, $messageBox } } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
hostId: {
|
||||
@@ -256,7 +254,6 @@ const makeImageRules = ref({
|
||||
{ required: false, message: '请输入提交说明', trigger: 'blur' },
|
||||
]
|
||||
})
|
||||
const token = computed(() => $store.token)
|
||||
const hostId = computed(() => props.hostId)
|
||||
const containerName = computed(() => `${ currentContainer.value?.name }.log(自动刷新)` || '')
|
||||
const isPlusActive = computed(() => $store.isPlusActive)
|
||||
@@ -310,16 +307,11 @@ const getStatusType = (status) => {
|
||||
}
|
||||
|
||||
const connectDocker = () => {
|
||||
socket.value = io($serviceURI, {
|
||||
path: '/docker',
|
||||
forceNew: false,
|
||||
reconnection: false,
|
||||
reconnectionAttempts: 0
|
||||
})
|
||||
socket.value = generateSocketInstance('/docker')
|
||||
socket.value.on('connect', () => {
|
||||
console.log('/docker socket已连接:', hostId.value)
|
||||
loading.value = true
|
||||
socket.value.emit('ws_docker', { hostId: hostId.value, token: token.value })
|
||||
socket.value.emit('ws_docker', { hostId: hostId.value })
|
||||
socket.value.on('docker_containers_data', (data) => {
|
||||
// console.log('docker_containers_data:', data)
|
||||
dockerServerErr.value = false
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted, watch, getCurrentInstance, nextTick } from 'vue'
|
||||
import { ArrowDown, ArrowLeft, Refresh, View, Hide, Edit, ArrowRight, HomeFilled, Check, Close as CloseIcon, Download, Upload, DocumentCopy, Loading, WarningFilled, Star, StarFilled, Delete, SwitchButton } from '@element-plus/icons-vue'
|
||||
import socketIo from 'socket.io-client'
|
||||
import { generateSocketInstance } from '@/utils'
|
||||
import dirIcon from '@/assets/image/system/dir.png'
|
||||
import linkIcon from '@/assets/image/system/link.png'
|
||||
import fileIcon from '@/assets/image/system/file.png'
|
||||
@@ -561,7 +561,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
// 组件实例上下文
|
||||
const { proxy: { $store, $message, $serviceURI, $messageBox } } = getCurrentInstance()
|
||||
const { proxy: { $message, $messageBox } } = getCurrentInstance()
|
||||
|
||||
// 路径 & 隐藏文件显示
|
||||
const currentPath = ref('/')
|
||||
@@ -574,7 +574,6 @@ watch(showHidden, (val) => {
|
||||
|
||||
// Socket & 列表
|
||||
const socket = ref(null)
|
||||
const token = computed(() => $store.token)
|
||||
const loading = ref(false)
|
||||
|
||||
// 连接状态管理
|
||||
@@ -707,8 +706,6 @@ onUnmounted(() => {
|
||||
//----------------------------------
|
||||
|
||||
const connectSftp = () => {
|
||||
const { io } = socketIo
|
||||
|
||||
// 清理旧的socket连接
|
||||
if (socket.value) {
|
||||
socket.value.removeAllListeners()
|
||||
@@ -720,14 +717,13 @@ const connectSftp = () => {
|
||||
connectionError.value = ''
|
||||
loading.value = true
|
||||
|
||||
socket.value = io($serviceURI, {
|
||||
path: '/sftp-v2',
|
||||
socket.value = generateSocketInstance('/sftp-v2', {
|
||||
forceNew: true,
|
||||
reconnectionAttempts: 0 // 禁用socket.io自带的重连,我们自己控制
|
||||
reconnectionAttempts: 0
|
||||
})
|
||||
|
||||
socket.value.on('connect', () => {
|
||||
socket.value.emit('ws_sftp', { hostId: props.hostId, token: token.value })
|
||||
socket.value.emit('ws_sftp', { hostId: props.hostId })
|
||||
|
||||
socket.value.on('connect_success', ({ rootList, isRootUser, currentPath: serverCurrentPath }) => {
|
||||
fileListRaw.value = rootList
|
||||
|
||||
@@ -23,20 +23,17 @@ import { FitAddon } from '@xterm/addon-fit'
|
||||
import { SearchAddon } from '@xterm/addon-search'
|
||||
// import { SearchBarAddon } from 'xterm-addon-search-bar'
|
||||
import { WebLinksAddon } from '@xterm/addon-web-links'
|
||||
import socketIo from 'socket.io-client'
|
||||
import themeList from 'xterm-theme'
|
||||
import { terminalStatus } from '@/utils/enum'
|
||||
import { useContextMenu } from '@/composables/useContextMenu'
|
||||
import { EventBus, isDockerId, isDockerComposeYml } from '@/utils'
|
||||
import { EventBus, isDockerId, isDockerComposeYml, generateSocketInstance } from '@/utils'
|
||||
import useMobileWidth from '@/composables/useMobileWidth'
|
||||
|
||||
const { CONNECTING, CONNECT_SUCCESS, CONNECT_FAIL } = terminalStatus
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
const { uid } = instance
|
||||
const { proxy: { $api, $store, $serviceURI, $notification, $router, $message, $messageBox } } = instance
|
||||
|
||||
const { io } = socketIo
|
||||
const { proxy: { $api, $store, $notification, $router, $message, $messageBox } } = instance
|
||||
|
||||
const { isMobileScreen } = useMobileWidth()
|
||||
|
||||
@@ -79,7 +76,6 @@ const cursorTheme = {
|
||||
cursorAccent: '#000000'
|
||||
}
|
||||
|
||||
const token = computed(() => $store.token)
|
||||
const theme = computed(() => themeList[$store.terminalConfig.themeName])
|
||||
const fontSize = computed(() => $store.terminalConfig.fontSize)
|
||||
const background = computed(() => $store.terminalConfig.background)
|
||||
@@ -153,8 +149,7 @@ const getCommand = async () => {
|
||||
|
||||
const connectIO = () => {
|
||||
curStatus.value = CONNECTING
|
||||
socket.value = io($serviceURI, {
|
||||
path: '/terminal',
|
||||
socket.value = generateSocketInstance('/terminal', {
|
||||
forceNew: false,
|
||||
reconnection: false,
|
||||
reconnectionAttempts: 0
|
||||
@@ -163,7 +158,7 @@ const connectIO = () => {
|
||||
console.log('/terminal socket已连接:', hostId.value)
|
||||
|
||||
socketConnected.value = true
|
||||
socket.value.emit('ws_terminal', { hostId: hostId.value, token: token.value })
|
||||
socket.value.emit('ws_terminal', { hostId: hostId.value })
|
||||
socket.value.on('terminal_connect_success', () => {
|
||||
socket.value.on('output', (str) => {
|
||||
if (props.isSingleWindow && !isPlusActive.value) return
|
||||
@@ -192,8 +187,8 @@ const connectIO = () => {
|
||||
emit('ping-data', { host: host.value, time })
|
||||
})
|
||||
|
||||
socket.value.on('token_verify_fail', () => {
|
||||
$notification({ title: 'Error', message: 'token校验失败,请重新登录', type: 'error' })
|
||||
socket.value.on('user_verify_fail', () => {
|
||||
$notification({ title: 'Error', message: '登录态校验失败,请重新登录', type: 'error' })
|
||||
$router.push('/login')
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user