diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..923ec87 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,67 @@ +# build.yml + +name: Auto Build + +on: + workflow_dispatch: + push: + branches: + - master + - release-test + + + +env: + ELECTRON_OUTPUT_PATH: interface/dist_electron + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + + +jobs: + release: + name: build and release electron app + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-11, windows-latest] + + steps: + - name: Check out git repository + uses: actions/checkout@v3 + + - uses: actions/setup-go@v3 + with: + go-version: 1.18 + + - name: build go client + run: make build-client + working-directory: go-client + + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: '16.x' + + - name: Install system deps + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils + + - name: Yarn install + run: | + yarn + yarn global add xvfb-maybe + + - name: Build & release app + run: | + yarn build + + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + XCODE_APP_TEAM_ID: ${{ secrets.XCODE_APP_TEAM_ID }} + XCODE_APP_LOADER_EMAIL: ${{ secrets.XCODE_APP_LOADER_EMAIL }} + XCODE_APP_LOADER_PASSWORD: ${{ secrets.XCODE_APP_LOADER_PASSWORD }} + BUILD_CERTIFICATE_BASE64: ${{ secrets.CSC_LINK }} + P12_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} \ No newline at end of file diff --git a/README.md b/README.md index 040cedf..0f3202e 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Jumpserve Clients +# Jumpserve Client JumpServer 客户端,支持本地本地客户端拉起。 ## 概览 diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..823acaa --- /dev/null +++ b/README_EN.md @@ -0,0 +1,58 @@ +# Jumpserve Client + +JumpServer client, supports local client pull up. + +## Overview + +![windows](static/windows.png) + + +## Install + +### Mac + +Download the Mac installation package, install JumpServer.dmg and put JumpServer.app into Applications. Double-click to open JumpServer.app to complete the custom protocol. + + +### Win + +Download the Win installation package and double-click the JumpServer installation package file (you need to wait for 10 seconds). + + +### Linux + +Download the Linux installation package, double-click the Deb installation package file, and open the installation package manager to install it. Or install it using the command + +``` + +sudo dpkg -i + +``` + + +## uninstall + +### Windows + +Just uninstall the control panel directly. + + +### Mac + +Remove JumpServer.app. + + +### Linux + +Click to open Software Manager to uninstall. Or use the command to uninstall + +``` + +sudo dpkg --purge + +``` + + +## package + +- [packaging scheme](https://github.com/jumpserver/apps/blob/master/README_PACK.md) diff --git a/go-client/config.json b/go-client/config.json index a98f57d..6a43d8b 100644 --- a/go-client/config.json +++ b/go-client/config.json @@ -82,7 +82,7 @@ "download_url": "内置", "type": "windows", "path": "mstsc.exe", - "arg_format": "", + "arg_format": "{file}", "match_first": [], "is_internal": true, "is_default": true, @@ -169,7 +169,7 @@ "download_url": "https://github.com/FuckDoctors/rdm-builder/releases/download/2022.5.1/resp-2022.5.1.exe", "type": "databases", "path": "", - "arg_format": "--settings-dir {config_file}", + "arg_format": "--settings-dir*{config_file}", "match_first": [], "is_internal": false, "is_default": false, @@ -183,7 +183,8 @@ "name": "terminal", "display_name": "Terminal", "protocol": [ - "ssh" + "ssh", + "telnet" ], "comment": "Terminal是MacOS操作系统上的虚拟终端应用软件,位于“实用工具”文件夹内。", "download_url": "内置", @@ -199,7 +200,8 @@ "name": "iterm", "display_name": "iTerm2", "protocol": [ - "ssh" + "ssh", + "telnet" ], "comment": "iTerm2是MacOS操作系统上的虚拟终端应用软件。\n\n!!!手动下载安装,点击保存启用!!!", "download_url": "https://iterm2.com/downloads.html", @@ -240,7 +242,7 @@ "download_url": "内置", "type": "windows", "path": "/Applications/Microsoft Remote Desktop.app", - "arg_format": "", + "arg_format": "{file}", "match_first": [], "is_internal": true, "is_default": true, @@ -319,7 +321,8 @@ "name": "terminal", "display_name": "Terminal", "protocol": [ - "ssh" + "ssh", + "telnet" ], "comment": "Terminal是Linux操作系统上的虚拟终端应用软件。", "download_url": "内置", @@ -343,7 +346,7 @@ "download_url": "https://remmina.org/how-to-install-remmina/-内置", "type": "windows", "path": "remmina", - "arg_format": "", + "arg_format": "{file}", "match_first": [], "is_internal": true, "is_default": false, @@ -359,7 +362,7 @@ "download_url": "https://github.com/FreeRDP/FreeRDP-内置", "type": "windows", "path": "xfreerdp", - "arg_format": "", + "arg_format": "{file} /cert-ignore", "match_first": [], "is_internal": true, "is_default": false, diff --git a/go-client/pkg/awaken/awaken.go b/go-client/pkg/awaken/awaken.go index e2e7115..6443183 100755 --- a/go-client/pkg/awaken/awaken.go +++ b/go-client/pkg/awaken/awaken.go @@ -126,7 +126,7 @@ func (r *Rouse) Run() { switch protocol { case "rdp": r.HandleRDP(&appConfig) - case "ssh", "sftp": + case "ssh", "sftp", "telnet": r.HandleSSH(&appConfig) case "mysql", "mariadb", "postgresql", "redis", "oracle", "sqlserver": r.HandleDB(&appConfig) diff --git a/go-client/pkg/awaken/awaken_darwin.go b/go-client/pkg/awaken/awaken_darwin.go index 031cadf..9344cda 100755 --- a/go-client/pkg/awaken/awaken_darwin.go +++ b/go-client/pkg/awaken/awaken_darwin.go @@ -28,7 +28,8 @@ func awakenSSHCommand(r *Rouse, cfg *config.AppConfig) *exec.Cmd { var appItem *config.AppItem var appLst []config.AppItem switch r.Protocol { - case "ssh": + case "ssh", "telnet": + r.Protocol = "ssh" appLst = cfg.MacOS.Terminal case "sftp": appLst = cfg.MacOS.FileTransfer diff --git a/go-client/pkg/awaken/awaken_linux.go b/go-client/pkg/awaken/awaken_linux.go index 22df10b..38a695b 100755 --- a/go-client/pkg/awaken/awaken_linux.go +++ b/go-client/pkg/awaken/awaken_linux.go @@ -31,7 +31,8 @@ func awakenRDPCommand(filePath string, cfg *config.AppConfig) *exec.Cmd { if appItem == nil { return nil } - cmd := exec.Command(appItem.Name, filePath) + args := strings.Replace(appItem.ArgFormat, "{file}", filePath, 1) + cmd := exec.Command(appItem.Name, strings.Split(args, " ")...) return cmd } @@ -39,7 +40,8 @@ func awakenSSHCommand(r *Rouse, cfg *config.AppConfig) *exec.Cmd { var appItem *config.AppItem var appLst []config.AppItem switch r.Protocol { - case "ssh": + case "ssh", "telnet": + r.Protocol = "ssh" appLst = cfg.Linux.Terminal case "sftp": appLst = cfg.Linux.FileTransfer diff --git a/go-client/pkg/awaken/awaken_windows.go b/go-client/pkg/awaken/awaken_windows.go index 92a15e9..85a5b9e 100755 --- a/go-client/pkg/awaken/awaken_windows.go +++ b/go-client/pkg/awaken/awaken_windows.go @@ -37,7 +37,8 @@ func handleSSH(r *Rouse, cfg *config.AppConfig) *exec.Cmd { var appItem *config.AppItem var appLst []config.AppItem switch r.Protocol { - case "ssh": + case "ssh", "telnet": + r.Protocol = "ssh" appLst = cfg.Windows.Terminal case "sftp": appLst = cfg.Windows.FileTransfer @@ -107,7 +108,7 @@ func handleDB(r *Rouse, cfg *config.AppConfig) *exec.Cmd { ss := make(map[string]string) ss["host"] = r.Host ss["port"] = strconv.Itoa(r.Port) - ss["name"] = r.Name + ss["name"] = r.getName() ss["auth"] = r.Token.ID + "@" + r.Value ss["ssh_agent_path"] = "" ss["ssh_password"] = "" @@ -130,7 +131,13 @@ func handleDB(r *Rouse, cfg *config.AppConfig) *exec.Cmd { connectMap["config_file"] = currentPath } commands := getCommandFromArgs(connectMap, appItem.ArgFormat) - return exec.Command(appPath, strings.Split(commands, " ")...) + if strings.Contains(commands, "*") { + commands := strings.Split(commands, "*") + return exec.Command(appPath, commands...) + } else { + commands := strings.Split(commands, " ") + return exec.Command(appPath, commands...) + } } func handleCommand(r *Rouse, cfg *config.AppConfig) *exec.Cmd { diff --git a/interface/build/win/installer.nsh b/interface/build/win/installer.nsh index c588eb5..9cc3a55 100644 --- a/interface/build/win/installer.nsh +++ b/interface/build/win/installer.nsh @@ -8,6 +8,14 @@ WriteRegStr HKCR "jms\shell" "" "" WriteRegStr HKCR "jms\shell\open" "" "" WriteRegStr HKCR "jms\shell\open\command" "" '"$INSTDIR\resources\bin\windows\JumpServerClient.exe" "%1"' + + AccessControl::GrantOnFile \ + "$INSTDIR\resources\bin" "(BU)" "GenericWrite + GenericRead" + Pop $R0 + ${If} $R0 == error + Pop $R0 + MessageBox MB_OK `AccessControl error: $R0` + ${EndIf} !macroend !macro customUnInstall diff --git a/interface/build/x86-ansi/AccessControl.dll b/interface/build/x86-ansi/AccessControl.dll new file mode 100644 index 0000000..c5b3b54 Binary files /dev/null and b/interface/build/x86-ansi/AccessControl.dll differ diff --git a/interface/build/x86-unicode/AccessControl.dll b/interface/build/x86-unicode/AccessControl.dll new file mode 100644 index 0000000..f1840fc Binary files /dev/null and b/interface/build/x86-unicode/AccessControl.dll differ diff --git a/interface/package.json b/interface/package.json index 9b2b167..36162b3 100644 --- a/interface/package.json +++ b/interface/package.json @@ -1,7 +1,7 @@ { "name": "jumpserver-client", "author": "Fit2Cloud Technology Co., Ltd.; ", - "version": "v2.0.2", + "version": "v2.1.0", "homepage": "https://jumpserver.org", "private": true, "scripts": { @@ -22,6 +22,7 @@ "element-plus": "^2.3.0", "fs-extra": "^11.1.1", "vue": "^3.2.13", + "vue-i18n": "^9.6.5", "vue-router": "^4.1.6" }, "devDependencies": { @@ -31,7 +32,7 @@ "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-service": "~5.0.0", - "electron": "^23.1.3", + "electron": "^22.3.27", "electron-devtools-installer": "^3.1.0", "eslint": "^8.31.0", "eslint-config-standard": ">=16.0.0", diff --git a/interface/src/background.js b/interface/src/background.js index d9914cc..d36ce77 100644 --- a/interface/src/background.js +++ b/interface/src/background.js @@ -1,4 +1,4 @@ -import {app, BrowserWindow, ipcMain, protocol} from "electron"; +import {app, shell, BrowserWindow, ipcMain, protocol} from "electron"; import {createProtocol} from "vue-cli-plugin-electron-builder/lib"; import installExtension, {VUEJS3_DEVTOOLS} from "electron-devtools-installer"; import path from 'path' @@ -37,7 +37,12 @@ async function createWindow() { }, }); - if (process.env.WEBPACK_DEV_SERVER_URL) { + mainWindow.webContents.setWindowOpenHandler((details) => { + shell.openExternal(details.url) + return { action: "deny" } + }); + + if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL); // if (!process.env.IS_TEST) mainWindow.webContents.openDevTools() diff --git a/interface/src/i18n/i18n.js b/interface/src/i18n/i18n.js new file mode 100644 index 0000000..f9a3cb1 --- /dev/null +++ b/interface/src/i18n/i18n.js @@ -0,0 +1,17 @@ +import { createI18n } from 'vue-i18n' +import messages from './langs' + + +const browserLang = navigator.systemLanguage || navigator.language +let lang = browserLang || 'zh' +lang = localStorage.getItem('lang') || lang.slice(0, 2) +const i18n = createI18n({ + locale: lang, + legacy: false, + fallbackLocale: 'en', + silentFallbackWarn: true, + silentTranslationWarn: true, + messages +}) + +export default i18n diff --git a/interface/src/i18n/langs/en.json b/interface/src/i18n/langs/en.json new file mode 100644 index 0000000..b0a5016 --- /dev/null +++ b/interface/src/i18n/langs/en.json @@ -0,0 +1,43 @@ +{ + "Common": { + "JumpServerClient": "JumpServerClient", + "AppDesc": "Introduction", + "DownloadUrl":"Download Link", + "AppPath": "Application Path", + "Priority": "Priority Matching", + "ProtocolPlaceholder": "Please select the database protocol that matches first", + "ProtocolValidate": "Protocol cannot be empty", + "PathValidate": "Path cannot be empty", + "PathPlaceholder": "Please select the database tool launcher path", + "DefaultApp": "Default Application", + "Configured": "Configured", + "NotConfigured": "Not Configured", + "OracleOCI": "The client needs to use OCI version 21.0 and above to connect to the Oracle." + }, + "Dialog": { + "Save&Default": "Save And Default", + "Cancel": "Cancel", + "Save":"Save" + }, + "Router": { + "Terminal": "Terminal", + "RemoteDesktop": "Remote Desktop", + "FileTransfer": "File Transfer", + "Database": "Database", + "AboutUs": "About Us", + "Language": "Language" + }, + "AboutUs": { + "Author": "Author", + "Version": "Version", + "Copyright": "Copyright", + "AllRightsReserved": "All Rights Reserved", + "OfficialWebsite": "Official Website", + "OnlineDocumentation": "Online Documentation", + "CommunityForum": "Community Forum", + "AboutUs": "About Us" + }, + "Language": { + "ChooseLanguage": "Choose Language" + } +} diff --git a/interface/src/i18n/langs/index.js b/interface/src/i18n/langs/index.js new file mode 100644 index 0000000..0656a9e --- /dev/null +++ b/interface/src/i18n/langs/index.js @@ -0,0 +1,15 @@ +import zhLocale from "element-plus/lib/locale/lang/zh-cn"; +import enLocale from "element-plus/lib/locale/lang/en"; +import zh from './zh.json' +import en from './en.json' + +export default { + zh: { + ...zhLocale, + ...zh + }, + en: { + ...enLocale, + ...en + } +} diff --git a/interface/src/i18n/langs/zh.json b/interface/src/i18n/langs/zh.json new file mode 100644 index 0000000..90a898b --- /dev/null +++ b/interface/src/i18n/langs/zh.json @@ -0,0 +1,43 @@ +{ + "Common": { + "JumpServerClient": "JumpServer客户端", + "AppDesc": "应用说明", + "DownloadUrl":"下载地址", + "AppPath": "应用路径", + "Priority": "优先匹配", + "ProtocolPlaceholder": "请选择优先匹配的数据库协议", + "ProtocolValidate": "协议不能为空", + "PathValidate": "路径不能为空", + "PathPlaceholder": "请选择数据库工具启动程序路径", + "DefaultApp": "默认应用", + "Configured": "已配置", + "NotConfigured": "未配置", + "OracleOCI": "本地客户端连 Oracle 数据库需要使用 21.0 及以上版本 OCI" + }, + "Dialog": { + "Save&Default": "保存且默认", + "Cancel": "关闭", + "Save":"保存" + }, + "Router": { + "Terminal": "远程终端", + "RemoteDesktop": "远程桌面", + "FileTransfer": "文件传输", + "Database": "数据库", + "AboutUs": "关于我们", + "Language": "语言设置" + }, + "AboutUs": { + "Author": "作者", + "Version": "版本", + "Copyright": "版权公告", + "AllRightsReserved": "版权所有", + "OfficialWebsite": "产品官网", + "OnlineDocumentation": "在线文档", + "CommunityForum": "社区论坛", + "AboutUs": "关于我们" + }, + "Language": { + "ChooseLanguage": "选择语言" + } +} diff --git a/interface/src/main.js b/interface/src/main.js index ba1ee5f..097ea94 100644 --- a/interface/src/main.js +++ b/interface/src/main.js @@ -7,9 +7,11 @@ import 'element-plus/dist/index.css' import 'element-plus/theme-chalk/dark/css-vars.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import './renderer/assets/fonts/font-awesome.min.css'; +import i18n from './i18n/i18n' const app = createApp(App) +app.use(i18n) app.use(ElementUI) app.use(router) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { diff --git a/interface/src/renderer/components/Dialog.vue b/interface/src/renderer/components/Dialog.vue index f8604b4..9315bff 100644 --- a/interface/src/renderer/components/Dialog.vue +++ b/interface/src/renderer/components/Dialog.vue @@ -1,24 +1,25 @@ - diff --git a/interface/src/renderer/pages/About.vue b/interface/src/renderer/pages/About.vue index 94f39d9..1136a67 100644 --- a/interface/src/renderer/pages/About.vue +++ b/interface/src/renderer/pages/About.vue @@ -1,14 +1,22 @@ @@ -18,9 +26,20 @@ div { height: 100%; text-align: center; color: #e1e1e1e1; + + p { + font-size: 15px; + } + + a { + line-height: 50px; + margin: 0 15px; + color: #3f83cc; + } + #logo { margin-top: 80px; - width: 300px; + width: 200px; } } diff --git a/interface/src/renderer/pages/Databases.vue b/interface/src/renderer/pages/Databases.vue index d11301c..f4d078a 100644 --- a/interface/src/renderer/pages/Databases.vue +++ b/interface/src/renderer/pages/Databases.vue @@ -9,22 +9,22 @@ @cancel="onCancelItem" @save="onSaveItem" > - - + + - + - - + + - + @@ -36,7 +36,7 @@ - 本地客户端连 Oracle 数据库需要使用 21.0 及以上版本 OCI + {{ $t('Common.OracleOCI') }} - - + + - + - + diff --git a/interface/src/renderer/pages/Language.vue b/interface/src/renderer/pages/Language.vue new file mode 100644 index 0000000..a148399 --- /dev/null +++ b/interface/src/renderer/pages/Language.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/interface/src/renderer/pages/RemoteDesktop.vue b/interface/src/renderer/pages/RemoteDesktop.vue index 2391fea..e236ae5 100644 --- a/interface/src/renderer/pages/RemoteDesktop.vue +++ b/interface/src/renderer/pages/RemoteDesktop.vue @@ -8,17 +8,17 @@ @save="onSaveItem" @confirm="onConfirmItem" > - - + + - - + + - + diff --git a/interface/src/renderer/pages/Terminal.vue b/interface/src/renderer/pages/Terminal.vue index a011c48..780a963 100644 --- a/interface/src/renderer/pages/Terminal.vue +++ b/interface/src/renderer/pages/Terminal.vue @@ -8,17 +8,17 @@ @save="onSaveItem" @confirm="onConfirmItem" > - - + + - + - + diff --git a/interface/src/renderer/router/index.js b/interface/src/renderer/router/index.js index 66f2eea..c12d41c 100644 --- a/interface/src/renderer/router/index.js +++ b/interface/src/renderer/router/index.js @@ -33,6 +33,11 @@ export default createRouter({ path: '/about', name: 'aboutPage', component: () => import('@/pages/About.vue') + }, + { + path: '/i18n', + name: 'languagePage', + component: () => import('@/pages/Language.vue') } ] },