diff --git a/README.md b/README.md index 3e54ed4..dc5edf7 100644 --- a/README.md +++ b/README.md @@ -81,16 +81,10 @@ pm2 start index.js --name easynode-server - 占用端口:**22022** -#### X86架构 +> 安装 ```shell -wget -qO- --no-check-certificate https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-install-x86.sh | bash -``` - -#### ARM架构 - -```shell -wget -qO- --no-check-certificate https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-install-arm.sh | bash +wget -qO- --no-check-certificate https://mirror.ghproxy.com/https://raw.githubusercontent.com/chaos-zhu/easynode/main/client/easynode-client-instal.sh | bash ``` > 卸载 diff --git a/client/easynode-client-install-x86.sh b/client/easynode-client-install-x86.sh deleted file mode 100644 index 58256a2..0000000 --- a/client/easynode-client-install-x86.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -if [ "$(id -u)" != "0" ] ; then - echo "***********************请切换到root再尝试执行***********************" - exit 1 -fi - -SERVER_NAME=easynode-client -FILE_PATH=/root/local/easynode-client -SERVICE_PATH=/etc/systemd/system -SERVER_VERSION=v1.0 - -echo "***********************开始安装 easynode-client_${SERVER_VERSION}***********************" - -systemctl status ${SERVER_NAME} > /dev/null 2>&1 -if [ $? != 4 ] -then - echo "***********************停用旧服务***********************" - systemctl stop ${SERVER_NAME} - systemctl disable ${SERVER_NAME} - systemctl daemon-reload -fi - -if [ -f "${SERVICE_PATH}/${SERVER_NAME}.service" ] -then - echo "***********************移除旧服务***********************" - chmod 777 ${SERVICE_PATH}/${SERVER_NAME}.service - rm -Rf ${SERVICE_PATH}/${SERVER_NAME}.service - systemctl daemon-reload -fi - -if [ -d ${FILE_PATH} ] -then - echo "***********************移除旧文件***********************" - chmod 777 ${FILE_PATH} - rm -Rf ${FILE_PATH} -fi - -# 开始安装 - -echo "***********************创建文件PATH***********************" -mkdir -p ${FILE_PATH} - -echo "***********************下载开始***********************" -DOWNLOAD_FILE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client-x86" -DOWNLOAD_SERVICE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client.service" - -# -O 指定路径和文件名(这里是二进制文件, 不需要扩展名) -wget -O ${FILE_PATH}/${SERVER_NAME} --no-check-certificate --no-cache ${DOWNLOAD_FILE_URL} -if [ $? != 0 ] -then - echo "***********************下载${SERVER_NAME}失败***********************" - exit 1 -fi - -wget -O ${FILE_PATH}/${SERVER_NAME}.service --no-check-certificate --no-cache ${DOWNLOAD_SERVICE_URL} - -if [ $? != 0 ] -then - echo "***********************下载${SERVER_NAME}.service失败***********************" - exit 1 -fi - -echo "***********************下载成功***********************" - -# echo "***********************设置权限***********************" -chmod +x ${FILE_PATH}/${SERVER_NAME} -chmod 777 ${FILE_PATH}/${SERVER_NAME}.service - -# echo "***********************移动service&reload***********************" -mv ${FILE_PATH}/${SERVER_NAME}.service ${SERVICE_PATH} - -# echo "***********************daemon-reload***********************" -systemctl daemon-reload - -echo "***********************准备启动服务***********************" -systemctl start ${SERVER_NAME} - -if [ $? != 0 ] -then - echo "***********************${SERVER_NAME}.service启动失败***********************" - echo "***********************可能是服务器开启了SELinux, 参见Q&A***********************" - exit 1 -fi -echo "***********************服务启动成功***********************" - -# echo "***********************设置开机启动***********************" -systemctl enable ${SERVER_NAME} - -echo "***********************安装成功***********************" diff --git a/client/easynode-client-install-arm.sh b/client/easynode-client-install.sh similarity index 86% rename from client/easynode-client-install-arm.sh rename to client/easynode-client-install.sh index 76be731..90d4bb4 100644 --- a/client/easynode-client-install-arm.sh +++ b/client/easynode-client-install.sh @@ -42,9 +42,19 @@ echo "***********************创建文件PATH***********************" mkdir -p ${FILE_PATH} echo "***********************下载开始***********************" -DOWNLOAD_FILE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client-arm64" DOWNLOAD_SERVICE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client.service" +ARCH=$(uname -m) + +if [ "$ARCH" = "x86_64" ] ; then + DOWNLOAD_FILE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client-x86" +elif [ "$ARCH" = "aarch64" ] ; then + DOWNLOAD_FILE_URL="https://mirror.ghproxy.com/https://github.com/chaos-zhu/easynode/releases/download/v2.0.0/easynode-client-arm64" +else + echo "未知的架构:$ARCH" + exit 1 +fi + # -O 指定路径和文件名(这里是二进制文件, 不需要扩展名) wget -O ${FILE_PATH}/${SERVER_NAME} --no-check-certificate --no-cache ${DOWNLOAD_FILE_URL} if [ $? != 0 ] diff --git a/server/app/config/index.js b/server/app/config/index.js index b0b4bd9..c1224f2 100644 --- a/server/app/config/index.js +++ b/server/app/config/index.js @@ -14,6 +14,7 @@ module.exports = { notifyConfDBPath: path.join(process.cwd(),'app/db/notify.db'), groupConfDBPath: path.join(process.cwd(),'app/db/group.db'), emailNotifyDBPath: path.join(process.cwd(),'app/db/email.db'), + scriptsDBPath: path.join(process.cwd(),'app/db/scripts.db'), apiPrefix: '/api/v1', logConfig: { outDir: path.join(process.cwd(),'./app/logs'), diff --git a/server/app/controller/group.js b/server/app/controller/group.js index 7737d2f..bcd9b66 100644 --- a/server/app/controller/group.js +++ b/server/app/controller/group.js @@ -16,7 +16,7 @@ const addGroupList = async ({ res, request }) => { let group = { name, index } groupList.push(group) await writeGroupList(groupList) - res.success({ data: '新增成功' }) + res.success({ data: '添加成功' }) } const updateGroupList = async ({ res, request }) => { diff --git a/server/app/controller/host.js b/server/app/controller/host.js index 27389d5..873cfae 100644 --- a/server/app/controller/host.js +++ b/server/app/controller/host.js @@ -57,7 +57,7 @@ async function updateHost({ name: newName, host: newHost, index, expired, expiredNotify, group, consoleUrl, remark, port, username, authType, password, privateKey, credential, command } - if (!hostList.some(({ host }) => host === oldHost)) return res.fail({ msg: `原实例[${ oldHost }]不存在,请尝试新增实例` }) + if (!hostList.some(({ host }) => host === oldHost)) return res.fail({ msg: `原实例[${ oldHost }]不存在,请尝试添加实例` }) let idx = hostList.findIndex(({ host }) => host === oldHost) const oldRecord = hostList[idx] diff --git a/server/app/controller/scripts.js b/server/app/controller/scripts.js new file mode 100644 index 0000000..4d0bba6 --- /dev/null +++ b/server/app/controller/scripts.js @@ -0,0 +1,52 @@ +const { readScriptList, writeScriptList } = require('../utils') + +async function getScriptList({ res }) { + let data = await readScriptList() + data = data.map(item => { + return { ...item, id: item._id } + }) + data?.sort((a, b) => Number(b.index || 0) - Number(a.index || 0)) + res.success({ data }) +} + +const addScript = async ({ res, request }) => { + let { body: { name, remark, content, index } } = request + if (!name || !content) return res.fail({ data: false, msg: '参数错误' }) + index = Number(index) || 0 + let scriptsList = await readScriptList() + let record = { name, remark, content, index } + scriptsList.push(record) + await writeScriptList(scriptsList) + res.success({ data: '添加成功' }) +} + +const updateScriptList = async ({ res, request }) => { + let { params: { id } } = request + let { body: { name, remark, content, index } } = request + if (!name || !content) return res.fail({ data: false, msg: '参数错误' }) + let scriptsList = await readScriptList() + let idx = scriptsList.findIndex(item => item._id === id) + if (idx === -1) return res.fail({ data: false, msg: `脚本ID${ id }不存在` }) + const { _id } = scriptsList[idx] + let record = Object.assign({ _id }, { name, remark, content, index }) + scriptsList.splice(idx, 1, record) + await writeScriptList(scriptsList) + res.success({ data: '修改成功' }) +} + +const removeScript = async ({ res, request }) => { + let { params: { id } } = request + let scriptsList = await readScriptList() + let idx = scriptsList.findIndex(item => item._id === id) + if (idx === -1) return res.fail({ msg: '脚本ID不存在' }) + scriptsList.splice(idx, 1) + await writeScriptList(scriptsList) + res.success({ data: '移除成功' }) +} + +module.exports = { + addScript, + getScriptList, + updateScriptList, + removeScript +} diff --git a/server/app/controller/ssh.js b/server/app/controller/ssh.js index 7cad4fa..79f57ff 100644 --- a/server/app/controller/ssh.js +++ b/server/app/controller/ssh.js @@ -27,7 +27,7 @@ const addSSH = async ({ res, request }) => { sshRecord.push({ ...record, date: Date.now() }) await writeSSHRecord(sshRecord) - consola.info('新增凭证:', name) + consola.info('添加凭证:', name) res.success({ data: '保存成功' }) } diff --git a/server/app/router/routes.js b/server/app/router/routes.js index 6298eb2..d54065f 100644 --- a/server/app/router/routes.js +++ b/server/app/router/routes.js @@ -3,6 +3,7 @@ const { getHostList, addHost, updateHost, removeHost, importHost } = require('.. const { login, getpublicKey, updatePwd, getLoginRecord } = require('../controller/user') const { getSupportEmailList, getUserEmailList, updateUserEmailList, removeUserEmail, pushEmail, getNotifyList, updateNotifyList } = require('../controller/notify') const { getGroupList, addGroupList, updateGroupList, removeGroup } = require('../controller/group') +const { getScriptList, addScript, updateScriptList, removeScript } = require('../controller/scripts') const ssh = [ { @@ -141,4 +142,27 @@ const group = [ } ] -module.exports = [].concat(ssh, host, user, notify, group) +const scripts = [ + { + method: 'get', + path: '/script', + controller: getScriptList + }, + { + method: 'post', + path: '/script', + controller: addScript + }, + { + method: 'delete', + path: '/script/:id', + controller: removeScript + }, + { + method: 'put', + path: '/script/:id', + controller: updateScriptList + } +] + +module.exports = [].concat(ssh, host, user, notify, group, scripts) diff --git a/server/app/socket/clients.js b/server/app/socket/clients.js index 326dd3b..e1f235f 100644 --- a/server/app/socket/clients.js +++ b/server/app/socket/clients.js @@ -37,7 +37,7 @@ async function getClientsInfo(socketId) { }) }) .on('connect_error', (error) => { - consola.error('client connect fail:', host, name, error.message) + // consola.error('client connect fail:', host, name, error.message) clientsData[host] = null }) .on('disconnect', () => { diff --git a/server/app/utils/db-class.js b/server/app/utils/db-class.js index 4498bbc..c2cdc3b 100644 --- a/server/app/utils/db-class.js +++ b/server/app/utils/db-class.js @@ -1,5 +1,5 @@ const Datastore = require('@seald-io/nedb') -const { credentialsDBPath, hostListDBPath, keyDBPath, emailNotifyDBPath, notifyConfDBPath, groupConfDBPath } = require('../config') +const { credentialsDBPath, hostListDBPath, keyDBPath, emailNotifyDBPath, notifyConfDBPath, groupConfDBPath, scriptsDBPath } = require('../config') module.exports.KeyDB = class KeyDB { constructor() { @@ -66,3 +66,14 @@ module.exports.EmailNotifyDB = class EmailNotifyDB { return EmailNotifyDB.instance } } + +module.exports.ScriptsDB = class ScriptsDB { + constructor() { + if (!ScriptsDB.instance) { + ScriptsDB.instance = new Datastore({ filename: scriptsDBPath, autoload: true }) + } + } + getInstance() { + return ScriptsDB.instance + } +} diff --git a/server/app/utils/index.js b/server/app/utils/index.js index 68d7996..bc85799 100644 --- a/server/app/utils/index.js +++ b/server/app/utils/index.js @@ -12,7 +12,9 @@ const { getNotifySwByType, writeNotifyList, readGroupList, - writeGroupList + writeGroupList, + readScriptList, + writeScriptList } = require('./storage') const { RSADecryptSync, AESEncryptSync, AESDecryptSync, SHA1Encrypt } = require('./encrypt') const { verifyAuthSync, isProd } = require('./verify-auth') @@ -47,5 +49,7 @@ module.exports = { getNotifySwByType, writeNotifyList, readGroupList, - writeGroupList + writeGroupList, + readScriptList, + writeScriptList } diff --git a/server/app/utils/storage.js b/server/app/utils/storage.js index 0088397..14683f8 100644 --- a/server/app/utils/storage.js +++ b/server/app/utils/storage.js @@ -1,4 +1,4 @@ -const { KeyDB, HostListDB, SshRecordDB, NotifyDB, GroupDB, EmailNotifyDB } = require('./db-class') +const { KeyDB, HostListDB, SshRecordDB, NotifyDB, GroupDB, EmailNotifyDB, ScriptsDB } = require('./db-class') const readKey = async () => { return new Promise((resolve, reject) => { @@ -234,6 +234,42 @@ const writeGroupList = async (list = []) => { }) } +const readScriptList = async () => { + return new Promise((resolve, reject) => { + const scriptsDB = new ScriptsDB().getInstance() + scriptsDB.find({}, (err, docs) => { + if (err) { + consola.error('读取scripts list错误: ', err) + reject(err) + } else { + resolve(docs) + } + }) + }) +} + +const writeScriptList = async (list = []) => { + return new Promise((resolve, reject) => { + const scriptsDB = new ScriptsDB().getInstance() + scriptsDB.remove({}, { multi: true }, (err) => { + if (err) { + consola.error('清空group list出错:', err) + reject(err) + } else { + scriptsDB.insert(list, (err, newDocs) => { + if (err) { + consola.error('写入新的group list出错:', err) + reject(err) + } else { + scriptsDB.compactDatafile() + resolve(newDocs) + } + }) + } + }) + }) +} + module.exports = { readSSHRecord, writeSSHRecord, @@ -248,5 +284,7 @@ module.exports = { writeGroupList, readSupportEmailList, readUserEmailList, - writeUserEmailList + writeUserEmailList, + readScriptList, + writeScriptList } \ No newline at end of file diff --git a/web/src/api/index.js b/web/src/api/index.js index 16d2a2c..5e242cb 100644 --- a/web/src/api/index.js +++ b/web/src/api/index.js @@ -87,5 +87,17 @@ export default { }, deleteGroup(id) { return axios({ url: `/group/${ id }`, method: 'delete' }) + }, + getScriptList() { + return axios({ url: '/script', method: 'get' }) + }, + addScript(data) { + return axios({ url: '/script', method: 'post', data }) + }, + updateScript(id, data) { + return axios({ url: `/script/${ id }`, method: 'put', data }) + }, + deleteScript(id) { + return axios({ url: `/script/${ id }`, method: 'delete' }) } } diff --git a/web/src/components/aside-box.vue b/web/src/components/aside-box.vue index 1740842..a642b45 100644 --- a/web/src/components/aside-box.vue +++ b/web/src/components/aside-box.vue @@ -62,16 +62,16 @@ let menuList = reactive([ icon: markRaw(FolderOpened), index: '/group' }, + { + name: '脚本库', + icon: markRaw(ArrowRight), + index: '/scripts' + }, // { // name: '批量指令', // icon: markRaw(Pointer), // index: '/onekey' // }, - // { - // name: '脚本库', - // icon: markRaw(ArrowRight), - // index: '/scripts' - // }, { name: '系统设置', icon: markRaw(Setting), diff --git a/web/src/store/index.js b/web/src/store/index.js index c9746e1..6574b33 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -10,6 +10,7 @@ const useStore = defineStore({ hostList: [], groupList: [], sshList: [], + scriptList: [], HostStatusSocket: null, user: localStorage.getItem('user') || null, token: sessionStorage.getItem('token') || localStorage.getItem('token') || null, @@ -37,6 +38,7 @@ const useStore = defineStore({ await this.getGroupList() await this.getHostList() await this.getSSHList() + await this.getScriptList() }, async getHostList() { const { data: hostList } = await $api.getHostList() @@ -54,6 +56,11 @@ const useStore = defineStore({ // console.log('sshList:', sshList) this.$patch({ sshList }) }, + async getScriptList() { + const { data: scriptList } = await $api.getScriptList() + // console.log('scriptList:', scriptList) + this.$patch({ scriptList }) + }, getHostPing() { setTimeout(() => { this.hostList.forEach((item) => { diff --git a/web/src/views/credentials/index.vue b/web/src/views/credentials/index.vue index 1f774ab..101b93f 100644 --- a/web/src/views/credentials/index.vue +++ b/web/src/views/credentials/index.vue @@ -80,7 +80,7 @@ diff --git a/web/src/views/group/index.vue b/web/src/views/group/index.vue index 3b6f4a6..4ac43c3 100644 --- a/web/src/views/group/index.vue +++ b/web/src/views/group/index.vue @@ -44,7 +44,7 @@ diff --git a/web/src/views/scripts/index.vue b/web/src/views/scripts/index.vue index 1294743..11b07b9 100644 --- a/web/src/views/scripts/index.vue +++ b/web/src/views/scripts/index.vue @@ -1,19 +1,175 @@ - +.host_count { + display: block; + width: 100px; + text-align: center; + font-size: 15px; + color: #87cf63; + cursor: pointer; +} + \ No newline at end of file diff --git a/web/src/views/server/components/host-form.vue b/web/src/views/server/components/host-form.vue index 982bb60..f09ace7 100644 --- a/web/src/views/server/components/host-form.vue +++ b/web/src/views/server/components/host-form.vue @@ -288,7 +288,7 @@ const visible = computed({ set: (newVal) => emit('update:show', newVal) }) -const title = computed(() => props.defaultData ? '修改实例' : '新增实例') +const title = computed(() => props.defaultData ? '修改实例' : '添加实例') let groupList = computed(() => $store.groupList) let sshList = computed(() => $store.sshList) diff --git a/web/src/views/terminal/components/info-side.vue b/web/src/views/terminal/components/info-side.vue index 0083dcc..df46c86 100644 --- a/web/src/views/terminal/components/info-side.vue +++ b/web/src/views/terminal/components/info-side.vue @@ -190,7 +190,7 @@ --> 命令输入框 diff --git a/web/src/views/terminal/components/terminal-tab.vue b/web/src/views/terminal/components/terminal-tab.vue index 2afde4a..2f6b693 100644 --- a/web/src/views/terminal/components/terminal-tab.vue +++ b/web/src/views/terminal/components/terminal-tab.vue @@ -26,7 +26,7 @@ const props = defineProps({ } }) -const emit = defineEmits(['input',]) +const emit = defineEmits(['inputCommand',]) const socket = ref(null) const term = ref(null) @@ -206,8 +206,7 @@ const onData = () => { let acsiiCode = key.codePointAt() if (acsiiCode === 22) return handlePaste() if (acsiiCode === 6) return searchBar.value.show() - emit('input', { idx: props.index, key }) - // console.log('input:', key) + emit('inputCommand', key) socket.value.emit('input', key) }) } @@ -218,8 +217,7 @@ const handleClear = () => { const handlePaste = async () => { let key = await navigator.clipboard.readText() - emit('input', { idx: props.index, key }) - // console.log('input:', key) + emit('inputCommand', key) socket.value.emit('input', key) term.value.focus() } @@ -231,7 +229,7 @@ const focusTab = () => { }, 200) } -const handleInputCommand = (command) => { +const inputCommand = (command) => { socket.value.emit('input', command) } @@ -250,7 +248,7 @@ onBeforeUnmount(() => { defineExpose({ focusTab, handleResize, - handleInputCommand, + inputCommand, handleClear }) diff --git a/web/src/views/terminal/components/terminal.vue b/web/src/views/terminal/components/terminal.vue index 1534bac..591120d 100644 --- a/web/src/views/terminal/components/terminal.vue +++ b/web/src/views/terminal/components/terminal.vue @@ -1,34 +1,99 @@