├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── feature_request.yml
└── workflows
│ ├── build-windows.yml
│ ├── build.yml
│ ├── crowdin.yml
│ ├── release.yml
│ └── sync-branch.yml
├── .gitignore
├── .husky
├── commit-msg
├── common.sh
└── pre-commit
├── .npmrc
├── .prettierignore
├── .stylelintignore
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
├── tasks.json
└── ts.code-snippets
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api
└── unit.js
├── commitlint.config.js
├── crowdin.yml
├── donkey.config.js
├── package.json
├── patches
├── monaco-editor+0.36.1.patch
└── windowsCodeSign.js
├── prettier.config.js
├── scripts
├── afterBuild.js
├── afterInstall.js
├── afterPackHook.js
├── baseConfig.ts
├── beforeNSISBuild.js
├── build.ts
├── buildNoSign.ts
├── deployWindows.js
├── entitlements.mac.plist
├── notarize.js
├── publish.js
├── releaseWindows.ts
├── tools
│ ├── debugComponents.js
│ ├── getVSCodeThemes.js
│ ├── replaceCssVariable.js
│ ├── unlinkComponents.js
│ └── upgradeComponent.js
├── upload.js
└── urlProtoco.nsh
├── src
├── app
│ ├── common
│ │ └── images
│ │ │ ├── 1024x1024.png
│ │ │ ├── 128x128.png
│ │ │ ├── 16x16.png
│ │ │ ├── 24x24.png
│ │ │ ├── 256x256.png
│ │ │ ├── 32x32.png
│ │ │ ├── 48x48.png
│ │ │ ├── 512x512.png
│ │ │ ├── 64x64.png
│ │ │ ├── icon.icns
│ │ │ ├── logo.ico
│ │ │ └── postcat.bmp
│ ├── electron-main
│ │ ├── language.service.ts
│ │ ├── main.ts
│ │ └── updater.ts
│ └── typings.d.ts
├── browser
│ ├── .editorconfig
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .eslintrc.json
│ ├── .gitignore
│ ├── .npmrc
│ ├── .vscode
│ │ ├── extensions.json
│ │ ├── launch.json
│ │ └── tasks.json
│ ├── LICENSE
│ ├── README.md
│ ├── angular.json
│ ├── angular.webpack.js
│ ├── build
│ │ └── build.js
│ ├── locale
│ │ ├── messages.xlf
│ │ └── messages.zh.xlf
│ ├── markdown-loader.js
│ ├── package.json
│ ├── proxy.conf.json
│ ├── src
│ │ ├── app
│ │ │ ├── app-routing.module.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── components
│ │ │ │ ├── chat-robot
│ │ │ │ │ ├── chat-robot-container
│ │ │ │ │ │ ├── chat-robot.component.scss
│ │ │ │ │ │ └── chat-robot.component.ts
│ │ │ │ │ ├── chat-robot-form
│ │ │ │ │ │ └── chat-robot-form.component.ts
│ │ │ │ │ ├── chat-robot-message
│ │ │ │ │ │ ├── chat-robot-message.component.scss
│ │ │ │ │ │ └── chat-robot-message.component.ts
│ │ │ │ │ ├── chat-robot.module.ts
│ │ │ │ │ └── chat-robot.service.ts
│ │ │ │ ├── download-client
│ │ │ │ │ └── download-client.component.ts
│ │ │ │ ├── eo-ui
│ │ │ │ │ ├── NOTE.md
│ │ │ │ │ ├── collapse
│ │ │ │ │ │ ├── collapse-panel.component.ts
│ │ │ │ │ │ ├── collapse.component.scss
│ │ │ │ │ │ ├── collapse.component.ts
│ │ │ │ │ │ └── collapse.module.ts
│ │ │ │ │ ├── iconpark-icon
│ │ │ │ │ │ ├── eo-iconpark-icon.component.ts
│ │ │ │ │ │ └── eo-iconpark-icon.module.ts
│ │ │ │ │ ├── monaco-editor
│ │ │ │ │ │ ├── defaultCompletions.ts
│ │ │ │ │ │ ├── monaco-editor.component.html
│ │ │ │ │ │ ├── monaco-editor.component.scss
│ │ │ │ │ │ ├── monaco-editor.component.ts
│ │ │ │ │ │ ├── monaco.module.ts
│ │ │ │ │ │ └── postmanSnippets.ts
│ │ │ │ │ ├── setting
│ │ │ │ │ │ ├── setting.component.scss
│ │ │ │ │ │ ├── setting.component.ts
│ │ │ │ │ │ └── setting.module.ts
│ │ │ │ │ ├── shadow
│ │ │ │ │ │ ├── eo-shadow-dom.component.scss
│ │ │ │ │ │ ├── shadow-dom-encapsulation.component.ts
│ │ │ │ │ │ └── shadow-dom-encapsulation.module.ts
│ │ │ │ │ ├── tab
│ │ │ │ │ │ ├── tab-operate.service.ts
│ │ │ │ │ │ ├── tab-storage.service.ts
│ │ │ │ │ │ ├── tab.component.html
│ │ │ │ │ │ ├── tab.component.scss
│ │ │ │ │ │ ├── tab.component.ts
│ │ │ │ │ │ ├── tab.model.ts
│ │ │ │ │ │ └── tab.module.ts
│ │ │ │ │ └── table-pro
│ │ │ │ │ │ ├── table-pro.component.html
│ │ │ │ │ │ ├── table-pro.component.scss
│ │ │ │ │ │ ├── table-pro.component.ts
│ │ │ │ │ │ ├── table-pro.model.ts
│ │ │ │ │ │ ├── table-pro.module.ts
│ │ │ │ │ │ └── table-pro.token.ts
│ │ │ │ ├── extension-select
│ │ │ │ │ ├── export-api
│ │ │ │ │ │ └── export-api.component.ts
│ │ │ │ │ ├── extension-select.module.ts
│ │ │ │ │ ├── import-api
│ │ │ │ │ │ ├── import-api.component.ts
│ │ │ │ │ │ └── old2new.ts
│ │ │ │ │ ├── push-api
│ │ │ │ │ │ └── push-api.component.ts
│ │ │ │ │ ├── select
│ │ │ │ │ │ ├── extension-select.component.html
│ │ │ │ │ │ ├── extension-select.component.scss
│ │ │ │ │ │ └── extension-select.component.ts
│ │ │ │ │ └── sync-api
│ │ │ │ │ │ ├── schema.ts
│ │ │ │ │ │ └── sync-api.component.ts
│ │ │ │ ├── logo
│ │ │ │ │ ├── logo.component.scss
│ │ │ │ │ ├── logo.component.ts
│ │ │ │ │ └── logo.module.ts
│ │ │ │ ├── member-list
│ │ │ │ │ ├── member-list.component.scss
│ │ │ │ │ ├── member-list.component.ts
│ │ │ │ │ ├── member-list.module.ts
│ │ │ │ │ └── member.service.ts
│ │ │ │ ├── nps-mask
│ │ │ │ │ ├── component
│ │ │ │ │ │ ├── nps-mask.component.scss
│ │ │ │ │ │ └── nps-mask.component.ts
│ │ │ │ │ ├── nps-mask-postion.directive.ts
│ │ │ │ │ └── nps-mask.module.ts
│ │ │ │ ├── pc-console
│ │ │ │ │ ├── debug-theme
│ │ │ │ │ │ └── debug-theme.component.ts
│ │ │ │ │ ├── pc-console.module.ts
│ │ │ │ │ └── pc-console
│ │ │ │ │ │ ├── pc-console.component.scss
│ │ │ │ │ │ └── pc-console.component.ts
│ │ │ │ ├── star-motivation
│ │ │ │ │ └── star-motivation.component.ts
│ │ │ │ └── system-setting
│ │ │ │ │ ├── common
│ │ │ │ │ ├── about.component.ts
│ │ │ │ │ ├── account.component.ts
│ │ │ │ │ ├── data-storage.component.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── language-swtcher.component.ts
│ │ │ │ │ ├── select-theme
│ │ │ │ │ │ ├── select-theme.component.scss
│ │ │ │ │ │ └── select-theme.component.ts
│ │ │ │ │ ├── token.component.scss
│ │ │ │ │ └── token.component.ts
│ │ │ │ │ ├── settings.service.ts
│ │ │ │ │ ├── system-setting.component.scss
│ │ │ │ │ ├── system-setting.component.ts
│ │ │ │ │ └── system-setting.module.ts
│ │ │ ├── core
│ │ │ │ ├── README.md
│ │ │ │ ├── core.module.ts
│ │ │ │ └── services
│ │ │ │ │ ├── electron
│ │ │ │ │ └── electron.service.ts
│ │ │ │ │ ├── errorHandle.service.ts
│ │ │ │ │ ├── feature-control
│ │ │ │ │ ├── feature-control.service.ts
│ │ │ │ │ └── feature.json
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── language
│ │ │ │ │ ├── language.model.ts
│ │ │ │ │ └── language.service.ts
│ │ │ │ │ ├── notification.service.ts
│ │ │ │ │ ├── theme
│ │ │ │ │ ├── theme-colors.json
│ │ │ │ │ ├── theme-extension.service.ts
│ │ │ │ │ ├── theme-variable.service.ts
│ │ │ │ │ ├── theme.constant.ts
│ │ │ │ │ ├── theme.model.ts
│ │ │ │ │ └── theme.service.ts
│ │ │ │ │ └── web
│ │ │ │ │ └── web.service.ts
│ │ │ ├── layouts
│ │ │ │ ├── local-workspace-tip
│ │ │ │ │ ├── local-workspace-tip.component.scss
│ │ │ │ │ └── local-workspace-tip.component.ts
│ │ │ │ ├── navbar
│ │ │ │ │ ├── breadcrumb
│ │ │ │ │ │ ├── nav-breadcrumb.component.html
│ │ │ │ │ │ ├── nav-breadcrumb.component.scss
│ │ │ │ │ │ ├── nav-breadcrumb.component.ts
│ │ │ │ │ │ └── select-workspace
│ │ │ │ │ │ │ ├── select-workspace.component.scss
│ │ │ │ │ │ │ └── select-workspace.component.ts
│ │ │ │ │ ├── btn-user
│ │ │ │ │ │ └── btn-user.component.ts
│ │ │ │ │ ├── get-share-link.component.ts
│ │ │ │ │ ├── help-dropdown
│ │ │ │ │ │ └── help-dropdown.component.ts
│ │ │ │ │ ├── nav-operate.component.ts
│ │ │ │ │ ├── navbar.component.html
│ │ │ │ │ ├── navbar.component.scss
│ │ │ │ │ ├── navbar.component.ts
│ │ │ │ │ ├── navbar.module.ts
│ │ │ │ │ └── share-navbar
│ │ │ │ │ │ └── share-navbar.component.ts
│ │ │ │ ├── page-not-found
│ │ │ │ │ ├── page-not-find.module.ts
│ │ │ │ │ ├── page-not-found.component.html
│ │ │ │ │ ├── page-not-found.component.scss
│ │ │ │ │ └── page-not-found.component.ts
│ │ │ │ ├── sidebar
│ │ │ │ │ ├── sidebar.component.html
│ │ │ │ │ ├── sidebar.component.scss
│ │ │ │ │ ├── sidebar.component.ts
│ │ │ │ │ ├── sidebar.model.ts
│ │ │ │ │ └── sidebar.service.ts
│ │ │ │ └── toolbar
│ │ │ │ │ ├── toolbar.component.html
│ │ │ │ │ ├── toolbar.component.scss
│ │ │ │ │ ├── toolbar.component.ts
│ │ │ │ │ └── toolbar.module.ts
│ │ │ ├── pages
│ │ │ │ ├── components
│ │ │ │ │ ├── ai-input-group
│ │ │ │ │ │ ├── ai-input-group.component.html
│ │ │ │ │ │ ├── ai-input-group.component.scss
│ │ │ │ │ │ └── ai-input-group.component.ts
│ │ │ │ │ ├── chatgpt-robot
│ │ │ │ │ │ ├── chatgpt-robot.component.scss
│ │ │ │ │ │ └── chatgpt-robot.component.ts
│ │ │ │ │ ├── extension
│ │ │ │ │ │ ├── detail
│ │ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ │ └── extensions-settings.component.ts
│ │ │ │ │ │ │ ├── extension-detail.component.html
│ │ │ │ │ │ │ ├── extension-detail.component.scss
│ │ │ │ │ │ │ └── extension-detail.component.ts
│ │ │ │ │ │ ├── download-count-formater.pipe.ts
│ │ │ │ │ │ ├── extension-routing.module.ts
│ │ │ │ │ │ ├── extension.component.html
│ │ │ │ │ │ ├── extension.component.scss
│ │ │ │ │ │ ├── extension.component.ts
│ │ │ │ │ │ ├── extension.model.ts
│ │ │ │ │ │ ├── extension.module.ts
│ │ │ │ │ │ ├── list
│ │ │ │ │ │ │ ├── extension-list.component.html
│ │ │ │ │ │ │ ├── extension-list.component.scss
│ │ │ │ │ │ │ └── extension-list.component.ts
│ │ │ │ │ │ └── socket.service.ts
│ │ │ │ │ ├── model-article
│ │ │ │ │ │ ├── newbie-guide
│ │ │ │ │ │ │ ├── newPeopleGuide.md
│ │ │ │ │ │ │ ├── newbie-guide.component.scss
│ │ │ │ │ │ │ ├── newbie-guide.component.spec.ts
│ │ │ │ │ │ │ └── newbie-guide.component.ts
│ │ │ │ │ │ └── update-log
│ │ │ │ │ │ │ ├── update-log.component.scss
│ │ │ │ │ │ │ ├── update-log.component.spec.ts
│ │ │ │ │ │ │ ├── update-log.component.ts
│ │ │ │ │ │ │ └── update-log.md
│ │ │ │ │ ├── third-login
│ │ │ │ │ │ ├── third-login.component.scss
│ │ │ │ │ │ └── third-login.component.ts
│ │ │ │ │ └── user-modal
│ │ │ │ │ │ └── user-modal.component.ts
│ │ │ │ ├── modules
│ │ │ │ │ └── ai-to-api
│ │ │ │ │ │ ├── ai-to-api.component.html
│ │ │ │ │ │ ├── ai-to-api.component.scss
│ │ │ │ │ │ ├── ai-to-api.component.spec.ts
│ │ │ │ │ │ ├── ai-to-api.component.ts
│ │ │ │ │ │ ├── ai-to-api.module.ts
│ │ │ │ │ │ ├── ai-to-api.service.ts
│ │ │ │ │ │ └── example-yaml
│ │ │ │ │ │ ├── en-yaml.ts
│ │ │ │ │ │ └── zh-yaml.ts
│ │ │ │ ├── pages-routing.module.ts
│ │ │ │ ├── pages.component.html
│ │ │ │ ├── pages.component.scss
│ │ │ │ ├── pages.component.ts
│ │ │ │ ├── pages.module.ts
│ │ │ │ ├── services
│ │ │ │ │ └── redirect.services.ts
│ │ │ │ ├── share-project
│ │ │ │ │ ├── share-project.module.ts
│ │ │ │ │ ├── share-routing.module.ts
│ │ │ │ │ └── view
│ │ │ │ │ │ ├── share-project.component.scss
│ │ │ │ │ │ └── share-project.component.ts
│ │ │ │ └── workspace
│ │ │ │ │ ├── overview
│ │ │ │ │ ├── edit
│ │ │ │ │ │ └── workspace-edit.component.ts
│ │ │ │ │ ├── member
│ │ │ │ │ │ ├── workspace-member.component.ts
│ │ │ │ │ │ └── workspace-member.service.ts
│ │ │ │ │ ├── project-list
│ │ │ │ │ │ ├── project-list.component.html
│ │ │ │ │ │ ├── project-list.component.scss
│ │ │ │ │ │ ├── project-list.component.ts
│ │ │ │ │ │ ├── project-list.module.ts
│ │ │ │ │ │ └── project-list.service.ts
│ │ │ │ │ ├── workspace-overview.component.html
│ │ │ │ │ ├── workspace-overview.component.scss
│ │ │ │ │ ├── workspace-overview.component.ts
│ │ │ │ │ └── workspace-overview.module.ts
│ │ │ │ │ ├── project
│ │ │ │ │ ├── api
│ │ │ │ │ │ ├── api-routing.module.ts
│ │ │ │ │ │ ├── api-shared.module.ts
│ │ │ │ │ │ ├── api-tab.service.ts
│ │ │ │ │ │ ├── api.component.html
│ │ │ │ │ │ ├── api.component.scss
│ │ │ │ │ │ ├── api.component.ts
│ │ │ │ │ │ ├── api.module.ts
│ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ ├── action
│ │ │ │ │ │ │ │ ├── action.component.html
│ │ │ │ │ │ │ │ ├── action.component.scss
│ │ │ │ │ │ │ │ ├── action.component.spec.ts
│ │ │ │ │ │ │ │ └── action.component.ts
│ │ │ │ │ │ │ ├── api-mock-table.component.ts
│ │ │ │ │ │ │ ├── api-script
│ │ │ │ │ │ │ │ ├── api-script.component.html
│ │ │ │ │ │ │ │ ├── api-script.component.scss
│ │ │ │ │ │ │ │ ├── api-script.component.ts
│ │ │ │ │ │ │ │ └── constant.ts
│ │ │ │ │ │ │ ├── api-test-form
│ │ │ │ │ │ │ │ └── api-test-form.component.ts
│ │ │ │ │ │ │ ├── api-test-result-header
│ │ │ │ │ │ │ │ ├── api-test-result-header.component.html
│ │ │ │ │ │ │ │ ├── api-test-result-header.component.scss
│ │ │ │ │ │ │ │ └── api-test-result-header.component.ts
│ │ │ │ │ │ │ ├── authorization-extension-form
│ │ │ │ │ │ │ │ └── authorization-extension-form.component.ts
│ │ │ │ │ │ │ ├── group
│ │ │ │ │ │ │ │ ├── api-group-tree.component.html
│ │ │ │ │ │ │ │ ├── api-group-tree.component.scss
│ │ │ │ │ │ │ │ ├── api-group-tree.component.ts
│ │ │ │ │ │ │ │ ├── api-group-tree.directive.ts
│ │ │ │ │ │ │ │ └── api-group.service.ts
│ │ │ │ │ │ │ ├── history
│ │ │ │ │ │ │ │ ├── eo-history.component.html
│ │ │ │ │ │ │ │ ├── eo-history.component.scss
│ │ │ │ │ │ │ │ └── eo-history.component.ts
│ │ │ │ │ │ │ ├── params-import
│ │ │ │ │ │ │ │ ├── params-import.component.html
│ │ │ │ │ │ │ │ ├── params-import.component.scss
│ │ │ │ │ │ │ │ └── params-import.component.ts
│ │ │ │ │ │ │ └── response-steps
│ │ │ │ │ │ │ │ ├── response-steps.component.html
│ │ │ │ │ │ │ │ ├── response-steps.component.scss
│ │ │ │ │ │ │ │ ├── response-steps.component.spec.ts
│ │ │ │ │ │ │ │ └── response-steps.component.ts
│ │ │ │ │ │ ├── constants
│ │ │ │ │ │ │ ├── api.model.ts
│ │ │ │ │ │ │ └── auth.model.ts
│ │ │ │ │ │ ├── env
│ │ │ │ │ │ │ ├── env-edit
│ │ │ │ │ │ │ │ ├── env-edit.component.html
│ │ │ │ │ │ │ │ ├── env-edit.component.scss
│ │ │ │ │ │ │ │ └── env-edit.component.ts
│ │ │ │ │ │ │ ├── env-list
│ │ │ │ │ │ │ │ ├── env-list.component.html
│ │ │ │ │ │ │ │ ├── env-list.component.scss
│ │ │ │ │ │ │ │ └── env-list.component.ts
│ │ │ │ │ │ │ ├── env-select
│ │ │ │ │ │ │ │ ├── env-select.component.scss
│ │ │ │ │ │ │ │ └── env-select.component.ts
│ │ │ │ │ │ │ └── env.module.ts
│ │ │ │ │ │ ├── graph-ql
│ │ │ │ │ │ │ ├── graph-ql.module.ts
│ │ │ │ │ │ │ └── test
│ │ │ │ │ │ │ │ ├── graph-ql-test.component.html
│ │ │ │ │ │ │ │ ├── graph-ql-test.component.scss
│ │ │ │ │ │ │ │ └── graph-ql-test.component.ts
│ │ │ │ │ │ ├── group-edit
│ │ │ │ │ │ │ ├── group.component.html
│ │ │ │ │ │ │ ├── group.component.scss
│ │ │ │ │ │ │ ├── group.component.ts
│ │ │ │ │ │ │ └── group.module.ts
│ │ │ │ │ │ ├── grpc
│ │ │ │ │ │ │ ├── grpc-routing.module.ts
│ │ │ │ │ │ │ ├── grpc.component.html
│ │ │ │ │ │ │ ├── grpc.component.scss
│ │ │ │ │ │ │ ├── grpc.component.ts
│ │ │ │ │ │ │ └── grpc.module.ts
│ │ │ │ │ │ ├── http
│ │ │ │ │ │ │ ├── detail
│ │ │ │ │ │ │ │ ├── api-detail.component.html
│ │ │ │ │ │ │ │ ├── api-detail.component.scss
│ │ │ │ │ │ │ │ ├── api-detail.component.ts
│ │ │ │ │ │ │ │ ├── api-detail.module.ts
│ │ │ │ │ │ │ │ ├── api-detail.service.ts
│ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ │ ├── api-detail-body.component.html
│ │ │ │ │ │ │ │ │ └── api-detail-body.component.ts
│ │ │ │ │ │ │ │ ├── eo-api-methods-tag
│ │ │ │ │ │ │ │ │ ├── eo-api-methods-tag.component.scss
│ │ │ │ │ │ │ │ │ └── eo-api-methods-tag.component.ts
│ │ │ │ │ │ │ │ └── form
│ │ │ │ │ │ │ │ │ └── api-detail-form.component.ts
│ │ │ │ │ │ │ ├── edit
│ │ │ │ │ │ │ │ ├── api-edit-util.service.ts
│ │ │ │ │ │ │ │ ├── api-edit.component.html
│ │ │ │ │ │ │ │ ├── api-edit.component.scss
│ │ │ │ │ │ │ │ ├── api-edit.component.ts
│ │ │ │ │ │ │ │ ├── api-edit.module.ts
│ │ │ │ │ │ │ │ ├── api-edit.service.ts
│ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ │ ├── api-edit-body.component.html
│ │ │ │ │ │ │ │ │ ├── api-edit-body.component.scss
│ │ │ │ │ │ │ │ │ └── api-edit-body.component.ts
│ │ │ │ │ │ │ │ ├── extra-setting
│ │ │ │ │ │ │ │ │ ├── api-params-extra-setting.component.html
│ │ │ │ │ │ │ │ │ ├── api-params-extra-setting.component.scss
│ │ │ │ │ │ │ │ │ └── api-params-extra-setting.component.ts
│ │ │ │ │ │ │ │ └── form
│ │ │ │ │ │ │ │ │ └── api-edit-form.component.ts
│ │ │ │ │ │ │ ├── mock
│ │ │ │ │ │ │ │ ├── api-mock.service.ts
│ │ │ │ │ │ │ │ ├── mock.component.html
│ │ │ │ │ │ │ │ ├── mock.component.scss
│ │ │ │ │ │ │ │ ├── mock.component.spec.ts
│ │ │ │ │ │ │ │ ├── mock.component.ts
│ │ │ │ │ │ │ │ └── mock.module.ts
│ │ │ │ │ │ │ └── test
│ │ │ │ │ │ │ │ ├── api-case.service.ts
│ │ │ │ │ │ │ │ ├── api-test-ui.component.html
│ │ │ │ │ │ │ │ ├── api-test-ui.component.scss
│ │ │ │ │ │ │ │ ├── api-test-ui.component.ts
│ │ │ │ │ │ │ │ ├── api-test.component.html
│ │ │ │ │ │ │ │ ├── api-test.component.scss
│ │ │ │ │ │ │ │ ├── api-test.component.ts
│ │ │ │ │ │ │ │ ├── api-test.model.ts
│ │ │ │ │ │ │ │ ├── api-test.module.ts
│ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ ├── api-test-body.component.html
│ │ │ │ │ │ │ │ ├── api-test-body.component.scss
│ │ │ │ │ │ │ │ └── api-test-body.component.ts
│ │ │ │ │ │ │ │ ├── result-request-body
│ │ │ │ │ │ │ │ ├── api-test-result-request-body.component.html
│ │ │ │ │ │ │ │ ├── api-test-result-request-body.component.scss
│ │ │ │ │ │ │ │ └── api-test-result-request-body.component.ts
│ │ │ │ │ │ │ │ ├── result-response
│ │ │ │ │ │ │ │ ├── api-test-result-response.component.html
│ │ │ │ │ │ │ │ ├── api-test-result-response.component.scss
│ │ │ │ │ │ │ │ ├── api-test-result-response.component.ts
│ │ │ │ │ │ │ │ └── get-size.pipe.ts
│ │ │ │ │ │ │ │ └── test-status-bar
│ │ │ │ │ │ │ │ ├── test-status-bar.component.scss
│ │ │ │ │ │ │ │ └── test-status-bar.component.ts
│ │ │ │ │ │ ├── pipe
│ │ │ │ │ │ │ ├── api-formater.pipe.ts
│ │ │ │ │ │ │ └── api-param-num.pipe.ts
│ │ │ │ │ │ ├── service
│ │ │ │ │ │ │ ├── api-table.service.ts
│ │ │ │ │ │ │ ├── api-test-util.service.ts
│ │ │ │ │ │ │ ├── project-api.service.ts
│ │ │ │ │ │ │ └── test-server
│ │ │ │ │ │ │ │ ├── local-node
│ │ │ │ │ │ │ │ ├── api-server-data.model.ts
│ │ │ │ │ │ │ │ └── test-connect.service.ts
│ │ │ │ │ │ │ │ ├── remote-node
│ │ │ │ │ │ │ │ └── test-connect.service.ts
│ │ │ │ │ │ │ │ ├── serverless-node
│ │ │ │ │ │ │ │ └── test-connect.service.ts
│ │ │ │ │ │ │ │ ├── test-server.model.ts
│ │ │ │ │ │ │ │ └── test-server.service.ts
│ │ │ │ │ │ ├── store
│ │ │ │ │ │ │ ├── api-effect.service.ts
│ │ │ │ │ │ │ └── api-state.service.ts
│ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ ├── api.utils.ts
│ │ │ │ │ │ │ └── parse-curl.utils.ts
│ │ │ │ │ │ └── websocket
│ │ │ │ │ │ │ ├── websocket.component.html
│ │ │ │ │ │ │ ├── websocket.component.scss
│ │ │ │ │ │ │ ├── websocket.component.ts
│ │ │ │ │ │ │ ├── websocket.module.ts
│ │ │ │ │ │ │ └── websocket.routing.module.ts
│ │ │ │ │ ├── components
│ │ │ │ │ │ └── operate-project-form.compoent.ts
│ │ │ │ │ ├── member
│ │ │ │ │ │ ├── project-member-routing.module.ts
│ │ │ │ │ │ ├── project-member.component.ts
│ │ │ │ │ │ ├── project-member.module.ts
│ │ │ │ │ │ └── project-member.service.ts
│ │ │ │ │ ├── project-routing.module.ts
│ │ │ │ │ ├── project.module.ts
│ │ │ │ │ └── setting
│ │ │ │ │ │ ├── project-setting.component.html
│ │ │ │ │ │ ├── project-setting.component.scss
│ │ │ │ │ │ ├── project-setting.component.ts
│ │ │ │ │ │ └── project-setting.module.ts
│ │ │ │ │ ├── workspace.component.ts
│ │ │ │ │ └── workspace.module.ts
│ │ │ ├── services
│ │ │ │ ├── data-source
│ │ │ │ │ └── data-source.service.ts
│ │ │ │ ├── extensions
│ │ │ │ │ ├── extension-store.service.ts
│ │ │ │ │ ├── extension.service.ts
│ │ │ │ │ └── webExtension.service.ts
│ │ │ │ ├── globalProvider.ts
│ │ │ │ ├── message
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── message.model.ts
│ │ │ │ │ └── message.service.ts
│ │ │ │ ├── mock.service.ts
│ │ │ │ ├── modal.service.ts
│ │ │ │ ├── storage
│ │ │ │ │ ├── api.service.ts
│ │ │ │ │ ├── api.ts
│ │ │ │ │ ├── db
│ │ │ │ │ │ ├── dataSource
│ │ │ │ │ │ │ ├── convert.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── migration.ts
│ │ │ │ │ │ │ ├── oldApiData.ts
│ │ │ │ │ │ │ └── versions.ts
│ │ │ │ │ │ ├── decorators
│ │ │ │ │ │ │ ├── api-response.decorator.ts
│ │ │ │ │ │ │ └── base-hook.decorator.ts
│ │ │ │ │ │ ├── dto
│ │ │ │ │ │ │ ├── apiCase.dto.ts
│ │ │ │ │ │ │ ├── apiData.dto.ts
│ │ │ │ │ │ │ ├── common.dto.ts
│ │ │ │ │ │ │ ├── group.dto.ts
│ │ │ │ │ │ │ └── project.dto.ts
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── initData
│ │ │ │ │ │ │ └── apiData.ts
│ │ │ │ │ │ ├── models
│ │ │ │ │ │ │ ├── apiData.ts
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── schema
│ │ │ │ │ │ │ ├── apiData.schema.json
│ │ │ │ │ │ │ ├── env.schema.json
│ │ │ │ │ │ │ └── group.schema.json
│ │ │ │ │ │ ├── services
│ │ │ │ │ │ │ ├── apiCase.service.ts
│ │ │ │ │ │ │ ├── apiData.service.ts
│ │ │ │ │ │ │ ├── apiTestHistory.service.ts
│ │ │ │ │ │ │ ├── base.service.ts
│ │ │ │ │ │ │ ├── environment.service.ts
│ │ │ │ │ │ │ ├── group.service.ts
│ │ │ │ │ │ │ ├── mock.service.ts
│ │ │ │ │ │ │ ├── project.service.ts
│ │ │ │ │ │ │ └── workspace.service.ts
│ │ │ │ │ │ ├── tests
│ │ │ │ │ │ │ ├── apiData.test.ts
│ │ │ │ │ │ │ ├── apiGroup.test.ts
│ │ │ │ │ │ │ ├── environment.test.ts
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── project.test.ts
│ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ └── validate
│ │ │ │ │ │ │ └── validate.ts
│ │ │ │ │ ├── http
│ │ │ │ │ │ ├── lib
│ │ │ │ │ │ │ └── baseUrl.service.ts
│ │ │ │ │ │ └── model.d.ts
│ │ │ │ │ ├── local.service.ts
│ │ │ │ │ └── remote.service.ts
│ │ │ │ └── trace.service.ts
│ │ │ └── shared
│ │ │ │ ├── README.md
│ │ │ │ ├── components
│ │ │ │ ├── download-client.component.ts
│ │ │ │ ├── extension-app
│ │ │ │ │ └── extension-app.component.ts
│ │ │ │ ├── extension-feedback
│ │ │ │ │ └── extension-feedback.ts
│ │ │ │ └── schema-form
│ │ │ │ │ └── schema-form.component.ts
│ │ │ │ ├── constans
│ │ │ │ ├── featureName.ts
│ │ │ │ ├── modal-size.ts
│ │ │ │ ├── newbie-guide.ts
│ │ │ │ └── update-log.ts
│ │ │ │ ├── decorators
│ │ │ │ ├── index.ts
│ │ │ │ └── memo.ts
│ │ │ │ ├── directives
│ │ │ │ ├── focus-form-input.directive.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── stop-propagation.directive.ts
│ │ │ │ └── trace.directive.ts
│ │ │ │ ├── models
│ │ │ │ ├── extension-manager
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── module.ts
│ │ │ │ ├── extension.ts
│ │ │ │ ├── member.model.ts
│ │ │ │ ├── protocol.constant.ts
│ │ │ │ ├── shared.model.ts
│ │ │ │ └── storageKeys.constant.ts
│ │ │ │ ├── shared.module.ts
│ │ │ │ ├── store
│ │ │ │ ├── effect.service.ts
│ │ │ │ └── state.service.ts
│ │ │ │ └── utils
│ │ │ │ ├── browser-type.ts
│ │ │ │ ├── data-transfer
│ │ │ │ ├── data-transfer.utils.spec.ts
│ │ │ │ └── data-transfer.utils.ts
│ │ │ │ ├── index.utils.ts
│ │ │ │ ├── parseOpenAPI
│ │ │ │ ├── OpenAPIParser.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── type.ts
│ │ │ │ ├── pc-merge.ts
│ │ │ │ ├── storage
│ │ │ │ └── storage.utils.ts
│ │ │ │ └── tree
│ │ │ │ └── tree.utils.ts
│ │ ├── assets
│ │ │ ├── .gitkeep
│ │ │ ├── font
│ │ │ │ └── iconpark.js
│ │ │ ├── icons
│ │ │ │ ├── icon.ico
│ │ │ │ └── iconLogo.png
│ │ │ ├── images
│ │ │ │ ├── feishu.png
│ │ │ │ ├── github.png
│ │ │ │ ├── heart.png
│ │ │ │ ├── logo.svg
│ │ │ │ ├── newbie-guide.png
│ │ │ │ └── qq.png
│ │ │ └── libs
│ │ │ │ └── protocolcheck.js
│ │ ├── environments
│ │ │ ├── common.constant.ts
│ │ │ ├── environment.dev.ts
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── extensions
│ │ │ ├── README.md
│ │ │ └── themes
│ │ │ │ ├── blue.json
│ │ │ │ ├── dark.json
│ │ │ │ ├── debug.json
│ │ │ │ ├── green.json
│ │ │ │ ├── light.json
│ │ │ │ └── orange.json
│ │ ├── index.html
│ │ ├── karma.conf.js
│ │ ├── main.ts
│ │ ├── polyfills-test.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ ├── styles
│ │ │ ├── antd.less
│ │ │ ├── atom.scss
│ │ │ ├── dark.less
│ │ │ ├── light.less
│ │ │ └── variables.css
│ │ ├── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.spec.json
│ │ └── typings.d.ts
│ ├── stylelint.config.js
│ ├── tailwind.config.js
│ └── tsconfig.json
├── environment.ts
├── node
│ ├── extensions-manage
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── ecosystem.config.cjs
│ │ ├── extension-manage.js
│ │ ├── main.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ └── tools
│ │ │ └── install.cjs
│ ├── mock
│ │ └── README.md
│ └── test-server
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── ecosystem.config.js
│ │ ├── electron
│ │ ├── forkUnit.js
│ │ ├── main.ts
│ │ └── unitWorker.ts
│ │ ├── package.json
│ │ ├── request
│ │ ├── config.json
│ │ ├── domain.json
│ │ ├── lang.json
│ │ ├── libs
│ │ │ ├── apiUtil.js
│ │ │ ├── common.js
│ │ │ ├── data_constructor.js
│ │ │ ├── encrypt.js
│ │ │ ├── exec_worker_thread.js
│ │ │ ├── getFile.package.js
│ │ │ ├── http.package.js
│ │ │ ├── mineType.package.js
│ │ │ ├── redirect.js
│ │ │ ├── script-engines
│ │ │ │ ├── postman
│ │ │ │ │ └── postman-sandbox.js
│ │ │ │ └── vm2
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── lib
│ │ │ │ │ ├── cli.js
│ │ │ │ │ ├── contextify.js
│ │ │ │ │ ├── main.js
│ │ │ │ │ ├── sandbox.js
│ │ │ │ │ └── wildcard.js
│ │ │ ├── xml.js
│ │ │ └── zlib.js
│ │ ├── pc
│ │ │ └── extension-manage.js
│ │ └── unit.js
│ │ └── server
│ │ ├── README.md
│ │ ├── grpc_client.js
│ │ ├── main.js
│ │ └── socketio.js
├── platform
│ ├── common
│ │ └── i18n.ts
│ ├── electron-browser
│ │ ├── i18n.ts
│ │ └── preload.ts
│ ├── node
│ │ ├── constant.ts
│ │ ├── extension-manager
│ │ │ ├── core.ts
│ │ │ ├── handler.model.ts
│ │ │ ├── handler.ts
│ │ │ └── manager.ts
│ │ ├── grpc
│ │ │ └── index.ts
│ │ ├── i18n.ts
│ │ └── mock-server
│ │ │ └── index.ts
│ └── typings.d.ts
└── shared
│ ├── common
│ ├── browserView.ts
│ └── common.ts
│ ├── electron-main
│ └── constant.ts
│ └── node
│ ├── file.ts
│ └── module.ts
├── stylelint.config.js
├── test
├── e2e
│ ├── .auth
│ │ └── user.json
│ ├── .gitignore
│ ├── assets
│ │ └── import-file
│ │ │ ├── postcat.json
│ │ │ ├── postman.json
│ │ │ └── swagger.json
│ ├── mikasa
│ │ ├── README.md
│ │ ├── api.t
│ │ ├── env.t
│ │ ├── tifa.config.js
│ │ └── ws.t
│ ├── package.json
│ ├── playwright.config.ts
│ ├── playwright
│ │ └── .auth
│ │ │ └── user.json
│ ├── tests
│ │ ├── api-case.spec.ts
│ │ ├── api-edit.spec.ts
│ │ ├── api-group.spec.ts
│ │ ├── api-mock.spec.ts
│ │ ├── api-unit-test.spec.ts
│ │ ├── env.spec.ts
│ │ ├── extension.spec.ts
│ │ ├── member.spec.ts
│ │ ├── mock.spec.ts
│ │ ├── project.spec.ts
│ │ ├── tab.spec.ts
│ │ ├── user.spec.ts
│ │ ├── websocket-test.spec.ts
│ │ └── workspace.spec.ts
│ ├── utils
│ │ ├── commom.util.ts
│ │ └── remote.setup.ts
│ └── yarn.lock
└── unit
│ ├── .gitignore
│ └── electron
│ └── extension_manage.js
├── tsconfig.json
├── wiki
├── README.en.md
└── images
│ └── logo.png
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | _cli-tpl/
2 | dist/
3 | coverage/
4 | src/node
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | **/*.json
13 | # Dependency directories
14 | node_modules/
15 |
16 | # TypeScript cache
17 | *.tsbuildinfo
18 |
19 | # Optional npm cache directory
20 | .npm
21 |
22 | # Optional eslint cache
23 | .eslintcache
24 |
25 | # Yarn Integrity file
26 | .yarn-integrity
27 |
28 | # dotenv environment variables file
29 | .env
30 | .env.test
31 |
32 | .cache/
33 |
34 | # yarn v2
35 | .yarn
36 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | build/** linguist-vendored=true
2 | * text=auto eol=lf
3 | *.ts linguist-detectable=false
4 | *.css linguist-detectable=false
5 | *.scss linguist-detectable=false
6 | *.js linguist-detectable=true
7 |
8 | # 无格式的文本文件,保证 Windows 的批处理文件在 checkout 至工作区时,始终被转换为 CRLF 风格的换行符;
9 | *.bat text eol=crlf
10 |
11 | # 对于sh文件,标记为文本文件,在文件入Git库时进行规范化,即行尾为LF。在检出到工作目录时,行尾也不会转换为CRLF(即保持LF)。
12 | *.sh text eol=lf
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: 'Bug Report'
2 | description: Create a report to help project improve
3 | body:
4 | - type: markdown
5 | attributes:
6 | value: |
7 | Before opening an issue, we recommend:
8 | * Use English to communicate.
9 | * Search for duplicates of [Issues](https://github.com/Postcatlab/postcat/issues) and [Discussions](https://github.com/Postcatlab/postcat/discussions) unresolved.
10 | - type: textarea
11 | id: bug-description
12 | attributes:
13 | label: Describe the bug
14 | description: A clear and concise description of what the bug is..
15 | placeholder: |
16 | What is the issue with the current behavior?
17 | validations:
18 | required: true
19 | - type: textarea
20 | id: environment
21 | attributes:
22 | label: Environment
23 | description: Share your environment details. Reports without proper environment details will likely be closed.
24 | placeholder: |
25 | - Postcat Version [e.g. 1.7.0]
26 | - OS: [e.g. Darwin arm64 21.5.0]
27 | validations:
28 | required: true
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: 'Feature Request'
2 | description: Suggest an idea for this project
3 | body:
4 | - type: markdown
5 | attributes:
6 | value: |
7 | Before opening an issue, we recommend:
8 | * Use English to communicate.
9 | * Search for duplicates of [Issues](https://github.com/Postcatlab/postcat/issues) and [Discussions](https://github.com/Postcatlab/postcat/discussions) unresolved.
10 | - type: textarea
11 | id: bug-description
12 | attributes:
13 | label: Describe the feature
14 | description: A clear and concise description of what the feature is..
15 | placeholder: |
16 | A clear and concise description of any alternative solutions or features you've considered.
17 | validations:
18 | required: true
19 | - type: textarea
20 | id: environment
21 | attributes:
22 | label: Environment
23 | description: Share your environment details. Reports without proper environment details will likely be closed.
24 | placeholder: |
25 | - Postcat Version [e.g. 1.7.0]
26 | - OS: [e.g. Darwin arm64 21.5.0]
27 | validations:
28 | required: true
29 |
--------------------------------------------------------------------------------
/.github/workflows/build-windows.yml:
--------------------------------------------------------------------------------
1 | name: Build Windows
2 |
3 | on:
4 | push:
5 | # branches: [build/windows]
6 | tags:
7 | - 'v*.*.*'
8 |
9 | jobs:
10 | build:
11 | name: Build
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Install Node.js
15 | uses: actions/setup-node@v3.0.0
16 | with:
17 | node-version: '16'
18 | - name: Checkout
19 | uses: actions/checkout@v3
20 | - name: Install OpenVPN
21 | run: |
22 | echo "${{ secrets.OPENVPN_CONFIG_FILE }}" > .github/workflows/client.ovpn
23 | sudo apt update
24 | sudo apt install -y openvpn openvpn-systemd-resolved
25 | - name: Connect to VPN
26 | uses: 'kota65535/github-openvpn-connect-action@v2'
27 | with:
28 | config_file: .github/workflows/client.ovpn
29 | - name: Build something
30 | env:
31 | SSH_WINDOWS_IP: ${{ secrets.SSH_WINDOWS_IP }}
32 | SSH_WINDOWS_USERNAME: ${{ secrets.SSH_WINDOWS_USERNAME }}
33 | SSH_WINDOWS_PASSWORD: ${{ secrets.SSH_WINDOWS_PASSWORD }}
34 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
35 | QINIU_ENV_JS: ${{ secrets.QINIU_ENV_JS }}
36 | run: |
37 | yarn
38 | yarn deployWindows
39 |
--------------------------------------------------------------------------------
/.github/workflows/crowdin.yml:
--------------------------------------------------------------------------------
1 | name: Crowdin Action
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | jobs:
8 | synchronize-with-crowdin:
9 | if: contains(github.event.head_commit.message , 'i18n')
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v2
14 |
15 | - name: crowdin action
16 | uses: crowdin/github-action@1.4.10
17 | with:
18 | upload_translations: true
19 | download_translations: true
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
22 | CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
23 | CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
24 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | # github release event list: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release
4 |
5 | on:
6 | release:
7 | types: [published]
8 |
9 | jobs:
10 | release:
11 | name: Qi niu published
12 | runs-on: ${{ matrix.os }}
13 |
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | os: [ubuntu-latest]
18 |
19 | steps:
20 | - name: Check out git repository
21 | uses: actions/checkout@v3.0.0
22 |
23 | - name: Install Node.js
24 | uses: actions/setup-node@v3.0.0
25 | with:
26 | node-version: '16'
27 |
28 | - name: Release for Linux
29 | if: matrix.os == 'ubuntu-latest'
30 | run: |
31 | npm i -g qiniu@6.x
32 | echo "${{ secrets.QINIU_ENV_JS }}" > ./scripts/qiniu_env.js
33 | node scripts/publish.js
34 |
--------------------------------------------------------------------------------
/.github/workflows/sync-branch.yml:
--------------------------------------------------------------------------------
1 | name: Merge main branch to build/windows
2 | on:
3 | push:
4 | branches:
5 | - 'main'
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | merge-branch:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@master
15 |
16 | - name: Merge main -> build/windows
17 | uses: devmasx/merge-branch@master
18 | with:
19 | type: now
20 | from_branch: main
21 | target_branch: build/windows
22 | github_token: ${{ github.token }}
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | dist/
5 | /build/ElectronInstall/
6 | /build/FilesToInstall/
7 | /build/Output/
8 | /build/NiuNiuCaptureElectronDemo/
9 | /build/SetupScripts/skinpwd.nsh
10 | out/
11 | /tmp
12 | /out-tsc
13 | /app-builds
14 | /release
15 | *.js
16 | *.js.map
17 | !patches/*
18 | !src/node/**/*.js
19 | !/api/*.js
20 | !/scripts/**/*.js
21 | !*.config.js
22 | !.*.js
23 |
24 | # dependencies
25 | node_modules
26 | # package-lock.json
27 |
28 | # IDEs and editors
29 | .idea
30 | .project
31 | .classpath
32 | .c9/
33 | *.launch
34 | .settings/
35 | *.sublime-workspace
36 |
37 | # misc
38 | /connect.lock
39 | /coverage
40 | /libpeerconnection.log
41 | npm-debug.log
42 | *-error.log
43 | testem.log
44 | /typings
45 |
46 | # e2e
47 | /e2e/test
48 | /e2e/imgs
49 | !/e2e/protractor.conf.js
50 | /e2e/*.map
51 | /e2e/tracing
52 | /e2e/screenshots
53 |
54 | # System Files
55 | .DS_Store
56 | Thumbs.db
57 | # *-lock.json
58 | -error.log
59 | .vercel
60 | nginx-test.conf
61 | docker-compose.dev.yml
62 | Dockerfile.dev
63 |
64 | #cache
65 | buildFile
66 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # shellcheck source=./_/husky.sh
4 | . "$(dirname "$0")/_/husky.sh"
5 |
6 | npx --no-install commitlint --edit "$1"
7 |
--------------------------------------------------------------------------------
/.husky/common.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | command_exists () {
3 | command -v "$1" >/dev/null 2>&1
4 | }
5 |
6 | # Workaround for Windows 10, Git Bash and Yarn
7 | if command_exists winpty && test -t 1; then
8 | exec < /dev/tty
9 | fi
10 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 | . "$(dirname "$0")/common.sh"
4 |
5 | [ -n "$CI" ] && exit 0
6 |
7 | # Format and submit code according to lintstagedrc.js configuration
8 | npm run lint:lint-staged
9 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | save=true
2 | save-exact=true
3 | electron_mirror=https://npmmirror.com/mirrors/electron/
4 | registry=https://registry.npmmirror.com
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # add files you wish to ignore here
2 | **/*.md
3 | **/*.svg
4 | **/test.ts
5 |
6 | .stylelintrc
7 | .prettierrc
8 |
9 | src/assets/*
10 | src/index.html
11 | node_modules/
12 | .vscode/
13 | coverage/
14 | dist/
15 | package.json
16 | tslint.json
17 |
18 | _cli-tpl/**/*
19 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | /public/*
3 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "cyrilletuzi.angular-schematics",
4 | "dbaeumer.vscode-eslint",
5 | "stylelint.vscode-stylelint",
6 | "esbenp.prettier-vscode",
7 | "heybourn.headwind",
8 | "streetsidesoftware.code-spell-checker"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/.vscode/ts.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | "Print to console": {
3 | "scope": "typescript",
4 | "prefix": "log",
5 | "body": [
6 | "console.log('111$1');"
7 | ],
8 | "description": "Log output to console"
9 | },
10 | "Create a Angular Component": {
11 | "scope": "typescript",
12 | "prefix": "comp",
13 | "body": [
14 | "import { Component, OnInit } from '@angular/core';",
15 | "@Component({",
16 | "// standalone: true,",
17 | "selector: '$1',",
18 | "template: `
`,",
19 | "styleUrls: []",
20 | "})",
21 | "export class $2Component implements OnInit {",
22 | "constructor() {}",
23 | "ngOnInit() {}",
24 | "}"
25 | ],
26 | "description": "Create a new Angular Component"
27 | }
28 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | First off, thank you for considering contributing to Postcat. It's people
4 | like you that make Postcat such a great tool.
5 |
6 | ## Contributing Guide
7 | * [中文](https://github.com/Postcatlab/postcat/wiki/%E8%B4%A1%E7%8C%AE%E8%80%85%E6%8C%87%E5%8D%97)
8 | * English
--------------------------------------------------------------------------------
/api/unit.js:
--------------------------------------------------------------------------------
1 | let _LibsFlowCommon = require('../src/node/test-server/request/unit.js');
2 | let _LibsCommon = require('../src/node/test-server/request/libs/common.js');
3 |
4 | module.exports = (req, res) => {
5 | console.log('unit.js', req.body);
6 | try {
7 | let reqJSON = req.body.data;
8 | reqJSON.env = _LibsCommon.parseEnv(reqJSON.env);
9 | new _LibsFlowCommon.core().main(reqJSON).then(({ globals, report, history }) => {
10 | ['general', 'requestInfo', 'resultInfo'].forEach(keyName => {
11 | if (typeof history[keyName] === 'string') history[keyName] = JSON.parse(history[keyName]);
12 | });
13 | res.send(
14 | JSON.stringify({
15 | action: 'finish',
16 | data: {
17 | id: req.body.id,
18 | globals: globals,
19 | report: report,
20 | history: history
21 | }
22 | })
23 | );
24 | });
25 | } catch (e) {
26 | console.error('unit.js', e, req.body);
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /src/browser/src/locale/messages.xlf
3 | translation: /src/browser/src/locale/messages.%two_letters_code%.xlf
4 | project_id_env: CROWDIN_PROJECT_ID
5 | api_token_env: CROWDIN_PERSONAL_TOKEN
6 |
--------------------------------------------------------------------------------
/donkey.config.js:
--------------------------------------------------------------------------------
1 | const baseUrl = './src/browser/src/app/services/storage/';
2 | module.exports = {
3 | entry: {
4 | // target: ["./test/apiData.ts", "./test/env.ts"]
5 | target: [baseUrl + 'api.ts']
6 | },
7 | output: [
8 | {
9 | mode: 'angular',
10 | name: 'remote.service',
11 | path: baseUrl
12 | },
13 | {
14 | mode: 'dexie',
15 | name: 'local.service',
16 | path: baseUrl
17 | },
18 | {
19 | mode: 'glue',
20 | name: 'api.service',
21 | path: baseUrl
22 | }
23 | // schema: "./output/entities"
24 | // typeorm: "./output/typeorm"
25 | ],
26 | remoteBase: '',
27 | paramsFill: {
28 | projectUuid: 'this.store.getCurrentProjectID',
29 | workSpaceUuid: 'this.store.getCurrentWorkspaceUuid',
30 | withItem: true
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | useTabs: false,
4 | printWidth: 140,
5 | tabWidth: 2,
6 | semi: true,
7 | htmlWhitespaceSensitivity: 'strict',
8 | arrowParens: 'avoid',
9 | bracketSpacing: true,
10 | proseWrap: 'preserve',
11 | trailingComma: 'none',
12 | endOfLine: 'lf'
13 | };
14 |
--------------------------------------------------------------------------------
/scripts/afterBuild.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const glob = require('glob');
3 | const { resolve } = require('path');
4 |
5 | glob.sync(resolve('./release', './*.exe.*')).forEach((item, i) => {
6 | const newName = item.replace(/ /g, '-');
7 | fs.rename(item, newName, err => {
8 | if (err) throw err;
9 | console.log('Rename complete!');
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/scripts/afterInstall.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // 输入文件
5 | const inputPath = path.join(__dirname, '../patches/windowsCodeSign.js');
6 | // 输出文件
7 | const outputPath = path.join(__dirname, '../node_modules/app-builder-lib/out/codeSign/windowsCodeSign.js');
8 |
9 | fs.readFile(inputPath, 'utf8', (err, data) => {
10 | if (err) {
11 | return console.error(err);
12 | }
13 |
14 | fs.writeFile(outputPath, data, { flag: 'w', encoding: 'utf8' }, err => {
15 | if (err) {
16 | console.error('app-builder-lib writeFile', err);
17 | }
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/scripts/afterPackHook.js:
--------------------------------------------------------------------------------
1 | //If stuck,check yarn.lock file,maybe has conflict
2 | // upgrade.js
3 | const child_process = require('child_process');
4 | const os = require('os');
5 |
6 | exports.default = async function (context) {
7 | //windows
8 | if (os.type() == 'Windows_NT') {
9 | child_process.execSync(`yarn wininstaller`);
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/scripts/beforeNSISBuild.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | // 模板文件
5 | const inputPath = path.join(__dirname, '../build/SetupScripts/nim/nim_setup.template.nsi');
6 | // 输出文件
7 | const outputPath = path.join(__dirname, '../build/SetupScripts/nim/nim_setup.nsi');
8 |
9 | const version = process.env.npm_package_version;
10 |
11 | fs.readFile(inputPath, 'utf8', (err, data) => {
12 | if (err) {
13 | return console.error('before NSIS build readFile', err);
14 | }
15 |
16 | let text = data.toString();
17 |
18 | const versionArr = version.split('-');
19 | versionArr[0] = versionArr[0] + '.0';
20 | const _version = versionArr.join('-');
21 |
22 | text = text.replace('#{PRODUCT_VERSION}', _version);
23 | text = text.replace('#{INSTALL_OUTPUT_NAME}', version);
24 |
25 | fs.writeFile(outputPath, text, { flag: 'w', encoding: 'utf8' }, err => {
26 | if (err) {
27 | console.error('before NSIS build writeFile', err);
28 | }
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/scripts/entitlements.mac.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | com.apple.security.cs.allow-jit
7 |
8 | com.apple.security.cs.allow-unsigned-executable-memory
9 |
10 |
11 |
--------------------------------------------------------------------------------
/scripts/notarize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Use electron-notarize to notarize app
3 | *
4 | * @description The app should be notarized after it has been signed and before it’s packaged into a dmg.
5 | * Electron-builder has a hook for this called afterSign. You can link that to a javascript file that will be called (and waited for) after sign.
6 | * You add it to your top level “build” configuration
7 | *
8 | * @link https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/
9 | */
10 |
11 | exports.default = function notarizing(context) {
12 | return context;
13 | };
14 |
--------------------------------------------------------------------------------
/scripts/tools/debugComponents.js:
--------------------------------------------------------------------------------
1 | const { execSync } = require('child_process');
2 | const components = [
3 | 'eo-ng-auto-complete',
4 | 'eo-ng-breadcrumb',
5 | 'eo-ng-button',
6 | 'eo-ng-card-switch',
7 | 'eo-ng-cascader',
8 | 'eo-ng-checkbox',
9 | 'eo-ng-codebox',
10 | 'eo-ng-copy',
11 | 'eo-ng-counter',
12 | 'eo-ng-date-picker',
13 | 'eo-ng-dropdown',
14 | 'eo-ng-empty',
15 | 'eo-ng-feedback',
16 | 'eo-ng-input',
17 | 'eo-ng-layout',
18 | 'eo-ng-menu',
19 | 'eo-ng-radio',
20 | 'eo-ng-select',
21 | 'eo-ng-switch',
22 | 'eo-ng-table',
23 | 'eo-ng-tabs',
24 | 'eo-ng-tree',
25 | 'eo-ng-upload'
26 | ];
27 | components.forEach(name => {
28 | execSync(`yarn link ${name}`, { stdio: 'inherit' });
29 | });
30 |
--------------------------------------------------------------------------------
/scripts/tools/unlinkComponents.js:
--------------------------------------------------------------------------------
1 | const { execSync } = require('child_process');
2 | const components = [
3 | 'eo-ng-auto-complete',
4 | 'eo-ng-breadcrumb',
5 | 'eo-ng-button',
6 | 'eo-ng-card-switch',
7 | 'eo-ng-cascader',
8 | 'eo-ng-checkbox',
9 | 'eo-ng-codebox',
10 | 'eo-ng-copy',
11 | 'eo-ng-counter',
12 | 'eo-ng-date-picker',
13 | 'eo-ng-dropdown',
14 | 'eo-ng-empty',
15 | 'eo-ng-feedback',
16 | 'eo-ng-input',
17 | 'eo-ng-layout',
18 | 'eo-ng-menu',
19 | 'eo-ng-radio',
20 | 'eo-ng-select',
21 | 'eo-ng-switch',
22 | 'eo-ng-table',
23 | 'eo-ng-tabs',
24 | 'eo-ng-tree',
25 | 'eo-ng-upload'
26 | ];
27 | components.forEach(name => {
28 | execSync(`yarn unlink ${name}`, { stdio: 'inherit' });
29 | });
30 |
--------------------------------------------------------------------------------
/scripts/tools/upgradeComponent.js:
--------------------------------------------------------------------------------
1 | //If stuck,check yarn.lock file,maybe has conflict
2 | // upgrade.js
3 | const child_process = require('child_process');
4 | const pkg = require('../../src/browser/package.json');
5 | const filterRegex = /eo-ng-.*/;
6 |
7 | const dependencies = pkg['dependencies'];
8 | const dependencyList = Object.keys(dependencies).filter(dependency => filterRegex.test(dependency));
9 | child_process.execSync(`cd ../../src/browser&&yarn upgrade ${dependencyList.join('@latest ')}@latest`);
10 |
--------------------------------------------------------------------------------
/scripts/urlProtoco.nsh:
--------------------------------------------------------------------------------
1 | !macro customInstall
2 | DetailPrint "Register postcat URI Handler"
3 | DeleteRegKey HKCR "postcat"
4 | WriteRegStr HKCR "postcat" "" "URL:postcat"
5 | WriteRegStr HKCR "postcat" "URL Protocol" ""
6 | WriteRegStr HKCR "postcat\shell" "" ""
7 | WriteRegStr HKCR "postcat\shell\Open" "" ""
8 | WriteRegStr HKCR "postcat\shell\Open\command" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME} %1"
9 | !macroend
10 |
--------------------------------------------------------------------------------
/src/app/common/images/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/1024x1024.png
--------------------------------------------------------------------------------
/src/app/common/images/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/128x128.png
--------------------------------------------------------------------------------
/src/app/common/images/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/16x16.png
--------------------------------------------------------------------------------
/src/app/common/images/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/24x24.png
--------------------------------------------------------------------------------
/src/app/common/images/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/256x256.png
--------------------------------------------------------------------------------
/src/app/common/images/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/32x32.png
--------------------------------------------------------------------------------
/src/app/common/images/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/48x48.png
--------------------------------------------------------------------------------
/src/app/common/images/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/512x512.png
--------------------------------------------------------------------------------
/src/app/common/images/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/64x64.png
--------------------------------------------------------------------------------
/src/app/common/images/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/icon.icns
--------------------------------------------------------------------------------
/src/app/common/images/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/logo.ico
--------------------------------------------------------------------------------
/src/app/common/images/postcat.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/app/common/images/postcat.bmp
--------------------------------------------------------------------------------
/src/app/electron-main/language.service.ts:
--------------------------------------------------------------------------------
1 | import { app } from 'electron';
2 | import Store from 'electron-store';
3 |
4 | import { LANGUAGES } from '../../browser/src/app/core/services/language/language.model';
5 | const store = new Store();
6 | class LanguageInstance {
7 | private static _instance: LanguageInstance;
8 | constructor() {}
9 | public static get Instance(): LanguageInstance {
10 | return this._instance || (this._instance = new this());
11 | }
12 | async get() {
13 | await app.whenReady();
14 | let lang = store.get('language') || (app.getLocale().includes('zh') ? 'zh-Hans' : 'en-US');
15 | return lang as string;
16 | }
17 | async getPath() {
18 | const systemLanguage = await this.get();
19 | return LANGUAGES.find(val => val.value === systemLanguage).path;
20 | }
21 | set(localeID) {
22 | store.set('language', localeID);
23 | }
24 | }
25 | export const LanguageService = LanguageInstance.Instance;
26 |
--------------------------------------------------------------------------------
/src/app/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | interface Window {
3 | process: any;
4 | requirejs: any;
5 | require: any;
6 | angular: any;
7 | electron: any;
8 | }
9 |
--------------------------------------------------------------------------------
/src/browser/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/src/browser/.eslintignore:
--------------------------------------------------------------------------------
1 | _cli-tpl/
2 | dist/
3 | coverage/
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | **/*.json
12 | # Dependency directories
13 | node_modules/
14 |
15 | # TypeScript cache
16 | *.tsbuildinfo
17 |
18 | # Optional npm cache directory
19 | .npm
20 |
21 | # Optional eslint cache
22 | .eslintcache
23 |
24 | # Yarn Integrity file
25 | .yarn-integrity
26 |
27 | # dotenv environment variables file
28 | .env
29 | .env.test
30 |
31 | .cache/
32 |
33 | # yarn v2
34 | .yarn
35 |
--------------------------------------------------------------------------------
/src/browser/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["dist/**/*", "release/**/*"],
4 | "parserOptions": {
5 | "ecmaVersion": "latest"
6 | },
7 | "overrides": [
8 | {
9 | "files": ["*.ts"],
10 | "parserOptions": {
11 | "project": ["./src/tsconfig.app.json", "./src/tsconfig.spec.json"],
12 | "createDefaultProgram": true
13 | },
14 | "extends": [
15 | "plugin:@angular-eslint/ng-cli-compat",
16 | "plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
17 | "plugin:@angular-eslint/template/process-inline-templates"
18 | ],
19 | "rules": {
20 | "no-underscore-dangle": 0,
21 | "arrow-body-style": 0,
22 | "prefer-arrow/prefer-arrow-functions": 0,
23 | "@typescript-eslint/member-ordering": 0,
24 | "@typescript-eslint/no-unused-expressions": 0,
25 | "@typescript-eslint/naming-convention": 0,
26 | "@angular-eslint/directive-selector": 0,
27 | "@angular-eslint/component-selector": [
28 | "error",
29 | {
30 | "type": "element",
31 | "style": "kebab-case"
32 | }
33 | ]
34 | }
35 | },
36 | {
37 | "files": ["*.html"],
38 | "extends": ["plugin:@angular-eslint/template/recommended"],
39 | "rules": {}
40 | }
41 | ]
42 | }
43 |
--------------------------------------------------------------------------------
/src/browser/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | dist/
5 | /tmp
6 | /out-tsc
7 | /app-builds
8 | /release
9 | src/**/*.js
10 | !markdown-loader.js
11 | !scripts/*.js
12 | !src/karma.conf.js
13 | !src/assets/font/*.js
14 | !src/assets/libs/*.js
15 | !build/*.js
16 | *.js.map
17 | !*.webpack.js
18 |
19 | # dependencies
20 | node_modules
21 |
22 | # IDEs and editors
23 | .idea
24 | .project
25 | .classpath
26 | .c9/
27 | *.launch
28 | .settings/
29 | *.sublime-workspace
30 |
31 | # IDE - VSCode
32 | .vscode/*
33 | .vscode/settings.json
34 | !.vscode/tasks.json
35 | !.vscode/launch.json
36 | !.vscode/extensions.json
37 |
38 | # misc
39 | .angular/
40 | /.sass-cache
41 | /connect.lock
42 | /coverage
43 | /libpeerconnection.log
44 | npm-debug.log
45 | testem.log
46 | /typings
47 |
48 | # e2e
49 | /e2e/test
50 | !/e2e/protractor.conf.js
51 | /e2e/*.map
52 | /e2e/tracing
53 | /e2e/screenshots
54 |
55 | # System Files
56 | .DS_Store
57 | Thumbs.db
58 | # *-lock.json
59 |
--------------------------------------------------------------------------------
/src/browser/.npmrc:
--------------------------------------------------------------------------------
1 | save=true
2 | save-exact=true
3 | registry=https://registry.npmmirror.com
4 |
--------------------------------------------------------------------------------
/src/browser/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "bradlc.vscode-tailwindcss"
4 | ]
5 | }
--------------------------------------------------------------------------------
/src/browser/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Renderer",
9 | "type": "chrome",
10 | "request": "attach",
11 | "port": 9876,
12 | "url": "http://localhost:4200",
13 | "sourceMaps": true,
14 | "timeout": 10000,
15 | "trace": "verbose",
16 | "sourceMapPathOverrides": {
17 | "webpack:///./*": "${workspaceFolder}/*"
18 | },
19 | "preLaunchTask": "Build.Renderer"
20 | }
21 | ],
22 | "compounds": [
23 | {
24 | "name": "Application Debug",
25 | "configurations": ["Renderer"]
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/src/browser/README.md:
--------------------------------------------------------------------------------
1 | # Postcat Workbench
2 |
3 | Postcat browser code
4 |
5 | # Run
6 |
7 | | Command | Desc |
8 | | ------------ | --------------------------------- |
9 | | `yarn start` | Only run frontend,local workspace |
10 | | `yarn serve` | Use proxy |
11 | | `yarn build` | Pack static frontend resource |
12 |
--------------------------------------------------------------------------------
/src/browser/markdown-loader.js:
--------------------------------------------------------------------------------
1 | const markdownIt = require('markdown-it');
2 |
3 | module.exports = source => {
4 | let md = new markdownIt();
5 | const html = md.render(source);
6 | return html;
7 | };
8 |
--------------------------------------------------------------------------------
/src/browser/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | // "targe": "https://postcat.com",
4 | "target": "http://52.76.76.88:8080",
5 | "secure": false,
6 | "changeOrigin": true,
7 | "logLevel": "debug"
8 | },
9 | "/usercenter": {
10 | // "targe": "https://postcat.com",
11 | "target": "http://52.76.76.88:8080",
12 | "secure": false,
13 | "changeOrigin": true,
14 | "logLevel": "debug"
15 | }
16 | // "/api": {
17 | // "target": "https://mockapi.eolink.com/Dr31QyT4b832495fe945fb9420215f167e33b2dc1fb4f27",
18 | // "secure": false,
19 | // "changeOrigin": true,
20 | // "logLevel": "debug",
21 | // "pathRewrite": {
22 | // "^/api": ""
23 | // }
24 | // }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'eo-root',
5 | template: ` `
6 | })
7 | export class AppComponent {
8 | openConsole = false;
9 | constructor() {
10 | //@ts-ignore
11 | window.tooglePcConsole = (isOpen = true) => {
12 | this.openConsole = isOpen;
13 | };
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/chat-robot/chat-robot-container/chat-robot.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | flex-direction: column;
4 | background-color: var(--background-color);
5 | width: 500px;
6 | height: 600px;
7 | border-radius: 6px;
8 | box-shadow: 0 0 10px var(--shadow-color);
9 | }
10 |
11 | .header,
12 | .messages,
13 | .form {
14 | padding: 15px 20px;
15 | }
16 |
17 | .header,
18 | .messages {
19 | border-bottom: 1px solid var(--system-border-color);
20 | }
21 |
22 | .header {
23 | font-weight: bold;
24 | font-size: 15px;
25 | }
26 |
27 | .messages {
28 | overflow-y: auto;
29 | overflow-x: hidden;
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/chat-robot/chat-robot-form/chat-robot-form.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, EventEmitter, Input, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'pc-chat-robot-form',
5 | template: `
6 |
16 |
17 |
`,
18 | styles: []
19 | })
20 | export class ChatRobotFormComponent {
21 | /**
22 | * Predefined message text
23 | *
24 | * @type {string}
25 | */
26 | @Input() message: string = '';
27 | @Input() placeholder: string = '';
28 |
29 | @Input() loading: boolean = false;
30 |
31 | /**
32 | * Send message trigger event
33 | */
34 | @Output() readonly send = new EventEmitter<{ message: string }>();
35 | sendMessage() {
36 | if (this.loading) return;
37 | if (!String(this.message).trim().length) return;
38 | this.send.emit({ message: this.message.replace(/^[\r\n]+|[\r\n]+$/g, '') });
39 | this.message = '';
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/chat-robot/chat-robot-message/chat-robot-message.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | margin-bottom: 1.5rem;
4 |
5 | .message-content {
6 | padding: 1rem;
7 | border-radius: 0.5rem;
8 | max-width: 400px;
9 | }
10 |
11 | .text {
12 | word-wrap: break-word;
13 | white-space: pre-wrap;
14 | }
15 |
16 | &.not-reply {
17 | flex-direction: row-reverse;
18 |
19 | .message-content {
20 | background: var(--item-hover-background-color);
21 | color: var(--text-color);
22 | }
23 |
24 | nz-avatar {
25 | margin-left: 5px;
26 | }
27 | }
28 |
29 | &.reply {
30 | .message-content {
31 | background: #36f;
32 | color: #fff;
33 | }
34 |
35 | nz-avatar {
36 | margin-right: 5px;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/chat-robot/chat-robot.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { EoNgButtonModule } from 'eo-ng-button';
5 | import { EoNgInputModule } from 'eo-ng-input';
6 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
7 |
8 | import { EoIconparkIconModule } from '../eo-ui/iconpark-icon/eo-iconpark-icon.module';
9 | import { ChatRobotComponent } from './chat-robot-container/chat-robot.component';
10 | import { ChatRobotFormComponent } from './chat-robot-form/chat-robot-form.component';
11 | import { ChatRobotMessageComponent } from './chat-robot-message/chat-robot-message.component';
12 | import { ChatRobotService } from './chat-robot.service';
13 | @NgModule({
14 | declarations: [ChatRobotComponent, ChatRobotMessageComponent, ChatRobotFormComponent],
15 | providers: [ChatRobotService],
16 | imports: [CommonModule, EoNgInputModule, EoIconparkIconModule, NzAvatarModule, EoNgButtonModule, FormsModule],
17 | exports: [ChatRobotComponent, ChatRobotMessageComponent, ChatRobotFormComponent]
18 | })
19 | export class ChatRobotModule {}
20 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/chat-robot/chat-robot.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable({
4 | providedIn: 'root'
5 | })
6 | export class ChatRobotService {
7 | isShow: boolean = false;
8 | constructor() {}
9 | open() {
10 | this.isShow = true;
11 | }
12 | close() {
13 | this.isShow = false;
14 | }
15 | toggleShowStatus() {
16 | this.isShow = !this.isShow;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/NOTE.md:
--------------------------------------------------------------------------------
1 | ## Table
2 |
3 | 功能复杂的组件,如表格,其接口尽可能设计成数据驱动的,减少业务层的动作。
4 |
5 | - [x] 数据驱动
6 | - [x] 自定义插槽
7 | - [x] 支持虚拟滚动
8 | - [x] 控制页脚的隐藏与显示
9 | - [x] 筛选条件控制
10 | - [x] 树状展示
11 | - [x] 复制单元格内容
12 | - [x] 行信息描述
13 | - [x] 通过在 columns 数据结构中添加`isExpand`, 支持在列表指令某列进行树状的展开和收缩
14 | - [x] 内置支持行内编辑
15 | - [ ] 拖动排序
16 | - [ ] 单元格内嵌套下拉框
17 | - [ ] 关于树状展示的功能,Antd 官方提供的例子所使用的算法会合成一个比较臃肿的数据结构,后续可以尝试进行算法和数据结构的优化。
18 |
19 | ```
20 | {
21 | filterType: type, // * 子组件支持
22 | filterFn: () => {} // * 可选
23 | }
24 | ```
25 |
26 | ## Ace
27 |
28 | 在 config 中可以配置许多属性,但如果其他属性与 config 同时绑定在模板中,其他属性会覆盖 config
29 | disabled 属性不算在 config 中
30 |
31 | ```
32 | (blur)
33 | (focus)
34 | (copy)
35 | (paste)
36 | (change)
37 | (changeSession)
38 | (changeCursor)
39 | (changeSelection)
40 | ```
41 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/collapse/collapse.component.scss:
--------------------------------------------------------------------------------
1 | .ant-collapse > .ant-collapse-item > .ant-collapse-header {
2 | align-items: center;
3 | }
4 |
5 | .ant-collapse-item:last-child .ant-collapse-content,
6 | .ant-collapse-item:last-child,
7 | .ant-collapse-item:last-child .ant-collapse-header {
8 | color: var(--text-color);
9 | border-radius: 0 0 var(--border-radius) var(--border-radius);
10 | }
11 |
12 | .ant-collapse-header {
13 | background-color: var(--collapse-header-background-color);
14 | height: var(--collapse-header-height);
15 | }
16 |
17 | .ant-collapse-content {
18 | color: var(--text-color);
19 | background-color: var(--collapse-content-background-color);
20 | border-top: 1px solid var(--collapse-border-color);
21 |
22 | > .ant-collapse-content-box {
23 | padding: 0;
24 | }
25 | }
26 |
27 | .ant-collapse > .ant-collapse-item {
28 | border-bottom-color: var(--collapse-border-color);
29 | }
30 |
31 | .ant-collapse {
32 | border-color: var(--collapse-border-color);
33 | background-color: var(--background-color);
34 | border-radius: var(--border-radius);
35 | }
36 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/collapse/collapse.module.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Use of this source code is governed by an MIT-style license that can be
3 | * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
4 | */
5 |
6 | import { BidiModule } from '@angular/cdk/bidi';
7 | import { CommonModule } from '@angular/common';
8 | import { NgModule } from '@angular/core';
9 | import { NzNoAnimationModule } from 'ng-zorro-antd/core/no-animation';
10 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet';
11 |
12 | import { EoNgCollapsePanelComponent } from './collapse-panel.component';
13 | import { EoNgCollapseComponent } from './collapse.component';
14 |
15 | @NgModule({
16 | declarations: [EoNgCollapsePanelComponent, EoNgCollapseComponent],
17 | exports: [EoNgCollapsePanelComponent, EoNgCollapseComponent],
18 | imports: [BidiModule, CommonModule, NzOutletModule, NzNoAnimationModule]
19 | })
20 | export class EoNgCollapseModule {}
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/iconpark-icon/eo-iconpark-icon.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | @Component({
4 | // standalone: true,
5 | selector: 'eo-iconpark-icon',
6 | template: ``,
9 | styles: [
10 | `
11 | ::ng-deep eo-iconpark-icon {
12 | display: inline-flex;
13 | vertical-align: middle;
14 | }
15 | .ant-btn eo-iconpark-icon {
16 | color: var(--icon-color);
17 | }
18 | `
19 | ]
20 | })
21 | export class EoIconparkIconComponent {
22 | @Input() name: string;
23 | @Input() size?: string;
24 | constructor() {}
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/iconpark-icon/eo-iconpark-icon.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3 |
4 | import { EoIconparkIconComponent } from './eo-iconpark-icon.component';
5 |
6 | @NgModule({
7 | declarations: [EoIconparkIconComponent],
8 | imports: [CommonModule],
9 | exports: [EoIconparkIconComponent],
10 | schemas: [CUSTOM_ELEMENTS_SCHEMA]
11 | })
12 | export class EoIconparkIconModule {}
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.html:
--------------------------------------------------------------------------------
1 |
7 |
15 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | flex-direction: column;
4 | height: 100%;
5 | }
6 |
7 | .button-list {
8 | border-bottom: 1px solid var(--border-color);
9 | border-top-left-radius: 3px;
10 | border-top-right-radius: 3px;
11 | width: 100%;
12 | display: flex;
13 | font-size: 12px;
14 | align-items: center;
15 | padding-left: 10px;
16 |
17 | nz-select {
18 | width: 90px;
19 | }
20 |
21 | .btn {
22 | display: inline-flex;
23 | align-items: center;
24 | height: 30px;
25 | padding: 10px;
26 | cursor: pointer;
27 | color: var(--text-color);
28 |
29 | &:hover {
30 | color: var(--info-color);
31 | }
32 |
33 | > i {
34 | margin: 0 5px;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/monaco-editor/monaco.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { NzCodeEditorModule } from 'ng-zorro-antd/code-editor';
5 |
6 | import { EoIconparkIconModule } from '../iconpark-icon/eo-iconpark-icon.module';
7 | import { EoMonacoEditorComponent } from './monaco-editor.component';
8 |
9 | @NgModule({
10 | declarations: [EoMonacoEditorComponent],
11 | imports: [CommonModule, FormsModule, EoIconparkIconModule, NzCodeEditorModule],
12 | exports: [EoMonacoEditorComponent],
13 | schemas: []
14 | })
15 | export class EoMonacoEditorModule {}
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/setting/setting.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/components/eo-ui/setting/setting.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/setting/setting.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { EoNgTabsModule } from 'eo-ng-tabs';
3 |
4 | import { SharedModule } from '../../../shared/shared.module';
5 | import { EoSettingComponent } from './setting.component';
6 | @NgModule({
7 | declarations: [EoSettingComponent],
8 | imports: [SharedModule, EoNgTabsModule],
9 | exports: [EoSettingComponent]
10 | })
11 | export class EoSettingModule {}
12 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/shadow/shadow-dom-encapsulation.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { ShadowDomEncapsulationComponent } from './shadow-dom-encapsulation.component';
5 |
6 | @NgModule({
7 | declarations: [ShadowDomEncapsulationComponent],
8 | imports: [CommonModule],
9 | exports: [ShadowDomEncapsulationComponent]
10 | })
11 | export class ShadowDomEncapsulationModule {}
12 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/tab/tab.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EoNgButtonModule } from 'eo-ng-button';
4 | import { EoNgDropdownModule } from 'eo-ng-dropdown';
5 | import { EoNgTabsModule } from 'eo-ng-tabs';
6 | import { NzBadgeModule } from 'ng-zorro-antd/badge';
7 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet';
8 | import { NzSpinModule } from 'ng-zorro-antd/spin';
9 |
10 | import { EoIconparkIconModule } from '../iconpark-icon/eo-iconpark-icon.module';
11 | import { TabOperateService } from './tab-operate.service';
12 | import { TabStorageService } from './tab-storage.service';
13 | import { EoTabComponent } from './tab.component';
14 |
15 | @NgModule({
16 | declarations: [EoTabComponent],
17 | imports: [
18 | CommonModule,
19 | NzOutletModule,
20 | EoNgTabsModule,
21 | EoNgButtonModule,
22 | EoIconparkIconModule,
23 | EoNgDropdownModule,
24 | NzSpinModule,
25 | NzBadgeModule
26 | ],
27 | providers: [TabOperateService, TabStorageService],
28 | exports: [EoTabComponent]
29 | })
30 | export class EoTabModule {}
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/table-pro/table-pro.component.scss:
--------------------------------------------------------------------------------
1 | .eo-ng-table-full-screen {
2 | position: fixed;
3 | width: 100%;
4 | left: 0;
5 | top: 0;
6 | height: 100%;
7 | background-color: var(--background-color);
8 | z-index: 11;
9 | }
10 |
11 | :host {
12 | display: block;
13 | }
14 |
15 | :host ::ng-deep {
16 | .show-btn-when-hover-row {
17 | .eo-ng-table-btns {
18 | visibility: hidden;
19 | }
20 |
21 | .ant-table-row:hover .eo-ng-table-btns {
22 | visibility: visible;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/eo-ui/table-pro/table-pro.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { EoNgButtonModule } from 'eo-ng-button';
5 | import { EoNgCheckboxModule } from 'eo-ng-checkbox';
6 | import { EoNgDropdownModule } from 'eo-ng-dropdown';
7 | import { EoNgFeedbackTooltipModule } from 'eo-ng-feedback';
8 | import { EoNgTableModule } from 'eo-ng-table';
9 | import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
10 | import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm';
11 |
12 | import { EoTableProComponent } from './table-pro.component';
13 |
14 | @NgModule({
15 | declarations: [EoTableProComponent],
16 | imports: [
17 | CommonModule,
18 | FormsModule,
19 | EoNgTableModule,
20 | EoNgButtonModule,
21 | EoNgDropdownModule,
22 | EoNgCheckboxModule,
23 | NzPopconfirmModule,
24 | EoNgFeedbackTooltipModule,
25 | NzInputNumberModule
26 | ],
27 | exports: [EoTableProComponent]
28 | })
29 | export class EoTableProModule {}
30 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/extension-select/extension-select.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { EoNgFeedbackTooltipModule } from 'eo-ng-feedback';
5 | import { EoNgRadioModule } from 'eo-ng-radio';
6 | import { NzUploadModule } from 'ng-zorro-antd/upload';
7 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
8 |
9 | import { EoIconparkIconModule } from '../eo-ui/iconpark-icon/eo-iconpark-icon.module';
10 | import { ExportApiComponent } from './export-api/export-api.component';
11 | import { ImportApiComponent } from './import-api/import-api.component';
12 | import { PushApiComponent } from './push-api/push-api.component';
13 | import { ExtensionSelectComponent } from './select/extension-select.component';
14 | import { SyncApiComponent } from './sync-api/sync-api.component';
15 |
16 | const COMPONENTS = [ExtensionSelectComponent, ExportApiComponent, ImportApiComponent, PushApiComponent, SyncApiComponent];
17 | @NgModule({
18 | imports: [EoNgRadioModule, NzUploadModule, EoNgFeedbackTooltipModule, EoIconparkIconModule, CommonModule, FormsModule, SharedModule],
19 | declarations: [...COMPONENTS]
20 | })
21 | export class ExtensionSelectModule {}
22 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/extension-select/import-api/old2new.ts:
--------------------------------------------------------------------------------
1 | import { GroupModuleType, GroupType } from 'pc/browser/src/app/services/storage/db/dto/group.dto';
2 | import { ImportProjectDto } from 'pc/browser/src/app/services/storage/db/dto/project.dto';
3 |
4 | import { convertApiData } from '../../../services/storage/db/dataSource/convert';
5 |
6 | export const old2new = (params, projectUuid, workSpaceUuid): ImportProjectDto => {
7 | const { collections = [], environments } = params;
8 |
9 | const environmentList = environments.map(n => ({
10 | name: n.name,
11 | hostUri: n.hostUri,
12 | parameters: n.parameters,
13 | projectUuid,
14 | workSpaceUuid
15 | }));
16 | const formatData = (collections = []) => {
17 | collections.forEach(item => {
18 | // API
19 | if (item.uri) {
20 | const newApiData = convertApiData(item);
21 | Object.assign(item, newApiData);
22 | item.type = GroupType.virtual;
23 | item.module = GroupModuleType.API;
24 | }
25 | // 分组
26 | else {
27 | item.type = GroupType.USER_CREATED;
28 | if (item.children?.length) {
29 | formatData(item.children);
30 | }
31 | }
32 | });
33 | };
34 |
35 | formatData(collections);
36 |
37 | return {
38 | collections,
39 | environmentList
40 | };
41 | };
42 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/extension-select/select/extension-select.component.scss:
--------------------------------------------------------------------------------
1 | .extension {
2 | cursor: pointer;
3 | color: var(--text-color);
4 | border-color: var(--border-color);
5 | box-shadow: 0 0 3px var(--shadow-color);
6 | transition: all 0.3s ease;
7 |
8 | .logobg {
9 | background-repeat: no-repeat;
10 | background-size: 45px;
11 | background-position: center center;
12 | }
13 |
14 | &:hover {
15 | box-shadow: 0 0 10px var(--shadow-color);
16 | }
17 |
18 | &.active {
19 | box-shadow: 0 0 10px var(--shadow-color);
20 |
21 | eo-iconpark-icon {
22 | color: var(--success-color);
23 | display: flex;
24 | }
25 | }
26 |
27 | eo-iconpark-icon {
28 | display: none;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/extension-select/sync-api/schema.ts:
--------------------------------------------------------------------------------
1 | export const SYNC_API_SCHEMA = {
2 | $schema: 'http://json-schema.org/draft-07/schema#',
3 | type: 'object',
4 | properties: {
5 | __crontab: {
6 | type: 'number',
7 | label: $localize`Auto Sync`,
8 | ui: {
9 | widget: 'radio'
10 | },
11 | default: 4,
12 | required: true,
13 | oneOf: [
14 | {
15 | type: 'number',
16 | title: $localize`Off`,
17 | default: 0,
18 | const: 0
19 | },
20 | {
21 | type: 'number',
22 | title: $localize`Every four hours`,
23 | default: 4,
24 | const: 4
25 | },
26 | {
27 | type: 'number',
28 | title: $localize`Every twelve hours`,
29 | default: 12,
30 | const: 12
31 | },
32 | {
33 | type: 'number',
34 | title: $localize`Every day`,
35 | default: 24,
36 | const: 24
37 | }
38 | ]
39 | },
40 | __formater: {
41 | type: 'string',
42 | label: $localize`:@@SyncFormat:Format`,
43 | ui: {
44 | widget: 'radio'
45 | },
46 | default: '',
47 | required: true,
48 | oneOf: []
49 | }
50 | },
51 | allOf: []
52 | };
53 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/logo/logo.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep {
2 | .theme-pc-dark eo-logo .logo {
3 | rect {
4 | // fill: rgb(27 51 132);
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/logo/logo.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 |
3 | import { LogoComponent } from './logo.component';
4 |
5 | @NgModule({
6 | declarations: [LogoComponent],
7 | exports: [LogoComponent]
8 | })
9 | export class LogoModule {}
10 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/member-list/member-list.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .ant-list-items {
3 | .ant-list-item {
4 | @apply px-[10px] rounded-[3px];
5 |
6 | border: 1px solid transparent;
7 |
8 | &:hover {
9 | border: 1px solid var(--primary-color);
10 | }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/member-list/member-list.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EoNgButtonModule } from 'eo-ng-button';
4 | import { EoNgDropdownModule } from 'eo-ng-dropdown';
5 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
6 | import { NzListModule } from 'ng-zorro-antd/list';
7 |
8 | import { EoIconparkIconModule } from '../eo-ui/iconpark-icon/eo-iconpark-icon.module';
9 | import { MemberListComponent } from './member-list.component';
10 |
11 | @NgModule({
12 | declarations: [MemberListComponent],
13 | imports: [CommonModule, NzListModule, EoNgDropdownModule, NzAvatarModule, EoIconparkIconModule, EoNgButtonModule],
14 | exports: [MemberListComponent]
15 | })
16 | export class MemberListModule {}
17 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/member-list/member.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | export type UserMeta = {
3 | userName: string;
4 | username?: string;
5 | userNickName: string;
6 | roleTitle: string;
7 | myself: boolean;
8 | id: number;
9 | roles: any;
10 | role: {
11 | name: string;
12 | id: number;
13 | };
14 | email: string;
15 | mobilePhone: string;
16 | permissions: string[];
17 | };
18 | @Injectable()
19 | export class MemberService {
20 | role: any[];
21 | isOwner: boolean;
22 | constructor() {
23 | console.log('MemberService');
24 | }
25 | async addMember(items) {}
26 | searchUser: (search: string) => Promise;
27 | changeRole: (item) => boolean;
28 | removeMember: (item) => void;
29 | queryMember: (item) => UserMeta[];
30 | quitMember: (members: UserMeta[]) => void;
31 | }
32 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/nps-mask/component/nps-mask.component.scss:
--------------------------------------------------------------------------------
1 | .title {
2 | display: none;
3 | position: absolute;
4 | z-index: 1000000000;
5 | font-weight: 600;
6 | background-color: #fff;
7 | text-align: center;
8 | height: 43px;
9 | font-size: 15px;
10 | font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Hiragino Sans GB', 'Heiti SC', 'Microsoft YaHei', 'WenQuanYi Micro Hei',
11 | sans-serif;
12 | }
13 |
14 | .tips {
15 | display: none;
16 | position: absolute;
17 | z-index: 1000000000;
18 | background-color: #fff;
19 | height: 20px;
20 | line-height: 53px;
21 | text-align: center;
22 | }
23 |
24 | ::ng-deep {
25 | .nps-show,
26 | .nps-show-title {
27 | .title {
28 | display: block;
29 | }
30 | }
31 |
32 | .nps-show,
33 | .nps-show-tips {
34 | .tips {
35 | display: block;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/nps-mask/nps-mask.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { NpsMaskComponent } from './component/nps-mask.component';
5 |
6 | @NgModule({
7 | declarations: [NpsMaskComponent],
8 | imports: [CommonModule],
9 | exports: [NpsMaskComponent]
10 | })
11 | export class NpsMaskModule {}
12 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/pc-console/debug-theme/debug-theme.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { ThemeService } from '../../../core/services/theme/theme.service';
4 | import StorageUtil from '../../../shared/utils/storage/storage.utils';
5 |
6 | @Component({
7 | selector: 'pc-debug-theme',
8 | template: `
`,
16 | styles: []
17 | })
18 | export class DebugThemeComponent {
19 | code: string;
20 | constructor(private theme: ThemeService) {
21 | const currentTheme = StorageUtil.get('pc_theme');
22 | this.code = JSON.stringify(currentTheme.colors);
23 | }
24 | changeTheme() {
25 | this.theme.changeCurrentThemeColorForDebug(JSON.parse(this.code));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/pc-console/pc-console.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EoNgButtonModule } from 'eo-ng-button';
4 |
5 | import { EoMonacoEditorModule } from '../eo-ui/monaco-editor/monaco.module';
6 | import { DebugThemeComponent } from './debug-theme/debug-theme.component';
7 | import { PcConsoleComponent } from './pc-console/pc-console.component';
8 | @NgModule({
9 | declarations: [PcConsoleComponent, DebugThemeComponent],
10 | imports: [CommonModule, EoNgButtonModule, EoMonacoEditorModule],
11 | exports: [PcConsoleComponent]
12 | })
13 | export class PcConsoleModule {}
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/pc-console/pc-console/pc-console.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | width: 500px;
3 | }
4 |
5 | ::ng-deep {
6 | eo-root {
7 | display: flex;
8 | }
9 |
10 | .console-page + eo-pages {
11 | flex: 1;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/pc-console/pc-console/pc-console.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'pc-console',
5 | template: ``,
6 | styleUrls: ['./pc-console.component.scss']
7 | })
8 | export class PcConsoleComponent {
9 | constructor() {}
10 | }
11 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/system-setting/common/index.ts:
--------------------------------------------------------------------------------
1 | export { DataStorageComponent } from './data-storage.component';
2 | export { LanguageSwticherComponent } from './language-swtcher.component';
3 | export { AboutComponent } from './about.component';
4 | export { TokenComponent } from './token.component';
5 | export { SelectThemeComponent } from './select-theme/select-theme.component';
6 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/system-setting/common/select-theme/select-theme.component.scss:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | border-right: 1px solid var(--border-color);
3 | }
4 |
5 | .navbar {
6 | border-bottom: 1px solid var(--border-color);
7 | }
8 |
9 | .tree {
10 | border-right: 1px solid var(--border-color);
11 | }
12 |
13 | .footer {
14 | border-top: 1px solid var(--border-color);
15 | }
16 |
17 | .theme-container-active {
18 | .theme-block {
19 | border-color: var(--primary-color);
20 | box-shadow: 0 0 10px var(--shadow-color);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/system-setting/common/token.component.scss:
--------------------------------------------------------------------------------
1 | .alert-text {
2 | color: var(--text-color);
3 | }
4 |
5 | .alert-title {
6 | color: #00785a;
7 | }
8 |
9 | .copy-btn {
10 | color: var(--text-color);
11 | }
12 |
--------------------------------------------------------------------------------
/src/browser/src/app/components/system-setting/system-setting.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep {
2 | .eo-system-setting-modal {
3 | width: 600px !important;
4 |
5 | .ant-modal-body {
6 | padding: 0 0 20px;
7 | min-height: 300px;
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/README.md:
--------------------------------------------------------------------------------
1 | # Core module
2 | Reuse module in other project
--------------------------------------------------------------------------------
/src/browser/src/app/core/core.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 |
4 | @NgModule({
5 | declarations: [],
6 | imports: [CommonModule]
7 | })
8 | export class CoreModule {}
9 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/errorHandle.service.ts:
--------------------------------------------------------------------------------
1 | import { ErrorHandler, Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export class GlobalErrorHandler implements ErrorHandler {
5 | handleError(error: any): void {
6 | console.error(error);
7 | const chunkFailedMessage = /Loading chunk [\d]+ failed/;
8 |
9 | if (chunkFailedMessage.test(error.message)) {
10 | window.location.reload();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/feature-control/feature.json:
--------------------------------------------------------------------------------
1 | {
2 | "cloudFeature": true,
3 | "apimanage.test": true,
4 | "apimanage.document": true,
5 | "apimanage.edit": true,
6 | "apimanage.mock": true,
7 | "chatRobot": false
8 | }
9 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './electron/electron.service';
2 | export * from './web/web.service';
3 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/language/language.model.ts:
--------------------------------------------------------------------------------
1 | export const LANGUAGES = [
2 | {
3 | name: 'English',
4 | value: 'en-US',
5 | path: 'en'
6 | },
7 | {
8 | name: '简体中文',
9 | value: 'zh-Hans',
10 | path: 'zh'
11 | }
12 | ];
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/notification.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ModalService } from 'pc/browser/src/app/services/modal.service';
3 | import StorageUtil from 'pc/browser/src/app/shared/utils/storage/storage.utils';
4 |
5 | @Injectable({
6 | providedIn: 'root'
7 | })
8 | export class NotificationService {
9 | constructor(private modal: ModalService) {}
10 | init() {
11 | const hasShow = StorageUtil.get('notification_has_show');
12 | if (hasShow) return;
13 |
14 | const logInfo = {
15 | startTime: new Date('2023-02-07 16:00:00'),
16 | endTime: new Date('2023-02-07 18:00:00')
17 | };
18 | const currentData = new Date();
19 |
20 | //Curent data is greater than end time
21 | if (currentData.getTime() > logInfo.endTime.getTime()) {
22 | return;
23 | }
24 |
25 | //! safari may cause erro when the user first open page,it will show even if the time has passed
26 |
27 | // this.modal.create({
28 | // stayWhenRouterChange: true,
29 | // nzTitle: $localize`Release Notes`,
30 | // nzContent: $localize`There will be downtime updates from ${logInfo.startTime.getHours()}\:00 to ${logInfo.endTime.getHours()}\:00 today, and may be temporarily inaccessible.`
31 | // });
32 | // StorageUtil.set('notification_has_show', true, 60 * 60 * 24);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/browser/src/app/core/services/theme/theme-extension.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { ExtensionService } from '../../../services/extensions/extension.service';
4 | import { ThemeVariableService } from './theme-variable.service';
5 |
6 | @Injectable({
7 | providedIn: 'root'
8 | })
9 | export class ThemeExtensionService {
10 | constructor(private extension: ExtensionService, private themeVariable: ThemeVariableService) {}
11 | getExtensionThemes(coreThemes) {
12 | const result = [];
13 | const features = this.extension.getValidExtensionsByFature('theme');
14 | features.forEach((feature, extensionID) => {
15 | feature.theme.forEach(theme => {
16 | result.push({
17 | label: theme.label,
18 | id: this.getExtensionID(extensionID, theme.id),
19 | isExtension: true,
20 | baseTheme: theme.baseTheme,
21 | colors: this.themeVariable.getColors(theme, coreThemes.find(val => val.id === theme.baseTheme) || coreThemes[0])
22 | });
23 | });
24 | });
25 | return result;
26 | }
27 | getExtensionID(name, themeID) {
28 | return `${name}_${themeID}`;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/local-workspace-tip/local-workspace-tip.component.scss:
--------------------------------------------------------------------------------
1 | .remote-notification {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | height: var(--remote-notification-height, 0);
6 | overflow: hidden;
7 | }
8 |
9 | :host ::ng-deep {
10 | .ant-alert {
11 | width: 100%;
12 | text-align: center;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/breadcrumb/nav-breadcrumb.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 | {{ projectName }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/breadcrumb/nav-breadcrumb.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep {
2 | .btn-breadcrumb-item {
3 | .ant-breadcrumb-separator {
4 | margin-left: 3px;
5 | }
6 | }
7 |
8 | .ant-breadcrumb > nz-breadcrumb-item:last-child,
9 | .ant-breadcrumb-separator {
10 | color: var(--text-color);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/breadcrumb/nav-breadcrumb.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { autorun } from 'mobx';
3 |
4 | import { StoreService } from '../../../shared/store/state.service';
5 |
6 | @Component({
7 | selector: 'eo-nav-breadcrumb',
8 | templateUrl: './nav-breadcrumb.component.html',
9 | styleUrls: ['./nav-breadcrumb.component.scss']
10 | })
11 | export class NavBreadcrumbComponent {
12 | level: 'project' | 'workspace';
13 | projectName: string = 'Default';
14 | projectID;
15 | workspaceID;
16 | constructor(public store: StoreService) {
17 | autorun(() => {
18 | if (this.store.getCurrentProject?.name) {
19 | this.projectName = this.store.getCurrentProject.name;
20 | this.projectID = this.store.getCurrentProject.projectUuid;
21 | }
22 | this.workspaceID = this.store.getCurrentWorkspaceUuid;
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/breadcrumb/select-workspace/select-workspace.component.scss:
--------------------------------------------------------------------------------
1 | .workspace-title {
2 | padding: 0 10px;
3 | font-size: 12px;
4 | font-weight: bold;
5 | margin-bottom: 5px;
6 | }
7 |
8 | .workspace-item {
9 | padding: 0 20px;
10 | height: 30px;
11 | }
12 |
13 | .active-item {
14 | color: var(--dropdown-item-hover-text-color);
15 | background-color: var(--dropdown-item-hover-background-color);
16 |
17 | &:hover {
18 | color: var(--dropdown-item-hover-text-color);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/help-dropdown/help-dropdown.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | import { ElectronService, WebService } from '../../../core/services';
4 |
5 | @Component({
6 | selector: 'pc-help-dropdown',
7 | template: `
10 |
11 |
15 | `,
16 | styles: []
17 | })
18 | export class HelpDropdownComponent {
19 | constructor(public web: WebService) {}
20 | }
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/navbar/navbar.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | --button-text-text-color: var(--layout-header-text-color);
3 | --button-text-hover-text-color: var(--layout-header-text-color);
4 | }
5 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/page-not-found/page-not-find.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EoNgButtonModule } from 'eo-ng-button';
4 | import { NzResultModule } from 'ng-zorro-antd/result';
5 |
6 | import { PageNotFoundComponent } from './page-not-found.component';
7 |
8 | @NgModule({
9 | declarations: [PageNotFoundComponent],
10 | imports: [CommonModule, EoNgButtonModule, NzResultModule]
11 | })
12 | export class PageNotFindModule {}
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/page-not-found/page-not-found.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/page-not-found/page-not-found.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/layouts/page-not-found/page-not-found.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/page-not-found/page-not-found.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { Router } from '@angular/router';
3 |
4 | @Component({
5 | selector: 'eo-page-not-found',
6 | templateUrl: './page-not-found.component.html',
7 | styleUrls: ['./page-not-found.component.scss']
8 | })
9 | export class PageNotFoundComponent {
10 | constructor(private router: Router) {}
11 |
12 | backHome() {
13 | this.router.navigate(['/home']);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/sidebar/sidebar.component.html:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/sidebar/sidebar.model.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Sidebar offical module
3 | */
4 | export interface SidebarModuleInfo {
5 | /**
6 | * icon or logo image
7 | **/
8 | logo: string;
9 | /**
10 | * unique extension id
11 | **/
12 | id: string;
13 | /**
14 | * showname
15 | **/
16 | title: string;
17 | /**
18 | * is offcial app
19 | **/
20 | isOffical: boolean;
21 | /**
22 | * module route,click sidebar will navigate this route
23 | **/
24 | route: string;
25 | /**
26 | * sidebar active when match activeRoute
27 | **/
28 | activeRoute: string;
29 | }
30 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/sidebar/sidebar.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Subject } from 'rxjs';
3 |
4 | import { SettingService } from '../../components/system-setting/settings.service';
5 | import StorageUtil from '../../shared/utils/storage/storage.utils';
6 |
7 | @Injectable({
8 | providedIn: 'root'
9 | })
10 | export class SidebarService {
11 | collapsed;
12 | visible = true;
13 | private selectKey = 'side_bar_selected';
14 | currentID: string = StorageUtil.get(this.selectKey);
15 | private collapsedChanged$: Subject = new Subject();
16 | constructor(private setting: SettingService) {
17 | this.collapsed = this.setting.get('workbench.sidebar.shrink');
18 | }
19 | getCollapsed() {
20 | return this.collapsed;
21 | }
22 | setModule(module) {
23 | this.currentID = module;
24 | StorageUtil.set(this.selectKey, module.id);
25 | }
26 | toggleCollapsed() {
27 | this.collapsed = !this.collapsed;
28 | this.collapsedChanged$.next(this.collapsed);
29 | this.setting.set('workbench.sidebar.shrink', this.collapsed);
30 | }
31 | onCollapsedChanged = function () {
32 | return this.collapsedChanged$.pipe();
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/toolbar/toolbar.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | z-index: 10;
3 | }
4 |
5 | .app_toolbar {
6 | height: var(--layout-footer-height);
7 | border-top-width: 1px;
8 | border-top-style: solid;
9 | border-color: var(--border-color);
10 | background-color: var(--layout-footer-background-color);
11 | width: 100%;
12 | z-index: 20;
13 | color: var(--layout-footer-text-color);
14 |
15 | .ant-btn-text {
16 | color: var(--layout-footer-text-color);
17 | height: calc(var(--layout-footer-height) - 4px);
18 |
19 | &:hover,
20 | &:focus,
21 | &:active {
22 | background-color: var(--layout-footer-item-hover-background-color);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/layouts/toolbar/toolbar.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { ChatgptRobotComponent } from 'pc/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component';
4 |
5 | import { SharedModule } from '../../shared/shared.module';
6 | import { ToolbarComponent } from './toolbar.component';
7 |
8 | const COMPONENTS = [ToolbarComponent];
9 | @NgModule({
10 | declarations: [...COMPONENTS],
11 | imports: [CommonModule, SharedModule],
12 | providers: [],
13 | exports: [...COMPONENTS]
14 | })
15 | export class ToolbarModule {}
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/ai-input-group/ai-input-group.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .ai-input-group {
3 | .ant-input-affix-wrapper {
4 | background: linear-gradient(266.83deg, rgb(181 87 249 / 14%) 8.65%, rgb(115 113 252 / 8%) 94.57%);
5 |
6 | eo-iconpark-icon svg {
7 | color: #b557f9;
8 | }
9 |
10 | .ant-input {
11 | background: transparent !important;
12 | }
13 | }
14 |
15 | eo-ng-input-group,
16 | .ant-input,
17 | .generateBtn {
18 | height: 40px !important;
19 | box-sizing: border-box;
20 | }
21 | }
22 |
23 | .param-box-header {
24 | padding: 5px 15px;
25 | box-sizing: border-box;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | position: fixed;
3 | right: 25px;
4 | bottom: 25px;
5 | z-index: 10;
6 | }
7 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/extension/download-count-formater.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({ name: 'downloadCountFormater' })
4 | export class DownloadCountFormaterPipe implements PipeTransform {
5 | constructor() {}
6 |
7 | transform(count = 0) {
8 | if (count > 999) {
9 | return `${(count / 1000).toFixed(1)}K`;
10 | } else if (count > 9999) {
11 | return `${(count / 10000).toFixed(1)}M`;
12 | } else {
13 | return count;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/extension/extension-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | // import { ExtensionDetailComponent } from './detail/extension-detail.component';
5 | import { ExtensionComponent } from './extension.component';
6 | import { ExtensionListComponent } from './list/extension-list.component';
7 |
8 | const routes: Routes = [
9 | {
10 | path: '',
11 | component: ExtensionComponent,
12 | children: [
13 | {
14 | path: '',
15 | redirectTo: 'list',
16 | pathMatch: 'full'
17 | },
18 | {
19 | path: 'list',
20 | component: ExtensionListComponent
21 | }
22 | // {
23 | // path: 'detail',
24 | // component: ExtensionDetailComponent,
25 | // },
26 | ]
27 | }
28 | ];
29 |
30 | @NgModule({
31 | imports: [RouterModule.forChild(routes)],
32 | exports: [RouterModule]
33 | })
34 | export class ExtensionRoutingModule {}
35 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/extension/extension.component.scss:
--------------------------------------------------------------------------------
1 | @import '../../workspace/project/api/components/group/api-group-tree.component';
2 |
3 | :host ::ng-deep {
4 | display: flex;
5 | width: 100%;
6 | height: 100%;
7 |
8 | eo-ng-tree-default {
9 | height: calc(62vh - 32px);
10 | overflow: auto;
11 | display: block;
12 |
13 | .ant-tree-switcher {
14 | display: none;
15 | }
16 | }
17 |
18 | eo-ng-auto-complete input {
19 | border-radius: 999px;
20 | }
21 |
22 | .ant-btn-link {
23 | padding: 0;
24 | }
25 |
26 | .left {
27 | width: 250px;
28 | height: 70vh; // * necessary
29 | border-right: 1px solid var(--border-color);
30 |
31 | .plugin-link {
32 | &:hover,
33 | &.active {
34 | color: var(--primary-color);
35 | cursor: pointer;
36 | }
37 |
38 | &:hover {
39 | background-color: rgb(0 0 0 / 10%);
40 | }
41 | }
42 | }
43 |
44 | .right {
45 | height: 100%;
46 | overflow: hidden;
47 | }
48 | }
49 |
50 | ::ng-deep {
51 | .eo-extension-modal {
52 | .ant-modal-body {
53 | padding: 0;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/extension/list/extension-list.component.scss:
--------------------------------------------------------------------------------
1 | .desc {
2 | overflow: hidden;
3 | display: -webkit-box;
4 | text-overflow: ellipsis;
5 | -webkit-box-orient: vertical;
6 | -webkit-line-clamp: 4;
7 | }
8 |
9 | .extension-list {
10 | height: 70vh;
11 | }
12 |
13 | .plugin-block {
14 | user-select: none;
15 | cursor: pointer;
16 | }
17 |
18 | @media (max-width: 1279.9px) {
19 | .grid-cols-4 {
20 | grid-template-columns: repeat(3, minmax(0, 1fr));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/newbie-guide/newPeopleGuide.md:
--------------------------------------------------------------------------------
1 | ### Postcat 是一个强大的免费、跨平台、可扩展的 API 工具!
2 |
3 | **和 Postman 等产品相比,它拥有以下备受喜爱的特性:**
4 |
5 | 1.❤️ 免费的团队协作:好的产品就应该更多人一起使用,我们不限制免费人数!
6 |
7 |
8 |
9 | 2.🚀 极具扩展性的插件系统:一键安装 ChatGPT、主题等各类插件来定制属于你的 API 开发工具~
10 |
11 |
12 |
13 | 3.😊 优秀的用户体验:一切都可以更简单,让我们的工作更高效~
14 |
15 | **当然,我们还包括众多实用的基本功能:**
16 |
17 | 1.🍩 多协议支持:REST、Websocket 等协议(即将支持 GraphQL、gRPC、TCP、UDP)
18 |
19 | 2.📕 API 设计、文档展示、分享
20 |
21 | 3.⚡ API 测试
22 |
23 | 4.🎭 Mock Server
24 |
25 | 5.🙌 环境管理
26 |
27 |
28 | ...
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/newbie-guide/newbie-guide.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep {
2 | .model-article {
3 | .ant-modal-footer {
4 | text-align: center;
5 | }
6 |
7 | h3 {
8 | margin-top: 20px;
9 | font-weight: bold;
10 | color: var(--primary-color) !important;
11 | }
12 |
13 | p {
14 | display: block;
15 | font-size: 12px;
16 | line-height: 1.5rem !important;
17 | margin: 5px;
18 | }
19 |
20 | strong {
21 | display: inline-block;
22 | margin-top: 20px !important;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/newbie-guide/newbie-guide.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { NewbieGuideComponent } from './newbie-guide.component';
4 |
5 | describe('NewbieGuideComponent', () => {
6 | let component: NewbieGuideComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [NewbieGuideComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(NewbieGuideComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/newbie-guide/newbie-guide.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import markdownIt from 'markdown-it/dist/markdown-it';
3 | import NEWBIE_GUIDE from 'pc/browser/src/app/shared/constans/newbie-guide';
4 |
5 | @Component({
6 | standalone: true,
7 | selector: 'pc-newbie-guide',
8 | template: `
9 |
10 |

11 |
12 |
13 | `,
14 | styleUrls: ['./newbie-guide.component.scss']
15 | })
16 | export class NewbieGuideComponent {
17 | async ngAfterViewInit() {
18 | let md = new markdownIt();
19 | const newbieGuideHtml = md.render(NEWBIE_GUIDE);
20 |
21 | document.getElementById('newbie-guide-markdown').innerHTML = newbieGuideHtml;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/update-log/update-log.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep {
2 | .model-article {
3 | .ant-modal-footer {
4 | text-align: center;
5 | }
6 |
7 | h3 {
8 | margin-top: 20px;
9 | font-weight: bold;
10 | color: var(--primary-color) !important;
11 | }
12 |
13 | p {
14 | display: block;
15 | font-size: 12px;
16 | line-height: 30px !important;
17 | }
18 |
19 | strong {
20 | display: inline-block;
21 | margin-top: 20px !important;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/update-log/update-log.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { UpdateLogComponent } from './update-log.component';
4 |
5 | describe('UpdateLogComponent', () => {
6 | let component: UpdateLogComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [UpdateLogComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(UpdateLogComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/components/model-article/update-log/update-log.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import markdownIt from 'markdown-it/dist/markdown-it';
3 | import UPDATE_LOG from 'pc/browser/src/app/shared/constans/update-log';
4 |
5 | @Component({
6 | selector: 'pc-update-log',
7 | template: `
8 |
11 | `,
12 | styleUrls: ['./update-log.component.scss']
13 | })
14 | export class UpdateLogComponent {
15 | async ngAfterViewInit() {
16 | let md = new markdownIt();
17 | const html = md.render(UPDATE_LOG);
18 |
19 | const updateLogHtml = html.replace(/
]*src=['"]([^'"]+)[^>]*>/gi, match =>
20 | match.replace(/
{
6 | let component: AiToApiComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [AiToApiComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(AiToApiComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/pages-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | import { PagesComponent } from './pages.component';
5 | import { RedirectWorkspace } from './services/redirect.services';
6 |
7 | const routes: Routes = [
8 | {
9 | path: '',
10 | component: PagesComponent,
11 | children: [
12 | {
13 | path: '',
14 | redirectTo: 'workspace',
15 | pathMatch: 'full'
16 | },
17 | {
18 | path: 'workspace',
19 | canActivate: [RedirectWorkspace],
20 | runGuardsAndResolvers: 'always',
21 | loadChildren: () => import('./workspace/workspace.module').then(m => m.WorkspaceModule)
22 | },
23 | {
24 | path: 'extension',
25 | loadChildren: () => import('./components/extension/extension.module').then(m => m.ExtensionModule)
26 | }
27 | ]
28 | }
29 | ];
30 |
31 | @NgModule({
32 | imports: [RouterModule.forChild(routes)],
33 | providers: [RedirectWorkspace],
34 | exports: [RouterModule]
35 | })
36 | export class PagesRoutingModule {}
37 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/pages.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | We use cookies
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/pages.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 |
4 | /**
5 | ! can't be delete,for pc-debug-component
6 | */
7 | flex: 1;
8 | flex-direction: column;
9 | height: 100vh;
10 | overflow: hidden;
11 | }
12 |
13 | .home-container {
14 | height: 100%;
15 | }
16 |
17 | .home {
18 | background-color: var(--background-color);
19 | color: var(--text-color);
20 | overflow-x: hidden;
21 | }
22 |
23 | :host ::ng-deep {
24 | .home-container {
25 | --body-height: calc(100vh - var(--layout-header-height) - var(--layout-footer-height) - var(--remote-notification-height, 0));
26 |
27 | height: var(--body-height);
28 |
29 | router-outlet + * {
30 | display: block;
31 | height: 100%;
32 |
33 | // height: var(--body-height);
34 | }
35 | }
36 | }
37 |
38 | ::ng-deep {
39 | .cookie-notification {
40 | width: 200px;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/share-project/share-project.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { API_TABS } from 'pc/browser/src/app/pages/workspace/project/api/api-tab.service';
4 | import { ApiModule } from 'pc/browser/src/app/pages/workspace/project/api/api.module';
5 | import { BASIC_TABS_INFO, TabsConfig } from 'pc/browser/src/app/pages/workspace/project/api/constants/api.model';
6 |
7 | import { NavbarModule } from '../../layouts/navbar/navbar.module';
8 | import { SharedModule } from '../../shared/shared.module';
9 | import { ShareRoutingModule } from './share-routing.module';
10 | import { ShareComponent } from './view/share-project.component';
11 |
12 | const tabs = API_TABS.map(val => ({ ...val, pathname: `/share${val.pathname}` }));
13 |
14 | @NgModule({
15 | imports: [ShareRoutingModule, NavbarModule, CommonModule, SharedModule, ApiModule],
16 | declarations: [ShareComponent],
17 | providers: [
18 | {
19 | provide: BASIC_TABS_INFO,
20 | useValue: {
21 | BASIC_TABS: tabs,
22 | pathByName: tabs.reduce((acc, curr) => ({ ...acc, [curr.uniqueName]: curr.pathname }), {})
23 | } as TabsConfig
24 | }
25 | ]
26 | })
27 | export class ShareProjectModule {}
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/share-project/view/share-project.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .home-container {
3 | --body-height: calc(100vh - var(--layout-header-height));
4 |
5 | height: var(--body-height);
6 |
7 | eo-api {
8 | display: block;
9 | height: 100%;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/share-project/view/share-project.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ActivatedRoute } from '@angular/router';
3 | import { StoreService } from 'pc/browser/src/app/shared/store/state.service';
4 |
5 | @Component({
6 | selector: 'eo-share',
7 | template: ``,
11 | styleUrls: ['./share-project.component.scss']
12 | })
13 | export class ShareComponent implements OnInit {
14 | constructor(private route: ActivatedRoute, private store: StoreService) {}
15 | async ngOnInit(): Promise {
16 | this.route.queryParams.subscribe(({ shareId }) => {
17 | this.store.setShareId(shareId);
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/overview/project-list/project-list.component.scss:
--------------------------------------------------------------------------------
1 | @mixin hover-item {
2 | transition: border 0.2s;
3 |
4 | &:hover {
5 | border-color: var(--button-primary-background-color);
6 |
7 | .operate-btn-list {
8 | display: flex;
9 | }
10 | }
11 | }
12 |
13 | :host {
14 | .operate-btn-list {
15 | display: none;
16 | }
17 |
18 | .ant-card {
19 | @include hover-item;
20 | }
21 |
22 | ::ng-deep {
23 | .ant-list-items {
24 | .ant-list-item {
25 | @apply px-[10px] rounded-[3px];
26 | @include hover-item;
27 |
28 | border: 1px solid transparent;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/overview/project-list/project-list.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { FormsModule } from '@angular/forms';
3 | import { RouterModule } from '@angular/router';
4 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
5 | import { NzCardModule } from 'ng-zorro-antd/card';
6 | import { NzEmptyModule } from 'ng-zorro-antd/empty';
7 | import { NzFormModule } from 'ng-zorro-antd/form';
8 | import { OperateProjectFormComponent } from 'pc/browser/src/app/pages/workspace/project/components/operate-project-form.compoent';
9 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
10 |
11 | import { ProjectListComponent } from './project-list.component';
12 | import { ProjectListService } from './project-list.service';
13 |
14 | @NgModule({
15 | imports: [
16 | NzEmptyModule,
17 | RouterModule.forChild([
18 | {
19 | path: '',
20 | component: ProjectListComponent
21 | }
22 | ]),
23 | NzAvatarModule,
24 | NzCardModule,
25 | FormsModule,
26 | NzFormModule,
27 | SharedModule
28 | ],
29 | declarations: [ProjectListComponent, OperateProjectFormComponent],
30 | exports: [ProjectListComponent]
31 | })
32 | export class ProjectListModule {}
33 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/overview/workspace-overview.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | eo-workspace-member,
3 | eo-workspace-setting,
4 | eo-project-list {
5 | height: calc(var(--body-height) - 127px) !important;
6 | padding-top: 15px;
7 | display: block;
8 | overflow: auto;
9 | }
10 |
11 | eo-workspace-member,
12 | eo-workspace-setting {
13 | width: 50%;
14 | margin: 0 auto;
15 | }
16 | }
17 |
18 | .list-type-switcher {
19 | .active {
20 | color: var(--button-primary-background-color);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/action/action.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/action/action.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep .action-contain {
2 | .ant-tabs-tabpane {
3 | padding-left: unset !important;
4 | border-left: 1px solid var(--border-color);
5 | }
6 |
7 | .ant-tabs-content-holder {
8 | height: 100% !important;
9 | }
10 |
11 | .ant-tabs-tab {
12 | text-align: left !important;
13 | margin: 5px !important;
14 | border: 1px solid transparent;
15 | padding: 5px 20px !important;
16 | }
17 |
18 | .ant-tabs-tab:hover,
19 | .ant-tabs-tab-active {
20 | border: 1px solid var(--border-color);
21 | background: var(--bar-background-color);
22 | border-radius: 3px;
23 | }
24 |
25 | .ant-tabs > .ant-tabs-nav .ant-tabs-nav-list {
26 | align-items: unset;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/action/action.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ActionComponent } from './action.component';
4 |
5 | describe('ActionComponent', () => {
6 | let component: ActionComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ActionComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(ActionComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/action/action.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | interface OperationType {
4 | title: string;
5 | type: string;
6 | }
7 |
8 | @Component({
9 | selector: 'pc-action',
10 | templateUrl: './action.component.html',
11 | styleUrls: ['./action.component.scss']
12 | })
13 | export class ActionComponent {
14 | selectedIndex: number;
15 | operationArr: OperationType[] = [
16 | {
17 | title: $localize`Pre-request Script`,
18 | type: 'pre'
19 | },
20 | {
21 | title: $localize`After-response Script`,
22 | type: 'after'
23 | }
24 | ];
25 | type: string = 'pre';
26 |
27 | typeChange(type) {
28 | if (type === this.type) return;
29 | this.type = type;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/api-script/api-script.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | height: 100%;
4 | }
5 |
6 | :host ::ng-deep {
7 | nz-tree-node-title {
8 | height: 100%;
9 | }
10 |
11 | .ant-tree .ant-tree-node-content-wrapper {
12 | line-height: 28px;
13 | }
14 |
15 | .eo-api-script {
16 | .ant-tree-switcher {
17 | width: 0;
18 | }
19 |
20 | .ant-tree-treenode-switcher-open {
21 | .ant-tree-indent {
22 | width: 0;
23 | }
24 |
25 | .ant-tree-switcher {
26 | width: 20px;
27 | }
28 | }
29 |
30 | .ant-tree-treenode-switcher-close {
31 | .ant-tree-indent {
32 | width: 0;
33 | }
34 |
35 | .ant-tree-switcher {
36 | width: 20px;
37 | }
38 | }
39 | }
40 |
41 | .ant-tree-indent {
42 | width: 20px;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/api-test-result-header/api-test-result-header.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | No Headers
4 |
5 |
6 |
7 | -
8 | {{ item.name }}: {{ item.value }}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/api-test-result-header/api-test-result-header.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: block;
3 | }
4 |
5 | ul {
6 | list-style-type: none;
7 | }
8 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/api-test-result-header/api-test-result-header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | import { ApiTestResHeader } from '../../service/test-server/test-server.model';
4 |
5 | @Component({
6 | selector: 'eo-api-test-result-header',
7 | templateUrl: './api-test-result-header.component.html',
8 | styleUrls: ['./api-test-result-header.component.scss']
9 | })
10 | export class ApiTestResultHeaderComponent {
11 | @Input() model: ApiTestResHeader[];
12 | constructor() {}
13 | }
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | display: block;
3 | height: 100%;
4 |
5 | .ant-skeleton-content {
6 | padding: 10px;
7 | }
8 |
9 | .group-tree {
10 | overflow: hidden auto;
11 | background-color: var(--tree-background-color);
12 | height: calc(100% - 52px);
13 | }
14 |
15 | .dragging::after {
16 | display: none;
17 | animation: initial;
18 | }
19 |
20 | .ant-tree-indent-unit {
21 | width: 24px;
22 | }
23 |
24 | .ant-tree-switcher {
25 | width: 24px;
26 | }
27 |
28 | .width-box {
29 | width: 24px;
30 | height: 24px;
31 | }
32 | }
33 |
34 | .ant-dropdown-menu {
35 | min-width: 100px;
36 | }
37 |
38 | .method-text {
39 | max-width: 50px;
40 | min-width: 32px;
41 | font-size: 14px;
42 | font-weight: 700;
43 | flex-shrink: 0;
44 | }
45 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/group/api-group-tree.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[apiGroupTree]'
5 | })
6 | export class ApiGroupTreeDirective implements OnChanges {
7 | @Input() node?: any;
8 |
9 | treeNodeTitle: HTMLDivElement;
10 | constructor(private readonly el: ElementRef) {}
11 |
12 | ngOnChanges(): void {
13 | this.treeNodeTitle = this.el.nativeElement.closest('nz-tree-node-title');
14 | if (this.treeNodeTitle) {
15 | this.treeNodeTitle.style.display = 'block';
16 | this.treeNodeTitle.style.width = `calc(100% - 24px * ${this.node.level + 1})`;
17 | // const treenode = this.treeNodeTitle.closest('.ant-tree-treenode');
18 | // treenode.removeEventListener('click', this.clickTitle);
19 | // treenode.addEventListener('click', this.clickTitle);
20 | }
21 | }
22 |
23 | // clickTitle = (e) => {
24 | // const isIndent = e.target.closest('.ant-tree-indent');
25 | // if (isIndent) {
26 | // this.treeNodeTitle.click();
27 | // }
28 | // };
29 | }
30 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.scss:
--------------------------------------------------------------------------------
1 | @import '../group/api-group-tree.component';
2 |
3 | .history-body {
4 | height: 100%;
5 | }
6 |
7 | eo-ng-tree-default {
8 | height: calc(100% - 40px);
9 | }
10 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/params-import/params-import.component.scss:
--------------------------------------------------------------------------------
1 | .menu {
2 | display: inline-flex;
3 | align-items: center;
4 | justify-content: center;
5 | user-select: none;
6 | }
7 |
8 | .eg {
9 | font-size: 12px;
10 | color: #888;
11 | }
12 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/response-steps/response-steps.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/response-steps/response-steps.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | .ant-steps-item-active {
3 | ::ng-deep .ant-steps-item-container .ant-steps-item-content .ant-steps-item-title {
4 | color: var(--primary-color);
5 | }
6 | }
7 |
8 | .icon-contain-success {
9 | border: 1px solid lawngreen;
10 | border-radius: 50%;
11 |
12 | ::ng-deep eo-iconpark-icon svg {
13 | color: lawngreen !important;
14 | }
15 | }
16 |
17 | .icon-contain-fail {
18 | border: 1px solid darkred;
19 | border-radius: 50%;
20 |
21 | ::ng-deep eo-iconpark-icon svg {
22 | color: darkred !important;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/response-steps/response-steps.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { ResponseStepsComponent } from './response-steps.component';
4 |
5 | describe('ResponseStepsComponent', () => {
6 | let component: ResponseStepsComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [ResponseStepsComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(ResponseStepsComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/components/response-steps/response-steps.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 | import { NzIconModule } from 'ng-zorro-antd/icon';
3 | import { NzSpinModule } from 'ng-zorro-antd/spin';
4 | import { NzStepsModule } from 'ng-zorro-antd/steps';
5 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
6 |
7 | export interface stepType {
8 | status: 'success' | 'fail' | 'loading' | 'non-execution';
9 | title: string;
10 | description: string;
11 | }
12 |
13 | @Component({
14 | standalone: true,
15 | selector: 'pc-response-steps',
16 | templateUrl: './response-steps.component.html',
17 | styleUrls: ['./response-steps.component.scss'],
18 | imports: [NzStepsModule, SharedModule, NzSpinModule, NzIconModule]
19 | })
20 | export class ResponseStepsComponent {
21 | @Input() currentIndex: number = 1;
22 |
23 | @Input() stepArr: stepType[] = [
24 | {
25 | title: 'aa',
26 | status: 'loading',
27 | description: '你好呀'
28 | },
29 | {
30 | title: 'bb',
31 | status: 'success',
32 | description: '你好呀'
33 | },
34 | {
35 | title: 'cc',
36 | status: 'fail',
37 | description: '你好呀'
38 | }
39 | ];
40 | }
41 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/constants/auth.model.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Auth Type
3 | */
4 | export enum AuthTypeValue {
5 | Inherited = 'inherited',
6 | None = 'none'
7 | //Other auth exetnsion id
8 | }
9 | export const NONE_AUTH_OPTION = {
10 | name: AuthTypeValue.None,
11 | label: $localize`No Auth`
12 | };
13 |
14 | export const INHERIT_AUTH_OPTION = {
15 | name: AuthTypeValue.Inherited,
16 | label: $localize`Inherit auth from parent`
17 | };
18 |
19 | export type AuthInfo = {
20 | authType: AuthTypeValue | string;
21 | isInherited?: isInherited;
22 | authInfo: Record;
23 | };
24 | export enum isInherited {
25 | inherit = 1,
26 | notInherit = 0
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/env/env-edit/env-edit.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/pages/workspace/project/api/env/env-edit/env-edit.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/env/env-list/env-list.component.scss:
--------------------------------------------------------------------------------
1 | .env-main {
2 | height: 100%;
3 | }
4 |
5 | .env-list {
6 | height: calc(100% - var(--tree-item-height));
7 | overflow: auto;
8 | }
9 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss:
--------------------------------------------------------------------------------
1 | // nz-divider {
2 | // margin: 0.1em 0;
3 | // }
4 |
5 | :host ::ng-deep {
6 | eo-ng-select eo-ng-select-top-control {
7 | border: none !important;
8 | }
9 |
10 | .ant-select {
11 | width: 140px;
12 | }
13 |
14 | .ant-select-selector {
15 | box-shadow: unset !important;
16 | }
17 | }
18 |
19 | .content {
20 | color: var(--text-color);
21 | }
22 |
23 | .title {
24 | background-color: var(--item-active-background-color);
25 | color: var(--text-color);
26 | }
27 |
28 | ::ng-deep {
29 | .env-selector-dropdown {
30 | width: 224px;
31 | }
32 |
33 | .preview-env .ant-popover-inner-content {
34 | padding: 0;
35 | }
36 |
37 | .env-select {
38 | .ant-select .clear-btn:hover eo-iconpark-icon {
39 | background-color: var(--item-active-background-color);
40 | }
41 |
42 | .clear-btn eo-iconpark-icon {
43 | padding: 3px;
44 | border-radius: 50%;
45 |
46 | svg {
47 | color: var(--danger-color) !important;
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/env/env.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule } from '@angular/router';
3 | import { EoNgTreeModule } from 'eo-ng-tree';
4 | import { NzEmptyModule } from 'ng-zorro-antd/empty';
5 | import { NzTypographyModule } from 'ng-zorro-antd/typography';
6 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
7 |
8 | import { EoTableProModule } from '../../../../../components/eo-ui/table-pro/table-pro.module';
9 | import { EnvEditComponent } from './env-edit/env-edit.component';
10 | import { EnvListComponent } from './env-list/env-list.component';
11 | import { EnvSelectComponent } from './env-select/env-select.component';
12 |
13 | const ANTDMODULES = [EoTableProModule];
14 | const COMPONENTA = [EnvListComponent, EnvSelectComponent];
15 | @NgModule({
16 | declarations: [...COMPONENTA, EnvEditComponent],
17 | imports: [
18 | RouterModule.forChild([
19 | {
20 | path: 'edit',
21 | component: EnvEditComponent
22 | }
23 | ]),
24 | NzTypographyModule,
25 | EoNgTreeModule,
26 | NzEmptyModule,
27 | SharedModule,
28 | ...ANTDMODULES
29 | ],
30 | exports: [...COMPONENTA]
31 | })
32 | export class EnvModule {}
33 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/graph-ql/graph-ql.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { GraphQlTestComponent } from './test/graph-ql-test.component';
5 |
6 | @NgModule({
7 | declarations: [GraphQlTestComponent],
8 | imports: [CommonModule]
9 | })
10 | export class GraphQlModule {}
11 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.html:
--------------------------------------------------------------------------------
1 | graph-ql-test works!
2 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'eo-graph-ql-test',
5 | templateUrl: './graph-ql-test.component.html',
6 | styleUrls: ['./graph-ql-test.component.scss']
7 | })
8 | export class GraphQlTestComponent {
9 | constructor() {}
10 | }
11 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/group-edit/group.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .ant-tabs-nav {
3 | margin-left: -10px;
4 | }
5 |
6 | .ant-form-inline .ant-form-item-with-help {
7 | margin-bottom: 0;
8 | }
9 | }
10 |
11 | .top-save-bar {
12 | top: -21px;
13 | background-color: var(--background-color);
14 | border-bottom: none;
15 | margin: 15px 0;
16 | padding-left: 0;
17 | height: auto;
18 |
19 | &.is-pinned {
20 | padding-top: 1px;
21 | border-bottom: 1px solid var(--border-color);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/group-edit/group.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule } from '@angular/router';
3 | import { EoNgTabsModule } from 'eo-ng-tabs';
4 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
5 |
6 | import { GroupComponent } from './group.component';
7 |
8 | @NgModule({
9 | declarations: [GroupComponent],
10 | imports: [
11 | RouterModule.forChild([
12 | {
13 | path: 'edit',
14 | component: GroupComponent
15 | }
16 | ]),
17 | SharedModule,
18 | EoNgTabsModule
19 | ],
20 | exports: [GroupComponent]
21 | })
22 | export class GroupModule {}
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/grpc/grpc-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { GrpcComponent } from './grpc.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | component: GrpcComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [RouterModule.forChild(routes)],
15 | exports: [RouterModule]
16 | })
17 | export class GrpcRoutingModule {}
18 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/grpc/grpc.component.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/grpc/grpc.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/pages/workspace/project/api/grpc/grpc.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/grpc/grpc.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
3 |
4 | @Component({
5 | selector: 'eo-grpc',
6 | templateUrl: './grpc.component.html',
7 | styleUrls: ['./grpc.component.scss']
8 | })
9 | export class GrpcComponent implements OnInit {
10 | validateForm!: UntypedFormGroup;
11 |
12 | submitForm(): void {
13 | console.log('submit', this.validateForm.value);
14 | }
15 |
16 | constructor(private fb: UntypedFormBuilder) {}
17 |
18 | ngOnInit(): void {
19 | this.validateForm = this.fb.group({
20 | url: [null, [Validators.required]],
21 | serviceName: [null, [Validators.required]],
22 | methodName: [null, [Validators.required]]
23 | });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/grpc/grpc.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
5 |
6 | import { GrpcRoutingModule } from './grpc-routing.module';
7 | import { GrpcComponent } from './grpc.component';
8 |
9 | @NgModule({
10 | imports: [SharedModule, FormsModule, CommonModule, GrpcRoutingModule],
11 | declarations: [GrpcComponent]
12 | })
13 | export class GrpcModule {}
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/detail/api-detail.component.scss:
--------------------------------------------------------------------------------
1 | .http,
2 | .https {
3 | background-color: rgb(6 125 219 / 100%);
4 | border: 1px solid rgb(6 125 219 / 100%);
5 | color: #fff;
6 | }
7 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/detail/api-detail.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | @Injectable()
4 | export class ApiDetailService {
5 | constructor() {}
6 | }
7 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/detail/body/api-detail-body.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 | {{ item.paramAttr.example }}
13 |
14 |
15 |
23 |
24 |
25 | {{ model[0].binaryRawData }}
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit.component.scss:
--------------------------------------------------------------------------------
1 | .ant-input-group.ant-input-group-compact {
2 | display: flex;
3 | }
4 |
5 | .ant-select {
6 | height: 32px;
7 | }
8 |
9 | eo-api-edit-body {
10 | display: block;
11 | }
12 |
13 | ::ng-deep {
14 | .group-select.ant-select:not(.ant-select-customize-input) .ant-select-selector {
15 | border-radius: var(--border-radius) 0 0 var(--border-radius);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/edit/body/api-edit-body.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/pages/workspace/project/api/http/edit/body/api-edit-body.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/edit/extra-setting/api-params-extra-setting.component.scss:
--------------------------------------------------------------------------------
1 | .ant-table-thead > tr > th {
2 | color: var(--table-header-text-color);
3 | background-color: var(--table-header-background-color);
4 | border-color: var(--table-border-color);
5 | }
6 |
7 | .example-container {
8 | border: 1px solid var(--table-border-color);
9 | }
10 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/mock/mock.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep .mockPopover .ant-popover-inner-content {
2 | color: #fff !important;
3 | }
4 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/mock/mock.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { MockComponent } from './mock.component';
4 |
5 | describe('MockComponent', () => {
6 | let component: MockComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async () => {
10 | await TestBed.configureTestingModule({
11 | declarations: [MockComponent]
12 | }).compileComponents();
13 |
14 | fixture = TestBed.createComponent(MockComponent);
15 | component = fixture.componentInstance;
16 | fixture.detectChanges();
17 | });
18 |
19 | it('should create', () => {
20 | expect(component).toBeTruthy();
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/mock/mock.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { RouterModule } from '@angular/router';
4 | import { NzResultModule } from 'ng-zorro-antd/result';
5 | import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
6 | import { DownloadClientComponent } from 'pc/browser/src/app/components/download-client/download-client.component';
7 | import { EoMonacoEditorModule } from 'pc/browser/src/app/components/eo-ui/monaco-editor/monaco.module';
8 | import { MockComponent } from 'pc/browser/src/app/pages/workspace/project/api/http/mock/mock.component';
9 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
10 |
11 | @NgModule({
12 | declarations: [MockComponent],
13 | imports: [
14 | RouterModule.forChild([
15 | {
16 | path: '',
17 | component: MockComponent
18 | }
19 | ]),
20 | CommonModule,
21 | SharedModule,
22 | EoMonacoEditorModule,
23 | DownloadClientComponent,
24 | NzResultModule,
25 | NzToolTipModule
26 | ]
27 | })
28 | export class MockModule {}
29 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/body/api-test-body.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex !important;
3 | flex-direction: column;
4 | height: 100%;
5 | overflow: hidden;
6 | }
7 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/result-request-body/api-test-result-request-body.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | No Request Body
4 |
5 |
6 |
7 |
8 |
9 | -
10 | {{ item.name }}: {{ item.value }}
11 |
12 |
13 |
14 |
22 |
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/result-request-body/api-test-result-request-body.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .ant-code-editor {
3 | height: calc(var(--bottom-height) - 128px);
4 | }
5 | }
6 |
7 | :host {
8 | display: block;
9 | }
10 |
11 | ul {
12 | list-style-type: none;
13 | }
14 |
15 | .header-key {
16 | color: var(--text-secondary-color);
17 | }
18 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/result-request-body/api-test-result-request-body.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input, OnChanges } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'eo-api-test-result-request-body',
5 | templateUrl: './api-test-result-request-body.component.html',
6 | styleUrls: ['./api-test-result-request-body.component.scss']
7 | })
8 | export class ApiTestResultRequestBodyComponent {
9 | @Input() model: Array<{ name: string; type: string; value: string }> | string | any;
10 | @Input() contentType: string;
11 | constructor() {}
12 | }
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.scss:
--------------------------------------------------------------------------------
1 | :host ::ng-deep {
2 | .eo-alert-bar {
3 | display: block;
4 | margin-bottom: 10px;
5 |
6 | .ant-alert-info {
7 | background-color: #fff;
8 | border: 1px solid var(--success-color);
9 | }
10 | }
11 |
12 | .ant-code-editor {
13 | height: calc(var(--bottom-height) - 99px - 2px);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/result-response/get-size.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | @Pipe({
4 | name: 'byteToString'
5 | })
6 | export class ByteToStringPipe implements PipeTransform {
7 | transform(inputByteLength: number): string {
8 | inputByteLength = inputByteLength || 0;
9 | let tmpSizeInt = '';
10 | if (inputByteLength < 0.1 * 1024) {
11 | //如果小于0.1KB转化成B
12 | tmpSizeInt = `${inputByteLength.toFixed(2)} B`;
13 | } else if (inputByteLength < 0.1 * 1024 * 1024) {
14 | //如果小于0.1MB转化成KB
15 | tmpSizeInt = `${(inputByteLength / 1024).toFixed(2)} KB`;
16 | } else if (inputByteLength < 0.1 * 1024 * 1024 * 1024) {
17 | //如果小于0.1GB转化成MB
18 | tmpSizeInt = `${(inputByteLength / (1024 * 1024)).toFixed(2)} MB`;
19 | } else {
20 | //其他转化成GB
21 | tmpSizeInt = `${(inputByteLength / (1024 * 1024 * 1024)).toFixed(2)} GB`;
22 | }
23 | const tmpSizeStr = tmpSizeInt.toString();
24 | const tmpLen = tmpSizeStr.indexOf('.');
25 | if (tmpSizeStr.substring(tmpLen + 1, 2) === '00') {
26 | return tmpSizeStr.substring(0, tmpLen) + tmpSizeStr.substring(tmpLen + 3, 2);
27 | }
28 | return tmpSizeStr;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/http/test/test-status-bar/test-status-bar.component.scss:
--------------------------------------------------------------------------------
1 | .test-success {
2 | color: var(--success-color);
3 | }
4 |
5 | .test-default {
6 | color: var(--info-color);
7 | }
8 |
9 | .test-error {
10 | color: var(--danger-color);
11 | }
12 |
13 | .test-warning {
14 | color: var(--warning-color);
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/pipe/api-formater.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { protocalMap, requestMethodMap } from 'pc/browser/src/app/pages/workspace/project/api/constants/api.model';
3 |
4 | type FormatType = keyof typeof formatMap;
5 |
6 | const formatMap = {
7 | protocal: protocalMap,
8 | requestMethod: requestMethodMap
9 | } as const;
10 |
11 | @Pipe({ name: 'apiFormater' })
12 | export class ApiFormaterPipe implements PipeTransform {
13 | constructor() {}
14 |
15 | transform(value: any, type: FormatType) {
16 | return formatMap[type][value];
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/pipe/api-param-num.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { BodyParam } from 'pc/browser/src/app/services/storage/db/models/apiData';
3 |
4 | @Pipe({
5 | name: 'apiParamsNum'
6 | })
7 | export class ApiParamsNumPipe implements PipeTransform {
8 | transform(params: BodyParam[], ...args: unknown[]): number {
9 | if (!params || typeof params !== 'object') {
10 | return 0;
11 | }
12 | const data = params.filter(val => val.name || val.paramAttr?.example || val.description);
13 | return data.length || params?.[0]?.binaryRawData?.length;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/service/test-server/local-node/test-connect.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable, Inject, LOCALE_ID } from '@angular/core';
2 | import { toJS } from 'mobx';
3 | import { eoDeepCopy } from 'pc/browser/src/app/shared/utils/index.utils';
4 |
5 | import { ElectronService } from '../../../../../../../core/services';
6 | import { ApiTestUtilService } from '../../api-test-util.service';
7 | import { TestServerService } from '../test-server.service';
8 | @Injectable()
9 | export class TestServerLocalNodeService extends TestServerService {
10 | constructor(public electron: ElectronService, @Inject(LOCALE_ID) public locale: string, public apiTestUtil: ApiTestUtilService) {
11 | super(electron, locale, apiTestUtil);
12 | }
13 | init(receiveMessage: (message) => void) {
14 | this.electron.ipcRenderer.on('unitTest', (event, args) => {
15 | // console.log('[localNode]receiveMessage', args);
16 | receiveMessage(this.formatResponseData(args));
17 | });
18 | }
19 | send(module, message) {
20 | console.log('[localNode]send message', message);
21 | //!Prevent Proxy Object can't send ipcRender
22 | this.electron.ipcRenderer.send(module, eoDeepCopy(message));
23 | }
24 | close() {
25 | this.electron.ipcRenderer.removeAllListeners('unitTest');
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/api/websocket/websocket.routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { WebsocketComponent } from './websocket.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | component: WebsocketComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [RouterModule.forChild(routes)],
15 | exports: [RouterModule]
16 | })
17 | export class WebsocketRoutingModule {}
18 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/components/operate-project-form.compoent.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'operate-project-form',
5 | template: `
6 |
14 |
`
15 | })
16 | export class OperateProjectFormComponent {
17 | @Input() model;
18 | }
19 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/member/project-member-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { ProjectMemberComponent } from './project-member.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | component: ProjectMemberComponent
10 | }
11 | ];
12 |
13 | @NgModule({
14 | imports: [RouterModule.forChild(routes)],
15 | exports: [RouterModule]
16 | })
17 | export class ProjectMemberRoutingModule {}
18 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/project.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { FormsModule } from '@angular/forms';
3 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
4 | import { NzCardModule } from 'ng-zorro-antd/card';
5 | import { NzFormModule } from 'ng-zorro-antd/form';
6 | import { ProjectRoutingModule } from 'pc/browser/src/app/pages/workspace/project/project-routing.module';
7 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
8 |
9 | @NgModule({
10 | imports: [ProjectRoutingModule, NzAvatarModule, NzCardModule, FormsModule, NzFormModule, SharedModule],
11 | declarations: []
12 | })
13 | export class ProjectModule {}
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/setting/project-setting.component.scss:
--------------------------------------------------------------------------------
1 | ::ng-deep .project-info-tooltip {
2 | padding: 15px;
3 |
4 | .ant-tooltip-arrow-content::before {
5 | background: linear-gradient(to right bottom, var(--background-color), var(--background-color));
6 | background-repeat: no-repeat;
7 | background-position: -10px -10px;
8 | }
9 |
10 | .ant-tooltip-inner {
11 | background-color: var(--background-color);
12 | padding: 15px;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/project/setting/project-setting.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule } from '@angular/router';
3 | import { NzCardModule } from 'ng-zorro-antd/card';
4 | import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
5 | import { NzUploadModule } from 'ng-zorro-antd/upload';
6 | import { SharedModule } from 'pc/browser/src/app/shared/shared.module';
7 |
8 | import { ExtensionSelectModule } from '../../../../components/extension-select/extension-select.module';
9 | import { ProjectSettingComponent } from './project-setting.component';
10 |
11 | @NgModule({
12 | imports: [
13 | RouterModule.forChild([
14 | {
15 | path: '',
16 | component: ProjectSettingComponent
17 | }
18 | ]),
19 | ExtensionSelectModule,
20 | NzUploadModule,
21 | NzCardModule,
22 | SharedModule,
23 | NzToolTipModule
24 | ],
25 | declarations: [ProjectSettingComponent]
26 | })
27 | export class ProjectSettingModule {}
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/pages/workspace/workspace.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { EoNgFeedbackDrawerService } from 'eo-ng-feedback';
3 | import { StoreService } from 'pc/browser/src/app/shared/store/state.service';
4 |
5 | @Component({
6 | selector: 'eo-workspace',
7 | template: ``,
8 | styles: []
9 | })
10 | export class WorkspaceComponent {
11 | constructor(public store: StoreService) {}
12 | }
13 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/message/index.ts:
--------------------------------------------------------------------------------
1 | export * from './message.model';
2 | export * from './message.service';
3 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/message/message.model.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 消息对象接口
3 | */
4 |
5 | export type Message = {
6 | /**
7 | * 消息类型
8 | *
9 | * @type {string}
10 | */
11 | type: string;
12 |
13 | /**
14 | * 消息数据
15 | *
16 | * @data {object}
17 | */
18 | data: any;
19 | };
20 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/message/message.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Observable, Subject } from 'rxjs';
3 |
4 | import { Message } from './message.model';
5 | /**
6 | * @description
7 | * A message queue global send and get message
8 | */
9 | @Injectable({ providedIn: 'root' })
10 | export class MessageService {
11 | private subject = new Subject();
12 |
13 | constructor() {}
14 |
15 | /**
16 | * send message
17 | *
18 | * @param message
19 | */
20 | send(message: Message): void {
21 | this.subject.next(message);
22 | }
23 |
24 | /**
25 | * get message
26 | *
27 | * @returns message mutation observer
28 | */
29 | get(): Observable {
30 | return this.subject.asObservable();
31 | }
32 | }
33 |
34 | export const messageService = new MessageService();
35 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/decorators/base-hook.decorator.ts:
--------------------------------------------------------------------------------
1 | const genHookName = (prefix: string, str: string) => {
2 | const firstWord = str.charAt(0);
3 | return str.replace(firstWord, `${prefix}${firstWord.toUpperCase()}`);
4 | };
5 | /**
6 | * 给 CRUD 添加一些前后置钩子
7 | * beforeXxx 服务函数执行的前置钩子,在某个动作之前执行,不会对该动作有影响
8 | * afterXxx 服务的函数执行后置钩子,在某个动作之后执行,如果该钩子有返回值,那么将使用该钩子的返回值作为该动作执行结果的返回值
9 | * xxxParamTransformer 服务的函数入参前 对参数进行处理(参数转换器)
10 | */
11 | export function HookGenerator() {
12 | return function (taget: any, propName: string, descriptor: TypedPropertyDescriptor) {
13 | const beforeHook = genHookName('before', propName);
14 | const afterHook = genHookName('after', propName);
15 | const paramTransformer = genHookName(propName, 'paramTransformer');
16 |
17 | const original = descriptor.value;
18 |
19 | if (typeof original === 'function') {
20 | descriptor.value = async function (...args) {
21 | args = (await this[paramTransformer]?.(...args)) ?? args;
22 | await this[beforeHook]?.(...args);
23 | const result = await original.apply(this, args);
24 | const afterHookresult = await this[afterHook]?.({ params: args.reduce((p, v) => ({ ...p, ...v }), {}), result });
25 |
26 | return afterHookresult || result;
27 | };
28 | }
29 |
30 | return descriptor;
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/dto/apiCase.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiCase } from 'pc/browser/src/app/services/storage/db/models';
2 |
3 | export interface ApiCaseDeleteDto {
4 | apiCaseUuids?: number[];
5 | projectUuid: string;
6 | workSpaceUuid: string;
7 | }
8 |
9 | export interface ApiCaseCreateDto {
10 | apiCaseList: Partial;
11 | projectUuid: string;
12 | workSpaceUuid: string;
13 | }
14 | export interface ApiCaseUpdateDto extends ApiCase {
15 | apiCaseUuid: number;
16 | }
17 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/dto/apiData.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiData } from 'pc/browser/src/app/services/storage/db/models/apiData';
2 |
3 | export interface ApiDataBulkCreateDto {
4 | apiList: Partial;
5 | projectUuid: string;
6 | workSpaceUuid: string;
7 | }
8 |
9 | export interface ApiDataUpdateDto {
10 | api: Partial;
11 | projectUuid: string;
12 | workSpaceUuid: string;
13 | }
14 |
15 | export interface ApiDataPageDto {
16 | projectUuid: string;
17 | workSpaceUuid: string;
18 | groupIds?: number[];
19 | keyword?: string;
20 | /** 排序正逆 ASC DESC(默认) */
21 | sort?: string;
22 | /** 排序字段 默认api_update_time */
23 | order?: string;
24 | page?: number;
25 | pageSize?: number;
26 | }
27 |
28 | export interface ApiDataBulkReadDto {
29 | projectUuid: string;
30 | workSpaceUuid?: string;
31 | }
32 |
33 | export interface ApiDataBulkReadDetailDto {
34 | apiUuids: string[];
35 | projectUuid: string;
36 | workSpaceUuid: string;
37 | }
38 |
39 | export interface ApiDataDeleteDto {
40 | apiUuids: string[];
41 | projectUuid: string;
42 | workSpaceUuid: string;
43 | }
44 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/dto/common.dto.ts:
--------------------------------------------------------------------------------
1 | export interface CommonDto {
2 | workSpaceUuid: string;
3 | }
4 |
5 | export interface QueryAllDto {
6 | projectUuid: string;
7 | workSpaceUuid?: string;
8 | }
9 |
10 | export interface PageDto extends CommonDto {
11 | page?: number;
12 | pageSize?: number;
13 | }
14 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/dto/group.dto.ts:
--------------------------------------------------------------------------------
1 | import { GroupModuleType, GroupType } from 'pc/browser/src/app/services/storage/db/models';
2 |
3 | export interface GroupDeleteDto {
4 | id?: number;
5 | projectUuid: string;
6 | workSpaceUuid: string;
7 | }
8 |
9 | export interface GroupCreateDto {
10 | name: string;
11 | type?: GroupType;
12 | module?: GroupModuleType;
13 | path?: string;
14 | depth?: number;
15 | parentId?: number;
16 | sort?: number;
17 | projectUuid?: string;
18 | workSpaceUuid?: string;
19 | }
20 | export interface GroupUpdateDto extends GroupCreateDto {
21 | id: number;
22 | }
23 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/dto/project.dto.ts:
--------------------------------------------------------------------------------
1 | import { PageDto } from 'pc/browser/src/app/services/storage/db/dto/common.dto';
2 | import { CollectionTypeEnum, Environment, Group } from 'pc/browser/src/app/services/storage/db/models';
3 | import { ApiData } from 'pc/browser/src/app/services/storage/db/models/apiData';
4 |
5 | export interface ProjectBulkCreateDto {
6 | projectMsgs: ProjectMsg[];
7 | workSpaceUuid: string;
8 | }
9 |
10 | export interface ProjectMsg {
11 | name: string;
12 | description?: string;
13 | }
14 |
15 | export interface ProjectDeleteDto {
16 | projectUuids: string[];
17 | }
18 |
19 | export interface ProjectUpdateDto {
20 | projectUuid: string;
21 | name: string;
22 | description: string;
23 | }
24 |
25 | export interface ProjectPageDto extends PageDto {
26 | projectUuids?: string[];
27 | }
28 |
29 | export interface ImportProjectDto {
30 | environmentList: Environment[];
31 | collections: Collection[];
32 | projectUuid?: string;
33 | workSpaceUuid?: string;
34 | }
35 | export type Collection = ApiData | Group;
36 | export type ImportCollection = Collection & {
37 | /**
38 | * 0:group
39 | * 1: apiData
40 | */
41 | collectionType: CollectionTypeEnum;
42 | };
43 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/index.ts:
--------------------------------------------------------------------------------
1 | import { DbApiCaseService } from 'pc/browser/src/app/services/storage/db/services/apiCase.service';
2 |
3 | import { DbApiDataService } from './services/apiData.service';
4 | import { DbApiTestHistoryService } from './services/apiTestHistory.service';
5 | import { DbEnvironmentService } from './services/environment.service';
6 | import { DbGroupService } from './services/group.service';
7 | import { DbMockService } from './services/mock.service';
8 | import { DbProjectService } from './services/project.service';
9 | import { DbWorkspaceService } from './services/workspace.service';
10 |
11 | export const db = {
12 | apiData: new DbApiDataService(),
13 | group: new DbGroupService(),
14 | environment: new DbEnvironmentService(),
15 | project: new DbProjectService(),
16 | workspace: new DbWorkspaceService(),
17 | mock: new DbMockService(),
18 | apiCase: new DbApiCaseService(),
19 | apiTestHistory: new DbApiTestHistoryService()
20 | } as const;
21 |
22 | if (process.env.NODE_ENV === 'development') {
23 | // const module = await import('./tests/index');
24 | // module.setupTests();
25 | }
26 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/schema/env.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "properties": {
4 | "hostUri": {
5 | "type": "string"
6 | },
7 | "id": {
8 | "type": "number"
9 | },
10 | "name": {
11 | "type": "string"
12 | },
13 | "parameters": {
14 | "type": "string",
15 | "default": "[]"
16 | },
17 | "projectUuid": {
18 | "type": "string"
19 | },
20 | "uuid": {
21 | "type": "string"
22 | },
23 | "workSpaceUuid": {
24 | "type": "string"
25 | }
26 | },
27 | "required": ["name"],
28 | "additionalProperties": false,
29 | "type": "object"
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/schema/group.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "properties": {
4 | "id": {
5 | "type": "number"
6 | },
7 | "name": {
8 | "type": "string"
9 | },
10 | "type": {
11 | "type": "number",
12 | "const": 1,
13 | "default": 1
14 | },
15 | "collectionType": {
16 | "type": "number",
17 | "const": 0,
18 | "default": 0
19 | },
20 | "children": {
21 | "type": "array"
22 | }
23 | },
24 | "required": ["name"],
25 | "additionalProperties": false,
26 | "type": "object"
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/services/apiTestHistory.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'pc/browser/src/app/services/storage/db/dataSource';
2 | import { ApiTestHistory } from 'pc/browser/src/app/services/storage/db/models';
3 | import { DbBaseService } from 'pc/browser/src/app/services/storage/db/services/base.service';
4 |
5 | export class DbApiTestHistoryService extends DbBaseService {
6 | constructor() {
7 | super(dataSource.apiTestHistory);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/services/environment.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'pc/browser/src/app/services/storage/db/dataSource';
2 | import { ApiResponse, ResObj } from 'pc/browser/src/app/services/storage/db/decorators/api-response.decorator';
3 | import { Environment } from 'pc/browser/src/app/services/storage/db/models';
4 | import { DbBaseService } from 'pc/browser/src/app/services/storage/db/services/base.service';
5 |
6 | export class DbEnvironmentService extends DbBaseService {
7 | baseService = new DbBaseService(dataSource.environment);
8 | constructor() {
9 | super(dataSource.environment);
10 | }
11 |
12 | @ApiResponse()
13 | create(params: any) {
14 | if (params.name?.length > 32) {
15 | throw new ResObj(null, { code: 131000001, message: $localize`Environment name length needs to be less than 32` });
16 | }
17 | return this.baseService.create(params);
18 | }
19 |
20 | @ApiResponse()
21 | update(params: any) {
22 | if (params.name?.length > 32) {
23 | throw new ResObj(null, { code: 131000001, message: $localize`Environment name length needs to be less than 32` });
24 | }
25 | return this.baseService.update(params);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/services/workspace.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'pc/browser/src/app/services/storage/db/dataSource';
2 | import { Workspace } from 'pc/browser/src/app/services/storage/db/models';
3 | import { DbBaseService } from 'pc/browser/src/app/services/storage/db/services/base.service';
4 |
5 | export class DbWorkspaceService extends DbBaseService {
6 | baseService = new DbBaseService(dataSource.workspace);
7 |
8 | constructor() {
9 | super(dataSource.workspace);
10 | }
11 |
12 | async bulkRead(params: Record) {
13 | const result = await this.baseService.bulkRead(params);
14 |
15 | result.data.forEach(item => {
16 | item.title = $localize`Personal Workspace`;
17 | });
18 |
19 | return result;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/tests/index.ts:
--------------------------------------------------------------------------------
1 | // 接口文档:https://eolinker.w.eolink.com/old/home/api_studio/inside/api/list?groupID=-1&projectHashKey=Dr31QyT4b832495fe945fb9420215f167e33b2dc1fb4f27&spaceKey=eolinker
2 | export const setupTests = async () => {
3 | await import('./project.test');
4 | await import('./apiData.test');
5 | await import('./apiGroup.test');
6 | await import('./environment.test');
7 | };
8 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/storage/db/utils/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 将对象进行序列化及反序列化,因为 dexie.js 会将 属性 为 undefined 的属性删掉
3 | * 同时也是为了过滤掉不能够被序列化的数据
4 | * */
5 | export const serializeObj = (obj: T) => JSON.parse(JSON.stringify(obj)) as T;
6 |
--------------------------------------------------------------------------------
/src/browser/src/app/services/trace.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import packageJson from '../../../../../package.json';
4 | import { ElectronService } from '../core/services/electron/electron.service';
5 | declare const gio;
6 |
7 | /**
8 | * GrowingIO Event Tracking Service
9 | */
10 | @Injectable({
11 | providedIn: 'root'
12 | })
13 | export class TraceService {
14 | constructor(private electron: ElectronService) {
15 | this.setVisitor({ client_type: this.electron.isElectron ? 'client' : 'web', app_version: packageJson.version });
16 | }
17 |
18 | report(eventId, params = {}) {
19 | if (!eventId) {
20 | return;
21 | }
22 | // scheduler.postTask(report, {priority: 'background'});
23 | //TODO Use background priority to optimize job: https://web.dev/optimize-long-tasks/#a-dedicated-scheduler-api
24 | gio('track', eventId, params);
25 | }
26 | setUserID(id) {
27 | gio('setUserId', id);
28 | }
29 | setUser(data = {}) {
30 | gio('people.set', data);
31 | }
32 | setVisitor(data = {}) {
33 | gio('visitor.set', data);
34 | }
35 | start() {
36 | gio('config', { dataCollect: false });
37 | }
38 | stop() {
39 | gio('config', { dataCollect: true });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/README.md:
--------------------------------------------------------------------------------
1 | # Shared module
2 | Global module in project
--------------------------------------------------------------------------------
/src/browser/src/app/shared/components/download-client.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { WebService } from 'pc/browser/src/app/core/services';
3 |
4 | @Component({
5 | selector: 'eo-download-client-modal',
6 | template: `
7 | Don't have Postcat Client?
8 |
9 |
10 | {{ item.name }}
11 |
12 | `
13 | })
14 | export class DownloadClientModalComponent {
15 | resourceInfo;
16 |
17 | constructor(public web: WebService) {
18 | this.resourceInfo = this.web.resourceInfo;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/constans/featureName.ts:
--------------------------------------------------------------------------------
1 | export const IMPORT_API = 'importAPI';
2 | export const API_PREVIEW_TAB = 'apiPreviewTab';
3 | export const EXPORT_API = 'exportAPI';
4 | export const PUSH_API = 'pushAPI';
5 | export const PULL_API = 'pullAPI';
6 | export const FEATURE_CONTROL = 'featureControl';
7 | export const THEME = 'theme';
8 | export const SIDEBAR_VIEW = 'sidebarView';
9 | export const AUTH_API = 'authAPI';
10 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/constans/modal-size.ts:
--------------------------------------------------------------------------------
1 | export const MODAL_NORMAL_SIZE: number = 900;
2 | export const MODAL_SMALL_SIZE: number = 600;
3 | export const MODAL_LARGE_SIZE: number = 1200;
4 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/constans/newbie-guide.ts:
--------------------------------------------------------------------------------
1 | const NEWBIE_GUIDE = $localize`
2 | ### Postcat is a powerful free, cross-platform, extensible API tool
3 |
4 | **Compared to products like Postman, it has the following much-loved features:**
5 |
6 | 1.❤️ Free teamwork: Good products should be used by more people, we do not limit the number of free people!
7 |
8 | 2.🚀 Extensible extension system: One-click installation of various extensions such as ChatGPT and themes to customize your own API development tools~
9 |
10 | 3.😊 Excellent user experience: everything can be simpler and make our work more efficient~
11 |
12 | **Of course, we also include many useful basic functions:**
13 |
14 | 1.🍩 Multi-protocol support: REST, Websocket and other protocols (soon to support GraphQL, gRPC, TCP, UDP)
15 |
16 | 2.📕 API design, document display, sharing
17 |
18 | 3.⚡ API testing
19 |
20 | 4.🎭 Mock Service
21 |
22 | 5.🙌 Environmental management
23 |
24 |
25 | ...
26 | `;
27 | export default NEWBIE_GUIDE;
28 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/decorators/memo.ts:
--------------------------------------------------------------------------------
1 | import memoizee from 'memoizee';
2 |
3 | /**
4 | * memo decorator
5 | *
6 | * @returns memoization
7 | */
8 | export function memo() {
9 | return function (target: any, key: keyof any, descriptor: PropertyDescriptor) {
10 | const oldFn = descriptor.value;
11 | const newFn = memoizee(oldFn);
12 | descriptor.value = function () {
13 | return newFn.apply(this, arguments);
14 | };
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/directives/focus-form-input.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ElementRef, HostListener, AfterViewInit, ChangeDetectorRef } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[auto-focus-form]'
5 | })
6 | export class FormFocusDirective implements AfterViewInit {
7 | /**
8 | * Priority: input > textarea > select > search
9 | */
10 | focusables = ['.ant-input', 'textarea', 'select', '.ant-select-selection-search-input'];
11 |
12 | constructor(private element: ElementRef, private cdk: ChangeDetectorRef) {}
13 | ngAfterViewInit() {
14 | let input;
15 | this.focusables.some(className => {
16 | const dom = this.element.nativeElement.querySelector(className);
17 | if (dom) {
18 | input = dom;
19 | return true;
20 | }
21 | });
22 | if (input) {
23 | setTimeout(() => {
24 | input.focus();
25 | this.cdk.detectChanges();
26 | }, 300);
27 | }
28 | }
29 |
30 | @HostListener('submit')
31 | submit() {
32 | const input = this.element.nativeElement.querySelector(this.focusables.map(x => `${x}.ng-invalid`).join(','));
33 | if (input) {
34 | input.focus();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/directives/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stop-propagation.directive';
2 | export * from './focus-form-input.directive';
3 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/directives/stop-propagation.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, HostListener } from '@angular/core';
2 |
3 | @Directive({
4 | selector: '[click-stop-propagation]'
5 | })
6 | export class ClickStopPropagationDirective {
7 | @HostListener('click', ['$event'])
8 | public onClick(event: any): void {
9 | event.stopPropagation();
10 | }
11 | @HostListener('mousedown', ['$event'])
12 | public onMousedown(event: any): void {
13 | event.stopPropagation();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/directives/trace.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, HostListener, Input } from '@angular/core';
2 | import { TraceService } from 'pc/browser/src/app/services/trace.service';
3 |
4 | @Directive({
5 | selector: '[trace]'
6 | })
7 | export class TraceDirective {
8 | @Input() traceID: string;
9 | @Input() traceParams: any = {};
10 | constructor(private trace: TraceService) {}
11 |
12 | @HostListener('click', ['$event'])
13 | tClick(event) {
14 | this.trace.report(this.traceID, this.traceParams);
15 | }
16 |
17 | @HostListener('keydown')
18 | tKeyDown() {}
19 |
20 | @HostListener('focus', ['$event'])
21 | onFocus(event) {
22 | const tagName = event.target.tagName;
23 | if (!['INPUT', 'SELECT', 'RADIO'].includes(tagName)) {
24 | return;
25 | }
26 | this.trace.report(this.traceID, this.traceParams);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/extension-manager/index.ts:
--------------------------------------------------------------------------------
1 | export * from './module';
2 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/extension.ts:
--------------------------------------------------------------------------------
1 | export const defaultExtensions = ['postcat-export-openapi', 'postcat-import-openapi', 'postcat-basic-auth'];
2 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/member.model.ts:
--------------------------------------------------------------------------------
1 | export const ROLE_TITLE_BY_ID = {
2 | 'Project Owner': $localize`Project Owner`,
3 | 'Project Editor': $localize`Project Editor`,
4 | 'Workspace Owner': $localize`Workspace Owner`,
5 | 'Workspace Editor': $localize`Workspace Editor`
6 | };
7 | export interface Role {
8 | /**
9 | * Role belongs module
10 | * 1: workspace, 2: project
11 | */
12 | module: 1 | 2;
13 | name: string;
14 | /**
15 | * Workspace Owner/Project Owner
16 | */
17 | uuid: string;
18 | }
19 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/protocol.constant.ts:
--------------------------------------------------------------------------------
1 | export const PROTOCOL = 'eoapi://';
2 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/shared.model.ts:
--------------------------------------------------------------------------------
1 | export const REQURIED_ENUMS = [
2 | { title: $localize`Yes`, value: 1 },
3 | { title: '-', value: 0 }
4 | ];
5 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/models/storageKeys.constant.ts:
--------------------------------------------------------------------------------
1 | /** disable extensions */
2 | export const DISABLE_EXTENSION_NAMES = 'DISABLE_EXTENSION_NAMES';
3 |
4 | /** is show local data source tips */
5 | export const IS_SHOW_REMOTE_SERVER_NOTIFICATION = 'IS_SHOW_REMOTE_SERVER_NOTIFICATION';
6 |
--------------------------------------------------------------------------------
/src/browser/src/app/shared/utils/parseOpenAPI/index.ts:
--------------------------------------------------------------------------------
1 | import { OpenAPIParser } from './OpenAPIParser';
2 | import type { OpenAPIV3 } from './type';
3 |
4 | export const parseOpenAPI = (openapi: any) => {
5 | if (Object.keys(openapi).length === 0) {
6 | return [null, { msg: '请上传合法的文件' }];
7 | }
8 |
9 | const openapiVersion = openapi.openapi || openapi['swagger'];
10 |
11 | if (!openapiVersion && !openapiVersion) {
12 | return [null, { msg: '文件不合法,缺乏 OpenAPI 字段' }];
13 | }
14 |
15 | if (Number.parseFloat(openapiVersion) < 3) {
16 | return [null, { msg: '仅支持 openapi 3.0 以上版本' }];
17 | }
18 |
19 | const data = new OpenAPIParser(openapi).data;
20 | return [data, null];
21 | };
22 |
--------------------------------------------------------------------------------
/src/browser/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/browser/src/assets/icons/iconLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/icons/iconLogo.png
--------------------------------------------------------------------------------
/src/browser/src/assets/images/feishu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/images/feishu.png
--------------------------------------------------------------------------------
/src/browser/src/assets/images/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/images/github.png
--------------------------------------------------------------------------------
/src/browser/src/assets/images/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/images/heart.png
--------------------------------------------------------------------------------
/src/browser/src/assets/images/newbie-guide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/images/newbie-guide.png
--------------------------------------------------------------------------------
/src/browser/src/assets/images/qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/assets/images/qq.png
--------------------------------------------------------------------------------
/src/browser/src/environments/common.constant.ts:
--------------------------------------------------------------------------------
1 | import { COMMON_APP_CONFIG } from 'pc/environment';
2 |
3 | export const COMMON_CONFIG = { ...COMMON_APP_CONFIG };
4 |
5 | export type APP_CONFIG_INSTANT = {
6 | production: boolean;
7 | environment: 'DEV' | 'PROD';
8 | EXTENSION_URL: string;
9 | REMOTE_SOCKET_URL: 'wss://postcat.com';
10 | SOCKET_PORT: number;
11 | NODE_SERVER_PORT: number;
12 | };
13 |
--------------------------------------------------------------------------------
/src/browser/src/environments/environment.dev.ts:
--------------------------------------------------------------------------------
1 | import { COMMON_CONFIG } from 'pc/browser/src/environments/common.constant';
2 | export const APP_CONFIG = {
3 | serverUrl: 'http://52.76.76.88:8080',
4 | production: false,
5 | environment: 'DEV',
6 | ...COMMON_CONFIG
7 | };
8 |
--------------------------------------------------------------------------------
/src/browser/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | import { APP_CONFIG_INSTANT, COMMON_CONFIG } from 'pc/browser/src/environments/common.constant';
2 |
3 | export const APP_CONFIG = {
4 | serverUrl: 'https://postcat.com',
5 | production: true,
6 | environment: 'PROD',
7 | ...COMMON_CONFIG,
8 | REMOTE_SOCKET_URL: '',
9 | NODE_SERVER_PORT: ''
10 | };
11 |
--------------------------------------------------------------------------------
/src/browser/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | import { APP_CONFIG_INSTANT, COMMON_CONFIG } from 'pc/browser/src/environments/common.constant';
2 |
3 | export const APP_CONFIG: APP_CONFIG_INSTANT | any = {
4 | production: false,
5 | environment: 'LOCAL',
6 | ...COMMON_CONFIG
7 | };
8 |
--------------------------------------------------------------------------------
/src/browser/src/extensions/README.md:
--------------------------------------------------------------------------------
1 | # Exetension
2 | Core exension inject in project
--------------------------------------------------------------------------------
/src/browser/src/extensions/themes/blue.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#2196f3"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/browser/src/extensions/themes/dark.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#7371fc",
4 | "text": "#ccc",
5 | "inputIcon": "#ccc",
6 | "textSecondary": "#ccc",
7 | "border": "rgba(255, 255, 255, 0.05)",
8 | "background": "#212121",
9 | "barBackground": "#2d2d2d",
10 | "disabledText": "#666",
11 | "disabledBackground": "#282828",
12 | "textLink": "#1890ff",
13 | "layoutHeaderBackground": "rgb(60, 60, 60)",
14 | "layoutSidebarBackground": "#2d2d2d",
15 | "layoutSidebarText": "rgba(255, 255, 255, 0.4)",
16 | "layoutFooterBackground": "rgb(60, 60, 60)",
17 | "layoutFooterText": "rgba(255, 255, 255, 0.4)",
18 | "textLinkHover": "#40a9ff",
19 | "textLinkActive": "#096dd9",
20 | "divider": "rgba(255, 255, 255, 0.05)",
21 | "alertDefaultBackground": "rgba(149,149,149,.1)",
22 | "scrollbarTrackBackground": "#212121",
23 | "scrollbarThumbBackground": "rgba(255, 255, 255, 0.2)",
24 | "popoverBackground": "#fff",
25 | "popoverText": "#333"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/browser/src/extensions/themes/green.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#41aa64"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/browser/src/extensions/themes/light.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#7371fc",
4 | "text": "#333",
5 | "textSecondary": "#999",
6 | "inputIcon": "rgba(0, 0, 0, 0.25)",
7 | "border": "rgba(0, 0, 0, 0.07)",
8 | "background": "#fff",
9 | "barBackground": "#fafafa",
10 | "disabledText": "#bbbbbb",
11 | "disabledBackground": "#f6f6f6",
12 | "textLink": "#1890ff",
13 | "textLinkHover": "#40a9ff",
14 | "textLinkActive": "#096dd9",
15 | "divider": "rgba(0,0,0,.06)",
16 | "alertDefaultBackground": "rgba(149,149,149,.1)",
17 | "scrollbarTrackBackground": "rgba(255, 255, 255, 0.05)",
18 | "scrollbarThumbBackground": "rgba(0, 0, 0, 0.2)",
19 | "popoverText": "#fff",
20 | "popoverBackground": "rgba(0,0,0,.75)"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/browser/src/extensions/themes/orange.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#f47023"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/browser/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { APP_CONFIG } from './environments/environment';
6 |
7 | if (APP_CONFIG.production) {
8 | enableProdMode();
9 | }
10 |
11 | // import microApp from '@micro-zoe/micro-app';
12 |
13 | // microApp.start({
14 | // fetch(url, options, appName) {
15 | // if (url.startsWith('https://unpkg.com/') && !url.startsWith(`https://unpkg.com/${appName}`)) {
16 | // // 删除 http://localhost:3001/error.js 的内容
17 | // return window
18 | // .fetch(new URL(url.replace('https://unpkg.com/', ''), `https://unpkg.com/${appName}/page/`).href, options)
19 | // .then(res => res.text());
20 | // }
21 |
22 | // return window.fetch(url, options).then(res => {
23 | // if (res.status > 400) {
24 | // console.error(res);
25 | // return '';
26 | // }
27 | // return res.text();
28 | // });
29 | // }
30 | // });
31 |
32 | platformBrowserDynamic()
33 | .bootstrapModule(AppModule)
34 | .catch(err => console.error(err));
35 |
--------------------------------------------------------------------------------
/src/browser/src/polyfills-test.ts:
--------------------------------------------------------------------------------
1 | import 'zone.js';
2 | /***************************************************************************************************
3 | * global is required by default
4 | */
5 | (window as any).global = window; // Included with Angular CLI.
6 |
7 | /***************************************************************************************************
8 | * APPLICATION IMPORTS
9 | */
10 |
--------------------------------------------------------------------------------
/src/browser/src/styles/dark.less:
--------------------------------------------------------------------------------
1 | @import '../../../../../node_modules/ng-zorro-antd/ng-zorro-antd.less';
2 | @import '../../../../../../node_modules/ng-zorro-antd/src/style/themes/dark.less';
3 | @import './themes/base.less';
4 | @body-background: #333333;
5 | @import './themes/dark.css';
6 |
--------------------------------------------------------------------------------
/src/browser/src/styles/light.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/browser/src/styles/light.less
--------------------------------------------------------------------------------
/src/browser/src/styles/variables.css:
--------------------------------------------------------------------------------
1 | /* UI component */
2 | :root {
3 | --icon-size: 14px;
4 | --border-radius: 4px;
5 |
6 | --layout-header-height: 50px;
7 | --layout-footer-height: 30px;
8 |
9 | --btn-icon-margin: 5px;
10 | --button-height: 32px;
11 | --tabs-height: 43px;
12 | --tree-item-height: 30px;
13 | --collapse-header-height: 46px;
14 |
15 | --table-font-size: 14px;
16 | --table-row-height: 40px;
17 | --table-item-padding: 4px;
18 |
19 | --padding-x: 20px;
20 | --padding-y: 15px;
21 | --padding: var(--padding-y) var(--padding-x);
22 | --margin: 20px;
23 | }
24 | /* System customer */
25 | :root {
26 | --layout-sidebar-width: 85px;
27 | --layout-sidebar-shrink-width: 40px;
28 | --layout-sidebar-item-height: auto;
29 | --edit-inside-bar-height: 53px;
30 | }
31 |
--------------------------------------------------------------------------------
/src/browser/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting(), {
16 | teardown: { destroyAfterEach: false }
17 | }
18 | );
19 | // Then we find all the tests.
20 | const context = require.context('./', true, /\.spec\.ts$/);
21 | // And load the modules.
22 | context.keys().map(context);
23 |
--------------------------------------------------------------------------------
/src/browser/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "",
6 | "types": ["node"],
7 | "lib": ["DOM", "ESNext"],
8 | "paths": {
9 | "pc/*": ["../../../src/*"]
10 | }
11 | },
12 | "files": ["main.ts", "polyfills.ts"],
13 | "include": ["**/*.d.ts"],
14 | "exclude": ["**/*.spec.ts", "./app/views/*"]
15 | }
16 |
--------------------------------------------------------------------------------
/src/browser/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["test.ts", "polyfills-test.ts"],
8 | "include": ["**/*.spec.ts", "**/*.d.ts"],
9 | "exclude": ["dist", "release", "node_modules"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/browser/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | /* SystemJS module definition */
3 | declare const nodeModule: NodeModule;
4 |
5 | interface NodeModule {
6 | id: string;
7 | }
8 | declare global {
9 | interface Window {
10 | process: any;
11 | requirejs: any;
12 | require: any;
13 | angular: any;
14 | eo: any;
15 | pc: any;
16 | electron: any;
17 | pcConsole: {
18 | warn: (...args) => void;
19 | error: (...args) => void;
20 | success: (...args) => void;
21 | log: (...args) => void;
22 | };
23 | BlobBuilder: any;
24 | WebKitBlobBuilder: any;
25 | MozBlobBuilder: any;
26 | MSBlobBuilder: any;
27 | //TODO compatible with old version
28 | __POWERED_BY_EOAPI__: boolean;
29 | __POWERED_BY_POSTCAT__: boolean;
30 | }
31 |
32 | interface Array {
33 | // 已经添加 polyfill 了 放下食用
34 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/group
35 | group(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): Record;
36 | groupToMap(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): Map;
37 | }
38 |
39 | declare const pcConsole: typeof pcConsole;
40 | }
41 |
42 | export {};
43 |
--------------------------------------------------------------------------------
/src/browser/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./src/**/*.{html,ts,scss}'],
3 | theme: {
4 | extend: {}
5 | },
6 | plugins: [],
7 | shortcuts: {
8 | 'wh-full': 'w-full h-full',
9 | 'flex-center': 'flex justify-center items-center',
10 | 'flex-col-center': 'flex-center flex-col',
11 | 'flex-x-center': 'flex justify-center',
12 | 'flex-y-center': 'flex items-center',
13 | 'fixed-center': 'fixed-lt flex-center wh-full',
14 | 'nowrap-hidden': 'whitespace-nowrap overflow-hidden',
15 | 'ellipsis-text': 'nowrap-hidden overflow-ellipsis'
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/src/browser/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "../../",
5 | "outDir": "./dist/out-tsc",
6 | "resolveJsonModule": true,
7 | "allowSyntheticDefaultImports": true,
8 | "module": "esnext",
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "declaration": false,
12 | "moduleResolution": "node",
13 | "emitDecoratorMetadata": false,
14 | "experimentalDecorators": true,
15 | "target": "esnext",
16 | "lib": ["esnext", "dom"],
17 | "paths": {
18 | "pc/*": ["./src/*"]
19 | },
20 | "allowJs": true,
21 | "typeRoots": ["node_modules/@types"]
22 | },
23 | "exclude": ["node_modules", "dist", ".angular", "src/app/assets"],
24 | "include": ["src/**/*.ts", "src/**/*.d.ts"],
25 | "useDefineForClassFields": true,
26 | "angularCompilerOptions": {
27 | "enableIvy": true,
28 | "strictTemplates": true,
29 | "fullTemplateTypeCheck": true,
30 | "annotateForClosureCompiler": true,
31 | "strictInjectionParameters": true,
32 | "skipTemplateCodegen": false,
33 | "skipMetadataEmit": false,
34 | "disableTypeScriptVersionCheck": true,
35 | "enableI18nLegacyMessageIdFormat": false,
36 | "strictInputAccessModifiers": true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/environment.ts:
--------------------------------------------------------------------------------
1 | const variables = {
2 | GITHUB_ORANIZATION_NAME: 'postcatlab',
3 | GITHUB_PROJECT_NAME: 'postcat'
4 | };
5 | export const COMMON_APP_CONFIG = {
6 | // EXTENSION_URL: 'http://localhost:5000',
7 | EXTENSION_URL: 'https://extensions.postcat.com',
8 | REMOTE_SOCKET_URL: 'wss://postcat.com',
9 | // SOCKET_PORT: '',
10 | // MOCK_URL: 'http://8.219.85.124:5000',
11 | NODE_SERVER_PORT: 4201,
12 | GITHUB_ORANIZATION_NAME: variables.GITHUB_ORANIZATION_NAME,
13 | GITHUB_PROJECT_NAME: variables.GITHUB_PROJECT_NAME,
14 | GITHUB_REPO_URL: `https://github.com/${variables.GITHUB_ORANIZATION_NAME}/${variables.GITHUB_PROJECT_NAME}`,
15 | BASE_DOWNLOAD_URL: 'https://data.postcat.com/download/'
16 | // BASE_DOWNLOAD_URL: 'http://127.0.0.1:8080'
17 | } as const;
18 |
--------------------------------------------------------------------------------
/src/node/extensions-manage/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .Ds_Store
3 | dist
4 | config.json
--------------------------------------------------------------------------------
/src/node/extensions-manage/README.md:
--------------------------------------------------------------------------------
1 | # Extension Manager
2 | Call Extension function at Node.js
--------------------------------------------------------------------------------
/src/node/extensions-manage/ecosystem.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps: [
3 | {
4 | name: 'extension-runtime',
5 | script: 'main.js',
6 | watch: '.'
7 | }
8 | ],
9 |
10 | deploy: {
11 | production: {}
12 | }
13 | };
14 |
--------------------------------------------------------------------------------
/src/node/extensions-manage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "postcat-extensions-manage",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "main.js",
6 | "scripts": {
7 | "prepare": "node tools/install.cjs",
8 | "dev": "nodemon main",
9 | "start": "pm2 start ecosystem.config.cjs",
10 | "stop": "pm2 stop ecosystem.config.cjs",
11 | "log": "pm2 logs"
12 | },
13 | "type": "module",
14 | "keywords": [],
15 | "author": "",
16 | "license": "ISC",
17 | "dependencies": {
18 | "@koa/cors": "^4.0.0",
19 | "koa": "^2.14.1",
20 | "koa-bodyparser": "^4.3.0",
21 | "koa-json": "^2.0.2",
22 | "koa-router": "^12.0.0",
23 | "network-activity-viewer": "^1.0.8",
24 | "node-fetch": "^3.3.0",
25 | "pm2": "^5.2.2"
26 | },
27 | "devDependencies": {
28 | "nodemon": "^2.0.20"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/node/extensions-manage/tools/install.cjs:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const config = {
3 | url: 'https://postcat.com',
4 | appId: 'postcat-node-server',
5 | // Postcat backend appKey
6 | appKey: ''
7 | };
8 | fs.writeFile(
9 | require('path').join(__dirname, '../config.json'),
10 | JSON.stringify(config),
11 | {
12 | flag: 'w'
13 | },
14 | err => {
15 | if (err) {
16 | console.error(err);
17 | return;
18 | }
19 | }
20 | );
21 |
--------------------------------------------------------------------------------
/src/node/mock/README.md:
--------------------------------------------------------------------------------
1 | # Mock Server
--------------------------------------------------------------------------------
/src/node/test-server/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
--------------------------------------------------------------------------------
/src/node/test-server/README.md:
--------------------------------------------------------------------------------
1 | # Node Test server
2 | warning: If the root directory exists,`package.json` dependencies need added to root `package.json`
3 |
4 | need `yarn` globally
5 |
6 | # Debug Test Server
7 |
8 | 1. Run Test Server in this directory
9 | ```shell
10 | yarn dev
11 | ```
12 | 2. Run the core system in root directory
13 |
14 | ```shell
15 | yarn start
16 | ```
--------------------------------------------------------------------------------
/src/node/test-server/ecosystem.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | apps: [
3 | {
4 | name: 'http-server',
5 | script: 'server/main.js',
6 | watch: '.'
7 | },
8 | {
9 | name: 'websocket-server',
10 | script: 'server/socketio.js',
11 | watch: '.'
12 | }
13 | ],
14 | deploy: {
15 | production: {}
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/src/node/test-server/electron/forkUnit.js:
--------------------------------------------------------------------------------
1 | let _LibsFlowCommon = require('../request/unit.js');
2 | let _LibsCommon = require('../request/libs/common.js');
3 | process.on('message', async message => {
4 | switch (message.action) {
5 | case 'setGlobal': {
6 | global['__HOME_DIR'] = message.data.homeDir;
7 | break;
8 | }
9 | case 'ajax': {
10 | message.data.env = _LibsCommon.parseEnv(message.data.env);
11 | await new _LibsFlowCommon.core().main(message.data).then(({ globals, report, history }) => {
12 | ['general', 'requestInfo', 'resultInfo'].forEach(keyName => {
13 | if (typeof history[keyName] === 'string') history[keyName] = JSON.parse(history[keyName]);
14 | });
15 | process.send({
16 | action: 'finish',
17 | data: {
18 | id: message.id,
19 | report: report,
20 | history: history,
21 | globals: globals
22 | }
23 | });
24 | });
25 | break;
26 | }
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/src/node/test-server/electron/main.ts:
--------------------------------------------------------------------------------
1 | import { ipcMain } from 'electron';
2 | import { UnitWorker } from 'pc/node/test-server/electron/unitWorker';
3 | /**
4 | * Electron Test use Ipc to communicate
5 | */
6 | export const UnitWorkerModule = {
7 | works: {},
8 | setup(pc: any) {
9 | ipcMain.removeAllListeners('unitTest');
10 | ipcMain.on('unitTest', function (event, message) {
11 | const id = message.id;
12 | switch (message.action) {
13 | case 'ajax': {
14 | UnitWorkerModule.works[id] = new UnitWorker(pc.view);
15 | UnitWorkerModule.works[id].start(message);
16 | break;
17 | }
18 | case 'abort': {
19 | if (UnitWorkerModule.works[id]) {
20 | UnitWorkerModule.works[id].kill();
21 | }
22 | break;
23 | }
24 | }
25 | });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/src/node/test-server/electron/unitWorker.ts:
--------------------------------------------------------------------------------
1 | import { BrowserView } from 'electron';
2 | import { HOME_DIR } from 'pc/shared/electron-main/constant';
3 |
4 | import * as child_process from 'child_process';
5 | export class UnitWorker {
6 | instance: child_process.ChildProcess;
7 | view: BrowserView;
8 | constructor(view: BrowserView) {
9 | this.view = view;
10 | }
11 | start(message: any) {
12 | this.instance = child_process.fork(`${__dirname}/forkUnit.js`);
13 | this.watch();
14 | this.instance.send({
15 | action: 'setGlobal',
16 | data: {
17 | homeDir: HOME_DIR
18 | }
19 | });
20 | this.instance.send(message);
21 | }
22 | finish(message: any) {
23 | this.view.webContents.send('unitTest', message);
24 | this.kill();
25 | }
26 | kill() {
27 | this.instance.kill();
28 | }
29 | private watch() {
30 | this.instance.on('message', (message: any) => {
31 | switch (message.action) {
32 | case 'finish': {
33 | this.finish(message.data);
34 | break;
35 | }
36 | }
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/node/test-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "postcat-test-server",
3 | "version": "1.0.0",
4 | "description": "Recieve postcat test config and response http result",
5 | "main": "./server/main.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+git@github.com:Postcatlab/postcat.git"
9 | },
10 | "scripts": {
11 | "dev": "npm-run-all -p dev:http dev:ws",
12 | "dev:http":"node ./server/main.js",
13 | "dev:ws":"node ./server/socketio.js",
14 | "start": "pm2 start ecosystem.config.js",
15 | "start:watch": "pm2-runtime start ecosystem.config.js",
16 | "stop": "pm2 stop ecosystem.config.js",
17 | "test": "echo \"Error: no test specified\" && exit 1"
18 | },
19 | "author": "Postcat",
20 | "license": "ISC",
21 | "dependencies": {
22 | "@grpc/grpc-js": "1.8.13",
23 | "@grpc/proto-loader": "0.7.6",
24 | "@koa/cors": "3.3.0",
25 | "axios": "1.3.4",
26 | "content-disposition": "^0.5.4",
27 | "crypto-js": "4.1.1",
28 | "form-data": "^4.0.0",
29 | "iconv-lite": "^0.6.3",
30 | "jquery": "3.6.1",
31 | "jsdom": "20.0.1",
32 | "koa": "2.13.4",
33 | "koa-body": "5.0.0",
34 | "pm2": "5.2.0",
35 | "postman-sandbox": "^4.2.3",
36 | "socket.io": "4.5.3",
37 | "ws": "8.8.1",
38 | "xml2js": "0.4.23"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/node/test-server/request/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "MAX_TIME_LIMIT": 3600000,
3 | "MAX_TIME_DELAY": 3600000,
4 | "REQUEST_BODY_LIMIT_STORAGE_LENGTH": 1048576
5 | }
6 |
--------------------------------------------------------------------------------
/src/node/test-server/request/domain.json:
--------------------------------------------------------------------------------
1 | [
2 | "com",
3 | "cn",
4 | "xin",
5 | "net",
6 | "top",
7 | "xyz",
8 | "wang",
9 | "shop",
10 | "site",
11 | "club",
12 | "cc",
13 | "fun",
14 | "online",
15 | "biz",
16 | "red",
17 | "link",
18 | "ltd",
19 | "mobi",
20 | "info",
21 | "org",
22 | "name",
23 | "vip",
24 | "pro",
25 | "work",
26 | "tv",
27 | "kim",
28 | "group",
29 | "tech",
30 | "store",
31 | "ren",
32 | "ink",
33 | "pub",
34 | "live",
35 | "wiki",
36 | "design",
37 | "ai",
38 | "me",
39 | "io",
40 | "test",
41 | "example",
42 | "invalid",
43 | "localhost"
44 | ]
45 |
--------------------------------------------------------------------------------
/src/node/test-server/request/libs/script-engines/vm2/index.js:
--------------------------------------------------------------------------------
1 | if (parseInt(process.versions.node.split('.')[0]) < 6) throw new Error('vm2 requires Node.js version 6 or newer.');
2 |
3 | module.exports = require('./lib/main');
4 |
--------------------------------------------------------------------------------
/src/node/test-server/request/libs/script-engines/vm2/lib/cli.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const pa = require('path');
4 |
5 | const { NodeVM, VMError } = require('..');
6 |
7 | if (process.argv[2]) {
8 | const path = pa.resolve(process.argv[2]);
9 |
10 | console.log(`\x1B[90m[vm] creating VM for ${path}\x1B[39m`);
11 | const started = Date.now();
12 |
13 | try {
14 | NodeVM.file(path, {
15 | verbose: true,
16 | require: {
17 | external: true
18 | }
19 | });
20 |
21 | console.log(`\x1B[90m[vm] VM completed in ${Date.now() - started}ms\x1B[39m`);
22 | } catch (ex) {
23 | if (ex instanceof VMError) {
24 | console.error(`\x1B[31m[vm:error] ${ex.message}\x1B[39m`);
25 | } else {
26 | const { stack } = ex;
27 |
28 | if (stack) {
29 | console.error(`\x1B[31m[vm:error] ${stack}\x1B[39m`);
30 | } else {
31 | console.error(`\x1B[31m[vm:error] ${ex}\x1B[39m`);
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/node/test-server/request/libs/script-engines/vm2/lib/wildcard.js:
--------------------------------------------------------------------------------
1 | const match = (wildcard, s) => {
2 | const regexString = wildcard.replace(/\*/, '\\S*').replace(/\?/g, '.');
3 | const regex = new RegExp(regexString);
4 | return regex.test(s);
5 | };
6 |
7 | module.exports = { match };
8 |
--------------------------------------------------------------------------------
/src/node/test-server/server/README.md:
--------------------------------------------------------------------------------
1 | # Postcat-Test-Server
2 | Only deploy in web server.
3 | Recieve postcat test config and response http result
4 |
5 | > This service does not automatically install dependencies,please install it manually
6 |
7 | # Command
8 |
9 | dev/star
10 | ```
11 | yarn start
12 | ```
--------------------------------------------------------------------------------
/src/platform/electron-browser/i18n.ts:
--------------------------------------------------------------------------------
1 | import { LANGUAGES } from '../../browser/src/app/core/services/language/language.model';
2 | import { getLocaleData } from '../node/i18n';
3 | export class I18N {
4 | constructor() {}
5 | getSystemLanguage() {
6 | // let deafultLanguage =
7 | // LANGUAGES.find(val => window.location.href.includes(`/${val.path}`))?.value ||
8 | // (navigator.language.includes('zh') ? 'zh-Hans' : 'en-US');
9 | // return window.eo.getSettings()['eoapi-language'] || deafultLanguage;
10 | }
11 | localize(id: string, originText: string, ...args) {
12 | // let result = originText;
13 | // const locale: Object = getLocaleData(window.eo.getModule(window.eo._currentExtensionID), this.getSystemLanguage());
14 | // if (!locale) return result;
15 | // result ??= locale[id];
16 | // result = result.replace(/\{(\d+)\}/g, (match, rest) => {
17 | // let index = rest[0];
18 | // let arg = args[index];
19 | // let replacement = match;
20 | // if (typeof arg === 'string') {
21 | // replacement = arg;
22 | // } else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) {
23 | // replacement = String(arg);
24 | // }
25 | // return replacement;
26 | // });
27 | // return result;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/platform/node/constant.ts:
--------------------------------------------------------------------------------
1 | const args = process.argv.slice(1);
2 | export const processEnv = args.some((val) => val === '--serve')
3 | ? 'serve'
4 | : args.some((val) => val === '--development')
5 | ? 'development'
6 | : 'production';
7 |
--------------------------------------------------------------------------------
/src/platform/node/extension-manager/handler.model.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 模块管理器配置
3 | * baseDir 模块安装目录
4 | * registry 模块下载源(NPM源)
5 | * proxy 代理服务器
6 | */
7 | export interface ModuleHandlerOptions {
8 | baseDir: string;
9 | registry?: string;
10 | proxy?: string;
11 | }
12 |
13 | /**
14 | * 模块管理命令执行结果.
15 | */
16 | export interface ModuleHandlerResult {
17 | code: number;
18 | data: string;
19 | }
20 |
21 | /**
22 | * 模块管理信息
23 | * name 模块名称
24 | * isLocal 是否本地模块 (本地调用link, unlink安装与卸载)
25 | */
26 | export interface ModuleManagerInfo {
27 | name: string;
28 | isLocal?: boolean;
29 | }
30 |
--------------------------------------------------------------------------------
/src/platform/node/grpc/index.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/src/platform/node/grpc/index.ts
--------------------------------------------------------------------------------
/src/platform/node/i18n.ts:
--------------------------------------------------------------------------------
1 | import { I18nLocale, ExtensionInfo } from 'pc/browser/src/app/shared/models/extension-manager';
2 | interface LooseObject {
3 | [key: string]: any;
4 | }
5 | const localeStorage: LooseObject = {};
6 | /**
7 | * Get locale file from extension i18 file dir
8 | *
9 | * @param module
10 | * @returns json
11 | */
12 | function getLocaleFile(module: ExtensionInfo, lang): Object {
13 | let result = {};
14 | try {
15 | result = require(`${module.baseDir}/i18n/${lang}.json`);
16 | } catch (e) {}
17 | return result;
18 | }
19 | function getSupportLang(module: ExtensionInfo) {
20 | return [module.features.i18n.sourceLocale, ...module.features.i18n.locales].filter(val => val);
21 | }
22 | /**
23 | * Get locale data from storage or file
24 | *
25 | * @returns json
26 | */
27 | export function getLocaleData(module: ExtensionInfo, lang) {
28 | let supportLang = getSupportLang(module);
29 | if (!supportLang.includes(lang)) {
30 | console.error(`Error: extension ${module.title} can't find the i18n package ${lang}`);
31 | return null;
32 | }
33 | //Get and storage locale data
34 | localeStorage[module.name] = localeStorage[module.name] || {};
35 | if (!localeStorage[module.name][lang]) {
36 | localeStorage[module.name][lang] = getLocaleFile(module, lang);
37 | }
38 |
39 | return localeStorage[module.name][lang];
40 | }
41 |
--------------------------------------------------------------------------------
/src/platform/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | interface Window {
3 | process: any;
4 | requirejs: any;
5 | require: any;
6 | angular: any;
7 | eo: any;
8 | pc: any;
9 | }
10 |
--------------------------------------------------------------------------------
/src/shared/common/browserView.ts:
--------------------------------------------------------------------------------
1 | import { shell } from 'electron';
2 | //Open link through system default browser not Electron browserwin
3 | export function proxyOpenExternal(view) {
4 | view.webContents.setWindowOpenHandler(({ url }) => {
5 | setImmediate(() => {
6 | shell.openExternal(url);
7 | });
8 | return { action: 'deny' };
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/src/shared/common/common.ts:
--------------------------------------------------------------------------------
1 | export const isNotEmpty = (value: any) => {
2 | switch (typeof value) {
3 | case 'undefined':
4 | return false;
5 | case 'string':
6 | return value.length !== 0;
7 | case 'object':
8 | if (Array.isArray(value)) {
9 | return value.length !== 0;
10 | } else if (value === null) {
11 | return false;
12 | } else {
13 | return Object.keys(value).length !== 0;
14 | }
15 | default:
16 | return true;
17 | }
18 | };
19 |
20 |
--------------------------------------------------------------------------------
/src/shared/electron-main/constant.ts:
--------------------------------------------------------------------------------
1 | import { app } from 'electron';
2 |
3 | import * as path from 'path';
4 |
5 | export const home: string = app.getPath('home');
6 | export const HOME_DIR = path.join(home, '.postcat');
7 | export const STORAGE_TEMP = path.join(HOME_DIR, 'tmp.storage');
8 |
9 | export const GET_MODULES = 'getModules';
10 | export const GET_MODULE = 'getModule';
11 | export const INSTALL_MODULE = 'installModule';
12 | export const UNINSTALL_MODULE = 'uninstallModule';
13 | export const GET_FEATURE = 'getFeature';
14 | export const GET_MOCK_URL = 'getMockUrl';
15 | export const GET_WEBSOCKET_PORT = 'getWebsocketPort';
16 | export const LOGIN_WITH = 'loginWith';
17 | export const GET_SIDEBAR_VIEW = 'getSidebarView';
18 | export const GET_SIDEBAR_VIEWS = 'getSidebarViews';
19 |
--------------------------------------------------------------------------------
/src/shared/node/module.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as resolve from 'resolve';
3 |
4 | /**
5 | * 获取模块路径.
6 | * @param name
7 | */
8 | export const resolveModule = (name: string, baseDir: string): string => {
9 | try {
10 | return require.resolve(name, { paths: [baseDir] });
11 | } catch (err) {
12 | try {
13 | return resolve.sync(name, { basedir: baseDir });
14 | } catch (err) {
15 | return path.join(baseDir, 'node_modules', name);
16 | }
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/test/e2e/.auth/user.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/.auth/user.json
--------------------------------------------------------------------------------
/test/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | /images/*.png
2 | /test-results/**/*.png
3 | /playwright-report/**/*
--------------------------------------------------------------------------------
/test/e2e/assets/import-file/postcat.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/assets/import-file/postcat.json
--------------------------------------------------------------------------------
/test/e2e/assets/import-file/postman.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/assets/import-file/postman.json
--------------------------------------------------------------------------------
/test/e2e/assets/import-file/swagger.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/assets/import-file/swagger.json
--------------------------------------------------------------------------------
/test/e2e/mikasa/README.md:
--------------------------------------------------------------------------------
1 | ## 使用步骤
2 |
3 | ### 环境配置
4 |
5 | 1. 全局安装 ark 工具包:`yarn add ark-pkg --global`
6 | 2. 在 /e2e 目录下安装playwright的相关依赖:`yarn`
7 |
8 | 另:为了让测试用例有语法高亮,请将其命名为 .t 后缀。
9 |
10 | ### 运行
11 |
12 | 运行已有的所有测试用例,在 /e2e 目录下执行:
13 | ```bash
14 | $ ark mikasa ./ # 编译用例
15 | $ yarn test # 运行用例
16 |
17 | ```
18 | 即可运行并打印出测试报告
19 |
20 | 另一种情况是,需要单独运行某一个用例,在这种模式下,编译后的代码可以使用 NodeJS 直接运行,多数用在排查问题或写用例时单独看运行效果。
21 |
22 | ```
23 | $ ark mikasa ./ -d # debug 模式编译
24 | $ node xxx.test.js
25 | ```
26 |
--------------------------------------------------------------------------------
/test/e2e/mikasa/api.t:
--------------------------------------------------------------------------------
1 | === API
2 |
3 | --- API test
4 |
5 | goto "http://localhost:4200"
6 |
7 | find:
8 | [img { width: 14px }] [img { width: 14px }] = history [label "New Request"]
9 | [select "POST"] = method [input] = input [button "Send"] = sendBtn
10 | [label "Headers"] = header
11 |
12 | method -> "GET"
13 | input -> "https://weibo.com/ajax/side/cards/sideInterested?count=60"
14 | sendBtn -> click
15 | wait 3000
16 |
17 | find:
18 | [label "Response"]=res
19 |
20 | res -> click
21 | capture
22 |
23 | history -> click
24 |
25 | find:
26 | [label "https://weibo.com/ajax/side/cards/sideInterested?count=60"] = target
27 |
28 | wait
29 |
30 | --- add New API
31 |
32 | goto "http://localhost:4200"
33 |
34 | find:
35 | [input 'Search' { height: 22px }] [button '' { height: 32px; width: 32px }] = addBtn
36 |
37 | addBtn -> hover
38 |
39 | find:
40 | [label "New API"]=newApi
41 | [label "New Group"]
42 |
43 | newApi -> click
44 |
45 | find:
46 | [button "Save"] = save
47 | [select "POST"] = method [input "/"] = path
48 | [select "Root Group"] [input] = name
49 | # [input]=paramName [input]=desc [input]=example
50 |
51 | method -> "GET"
52 | # TODO path => '/api/data'
53 | path -> "api/data"
54 | name -> "yoo"
55 | # paramName -> 'a'
56 | # desc -> 'adesc'
57 | # example -> 'a Example'
58 | # save -> click
59 |
60 | capture
61 |
--------------------------------------------------------------------------------
/test/e2e/mikasa/tifa.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // 指定浏览器尺寸
3 | windowSize: [1280, 920],
4 | // 用于指定浏览器,默认 chromium
5 | use: ['chromium'],
6 | // 用于指定是否无 UI,默认为 false
7 | headless: false,
8 | // 是否打开开发者工具?默认为 false
9 | devtools: false,
10 | // 用于指定截图保存的位置
11 | captureUrl: './imgs',
12 | // 用于指定视频输出的位置
13 | video: false,
14 | videoUrl: './videos',
15 | // 用于指定 Gif 输出的位置
16 | gifUrl: './gif',
17 | successDoc: false,
18 | // 用于指定是否生成失败的操作文档,会包括描述和截图
19 | failDoc: false,
20 | elements: {
21 | img: ['eo-iconpark-icon']
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/test/e2e/mikasa/ws.t:
--------------------------------------------------------------------------------
1 | === Websocket
2 |
3 | --- add
4 |
5 | goto "http://localhost:4200"
6 |
7 | find:
8 | [label "New Request"] [img] = plus [img]
9 |
10 | plus -> hover
11 | wait
12 |
13 | find:
14 | [label "HTTP"]
15 | [label "Websocket"] = ws
16 |
17 | ws -> click
18 |
19 | find:
20 | [input] = urlInput [button "Connect"] = connect
21 |
22 | urlInput -> "wss://echo-websocket.hoppscotch.io"
23 | connect -> click
24 |
25 | wait 5000
26 |
27 | find:
28 | [button "Disconnect"] = disconnect
29 |
30 | disconnect -> click
31 |
32 | find:
33 | [label "Disconnect from wss://echo-websocket.hoppscotch.io"] = disconnectSuccess
34 | [label "Connected to wss://echo-websocket.hoppscotch.io"] = connectSuccess
35 |
36 | # check history
37 |
38 | find:
39 | [img] [img] = historyIcon [label "New Request"] = newRequest
40 |
41 | newRequest -> click
42 | historyIcon -> click
43 |
44 | find:
45 | [label "History"]
46 | [label "wss://echo-websocket.hoppscotch.io"] = targetHistory
47 |
48 | targetHistory -> click
49 |
50 | find:
51 | [label "Disconnect from wss://echo-websocket.hoppscotch.io"] = disconnectSuccess
52 | [label "Connected to wss://echo-websocket.hoppscotch.io"] = connectSuccess
--------------------------------------------------------------------------------
/test/e2e/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "postcat-e2e",
3 | "version": "1.0.0",
4 | "license": "MIT",
5 | "scripts": {
6 | "codegen": "playwright codegen localhost:4200",
7 | "test": "playwright test",
8 | "ui": "playwright test --ui"
9 | },
10 | "devDependencies": {
11 | "@playwright/test": "^1.32.1",
12 | "playwright": "^1.32.1"
13 | },
14 | "dependencies": {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/e2e/tests/api-mock.spec.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/tests/api-mock.spec.ts
--------------------------------------------------------------------------------
/test/e2e/tests/env.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | import { adaTabledRow, addEnv, ECHO_API_URL, ifTipsExist } from '../utils/commom.util';
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/');
6 | await page.getByRole('button', { name: 'Got it' }).click();
7 | await addEnv(page, {
8 | name: 'DEV'
9 | });
10 | });
11 | test('Env Basic', async ({ page }) => {
12 | //Add first env will choose it
13 | await page.locator('nz-tree-node-title div').first().click();
14 |
15 | //Edit env
16 | await page.getByLabel('Name').press('Meta+s');
17 | await ifTipsExist(page, 'Edited successfully');
18 |
19 | //Delete env
20 | await page.locator('nz-tree-node-title div').first().hover();
21 | await page.locator('nz-tree-node-title').getByRole('button').click();
22 | await page.getByRole('button', { name: 'Delete' }).click();
23 | await ifTipsExist(page, 'Successfully deleted');
24 | });
25 | // test('Preview Env', async ({ page }) => {
26 | // //Host uri
27 | // //Global variable
28 | // //Change env will change host uri
29 | // });
30 |
--------------------------------------------------------------------------------
/test/e2e/tests/extension.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 | const installExtension = async () => {};
3 | test.beforeEach(async ({ page }) => {
4 | await page.goto('/');
5 | await page.getByRole('button', { name: 'Got it' }).click();
6 | });
7 |
8 | // test('Basic Operate', async ({ page }) => {
9 | // //Install Extension
10 | // //Close Extension
11 | // //Open Extension
12 | // //Uninstall Extension
13 | // //Switch Extension Type
14 | // //Search Extension
15 | // });
16 | // test('Sync URL From TEST', async ({ page }) => {});
17 | // test('Import Swagger', async ({ page }) => {
18 | // await page.getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' }).click();
19 | // await page
20 | // .getByRole('button', { name: 'Tap or drag files directly to this area Only supports importing a single file' })
21 | // .setInputFiles('Postcat-Export-0403.json');
22 | // });
23 | // test('APISpace Extension', async ({ page }) => {});
24 | test('Export API', async ({ page }) => {
25 | await page.goto('/');
26 | await page.locator('a:has-text("Setting")').click();
27 | await page.getByRole('button', { name: 'Export' }).click();
28 | await page.getByRole('button', { name: 'Confirm' }).click();
29 | });
30 |
--------------------------------------------------------------------------------
/test/e2e/tests/member.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | import { login } from '../utils/commom.util';
4 | // test.beforeEach(async ({ page }) => {
5 | // await page.goto('/');
6 | // });
7 | // test('Workspace Member', async ({ page }) => {
8 | // //Login
9 | // await login(page);
10 | // //Switch to cloud workspace
11 | // //Add member to workspace
12 | // //Change role,default
13 | // //Remove member
14 | // //Add Member
15 | // //Login with new member
16 | // //Quit workspace
17 | // });
18 | // test('Project Member', async ({ page }) => {
19 | // //Add member to workspace
20 | // //Add member to project
21 | // });
22 |
--------------------------------------------------------------------------------
/test/e2e/tests/mock.spec.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/tests/mock.spec.ts
--------------------------------------------------------------------------------
/test/e2e/tests/project.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 | // test.beforeEach(async ({ page }) => {
3 | // await page.goto('/');
4 | // await page.getByRole('button', { name: 'Got it' }).click();
5 | // });
6 | // test('Basic Operate', async ({ page }) => {
7 | // //Back to Project List
8 | // //Add project
9 | // //Edit project
10 | // //Delete project
11 | // });
12 | // //Share Project
13 |
--------------------------------------------------------------------------------
/test/e2e/tests/tab.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | import { ECHO_API_URL, ifTipsExist } from '../utils/commom.util';
4 | // test.describe('Env Operate', () => {
5 | // const url = new URL(ECHO_API_URL);
6 | // test.beforeEach(async ({ page }) => {
7 | // await page.goto('/');
8 | // });
9 | // test('Baisc Opeate', async ({ page }) => {});
10 | // test('Fixed Tab', async ({ page }) => {});
11 | // test('Unsaved Tab', async ({ page }) => {});
12 | // test('Unsaved Tab', async ({ page }) => {});
13 | // });
14 |
--------------------------------------------------------------------------------
/test/e2e/tests/websocket-test.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, chromium } from '@playwright/test';
2 |
3 | import { adaTabledRow, addEnv, addTextToEditor, ECHO_API_URL, ifTipsExist } from '../utils/commom.util';
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/');
6 | await page.getByRole('button', { name: 'Got it' }).click();
7 | });
8 | /**
9 | * Basic Test
10 | */
11 | test('Websocekt Test', async ({ page }) => {
12 | await page.locator('eo-tab').getByRole('button').first().hover();
13 | await page.getByText('Websocket').click();
14 |
15 | //Connect
16 | await page.getByPlaceholder('Enter URL').click();
17 | await page
18 | .getByPlaceholder('Enter URL')
19 | .fill('wss://demo.piesocket.com/v3/channel_1?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self=');
20 | await page.getByRole('button', { name: 'Connect' }).click();
21 | await ifTipsExist(page, 'Connected to');
22 |
23 | //Send Body
24 | await addTextToEditor(page, 'i am a body');
25 | await page.getByRole('button', { name: 'Send' }).click();
26 | await page.getByRole('list').getByText('i am a body').isVisible();
27 |
28 | //Disconnect
29 | await page.getByRole('button', { name: 'Disconnect' }).click();
30 | await ifTipsExist(page, 'Disconnect from');
31 | });
32 |
--------------------------------------------------------------------------------
/test/e2e/tests/workspace.spec.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/e2e/tests/workspace.spec.ts
--------------------------------------------------------------------------------
/test/e2e/utils/remote.setup.ts:
--------------------------------------------------------------------------------
1 | // remote.setup.ts
2 | import { test as setup } from '@playwright/test';
3 |
4 | import { login } from './commom.util';
5 |
6 | const authFile = 'playwright/.auth/user.json';
7 |
8 | // setup('authenticate', async ({ page }) => {
9 | // // Perform authentication steps. Replace these actions with your own.
10 | // await page.goto('/');
11 | // await login(page);
12 |
13 | // // End of authentication steps.
14 |
15 | // await page.context().storageState({ path: authFile });
16 | // });
17 |
--------------------------------------------------------------------------------
/test/unit/.gitignore:
--------------------------------------------------------------------------------
1 | !*.js
--------------------------------------------------------------------------------
/test/unit/electron/extension_manage.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/test/unit/electron/extension_manage.js
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
5 | "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
6 | "allowJs": true,
7 | "checkJs": false,
8 | "sourceMap": true,
9 | "declaration": false,
10 | "moduleResolution": "node",
11 | "emitDecoratorMetadata": false,
12 | "allowSyntheticDefaultImports": true,
13 | "esModuleInterop": true,
14 | "experimentalDecorators": true,
15 | "resolveJsonModule": true,
16 | "skipLibCheck": true,
17 | "module": "commonjs",
18 | "outDir": "./out",
19 | "rootDir": "./src",
20 | "target": "es6",
21 | "types": ["node"],
22 | "lib": ["ESNext", "dom"],
23 | "baseUrl": ".",
24 | "paths": {
25 | "pc/*": ["./src/*"]
26 | }
27 | },
28 | "include": [
29 | "**/*.d.ts",
30 | "./src/**/**.ts",
31 | "./src/**/**.js",
32 | "scripts/build.js",
33 | "test/e2e/test.ts",
34 | "test/e2e/test.ts",
35 | "src/node/extensions-manage/extension-manage.js",
36 | "src/node/test-server/ecosystem.config.js"
37 | ],
38 | "exclude": ["node_modules", "**/*.spec.ts", "**/browser/**/*.js", "**/browser/**/*.ts", "out"],
39 | "angularCompilerOptions": {
40 | "enableIvy": true
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/wiki/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Postcatlab/postcat/b3dbb57ef94ceb5f4111338594fa3345d6527dee/wiki/images/logo.png
--------------------------------------------------------------------------------