├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc └── img │ ├── addHost.png │ ├── addVar.png │ ├── fileManager.gif │ ├── homePage.png │ ├── openFileManager.jpg │ ├── openSession.png │ ├── quickCmd.png │ ├── quickCmdImpl.png │ ├── selectTips.png │ ├── session.png │ ├── showTips.png │ ├── showVar.png │ ├── useVar.png │ ├── varEffect.png │ └── wx.jpg ├── server ├── .dockerignore ├── .mvn │ └── wrapper │ │ ├── MavenWrapperDownloader.java │ │ └── maven-wrapper.properties ├── README.md ├── lib │ └── pty4j-0.12.7.jar ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ └── main │ ├── docker │ ├── Dockerfile.jvm │ ├── Dockerfile.legacy-jar │ ├── Dockerfile.native │ └── Dockerfile.native-micro │ ├── java │ └── com │ │ └── tshell │ │ ├── common │ │ ├── cache │ │ │ └── Cache.java │ │ └── response │ │ │ ├── BaseResponse.java │ │ │ └── PageData.java │ │ ├── config │ │ ├── AppLifecycleBean.java │ │ ├── AuthTypeConverter.java │ │ ├── CustomDateDeserializer.java │ │ ├── JsonToMapConverter.java │ │ ├── NanoIdGenerator.java │ │ └── ProxyTypeConverter.java │ │ ├── controller │ │ ├── AiChatController.java │ │ ├── CmdController.java │ │ ├── CmdGroupController.java │ │ ├── ConfigController.java │ │ ├── ConnectionLogController.java │ │ ├── DesktopController.java │ │ ├── ErrHandle.java │ │ ├── FileManagerController.java │ │ ├── GlobalVariableController.java │ │ ├── HistoryCmdController.java │ │ ├── RetrieveController.java │ │ ├── SessionGroupController.java │ │ ├── ShortcutCmdController.java │ │ ├── ShortcutCmdGroupController.java │ │ ├── SshSessionController.java │ │ ├── SyncChannelController.java │ │ ├── SystemController.java │ │ ├── TerminalController.java │ │ ├── TtyTypeController.java │ │ └── ValidationExceptionHandle.java │ │ ├── core │ │ ├── FileInfo.java │ │ ├── FileManager.java │ │ ├── FileManagerService.java │ │ ├── FileOperate.java │ │ ├── FileType.java │ │ ├── JschPtyConnector.java │ │ ├── LocalTtyConnector.java │ │ ├── Parameter.java │ │ ├── ProgressMonitor.java │ │ ├── SshPtyConnector.java │ │ ├── TerminalService.java │ │ ├── TtySize.java │ │ ├── client │ │ │ ├── ClientFactory.java │ │ │ ├── ClientHandler.java │ │ │ ├── CmdClientHandler.java │ │ │ ├── LinuxBashClientHandler.java │ │ │ ├── LinuxZshClientHandler.java │ │ │ ├── TtyType.java │ │ │ └── UbuntuBashClientHandler.java │ │ ├── local │ │ │ └── LocalTtyConnector.java │ │ ├── ssh │ │ │ ├── SshConfig.java │ │ │ ├── SshSessionPoll.java │ │ │ ├── SshUtil.java │ │ │ ├── WebShellService.java │ │ │ └── jsch │ │ │ │ ├── ChannelSftpFactory.java │ │ │ │ ├── ChannelSftpPool.java │ │ │ │ ├── ChannelSftpPoolFactory.java │ │ │ │ ├── ChannelType.java │ │ │ │ ├── JschUtil.java │ │ │ │ └── MyReflectionConfiguration.java │ │ ├── task │ │ │ └── TaskExecutor.java │ │ └── tty │ │ │ ├── TtyConnector.java │ │ │ ├── TtyConnectorFactory.java │ │ │ └── TtyConnectorManager.java │ │ ├── module │ │ ├── dto │ │ │ ├── PageDTO.java │ │ │ ├── cmd │ │ │ │ ├── AddCmdDto.java │ │ │ │ ├── AddCmdOptionDTO.java │ │ │ │ ├── AddCmdOsTypeDTO.java │ │ │ │ ├── AddCmdParameterDTO.java │ │ │ │ ├── UpdCmdDto.java │ │ │ │ ├── UpdCmdOptionDTO.java │ │ │ │ ├── UpdCmdOsTypeDTO.java │ │ │ │ └── UpdCmdParameterDTO.java │ │ │ ├── config │ │ │ │ ├── AppearanceDTO.java │ │ │ │ └── TerminalDTO.java │ │ │ ├── fileManager │ │ │ │ ├── CreateDTO.java │ │ │ │ └── UploadDTO.java │ │ │ ├── globalVariable │ │ │ │ ├── AddGlobalVar.java │ │ │ │ └── UpdGlobalVar.java │ │ │ ├── historyCmd │ │ │ │ ├── AddHistoryCmdDTO.java │ │ │ │ └── GetHistoryCmdPageDTO.java │ │ │ ├── hostGroup │ │ │ │ ├── AddHostGroupDTO.java │ │ │ │ └── UpdHostGroupDTO.java │ │ │ ├── retrieve │ │ │ │ └── ParseTemplateDTO.java │ │ │ ├── session │ │ │ │ ├── AddSshSessionDTO.java │ │ │ │ └── UpdSshSessionDTO.java │ │ │ ├── shortcutCmd │ │ │ │ ├── AddShortcutCmdDTO.java │ │ │ │ ├── AddShortcutCmdImplDTO.java │ │ │ │ ├── UpdShortcutCmdDTO.java │ │ │ │ └── UpdShortcutCmdImplDTO.java │ │ │ └── terminal │ │ │ │ ├── LocalInitConnectDTO.java │ │ │ │ └── SshInitConnectDTO.java │ │ ├── entity │ │ │ ├── Appearance.java │ │ │ ├── Breakpoint.java │ │ │ ├── Cmd.java │ │ │ ├── CmdOsType.java │ │ │ ├── Config.java │ │ │ ├── ConnectionLog.java │ │ │ ├── GlobalVariable.java │ │ │ ├── Group.java │ │ │ ├── GroupCmd.java │ │ │ ├── GroupHost.java │ │ │ ├── GroupShortcutCmd.java │ │ │ ├── HistoryCmd.java │ │ │ ├── LocalSession.java │ │ │ ├── Option.java │ │ │ ├── OsType.java │ │ │ ├── Parameter.java │ │ │ ├── Session.java │ │ │ ├── SessionGroup.java │ │ │ ├── ShortcutCmd.java │ │ │ ├── ShortcutCmdGroup.java │ │ │ ├── ShortcutCmdImpl.java │ │ │ ├── ShortcutCmdImplTtyType.java │ │ │ ├── SshSession.java │ │ │ └── TransferRecord.java │ │ ├── enums │ │ │ ├── AuthType.java │ │ │ └── ProxyType.java │ │ └── vo │ │ │ ├── CmdOptionVO.java │ │ │ ├── CmdOsTypeVO.java │ │ │ ├── CmdParameterVO.java │ │ │ ├── CmdVO.java │ │ │ ├── CompleteCmdVO.java │ │ │ ├── CompleteTransferRecordVO.java │ │ │ ├── GroupHostTree.java │ │ │ ├── PageVO.java │ │ │ ├── RetrieveMenuVO.java │ │ │ ├── RetrieveVO.java │ │ │ ├── ShortcutCmdImplVO.java │ │ │ ├── ShortcutCmdVO.java │ │ │ ├── SshSessionVO.java │ │ │ ├── TransferCountVO.java │ │ │ ├── config │ │ │ ├── AppearanceVO.java │ │ │ └── TerminalVO.java │ │ │ └── connectionLog │ │ │ └── TopVO.java │ │ ├── service │ │ ├── AiServer.java │ │ ├── CmdService.java │ │ ├── ConfigService.java │ │ ├── ConnectionLogService.java │ │ ├── DesktopService.java │ │ ├── HistoryCmdService.java │ │ ├── HostGroupService.java │ │ ├── RetrieveService.java │ │ ├── ShortcutCmdService.java │ │ ├── SshSessionService.java │ │ ├── SyncChannelService.java │ │ └── VariableService.java │ │ ├── socket │ │ ├── RetrieveCmdData.java │ │ ├── ShellSocketHandler.java │ │ └── WebSocket.java │ │ └── utils │ │ ├── OsUtil.java │ │ ├── PlaceholderResolver.java │ │ ├── StrUtil.java │ │ ├── enums │ │ ├── EnumUtil.java │ │ ├── KeyValueEnum.java │ │ └── ValueEnum.java │ │ └── io │ │ ├── FastByteArrayOutputStream.java │ │ ├── FastByteBuffer.java │ │ └── IoUtil.java │ └── resources │ ├── META-INF │ └── resources │ │ └── index.html │ └── application.properties └── ui ├── .cz-config.js ├── .editorconfig ├── .env ├── .env-config.ts ├── .env.development ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierrc.js ├── README.md ├── build ├── config │ ├── define.ts │ ├── index.ts │ └── proxy.ts ├── index.ts ├── plugins │ ├── compress.ts │ ├── html.ts │ ├── index.ts │ ├── mock.ts │ ├── unocss.ts │ ├── unplugin.ts │ ├── visualizer.ts │ └── vue.ts └── utils │ └── index.ts ├── commitlint.config.js ├── index.html ├── mock ├── api │ ├── auth.ts │ ├── demo.ts │ ├── index.ts │ ├── management.ts │ └── route.ts ├── index.ts └── model │ ├── auth.ts │ ├── index.ts │ └── route.ts ├── package.json ├── public ├── favicon.ico └── resource │ ├── loading.css │ └── loading.js ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── icons │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square30x30Logo.png │ ├── Square310x310Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── StoreLogo.png │ ├── shell-Icon.ico │ ├── shell-icon.icns │ ├── shell-icon.png │ ├── shell128x128.png │ └── shell32x32.png ├── src │ └── main.rs └── tauri.conf.json ├── src ├── App.vue ├── assets │ ├── fonts │ │ ├── aguazyuan-bold.ttf │ │ ├── aguazyuan-light.ttf │ │ └── aguazyuan-regular.ttf │ └── svg │ │ ├── activity.svg │ │ ├── at-sign.svg │ │ ├── avatar.svg │ │ ├── banner.svg │ │ ├── cast.svg │ │ ├── chrome.svg │ │ ├── copy.svg │ │ ├── custom-icon.svg │ │ ├── empty-data.svg │ │ ├── heart.svg │ │ ├── logo-fill.svg │ │ ├── logo.svg │ │ ├── network-error.svg │ │ ├── no-permission.svg │ │ ├── not-found.svg │ │ ├── service-error.svg │ │ └── wind.svg ├── components │ ├── business │ │ ├── LoadingEmptyWrapper.vue │ │ └── LoginAgreement.vue │ ├── common │ │ ├── DarkModeContainer.vue │ │ ├── DarkModeSwitch.vue │ │ ├── ExceptionBase.vue │ │ ├── HoverContainer.vue │ │ ├── NaiveProvider.vue │ │ └── SystemLogo.vue │ └── custom │ │ ├── BetterScroll.vue │ │ ├── CountTo.vue │ │ ├── GithubLink.vue │ │ ├── IconSelect.vue │ │ ├── ImageVerify.vue │ │ ├── SvgIcon.vue │ │ └── WebSiteLink.vue ├── composables │ ├── events.ts │ ├── index.ts │ ├── layout.ts │ ├── router.ts │ └── system.ts ├── config │ ├── index.ts │ ├── map-sdk.ts │ ├── regexp.ts │ └── service.ts ├── context │ ├── demo.ts │ └── index.ts ├── directives │ ├── index.ts │ ├── login.ts │ ├── network.ts │ └── permission.ts ├── enum │ ├── business.ts │ ├── common.ts │ ├── index.ts │ └── system.ts ├── hooks │ ├── business │ │ ├── index.ts │ │ ├── useCountDown.ts │ │ ├── useImageVerify.ts │ │ ├── useNaiveTable.ts │ │ └── useSmsCode.ts │ ├── common │ │ ├── index.ts │ │ ├── useBoolean.ts │ │ ├── useContext.ts │ │ ├── useLoading.ts │ │ ├── useLoadingEmpty.ts │ │ └── useReload.ts │ └── index.ts ├── layouts │ ├── BasicLayout │ │ └── index.vue │ ├── BlankLayout │ │ └── index.vue │ ├── common │ │ ├── AiChat │ │ │ ├── components │ │ │ │ ├── Chat │ │ │ │ │ └── index.vue │ │ │ │ ├── ChatButton │ │ │ │ │ └── index.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── GlobalBackTop │ │ │ └── index.vue │ │ ├── GlobalContent │ │ │ └── index.vue │ │ ├── GlobalFooter │ │ │ └── index.vue │ │ ├── GlobalHeader │ │ │ ├── components │ │ │ │ ├── FullScreen.vue │ │ │ │ ├── GithubSite.vue │ │ │ │ ├── GlobalBreadcrumb.vue │ │ │ │ ├── HeaderMenu.vue │ │ │ │ ├── MenuCollapse.vue │ │ │ │ ├── MessageList.vue │ │ │ │ ├── SettingButton.vue │ │ │ │ ├── SystemMessage.vue │ │ │ │ ├── ThemeMode.vue │ │ │ │ ├── UserAvatar.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── GlobalLogo │ │ │ └── index.vue │ │ ├── GlobalSearch │ │ │ ├── components │ │ │ │ ├── SearchFooter.vue │ │ │ │ ├── SearchModal.vue │ │ │ │ ├── SearchResult.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── GlobalSider │ │ │ ├── components │ │ │ │ ├── VerticalMixSider │ │ │ │ │ ├── components │ │ │ │ │ │ ├── MixMenuCollapse.vue │ │ │ │ │ │ ├── MixMenuDetail.vue │ │ │ │ │ │ ├── MixMenuDrawer.vue │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.vue │ │ │ │ ├── VerticalSider │ │ │ │ │ ├── components │ │ │ │ │ │ ├── VerticalMenu.vue │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── GlobalTab │ │ │ ├── components │ │ │ │ ├── ReloadButton │ │ │ │ │ └── index.vue │ │ │ │ ├── SettingButton │ │ │ │ │ └── index.vue │ │ │ │ ├── TabDetail │ │ │ │ │ ├── components │ │ │ │ │ │ ├── ContextMenu.vue │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── index.vue │ │ │ │ ├── WindowButton │ │ │ │ │ └── index.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── index.ts │ │ └── shell │ │ │ ├── cmd │ │ │ ├── components │ │ │ │ ├── cmd │ │ │ │ │ ├── addCmd.vue │ │ │ │ │ └── updCmd.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ │ ├── globalVariable │ │ │ ├── components │ │ │ │ ├── addGlobaVariable.vue │ │ │ │ ├── index.ts │ │ │ │ └── updGlobaVariable.vue │ │ │ └── index.vue │ │ │ ├── host │ │ │ ├── components │ │ │ │ ├── group │ │ │ │ │ ├── addGroup.vue │ │ │ │ │ └── updGroup.vue │ │ │ │ ├── index.ts │ │ │ │ └── session │ │ │ │ │ ├── addSession.vue │ │ │ │ │ └── updSession.vue │ │ │ └── index.vue │ │ │ ├── index.ts │ │ │ ├── osType │ │ │ ├── components │ │ │ │ ├── group │ │ │ │ │ ├── addOsType.vue │ │ │ │ │ └── updOsType.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ │ └── shortcutCmd │ │ │ ├── components │ │ │ ├── cmd │ │ │ │ ├── addCmd.vue │ │ │ │ └── updCmd.vue │ │ │ ├── group │ │ │ │ ├── addGroup.vue │ │ │ │ └── updGroup.vue │ │ │ └── index.ts │ │ │ └── index.vue │ └── index.ts ├── main.ts ├── plugins │ ├── assets.ts │ └── index.ts ├── router │ ├── guard │ │ ├── dynamic.ts │ │ ├── index.ts │ │ └── permission.ts │ ├── helpers │ │ ├── index.ts │ │ └── scroll.ts │ ├── index.ts │ ├── modules │ │ ├── index.ts │ │ ├── projectSetting.ts │ │ └── shell.ts │ └── routes │ │ └── index.ts ├── service │ ├── api │ │ ├── auth.ts │ │ ├── demo.adapter.ts │ │ ├── demo.ts │ │ ├── index.ts │ │ ├── management.adapter.ts │ │ └── management.ts │ ├── index.ts │ └── request │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── instance.ts │ │ └── request.ts ├── settings │ ├── color.json │ ├── color.ts │ ├── index.ts │ ├── theme.json │ └── theme.ts ├── store │ ├── index.ts │ ├── modules │ │ ├── app │ │ │ └── index.ts │ │ ├── auth │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── route │ │ │ └── index.ts │ │ ├── tab │ │ │ ├── helpers.ts │ │ │ └── index.ts │ │ └── theme │ │ │ ├── helpers.ts │ │ │ └── index.ts │ └── subscribe │ │ ├── app.ts │ │ ├── index.ts │ │ └── theme.ts ├── styles │ ├── css │ │ ├── global.css │ │ ├── reset.css │ │ ├── scrollbar.css │ │ └── transition.css │ └── scss │ │ ├── global.scss │ │ └── scrollbar.scss ├── theblind_shell │ ├── service │ │ └── shell │ │ │ ├── aiChat.ts │ │ │ ├── channel.ts │ │ │ ├── cmd.ts │ │ │ ├── config.ts │ │ │ ├── connectionLog.ts │ │ │ ├── desktop.js │ │ │ ├── fileManager.ts │ │ │ ├── globalVariable.ts │ │ │ ├── historyCmd.ts │ │ │ ├── host.ts │ │ │ ├── hostGroup.ts │ │ │ ├── retrieve.ts │ │ │ ├── shortcutCmd.ts │ │ │ ├── shortcutCmdGroup.ts │ │ │ ├── system.js │ │ │ ├── terminal.ts │ │ │ └── ttyType.ts │ ├── typings │ │ ├── OsType.ts │ │ └── cmd.ts │ └── utils │ │ └── TheBlindShellUtils.js ├── typings │ ├── api.d.ts │ ├── business.d.ts │ ├── env.d.ts │ ├── expose.d.ts │ ├── global.d.ts │ ├── package.d.ts │ ├── route.d.ts │ ├── router.d.ts │ ├── system.d.ts │ └── utils.d.ts ├── utils │ ├── auth │ │ ├── index.ts │ │ └── user.ts │ ├── common │ │ ├── color.ts │ │ ├── contextmenu.ts │ │ ├── icon.ts │ │ ├── index.ts │ │ ├── number.ts │ │ ├── object.ts │ │ ├── pattern.ts │ │ ├── theme.ts │ │ └── typeof.ts │ ├── crypto │ │ └── index.ts │ ├── form │ │ ├── index.ts │ │ └── rule.ts │ ├── index.ts │ ├── router │ │ ├── auth.ts │ │ ├── breadcrumb.ts │ │ ├── cache.ts │ │ ├── component.ts │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── menu.ts │ │ ├── module.ts │ │ ├── regexp.ts │ │ └── urlParams.ts │ ├── service │ │ ├── error.ts │ │ ├── handler.ts │ │ ├── index.ts │ │ ├── msg.ts │ │ └── transform.ts │ ├── shell │ │ ├── index.ts │ │ ├── msgWebSocket.ts │ │ └── webSocket.ts │ └── storage │ │ ├── index.ts │ │ ├── local.ts │ │ └── session.ts └── views │ ├── index.ts │ ├── project │ └── setting │ │ ├── components │ │ ├── Appearance │ │ │ └── index.vue │ │ ├── DarkMode │ │ │ └── index.vue │ │ ├── SettingMenu │ │ │ └── index.vue │ │ ├── Terminal │ │ │ └── index.vue │ │ ├── TerminalConfig │ │ │ └── index.vue │ │ ├── ThemeColorSelect │ │ │ ├── components │ │ │ │ ├── ColorCheckbox.vue │ │ │ │ ├── ColorModal.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ └── index.ts │ │ └── index.vue │ ├── shell │ ├── info │ │ └── index.vue │ └── terminal │ │ ├── components │ │ ├── FileManager.vue │ │ ├── HistoryCmd.vue │ │ ├── RetrieveBox.vue │ │ └── ShortcutCmd.vue │ │ └── index.vue │ └── system-view │ ├── login │ ├── components │ │ ├── BindWechat │ │ │ └── index.vue │ │ ├── CodeLogin │ │ │ └── index.vue │ │ ├── LoginBg │ │ │ ├── components │ │ │ │ ├── CornerBottom.vue │ │ │ │ ├── CornerTop.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── PwdLogin │ │ │ ├── components │ │ │ │ ├── OtherAccount.vue │ │ │ │ ├── OtherLogin.vue │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── Register │ │ │ └── index.vue │ │ ├── ResetPwd │ │ │ └── index.vue │ │ └── index.ts │ └── index.vue │ ├── no-permission │ └── index.vue │ ├── not-found-page │ └── index.vue │ ├── not-found │ └── index.vue │ └── service-error │ └── index.vue ├── tsconfig.json ├── uno.config.ts └── vite.config.ts /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 版本日志 2 | **0.1** 3 | * ✨集成 SSH 4 | * ✨快捷命令提示 一次编写,处处运行 5 | * ✨支持 linux中的终端如:bash,zsh 6 | * ✨支持 中文 7 | * ✨支持 SFTP -------------------------------------------------------------------------------- /doc/img/addHost.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/addHost.png -------------------------------------------------------------------------------- /doc/img/addVar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/addVar.png -------------------------------------------------------------------------------- /doc/img/fileManager.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/fileManager.gif -------------------------------------------------------------------------------- /doc/img/homePage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/homePage.png -------------------------------------------------------------------------------- /doc/img/openFileManager.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/openFileManager.jpg -------------------------------------------------------------------------------- /doc/img/openSession.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/openSession.png -------------------------------------------------------------------------------- /doc/img/quickCmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/quickCmd.png -------------------------------------------------------------------------------- /doc/img/quickCmdImpl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/quickCmdImpl.png -------------------------------------------------------------------------------- /doc/img/selectTips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/selectTips.png -------------------------------------------------------------------------------- /doc/img/session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/session.png -------------------------------------------------------------------------------- /doc/img/showTips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/showTips.png -------------------------------------------------------------------------------- /doc/img/showVar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/showVar.png -------------------------------------------------------------------------------- /doc/img/useVar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/useVar.png -------------------------------------------------------------------------------- /doc/img/varEffect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/varEffect.png -------------------------------------------------------------------------------- /doc/img/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/doc/img/wx.jpg -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !target/*-runner 3 | !target/*-runner.jar 4 | !target/lib/* 5 | !target/quarkus-app/* -------------------------------------------------------------------------------- /server/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # T-Shell Server 2 | 后台服务 3 | 4 | [![Supported JVM Versions](https://img.shields.io/badge/JVM-19-brightgreen.svg?style=for-the-badge&logo=openjdk)](https://github.com/openjdk/jdk) 5 | [![Quarkus Versions](https://img.shields.io/badge/Quarkus-brightgreen.svg?style=for-the-badge&logo=openjdk)](https://github.com/openjdk/jdk) 6 | 7 | ---- 8 | 基于最新的Graalvm JDK 19和Quarkus, 将java 打包成native -------------------------------------------------------------------------------- /server/lib/pty4j-0.12.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/server/lib/pty4j-0.12.7.jar -------------------------------------------------------------------------------- /server/src/main/docker/Dockerfile.native: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # 4 | # Before building the container image run: 5 | # 6 | # ./mvnw package -Pnative 7 | # 8 | # Then, build the image with: 9 | # 10 | # docker build -f src/main/docker/Dockerfile.native -t quarkus/demo1 . 11 | # 12 | # Then run the container using: 13 | # 14 | # docker run -i --rm -p 8080:8080 quarkus/demo1 15 | # 16 | ### 17 | FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5 18 | WORKDIR /work/ 19 | RUN chown 1001 /work \ 20 | && chmod "g+rwX" /work \ 21 | && chown 1001:root /work 22 | COPY --chown=1001:root target/*-runner /work/application 23 | 24 | EXPOSE 8080 25 | USER 1001 26 | 27 | CMD ["./application", "-Dquarkus.http.sshSession=0.0.0.0"] 28 | -------------------------------------------------------------------------------- /server/src/main/docker/Dockerfile.native-micro: -------------------------------------------------------------------------------- 1 | #### 2 | # This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. 3 | # It uses a micro base image, tuned for Quarkus native executables. 4 | # It reduces the size of the resulting container image. 5 | # Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. 6 | # 7 | # Before building the container image run: 8 | # 9 | # ./mvnw package -Pnative 10 | # 11 | # Then, build the image with: 12 | # 13 | # docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/demo1 . 14 | # 15 | # Then run the container using: 16 | # 17 | # docker run -i --rm -p 8080:8080 quarkus/demo1 18 | # 19 | ### 20 | FROM quay.io/quarkus/quarkus-micro-image:1.0 21 | WORKDIR /work/ 22 | RUN chown 1001 /work \ 23 | && chmod "g+rwX" /work \ 24 | && chown 1001:root /work 25 | COPY --chown=1001:root target/*-runner /work/application 26 | 27 | EXPOSE 8080 28 | USER 1001 29 | 30 | CMD ["./application", "-Dquarkus.http.sshSession=0.0.0.0"] 31 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/common/cache/Cache.java: -------------------------------------------------------------------------------- 1 | package com.tshell.common.cache; 2 | 3 | import java.util.Optional; 4 | 5 | public interface Cache { 6 | 7 | Optional get(K key); 8 | void put(K key, V value); 9 | 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/common/response/PageData.java: -------------------------------------------------------------------------------- 1 | package com.tshell.common.response; 2 | 3 | import io.quarkus.runtime.annotations.RegisterForReflection; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/8/2 8 | */ 9 | @RegisterForReflection 10 | public record PageData ( 11 | 12 | int page, 13 | Object data, 14 | int pageCount, 15 | int pageSize 16 | ){ 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/AppLifecycleBean.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import com.tshell.core.FileManagerService; 4 | import io.quarkus.runtime.ShutdownEvent; 5 | import io.quarkus.runtime.StartupEvent; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | import jakarta.enterprise.context.ApplicationScoped; 9 | import jakarta.enterprise.event.Observes; 10 | import jakarta.inject.Inject; 11 | 12 | @Slf4j 13 | @ApplicationScoped 14 | public class AppLifecycleBean { 15 | 16 | @Inject 17 | FileManagerService fileManagerService; 18 | 19 | void onStart(@Observes StartupEvent ev) { 20 | log.info("The application is starting..."); 21 | } 22 | 23 | void onStop(@Observes ShutdownEvent ev) { 24 | log.info("The application is stopping..."); 25 | fileManagerService.saveAllBreakpoint(); 26 | fileManagerService.deleteTempDir(); 27 | } 28 | } -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/AuthTypeConverter.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import com.tshell.module.enums.AuthType; 4 | import com.tshell.utils.enums.EnumUtil; 5 | 6 | import jakarta.persistence.AttributeConverter; 7 | 8 | /** 9 | * @author TheBlind 10 | * @version 1.0 11 | */ 12 | public class AuthTypeConverter implements AttributeConverter { 13 | @Override 14 | public Integer convertToDatabaseColumn(AuthType authType) { 15 | return authType.getValue(); 16 | } 17 | 18 | @Override 19 | public AuthType convertToEntityAttribute(Integer value) { 20 | //默认为 密码模式 21 | if (value == null) { 22 | return AuthType.PWD; 23 | } 24 | return EnumUtil.getEnumByValue(AuthType.values(), value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/CustomDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import jakarta.enterprise.context.ApplicationScoped; 8 | import java.io.IOException; 9 | import java.time.Instant; 10 | import java.time.LocalDate; 11 | import java.time.LocalDateTime; 12 | import java.time.ZoneId; 13 | 14 | /** 15 | * @author TheBlind 16 | */ 17 | @ApplicationScoped 18 | 19 | public class CustomDateDeserializer extends JsonDeserializer { 20 | 21 | @Override 22 | public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 23 | 24 | return LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(jsonParser.getText())), ZoneId.systemDefault()) .toLocalDate(); 25 | } 26 | } -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/JsonToMapConverter.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.tshell.module.entity.Config; 6 | import jakarta.persistence.AttributeConverter; 7 | import jakarta.persistence.Converter; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * @author TheBlind 13 | */ 14 | @Converter 15 | public class JsonToMapConverter 16 | implements AttributeConverter { 17 | 18 | @Override 19 | public String convertToDatabaseColumn(Config.Terminal terminal) { 20 | return JSONUtil.toJsonStr(terminal); 21 | } 22 | 23 | @Override 24 | public Config.Terminal convertToEntityAttribute(String dbData) { 25 | 26 | if (dbData == null) { 27 | return new Config.Terminal(); 28 | } 29 | try { 30 | ObjectMapper objectMapper = new ObjectMapper(); 31 | return objectMapper.readValue(dbData, Config.Terminal.class); 32 | } catch (IOException e) { 33 | new RuntimeException(e); 34 | } 35 | return new Config.Terminal(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/NanoIdGenerator.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import cn.hutool.core.util.IdUtil; 4 | import io.quarkus.runtime.annotations.RegisterForReflection; 5 | import org.hibernate.HibernateException; 6 | import org.hibernate.engine.spi.SharedSessionContractImplementor; 7 | import org.hibernate.id.IdentifierGenerator; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * @author TheBlind 13 | */ 14 | @RegisterForReflection 15 | public class NanoIdGenerator implements IdentifierGenerator { 16 | 17 | @Override 18 | public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException { 19 | 20 | return IdUtil.nanoId(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/config/ProxyTypeConverter.java: -------------------------------------------------------------------------------- 1 | package com.tshell.config; 2 | 3 | import com.tshell.module.enums.ProxyType; 4 | import com.tshell.utils.enums.EnumUtil; 5 | 6 | import jakarta.persistence.AttributeConverter; 7 | 8 | /** 9 | * @author TheBlind 10 | * @version 1.0 11 | */ 12 | public class ProxyTypeConverter implements AttributeConverter { 13 | @Override 14 | public Integer convertToDatabaseColumn(ProxyType authType) { 15 | return authType.getValue(); 16 | } 17 | 18 | @Override 19 | public ProxyType convertToEntityAttribute(Integer value) { 20 | //默认为 密码模式 21 | if (value == null) { 22 | return ProxyType.DIRECT; 23 | } 24 | return EnumUtil.getEnumByValue(ProxyType.values(), value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/AiChatController.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | import com.tshell.common.response.BaseResponse; 4 | import com.tshell.service.AiServer; 5 | import io.smallrye.common.annotation.RunOnVirtualThread; 6 | 7 | import jakarta.inject.Inject; 8 | import jakarta.transaction.Transactional; 9 | import jakarta.ws.rs.GET; 10 | import jakarta.ws.rs.Path; 11 | import jakarta.ws.rs.Produces; 12 | import jakarta.ws.rs.QueryParam; 13 | import jakarta.ws.rs.core.MediaType; 14 | 15 | /** 16 | * ai聊天 17 | * @author TheBlind 18 | * @version 1.0 19 | */ 20 | @Path("/ai") 21 | @Produces(MediaType.APPLICATION_JSON) 22 | @RunOnVirtualThread 23 | public class AiChatController { 24 | @Inject 25 | AiServer aiServer; 26 | 27 | @GET 28 | @Path("/chat") 29 | @Transactional(rollbackOn = Exception.class) 30 | public BaseResponse getListById(@QueryParam("question") String question) { 31 | return BaseResponse.ok("success",aiServer.doChat(question)); 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/ConnectionLogController.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | import com.tshell.common.response.BaseResponse; 4 | import com.tshell.module.vo.connectionLog.TopVO; 5 | import com.tshell.service.ConnectionLogService; 6 | import io.smallrye.common.annotation.RunOnVirtualThread; 7 | 8 | import jakarta.inject.Inject; 9 | import jakarta.ws.rs.GET; 10 | import jakarta.ws.rs.Path; 11 | import jakarta.ws.rs.Produces; 12 | import jakarta.ws.rs.core.MediaType; 13 | import java.util.List; 14 | 15 | /** 16 | * 连接日志 17 | * 18 | * @author TheBlind 19 | * @version 1.0 20 | */ 21 | @Path("/connectionLog") 22 | @Produces(MediaType.APPLICATION_JSON) 23 | @RunOnVirtualThread 24 | public class ConnectionLogController { 25 | 26 | @Inject 27 | ConnectionLogService connectionLogService; 28 | 29 | 30 | @GET 31 | @Path("/topList") 32 | public BaseResponse> topList() { 33 | return BaseResponse.ok(connectionLogService.topList()); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/DesktopController.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | import com.tshell.common.response.BaseResponse; 4 | import com.tshell.service.DesktopService; 5 | import io.smallrye.common.annotation.RunOnVirtualThread; 6 | 7 | import jakarta.inject.Inject; 8 | import jakarta.ws.rs.*; 9 | import jakarta.ws.rs.core.MediaType; 10 | import java.io.IOException; 11 | 12 | /** 13 | * @author TheBlind 14 | * @version 1.0 15 | */ 16 | @Path("/desktop") 17 | @Produces(MediaType.APPLICATION_JSON) 18 | @RunOnVirtualThread 19 | public class DesktopController { 20 | 21 | @Inject 22 | DesktopService desktopService; 23 | 24 | @POST 25 | @Path("/toGithub") 26 | @Consumes(MediaType.APPLICATION_JSON) 27 | public BaseResponse toGithub() throws IOException { 28 | desktopService.toGithub(); 29 | return BaseResponse.ok(true); 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/ErrHandle.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.tshell.common.response.BaseResponse; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import jakarta.ws.rs.WebApplicationException; 8 | import jakarta.ws.rs.core.Response; 9 | import jakarta.ws.rs.ext.ExceptionMapper; 10 | import jakarta.ws.rs.ext.Provider; 11 | 12 | /** 13 | * @author TheBlind 14 | * @date 2022/5/8 15 | */ 16 | @Slf4j 17 | @Provider 18 | public class ErrHandle implements ExceptionMapper { 19 | 20 | @Override 21 | public Response toResponse(Exception exception){ 22 | log.error("失败 to handle request", exception); 23 | int code = 500; 24 | if (exception instanceof WebApplicationException) { 25 | code = ((WebApplicationException) exception).getResponse().getStatus(); 26 | } 27 | BaseResponse response = new BaseResponse(); 28 | response.setCode(code); 29 | response.setMessage(exception.getMessage()); 30 | 31 | if (log.isDebugEnabled()) { 32 | response.setDevMessage(exception.getMessage()); 33 | } 34 | return Response.ok() 35 | .entity(JSONUtil.toJsonStr(response)) 36 | .build(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/SystemController.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | 4 | import io.quarkus.runtime.Quarkus; 5 | import io.smallrye.common.annotation.RunOnVirtualThread; 6 | 7 | import jakarta.ws.rs.Consumes; 8 | import jakarta.ws.rs.POST; 9 | import jakarta.ws.rs.Path; 10 | import jakarta.ws.rs.Produces; 11 | import jakarta.ws.rs.core.MediaType; 12 | 13 | @Path("/system") 14 | @Produces(MediaType.APPLICATION_JSON) 15 | @RunOnVirtualThread 16 | public class SystemController { 17 | 18 | 19 | @Consumes(MediaType.APPLICATION_JSON) 20 | @POST 21 | @Path("/shutdown") 22 | public void shutdown(){ 23 | Quarkus.asyncExit(0); 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/controller/ValidationExceptionHandle.java: -------------------------------------------------------------------------------- 1 | package com.tshell.controller; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.tshell.common.response.BaseResponse; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import jakarta.validation.ValidationException; 8 | import jakarta.ws.rs.core.Response; 9 | import jakarta.ws.rs.ext.ExceptionMapper; 10 | import jakarta.ws.rs.ext.Provider; 11 | 12 | /** 13 | * @author TheBlind 14 | * @date 2022/5/8 15 | */ 16 | @Slf4j 17 | @Provider 18 | public class ValidationExceptionHandle implements ExceptionMapper { 19 | 20 | @Override 21 | public Response toResponse(ValidationException exception){ 22 | log.error("失败 to handle request", exception); 23 | int code = 500; 24 | BaseResponse response = new BaseResponse(); 25 | response.setCode(code); 26 | response.setMessage(exception.getMessage()); 27 | if (log.isDebugEnabled()) { 28 | response.setDevMessage(exception.getMessage()); 29 | } 30 | return Response.ok() 31 | .entity(JSONUtil.toJsonStr(response)) 32 | .build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/FileInfo.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | 4 | public record FileInfo( 5 | String path, 6 | String name, 7 | long size, 8 | FileType type, 9 | String modifyDate 10 | ) { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/FileOperate.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | /** 4 | * @author TheBlind 5 | * 文件操作 6 | */ 7 | 8 | public enum FileOperate { 9 | /** 10 | * 上传 11 | */ 12 | PUT, 13 | /** 14 | * 获取 15 | */ 16 | GET 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/FileType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | /** 4 | * 文件类型 5 | * 6 | * @author TheBlind 7 | */ 8 | public enum FileType { 9 | /** 10 | * 目录(是一种特殊的文件) 11 | */ 12 | DIRECTORY, 13 | /** 14 | * 文件 15 | */ 16 | FILE; 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/Parameter.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | import com.tshell.module.enums.AuthType; 4 | import com.tshell.module.enums.ProxyType; 5 | import lombok.Data; 6 | 7 | /** 8 | * https://www.lmlphp.com/user/59001/article/item/600745/ 9 | *

10 | * https://www.cnblogs.com/kxqblog/p/16143152.html mobaxterm 怎么设置私钥登录 11 | * 12 | * @author TheBlind 13 | * @date 2022/7/21 14 | */ 15 | 16 | @Data 17 | public class Parameter { 18 | 19 | protected String sessionId; 20 | protected String channelId; 21 | protected TtySize ttySize; 22 | protected Integer ttyTypeId; 23 | 24 | @Data 25 | public static class SshParameter extends Parameter { 26 | protected String ip; 27 | protected int port; 28 | protected String username; 29 | protected String pwd; 30 | protected String proxyHost; 31 | protected Integer proxyPort; 32 | protected String privateKeyFile; 33 | protected String passphrase; 34 | protected AuthType authType; 35 | protected ProxyType proxyType; 36 | } 37 | 38 | 39 | } 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/ProgressMonitor.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | public interface ProgressMonitor { 4 | boolean call(Progress progress); 5 | 6 | void end(int sessionId, Progress progress); 7 | 8 | public record Progress(FileOperate op, double percent, String channelId, String fromPath, String toPath, 9 | String fileName, String key) { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/TtySize.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/9 6 | */ 7 | public record TtySize( 8 | int columns, 9 | int lines, 10 | int width, 11 | int height 12 | ) { 13 | } 14 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/client/LinuxZshClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.client; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/8/14 6 | */ 7 | public final class LinuxZshClientHandler extends LinuxBashClientHandler { 8 | public LinuxZshClientHandler(TtyType ttyType) { 9 | super(ttyType); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/client/UbuntuBashClientHandler.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.client; 2 | 3 | /** 4 | * @author TheBlind 5 | * @version 1.0 6 | */ 7 | public non-sealed class UbuntuBashClientHandler extends LinuxBashClientHandler { 8 | public UbuntuBashClientHandler(com.tshell.core.client.TtyType ttyType) { 9 | super(ttyType); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/ssh/SshConfig.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.ssh; 2 | 3 | /** 4 | * 5 | * ssh配置 6 | * @author TheBlind 7 | * @version 1.0 8 | * 9 | */ 10 | public record SshConfig(String ip, 11 | int port, 12 | String username, 13 | String pwd) { 14 | } 15 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/ssh/jsch/ChannelSftpPool.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.ssh.jsch; 2 | 3 | import com.tshell.core.Parameter; 4 | import com.tshell.core.ssh.SshConfig; 5 | import com.jcraft.jsch.ChannelSftp; 6 | import org.apache.commons.pool2.KeyedPooledObjectFactory; 7 | import org.apache.commons.pool2.impl.GenericKeyedObjectPool; 8 | import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; 9 | 10 | /** 11 | * @author TheBlind 12 | */ 13 | public class ChannelSftpPool extends GenericKeyedObjectPool { 14 | 15 | 16 | public ChannelSftpPool(KeyedPooledObjectFactory factory) { 17 | super(factory); 18 | } 19 | 20 | public ChannelSftpPool(KeyedPooledObjectFactory factory, GenericKeyedObjectPoolConfig config) { 21 | super(factory, config); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/ssh/jsch/ChannelType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.ssh.jsch; 2 | 3 | import com.tshell.utils.enums.ValueEnum; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/7/29 8 | */ 9 | public enum ChannelType implements ValueEnum { 10 | /** 11 | * 会话 12 | */ 13 | SESSION("session"), 14 | /** 15 | * shell 16 | */ 17 | SHELL("shell"), 18 | /** 19 | * 执行命令 20 | */ 21 | EXEC("exec"), 22 | /** 23 | * 直连 24 | */ 25 | X11("x11"), 26 | /** 27 | * 直连 28 | */ 29 | AGENT_FORWARDING("auth-agent@openssh.com"), 30 | /** 31 | * 直连 32 | */ 33 | DIRECT_TCPIP("direct-tcpip"), 34 | /** 35 | * 转发TCP ip 36 | */ 37 | FORWARDED_TCPIP("forwarded-tcpip"), 38 | /** 39 | * sftp 40 | */ 41 | SFTP("sftp"), 42 | /** 43 | * 子系统 44 | */ 45 | SUBSYSTEM("subsystem"); 46 | 47 | private final String value; 48 | 49 | ChannelType(String value) { 50 | this.value = value; 51 | } 52 | 53 | @Override 54 | public String getValue() { 55 | return this.value; 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/ssh/jsch/MyReflectionConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.ssh.jsch; 2 | 3 | import io.quarkus.runtime.annotations.RegisterForReflection; 4 | import org.apache.commons.pool2.impl.DefaultEvictionPolicy; 5 | 6 | /** 7 | * @author TheBlind 8 | * @version 1.0 9 | * @date 2022/10/13 9:37 10 | */ 11 | @RegisterForReflection(targets={ DefaultEvictionPolicy.class}) 12 | public class MyReflectionConfiguration { 13 | } 14 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/core/tty/TtyConnectorFactory.java: -------------------------------------------------------------------------------- 1 | package com.tshell.core.tty; 2 | 3 | import com.tshell.core.JschPtyConnector; 4 | import com.tshell.core.Parameter; 5 | import com.jcraft.jsch.JSchException; 6 | 7 | import jakarta.inject.Singleton; 8 | import java.io.IOException; 9 | 10 | /** 11 | * @author TheBlind 12 | * @date 2022/7/22 13 | */ 14 | @Singleton 15 | public class TtyConnectorFactory { 16 | 17 | 18 | 19 | public TtyConnector getTtyConnector(Parameter parameter) throws IOException { 20 | /** 21 | * jdk 12 支持 case 多个 箭头操作符,无需break switch 不仅可以作为语句,也可以作为表达式。 22 | * jdk 14 yield 处理复杂逻辑时返回值 switch 返回数据 23 | * jdk 17 支持 null 以及 Object 为switch中的参数 24 | */ 25 | switch (parameter) { 26 | case Parameter.SshParameter s -> { 27 | return new JschPtyConnector(s); 28 | // return new SshTtyConnector(s); 29 | } 30 | /* case Parameter p -> { 31 | return new LocalTtyConnector(p); 32 | }*/ 33 | default -> throw new IllegalStateException("Unexpected value: " + parameter); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/PageDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto; 2 | 3 | import io.quarkus.runtime.annotations.RegisterForReflection; 4 | 5 | @RegisterForReflection 6 | public record PageDTO( 7 | int page, 8 | int size, 9 | T param 10 | ) { 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/AddCmdDto.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.tshell.module.entity.Cmd; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author TheBlind 10 | * @date 2022/6/28 11 | */ 12 | public record AddCmdDto( 13 | @JsonProperty("channelId") 14 | Integer parentCmdId, 15 | @JsonProperty("channelId") 16 | 17 | String cmdText, 18 | @JsonProperty("channelId") 19 | 20 | String describe, 21 | @JsonProperty("channelId") 22 | 23 | List cmdOsTypeIdList, 24 | @JsonProperty("channelId") 25 | 26 | List cmdOptionList, 27 | @JsonProperty("channelId") 28 | 29 | List cmdParameterList) { 30 | public Cmd convert(){ 31 | return Cmd.builder().cmdText(this.cmdText).parentCmdId(parentCmdId).describe(describe).build(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/AddCmdOptionDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/7/12 10 | */ 11 | public record AddCmdOptionDTO( 12 | @JsonProperty("channelId") 13 | List optionNameList, 14 | /** 15 | * 描述 16 | */ 17 | @JsonProperty("channelId") 18 | String description 19 | 20 | ) { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/AddCmdOsTypeDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/12 6 | */ 7 | public record AddCmdOsTypeDTO(Integer osTypeId, boolean compatible){} 8 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/AddCmdParameterDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import com.tshell.module.entity.Parameter; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/7/12 8 | */ 9 | public record AddCmdParameterDTO( 10 | /** 11 | * 参数下标 12 | */ 13 | int index, 14 | /** 15 | * 描述 16 | */ 17 | String description) { 18 | 19 | public Parameter convert(){ 20 | return Parameter.builder().index(this.index).description(this.description).build(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/UpdCmdDto.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import com.tshell.module.entity.Cmd; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/6/28 10 | */ 11 | public record UpdCmdDto(Integer id, 12 | int parentCmdId, 13 | 14 | String cmd, 15 | 16 | String describe, 17 | 18 | List cmdOsTypeIdList, 19 | 20 | List cmdOptionList, 21 | 22 | List cmdParameterList) { 23 | public void copyProperty(Cmd cmd) { 24 | cmd.setCmdText(this.cmd); 25 | cmd.setDescribe(describe); 26 | cmd.setParentCmdId(parentCmdId); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/UpdCmdOptionDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/7/12 8 | */ 9 | public record UpdCmdOptionDTO( 10 | Integer id, 11 | List optionNameList, 12 | /** 13 | * 描述 14 | */ 15 | String description 16 | 17 | ) { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/UpdCmdOsTypeDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/12 6 | */ 7 | public record UpdCmdOsTypeDTO(Integer id, Integer osTypeId) 8 | { } 9 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/cmd/UpdCmdParameterDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.cmd; 2 | 3 | import com.tshell.module.entity.Parameter; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/7/12 8 | */ 9 | public record UpdCmdParameterDTO( 10 | Integer id, 11 | /** 12 | * 参数下标 13 | */ 14 | int index, 15 | /** 16 | * 描述 17 | */ 18 | String description) { 19 | 20 | public Parameter convert() { 21 | Parameter parameter = Parameter.builder().index(this.index).description(this.description).build(); 22 | parameter.id = id; 23 | return parameter; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/config/AppearanceDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.config; 2 | 3 | import com.tshell.module.entity.Config; 4 | 5 | /** 6 | * @author TheBlind 7 | */ 8 | public record AppearanceDTO( 9 | String fontFamily, 10 | Integer fontSize, 11 | Integer fontWeight, 12 | Integer fontWeightBold, 13 | boolean enableFontLigatures, 14 | String cursorShape, 15 | boolean cursorBlink 16 | ) { 17 | 18 | public void convert(Config.Terminal terminal) { 19 | terminal.setCursorBlink(this.cursorBlink()); 20 | terminal.setCursorShape(this.cursorShape); 21 | terminal.setFontSize(this.fontSize); 22 | terminal.setEnableFontLigatures(this.enableFontLigatures); 23 | terminal.setFontWeight(this.fontWeight); 24 | terminal.setFontWeightBold(this.fontWeightBold); 25 | terminal.setFontFamily(this.fontFamily); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/config/TerminalDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.config; 2 | 3 | import com.tshell.module.entity.Config; 4 | import com.tshell.module.vo.config.TerminalVO; 5 | 6 | /** 7 | * @author TheBlind 8 | */ 9 | public record TerminalDTO( 10 | Integer scrollbackLines, 11 | String wordSeparator, 12 | boolean copyOnSelect) { 13 | 14 | public void convert(Config.Terminal terminal) { 15 | terminal.setScrollbackLines(this.scrollbackLines()); 16 | terminal.setWordSeparator(this.wordSeparator()); 17 | terminal.setCopyOnSelect(this.copyOnSelect()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/fileManager/CreateDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.fileManager; 2 | 3 | import com.tshell.core.FileType; 4 | 5 | public record CreateDTO(String path, 6 | String name, 7 | FileType type) { 8 | } 9 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/fileManager/UploadDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.fileManager; 2 | 3 | import lombok.Data; 4 | import org.jboss.resteasy.annotations.providers.multipart.PartType; 5 | 6 | import jakarta.ws.rs.FormParam; 7 | import jakarta.ws.rs.core.MediaType; 8 | import java.io.InputStream; 9 | import java.util.List; 10 | 11 | public record UploadDTO( 12 | 13 | String path, 14 | 15 | List filePaths 16 | ){ 17 | 18 | } 19 | 20 | /* 21 | @Data 22 | public class UploadDTO { 23 | @FormParam("path") 24 | String path; 25 | @FormParam("filename") 26 | String filename; 27 | 28 | @PartType(MediaType.APPLICATION_OCTET_STREAM) 29 | @FormParam("file") 30 | InputStream file; 31 | } 32 | */ 33 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/globalVariable/AddGlobalVar.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.globalVariable; 2 | 3 | import com.tshell.module.entity.GlobalVariable; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | 7 | /** 8 | * @author TheBlind 9 | * @version 1.0 10 | */ 11 | public record AddGlobalVar( 12 | @NotBlank(message = "变量名称不能为空") 13 | String varName, 14 | @NotBlank(message = "值不能为空") 15 | String value) { 16 | public GlobalVariable convert() { 17 | return GlobalVariable.builder().value(this.value.strip()).varName(this.varName.strip()).build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/globalVariable/UpdGlobalVar.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.globalVariable; 2 | 3 | import com.tshell.module.entity.GlobalVariable; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | /** 9 | * @author TheBlind 10 | * @version 1.0 11 | */ 12 | public record UpdGlobalVar( 13 | @NotNull(message = "id不能为空") 14 | Integer id, 15 | @NotBlank(message = "变量名称不能为空") 16 | String varName, 17 | @NotBlank(message = "值不能为空") 18 | String value) { 19 | public void copyProperty(GlobalVariable source) { 20 | source.setVarName(this.varName); 21 | source.setValue(this.value); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/historyCmd/AddHistoryCmdDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.historyCmd; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.tshell.module.entity.HistoryCmd; 5 | 6 | public record AddHistoryCmdDTO( 7 | String cmdText, 8 | 9 | String sessionId, 10 | 11 | /** 12 | * 会话类型 13 | */ 14 | int sessionType 15 | ) { 16 | public HistoryCmd convert() { 17 | return HistoryCmd.builder().cmdText(this.cmdText.trim()).createTime(DateUtil.now()).sessionId(this.sessionId).build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/historyCmd/GetHistoryCmdPageDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.historyCmd; 2 | 3 | import com.tshell.config.CustomDateDeserializer; 4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 5 | 6 | import java.time.LocalDate; 7 | 8 | public record GetHistoryCmdPageDTO( 9 | String sessionId, 10 | 11 | @JsonDeserialize(using = CustomDateDeserializer.class) 12 | LocalDate startDate, 13 | @JsonDeserialize(using = CustomDateDeserializer.class) 14 | LocalDate endDate 15 | ) { 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/hostGroup/AddHostGroupDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.hostGroup; 2 | 3 | import com.tshell.module.entity.SessionGroup; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | 7 | public record AddHostGroupDTO( 8 | @NotBlank(message = "组名称不能为空") 9 | String groupName, 10 | int parentId 11 | ) { 12 | public SessionGroup convert() { 13 | return SessionGroup.builder().groupName(groupName).parentId(parentId).build(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/hostGroup/UpdHostGroupDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.hostGroup; 2 | 3 | import com.tshell.module.entity.SessionGroup; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | public record UpdHostGroupDTO( 9 | @NotNull(message = "id不能为null") 10 | Integer id, 11 | @NotBlank(message = "组名称不能为空") 12 | String groupName, 13 | int parentId 14 | ) { 15 | public void copyProperty(SessionGroup source) { 16 | source.setGroupName(this.groupName); 17 | source.setParentId(parentId); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/retrieve/ParseTemplateDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.retrieve; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author TheBlind 7 | * @version 1.0 8 | * @date 2022/10/19 13:36 9 | */ 10 | public record ParseTemplateDTO( 11 | Integer id, 12 | String channelId, 13 | Map items 14 | ) { 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/shortcutCmd/AddShortcutCmdDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.shortcutCmd; 2 | 3 | 4 | import com.tshell.module.entity.ShortcutCmd; 5 | 6 | import jakarta.validation.constraints.NotEmpty; 7 | import java.util.List; 8 | 9 | /** 10 | * 快捷命令 11 | */ 12 | public record AddShortcutCmdDTO( 13 | String name, 14 | @NotEmpty 15 | List shortcutCmdGroupIdList, 16 | @NotEmpty 17 | List shortcutCmdImplList 18 | ) { 19 | public ShortcutCmd convert() { 20 | ShortcutCmd shortcutCmd = new ShortcutCmd(); 21 | shortcutCmd.setName(name); 22 | return shortcutCmd; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/shortcutCmd/AddShortcutCmdImplDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.shortcutCmd; 2 | 3 | import com.tshell.module.entity.ShortcutCmdImpl; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/7/12 10 | */ 11 | public record AddShortcutCmdImplDTO( 12 | String cmdTemplate, 13 | 14 | List shortcutCmdTtyTypeIdList 15 | ) { 16 | public ShortcutCmdImpl convert() { 17 | return ShortcutCmdImpl.builder().cmdTemplate(cmdTemplate).build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/shortcutCmd/UpdShortcutCmdDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.shortcutCmd; 2 | 3 | 4 | import com.tshell.module.entity.ShortcutCmd; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 快捷命令 10 | */ 11 | public record UpdShortcutCmdDTO( 12 | Integer id, 13 | String name, 14 | List shortcutCmdGroupIdList, 15 | List shortcutCmdImplList 16 | ) { 17 | public void copyProperty(ShortcutCmd shortcutCmd) { 18 | shortcutCmd.setName(name); 19 | } 20 | public ShortcutCmd convert() { 21 | ShortcutCmd shortcutCmd = new ShortcutCmd(); 22 | shortcutCmd.setName(name); 23 | return shortcutCmd; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/shortcutCmd/UpdShortcutCmdImplDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.shortcutCmd; 2 | 3 | import com.tshell.module.entity.ShortcutCmdImpl; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/7/12 10 | */ 11 | public record UpdShortcutCmdImplDTO( 12 | Integer id, 13 | String cmdTemplate, 14 | 15 | List shortcutCmdTtyTypeIdList 16 | ) { 17 | public ShortcutCmdImpl convert() { 18 | return ShortcutCmdImpl.builder().cmdTemplate(cmdTemplate).build(); 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/terminal/LocalInitConnectDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.terminal; 2 | 3 | import com.tshell.core.TtySize; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | /** 9 | * @author TheBlind 10 | * @date 2022/8/16 11 | */ 12 | public record LocalInitConnectDTO( 13 | @NotBlank(message = "channelId不能为空") 14 | String channelId, 15 | @NotNull(message = "ptySize") 16 | TtySize ttySize, 17 | @NotBlank(message = "sessionId不能为空") 18 | String sessionId 19 | ) { 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/dto/terminal/SshInitConnectDTO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.dto.terminal; 2 | 3 | import com.tshell.core.TtySize; 4 | 5 | import jakarta.validation.constraints.NotBlank; 6 | import jakarta.validation.constraints.NotNull; 7 | 8 | /** 9 | * @author TheBlind 10 | * @date 2022/8/16 11 | */ 12 | public record SshInitConnectDTO( 13 | @NotBlank(message = "channelId不能为空") 14 | String channelId, 15 | @NotNull(message = "ptySize") 16 | TtySize ttySize, 17 | @NotNull(message = "sessionId不能为空") 18 | String sessionId, 19 | String username, 20 | String pwd 21 | 22 | ) { 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Appearance.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | /** 4 | * 5 | * 外观 6 | * @author TheBlind 7 | * @version 1.0 8 | */ 9 | public class Appearance { 10 | String fontFamily; 11 | int fontSize; 12 | int fontWeight; 13 | String fontVariantLigatures; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Breakpoint.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | 15 | 16 | 17 | @Entity 18 | @Data 19 | @Builder 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @GenericGenerator(name = "nanoId", strategy = "com.tshell.config.NanoIdGenerator") 23 | public class Breakpoint extends PanacheEntityBase { 24 | public Breakpoint(String transferRecordId, long start, long current, long end) { 25 | this.transferRecordId = transferRecordId; 26 | this.start = start; 27 | this.current = current; 28 | this.end = end; 29 | } 30 | 31 | @Id 32 | @GeneratedValue(generator = "nanoId") 33 | private String id; 34 | private String transferRecordId; 35 | private long start; 36 | private long current; 37 | private long end; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Cmd.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntity; 5 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 6 | import jakarta.persistence.GenerationType; 7 | import lombok.AllArgsConstructor; 8 | import lombok.Builder; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import jakarta.persistence.Entity; 13 | import jakarta.persistence.GeneratedValue; 14 | import jakarta.persistence.Id; 15 | 16 | 17 | /** 18 | * * 使用又划 19 | * * 20 | * * ip配置 21 | * * 22 | * * ipconfig {/all,/a}[显示完整信息] /are 展示ipv6 ifconfig 23 | * * 24 | * * 实现 实现命令的ostype 不得设置 为父命令中的ostype 25 | * * 26 | * * 完整ip配置 27 | * * 28 | * * ipconfig all 29 | * * 30 | * * 31 | * * Git配置 32 | * * 主 git --version 33 | * * 34 | * * 子 remote //查看远程库的信息 35 | * * remote -v //查看远程库的详细信息 36 | * * 37 | */ 38 | @Data 39 | @Entity 40 | @Builder 41 | @NoArgsConstructor 42 | @AllArgsConstructor 43 | public class Cmd extends PanacheEntityBase { 44 | 45 | @Id 46 | @GeneratedValue(strategy = GenerationType.IDENTITY) 47 | public int id; 48 | 49 | private int parentCmdId; 50 | 51 | private String cmdText; 52 | 53 | private String describe; 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/CmdOsType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntity; 5 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import jakarta.persistence.*; 12 | 13 | @Data 14 | @Entity 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class CmdOsType extends PanacheEntityBase { 19 | 20 | @Id 21 | @GeneratedValue(strategy = GenerationType.IDENTITY) 22 | public int id; 23 | 24 | private int cmdId; 25 | 26 | private int osTypeId; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/ConnectionLog.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import jakarta.persistence.GenerationType; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | 15 | /** 16 | * 连接日志 17 | * 18 | * @author TheBlind 19 | * @version 1.0 20 | */ 21 | @Data 22 | @Entity 23 | @Builder 24 | @NoArgsConstructor 25 | @AllArgsConstructor 26 | public class ConnectionLog extends PanacheEntityBase { 27 | 28 | @Id 29 | @GeneratedValue(strategy = GenerationType.IDENTITY) 30 | private int id; 31 | private String username; 32 | private String sessionId; 33 | private String startTime; 34 | private String endTime; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/GlobalVariable.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | 15 | /** 16 | * ${xx} 17 | * 全局变量 18 | * @author TheBlind 19 | */ 20 | @NoArgsConstructor 21 | @AllArgsConstructor 22 | @Builder 23 | @Data 24 | @Entity 25 | public class GlobalVariable extends PanacheEntityBase { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | public int id; 30 | private String varName; 31 | private String value; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Group.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.Data; 7 | 8 | import jakarta.persistence.Entity; 9 | import jakarta.persistence.GeneratedValue; 10 | import jakarta.persistence.Id; 11 | 12 | @Data 13 | @Entity 14 | public class Group extends PanacheEntityBase { 15 | 16 | @Id 17 | @GeneratedValue(strategy = GenerationType.IDENTITY) 18 | public int id; 19 | private String groupName; 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/GroupCmd.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.Data; 7 | 8 | import jakarta.persistence.Entity; 9 | import jakarta.persistence.GeneratedValue; 10 | import jakarta.persistence.Id; 11 | 12 | @Data 13 | @Entity 14 | public class GroupCmd extends PanacheEntityBase { 15 | 16 | 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | public int id; 20 | private Integer groupId; 21 | private Integer fastCmdId; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/GroupHost.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import jakarta.persistence.GenerationType; 5 | import lombok.Data; 6 | 7 | import jakarta.persistence.Entity; 8 | import jakarta.persistence.GeneratedValue; 9 | import jakarta.persistence.Id; 10 | 11 | @Entity 12 | @Data 13 | public class GroupHost extends PanacheEntityBase { 14 | 15 | @Id 16 | @GeneratedValue(strategy = GenerationType.IDENTITY) 17 | public int id; 18 | private int sessionGroupId; 19 | private int hostId; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/GroupShortcutCmd.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.Data; 7 | 8 | import jakarta.persistence.Entity; 9 | import jakarta.persistence.GeneratedValue; 10 | import jakarta.persistence.Id; 11 | 12 | /** 13 | * 快捷命令组 14 | */ 15 | @Data 16 | @Entity 17 | public class GroupShortcutCmd extends PanacheEntityBase { 18 | 19 | @Id 20 | @GeneratedValue(strategy = GenerationType.IDENTITY) 21 | public int id; 22 | 23 | private int shortcutCmdGroupId; 24 | 25 | private int shortcutCmdId; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/HistoryCmd.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import io.quarkus.runtime.annotations.RegisterForReflection; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | import java.time.LocalDateTime; 15 | 16 | /** 17 | * 历史命令 18 | * 19 | * @author TheBlind 20 | * @date 2022/8/26 21 | */ 22 | @Data 23 | @Entity 24 | @Builder 25 | @NoArgsConstructor 26 | @AllArgsConstructor 27 | @RegisterForReflection 28 | public class HistoryCmd extends PanacheEntityBase { 29 | 30 | @Id 31 | @GeneratedValue(strategy = GenerationType.IDENTITY) 32 | private int id; 33 | 34 | private String cmdText; 35 | 36 | private String sessionId; 37 | 38 | private String createTime; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/LocalSession.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import jakarta.persistence.GenerationType; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import jakarta.persistence.Entity; 11 | import jakarta.persistence.GeneratedValue; 12 | import jakarta.persistence.Id; 13 | 14 | @Entity 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | public class LocalSession extends PanacheEntityBase { 20 | 21 | @Id 22 | @GeneratedValue(strategy = GenerationType.IDENTITY) 23 | public int id; 24 | private String sessionName; 25 | private Integer sessionId; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Option.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntity; 5 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import jakarta.persistence.*; 12 | import java.util.List; 13 | 14 | /** 15 | * 选项 16 | * 17 | * git [--version] [--help] [-C ] 18 | * 19 | */ 20 | @Data 21 | @Entity 22 | @Builder 23 | @NoArgsConstructor 24 | @AllArgsConstructor 25 | public class Option extends PanacheEntityBase { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | public int id; 30 | 31 | private int cmdId; 32 | 33 | private String jsonNames; 34 | /** 35 | * 描述 36 | */ 37 | private String description; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Parameter.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntity; 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.*; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * 参数 13 | * 14 | * tar -c -f result.tar f1.txt f2.txt 15 | * 16 | * f1.txt f2.txt则为参数 17 | * 18 | * 参数 19 | * 20 | * @author TheBlind 21 | * @date 2022/6/30 22 | */ 23 | @Data 24 | @Entity 25 | @Builder 26 | @NoArgsConstructor 27 | @AllArgsConstructor 28 | public class Parameter extends PanacheEntityBase { 29 | 30 | 31 | @Id 32 | @GeneratedValue(strategy = GenerationType.IDENTITY) 33 | public int id; 34 | 35 | private int cmdId; 36 | 37 | /** 38 | * 参数下标 39 | */ 40 | @Column(name = "`index`") 41 | private int index; 42 | 43 | /** 44 | * 描述 45 | */ 46 | private String description; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/Session.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | import org.hibernate.annotations.GenericGenerator; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | 15 | /** 16 | * @author TheBlind 17 | */ 18 | @Entity 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @GenericGenerator(name = "nanoId", strategy = "com.tshell.config.NanoIdGenerator") 24 | public class Session extends PanacheEntityBase { 25 | 26 | @Id 27 | @GeneratedValue(generator = "nanoId") 28 | public String id; 29 | 30 | private String sessionName; 31 | private int ttyTypeId; 32 | /** 33 | * 0 为ssh 34 | * 1 为local 35 | */ 36 | private int type; 37 | 38 | private int sessionGroupId; 39 | 40 | private String updateTime; 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/SessionGroup.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import jakarta.persistence.GenerationType; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import jakarta.persistence.Entity; 12 | import jakarta.persistence.GeneratedValue; 13 | import jakarta.persistence.Id; 14 | 15 | @Entity 16 | @Data 17 | @Builder 18 | @NoArgsConstructor 19 | @AllArgsConstructor 20 | public class SessionGroup extends PanacheEntityBase { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | public int id; 25 | private String groupName; 26 | private int parentId; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/ShortcutCmd.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | 4 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 5 | import io.quarkus.runtime.annotations.RegisterForReflection; 6 | import jakarta.persistence.GenerationType; 7 | import lombok.Data; 8 | 9 | import jakarta.persistence.Entity; 10 | import jakarta.persistence.GeneratedValue; 11 | import jakarta.persistence.Id; 12 | 13 | /** 14 | * 快捷命令 15 | * 16 | */ 17 | @Data 18 | @Entity 19 | @RegisterForReflection 20 | public class ShortcutCmd extends PanacheEntityBase { 21 | 22 | @Id 23 | @GeneratedValue(strategy = GenerationType.IDENTITY) 24 | public int id; 25 | 26 | /** 27 | * 快捷名称 用于检索 28 | */ 29 | private String name; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/ShortcutCmdImpl.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import io.quarkus.runtime.annotations.RegisterForReflection; 5 | import jakarta.persistence.*; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author TheBlind 13 | * @date 2022/7/14 14 | */ 15 | @Data 16 | @Builder 17 | @NoArgsConstructor 18 | @AllArgsConstructor 19 | @Entity 20 | @RegisterForReflection 21 | public class ShortcutCmdImpl extends PanacheEntityBase { 22 | 23 | @Id 24 | @GeneratedValue(strategy=GenerationType.TABLE,generator="table_gen") 25 | @TableGenerator( 26 | name = "table_gen", 27 | initialValue = 50, //初始化值 28 | allocationSize=3 //累加值 29 | ) 30 | public Integer id; 31 | /** 32 | * 命令模板 支持占位符 如 ${全局变量} 33 | */ 34 | String cmdTemplate; 35 | private int shortcutCmdId; 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/entity/ShortcutCmdImplTtyType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.entity; 2 | 3 | import io.quarkus.hibernate.orm.panache.PanacheEntityBase; 4 | import jakarta.persistence.GenerationType; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Builder; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import jakarta.persistence.Entity; 11 | import jakarta.persistence.GeneratedValue; 12 | import jakarta.persistence.Id; 13 | 14 | /** 15 | * 16 | * 17 | * @author TheBlind 18 | * @date 2022/7/14 19 | */ 20 | @Data 21 | @Builder 22 | @AllArgsConstructor 23 | @NoArgsConstructor 24 | @Entity 25 | public class ShortcutCmdImplTtyType extends PanacheEntityBase { 26 | 27 | @Id 28 | @GeneratedValue(strategy = GenerationType.IDENTITY) 29 | public int id; 30 | 31 | int shortcutCmdImplId; 32 | 33 | private int ttyTypeId; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/enums/AuthType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.enums; 2 | 3 | import com.tshell.utils.enums.ValueEnum; 4 | 5 | /** 6 | * @author TheBlind 7 | * @version 1.0 8 | */ 9 | public enum AuthType implements ValueEnum { 10 | /** 11 | * 密码方式 12 | */ 13 | PWD(0), 14 | /** 15 | * 公私钥 16 | */ 17 | PUBLIC_KEY(1), 18 | /** 19 | * 键盘互动模式 20 | */ 21 | KEYBOARD_INTERACTIVE(2); 22 | 23 | AuthType(int value) { 24 | this.value = value; 25 | } 26 | 27 | private int value; 28 | 29 | @Override 30 | public Integer getValue() { 31 | return this.value; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/enums/ProxyType.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.enums; 2 | 3 | import com.tshell.utils.enums.ValueEnum; 4 | 5 | /** 6 | * @author TheBlind 7 | * @version 1.0 8 | */ 9 | public enum ProxyType implements ValueEnum { 10 | /** 11 | * 直连 12 | */ 13 | DIRECT(0), 14 | 15 | /** 16 | * http 17 | */ 18 | HTTP(1), 19 | /** 20 | * socket 21 | */ 22 | SOCKET(2); 23 | 24 | ProxyType(int value) { 25 | this.value = value; 26 | } 27 | 28 | private int value; 29 | 30 | @Override 31 | public Integer getValue() { 32 | return this.value; 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/CmdOptionVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author TheBlind 7 | * @date 2022/7/12 8 | */ 9 | public record CmdOptionVO( 10 | Integer id, 11 | List optionNameList, 12 | /** 13 | * 描述 14 | */ 15 | String description 16 | 17 | ) { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/CmdOsTypeVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/12 6 | */ 7 | public record CmdOsTypeVO(Integer id,Integer osTypeId, String osTypeName) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/CmdParameterVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/12 6 | */ 7 | public record CmdParameterVO( 8 | Integer id, 9 | /** 10 | * 参数下标 11 | */ 12 | int index, 13 | /** 14 | * 描述 15 | */ 16 | String description) { 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/CompleteCmdVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | 4 | import com.tshell.module.entity.Cmd; 5 | 6 | import java.util.List; 7 | 8 | 9 | /** 10 | * 完整命令 11 | */ 12 | public record CompleteCmdVO(Integer id, 13 | 14 | Integer parentCmdId, 15 | 16 | String cmdText, 17 | 18 | String describe, 19 | 20 | List cmdOsTypeIdList, 21 | 22 | List cmdOptionList, 23 | 24 | List cmdParameterList 25 | ) { 26 | 27 | public static CompleteCmdVO convert(Cmd cmd, List cmdOsTypeIdList, 28 | List parameterList, 29 | List optionList) { 30 | return new CompleteCmdVO(cmd.id,cmd.getParentCmdId(),cmd.getCmdText(), cmd.getDescribe(),cmdOsTypeIdList,optionList,parameterList); 31 | 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/CompleteTransferRecordVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import com.tshell.core.FileOperate; 4 | 5 | /** 6 | * @author TheBlind 7 | * @version 1.0 8 | */ 9 | public record CompleteTransferRecordVO(String id, 10 | String fileName, 11 | String sessionId, 12 | String createTime, 13 | String readPath, 14 | String writePath, 15 | FileOperate operate) { 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/GroupHostTree.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/5/23 6 | */ 7 | public class GroupHostTree { 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/PageVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import java.util.List; 4 | 5 | public record PageVO( 6 | int count, 7 | List list 8 | ) { 9 | } 10 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/RetrieveMenuVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.quarkus.runtime.annotations.RegisterForReflection; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author TheBlind 10 | * @date 2022/7/17 11 | */ 12 | @RegisterForReflection 13 | public record RetrieveMenuVO( 14 | @JsonProperty("isInCommandInput") 15 | boolean isInCommandInput, 16 | @JsonProperty("retrieves") 17 | List retrieves 18 | ) { 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/RetrieveVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import com.tshell.service.RetrieveService; 4 | import io.quarkus.runtime.annotations.RegisterForReflection; 5 | 6 | /** 7 | * @author TheBlind 8 | * @date 2022/7/17 9 | */ 10 | @RegisterForReflection 11 | public record RetrieveVO( 12 | String label, String value, RetrieveService.RetrieveItemType type 13 | ) { 14 | } 15 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/ShortcutCmdImplVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import com.tshell.module.entity.ShortcutCmdImpl; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/7/12 10 | */ 11 | public record ShortcutCmdImplVO( 12 | String cmdTemplate, 13 | List shortcutCmdTtyTypeIdList, 14 | List shortcutCmdTtyTypeNameList 15 | ) { 16 | public ShortcutCmdImpl convert() { 17 | return ShortcutCmdImpl.builder().cmdTemplate(cmdTemplate).build(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/ShortcutCmdVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | 4 | import java.util.List; 5 | 6 | 7 | /** 8 | * ShortcutCmdVO 9 | * 10 | */ 11 | public record ShortcutCmdVO(Integer id, 12 | String name, 13 | List shortcutCmdGroupIdList, 14 | List shortcutCmdImplList 15 | ) { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/SshSessionVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | import com.tshell.module.enums.AuthType; 4 | import com.tshell.module.enums.ProxyType; 5 | import io.quarkus.runtime.annotations.RegisterForReflection; 6 | 7 | @RegisterForReflection 8 | public record SshSessionVO( 9 | String sessionId, 10 | String ip, 11 | int port, 12 | String username, 13 | String pwd, 14 | int sessionGroupId, 15 | String sessionName, 16 | int ttyTypeId, 17 | AuthType authType, 18 | String privateKeyFile, 19 | String passphrase, 20 | String proxyHost, 21 | Integer proxyPort, 22 | ProxyType proxyType 23 | ) { 24 | } 25 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/TransferCountVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo; 2 | 3 | /** 4 | * @author TheBlind 5 | * @version 1.0 6 | */ 7 | public record TransferCountVO( 8 | int uploadCount, 9 | int downloadCount, 10 | int completeCount 11 | ) { 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/config/AppearanceVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo.config; 2 | 3 | import com.tshell.module.entity.Config; 4 | 5 | /** 6 | * @author TheBlind 7 | */ 8 | public record AppearanceVO(String fontFamily, 9 | Integer fontSize, 10 | Integer fontWeight, 11 | Integer fontWeightBold, 12 | boolean enableFontLigatures, 13 | String cursorShape, 14 | boolean cursorBlink) { 15 | 16 | public static AppearanceVO convert(Config.Terminal terminal) { 17 | return new AppearanceVO(terminal.getFontFamily(), terminal.getFontSize(), terminal.getFontWeight(), terminal.getFontWeightBold(), terminal.isEnableFontLigatures(), terminal.getCursorShape(), terminal.isCursorBlink()); 18 | } 19 | } -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/config/TerminalVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo.config; 2 | 3 | import com.tshell.module.entity.Config; 4 | 5 | /** 6 | * @author TheBlind 7 | */ 8 | public record TerminalVO( 9 | Integer scrollbackLines, 10 | String wordSeparator, 11 | boolean copyOnSelect) { 12 | 13 | public static TerminalVO convert(Config.Terminal terminal) { 14 | return new TerminalVO(terminal.getScrollbackLines(),terminal.getWordSeparator(), terminal.isCopyOnSelect()); 15 | } 16 | } -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/module/vo/connectionLog/TopVO.java: -------------------------------------------------------------------------------- 1 | package com.tshell.module.vo.connectionLog; 2 | 3 | import jakarta.persistence.Entity; 4 | import lombok.Data; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * @author TheBlind 10 | * @version 1.0 11 | */ 12 | @Data 13 | public class TopVO { 14 | private String sessionName; 15 | private String sessionId; 16 | } 17 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/service/DesktopService.java: -------------------------------------------------------------------------------- 1 | package com.tshell.service; 2 | 3 | import cn.hutool.core.io.FileUtil; 4 | 5 | import jakarta.enterprise.context.ApplicationScoped; 6 | import java.awt.*; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.net.URI; 10 | 11 | /** 12 | * @author TheBlind 13 | * @version 1.0 14 | */ 15 | @ApplicationScoped 16 | public class DesktopService { 17 | final String githubUrl = "https://github.com/TheBlindM/T-Shell"; 18 | 19 | /** 20 | * 前往github 上开源地址 21 | * 22 | * @throws IOException 23 | */ 24 | public void toGithub() throws IOException { 25 | URI uri = URI.create(githubUrl); 26 | Desktop.getDesktop().browse(uri); 27 | } 28 | 29 | /** 30 | * 利用系统默认 文件资源管理器 打开文件 31 | * @param path 文件路径 32 | * @throws IOException 33 | */ 34 | public void open(String path) throws IOException { 35 | Desktop.getDesktop().open(FileUtil.file(path)); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/socket/RetrieveCmdData.java: -------------------------------------------------------------------------------- 1 | package com.tshell.socket; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import io.quarkus.runtime.annotations.RegisterForReflection; 6 | 7 | /** 8 | * @author TheBlind 9 | * @date 2022/8/23 10 | */ 11 | @RegisterForReflection 12 | public record RetrieveCmdData( 13 | @JsonProperty("term") 14 | String term, 15 | @JsonProperty("skipVerify") 16 | boolean skipVerify) { 17 | } 18 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/utils/OsUtil.java: -------------------------------------------------------------------------------- 1 | package com.tshell.utils; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/7/20 6 | */ 7 | public class OsUtil { 8 | 9 | public static final String OS_NAME = System.getProperty("os.name"); 10 | public static final String OS_VERSION = System.getProperty("os.version").toLowerCase(); 11 | 12 | protected static final String _OS_NAME = OS_NAME.toLowerCase(); 13 | 14 | public static final boolean isWindows = _OS_NAME.startsWith("windows"); 15 | public static final boolean isMac = _OS_NAME.startsWith("mac"); 16 | public static final boolean isLinux = _OS_NAME.startsWith("linux"); 17 | 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/utils/enums/KeyValueEnum.java: -------------------------------------------------------------------------------- 1 | package com.tshell.utils.enums; 2 | 3 | /** 4 | * @author TheBlind 5 | * @date 2022/8/2 6 | */ 7 | public interface KeyValueEnum extends ValueEnum { 8 | 9 | String getKey(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/java/com/tshell/utils/enums/ValueEnum.java: -------------------------------------------------------------------------------- 1 | package com.tshell.utils.enums; 2 | 3 | /** 4 | * 5 | * @author TheBlind 6 | * @date 2022/8/2 7 | */ 8 | public interface ValueEnum { 9 | 10 | T getValue(); 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | quarkus.http.port=10218 2 | quarkus.datasource.jdbc.url=jdbc:sqlite:Shell.db 3 | 4 | 5 | # https://quarkus.io/guides/hibernate-orm#quarkus-hibernate-orm_configuration 6 | #quarkus.hibernate-orm.database.generation = drop-and-create 7 | quarkus.hibernate-orm.database.generation = update 8 | #quarkus.datasource.db-kind=h2 9 | quarkus.datasource.db-kind=sqlite 10 | 11 | quarkus.log.level=ERROR 12 | quarkus.datasource.jdbc.max-size=1 13 | quarkus.datasource.jdbc.initial-size=1 14 | quarkus.transaction-manager.default-transaction-timeout = 240s 15 | quarkus.shutdown.timeout=5 16 | # ?? 17 | quarkus.http.cors=true 18 | quarkus.http.cors.origins=* 19 | quarkus.http.cors.headers=accept, authorization, content-type, x-requested-with 20 | quarkus.http.cors.methods=GET,POST,DELETE,PUT, OPTIONS 21 | 22 | #quarkus.native.compression.level=5 23 | -------------------------------------------------------------------------------- /ui/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 2 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | -------------------------------------------------------------------------------- /ui/.env: -------------------------------------------------------------------------------- 1 | VITE_BASE_URL=/ 2 | 3 | VITE_APP_NAME=T-Shell 4 | 5 | VITE_APP_TITLE=T-Shell 6 | 7 | VITE_APP_DESC=T-Shell是一个中后台管理系统模版 8 | 9 | #是否多用户 10 | VITE_APP_MULTI_USER_MODE=N 11 | 12 | # 权限路由模式: static | dynamic 13 | VITE_AUTH_ROUTE_MODE=static 14 | 15 | # 路由首页(根路由重定向), 用于static模式的权限路由,dynamic模式取决于后端返回的路由首页 16 | VITE_ROUTE_HOME_PATH=/shell/info 17 | 18 | PROD=true 19 | 20 | VITE_VERCEL=Y 21 | -------------------------------------------------------------------------------- /ui/.env-config.ts: -------------------------------------------------------------------------------- 1 | /** 请求服务的环境配置 */ 2 | type ServiceEnv = Record; 3 | 4 | /** 不同请求服务的环境配置 */ 5 | const serviceEnv: ServiceEnv = { 6 | dev: { 7 | url: 'http://localhost:10218', 8 | urlPattern: '/url-pattern', 9 | secondUrl: 'http://localhost:10218', 10 | secondUrlPattern: '/second-url-pattern' 11 | }, 12 | test: { 13 | url: 'http://localhost:10218', 14 | urlPattern: '/url-pattern', 15 | secondUrl: 'http://localhost:10218', 16 | secondUrlPattern: '/second-url-pattern' 17 | }, 18 | prod: { 19 | url: 'http://localhost:10218', 20 | urlPattern: '/url-pattern', 21 | secondUrl: 'http://localhost:10218', 22 | secondUrlPattern: '/second-url-pattern' 23 | } 24 | }; 25 | 26 | /** 27 | * 获取当前环境模式下的请求服务的配置 28 | * @param env 环境 29 | */ 30 | export function getServiceEnvConfig(env: ImportMetaEnv) { 31 | const { VITE_SERVICE_ENV = 'dev' } = env; 32 | 33 | const config = serviceEnv[VITE_SERVICE_ENV]; 34 | 35 | return config; 36 | } 37 | -------------------------------------------------------------------------------- /ui/.env.development: -------------------------------------------------------------------------------- 1 | VITE_HTTP_PROXY=Y 2 | -------------------------------------------------------------------------------- /ui/.env.production: -------------------------------------------------------------------------------- 1 | VITE_VISUALIZER=N 2 | 3 | VITE_COMPRESS=N 4 | 5 | # gzip | brotliCompress | deflate | deflateRaw 6 | VITE_COMPRESS_TYPE=gzip 7 | -------------------------------------------------------------------------------- /ui/.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | node_modules 3 | lib 4 | *.md 5 | *.woff 6 | *.ttf 7 | .vscode 8 | .idea 9 | /dist/ 10 | /public 11 | /docs 12 | .vscode 13 | .local 14 | !.env-config.ts 15 | components.d.ts 16 | -------------------------------------------------------------------------------- /ui/.gitattributes: -------------------------------------------------------------------------------- 1 | "*.vue" eol=lf 2 | "*.js" eol=lf 3 | "*.ts" eol=lf 4 | "*.jsx" eol=lf 5 | "*.tsx" eol=lf 6 | "*.cjs" eol=lf 7 | "*.cts" eol=lf 8 | "*.mjs" eol=lf 9 | "*.mts" eol=lf 10 | "*.json" eol=lf 11 | "*.html" eol=lf 12 | "*.css" eol=lf 13 | "*.less" eol=lf 14 | "*.scss" eol=lf 15 | "*.sass" eol=lf 16 | "*.styl" eol=lf 17 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | stats.html 17 | 18 | /cypress/videos/ 19 | /cypress/screenshots/ 20 | 21 | # Editor directories and files 22 | .vscode/* 23 | !.vscode/extensions.json 24 | !.vscode/settings.json 25 | .idea 26 | *.suo 27 | *.ntvs* 28 | *.njsproj 29 | *.sln 30 | *.sw? 31 | 32 | /src/typings/components.d.ts 33 | pnpm-lock.yaml 34 | package-lock.json 35 | yarn.lock 36 | -------------------------------------------------------------------------------- /ui/.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit 5 | -------------------------------------------------------------------------------- /ui/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm run lint && npm run typecheck 5 | -------------------------------------------------------------------------------- /ui/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | shamefully-hoist=true 3 | strict-peer-dependencies=false 4 | -------------------------------------------------------------------------------- /ui/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { // https://prettier.io/docs/en/options.html 2 | arrowParens: 'avoid', 3 | bracketSameLine: false, 4 | bracketSpacing: true, 5 | embeddedLanguageFormatting: 'auto', 6 | htmlWhitespaceSensitivity: 'css', 7 | insertPragma: false, 8 | jsxSingleQuote: false, 9 | printWidth: 120, 10 | proseWrap: 'preserve', 11 | quoteProps: 'as-needed', 12 | requirePragma: false, 13 | semi: true, 14 | singleQuote: true, 15 | tabWidth: 2, 16 | trailingComma: 'none', 17 | useTabs: false, 18 | vueIndentScriptAndStyle: false, 19 | overrides: [ 20 | { 21 | files: '*.html', 22 | options: { 23 | parser: 'html', 24 | }, 25 | }, 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | # T-Shell UI 2 | UI服务 3 | -------------------------------------------------------------------------------- /ui/build/config/define.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | /** 项目构建时间 */ 4 | const PROJECT_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss')); 5 | 6 | export const viteDefine = { 7 | PROJECT_BUILD_TIME 8 | }; 9 | -------------------------------------------------------------------------------- /ui/build/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './define'; 2 | export * from './proxy'; 3 | -------------------------------------------------------------------------------- /ui/build/config/proxy.ts: -------------------------------------------------------------------------------- 1 | import type { ProxyOptions } from 'vite'; 2 | 3 | /** 4 | * 设置网络代理 5 | * @param isOpenProxy - 是否开启代理 6 | * @param envConfig - env环境配置 7 | */ 8 | export function createViteProxy(isOpenProxy: boolean, envConfig: ServiceEnvConfig) { 9 | if (!isOpenProxy) return undefined; 10 | 11 | const proxy: Record = { 12 | [envConfig.urlPattern]: { 13 | target: envConfig.url, 14 | changeOrigin: true, 15 | rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '') 16 | }, 17 | [envConfig.secondUrlPattern]: { 18 | target: envConfig.secondUrl, 19 | changeOrigin: true, 20 | rewrite: path => path.replace(new RegExp(`^${envConfig.secondUrlPattern}`), '') 21 | } 22 | }; 23 | 24 | return proxy; 25 | } 26 | -------------------------------------------------------------------------------- /ui/build/index.ts: -------------------------------------------------------------------------------- 1 | export * from './plugins'; 2 | export * from './config'; 3 | export * from './utils'; 4 | -------------------------------------------------------------------------------- /ui/build/plugins/compress.ts: -------------------------------------------------------------------------------- 1 | import ViteCompression from 'vite-plugin-compression'; 2 | 3 | export default (viteEnv: ImportMetaEnv) => { 4 | const { VITE_COMPRESS_TYPE = 'gzip' } = viteEnv; 5 | return ViteCompression({ algorithm: VITE_COMPRESS_TYPE }); 6 | }; 7 | -------------------------------------------------------------------------------- /ui/build/plugins/html.ts: -------------------------------------------------------------------------------- 1 | import type { PluginOption } from 'vite'; 2 | import { createHtmlPlugin } from 'vite-plugin-html'; 3 | 4 | export default (viteEnv: ImportMetaEnv): PluginOption[] => { 5 | return createHtmlPlugin({ 6 | minify: true, 7 | inject: { 8 | data: { 9 | appName: viteEnv.VITE_APP_NAME, 10 | appTitle: viteEnv.VITE_APP_TITLE 11 | } 12 | } 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /ui/build/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import type { PluginOption } from 'vite'; 2 | import vue from './vue'; 3 | import html from './html'; 4 | import unplugin from './unplugin'; 5 | import unocss from './unocss'; 6 | import mock from './mock'; 7 | import visualizer from './visualizer'; 8 | import compress from './compress'; 9 | 10 | /** 11 | * vite插件 12 | * @param viteEnv - 环境变量配置 13 | */ 14 | export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | PluginOption[])[] { 15 | const plugins = [...vue, html(viteEnv), ...unplugin, unocss, mock]; 16 | 17 | if (viteEnv.VITE_VISUALIZER === 'Y') { 18 | plugins.push(visualizer); 19 | } 20 | if (viteEnv.VITE_COMPRESS === 'Y') { 21 | plugins.push(compress(viteEnv)); 22 | } 23 | 24 | return plugins; 25 | } 26 | -------------------------------------------------------------------------------- /ui/build/plugins/mock.ts: -------------------------------------------------------------------------------- 1 | import { viteMockServe } from 'vite-plugin-mock'; 2 | 3 | export default viteMockServe({ 4 | mockPath: 'mock', 5 | injectCode: ` 6 | import { setupMockServer } from '../mock'; 7 | setupMockServer(); 8 | ` 9 | }); 10 | -------------------------------------------------------------------------------- /ui/build/plugins/unocss.ts: -------------------------------------------------------------------------------- 1 | import unocss from 'unocss/vite'; 2 | 3 | export default unocss(); 4 | -------------------------------------------------------------------------------- /ui/build/plugins/unplugin.ts: -------------------------------------------------------------------------------- 1 | import DefineOptions from 'unplugin-vue-define-options/vite'; 2 | import Icons from 'unplugin-icons/vite'; 3 | import IconsResolver from 'unplugin-icons/resolver'; 4 | import Components from 'unplugin-vue-components/vite'; 5 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'; 6 | import { FileSystemIconLoader } from 'unplugin-icons/loaders'; 7 | import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; 8 | import { getSrcPath } from '../utils'; 9 | 10 | const srcPath = getSrcPath(); 11 | 12 | const customIconPath = `${srcPath}/assets/svg`; 13 | 14 | export default [ 15 | DefineOptions(), 16 | Icons({ 17 | compiler: 'vue3', 18 | customCollections: { 19 | custom: FileSystemIconLoader(customIconPath) 20 | }, 21 | scale: 1, 22 | defaultClass: 'inline-block' 23 | }), 24 | Components({ 25 | dts: 'src/typings/components.d.ts', 26 | types: [{ from: 'vue-router', names: ['RouterLink', 'RouterView'] }], 27 | resolvers: [NaiveUiResolver(), IconsResolver({ customCollections: ['custom'], componentPrefix: 'icon' })] 28 | }), 29 | createSvgIconsPlugin({ 30 | iconDirs: [customIconPath], 31 | symbolId: 'icon-custom-[dir]-[name]', 32 | inject: 'body-last', 33 | customDomId: '__CUSTOM_SVG_ICON__' 34 | }) 35 | ]; 36 | -------------------------------------------------------------------------------- /ui/build/plugins/visualizer.ts: -------------------------------------------------------------------------------- 1 | import { visualizer } from 'rollup-plugin-visualizer'; 2 | 3 | export default visualizer({ 4 | gzipSize: true, 5 | brotliSize: true, 6 | open: true 7 | }); 8 | -------------------------------------------------------------------------------- /ui/build/plugins/vue.ts: -------------------------------------------------------------------------------- 1 | import vue from '@vitejs/plugin-vue'; 2 | import vueJsx from '@vitejs/plugin-vue-jsx'; 3 | 4 | const plugins = [vue(), vueJsx()]; 5 | 6 | export default plugins; 7 | -------------------------------------------------------------------------------- /ui/build/utils/index.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | /** 4 | * 获取项目根路径 5 | * @descrition 末尾不带斜杠 6 | */ 7 | export function getRootPath() { 8 | return path.resolve(process.cwd()); 9 | } 10 | 11 | /** 12 | * 获取项目src路径 13 | * @param srcName - src目录名称(默认: "src") 14 | * @descrition 末尾不带斜杠 15 | */ 16 | export function getSrcPath(srcName = 'src') { 17 | const rootPath = getRootPath(); 18 | 19 | return `${rootPath}/${srcName}`; 20 | } 21 | -------------------------------------------------------------------------------- /ui/commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ['@commitlint/config-conventional'] }; 2 | -------------------------------------------------------------------------------- /ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= appName %> 9 | 10 | 11 |

12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
<%= appTitle %>
23 |
24 | 25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ui/mock/api/demo.ts: -------------------------------------------------------------------------------- 1 | import type { MockMethod } from 'vite-plugin-mock'; 2 | 3 | const apis: MockMethod[] = [ 4 | { 5 | url: '/mock/apiDemoWithAdapter', 6 | method: 'post', 7 | response: (): Service.MockServiceResult => { 8 | return { 9 | code: 200, 10 | message: 'ok', 11 | data: { 12 | dataId: '123', 13 | dataName: 'demoName' 14 | } 15 | }; 16 | } 17 | } 18 | ]; 19 | 20 | export default apis; 21 | -------------------------------------------------------------------------------- /ui/mock/api/index.ts: -------------------------------------------------------------------------------- 1 | import auth from './auth'; 2 | import route from './route'; 3 | import management from './management'; 4 | 5 | export default [...auth, ...route, ...management]; 6 | -------------------------------------------------------------------------------- /ui/mock/api/route.ts: -------------------------------------------------------------------------------- 1 | import type { MockMethod } from 'vite-plugin-mock'; 2 | import { userModel, routeModel } from '../model'; 3 | 4 | const apis: MockMethod[] = [ 5 | { 6 | url: '/mock/getUserRoutes', 7 | method: 'post', 8 | response: (options: Service.MockOption): Service.MockServiceResult => { 9 | const { userId = undefined } = options.body; 10 | 11 | const routeHomeName: AuthRoute.RouteKey = 'dashboard_analysis'; 12 | 13 | const role = userModel.find(item => item.userId === userId)?.userRole || 'user'; 14 | 15 | const filterRoutes = routeModel[role]; 16 | 17 | return { 18 | code: 200, 19 | message: 'ok', 20 | data: { 21 | routes: filterRoutes, 22 | home: routeHomeName 23 | } 24 | }; 25 | } 26 | } 27 | ]; 28 | 29 | export default apis; 30 | -------------------------------------------------------------------------------- /ui/mock/index.ts: -------------------------------------------------------------------------------- 1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'; 2 | import api from './api'; 3 | 4 | export function setupMockServer() { 5 | createProdMockServer(api); 6 | } 7 | -------------------------------------------------------------------------------- /ui/mock/model/auth.ts: -------------------------------------------------------------------------------- 1 | interface UserModel extends Auth.UserInfo { 2 | token: string; 3 | refreshToken: string; 4 | password: string; 5 | } 6 | 7 | export const userModel: UserModel[] = [ 8 | { 9 | token: '__TOKEN_SOYBEAN__', 10 | refreshToken: '__REFRESH_TOKEN_SOYBEAN__', 11 | userId: '0', 12 | userName: 'Soybean', 13 | userRole: 'super', 14 | password: 'soybean123' 15 | }, 16 | { 17 | token: '__TOKEN_SUPER__', 18 | refreshToken: '__REFRESH_TOKEN_SUPER__', 19 | userId: '1', 20 | userName: 'Super', 21 | userRole: 'super', 22 | password: 'super123' 23 | }, 24 | { 25 | token: '__TOKEN_ADMIN__', 26 | refreshToken: '__REFRESH_TOKEN_ADMIN__', 27 | userId: '2', 28 | userName: 'Admin', 29 | userRole: 'admin', 30 | password: 'admin123' 31 | }, 32 | { 33 | token: '__TOKEN_USER01__', 34 | refreshToken: '__REFRESH_TOKEN_USER01__', 35 | userId: '3', 36 | userName: 'User01', 37 | userRole: 'user', 38 | password: 'user01123' 39 | } 40 | ]; 41 | -------------------------------------------------------------------------------- /ui/mock/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth'; 2 | export * from './route'; 3 | -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/public/favicon.ico -------------------------------------------------------------------------------- /ui/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | -------------------------------------------------------------------------------- /ui/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["you"] 6 | license = "" 7 | repository = "" 8 | default-run = "app" 9 | edition = "2021" 10 | rust-version = "1.57" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [build-dependencies] 15 | tauri-build = { version = "1.3.0", features = [] } 16 | 17 | [dependencies] 18 | reqwest = { version = "0.11.11", features = ["json","blocking"] } 19 | tokio = { version = "1", features = ["full"] } 20 | serde_json = "1.0" 21 | serde = { version = "1.0", features = ["derive"] } 22 | tauri = { version = "1.3.0", features = ["api-all", "process-command-api", "updater"] } 23 | tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/tauri-plugin-single-instance" } 24 | 25 | [features] 26 | # by default Tauri runs in production mode 27 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL 28 | default = [ "custom-protocol" ] 29 | # this feature is used used for production builds where `devPath` points to the filesystem 30 | # DO NOT remove this 31 | custom-protocol = [ "tauri/custom-protocol" ] 32 | -------------------------------------------------------------------------------- /ui/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/shell-Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/shell-Icon.ico -------------------------------------------------------------------------------- /ui/src-tauri/icons/shell-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/shell-icon.icns -------------------------------------------------------------------------------- /ui/src-tauri/icons/shell-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/shell-icon.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/shell128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/shell128x128.png -------------------------------------------------------------------------------- /ui/src-tauri/icons/shell32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src-tauri/icons/shell32x32.png -------------------------------------------------------------------------------- /ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ui/src/assets/fonts/aguazyuan-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src/assets/fonts/aguazyuan-bold.ttf -------------------------------------------------------------------------------- /ui/src/assets/fonts/aguazyuan-light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src/assets/fonts/aguazyuan-light.ttf -------------------------------------------------------------------------------- /ui/src/assets/fonts/aguazyuan-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheBlindM/T-Shell/9d280a360cdd2e0436f2557145057befd09bd12b/ui/src/assets/fonts/aguazyuan-regular.ttf -------------------------------------------------------------------------------- /ui/src/assets/svg/activity.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/at-sign.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/cast.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/chrome.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/custom-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/assets/svg/logo-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ui/src/assets/svg/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ui/src/assets/svg/wind.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/src/components/business/LoginAgreement.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ui/src/components/common/DarkModeContainer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ui/src/components/common/DarkModeSwitch.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ui/src/components/common/ExceptionBase.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ui/src/components/common/NaiveProvider.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 38 | 39 | -------------------------------------------------------------------------------- /ui/src/components/common/SystemLogo.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ui/src/components/custom/GithubLink.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /ui/src/components/custom/ImageVerify.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /ui/src/components/custom/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ui/src/components/custom/WebSiteLink.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 22 | -------------------------------------------------------------------------------- /ui/src/composables/events.ts: -------------------------------------------------------------------------------- 1 | import { useEventListener } from '@vueuse/core'; 2 | import { useThemeStore, useTabStore } from '@/store'; 3 | 4 | /** 全局事件 */ 5 | export function useGlobalEvents() { 6 | const theme = useThemeStore(); 7 | const tab = useTabStore(); 8 | 9 | /** 页面离开时缓存多页签数据 */ 10 | useEventListener(window, 'beforeunload', () => { 11 | theme.cacheThemeSettings(); 12 | tab.cacheTabRoutes(); 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /ui/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export * from './system'; 2 | export * from './router'; 3 | export * from './layout'; 4 | export * from './events'; 5 | -------------------------------------------------------------------------------- /ui/src/composables/system.ts: -------------------------------------------------------------------------------- 1 | import { useAuthStore } from '@/store'; 2 | import { isArray, isString } from '@/utils'; 3 | 4 | interface AppInfo { 5 | /** 项目名称 */ 6 | name: string; 7 | /** 项目标题 */ 8 | title: string; 9 | /** 项目描述 */ 10 | desc: string; 11 | } 12 | 13 | /** 项目信息 */ 14 | export function useAppInfo(): AppInfo { 15 | const { VITE_APP_NAME: name, VITE_APP_TITLE: title, VITE_APP_DESC: desc } = import.meta.env; 16 | 17 | return { 18 | name, 19 | title, 20 | desc 21 | }; 22 | } 23 | 24 | 25 | /** 权限判断 */ 26 | export function usePermission() { 27 | const auth = useAuthStore(); 28 | 29 | function hasPermission(permission: Auth.RoleType | Auth.RoleType[]) { 30 | const { userRole } = auth.userInfo; 31 | 32 | let has = userRole === 'super'; 33 | if (!has) { 34 | if (isArray(permission)) { 35 | has = (permission as Auth.RoleType[]).includes(userRole); 36 | } 37 | if (isString(permission)) { 38 | has = (permission as Auth.RoleType) === userRole; 39 | } 40 | } 41 | return has; 42 | } 43 | 44 | return { 45 | hasPermission 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /ui/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './service'; 2 | export * from './regexp'; 3 | export * from './map-sdk'; 4 | -------------------------------------------------------------------------------- /ui/src/config/map-sdk.ts: -------------------------------------------------------------------------------- 1 | /** 百度地图sdk地址 */ 2 | export const BAIDU_MAP_SDK_URL = `https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1`; 3 | 4 | /** 高德地图sdk地址 */ 5 | export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d'; 6 | 7 | /** 腾讯地图sdk地址 */ 8 | export const TENCENT_MAP_SDK_URL = 'https://map.qq.com/api/gljs?v=1.exp&key=A6DBZ-KXPLW-JKSRY-ONZF4-CPHY3-K6BL7'; 9 | -------------------------------------------------------------------------------- /ui/src/config/regexp.ts: -------------------------------------------------------------------------------- 1 | /** 手机号码正则 */ 2 | export const REGEXP_PHONE = 3 | /^[1](([3][0-9])|([4][0,1,4-9])|([5][0-3,5-9])|([6][2,5,6,7])|([7][0-8])|([8][0-9])|([9][0-3,5-9]))[0-9]{8}$/; 4 | 5 | /** 邮箱正则 */ 6 | export const REGEXP_EMAIL = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; 7 | 8 | /** 密码正则(密码为6-18位数字/字符/符号的组合) */ 9 | export const REGEXP_PWD = 10 | /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){6,18}$/; 11 | 12 | /** 6位数字验证码正则 */ 13 | export const REGEXP_CODE_SIX = /^\d{6}$/; 14 | 15 | /** 4位数字验证码正则 */ 16 | export const REGEXP_CODE_FOUR = /^\d{4}$/; 17 | 18 | /** url链接正则 */ 19 | export const REGEXP_URL = 20 | /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/; 21 | -------------------------------------------------------------------------------- /ui/src/context/demo.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | import type { Ref } from 'vue'; 3 | import { useContext } from '@/hooks'; 4 | 5 | interface DemoContext { 6 | counts: Ref; 7 | setCounts: (count: number) => void; 8 | } 9 | 10 | const { useProvide: useDemoProvide, useInject: useDemoInject } = useContext(); 11 | 12 | export function useDemoContext() { 13 | const counts = ref(0); 14 | 15 | function setCounts(count: number) { 16 | counts.value = count; 17 | } 18 | 19 | const demoContext: DemoContext = { 20 | counts, 21 | setCounts 22 | }; 23 | 24 | function useProvide() { 25 | useDemoProvide(demoContext); 26 | } 27 | 28 | return { 29 | useProvide, 30 | useInject: useDemoInject 31 | }; 32 | } 33 | 34 | // 示例用法: A.vue为父组件, B.vue为子孙组件 C.vue为子孙组件 35 | // A.vue 36 | // import { useDemoContext } from '@/context'; 37 | // const { useProvide } = useDemoContext(); 38 | // useProvide(); 39 | 40 | // B.vue 和 C.vue : 共享状态 counts 41 | // import { useDemoContext } from '@/context'; 42 | // const { useInject } = useDemoContext(); 43 | // const { counts, setCounts } = useInject(); 44 | -------------------------------------------------------------------------------- /ui/src/context/index.ts: -------------------------------------------------------------------------------- 1 | export * from './demo'; 2 | -------------------------------------------------------------------------------- /ui/src/directives/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import setupNetworkDirective from './network'; 3 | import setupLoginDirective from './login'; 4 | import setupPermissionDirective from './permission'; 5 | 6 | /** setup custom vue directives. - [安装自定义的vue指令] */ 7 | export function setupDirectives(app: App) { 8 | setupNetworkDirective(app); 9 | setupLoginDirective(app); 10 | setupPermissionDirective(app); 11 | } 12 | -------------------------------------------------------------------------------- /ui/src/directives/login.ts: -------------------------------------------------------------------------------- 1 | import type { App, Directive } from 'vue'; 2 | import { useAuthStore } from '@/store'; 3 | import { useRouterPush } from '@/composables'; 4 | 5 | export default function setupLoginDirective(app: App) { 6 | const auth = useAuthStore(); 7 | const { toLogin } = useRouterPush(false); 8 | function listenerHandler(event: MouseEvent) { 9 | if (!auth.isLogin) { 10 | event.stopPropagation(); 11 | toLogin(); 12 | } 13 | } 14 | 15 | const loginDirective: Directive = { 16 | mounted(el: HTMLElement, binding) { 17 | if (binding.value === false) return; 18 | el.addEventListener('click', listenerHandler, { capture: true }); 19 | }, 20 | unmounted(el: HTMLElement, binding) { 21 | if (binding.value === false) return; 22 | el.removeEventListener('click', listenerHandler); 23 | } 24 | }; 25 | 26 | app.directive('login', loginDirective); 27 | } 28 | -------------------------------------------------------------------------------- /ui/src/directives/network.ts: -------------------------------------------------------------------------------- 1 | import type { App, Directive } from 'vue'; 2 | import { NETWORK_ERROR_MSG } from '@/config'; 3 | 4 | export default function setupNetworkDirective(app: App) { 5 | function listenerHandler(event: MouseEvent) { 6 | const hasNetwork = window.navigator.onLine; 7 | if (!hasNetwork) { 8 | window.$message?.error(NETWORK_ERROR_MSG); 9 | event.stopPropagation(); 10 | } 11 | } 12 | 13 | const networkDirective: Directive = { 14 | mounted(el: HTMLElement, binding) { 15 | if (binding.value === false) return; 16 | el.addEventListener('click', listenerHandler, { capture: true }); 17 | }, 18 | unmounted(el: HTMLElement, binding) { 19 | if (binding.value === false) return; 20 | el.removeEventListener('click', listenerHandler); 21 | } 22 | }; 23 | 24 | app.directive('network', networkDirective); 25 | } 26 | -------------------------------------------------------------------------------- /ui/src/directives/permission.ts: -------------------------------------------------------------------------------- 1 | import type { App, Directive } from 'vue'; 2 | import { usePermission } from '@/composables'; 3 | 4 | export default function setupPermissionDirective(app: App) { 5 | const { hasPermission } = usePermission(); 6 | 7 | function updateElVisible(el: HTMLElement, permission: Auth.RoleType | Auth.RoleType[]) { 8 | if (!permission) { 9 | throw new Error(`need roles: like v-permission="'admin'", v-permission="['admin', 'test]"`); 10 | } 11 | if (!hasPermission(permission)) { 12 | el.parentElement?.removeChild(el); 13 | } 14 | } 15 | 16 | const permissionDirective: Directive = { 17 | mounted(el, binding) { 18 | updateElVisible(el, binding.value); 19 | }, 20 | beforeUpdate(el, binding) { 21 | updateElVisible(el, binding.value); 22 | } 23 | }; 24 | 25 | app.directive('permission', permissionDirective); 26 | } 27 | -------------------------------------------------------------------------------- /ui/src/enum/business.ts: -------------------------------------------------------------------------------- 1 | /** 用户角色 */ 2 | export enum EnumUserRole { 3 | super = '超级管理员', 4 | admin = '管理员', 5 | user = '普通用户' 6 | } 7 | 8 | /** 登录模块 */ 9 | export enum EnumLoginModule { 10 | 'pwd-login' = '账密登录', 11 | 'code-login' = '手机验证码登录', 12 | 'register' = '注册', 13 | 'reset-pwd' = '重置密码', 14 | 'bind-wechat' = '微信绑定' 15 | } 16 | 17 | export enum EnumGender { 18 | male = '男', 19 | female = '女', 20 | null = '' 21 | } 22 | -------------------------------------------------------------------------------- /ui/src/enum/common.ts: -------------------------------------------------------------------------------- 1 | /** http请求头的content-type类型 */ 2 | export enum EnumContentType { 3 | json = 'application/json', 4 | formUrlencoded = 'application/x-www-form-urlencoded', 5 | formData = 'multipart/form-data' 6 | } 7 | 8 | /** 缓存的key */ 9 | export enum EnumStorageKey { 10 | /** 主题颜色 */ 11 | 'theme-color' = '__THEME_COLOR__', 12 | /** 用户token */ 13 | 'token' = '__TOKEN__', 14 | /** 用户刷新token */ 15 | 'refresh-token' = '__REFRESH_TOKEN__', 16 | /** 用户信息 */ 17 | 'user-info' = '__USER_INFO__', 18 | /** 主题配置 */ 19 | 'theme-settings' = '__THEME_SETTINGS__', 20 | /** 多页签路由信息 */ 21 | 'multi-tab-routes' = '__MULTI_TAB_ROUTES__' 22 | } 23 | 24 | /** 数据类型 */ 25 | export enum EnumDataType { 26 | number = '[object Number]', 27 | string = '[object String]', 28 | boolean = '[object Boolean]', 29 | null = '[object Null]', 30 | undefined = '[object Undefined]', 31 | object = '[object Object]', 32 | array = '[object Array]', 33 | date = '[object Date]', 34 | regexp = '[object RegExp]', 35 | set = '[object Set]', 36 | map = '[object Map]', 37 | file = '[object File]' 38 | } 39 | -------------------------------------------------------------------------------- /ui/src/enum/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './system'; 3 | export * from './business'; 4 | -------------------------------------------------------------------------------- /ui/src/enum/system.ts: -------------------------------------------------------------------------------- 1 | /** 布局组件的名称 */ 2 | export enum EnumLayoutComponentName { 3 | basic = 'basic-layout', 4 | blank = 'blank-layout' 5 | } 6 | 7 | /** 布局模式 */ 8 | export enum EnumThemeLayoutMode { 9 | 'vertical' = '左侧菜单模式', 10 | 'horizontal' = '顶部菜单模式', 11 | 'vertical-mix' = '左侧菜单混合模式', 12 | 'horizontal-mix' = '顶部菜单混合模式' 13 | } 14 | 15 | /** 多页签风格 */ 16 | export enum EnumThemeTabMode { 17 | 'chrome' = '谷歌风格', 18 | 'button' = '按钮风格' 19 | } 20 | 21 | /** 水平模式的菜单位置 */ 22 | export enum EnumThemeHorizontalMenuPosition { 23 | 'flex-start' = '居左', 24 | 'center' = '居中', 25 | 'flex-end' = '居右' 26 | } 27 | 28 | /** 过渡动画类型 */ 29 | export enum EnumThemeAnimateMode { 30 | 'zoom-fade' = '渐变', 31 | 'zoom-out' = '闪现', 32 | 'fade-slide' = '滑动', 33 | 'fade' = '消退', 34 | 'fade-bottom' = '底部消退', 35 | 'fade-scale' = '缩放消退' 36 | } 37 | -------------------------------------------------------------------------------- /ui/src/hooks/business/index.ts: -------------------------------------------------------------------------------- 1 | import useCountDown from './useCountDown'; 2 | import useSmsCode from './useSmsCode'; 3 | import useImageVerify from './useImageVerify'; 4 | 5 | export { useCountDown, useSmsCode, useImageVerify }; 6 | -------------------------------------------------------------------------------- /ui/src/hooks/business/useCountDown.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue'; 2 | import { useBoolean } from '../common'; 3 | 4 | /** 5 | * 倒计时 6 | * @param second - 倒计时的时间(s) 7 | */ 8 | export default function useCountDown(second: number) { 9 | if (second <= 0 && second % 1 !== 0) { 10 | throw Error('倒计时的时间应该为一个正整数!'); 11 | } 12 | const { bool: isComplete, setTrue, setFalse } = useBoolean(false); 13 | 14 | const counts = ref(0); 15 | const isCounting = computed(() => Boolean(counts.value)); 16 | 17 | let intervalId: any; 18 | 19 | /** 20 | * 开始计时 21 | * @param updateSecond - 更改初时传入的倒计时时间 22 | */ 23 | function start(updateSecond: number = second) { 24 | if (!counts.value) { 25 | setFalse(); 26 | counts.value = updateSecond; 27 | intervalId = setInterval(() => { 28 | counts.value -= 1; 29 | if (counts.value <= 0) { 30 | clearInterval(intervalId); 31 | setTrue(); 32 | } 33 | }, 1000); 34 | } 35 | } 36 | 37 | /** 38 | * 停止计时 39 | */ 40 | function stop() { 41 | intervalId = clearInterval(intervalId); 42 | counts.value = 0; 43 | } 44 | 45 | return { 46 | counts, 47 | isCounting, 48 | start, 49 | stop, 50 | isComplete 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /ui/src/hooks/business/useNaiveTable.ts: -------------------------------------------------------------------------------- 1 | // import type { DataTableColumn } from 'naive-ui'; 2 | -------------------------------------------------------------------------------- /ui/src/hooks/common/index.ts: -------------------------------------------------------------------------------- 1 | import useContext from './useContext'; 2 | import useBoolean from './useBoolean'; 3 | import useLoading from './useLoading'; 4 | import useLoadingEmpty from './useLoadingEmpty'; 5 | import useReload from './useReload'; 6 | 7 | export { useContext, useBoolean, useLoading, useLoadingEmpty, useReload }; 8 | -------------------------------------------------------------------------------- /ui/src/hooks/common/useBoolean.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | 3 | /** 4 | * boolean组合式函数 5 | * @param initValue 初始值 6 | */ 7 | export default function useBoolean(initValue = false) { 8 | const bool = ref(initValue); 9 | 10 | function setBool(value: boolean) { 11 | bool.value = value; 12 | } 13 | function setTrue() { 14 | setBool(true); 15 | } 16 | function setFalse() { 17 | setBool(false); 18 | } 19 | function toggle() { 20 | setBool(!bool.value); 21 | } 22 | 23 | return { 24 | bool, 25 | setBool, 26 | setTrue, 27 | setFalse, 28 | toggle 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /ui/src/hooks/common/useContext.ts: -------------------------------------------------------------------------------- 1 | import { provide, inject } from 'vue'; 2 | import type { InjectionKey } from 'vue'; 3 | 4 | /** 创建共享上下文状态 */ 5 | export default function useContext(contextName = 'context') { 6 | const injectKey: InjectionKey = Symbol(contextName); 7 | 8 | function useProvide(context: T) { 9 | provide(injectKey, context); 10 | } 11 | 12 | function useInject() { 13 | return inject(injectKey) as T; 14 | } 15 | 16 | return { 17 | useProvide, 18 | useInject 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /ui/src/hooks/common/useLoading.ts: -------------------------------------------------------------------------------- 1 | import useBoolean from './useBoolean'; 2 | 3 | export default function useLoading(initValue = false) { 4 | const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initValue); 5 | 6 | return { 7 | loading, 8 | startLoading, 9 | endLoading 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /ui/src/hooks/common/useLoadingEmpty.ts: -------------------------------------------------------------------------------- 1 | import useBoolean from './useBoolean'; 2 | 3 | export default function useLoadingEmpty(initLoading = false, initEmpty = false) { 4 | const { bool: loading, setTrue: startLoading, setFalse: endLoading } = useBoolean(initLoading); 5 | const { bool: empty, setBool: setEmpty } = useBoolean(initEmpty); 6 | 7 | return { 8 | loading, 9 | startLoading, 10 | endLoading, 11 | empty, 12 | setEmpty 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /ui/src/hooks/common/useReload.ts: -------------------------------------------------------------------------------- 1 | import { nextTick } from 'vue'; 2 | import useBoolean from './useBoolean'; 3 | 4 | /** 重载 */ 5 | export default function useReload() { 6 | // 重载的标志 7 | const { bool: reloadFlag, setTrue, setFalse } = useBoolean(true); 8 | 9 | /** 10 | * 触发重载 11 | * @param duration - 延迟时间(ms) 12 | */ 13 | async function handleReload(duration = 0) { 14 | setFalse(); 15 | await nextTick(); 16 | 17 | if (duration > 0) { 18 | setTimeout(() => { 19 | setTrue(); 20 | }, duration); 21 | } 22 | } 23 | 24 | return { 25 | reloadFlag, 26 | handleReload 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /ui/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './business'; 3 | -------------------------------------------------------------------------------- /ui/src/layouts/BlankLayout/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ui/src/layouts/common/AiChat/components/ChatButton/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | 23 | 35 | -------------------------------------------------------------------------------- /ui/src/layouts/common/AiChat/components/index.ts: -------------------------------------------------------------------------------- 1 | import ChatButton from './ChatButton/index.vue'; 2 | import Chat from './Chat/index.vue'; 3 | 4 | export { ChatButton,Chat }; 5 | -------------------------------------------------------------------------------- /ui/src/layouts/common/AiChat/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalBackTop/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalFooter/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/FullScreen.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/GithubSite.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/MenuCollapse.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/SettingButton.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/ThemeMode.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalHeader/components/index.ts: -------------------------------------------------------------------------------- 1 | import MenuCollapse from './MenuCollapse.vue'; 2 | import GlobalBreadcrumb from './GlobalBreadcrumb.vue'; 3 | import HeaderMenu from './HeaderMenu.vue'; 4 | import GithubSite from './GithubSite.vue'; 5 | import FullScreen from './FullScreen.vue'; 6 | import ThemeMode from './ThemeMode.vue'; 7 | import UserAvatar from './UserAvatar.vue'; 8 | import SystemMessage from './SystemMessage.vue'; 9 | import SettingButton from './SettingButton.vue'; 10 | 11 | export { 12 | MenuCollapse, 13 | GlobalBreadcrumb, 14 | HeaderMenu, 15 | GithubSite, 16 | FullScreen, 17 | ThemeMode, 18 | UserAvatar, 19 | SystemMessage, 20 | SettingButton 21 | }; 22 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalLogo/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSearch/components/SearchFooter.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | 23 | 28 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSearch/components/index.ts: -------------------------------------------------------------------------------- 1 | import SearchModal from './SearchModal.vue'; 2 | 3 | export { SearchModal }; 4 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSearch/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/components/VerticalMixSider/components/MixMenuCollapse.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/components/VerticalMixSider/components/index.ts: -------------------------------------------------------------------------------- 1 | import MixMenuDetail from './MixMenuDetail.vue'; 2 | import MixMenuDrawer from './MixMenuDrawer.vue'; 3 | import MixMenuCollapse from './MixMenuCollapse.vue'; 4 | 5 | export { MixMenuDetail, MixMenuDrawer, MixMenuCollapse }; 6 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/components/VerticalSider/components/index.ts: -------------------------------------------------------------------------------- 1 | import VerticalMenu from './VerticalMenu.vue'; 2 | 3 | export { VerticalMenu }; 4 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/components/VerticalSider/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/components/index.ts: -------------------------------------------------------------------------------- 1 | import VerticalSider from './VerticalSider/index.vue'; 2 | import VerticalMixSider from './VerticalMixSider/index.vue'; 3 | 4 | export { VerticalSider, VerticalMixSider }; 5 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalSider/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalTab/components/ReloadButton/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalTab/components/SettingButton/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalTab/components/TabDetail/components/index.ts: -------------------------------------------------------------------------------- 1 | import ContextMenu from './ContextMenu.vue'; 2 | 3 | export { ContextMenu }; 4 | -------------------------------------------------------------------------------- /ui/src/layouts/common/GlobalTab/components/index.ts: -------------------------------------------------------------------------------- 1 | import TabDetail from './TabDetail/index.vue'; 2 | import ProjectSettingButton from './SettingButton/index.vue'; 3 | import WindowButton from './WindowButton/index.vue'; 4 | 5 | export { TabDetail, ProjectSettingButton,WindowButton }; 6 | -------------------------------------------------------------------------------- /ui/src/layouts/common/index.ts: -------------------------------------------------------------------------------- 1 | import GlobalHeader from './GlobalHeader/index.vue'; 2 | import GlobalTab from './GlobalTab/index.vue'; 3 | import GlobalSider from './GlobalSider/index.vue'; 4 | import GlobalContent from './GlobalContent/index.vue'; 5 | import GlobalFooter from './GlobalFooter/index.vue'; 6 | import GlobalLogo from './GlobalLogo/index.vue'; 7 | import GlobalBackTop from './GlobalBackTop/index.vue'; 8 | import AiChat from "./AiChat/index.vue" 9 | 10 | export {GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter, GlobalLogo, GlobalBackTop,AiChat }; 11 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/cmd/components/index.ts: -------------------------------------------------------------------------------- 1 | // import AddCmd from './cmd/addCmd.vue'; 2 | // import UpdCmd from './cmd/updCmd.vue'; 3 | // 4 | // export { AddCmd, UpdCmd }; 5 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/globalVariable/components/index.ts: -------------------------------------------------------------------------------- 1 | import AddGlobalVariable from './addGlobaVariable.vue'; 2 | import UpdGlobalVariable from './updGlobaVariable.vue'; 3 | 4 | export { AddGlobalVariable, UpdGlobalVariable }; 5 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/host/components/index.ts: -------------------------------------------------------------------------------- 1 | import AddSession from './session/addSession.vue'; 2 | import UpdSession from './session/updSession.vue'; 3 | import AddGroup from './group/addGroup.vue'; 4 | import UpdGroup from './group/updGroup.vue'; 5 | 6 | export { AddSession, AddGroup, UpdGroup, UpdSession }; 7 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/index.ts: -------------------------------------------------------------------------------- 1 | import Host from './host/index.vue'; 2 | import ShortcutCmd from './shortcutCmd/index.vue'; 3 | import GlobalVariable from './globalVariable/index.vue'; 4 | 5 | export { Host ,ShortcutCmd,GlobalVariable}; 6 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/osType/components/index.ts: -------------------------------------------------------------------------------- 1 | // import AddOsType from './group/addOsType.vue'; 2 | // import UpdOsType from './group/updOsType.vue'; 3 | // 4 | // export { UpdOsType, AddOsType }; 5 | -------------------------------------------------------------------------------- /ui/src/layouts/common/shell/shortcutCmd/components/index.ts: -------------------------------------------------------------------------------- 1 | import AddCmd from './cmd/addCmd.vue'; 2 | import UpdCmd from './cmd/updCmd.vue'; 3 | import AddGroup from './group/addGroup.vue'; 4 | import UpdGroup from './group/updGroup.vue'; 5 | 6 | export { AddCmd, UpdCmd, AddGroup, UpdGroup }; 7 | -------------------------------------------------------------------------------- /ui/src/layouts/index.ts: -------------------------------------------------------------------------------- 1 | const BasicLayout = () => import('./BasicLayout/index.vue'); 2 | const BlankLayout = () => import('./BlankLayout/index.vue'); 3 | 4 | export { BasicLayout, BlankLayout }; 5 | -------------------------------------------------------------------------------- /ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import { setupAssets } from './plugins'; 3 | import { setupStore } from './store'; 4 | import { setupDirectives } from './directives'; 5 | import { setupRouter } from './router'; 6 | import App from './App.vue'; 7 | 8 | async function setupApp() { 9 | // import assets: js、css 10 | setupAssets(); 11 | 12 | const app = createApp(App); 13 | 14 | // store plugin: pinia 15 | setupStore(app); 16 | 17 | // vue custom directives 18 | setupDirectives(app); 19 | 20 | // vue router 21 | await setupRouter(app); 22 | 23 | // mount app 24 | app.mount('#app'); 25 | } 26 | 27 | setupApp(); 28 | -------------------------------------------------------------------------------- /ui/src/plugins/assets.ts: -------------------------------------------------------------------------------- 1 | import 'uno.css'; 2 | import 'virtual:svg-icons-register'; 3 | import '../styles/css/global.css'; 4 | 5 | /** import static assets: css, js , font and so on. - [引入静态资源,css、js和字体文件等] */ 6 | export default function setupAssets() { 7 | // 8 | } 9 | -------------------------------------------------------------------------------- /ui/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import setupAssets from './assets'; 2 | 3 | export { setupAssets }; 4 | -------------------------------------------------------------------------------- /ui/src/router/guard/index.ts: -------------------------------------------------------------------------------- 1 | import type { Router } from 'vue-router'; 2 | import { useTitle } from '@vueuse/core'; 3 | import { createPermissionGuard } from './permission'; 4 | 5 | /** 6 | * 路由守卫函数 7 | * @param router - 路由实例 8 | */ 9 | export function createRouterGuard(router: Router) { 10 | router.beforeEach(async (to, from, next) => { 11 | // 开始 loadingBar 12 | window.$loadingBar?.start(); 13 | // 页面跳转权限处理 14 | await createPermissionGuard(to, from, next); 15 | }); 16 | router.afterEach(to => { 17 | // 设置document title 18 | console.log('router'); 19 | useTitle(to.meta.title); 20 | // 结束 loadingBar 21 | window.$loadingBar?.finish(); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /ui/src/router/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './scroll'; 2 | -------------------------------------------------------------------------------- /ui/src/router/helpers/scroll.ts: -------------------------------------------------------------------------------- 1 | import type { RouterScrollBehavior } from 'vue-router'; 2 | import { useTabStore } from '@/store'; 3 | 4 | export const scrollBehavior: RouterScrollBehavior = (to, from) => { 5 | return new Promise(resolve => { 6 | const tab = useTabStore(); 7 | 8 | if (to.hash) { 9 | const el = document.querySelector(to.hash); 10 | if (el) { 11 | resolve({ 12 | el, 13 | behavior: 'smooth' 14 | }); 15 | } 16 | } 17 | 18 | const { left, top } = tab.getTabScrollPosition(to.path); 19 | const scrollPosition = { 20 | left, 21 | top 22 | }; 23 | const { scrollLeft, scrollTop } = document.documentElement; 24 | 25 | const isFromCached = Boolean(from.meta.keepAlive); 26 | if (isFromCached) { 27 | tab.recordTabScrollPosition(from.path, { left: scrollLeft, top: scrollTop }); 28 | } 29 | 30 | const duration = !scrollPosition.left && !scrollPosition.top ? 0 : 350; 31 | 32 | setTimeout(() => { 33 | resolve(scrollPosition); 34 | }, duration); 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /ui/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'; 3 | import { transformAuthRoutesToVueRoutes, transformRouteNameToRoutePath } from '@/utils'; 4 | import { constantRoutes } from './routes'; 5 | import { scrollBehavior } from './helpers'; 6 | import { createRouterGuard } from './guard'; 7 | 8 | const { VITE_HASH_ROUTE = 'N', VITE_BASE_URL } = import.meta.env; 9 | 10 | export const router = createRouter({ 11 | history: VITE_HASH_ROUTE === 'Y' ? createWebHashHistory(VITE_BASE_URL) : createWebHistory(VITE_BASE_URL), 12 | routes: transformAuthRoutesToVueRoutes(constantRoutes), 13 | scrollBehavior 14 | }); 15 | 16 | /** setup vue router. - [安装vue路由] */ 17 | export async function setupRouter(app: App) { 18 | app.use(router); 19 | createRouterGuard(router); 20 | await router.isReady(); 21 | } 22 | 23 | /** 路由名称 */ 24 | export const routeName = (key: AuthRoute.RouteKey) => key; 25 | /** 路由路径 */ 26 | export const routePath = (key: Exclude) => transformRouteNameToRoutePath(key); 27 | 28 | export * from './routes'; 29 | export * from './modules'; 30 | -------------------------------------------------------------------------------- /ui/src/router/modules/index.ts: -------------------------------------------------------------------------------- 1 | import { handleModuleRoutes } from '@/utils'; 2 | 3 | const modules = import.meta.glob('./**/*.ts', { eager: true }) as AuthRoute.RouteModule; 4 | 5 | export const routes = handleModuleRoutes(modules); 6 | -------------------------------------------------------------------------------- /ui/src/router/modules/projectSetting.ts: -------------------------------------------------------------------------------- 1 | const project: AuthRoute.Route = { 2 | name: 'project', 3 | path: '/project', 4 | component: 'basic', 5 | children: [ 6 | { 7 | name: 'project_setting', 8 | path: '/project/setting', 9 | component: 'self', 10 | meta: { 11 | title: '设置', 12 | icon: 'mdi:vuejs', 13 | keepAlive: true, 14 | } 15 | } 16 | ], 17 | meta: { 18 | title: 'Setting', 19 | icon: 'carbon:document', 20 | order: 2 21 | } 22 | }; 23 | 24 | export default project; 25 | -------------------------------------------------------------------------------- /ui/src/router/modules/shell.ts: -------------------------------------------------------------------------------- 1 | const shell: AuthRoute.Route = { 2 | name: 'shell', 3 | path: '/shell', 4 | component: 'basic', 5 | children: [ 6 | { 7 | name: 'shell_info', 8 | path: '/shell/info', 9 | component: 'self', 10 | meta: { 11 | title: '首页', 12 | icon: 'icon-park-outline:analysis', 13 | isTty: false 14 | } 15 | }, 16 | { 17 | name: 'shell_terminal', 18 | path: '/shell/terminal', 19 | component: 'self', 20 | meta: { 21 | title: '连接中...', 22 | icon: 'mdi:vuejs', 23 | keepAlive: true, 24 | multiTab: true, 25 | isTty: true 26 | } 27 | } 28 | ], 29 | meta: { 30 | title: 'shell', 31 | icon: 'carbon:document', 32 | order: 2 33 | } 34 | }; 35 | 36 | export default shell; 37 | -------------------------------------------------------------------------------- /ui/src/service/api/auth.ts: -------------------------------------------------------------------------------- 1 | import { mockRequest } from '../request'; 2 | 3 | /** 4 | * 获取验证码 5 | * @param phone - 手机号 6 | * @returns - 返回boolean值表示是否发送成功 7 | */ 8 | export function fetchSmsCode(phone: string) { 9 | return mockRequest.post('/getSmsCode', { phone }); 10 | } 11 | 12 | /** 13 | * 登录 14 | * @param userName - 用户名 15 | * @param password - 密码 16 | */ 17 | export function fetchLogin(userName: string, password: string) { 18 | return mockRequest.post('/login', { userName, password }); 19 | } 20 | 21 | /** 获取用户信息 */ 22 | export function fetchUserInfo() { 23 | return mockRequest.get('/getUserInfo'); 24 | } 25 | 26 | /** 27 | * 获取用户路由数据 28 | * @param userId - 用户id 29 | * @description 后端根据用户id查询到对应的角色类型,并将路由筛选出对应角色的路由数据返回前端 30 | */ 31 | export function fetchUserRoutes(userId: string) { 32 | return mockRequest.post('/getUserRoutes', { userId }); 33 | } 34 | 35 | /** 36 | * 刷新token 37 | * @param refreshToken 38 | */ 39 | export function fetchUpdateToken(refreshToken: string) { 40 | return mockRequest.post('/updateToken', { refreshToken }); 41 | } 42 | -------------------------------------------------------------------------------- /ui/src/service/api/demo.adapter.ts: -------------------------------------------------------------------------------- 1 | export function adapterOfFetchDataWithAdapter(data: ApiDemo.DataWithAdapter): Demo.DataWithAdapter { 2 | const { dataId, dataName } = data; 3 | 4 | const result: Demo.DataWithAdapter = { 5 | id: dataId, 6 | name: dataName 7 | }; 8 | 9 | return result; 10 | } 11 | -------------------------------------------------------------------------------- /ui/src/service/api/demo.ts: -------------------------------------------------------------------------------- 1 | import { adapter } from '@/utils'; 2 | import { request, mockRequest } from '../request'; 3 | import { adapterOfFetchDataWithAdapter } from './demo.adapter'; 4 | 5 | /** 带有适配器的请求示例 */ 6 | export async function fetchDataWithAdapter() { 7 | const res = await mockRequest.post('/apiDemoWithAdapter'); 8 | return adapter(adapterOfFetchDataWithAdapter, res); 9 | } 10 | 11 | /** 测试代理后的请求 */ 12 | export function testRequestWithProxy() { 13 | return request.get('/test-proxy'); // 确保.env-config的url和当前地址组成的 `${url}/test-proxy` 是有效的后端接口 14 | } 15 | -------------------------------------------------------------------------------- /ui/src/service/api/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth'; 2 | export * from './demo'; 3 | export * from '../../theblind_shell/service/shell/host'; 4 | export * from './management'; 5 | -------------------------------------------------------------------------------- /ui/src/service/api/management.adapter.ts: -------------------------------------------------------------------------------- 1 | import { EnumGender } from '@/enum'; 2 | import { isUndefined } from '@/utils'; 3 | 4 | export function adapterOfFetchUserManagementList( 5 | requestData: ApiUserManagement.UserTable[], 6 | adminId: string 7 | ): UserManagement.UserTable[] { 8 | const genderMap: Record< 9 | NonNullable, 10 | NonNullable 11 | > = { 12 | '0': 'female', 13 | '1': 'male' 14 | }; 15 | 16 | // 1. 有可能依赖于多个接口的结果,再转换成页面的数据 17 | // 2. 接口定义的字段有可能为null, 例如 预期是数组却返回了null,导致调用数组方法报错 18 | // 3. 字段可能丢失 19 | 20 | return requestData.map(item => { 21 | const { id, name, age, gender } = item; 22 | 23 | const userName = name + (adminId === id ? '(管理员)' : ''); 24 | 25 | const userAge = isUndefined(age) ? '无' : String(age); 26 | 27 | const userGender = gender !== null ? genderMap[gender] : 'null'; 28 | 29 | const result: UserManagement.UserTable = { 30 | id, 31 | userName, 32 | userAge, 33 | userGender, 34 | userGenderLabel: EnumGender[userGender] 35 | }; 36 | 37 | return result; 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /ui/src/service/api/management.ts: -------------------------------------------------------------------------------- 1 | import { adapter } from '@/utils'; 2 | import { mockRequest } from '../request'; 3 | import { adapterOfFetchUserManagementList } from './management.adapter'; 4 | 5 | /** 6 | * 获取用户管理列表 7 | */ 8 | export async function fetchUserManagementList() { 9 | const data = await mockRequest.post('/getUserManagementList'); 10 | 11 | const id = '2'; 12 | 13 | return adapter(adapterOfFetchUserManagementList, data, { error: null, data: id }); 14 | } 15 | 16 | // export async function fetchFilterUserManagementList( 17 | // filterKey: keyof UserManagement.UserTable, 18 | // mode: 'fontEnd' | 'backEnd', 19 | // source: UserManagement.UserTable[] 20 | // ) { 21 | 22 | // } 23 | -------------------------------------------------------------------------------- /ui/src/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | -------------------------------------------------------------------------------- /ui/src/service/request/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { AxiosRequestConfig } from 'axios'; 2 | import { useAuthStore } from '@/store'; 3 | import { getRefreshToken, setToken, setRefreshToken } from '@/utils'; 4 | import { fetchUpdateToken } from '../api'; 5 | 6 | /** 7 | * 刷新token 8 | * @param axiosConfig - token失效时的请求配置 9 | */ 10 | export async function handleRefreshToken(axiosConfig: AxiosRequestConfig) { 11 | const { resetAuthStore } = useAuthStore(); 12 | const refreshToken = getRefreshToken(); 13 | const { data } = await fetchUpdateToken(refreshToken); 14 | if (data) { 15 | setToken(data.token); 16 | setRefreshToken(data.refreshToken); 17 | const config = { ...axiosConfig }; 18 | if (config.headers) { 19 | config.headers.Authorization = data.token; 20 | } 21 | return config; 22 | } 23 | 24 | resetAuthStore(); 25 | return null; 26 | } 27 | -------------------------------------------------------------------------------- /ui/src/service/request/index.ts: -------------------------------------------------------------------------------- 1 | import { getServiceEnvConfig } from '~/.env-config'; 2 | import { createRequest } from './request'; 3 | 4 | const { url, urlPattern, secondUrl, secondUrlPattern } = getServiceEnvConfig(import.meta.env); 5 | 6 | const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'Y'; 7 | 8 | export const request = createRequest({ baseURL: isHttpProxy ? urlPattern : url }); 9 | 10 | export const secondRequest = createRequest({ baseURL: isHttpProxy ? secondUrlPattern : secondUrl }); 11 | 12 | export const mockRequest = createRequest({ baseURL: '/mock' }); 13 | -------------------------------------------------------------------------------- /ui/src/settings/color.ts: -------------------------------------------------------------------------------- 1 | import colorJson from './color.json'; 2 | 3 | interface TraditionColorDetail { 4 | label: string; 5 | color: string; 6 | } 7 | interface TraditionColor { 8 | label: string; 9 | data: TraditionColorDetail[]; 10 | } 11 | 12 | /** 中国传统颜色 */ 13 | export const traditionColors = colorJson as TraditionColor[]; 14 | 15 | export function isInTraditionColors(color: string) { 16 | return traditionColors.some(item => { 17 | const flag = item.data.some(v => v.color === color); 18 | return flag; 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /ui/src/settings/index.ts: -------------------------------------------------------------------------------- 1 | export * from './theme'; 2 | export * from './color'; 3 | -------------------------------------------------------------------------------- /ui/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | 4 | /** setup vue store plugin: pinia. - [安装vue状态管理插件:pinia] */ 5 | export function setupStore(app: App) { 6 | const store = createPinia(); 7 | app.use(store); 8 | } 9 | 10 | export * from './modules'; 11 | export * from './subscribe'; 12 | -------------------------------------------------------------------------------- /ui/src/store/modules/index.ts: -------------------------------------------------------------------------------- 1 | export * from './app'; 2 | export * from './theme'; 3 | export * from './auth'; 4 | export * from './tab'; 5 | export * from './route'; 6 | -------------------------------------------------------------------------------- /ui/src/store/subscribe/app.ts: -------------------------------------------------------------------------------- 1 | /** 订阅app store */ 2 | export default function subscribeAppStore() { 3 | // 4 | } 5 | -------------------------------------------------------------------------------- /ui/src/store/subscribe/index.ts: -------------------------------------------------------------------------------- 1 | import subscribeAppStore from './app'; 2 | import subscribeThemeStore from './theme'; 3 | 4 | /** 订阅状态 */ 5 | export function subscribeStore() { 6 | subscribeAppStore(); 7 | subscribeThemeStore(); 8 | } 9 | -------------------------------------------------------------------------------- /ui/src/styles/css/global.css: -------------------------------------------------------------------------------- 1 | @import './transition.css'; 2 | @import './reset.css'; 3 | 4 | html, body, #app { 5 | height: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /ui/src/styles/css/scrollbar.css: -------------------------------------------------------------------------------- 1 | /*---滚动条默认显示样式--*/ 2 | ::-webkit-scrollbar-thumb { 3 | background-color: #e6e6e6; 4 | border-radius: 6px; 5 | } 6 | /*---鼠标点击滚动条显示样式--*/ 7 | ::-webkit-scrollbar-thumb:hover { 8 | background-color: #e6e6e6; 9 | border-radius: 6px; 10 | } 11 | /*---滚动条大小--*/ 12 | ::-webkit-scrollbar { 13 | width: 6px; 14 | height: 6px; 15 | } 16 | 17 | /*---滚动框背景样式--*/ 18 | ::-webkit-scrollbar-track-piece { 19 | background-color: rgba(0, 0, 0, 0); 20 | border-radius: 0; 21 | } 22 | 23 | html { 24 | scrollbar-width: thin; 25 | scrollbar-color: e6e6e6 transparent; 26 | } 27 | -------------------------------------------------------------------------------- /ui/src/styles/scss/global.scss: -------------------------------------------------------------------------------- 1 | @import './scrollbar.scss'; 2 | -------------------------------------------------------------------------------- /ui/src/styles/scss/scrollbar.scss: -------------------------------------------------------------------------------- 1 | @mixin scrollbar($size:8px, $color:#d9d9d9) { 2 | &::-webkit-scrollbar-thumb { 3 | background-color: $color; 4 | border-radius: $size; 5 | } 6 | &::-webkit-scrollbar-thumb:hover { 7 | background-color: $color; 8 | border-radius: $size; 9 | } 10 | &::-webkit-scrollbar { 11 | width: $size; 12 | height: $size; 13 | } 14 | &::-webkit-scrollbar-track-piece { 15 | background-color: rgba(0, 0, 0, 0); 16 | border-radius: 0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/aiChat.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function getAiChat(question: any) { 4 | return request.get('/ai/chat', { params:{question} }); 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/channel.ts: -------------------------------------------------------------------------------- 1 | import {EnumContentType} from "@/enum"; 2 | import {request} from '@/service/request'; 3 | 4 | 5 | export function bindChannel(syncChannelId,sshChannelId) { 6 | return request.post('/syncChannel/bind', { syncChannelId,sshChannelId },{ headers: { 'Content-Type': EnumContentType.formUrlencoded } }); 7 | } 8 | 9 | export function removeBindChannel(sshChannelId) { 10 | return request.post('/syncChannel/removeBind',{sshChannelId},{ headers: { 'Content-Type': EnumContentType.formUrlencoded } }); 11 | } 12 | export function getChannelId(sshChannelId) { 13 | return request.get('/syncChannel',{params:{sshChannelId}}); 14 | } 15 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/cmd.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function get() { 5 | return request.get('/cmd'); 6 | } 7 | 8 | export function getListById(params: { ids: string }) { 9 | return request.get('/listById', { params }); 10 | } 11 | export function getSingle(id: number) { 12 | return request.get(`/cmd/${id}`); 13 | } 14 | 15 | export function add(group: any) { 16 | return request.post('/cmd', group); 17 | } 18 | export function del(id: number) { 19 | return request.delete(`/cmd/${id}`); 20 | } 21 | export function upd(cmd: any, id: number) { 22 | return request.put(`/cmd/${id}`, cmd); 23 | } 24 | 25 | export function partUpd(cmd: any, id: number) { 26 | return request.patch(`/cmd/${id}`, cmd); 27 | } 28 | 29 | export function tree() { 30 | return request.get('/cmd/tree'); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/config.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function getAppearance() { 4 | return request.get('/config/appearance'); 5 | } 6 | 7 | export function updAppearance(appearance: any) { 8 | return request.put(`/config/appearance`, appearance); 9 | } 10 | 11 | export function getConfig() { 12 | return request.get('/config'); 13 | } 14 | 15 | export function getTerminal() { 16 | return request.get('/config/terminal'); 17 | } 18 | 19 | export function updTerminal(appearance: any) { 20 | return request.put(`/config/terminal`, appearance); 21 | } 22 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/connectionLog.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function topList() { 4 | return request.get('/connectionLog/topList'); 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/desktop.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function toGithub() { 4 | return request.post('/desktop/toGithub'); 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/globalVariable.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function get() { 5 | return request.get('/globalVariable'); 6 | } 7 | export function getSingle(id: number) { 8 | return request.get(`/globalVariable/${id}`); 9 | } 10 | 11 | export function add(globalVariable: any) { 12 | return request.post('/globalVariable', globalVariable); 13 | } 14 | export function del(id: number) { 15 | return request.delete(`/globalVariable/${id}`); 16 | } 17 | export function upd(globalVariable: any, id: number) { 18 | return request.put(`/globalVariable/${id}`, globalVariable); 19 | } 20 | 21 | /** 测试代理后的请求 */ 22 | export function page(query: any) { 23 | const params = { pageSize: query.pageSize, page: query.page, name: query.name }; 24 | return request.get('/globalVariable/page', { params }); 25 | } 26 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/historyCmd.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function getPage( 5 | page: any, 6 | size: any, 7 | param: { sessionId: any; sessionType: any; startDate: any; endDate: any } 8 | ) { 9 | return request.post('/historyCmd/getPage', { page, size, param }); 10 | } 11 | export function add(history: any) { 12 | return request.post(`/historyCmd`, history); 13 | } 14 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/host.ts: -------------------------------------------------------------------------------- 1 | import { EnumContentType } from '@/enum'; 2 | import { request } from '@/service/request'; 3 | 4 | /** 测试代理后的请求 */ 5 | export function get() { 6 | return request.get('/sshSession'); 7 | } 8 | export function getSingle(id: number | unknown) { 9 | return request.get(`/sshSession/${id}`); 10 | } 11 | 12 | export function addGroup(group: any) { 13 | return request.post('/sessionGroup', group); 14 | } 15 | export function add(sshSession: any) { 16 | return request.post('/sshSession', sshSession); 17 | } 18 | 19 | export function upd(sshSession: any, sshSessionId: number) { 20 | return request.put(`/sshSession/${sshSessionId}`, sshSession); 21 | } 22 | 23 | export function updGroup(groupId: any, id: number) { 24 | return request.patch( 25 | `/sshSession/${id}/group`, 26 | { groupId }, 27 | { headers: { 'Content-Type': EnumContentType.formUrlencoded } } 28 | ); 29 | } 30 | 31 | export function getHostGroupId(sshSessionId: any) { 32 | return request.get(`/sshSession/sshSessionGroup/${sshSessionId}`); 33 | } 34 | 35 | export function del(sshSessionId: number) { 36 | return request.delete(`/sshSession/${sshSessionId}`); 37 | } 38 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/hostGroup.ts: -------------------------------------------------------------------------------- 1 | import { EnumContentType } from '@/enum'; 2 | import { request } from '@/service/request'; 3 | 4 | /** 测试代理后的请求 */ 5 | export function get() { 6 | return request.get('/host'); 7 | } 8 | export function getSingle(id: number) { 9 | return request.get(`/sessionGroup/${id}`); 10 | } 11 | 12 | export function add(group: any) { 13 | return request.post('/sessionGroup', group); 14 | } 15 | export function del(id: number) { 16 | return request.delete(`/sessionGroup/${id}`); 17 | } 18 | export function upd(hostGroup: any, id: number) { 19 | return request.put(`/sessionGroup/${id}`, hostGroup); 20 | } 21 | 22 | export function updGroup(groupId: any, id: number) { 23 | return request.patch( 24 | `/sessionGroup/${id}/group`, 25 | { groupId }, 26 | { headers: { 'Content-Type': EnumContentType.formUrlencoded } } 27 | ); 28 | } 29 | 30 | export function tree() { 31 | return request.get('/sessionGroup/tree'); 32 | } 33 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/retrieve.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function retrieve(term: any, channelId: number | unknown, skipVerify: any) { 5 | return request.get('/retrieve', { params: { term, channelId, skipVerify } }); 6 | } 7 | 8 | export function retrieveCmd(term: any, currentCmdId: any, sessionId: any) { 9 | return request.get('/retrieve/cmd', { params: { term, currentCmdId, sessionId } }); 10 | } 11 | 12 | export function parseShortcutCmdPlaceholders(id: number, channelId: string | unknown) { 13 | return request.get('/retrieve/parseShortcutCmdPlaceholders', { params: { id, channelId } }); 14 | } 15 | 16 | export function getMatchItems(id: number, channelId: string | unknown) { 17 | return request.get('/retrieve/matchItems', { params: { id, channelId } }); 18 | } 19 | 20 | export function parseTemplate(id: number, channelId: string | unknown, items: any) { 21 | return request.post('/retrieve/parseTemplate', { id, channelId, items }); 22 | } 23 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/shortcutCmd.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function get() { 5 | return request.get('/shortcutCmd'); 6 | } 7 | export function getSingle(id: number) { 8 | return request.get(`/shortcutCmd/${id}`); 9 | } 10 | 11 | export function add(shortcutCmd: any) { 12 | return request.post('/shortcutCmd', shortcutCmd); 13 | } 14 | export function del( id: number) { 15 | return request.delete(`/shortcutCmd/${id}`); 16 | } 17 | export function upd(shortcutCmd: any, id: number) { 18 | return request.put(`/shortcutCmd/${id}`, shortcutCmd); 19 | } 20 | 21 | export function partUpd(shortcutCmd: any, id: number) { 22 | return request.put(`/shortcutCmd/${id}`, shortcutCmd); 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/shortcutCmdGroup.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | /** 测试代理后的请求 */ 4 | export function get() { 5 | return request.get('/shortcutCmdGroup'); 6 | } 7 | export function getSingle(id: number) { 8 | return request.get(`/shortcutCmdGroup/${id}`); 9 | } 10 | 11 | export function add(group: any) { 12 | return request.post('/shortcutCmdGroup', group); 13 | } 14 | export function del(id: number) { 15 | return request.delete(`/shortcutCmdGroup/${id}`); 16 | } 17 | export function upd(hostGroup: any, id: number) { 18 | return request.put(`/shortcutCmdGroup/${id}`, hostGroup); 19 | } 20 | 21 | export function tree() { 22 | return request.get('/shortcutCmdGroup/tree'); 23 | } 24 | export function parentTree() { 25 | return request.get('/shortcutCmdGroup/parentTree'); 26 | } 27 | 28 | export function partUpd() { 29 | return request.get('/shortcutCmdGroup/parentTree'); 30 | } 31 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/system.js: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function shutdown() { 4 | return request.post('/system/shutdown'); 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/service/shell/ttyType.ts: -------------------------------------------------------------------------------- 1 | import { request } from '@/service/request'; 2 | 3 | export function getListById(params: any) { 4 | return request.get('/ttyType/listById', { params }); 5 | } 6 | export function getSingle(id: number) { 7 | return request.get(`/ttyType/${id}`); 8 | } 9 | 10 | export function selectTree() { 11 | return request.get('/ttyType/selectTree'); 12 | } 13 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/typings/OsType.ts: -------------------------------------------------------------------------------- 1 | // 后端接口返回的数据类型 2 | 3 | declare namespace OsType { 4 | /** 返回的token和刷新token */ 5 | interface Token { 6 | token: string; 7 | refreshToken: string; 8 | } 9 | /** 返回的用户信息 */ 10 | type UserInfo = Auth.UserInfo; 11 | } 12 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/typings/cmd.ts: -------------------------------------------------------------------------------- 1 | // cmd 页面对象 2 | 3 | /** CmdTag */ 4 | interface CmdTag { 5 | osTypeId: number; 6 | compatible: boolean; 7 | osTypeName: string; 8 | } 9 | -------------------------------------------------------------------------------- /ui/src/theblind_shell/utils/TheBlindShellUtils.js: -------------------------------------------------------------------------------- 1 | export const reset = (data, reData) => { 2 | const keys = Object.keys(data); 3 | keys.forEach(item => { 4 | // eslint-disable-next-line no-param-reassign 5 | data[item] = reData[item]; 6 | }); 7 | }; 8 | 9 | export const cloneObj = obj => { 10 | let newObj = {}; 11 | if (obj instanceof Array) { 12 | newObj = []; 13 | } 14 | // eslint-disable-next-line guard-for-in,no-restricted-syntax 15 | for (const key in obj) { 16 | const val = obj[key]; 17 | newObj[key] = typeof val === 'object' ? cloneObj(val) : val; 18 | } 19 | return newObj; 20 | }; 21 | -------------------------------------------------------------------------------- /ui/src/typings/api.d.ts: -------------------------------------------------------------------------------- 1 | // 后端接口返回的数据类型 2 | 3 | /** 后端返回的用户权益相关类型 */ 4 | declare namespace ApiAuth { 5 | /** 返回的token和刷新token */ 6 | interface Token { 7 | token: string; 8 | refreshToken: string; 9 | } 10 | /** 返回的用户信息 */ 11 | type UserInfo = Auth.UserInfo; 12 | } 13 | 14 | /** 后端返回的路由相关类型 */ 15 | declare namespace ApiRoute { 16 | /** 后端返回的路由数据类型 */ 17 | interface Route { 18 | /** 动态路由 */ 19 | routes: AuthRoute.Route[]; 20 | /** 路由首页对应的key */ 21 | home: AuthRoute.RouteKey; 22 | } 23 | } 24 | 25 | declare namespace ApiDemo { 26 | interface DataWithAdapter { 27 | dataId: string; 28 | dataName: string; 29 | } 30 | } 31 | 32 | declare namespace ApiUserManagement { 33 | interface UserTable { 34 | /** 用户id */ 35 | id: string; 36 | /** 用户名 */ 37 | name: string; 38 | /** 用户年龄 */ 39 | age?: number; 40 | /** 41 | * 用户性别 42 | * - 男 1 43 | * - 女 0 44 | */ 45 | gender: '0' | '1' | null; 46 | /** 创建时间 */ 47 | createTime: string; 48 | /** 更新时间 */ 49 | updateTime: string; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ui/src/typings/expose.d.ts: -------------------------------------------------------------------------------- 1 | /** vue 的defineExpose导出的类型 */ 2 | declare namespace Expose { 3 | interface BetterScroll { 4 | instance: import('@better-scroll/core').BScrollInstance; 5 | } 6 | 7 | interface ImageVerify { 8 | /** 获取图片验证码 */ 9 | getImgCode(): void; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ui/src/typings/global.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | $loadingBar?: import('naive-ui').LoadingBarProviderInst; 3 | $dialog?: import('naive-ui').DialogProviderInst; 4 | $message?: import('naive-ui').MessageProviderInst; 5 | $notification?: import('naive-ui').NotificationProviderInst; 6 | } 7 | 8 | /** 通用类型 */ 9 | declare namespace Common { 10 | /** 11 | * 策略模式 12 | * [状态, 为true时执行的回调函数] 13 | */ 14 | type StrategyAction = [boolean, () => void]; 15 | } 16 | 17 | /** 构建时间 */ 18 | declare const PROJECT_BUILD_TIME: string; 19 | -------------------------------------------------------------------------------- /ui/src/typings/package.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | declare namespace BMap { 6 | class Map extends BMapGL.Map {} 7 | class Point extends BMapGL.Point {} 8 | } 9 | 10 | declare const TMap: any; 11 | 12 | declare module 'virtual:icons/*' { 13 | import type { FunctionalComponent, SVGAttributes } from 'vue'; 14 | 15 | const component: FunctionalComponent; 16 | export default component; 17 | } 18 | declare module '~icons/*' { 19 | import type { FunctionalComponent, SVGAttributes } from 'vue'; 20 | 21 | const component: FunctionalComponent; 22 | export default component; 23 | } 24 | -------------------------------------------------------------------------------- /ui/src/typings/router.d.ts: -------------------------------------------------------------------------------- 1 | import 'vue-router'; 2 | 3 | declare module 'vue-router' { 4 | interface RouteMeta extends AuthRoute.RouteMeta {} 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/typings/utils.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace TypeUtil { 2 | type Noop = (...args: any) => any; 3 | 4 | type UnionInclude = K extends keyof T ? true : false; 5 | 6 | type GetFunArgs = F extends (...args: infer P) => any ? P : never; 7 | 8 | type GetFunReturn = F extends (...args: any) => infer R ? R : never; 9 | 10 | type Writable = { [K in keyof T]: T[K] }; 11 | 12 | type FirstOfArray = T extends [infer First, ...infer _Rest] ? First : never; 13 | 14 | type LastOfArray = T extends [...infer _Rest, infer Last] ? Last : never; 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/utils/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './user'; 2 | -------------------------------------------------------------------------------- /ui/src/utils/common/contextmenu.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 禁用右键菜单 3 | * @param e - 图标名称 4 | */ 5 | export function disabledContextMenu(e: MouseEvent) { 6 | e.preventDefault(); 7 | } 8 | -------------------------------------------------------------------------------- /ui/src/utils/common/icon.ts: -------------------------------------------------------------------------------- 1 | import { h } from 'vue'; 2 | import { NIcon } from 'naive-ui'; 3 | import { Icon } from '@iconify/vue'; 4 | import SvgIcon from '@/components/custom/SvgIcon.vue'; 5 | 6 | /** 7 | * 动态渲染iconify 8 | * @param icon - 图标名称 9 | * @param color - 图标颜色 10 | * @param fontSize - 图标大小 11 | */ 12 | export function iconifyRender(icon: string, color?: string, fontSize?: number) { 13 | const style: { color?: string; fontSize?: string } = {}; 14 | if (color) { 15 | style.color = color; 16 | } 17 | if (fontSize) { 18 | style.fontSize = `${fontSize}px`; 19 | } 20 | return () => h(NIcon, null, { default: () => h(Icon, { icon, style }) }); 21 | } 22 | 23 | /** 24 | * 动态渲染自定义图标 25 | * @param icon - 图标名称 26 | * @param color - 图标颜色 27 | * @param fontSize - 图标大小 28 | */ 29 | export function customIconRender(icon: string, color?: string, fontSize?: number) { 30 | const style: { color?: string; fontSize?: string } = {}; 31 | if (color) { 32 | style.color = color; 33 | } 34 | if (fontSize) { 35 | style.fontSize = `${fontSize}px`; 36 | } 37 | 38 | return () => h(NIcon, null, { default: () => h(SvgIcon, { icon, style }) }); 39 | } 40 | -------------------------------------------------------------------------------- /ui/src/utils/common/index.ts: -------------------------------------------------------------------------------- 1 | export * from './typeof'; 2 | export * from './color'; 3 | export * from './number'; 4 | export * from './object'; 5 | export * from './icon'; 6 | export * from './pattern'; 7 | export * from './theme'; 8 | export * from './contextmenu'; 9 | -------------------------------------------------------------------------------- /ui/src/utils/common/number.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 根据数字获取对应的汉字 3 | * @param num - 数字(0-10) 4 | */ 5 | export function getHanByNumber(num: number) { 6 | const HAN_STR = '零一二三四五六七八九十'; 7 | return HAN_STR.charAt(num); 8 | } 9 | 10 | /** 11 | * 将总秒数转换成 分:秒 12 | * @param seconds - 秒 13 | */ 14 | export function transformToTimeCountDown(seconds: number) { 15 | const SECONDS_A_MINUTE = 60; 16 | function fillZero(num: number) { 17 | return num.toString().padStart(2, '0'); 18 | } 19 | const minuteNum = Math.floor(seconds / SECONDS_A_MINUTE); 20 | const minute = fillZero(minuteNum); 21 | const second = fillZero(seconds - minuteNum * SECONDS_A_MINUTE); 22 | return `${minute}: ${second}`; 23 | } 24 | 25 | /** 26 | * 获取指定整数范围内的随机整数 27 | * @param start - 开始范围 28 | * @param end - 结束范围 29 | */ 30 | export function getRandomInteger(end: number, start = 0) { 31 | const range = end - start; 32 | const random = Math.floor(Math.random() * range + start); 33 | return random; 34 | } 35 | -------------------------------------------------------------------------------- /ui/src/utils/common/object.ts: -------------------------------------------------------------------------------- 1 | /** 设置对象数据 */ 2 | export function objectAssign>(target: T, source: Partial) { 3 | Object.assign(target, source); 4 | } 5 | -------------------------------------------------------------------------------- /ui/src/utils/common/pattern.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 策略模式 3 | * @param actions 每一种可能执行的操作 4 | */ 5 | export function exeStrategyActions(actions: Common.StrategyAction[]) { 6 | actions.some(item => { 7 | const [flag, action] = item; 8 | if (flag) { 9 | action(); 10 | } 11 | return flag; 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /ui/src/utils/common/theme.ts: -------------------------------------------------------------------------------- 1 | import { EnumStorageKey } from '@/enum'; 2 | 3 | /** 4 | * 缓存主题颜色 5 | * @param color 6 | */ 7 | export function setThemeColor(color: string) { 8 | window.localStorage.setItem(EnumStorageKey['theme-color'], color); 9 | } 10 | 11 | /** 12 | * 获取缓存的主题颜色 13 | */ 14 | export function getThemeColor() { 15 | return window.localStorage.getItem(EnumStorageKey['theme-color']); 16 | } 17 | -------------------------------------------------------------------------------- /ui/src/utils/crypto/index.ts: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js'; 2 | 3 | const CryptoSecret = '__CryptoJS_Secret__'; 4 | 5 | /** 6 | * 加密数据 7 | * @param data - 数据 8 | */ 9 | export function encrypto(data: any) { 10 | const newData = JSON.stringify(data); 11 | return CryptoJS.AES.encrypt(newData, CryptoSecret).toString(); 12 | } 13 | 14 | /** 15 | * 解密数据 16 | * @param cipherText - 密文 17 | */ 18 | export function decrypto(cipherText: string) { 19 | const bytes = CryptoJS.AES.decrypt(cipherText, CryptoSecret); 20 | const originalText = bytes.toString(CryptoJS.enc.Utf8); 21 | if (originalText) { 22 | return JSON.parse(originalText); 23 | } 24 | return null; 25 | } 26 | -------------------------------------------------------------------------------- /ui/src/utils/form/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rule'; 2 | -------------------------------------------------------------------------------- /ui/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './common'; 2 | export * from './storage'; 3 | export * from './service'; 4 | export * from './auth'; 5 | export * from './router'; 6 | export * from './form'; 7 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 8 | // @ts-ignore 9 | export * from './shell'; 10 | -------------------------------------------------------------------------------- /ui/src/utils/router/auth.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 根据用户权限过滤路由 3 | * @param routes - 权限路由 4 | * @param permission - 权限 5 | */ 6 | export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], permission: Auth.RoleType) { 7 | return routes.map(route => filterAuthRouteByUserPermission(route, permission)).flat(1); 8 | } 9 | 10 | /** 11 | * 根据用户权限过滤单个路由 12 | * @param route - 单个权限路由 13 | * @param permission - 权限 14 | */ 15 | function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Auth.RoleType): AuthRoute.Route[] { 16 | const filterRoute = { ...route }; 17 | const hasPermission = 18 | !route.meta.permissions || permission === 'super' || route.meta.permissions.includes(permission); 19 | 20 | if (filterRoute.children) { 21 | const filterChildren = filterRoute.children.map(item => filterAuthRouteByUserPermission(item, permission)).flat(1); 22 | Object.assign(filterRoute, { children: filterChildren }); 23 | } 24 | return hasPermission ? [filterRoute] : []; 25 | } 26 | -------------------------------------------------------------------------------- /ui/src/utils/router/cache.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router'; 2 | 3 | /** 4 | * 获取缓存的路由对应组件的名称 5 | * @param routes - 转换后的vue路由 6 | */ 7 | export function getCacheRoutes(routes: RouteRecordRaw[]) { 8 | const cacheNames: string[] = []; 9 | debugger; 10 | routes.forEach(route => { 11 | // 只需要获取二级路由的缓存的组件名 12 | if (hasChildren(route)) { 13 | (route.children as RouteRecordRaw[]).forEach(item => { 14 | if (isKeepAlive(item)) { 15 | cacheNames.push(item.name as string); 16 | } 17 | }); 18 | } 19 | }); 20 | return cacheNames; 21 | } 22 | 23 | /** 24 | * 路由是否缓存 25 | * @param route 26 | */ 27 | function isKeepAlive(route: RouteRecordRaw) { 28 | return Boolean(route?.meta?.keepAlive); 29 | } 30 | /** 31 | * 是否有二级路由 32 | * @param route 33 | */ 34 | function hasChildren(route: RouteRecordRaw) { 35 | return Boolean(route.children && route.children.length); 36 | } 37 | -------------------------------------------------------------------------------- /ui/src/utils/router/component.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'vue'; 2 | import { BasicLayout, BlankLayout } from '@/layouts'; 3 | import { views } from '@/views'; 4 | 5 | type LayoutComponent = Record Promise>; 6 | 7 | /** 8 | * 获取页面导入的vue文件(懒加载的方式) 9 | * @param layoutType - 布局类型 10 | */ 11 | export function getLayoutComponent(layoutType: EnumType.LayoutComponentName) { 12 | const layoutComponent: LayoutComponent = { 13 | basic: BasicLayout, 14 | blank: BlankLayout 15 | }; 16 | return layoutComponent[layoutType]; 17 | } 18 | 19 | /** 20 | * 获取页面导入的vue文件(懒加载的方式) 21 | * @param routeKey - 路由key 22 | */ 23 | export function getViewComponent(routeKey: AuthRoute.RouteKey) { 24 | if (!views[routeKey]) { 25 | window.console.error(`路由“${routeKey}”没有对应的组件文件!`); 26 | } 27 | return () => setViewComponentName(views[routeKey], routeKey) as Promise; 28 | } 29 | 30 | /** 给页面组件设置名称 */ 31 | async function setViewComponentName(asyncComponent: () => Promise, name: string) { 32 | const component = (await asyncComponent()) as { default: Component }; 33 | Object.assign(component.default, { name }); 34 | return component; 35 | } 36 | -------------------------------------------------------------------------------- /ui/src/utils/router/index.ts: -------------------------------------------------------------------------------- 1 | export * from './module'; 2 | export * from './helpers'; 3 | export * from './cache'; 4 | export * from './auth'; 5 | export * from './menu'; 6 | export * from './breadcrumb'; 7 | export * from './regexp'; 8 | export * from './urlParams'; 9 | -------------------------------------------------------------------------------- /ui/src/utils/router/module.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 权限路由排序 3 | * @param routes - 权限路由 4 | */ 5 | function sortRoutes(routes: AuthRoute.Route[]) { 6 | return routes.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order)); 7 | } 8 | 9 | /** 10 | * 处理全部导入的路由模块 11 | * @param modules - 路由模块 12 | */ 13 | export function handleModuleRoutes(modules: AuthRoute.RouteModule) { 14 | const routes: AuthRoute.Route[] = []; 15 | 16 | Object.keys(modules).forEach(key => { 17 | const item = modules[key].default; 18 | if (item) { 19 | routes.push(item); 20 | } else { 21 | window.console.error(`路由模块解析出错: key = ${key}`); 22 | } 23 | }); 24 | 25 | return sortRoutes(routes); 26 | } 27 | -------------------------------------------------------------------------------- /ui/src/utils/router/regexp.ts: -------------------------------------------------------------------------------- 1 | /** 获取登录页面模块的动态路由的正则 */ 2 | export function getLoginModuleRegExp() { 3 | const modules: EnumType.LoginModuleKey[] = ['pwd-login', 'code-login', 'register', 'reset-pwd', 'bind-wechat']; 4 | return modules.join('|'); 5 | } 6 | -------------------------------------------------------------------------------- /ui/src/utils/router/urlParams.ts: -------------------------------------------------------------------------------- 1 | export function getUrlParams(url:string) { 2 | // 通过 ? 分割获取后面的参数字符串 3 | const urlStr = url.split('?')[1]; 4 | // 创建空对象存储参数 5 | const obj:any = {}; 6 | // 再通过 & 将每一个参数单独分割出来 7 | const paramsArr = urlStr.split('&'); 8 | for (let i = 0, len = paramsArr.length; i < len; i++) { 9 | // 再通过 = 将每一个参数分割为 key:value 的形式 10 | const arr = paramsArr[i].split('='); 11 | // eslint-disable-next-line prefer-destructuring 12 | obj[arr[0]] = arr[1]; 13 | } 14 | return obj; 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/utils/service/handler.ts: -------------------------------------------------------------------------------- 1 | /** 统一失败和成功的请求结果的数据类型 */ 2 | export async function handleServiceResult(error: Service.RequestError | null, data: any) { 3 | if (error) { 4 | const fail: Service.FailedResult = { 5 | error, 6 | data: null 7 | }; 8 | return fail; 9 | } 10 | const success: Service.SuccessResult = { 11 | error: null, 12 | data 13 | }; 14 | return success; 15 | } 16 | 17 | /** 请求结果的适配器:用于接收适配器函数和请求结果 */ 18 | export function adapter( 19 | adapterFun: T, 20 | ...args: Service.MultiRequestResult> 21 | ): Service.RequestResult> { 22 | let result: Service.RequestResult | undefined; 23 | 24 | const hasError = args.some(item => { 25 | const flag = Boolean(item.error); 26 | if (flag) { 27 | result = { 28 | error: item.error, 29 | data: null 30 | }; 31 | } 32 | return flag; 33 | }); 34 | 35 | if (!hasError) { 36 | const adapterFunArgs = args.map(item => item.data); 37 | result = { 38 | error: null, 39 | data: adapterFun(...adapterFunArgs) 40 | }; 41 | } 42 | 43 | return result!; 44 | } 45 | -------------------------------------------------------------------------------- /ui/src/utils/service/index.ts: -------------------------------------------------------------------------------- 1 | export * from './transform'; 2 | export * from './error'; 3 | export * from './handler'; 4 | -------------------------------------------------------------------------------- /ui/src/utils/service/msg.ts: -------------------------------------------------------------------------------- 1 | import { NO_ERROR_MSG_CODE, ERROR_MSG_DURATION } from '@/config'; 2 | 3 | /** 错误消息栈,防止同一错误同时出现 */ 4 | const errorMsgStack = new Map([]); 5 | 6 | function addErrorMsg(error: Service.RequestError) { 7 | errorMsgStack.set(error.code, error.msg); 8 | } 9 | function removeErrorMsg(error: Service.RequestError) { 10 | errorMsgStack.delete(error.code); 11 | } 12 | function hasErrorMsg(error: Service.RequestError) { 13 | return errorMsgStack.has(error.code); 14 | } 15 | 16 | /** 17 | * 显示错误信息 18 | * @param error 19 | */ 20 | export function showErrorMsg(error: Service.RequestError) { 21 | if (!error.msg || NO_ERROR_MSG_CODE.includes(error.code) || hasErrorMsg(error)) return; 22 | 23 | addErrorMsg(error); 24 | window.console.warn(error.code, error.msg); 25 | window.$message?.error(error.msg, { duration: ERROR_MSG_DURATION }); 26 | setTimeout(() => { 27 | removeErrorMsg(error); 28 | }, ERROR_MSG_DURATION); 29 | } 30 | -------------------------------------------------------------------------------- /ui/src/utils/shell/index.ts: -------------------------------------------------------------------------------- 1 | export * from './webSocket'; 2 | 3 | -------------------------------------------------------------------------------- /ui/src/utils/shell/webSocket.ts: -------------------------------------------------------------------------------- 1 | export class WebSocketClient { 2 | path: string; 3 | 4 | socket: WebSocket | undefined; 5 | 6 | constructor(path: string) { 7 | this.path = path; 8 | } 9 | 10 | // eslint-disable-next-line class-methods-use-this 11 | getWebSocketUrl() { 12 | const protocol = 'ws://'; 13 | /* if (window.location.protocol === 'https:') { 14 | protocol = 'wss://'; 15 | } */ 16 | return `${protocol}/127.0.0.1:10218/${this.path}`; 17 | } 18 | 19 | connect(params: { 20 | onError: (arg0: string) => void; 21 | onConnect: () => void; 22 | onData: (arg0: any) => void; 23 | onClose: () => void; 24 | }) { 25 | this.socket = new WebSocket(this.getWebSocketUrl()); 26 | 27 | this.socket.onopen = () => { 28 | params.onConnect(); 29 | }; 30 | 31 | this.socket.onmessage = evt => { 32 | const data = evt.data.toString(); 33 | params.onData(data); 34 | }; 35 | 36 | this.socket.onclose = () => { 37 | params.onClose(); 38 | }; 39 | } 40 | 41 | sendMessage(message: string) { 42 | this.socket?.send(message); 43 | } 44 | 45 | sendJsonMessage(message: any) { 46 | this.sendMessage(JSON.stringify(message)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ui/src/utils/storage/index.ts: -------------------------------------------------------------------------------- 1 | export * from './local'; 2 | export * from './session'; 3 | -------------------------------------------------------------------------------- /ui/src/utils/storage/session.ts: -------------------------------------------------------------------------------- 1 | import { encrypto, decrypto } from '../crypto'; 2 | 3 | export function setSession(key: string, value: unknown) { 4 | const json = encrypto(value); 5 | sessionStorage.setItem(key, json); 6 | } 7 | 8 | export function getSession(key: string) { 9 | const json = sessionStorage.getItem(key); 10 | let data: T | null = null; 11 | if (json) { 12 | try { 13 | data = decrypto(json); 14 | } catch { 15 | // 防止解析失败 16 | } 17 | } 18 | return data; 19 | } 20 | 21 | export function removeSession(key: string) { 22 | window.sessionStorage.removeItem(key); 23 | } 24 | 25 | export function clearSession() { 26 | window.sessionStorage.clear(); 27 | } 28 | -------------------------------------------------------------------------------- /ui/src/views/index.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'vue'; 2 | 3 | type ViewComponent = Record Promise>; 4 | 5 | const importViews = import.meta.glob('./**/index.vue') as ViewComponent; 6 | 7 | const COMPONENTS_KEY = 'components'; 8 | const PREFIX = './'; 9 | const SUFFIX = '/index.vue'; 10 | const PATH_SPLIT_MARK = '/'; 11 | const ROUTE_KEY_SPLIT_MARK = '_'; 12 | /** 系统的内置路由,该文件夹名称不作为RouteKey */ 13 | const SYSTEM_VIEW = 'system-view_'; 14 | 15 | /** 过滤掉组件文件 */ 16 | const viewKeys = Object.keys(importViews).filter(key => !key.includes(COMPONENTS_KEY)); 17 | 18 | function getViewComponent() { 19 | const components: ViewComponent = {}; 20 | viewKeys.forEach(key => { 21 | const routeKey = key 22 | .replace(PREFIX, '') 23 | .replace(SUFFIX, '') 24 | .replace(new RegExp(PATH_SPLIT_MARK, 'g'), ROUTE_KEY_SPLIT_MARK) 25 | .replace(SYSTEM_VIEW, ''); 26 | components[routeKey] = importViews[key]; 27 | }); 28 | return components; 29 | } 30 | 31 | export const views = getViewComponent(); 32 | -------------------------------------------------------------------------------- /ui/src/views/project/setting/components/SettingMenu/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ui/src/views/project/setting/components/ThemeColorSelect/components/ColorCheckbox.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ui/src/views/project/setting/components/ThemeColorSelect/components/index.ts: -------------------------------------------------------------------------------- 1 | import ColorCheckbox from './ColorCheckbox.vue'; 2 | import ColorModal from './ColorModal.vue'; 3 | 4 | export { ColorCheckbox, ColorModal }; 5 | -------------------------------------------------------------------------------- /ui/src/views/project/setting/components/index.ts: -------------------------------------------------------------------------------- 1 | import DarkMode from './DarkMode/index.vue'; 2 | import ThemeColorSelect from './ThemeColorSelect/index.vue'; 3 | import Appearance from './Appearance/index.vue'; 4 | import Terminal from './Terminal/index.vue'; 5 | 6 | export { DarkMode, ThemeColorSelect ,Appearance,Terminal }; 7 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/LoginBg/components/index.ts: -------------------------------------------------------------------------------- 1 | import CornerTop from './CornerTop.vue'; 2 | import CornerBottom from './CornerBottom.vue'; 3 | 4 | export { CornerTop, CornerBottom }; 5 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/LoginBg/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/PwdLogin/components/OtherAccount.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/PwdLogin/components/OtherLogin.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/PwdLogin/components/index.ts: -------------------------------------------------------------------------------- 1 | import OtherLogin from './OtherLogin.vue'; 2 | import OtherAccount from './OtherAccount.vue'; 3 | 4 | export { OtherLogin, OtherAccount }; 5 | -------------------------------------------------------------------------------- /ui/src/views/system-view/login/components/index.ts: -------------------------------------------------------------------------------- 1 | import LoginBg from './LoginBg/index.vue'; 2 | import PwdLogin from './PwdLogin/index.vue'; 3 | import CodeLogin from './CodeLogin/index.vue'; 4 | import Register from './Register/index.vue'; 5 | import ResetPwd from './ResetPwd/index.vue'; 6 | import BindWechat from './BindWechat/index.vue'; 7 | 8 | export { LoginBg, PwdLogin, CodeLogin, Register, ResetPwd, BindWechat }; 9 | -------------------------------------------------------------------------------- /ui/src/views/system-view/no-permission/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/src/views/system-view/not-found-page/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/src/views/system-view/not-found/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/src/views/system-view/service-error/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "lib": ["DOM", "ESNext"], 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "jsx": "preserve", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "noUnusedLocals": true, 14 | "strictNullChecks": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "paths": { 17 | "~/*": ["./*"], 18 | "@/*": ["./src/*"] 19 | }, 20 | "types": ["node", "naive-ui/volar"] 21 | }, 22 | "exclude": ["node_modules", "dist"] 23 | } 24 | --------------------------------------------------------------------------------