├── test
├── e2e
│ ├── src
│ │ ├── mock.spec.ts
│ │ ├── workspace.spec.ts
│ │ ├── websocket-test.spec.ts
│ │ ├── export_api.spec.ts
│ │ ├── project.spec.ts
│ │ ├── tab.spec.ts
│ │ ├── member.spce.ts
│ │ ├── extension.spec.ts
│ │ └── commom.util.ts
│ ├── .gitignore
│ ├── package.json
│ ├── mikasa
│ │ ├── README.md
│ │ ├── tifa.config.js
│ │ ├── ws.t
│ │ └── api.t
│ └── playwright.config.ts
├── unit
│ ├── .gitignore
│ └── electron
│ │ └── extension_manage.js
└── images
│ └── unit-test-basic.png
├── src
├── platform
│ ├── node
│ │ ├── grpc
│ │ │ └── index.ts
│ │ ├── constant.ts
│ │ ├── extension-manager
│ │ │ └── handler.model.ts
│ │ └── i18n.ts
│ ├── typings.d.ts
│ └── electron-browser
│ │ └── i18n.ts
├── workbench
│ ├── browser
│ │ ├── src
│ │ │ ├── assets
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── images
│ │ │ │ │ ├── qq.png
│ │ │ │ │ ├── heart.png
│ │ │ │ │ ├── feishu.png
│ │ │ │ │ └── github.png
│ │ │ │ └── icons
│ │ │ │ │ └── iconLogo.png
│ │ │ ├── styles
│ │ │ │ ├── light.less
│ │ │ │ ├── dark.less
│ │ │ │ └── variables.css
│ │ │ ├── app
│ │ │ │ ├── layouts
│ │ │ │ │ ├── page-blank
│ │ │ │ │ │ ├── page-blank.component.scss
│ │ │ │ │ │ ├── page-blank.component.html
│ │ │ │ │ │ └── page-blank.component.ts
│ │ │ │ │ ├── page-not-found
│ │ │ │ │ │ ├── page-not-found.component.scss
│ │ │ │ │ │ ├── page-not-found.component.html
│ │ │ │ │ │ ├── page-not-found.component.ts
│ │ │ │ │ │ └── page-not-find.module.ts
│ │ │ │ │ ├── navbar
│ │ │ │ │ │ ├── navbar.component.scss
│ │ │ │ │ │ ├── breadcrumb
│ │ │ │ │ │ │ ├── nav-breadcrumb.component.scss
│ │ │ │ │ │ │ ├── select-workspace
│ │ │ │ │ │ │ │ └── select-workspace.component.scss
│ │ │ │ │ │ │ ├── nav-breadcrumb.component.html
│ │ │ │ │ │ │ └── nav-breadcrumb.component.ts
│ │ │ │ │ │ ├── help-dropdown
│ │ │ │ │ │ │ └── help-dropdown.component.ts
│ │ │ │ │ │ └── navbar.module.ts
│ │ │ │ │ ├── local-workspace-tip
│ │ │ │ │ │ └── local-workspace-tip.component.scss
│ │ │ │ │ ├── toolbar
│ │ │ │ │ │ ├── toolbar.module.ts
│ │ │ │ │ │ ├── toolbar.component.scss
│ │ │ │ │ │ └── toolbar.component.html
│ │ │ │ │ └── sidebar
│ │ │ │ │ │ ├── sidebar.model.ts
│ │ │ │ │ │ ├── sidebar.component.html
│ │ │ │ │ │ ├── sidebar.service.ts
│ │ │ │ │ │ └── sidebar.component.scss
│ │ │ │ ├── modules
│ │ │ │ │ ├── eo-ui
│ │ │ │ │ │ ├── setting
│ │ │ │ │ │ │ ├── setting.component.scss
│ │ │ │ │ │ │ └── setting.module.ts
│ │ │ │ │ │ ├── shadow
│ │ │ │ │ │ │ └── shadow-dom-encapsulation.module.ts
│ │ │ │ │ │ ├── iconpark-icon
│ │ │ │ │ │ │ ├── eo-iconpark-icon.module.ts
│ │ │ │ │ │ │ └── eo-iconpark-icon.component.ts
│ │ │ │ │ │ ├── table-pro
│ │ │ │ │ │ │ ├── table-pro.component.scss
│ │ │ │ │ │ │ └── table-pro.module.ts
│ │ │ │ │ │ ├── monaco-editor
│ │ │ │ │ │ │ ├── monaco-editor.component.html
│ │ │ │ │ │ │ ├── monaco.module.ts
│ │ │ │ │ │ │ └── monaco-editor.component.scss
│ │ │ │ │ │ ├── NOTE.md
│ │ │ │ │ │ ├── collapse
│ │ │ │ │ │ │ ├── collapse.module.ts
│ │ │ │ │ │ │ └── collapse.component.scss
│ │ │ │ │ │ └── tab
│ │ │ │ │ │ │ └── tab.module.ts
│ │ │ │ │ ├── logo
│ │ │ │ │ │ ├── logo.component.scss
│ │ │ │ │ │ └── logo.module.ts
│ │ │ │ │ ├── api-shared
│ │ │ │ │ │ ├── api-test-result-header
│ │ │ │ │ │ │ ├── api-test-result-header.component.scss
│ │ │ │ │ │ │ ├── api-test-result-header.component.html
│ │ │ │ │ │ │ └── api-test-result-header.component.ts
│ │ │ │ │ │ ├── params-import
│ │ │ │ │ │ │ ├── params-import.component.scss
│ │ │ │ │ │ │ └── params-import.component.html
│ │ │ │ │ │ └── pipe
│ │ │ │ │ │ │ ├── api-formater.pipe.ts
│ │ │ │ │ │ │ └── api-param-num.pipe.ts
│ │ │ │ │ ├── system-setting
│ │ │ │ │ │ ├── common
│ │ │ │ │ │ │ ├── token.component.scss
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ └── select-theme
│ │ │ │ │ │ │ │ └── select-theme.component.scss
│ │ │ │ │ │ └── system-setting.component.scss
│ │ │ │ │ ├── pc-console
│ │ │ │ │ │ ├── pc-console
│ │ │ │ │ │ │ ├── pc-console.component.scss
│ │ │ │ │ │ │ └── pc-console.component.ts
│ │ │ │ │ │ ├── pc-console.module.ts
│ │ │ │ │ │ └── debug-theme
│ │ │ │ │ │ │ └── debug-theme.component.ts
│ │ │ │ │ ├── member-list
│ │ │ │ │ │ ├── member-list.component.scss
│ │ │ │ │ │ ├── member-list.module.ts
│ │ │ │ │ │ └── member.service.ts
│ │ │ │ │ ├── nps-mask
│ │ │ │ │ │ ├── nps-mask.module.ts
│ │ │ │ │ │ └── component
│ │ │ │ │ │ │ ├── nps-mask.component.scss
│ │ │ │ │ │ │ └── nps-mask.component.ts
│ │ │ │ │ ├── chat-robot
│ │ │ │ │ │ ├── chat-robot.service.ts
│ │ │ │ │ │ ├── chat-robot-container
│ │ │ │ │ │ │ └── chat-robot.component.scss
│ │ │ │ │ │ ├── chat-robot-message
│ │ │ │ │ │ │ └── chat-robot-message.component.scss
│ │ │ │ │ │ ├── chat-robot.module.ts
│ │ │ │ │ │ └── chat-robot-form
│ │ │ │ │ │ │ └── chat-robot-form.component.ts
│ │ │ │ │ ├── extension-select
│ │ │ │ │ │ ├── select
│ │ │ │ │ │ │ └── extension-select.component.scss
│ │ │ │ │ │ ├── sync-api
│ │ │ │ │ │ │ └── schema.ts
│ │ │ │ │ │ ├── import-api
│ │ │ │ │ │ │ └── old2new.ts
│ │ │ │ │ │ └── extension-select.module.ts
│ │ │ │ │ ├── download-client
│ │ │ │ │ │ └── download-client.module.ts
│ │ │ │ │ └── star-motivation
│ │ │ │ │ │ └── star-motivation.component.ts
│ │ │ │ ├── pages
│ │ │ │ │ ├── workspace
│ │ │ │ │ │ ├── project
│ │ │ │ │ │ │ ├── api
│ │ │ │ │ │ │ │ ├── grpc
│ │ │ │ │ │ │ │ │ ├── grpc.component.scss
│ │ │ │ │ │ │ │ │ ├── grpc-routing.module.ts
│ │ │ │ │ │ │ │ │ ├── grpc.module.ts
│ │ │ │ │ │ │ │ │ ├── grpc.component.ts
│ │ │ │ │ │ │ │ │ └── grpc.component.html
│ │ │ │ │ │ │ │ ├── env
│ │ │ │ │ │ │ │ │ ├── env-edit
│ │ │ │ │ │ │ │ │ │ └── env-edit.component.scss
│ │ │ │ │ │ │ │ │ ├── env-list
│ │ │ │ │ │ │ │ │ │ └── env-list.component.scss
│ │ │ │ │ │ │ │ │ ├── env-select
│ │ │ │ │ │ │ │ │ │ └── env-select.component.scss
│ │ │ │ │ │ │ │ │ └── env.module.ts
│ │ │ │ │ │ │ │ ├── graph-ql
│ │ │ │ │ │ │ │ │ ├── test
│ │ │ │ │ │ │ │ │ │ ├── graph-ql-test.component.scss
│ │ │ │ │ │ │ │ │ │ ├── graph-ql-test.component.html
│ │ │ │ │ │ │ │ │ │ └── graph-ql-test.component.ts
│ │ │ │ │ │ │ │ │ └── graph-ql.module.ts
│ │ │ │ │ │ │ │ ├── http
│ │ │ │ │ │ │ │ │ ├── edit
│ │ │ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ │ │ │ └── api-edit-body.component.scss
│ │ │ │ │ │ │ │ │ │ ├── extra-setting
│ │ │ │ │ │ │ │ │ │ │ └── api-params-extra-setting.component.scss
│ │ │ │ │ │ │ │ │ │ └── api-edit.component.scss
│ │ │ │ │ │ │ │ │ ├── detail
│ │ │ │ │ │ │ │ │ │ ├── api-detail.service.ts
│ │ │ │ │ │ │ │ │ │ ├── api-detail.component.scss
│ │ │ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ │ │ │ └── api-detail-body.component.html
│ │ │ │ │ │ │ │ │ │ └── form
│ │ │ │ │ │ │ │ │ │ │ └── api-detail-form.component.ts
│ │ │ │ │ │ │ │ │ ├── test
│ │ │ │ │ │ │ │ │ │ ├── body
│ │ │ │ │ │ │ │ │ │ │ └── api-test-body.component.scss
│ │ │ │ │ │ │ │ │ │ ├── result-request-body
│ │ │ │ │ │ │ │ │ │ │ ├── api-test-result-request-body.component.scss
│ │ │ │ │ │ │ │ │ │ │ ├── api-test-result-request-body.component.ts
│ │ │ │ │ │ │ │ │ │ │ └── api-test-result-request-body.component.html
│ │ │ │ │ │ │ │ │ │ ├── api-test.model.ts
│ │ │ │ │ │ │ │ │ │ ├── api-test.service.ts
│ │ │ │ │ │ │ │ │ │ ├── api-script
│ │ │ │ │ │ │ │ │ │ │ └── api-script.component.scss
│ │ │ │ │ │ │ │ │ │ └── result-response
│ │ │ │ │ │ │ │ │ │ │ ├── api-test-result-response.component.scss
│ │ │ │ │ │ │ │ │ │ │ └── get-size.pipe.ts
│ │ │ │ │ │ │ │ │ └── mock
│ │ │ │ │ │ │ │ │ │ ├── api-mock.component.html
│ │ │ │ │ │ │ │ │ │ ├── edit
│ │ │ │ │ │ │ │ │ │ └── api-mock-edit.component.ts
│ │ │ │ │ │ │ │ │ │ └── api-mock.module.ts
│ │ │ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ │ │ ├── group
│ │ │ │ │ │ │ │ │ │ ├── edit
│ │ │ │ │ │ │ │ │ │ │ ├── api-group-edit.component.scss
│ │ │ │ │ │ │ │ │ │ │ └── api-group-edit.component.html
│ │ │ │ │ │ │ │ │ │ └── tree
│ │ │ │ │ │ │ │ │ │ │ ├── api-group-tree.component.scss
│ │ │ │ │ │ │ │ │ │ │ └── api-group-tree.directive.ts
│ │ │ │ │ │ │ │ │ └── history
│ │ │ │ │ │ │ │ │ │ └── eo-history.component.scss
│ │ │ │ │ │ │ │ ├── websocket
│ │ │ │ │ │ │ │ │ ├── websocket.routing.module.ts
│ │ │ │ │ │ │ │ │ └── websocket.module.ts
│ │ │ │ │ │ │ │ └── service
│ │ │ │ │ │ │ │ │ └── test-server
│ │ │ │ │ │ │ │ │ └── local-node
│ │ │ │ │ │ │ │ │ └── test-connect.service.ts
│ │ │ │ │ │ │ ├── setting
│ │ │ │ │ │ │ │ ├── project-setting.component.scss
│ │ │ │ │ │ │ │ └── project-setting.module.ts
│ │ │ │ │ │ │ ├── member
│ │ │ │ │ │ │ │ ├── project-member-routing.module.ts
│ │ │ │ │ │ │ │ └── project-member.module.ts
│ │ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ │ └── operate-project-form.compoent.ts
│ │ │ │ │ │ │ ├── project.module.ts
│ │ │ │ │ │ │ └── project-routing.module.ts
│ │ │ │ │ │ ├── workspace.component.ts
│ │ │ │ │ │ ├── overview
│ │ │ │ │ │ │ ├── workspace-overview.component.scss
│ │ │ │ │ │ │ └── project-list
│ │ │ │ │ │ │ │ ├── project-list.component.scss
│ │ │ │ │ │ │ │ └── project-list.module.ts
│ │ │ │ │ │ └── workspace.module.ts
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── third-login
│ │ │ │ │ │ │ └── third-login.component.scss
│ │ │ │ │ │ └── chatgpt-robot
│ │ │ │ │ │ │ └── chatgpt-robot.component.scss
│ │ │ │ │ ├── share-project
│ │ │ │ │ │ ├── share-project.component.scss
│ │ │ │ │ │ ├── share-project.module.ts
│ │ │ │ │ │ ├── share-project.component.ts
│ │ │ │ │ │ └── share-routing.module.ts
│ │ │ │ │ ├── extension
│ │ │ │ │ │ ├── list
│ │ │ │ │ │ │ └── extension-list.component.scss
│ │ │ │ │ │ ├── download-count-formater.pipe.ts
│ │ │ │ │ │ ├── extension-routing.module.ts
│ │ │ │ │ │ ├── extension.component.scss
│ │ │ │ │ │ └── detail
│ │ │ │ │ │ │ └── components
│ │ │ │ │ │ │ └── extensions-settings.component.ts
│ │ │ │ │ ├── pages.component.scss
│ │ │ │ │ ├── pages.component.html
│ │ │ │ │ └── pages-routing.module.ts
│ │ │ │ ├── shared
│ │ │ │ │ ├── constants
│ │ │ │ │ │ ├── protocol.ts
│ │ │ │ │ │ └── storageKeys.ts
│ │ │ │ │ ├── models
│ │ │ │ │ │ ├── extension-manager
│ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ ├── shared.model.ts
│ │ │ │ │ │ └── member.model.ts
│ │ │ │ │ ├── services
│ │ │ │ │ │ ├── message
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── message.model.ts
│ │ │ │ │ │ │ └── message.service.ts
│ │ │ │ │ │ ├── storage
│ │ │ │ │ │ │ ├── db
│ │ │ │ │ │ │ │ ├── utils
│ │ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ │ ├── dto
│ │ │ │ │ │ │ │ │ ├── common.dto.ts
│ │ │ │ │ │ │ │ │ ├── group.dto.ts
│ │ │ │ │ │ │ │ │ └── project.dto.ts
│ │ │ │ │ │ │ │ ├── tests
│ │ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ │ ├── services
│ │ │ │ │ │ │ │ │ ├── mock.service.ts
│ │ │ │ │ │ │ │ │ ├── apiTestHistory.service.ts
│ │ │ │ │ │ │ │ │ ├── workspace.service.ts
│ │ │ │ │ │ │ │ │ └── environment.service.ts
│ │ │ │ │ │ │ │ ├── schema
│ │ │ │ │ │ │ │ │ └── env.json
│ │ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ │ └── decorators
│ │ │ │ │ │ │ │ │ └── base-hook.decorator.ts
│ │ │ │ │ │ │ └── IndexedDB
│ │ │ │ │ │ │ │ └── schema
│ │ │ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ │ │ └── env.schema.json
│ │ │ │ │ │ └── trace.service.ts
│ │ │ │ │ ├── directives
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── stop-propagation.directive.ts
│ │ │ │ │ │ ├── trace.directive.ts
│ │ │ │ │ │ └── focus-form-input.directive.ts
│ │ │ │ │ └── components
│ │ │ │ │ │ └── download-client.component.ts
│ │ │ │ ├── core
│ │ │ │ │ ├── services
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── feature-control
│ │ │ │ │ │ │ └── feature.json
│ │ │ │ │ │ ├── language
│ │ │ │ │ │ │ └── language.model.ts
│ │ │ │ │ │ ├── errorHandle.service.ts
│ │ │ │ │ │ ├── theme
│ │ │ │ │ │ │ └── theme-extension.service.ts
│ │ │ │ │ │ └── notification.service.ts
│ │ │ │ │ └── core.module.ts
│ │ │ │ └── app.component.ts
│ │ │ ├── extensions
│ │ │ │ └── themes
│ │ │ │ │ ├── blue.json
│ │ │ │ │ ├── green.json
│ │ │ │ │ ├── orange.json
│ │ │ │ │ ├── dark.json
│ │ │ │ │ └── light.json
│ │ │ ├── environments
│ │ │ │ ├── environment.dev.ts
│ │ │ │ ├── environment.ts
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── common.constant.ts
│ │ │ ├── polyfills-test.ts
│ │ │ ├── tsconfig.app.json
│ │ │ ├── tsconfig.spec.json
│ │ │ ├── test.ts
│ │ │ ├── main.ts
│ │ │ ├── typings.d.ts
│ │ │ └── karma.conf.js
│ │ ├── .npmrc
│ │ ├── .vscode
│ │ │ ├── extensions.json
│ │ │ ├── launch.json
│ │ │ └── tasks.json
│ │ ├── .editorconfig
│ │ ├── README.md
│ │ ├── .eslintignore
│ │ ├── tailwind.config.js
│ │ ├── proxy.conf.json
│ │ ├── .gitignore
│ │ ├── tsconfig.json
│ │ └── .eslintrc.json
│ └── node
│ │ ├── README.md
│ │ ├── request
│ │ ├── config.json
│ │ ├── libs
│ │ │ └── vm2
│ │ │ │ ├── index.js
│ │ │ │ └── lib
│ │ │ │ ├── wildcard.js
│ │ │ │ └── cli.js
│ │ └── domain.json
│ │ ├── server
│ │ └── README.md
│ │ ├── electron
│ │ ├── main.ts
│ │ ├── unitWorker.ts
│ │ └── forkUnit.js
│ │ └── package.json
├── app
│ ├── common
│ │ └── images
│ │ │ ├── 16x16.png
│ │ │ ├── 24x24.png
│ │ │ ├── 32x32.png
│ │ │ ├── 48x48.png
│ │ │ ├── 64x64.png
│ │ │ ├── icon.icns
│ │ │ ├── logo.ico
│ │ │ ├── 128x128.png
│ │ │ ├── 256x256.png
│ │ │ ├── 512x512.png
│ │ │ ├── postcat.bmp
│ │ │ └── 1024x1024.png
│ ├── typings.d.ts
│ └── electron-main
│ │ └── language.service.ts
├── shared
│ ├── electron-main
│ │ └── constant.ts
│ ├── common
│ │ ├── browserView.ts
│ │ └── common.ts
│ └── node
│ │ └── module.ts
└── environment.ts
├── .stylelintignore
├── wiki
└── images
│ └── logo.png
├── .npmrc
├── .husky
├── commit-msg
├── pre-commit
└── common.sh
├── crowdin.yml
├── .vscode
├── extensions.json
├── ts.code-snippets
├── launch.json
└── tasks.json
├── .prettierignore
├── CONTRIBUTING.md
├── .editorconfig
├── prettier.config.js
├── scripts
├── afterPackHook.js
├── afterBuild.js
├── urlProtoco.nsh
├── entitlements.mac.plist
├── tools
│ ├── upgradeComponent.js
│ ├── debugComponents.js
│ └── unlinkComponents.js
├── notarize.js
├── afterInstall.js
└── beforeNSISBuild.js
├── .gitattributes
├── .eslintignore
├── .github
├── workflows
│ ├── sync-branch.yml
│ ├── crowdin.yml
│ ├── release.yml
│ └── build-windows.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── feature_request.yml
├── donkey.config.js
├── api
└── unit.js
├── tsconfig.json
└── .gitignore
/test/e2e/src/mock.spec.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/.gitignore:
--------------------------------------------------------------------------------
1 | !*.js
--------------------------------------------------------------------------------
/src/platform/node/grpc/index.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | /images/*.png
--------------------------------------------------------------------------------
/test/e2e/src/workspace.spec.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/e2e/src/websocket-test.spec.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/electron/extension_manage.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | /public/*
3 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/styles/light.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/node/README.md:
--------------------------------------------------------------------------------
1 | # Node Test server
2 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/page-blank/page-blank.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/eo-ui/setting/setting.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/page-not-found/page-not-found.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/grpc/grpc.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/wiki/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/wiki/images/logo.png
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/env/env-edit/env-edit.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/constants/protocol.ts:
--------------------------------------------------------------------------------
1 | export const PROTOCOL = 'eoapi://';
2 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/edit/body/api-edit-body.component.scss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/models/extension-manager/index.ts:
--------------------------------------------------------------------------------
1 | export * from './module';
2 |
--------------------------------------------------------------------------------
/src/workbench/browser/.npmrc:
--------------------------------------------------------------------------------
1 | save=true
2 | save-exact=true
3 | registry=https://registry.npmmirror.com
4 |
--------------------------------------------------------------------------------
/src/app/common/images/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/16x16.png
--------------------------------------------------------------------------------
/src/app/common/images/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/24x24.png
--------------------------------------------------------------------------------
/src/app/common/images/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/32x32.png
--------------------------------------------------------------------------------
/src/app/common/images/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/48x48.png
--------------------------------------------------------------------------------
/src/app/common/images/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/64x64.png
--------------------------------------------------------------------------------
/src/app/common/images/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/icon.icns
--------------------------------------------------------------------------------
/src/app/common/images/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/logo.ico
--------------------------------------------------------------------------------
/test/images/unit-test-basic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/test/images/unit-test-basic.png
--------------------------------------------------------------------------------
/src/app/common/images/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/128x128.png
--------------------------------------------------------------------------------
/src/app/common/images/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/256x256.png
--------------------------------------------------------------------------------
/src/app/common/images/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/512x512.png
--------------------------------------------------------------------------------
/src/app/common/images/postcat.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/postcat.bmp
--------------------------------------------------------------------------------
/src/app/common/images/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/app/common/images/1024x1024.png
--------------------------------------------------------------------------------
/src/workbench/browser/src/extensions/themes/blue.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#2196f3"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/extensions/themes/green.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#41aa64"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/extensions/themes/orange.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#f47023"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/workbench/browser/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "bradlc.vscode-tailwindcss"
4 | ]
5 | }
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/graph-ql/test/graph-ql-test.component.html:
--------------------------------------------------------------------------------
1 |
graph-ql-test works!
2 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/message/index.ts:
--------------------------------------------------------------------------------
1 | export * from './message.model';
2 | export * from './message.service';
3 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/core/services/index.ts:
--------------------------------------------------------------------------------
1 | export * from './electron/electron.service';
2 | export * from './web/web.service';
3 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/components/third-login/third-login.component.scss:
--------------------------------------------------------------------------------
1 | .or {
2 | color: var(--text-secondary-color);
3 | }
4 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/images/qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/workbench/browser/src/assets/images/qq.png
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | save=true
2 | save-exact=true
3 | electron_mirror=https://npmmirror.com/mirrors/electron/
4 | registry=https://registry.npmmirror.com
5 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/images/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/workbench/browser/src/assets/images/heart.png
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/icons/iconLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/workbench/browser/src/assets/icons/iconLogo.png
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/images/feishu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/workbench/browser/src/assets/images/feishu.png
--------------------------------------------------------------------------------
/src/workbench/browser/src/assets/images/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ctfang/postcat/main/src/workbench/browser/src/assets/images/github.png
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/components/group/edit/api-group-edit.component.scss:
--------------------------------------------------------------------------------
1 | .ant-col {
2 | margin-bottom: 0;
3 | }
4 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/directives/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stop-propagation.directive';
2 | export * from './focus-form-input.directive';
3 |
--------------------------------------------------------------------------------
/src/workbench/node/request/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "MAX_TIME_LIMIT": 3600000,
3 | "MAX_TIME_DELAY":3600000,
4 | "REQUEST_BODY_LIMIT_STORAGE_LENGTH": 1048576
5 | }
6 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/page-blank/page-blank.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/shared/models/shared.model.ts:
--------------------------------------------------------------------------------
1 | export const REQURIED_ENUMS = [
2 | { title: $localize`Yes`, value: 1 },
3 | { title: $localize`No`, value: 0 }
4 | ];
5 |
--------------------------------------------------------------------------------
/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/api-shared/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/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/workbench/node/request/libs/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/workbench/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/workbench/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/workbench/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | files:
2 | - source: /src/workbench/browser/src/locale/messages.xlf
3 | translation: /src/workbench/browser/src/locale/messages.%two_letters_code%.xlf
4 | project_id_env: CROWDIN_PROJECT_ID
5 | api_token_env: CROWDIN_PERSONAL_TOKEN
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/shared/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/workbench/node/request/libs/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.scss:
--------------------------------------------------------------------------------
1 | @import '../group/tree/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/workbench/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/workbench/browser/src/app/modules/api-shared/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/shared/constants/storageKeys.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/workbench/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/environments/environment.dev.ts:
--------------------------------------------------------------------------------
1 | import { COMMON_CONFIG } from 'eo/workbench/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/src/workbench/browser/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | import { APP_CONFIG_INSTANT, COMMON_CONFIG } from 'eo/workbench/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/workbench/node/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 | ```
--------------------------------------------------------------------------------
/.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/workbench/browser/src/app/layouts/page-not-found/page-not-found.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Back Home
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/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/workbench/browser/src/app/pages/share-project/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/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 |
--------------------------------------------------------------------------------
/test/e2e/src/export_api.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 | test('Export API', async ({ page }) => {
3 | await page.goto('/');
4 | await page.locator('a:has-text("Setting")').click();
5 | await page.getByRole('button', { name: 'Export' }).click();
6 | await page.getByRole('button', { name: 'Confirm' }).click();
7 | });
8 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/shared/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/workbench/browser/src/app/modules/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | import { APP_CONFIG_INSTANT, COMMON_CONFIG } from 'eo/workbench/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/workbench/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/workbench/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/workbench/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/workbench/browser/src/app/modules/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/workbench/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/workbench/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/workbench/browser/src/app/layouts/page-blank/page-blank.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'eo-page-blank',
5 | templateUrl: './page-blank.component.html',
6 | styleUrls: ['./page-blank.component.scss'],
7 | })
8 | export class PageBlankComponent implements OnInit {
9 | constructor() {}
10 |
11 | ngOnInit(): void {}
12 | }
13 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/environments/common.constant.ts:
--------------------------------------------------------------------------------
1 | import { COMMON_APP_CONFIG } from 'eo/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/workbench/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 |
--------------------------------------------------------------------------------
/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/workbench/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 | const chunkFailedMessage = /Loading chunk [\d]+ failed/;
7 |
8 | if (chunkFailedMessage.test(error.message)) {
9 | window.location.reload();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/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/workbench/browser/src/app/modules/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/workbench/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 | "eo/*": ["../../../../src/*"]
10 | }
11 | },
12 | "files": ["main.ts", "polyfills.ts"],
13 | "include": ["**/*.d.ts"],
14 | "exclude": ["**/*.spec.ts", "./app/views/*"]
15 | }
16 |
--------------------------------------------------------------------------------
/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 | "test:watch": "playwright test --headed"
9 | },
10 | "devDependencies": {
11 | "@playwright/test": "^1.31.2",
12 | "playwright": "^1.31.1"
13 | },
14 | "dependencies": {
15 | "ark-pkg": "0.5.4"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/workbench/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "test.ts",
12 | "polyfills-test.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ],
18 | "exclude": [
19 | "dist",
20 | "release",
21 | "node_modules"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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 |
--------------------------------------------------------------------------------
/test/e2e/src/project.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 | test.describe('Project Operate', () => {
3 | test.beforeEach(async ({ page }) => {
4 | await page.goto('/');
5 | });
6 | test('Basic Operate', async ({ page }) => {
7 | //Back to Project List
8 | //Add project
9 | //Edit project
10 | //Delete project
11 | });
12 | test('Use Env', async ({ page }) => {
13 | //Host uri
14 | //Global variable
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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/workbench/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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/workbench/browser/src/app/shared/services/storage/db/services/mock.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'eo/workbench/browser/src/app/shared/services/storage/db/dataSource';
2 | import { Mock } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
3 | import { BaseService } from 'eo/workbench/browser/src/app/shared/services/storage/db/services/base.service';
4 |
5 | export class MockService extends BaseService {
6 | constructor() {
7 | super(dataSource.mock);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/pages/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/workbench/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 |
--------------------------------------------------------------------------------
/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/workbench/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/workbench/browser&&yarn upgrade ${dependencyList.join('@latest ')}@latest`);
10 |
--------------------------------------------------------------------------------
/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/workbench/browser/src/app/modules/api-shared/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/workbench/browser/src/app/shared/services/storage/db/dto/group.dto.ts:
--------------------------------------------------------------------------------
1 | export interface GroupDeleteDto {
2 | id?: number;
3 | projectUuid: string;
4 | workSpaceUuid: string;
5 | }
6 |
7 | export interface GroupCreateDto {
8 | name: string;
9 | type?: number;
10 | path?: string;
11 | depth?: number;
12 | parentId?: number;
13 | sort?: number;
14 | projectUuid?: string;
15 | workSpaceUuid?: string;
16 | }
17 |
18 | export interface GroupUpdateDto extends GroupCreateDto {
19 | id: number;
20 | }
21 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/toolbar/toolbar.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 |
4 | import { SharedModule } from '../../shared/shared.module';
5 | import { ToolbarComponent } from './toolbar.component';
6 |
7 | const COMPONENTS = [ToolbarComponent];
8 | @NgModule({
9 | declarations: [...COMPONENTS],
10 | imports: [CommonModule, SharedModule],
11 | providers: [],
12 | exports: [...COMPONENTS],
13 | })
14 | export class ToolbarModule {}
15 |
--------------------------------------------------------------------------------
/.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/workbench/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/workbench/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/workbench/browser/src/app/pages/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/workbench/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 'eo/workbench/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/workbench/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/workbench/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/workbench/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 |
--------------------------------------------------------------------------------
/test/e2e/src/tab.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | import { ECHO_API_URL, ifTipsExist } from './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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/modules/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/api-shared/api-test-result-header/api-test-result-header.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 |
3 | import { ApiTestResHeader } from '../../../pages/workspace/project/api/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/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/workbench/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 'eo/workbench/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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/workbench/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/workbench/browser/src/app/modules/api-shared/pipe/api-formater.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 | import { protocalMap, requestMethodMap } from 'eo/workbench/browser/src/app/modules/api-shared/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/workbench/browser/src/app/modules/eo-ui/monaco-editor/monaco-editor.component.html:
--------------------------------------------------------------------------------
1 |
7 |
15 |
--------------------------------------------------------------------------------
/src/workbench/browser/proxy.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api": {
3 | "target": "http://52.76.76.88:8080",
4 | "secure": false,
5 | "changeOrigin": true,
6 | "logLevel": "debug"
7 | },
8 | "/usercenter": {
9 | "target": "http://52.76.76.88:8080",
10 | "secure": false,
11 | "changeOrigin": true,
12 | "logLevel": "debug"
13 | }
14 | // "/api": {
15 | // "target": "https://mockapi.eolink.com/Dr31QyT4b832495fe945fb9420215f167e33b2dc1fb4f27",
16 | // "secure": false,
17 | // "changeOrigin": true,
18 | // "logLevel": "debug",
19 | // "pathRewrite": {
20 | // "^/api": ""
21 | // }
22 | // }
23 | }
24 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/api-shared/pipe/api-param-num.pipe.ts:
--------------------------------------------------------------------------------
1 | import { Pipe, PipeTransform } from '@angular/core';
2 |
3 | import { BodyParam } from '../../../shared/services/storage/db/models/apiData';
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/workbench/node/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 | ]
46 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/pages/workspace/project/api/http/test/api-test.model.ts:
--------------------------------------------------------------------------------
1 | export enum ApiTestParamsTypeFormData {
2 | text = 'string',
3 | file = 'file'
4 | }
5 | export const CONTENT_TYPE_BY_ABRIDGE = [
6 | {
7 | title: 'Text',
8 | value: 'text/plain'
9 | },
10 | {
11 | title: 'JSON',
12 | value: 'application/json'
13 | },
14 | {
15 | title: 'XML',
16 | value: 'application/xml'
17 | },
18 | {
19 | title: 'HTML',
20 | value: 'text/html'
21 | },
22 | {
23 | title: 'Javascript',
24 | value: 'application/javascript'
25 | }
26 | ] as const;
27 | export type ContentType = (typeof CONTENT_TYPE_BY_ABRIDGE)[number]['value'];
28 |
--------------------------------------------------------------------------------
/src/workbench/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 |
--------------------------------------------------------------------------------
/test/e2e/src/member.spce.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 |
3 | import { login } from './commom.util';
4 | test.describe('Member Operate', () => {
5 | test.beforeEach(async ({ page }) => {
6 | await page.goto('/');
7 | });
8 | test('Workspace Member', async ({ page }) => {
9 | //Login
10 | await login(page);
11 | //Switch to cloud workspace
12 | //Add member to workspace
13 | //Change role,default
14 | //Remove member
15 | //Add Member
16 | //Login with new member
17 | //Quit workspace
18 | });
19 | test('Project Member', async ({ page }) => {
20 | //Add member to workspace
21 | //Add member to project
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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/workbench/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/project.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { FormsModule } from '@angular/forms';
3 | import { ProjectRoutingModule } from 'eo/workbench/browser/src/app/pages/workspace/project/project-routing.module';
4 | import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
5 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
6 | import { NzCardModule } from 'ng-zorro-antd/card';
7 | import { NzFormModule } from 'ng-zorro-antd/form';
8 |
9 | @NgModule({
10 | imports: [ProjectRoutingModule, NzAvatarModule, NzCardModule, FormsModule, NzFormModule, SharedModule],
11 | declarations: []
12 | })
13 | export class ProjectModule {}
14 |
--------------------------------------------------------------------------------
/test/e2e/src/extension.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test';
2 | //TODO
3 | // const installExtension = async () => {};
4 | // test.describe('Extension Operate', () => {
5 | // test.beforeEach(async ({ page }) => {
6 | // await page.goto('/');
7 | // });
8 |
9 | // test('Basic Operate', async ({ page }) => {
10 | // //Install Extension
11 | // //Close Extension
12 | // //Open Extension
13 | // //Uninstall Extension
14 | // //Switch Extension Type
15 | // //Search Extension
16 | // });
17 | // test('Sync URL From TEST', async ({ page }) => {});
18 | // test('Import Swagger', async ({ page }) => {});
19 | // test('APISpace Extension', async ({ page }) => {});
20 | // });
21 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/share-project/share-project.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { ApiModule } from 'eo/workbench/browser/src/app/pages/workspace/project/api/api.module';
4 |
5 | import { NavbarModule } from '../../layouts/navbar/navbar.module';
6 | import { SharedModule } from '../../shared/shared.module';
7 | import { ShareComponent } from './share-project.component';
8 | import { ShareRoutingModule } from './share-routing.module';
9 |
10 | @NgModule({
11 | imports: [ShareRoutingModule, NavbarModule, CommonModule, SharedModule, ApiModule],
12 | declarations: [ShareComponent],
13 | providers: []
14 | })
15 | export class ShareProjectModule {}
16 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/db/schema/env.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "properties": {
4 | "createTime": {
5 | "type": "number"
6 | },
7 | "hostUri": {
8 | "type": "string"
9 | },
10 | "id": {
11 | "type": "number"
12 | },
13 | "name": {
14 | "type": "string"
15 | },
16 | "parameters": {
17 | "type": "string"
18 | },
19 | "projectUuid": {
20 | "type": "string"
21 | },
22 | "updateTime": {
23 | "type": "number"
24 | },
25 | "uuid": {
26 | "type": "string"
27 | },
28 | "workSpaceUuid": {
29 | "type": "string"
30 | }
31 | },
32 | "type": "object"
33 | }
34 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/navbar/breadcrumb/nav-breadcrumb.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Back
7 |
8 |
9 |
10 | {{ projectName }}
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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: `
7 |
8 | `,
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/workbench/browser/src/app/modules/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/workbench/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: 90px;
27 | --layout-sidebar-item-height: auto;
28 | --edit-inside-bar-height: 53px;
29 | }
30 |
--------------------------------------------------------------------------------
/test/e2e/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, devices } from '@playwright/test';
2 | export default defineConfig({
3 | workers: process.env.CI ? 2 : 10,
4 | use: {
5 | baseURL: 'http://localhost:4200',
6 | // baseURL: 'https://postcat.com',
7 | headless: true,
8 | viewport: { width: 1280, height: 720 },
9 | ignoreHTTPSErrors: true,
10 | video: 'on-first-retry',
11 | screenshot: 'only-on-failure'
12 | },
13 | projects: [
14 | {
15 | name: 'chromium',
16 | use: { ...devices['Desktop Chrome'] }
17 | },
18 | {
19 | name: 'firefox',
20 | use: { ...devices['Desktop Firefox'] }
21 | },
22 | {
23 | name: 'webkit',
24 | use: { ...devices['Desktop Safari'] }
25 | }
26 | ]
27 | });
28 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/extensions/themes/dark.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors": {
3 | "primary": "#7371fc",
4 | "text": "#fff",
5 | "inputIcon": "#fff",
6 | "textSecondary": "#ccc",
7 | "border": "rgba(255, 255, 255, 0.05)",
8 | "background": "#212121",
9 | "barBackground": "#212121",
10 | "disabledText": "#666",
11 | "disabledBackground": "#282828",
12 | "textLink": "#1890ff",
13 | "textLinkHover": "#40a9ff",
14 | "textLinkActive": "#096dd9",
15 | "divider": "rgba(255, 255, 255, 0.05)",
16 | "alertDefaultBackground": "rgba(149,149,149,.1)",
17 | "scrollbarTrackBackground": "#212121",
18 | "scrollbarThumbBackground": "rgba(255, 255, 255, 0.2)",
19 | "popoverBackground": "#fff",
20 | "popoverText": "#333"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/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/workbench/browser/src/app/modules/download-client/download-client.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 { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
6 |
7 | import { EoIconparkIconModule } from '../eo-ui/iconpark-icon/eo-iconpark-icon.module';
8 | import { DownloadClientComponent } from './download-client.component';
9 |
10 | @NgModule({
11 | imports: [CommonModule, EoIconparkIconModule, SharedModule, EoNgButtonModule, EoNgDropdownModule],
12 | declarations: [DownloadClientComponent],
13 | exports: [DownloadClientComponent]
14 | })
15 | export class DownloadClientModule {}
16 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/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/workbench/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss:
--------------------------------------------------------------------------------
1 | .env-select {
2 | --select-background-color: var(--background-color);
3 |
4 | border-bottom: 1px solid var(--border-color);
5 | }
6 |
7 | nz-divider {
8 | margin: 0.1em 0;
9 | }
10 |
11 | :host ::ng-deep {
12 | eo-ng-select eo-ng-select-top-control {
13 | border: none !important;
14 | }
15 |
16 | .ant-select {
17 | width: 140px;
18 | }
19 | }
20 |
21 | .content {
22 | color: var(--text-color);
23 | }
24 |
25 | .title {
26 | background-color: var(--item-active-background-color);
27 | color: var(--text-color);
28 | }
29 |
30 | ::ng-deep {
31 | .env-selector-dropdown {
32 | width: 224px;
33 | }
34 |
35 | .preview-env .ant-popover-inner-content {
36 | padding: 0;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/db/services/apiTestHistory.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'eo/workbench/browser/src/app/shared/services/storage/db/dataSource';
2 | import { ApiTestHistory } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
3 | import { BaseService } from 'eo/workbench/browser/src/app/shared/services/storage/db/services/base.service';
4 |
5 | export class ApiTestHistoryService extends BaseService {
6 | baseService = new BaseService(dataSource.apiTestHistory);
7 |
8 | constructor() {
9 | super(dataSource.apiTestHistory);
10 | }
11 |
12 | async bulkDelete(params) {
13 | const { ids, ...restParams } = params;
14 | return this.baseService.bulkDelete({
15 | id: ids,
16 | ...restParams
17 | });
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/components/group/edit/api-group-edit.component.html:
--------------------------------------------------------------------------------
1 |
9 |
10 | Data from
11 | {{ group?.name?.length > 50 ? group?.name.slice(0, 50) + '...' : group?.name }}
12 | will be deleted. This cannot be undone. Are you sure you want to delete?
13 |
14 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/test/api-test.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ApiTestHistory } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
3 |
4 | import { ApiEffectService } from '../../service/store/api-effect.service';
5 |
6 | @Injectable()
7 | export class ApiTestService {
8 | constructor(private effect: ApiEffectService) {}
9 | getHistory(id): Promise {
10 | return this.effect.getHistory(id);
11 | }
12 | addHistory(history: ApiTestHistory): Promise {
13 | return this.effect.createHistory({
14 | apiUuid: history.apiUuid || -1,
15 | general: '{}',
16 | request: JSON.stringify(history.request),
17 | response: JSON.stringify(history.response)
18 | });
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/share-project/share-project.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit } from '@angular/core';
2 | import { ActivatedRoute } from '@angular/router';
3 | import { StoreService } from 'eo/workbench/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 |
--------------------------------------------------------------------------------
/donkey.config.js:
--------------------------------------------------------------------------------
1 | const baseUrl = './src/workbench/browser/src/app/shared/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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/pages/workspace/project/api/http/mock/api-mock.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{
4 | btnTitle
5 | }}
6 |
7 |
8 |
9 |
10 |
11 | New Mock
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/shared/services/storage/db/services/workspace.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'eo/workbench/browser/src/app/shared/services/storage/db/dataSource';
2 | import { Workspace } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
3 | import { BaseService } from 'eo/workbench/browser/src/app/shared/services/storage/db/services/base.service';
4 |
5 | export class WorkspaceService extends BaseService {
6 | baseService = new BaseService(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/workbench/node/electron/main.ts:
--------------------------------------------------------------------------------
1 | import { ipcMain } from 'electron';
2 | import { UnitWorker } from 'eo/workbench/node/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/workbench/browser/src/app/shared/components/download-client.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { WebService } from 'eo/workbench/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/workbench/browser/src/app/modules/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 | min-width: fit-content;
9 | max-width: 400px;
10 | }
11 |
12 | .text {
13 | word-wrap: break-word;
14 | white-space: pre-wrap;
15 | }
16 |
17 | &.not-reply {
18 | flex-direction: row-reverse;
19 |
20 | .message-content {
21 | background: var(--item-hover-background-color);
22 | color: var(--text-color);
23 | }
24 |
25 | nz-avatar {
26 | margin-left: 5px;
27 | }
28 | }
29 |
30 | &.reply {
31 | .message-content {
32 | background: #36f;
33 | color: #fff;
34 | }
35 |
36 | nz-avatar {
37 | margin-right: 5px;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/shared/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/workbench/browser/src/app/pages/workspace/project/api/http/detail/body/api-detail-body.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
19 |
20 |
21 | {{ model[0].binaryRawData }}
22 |
--------------------------------------------------------------------------------
/.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 | }
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/shared/directives/trace.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, HostListener, Input } from '@angular/core';
2 | import { TraceService } from 'eo/workbench/browser/src/app/shared/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/workbench/browser/src/app/pages/workspace/project/api/components/group/tree/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/workbench/node/electron/unitWorker.ts:
--------------------------------------------------------------------------------
1 | import { BrowserView } from 'electron';
2 |
3 | import * as child_process from 'child_process';
4 | export class UnitWorker {
5 | instance: child_process.ChildProcess;
6 | view: BrowserView;
7 | constructor(view: BrowserView) {
8 | this.view = view;
9 | }
10 | start(message: any) {
11 | this.instance = child_process.fork(`${__dirname}/forkUnit.js`);
12 | this.watch();
13 | this.instance.send(message);
14 | }
15 | finish(message: any) {
16 | this.view.webContents.send('unitTest', message);
17 | this.kill();
18 | }
19 | kill() {
20 | this.instance.kill();
21 | }
22 | private watch() {
23 | this.instance.on('message', (message: any) => {
24 | switch (message.action) {
25 | case 'finish': {
26 | this.finish(message.data);
27 | break;
28 | }
29 | }
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/workbench/node/request/libs/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/node/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 'ajax': {
6 | message.data.env = _LibsCommon.parseEnv(message.data.env);
7 | await new _LibsFlowCommon.core().main(message.data).then(({ globals, report, history }) => {
8 | ['general', 'requestInfo', 'resultInfo'].forEach(keyName => {
9 | if (typeof history[keyName] === 'string') history[keyName] = JSON.parse(history[keyName]);
10 | });
11 | process.send({
12 | action: 'finish',
13 | data: {
14 | id: message.id,
15 | report: report,
16 | history: history,
17 | globals: globals
18 | }
19 | });
20 | });
21 | break;
22 | }
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/test/api-script/api-script.component.scss:
--------------------------------------------------------------------------------
1 | :host {
2 | display: flex;
3 | overflow: hidden;
4 | height: 100%;
5 | }
6 |
7 | :host ::ng-deep {
8 | nz-tree-node-title {
9 | height: 100%;
10 | }
11 |
12 | .ant-tree .ant-tree-node-content-wrapper {
13 | line-height: 28px;
14 | }
15 |
16 | .eo-api-script {
17 | .ant-tree-switcher {
18 | width: 0;
19 | }
20 |
21 | .ant-tree-treenode-switcher-open {
22 | .ant-tree-indent {
23 | width: 0;
24 | }
25 |
26 | .ant-tree-switcher {
27 | width: 20px;
28 | }
29 | }
30 |
31 | .ant-tree-treenode-switcher-close {
32 | .ant-tree-indent {
33 | width: 0;
34 | }
35 |
36 | .ant-tree-switcher {
37 | width: 20px;
38 | }
39 | }
40 | }
41 |
42 | .ant-tree-indent {
43 | width: 20px;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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/workbench/browser/src/app/shared/services/trace.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { ElectronService } from '../../core/services/electron/electron.service';
4 |
5 | declare const gio;
6 |
7 | @Injectable({
8 | providedIn: 'root'
9 | })
10 | export class TraceService {
11 | constructor(private electron: ElectronService) {
12 | this.setUser({ client_type: this.electron.isElectron ? 'client' : 'web' });
13 | }
14 |
15 | report(eventId, params = {}) {
16 | if (!eventId) {
17 | return;
18 | }
19 | // console.log('trace =>>', eventId, JSON.stringify(params, null, 2));
20 | gio('track', eventId, params);
21 | }
22 | setUserID(id) {
23 | gio('setUserId', id);
24 | }
25 | setUser(data = {}) {
26 | gio('people.set', data);
27 | }
28 | start() {
29 | gio('config', { dataCollect: false });
30 | }
31 | stop() {
32 | gio('config', { dataCollect: true });
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/app/electron-main/language.service.ts:
--------------------------------------------------------------------------------
1 | import { app } from 'electron';
2 | import Store from 'electron-store';
3 |
4 | import { LANGUAGES } from '../../workbench/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/workbench/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: `
8 |
9 |
10 |
11 |
15 | `,
16 | styles: []
17 | })
18 | export class HelpDropdownComponent {
19 | constructor(public web: WebService) {}
20 | }
21 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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 | !scripts/*.js
11 | !src/karma.conf.js
12 | !src/assets/font/*.js
13 | !src/assets/libs/*.js
14 | *.js.map
15 |
16 |
17 | # dependencies
18 | node_modules
19 |
20 | # IDEs and editors
21 | .idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | .vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
35 |
36 | # misc
37 | .angular/
38 | /.sass-cache
39 | /connect.lock
40 | /coverage
41 | /libpeerconnection.log
42 | npm-debug.log
43 | testem.log
44 | /typings
45 |
46 | # e2e
47 | /e2e/test
48 | !/e2e/protractor.conf.js
49 | /e2e/*.map
50 | /e2e/tracing
51 | /e2e/screenshots
52 |
53 | # System Files
54 | .DS_Store
55 | Thumbs.db
56 | # *-lock.json
57 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/db/index.ts:
--------------------------------------------------------------------------------
1 | import { ApiDataService } from './services/apiData.service';
2 | import { ApiTestHistoryService } from './services/apiTestHistory.service';
3 | import { EnvironmentService } from './services/environment.service';
4 | import { GroupService } from './services/group.service';
5 | import { MockService } from './services/mock.service';
6 | import { ProjectService } from './services/project.service';
7 | import { WorkspaceService } from './services/workspace.service';
8 |
9 | export const db = {
10 | apiData: new ApiDataService(),
11 | group: new GroupService(),
12 | environment: new EnvironmentService(),
13 | project: new ProjectService(),
14 | workspace: new WorkspaceService(),
15 | mock: new MockService(),
16 | apiTestHistory: new ApiTestHistoryService()
17 | } as const;
18 |
19 | if (process.env.NODE_ENV === 'development') {
20 | // const module = await import('./tests/index');
21 | // module.setupTests();
22 | }
23 |
--------------------------------------------------------------------------------
/api/unit.js:
--------------------------------------------------------------------------------
1 | let _LibsFlowCommon = require('../src/workbench/node/request/unit.js');
2 | let _LibsCommon = require('../src/workbench/node/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/sidebar/sidebar.component.html:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/grpc/grpc.component.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/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/workbench/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 { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
4 | import { NzCardModule } from 'ng-zorro-antd/card';
5 | import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
6 | import { NzUploadModule } from 'ng-zorro-antd/upload';
7 |
8 | import { ExtensionSelectModule } from '../../../../modules/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/workbench/browser/src/app/shared/services/storage/IndexedDB/schema/README.md:
--------------------------------------------------------------------------------
1 | # JSON schema
2 |
3 | Check data Valide
4 |
5 | # Genreate JSON schema from Typescript interface
6 |
7 | 1. install
8 |
9 | ```
10 | npm install typescript-json-schema -g
11 | ```
12 |
13 | 2. genreate
14 |
15 | ```
16 | cd src/workbench/browser/src/app/shared/services/storage/db/models
17 | //genrate apidata --ignoreErrors
18 | typescript-json-schema "apiData.ts" 'ApiData' -o "../schema/apiData.json"
19 | //genrate env
20 | typescript-json-schema "index.ts" 'Environment' -o "../schema/env.json"
21 | ```
22 |
23 | 3. compare and merge
24 | The generated rule verification is not strict enough and needs to be merged manually
25 |
26 | # Check
27 |
28 | ```javascript
29 | const ajv = new Ajv({
30 | useDefaults: true,
31 | });
32 | const validate = ajv.compile < ApiData > apiDataSchema;
33 | if (validate(apiData)) {
34 | return { validate: true, data: apiData };
35 | } else {
36 | return { validate: false, error: validate.errors };
37 | }
38 | ```
39 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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 '../../../utils/storage/storage.utils';
5 |
6 | @Component({
7 | selector: 'pc-debug-theme',
8 | template: `
Apply `,
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 |
--------------------------------------------------------------------------------
/test/e2e/src/commom.util.ts:
--------------------------------------------------------------------------------
1 | import { expect } from '@playwright/test';
2 | export const ECHO_API_URL = 'http://demo.gokuapi.com:8280/Web/Test/all/print';
3 | export const addTextToEditor = async (page, text, monacoEditor = page.locator('.monaco-editor').nth(0)) => {
4 | await monacoEditor.click();
5 | await page.keyboard.press('Meta+KeyA');
6 | await page.keyboard.type(text);
7 | };
8 |
9 | export const ifTipsExist = async (page, tips) => {
10 | await expect(page.locator(`text=${tips}`)).toBeVisible();
11 | };
12 |
13 | export const login = async page => {
14 | await page.getByRole('button', { name: 'Sign in/Up' }).click();
15 | await page.getByPlaceholder('Enter Email').click();
16 | await page.getByPlaceholder('Enter Email').fill('scar@qq.com');
17 | await page.getByPlaceholder('Enter Email').press('Tab');
18 | await page.getByPlaceholder('Enter password').fill('123456');
19 | await page.getByPlaceholder('Enter password').press('Enter');
20 | await page.getByRole('button', { name: 'switch to the cloud workspace' }).click();
21 | };
22 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.scss:
--------------------------------------------------------------------------------
1 | .basic_info_bar {
2 | @apply flex justify-between;
3 |
4 | color: white;
5 | padding: 5px 10px;
6 | border-radius: 3px;
7 | border-width: 1px;
8 | border-style: solid;
9 | }
10 |
11 | .test-success {
12 | background-color: #00785a;
13 | border: 1px solid #05654d;
14 | color: #fff;
15 | }
16 |
17 | .test-default {
18 | background-color: #a9a9a9;
19 | border: 1px solid #a9a9a9;
20 | color: #fff;
21 | }
22 |
23 | .test-error {
24 | background-color: #ff3c32;
25 | border: 1px solid #ff3c32;
26 | color: #fff;
27 | }
28 |
29 | .test-warning {
30 | background-color: #ff3c32;
31 | border: 1px solid #ff625a;
32 | color: #fff;
33 | }
34 |
35 | :host ::ng-deep {
36 | .eo_alert_bar {
37 | margin-bottom: 5px;
38 |
39 | .ant-alert-info {
40 | background-color: #fff;
41 | border: 1px solid #4c9e81;
42 | }
43 | }
44 |
45 | .ant-code-editor {
46 | height: calc(var(--bottom-height) - 156px);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/extension/extension.component.scss:
--------------------------------------------------------------------------------
1 | @import '../workspace/project/api/components/group/tree/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 |
14 | eo-ng-auto-complete input {
15 | border-radius: 999px;
16 | }
17 |
18 | .ant-btn-link {
19 | padding: 0;
20 | }
21 |
22 | .left {
23 | width: 250px;
24 | height: 70vh; // * necessary
25 | border-right: 1px solid var(--border-color);
26 |
27 | .plugin-link {
28 | &:hover,
29 | &.active {
30 | color: var(--primary-color);
31 | cursor: pointer;
32 | }
33 |
34 | &:hover {
35 | background-color: rgb(0 0 0 / 10%);
36 | }
37 | }
38 | }
39 |
40 | .right {
41 | height: 100%;
42 | overflow: hidden;
43 | }
44 | }
45 |
46 | ::ng-deep {
47 | .eo-extension-modal {
48 | .ant-modal-body {
49 | padding: 0;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/browser/src/app/modules/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/workbench/browser/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 | import microApp from '@micro-zoe/micro-app';
4 |
5 | import { AppModule } from './app/app.module';
6 | import { APP_CONFIG } from './environments/environment';
7 |
8 | if (APP_CONFIG.production) {
9 | enableProdMode();
10 | }
11 |
12 | microApp.start({
13 | fetch(url, options, appName) {
14 | if (url.startsWith('https://unpkg.com/') && !url.startsWith(`https://unpkg.com/${appName}`)) {
15 | // 删除 http://localhost:3001/error.js 的内容
16 | return window
17 | .fetch(new URL(url.replace('https://unpkg.com/', ''), `https://unpkg.com/${appName}/page/`).href, options)
18 | .then(res => res.text());
19 | }
20 |
21 | return window.fetch(url, options).then(res => {
22 | if (res.status > 400) {
23 | console.error(res);
24 | return '';
25 | }
26 | return res.text();
27 | });
28 | }
29 | });
30 |
31 | platformBrowserDynamic()
32 | .bootstrapModule(AppModule)
33 | .catch(err => console.error(err));
34 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/components/group/tree/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/workbench/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 |
3 | import { ElectronService } from '../../../../../../../core/services';
4 | import { ApiTestUtilService } from '../../api-test-util.service';
5 | import { TestServerService } from '../test-server.service';
6 | @Injectable()
7 | export class TestServerLocalNodeService extends TestServerService {
8 | constructor(private electron: ElectronService, @Inject(LOCALE_ID) public locale: string, public apiTestUtil: ApiTestUtilService) {
9 | super(locale, apiTestUtil);
10 | }
11 | init(receiveMessage: (message) => void) {
12 | this.electron.ipcRenderer.on('unitTest', (event, args) => {
13 | // console.log('[localNode]receiveMessage', args);
14 | receiveMessage(this.formatResponseData(args));
15 | });
16 | }
17 | send(module, message) {
18 | console.log('[localNode]send message', message);
19 | this.electron.ipcRenderer.send(module, message);
20 | }
21 | close() {
22 | this.electron.ipcRenderer.removeAllListeners('unitTest');
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/workbench/node/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 | "scripts": {
7 | "dev": "node ./server/main.js",
8 | "start": "pm2-runtime start ./server/main.js",
9 | "start:io": "pm2-runtime start ./server/socketio.js",
10 | "start:all": "yarn start && yarn start:io",
11 | "start:all:pm2": "pm2 start ./server/main.js",
12 | "watch": "pm2 list",
13 | "stop": "pm2 stop all",
14 | "test": "echo \"Error: no test specified\" && exit 1"
15 | },
16 | "author": "Postcat",
17 | "license": "ISC",
18 | "dependencies": {
19 | "@koa/cors": "3.3.0",
20 | "@grpc/grpc-js": "1.7.3",
21 | "@grpc/proto-loader": "0.7.3",
22 | "crypto-js": "4.1.1",
23 | "jquery": "3.6.1",
24 | "jsdom": "20.0.1",
25 | "koa": "2.13.4",
26 | "koa-body": "5.0.0",
27 | "ws": "8.8.1",
28 | "pm2": "5.2.0",
29 | "socket.io": "4.5.3",
30 | "xml2js": "0.4.23",
31 | "iconv-lite": "^0.6.3",
32 | "form-data": "^4.0.0",
33 | "content-disposition": "^0.5.4"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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/workbench/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 { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
5 | import { NzEmptyModule } from 'ng-zorro-antd/empty';
6 |
7 | import { EoTableProModule } from '../../../../../modules/eo-ui/table-pro/table-pro.module';
8 | import { EnvEditComponent } from './env-edit/env-edit.component';
9 | import { EnvListComponent } from './env-list/env-list.component';
10 | import { EnvSelectComponent } from './env-select/env-select.component';
11 |
12 | const ANTDMODULES = [EoTableProModule];
13 | const COMPONENTA = [EnvListComponent, EnvSelectComponent];
14 | @NgModule({
15 | declarations: [...COMPONENTA, EnvEditComponent],
16 | imports: [
17 | RouterModule.forChild([
18 | {
19 | path: 'edit',
20 | component: EnvEditComponent
21 | }
22 | ]),
23 | EoNgTreeModule,
24 | NzEmptyModule,
25 | SharedModule,
26 | ...ANTDMODULES
27 | ],
28 | exports: [...COMPONENTA]
29 | })
30 | export class EnvModule {}
31 |
--------------------------------------------------------------------------------
/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 | "eo/*": ["./src/*"]
26 | }
27 | },
28 | "include": ["**/*.d.ts", "./src/**/**.ts", "./src/**/**.js", "scripts/build.js", "test/e2e/test.ts", "test/e2e/test.ts"],
29 | "exclude": ["node_modules", "**/*.spec.ts", "**/browser/**/*.js", "**/browser/**/*.ts", "out"],
30 | "angularCompilerOptions": {
31 | "enableIvy": true
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/core/services/theme/theme-extension.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | import { ExtensionService } from '../../../shared/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/workbench/browser/src/app/layouts/sidebar/sidebar.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { Subject } from 'rxjs';
3 |
4 | import { SettingService } from '../../modules/system-setting/settings.service';
5 | import StorageUtil from '../../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/workbench/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/.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 | *.webpack.js
18 | !patches/*
19 | !src/workbench/node/electron/**/*.js
20 | !src/workbench/node/request/**/*.js
21 | !src/workbench/node/server/**/*.js
22 | !/api/*.js
23 | !/scripts/**/*.js
24 | !*.config.js
25 | !.*.js
26 |
27 | # dependencies
28 | node_modules
29 | # package-lock.json
30 |
31 | # IDEs and editors
32 | .idea
33 | .project
34 | .classpath
35 | .c9/
36 | *.launch
37 | .settings/
38 | *.sublime-workspace
39 |
40 | # misc
41 | /connect.lock
42 | /coverage
43 | /libpeerconnection.log
44 | npm-debug.log
45 | *-error.log
46 | testem.log
47 | /typings
48 |
49 | # e2e
50 | /e2e/test
51 | /e2e/imgs
52 | !/e2e/protractor.conf.js
53 | /e2e/*.map
54 | /e2e/tracing
55 | /e2e/screenshots
56 |
57 | # System Files
58 | .DS_Store
59 | Thumbs.db
60 | # *-lock.json
61 | -error.log
62 | .vercel
63 | nginx-test.conf
64 | docker-compose.dev.yml
65 | Dockerfile.dev
66 |
67 | #cache
68 | buildFile
69 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/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 | GOT IT
29 |
30 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/core/services/notification.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { ModalService } from 'eo/workbench/browser/src/app/shared/services/modal.service';
3 | import StorageUtil from 'eo/workbench/browser/src/app/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 | this.modal.create({
26 | stayWhenRouterChange: true,
27 | nzTitle: $localize`Release Notes`,
28 | nzContent: $localize`There will be downtime updates from ${logInfo.startTime.getHours()}\:00 to ${logInfo.endTime.getHours()}\:00 today, and may be temporarily inaccessible.`
29 | });
30 | StorageUtil.set('notification_has_show', true, 60 * 60 * 24);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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:widget': 'radio',
9 | default: 4,
10 | required: true,
11 | oneOf: [
12 | {
13 | type: 'number',
14 | title: $localize`Off`,
15 | default: 0,
16 | const: 0
17 | },
18 | {
19 | type: 'number',
20 | title: $localize`Every four hours`,
21 | default: 4,
22 | const: 4
23 | },
24 | {
25 | type: 'number',
26 | title: $localize`Every twelve hours`,
27 | default: 12,
28 | const: 12
29 | },
30 | {
31 | type: 'number',
32 | title: $localize`Every day`,
33 | default: 24,
34 | const: 24
35 | }
36 | ]
37 | },
38 | __formater: {
39 | type: 'string',
40 | label: $localize`:@@SyncFormat:Format`,
41 | 'ui:widget': 'radio',
42 | default: '',
43 | required: true,
44 | oneOf: []
45 | }
46 | },
47 | allOf: []
48 | };
49 |
--------------------------------------------------------------------------------
/src/workbench/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 | "eo/*": ["./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/workbench/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 { OperateProjectFormComponent } from 'eo/workbench/browser/src/app/pages/workspace/project/components/operate-project-form.compoent';
5 | import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
6 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
7 | import { NzCardModule } from 'ng-zorro-antd/card';
8 | import { NzEmptyModule } from 'ng-zorro-antd/empty';
9 | import { NzFormModule } from 'ng-zorro-antd/form';
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/workbench/browser/src/app/modules/extension-select/import-api/old2new.ts:
--------------------------------------------------------------------------------
1 | import { CollectionTypeEnum, ImportProjectDto } from 'eo/workbench/browser/src/app/shared/services/storage/db/dto/project.dto';
2 |
3 | import { convertApiData } from './../../../shared/services/storage/db/dataSource/convert';
4 |
5 | export const old2new = (params, projectUuid, workSpaceUuid): ImportProjectDto => {
6 | const { collections = [], environments } = params;
7 |
8 | const environmentList = environments.map(n => ({
9 | name: n.name,
10 | hostUri: n.hostUri,
11 | parameters: n.parameters,
12 | projectUuid,
13 | workSpaceUuid
14 | }));
15 | const formatData = (collections = []) => {
16 | collections.forEach(item => {
17 | // API
18 | if (item.uri) {
19 | const newApiData = convertApiData(item);
20 | Object.assign(item, newApiData);
21 | item.collectionType = CollectionTypeEnum.API_DATA;
22 | }
23 | // 分组
24 | else {
25 | item.collectionType = CollectionTypeEnum.GROUP;
26 |
27 | if (item.children?.length) {
28 | formatData(item.children);
29 | }
30 | }
31 | });
32 | };
33 |
34 | formatData(collections);
35 |
36 | return {
37 | collections,
38 | environmentList
39 | };
40 | };
41 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/db/services/environment.service.ts:
--------------------------------------------------------------------------------
1 | import { dataSource } from 'eo/workbench/browser/src/app/shared/services/storage/db/dataSource';
2 | import { ApiResponse, ResObj } from 'eo/workbench/browser/src/app/shared/services/storage/db/decorators/api-response.decorator';
3 | import { Environment } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
4 | import { BaseService } from 'eo/workbench/browser/src/app/shared/services/storage/db/services/base.service';
5 |
6 | export class EnvironmentService extends BaseService {
7 | baseService = new BaseService(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/workbench/browser/src/app/modules/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 { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
7 | import { NzUploadModule } from 'ng-zorro-antd/upload';
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/workbench/browser/src/app/pages/pages-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule, Routes } from '@angular/router';
3 |
4 | import { PageBlankComponent } from '../layouts/page-blank/page-blank.component';
5 | import { PagesComponent } from './pages.component';
6 | import { RedirectWorkspace } from './services/redirect.services';
7 |
8 | const routes: Routes = [
9 | {
10 | path: '',
11 | component: PagesComponent,
12 | children: [
13 | {
14 | path: '',
15 | redirectTo: 'workspace',
16 | pathMatch: 'full'
17 | },
18 | {
19 | path: 'blank',
20 | component: PageBlankComponent
21 | },
22 | {
23 | path: 'workspace',
24 | canActivate: [RedirectWorkspace],
25 | runGuardsAndResolvers: 'always',
26 | loadChildren: () => import('./workspace/workspace.module').then(m => m.WorkspaceModule)
27 | },
28 | {
29 | path: 'extension',
30 | loadChildren: () => import('./extension/extension.module').then(m => m.ExtensionModule)
31 | }
32 | ]
33 | }
34 | ];
35 |
36 | @NgModule({
37 | imports: [RouterModule.forChild(routes)],
38 | providers: [RedirectWorkspace],
39 | exports: [RouterModule]
40 | })
41 | export class PagesRoutingModule {}
42 |
--------------------------------------------------------------------------------
/src/workbench/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/workbench/browser/src/app/pages/workspace/project/api/http/mock/edit/api-mock-edit.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, Output } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'eo-api-mock-edit',
5 | template: `
6 |
28 |
`
29 | })
30 | export class ApiMockEditComponent {
31 | @Input() model;
32 | @Input() isEdit = true;
33 | }
34 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/db/dto/project.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiList } from 'eo/workbench/browser/src/app/shared/services/storage/db/dto/apiData.dto';
2 | import { PageDto } from 'eo/workbench/browser/src/app/shared/services/storage/db/dto/common.dto';
3 | import { Environment, Group } from 'eo/workbench/browser/src/app/shared/services/storage/db/models';
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 |
36 | export type Collection = (ApiList | Group) & {
37 | /**
38 | * 0:group
39 | * 1: apiData
40 | */
41 | collectionType: CollectionTypeEnum;
42 | };
43 |
44 | export enum CollectionTypeEnum {
45 | GROUP = 0,
46 | API_DATA = 1
47 | }
48 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/detail/form/api-detail-form.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, OnInit, Input } from '@angular/core';
2 | import { ApiTableService } from 'eo/workbench/browser/src/app/modules/api-shared/api-table.service';
3 | import { ApiTableConf } from 'eo/workbench/browser/src/app/modules/api-shared/api.model';
4 | import { HeaderParam } from 'eo/workbench/browser/src/app/shared/services/storage/db/models/apiData';
5 |
6 | @Component({
7 | selector: 'eo-api-detail-form',
8 | template: ` `
9 | })
10 | export class ApiDetailFormComponent implements OnInit {
11 | /**
12 | * Table ID
13 | */
14 | @Input() tid: string;
15 | @Input() model: HeaderParam[];
16 | @Input() module: 'rest' | 'header' | 'query';
17 | listConf: ApiTableConf = {};
18 | constructor(private apiTable: ApiTableService) {}
19 |
20 | ngOnInit(): void {
21 | this.initListConf();
22 | }
23 | private initListConf() {
24 | const config = this.apiTable.initTable({
25 | in: this.module,
26 | module: 'preview',
27 | isEdit: false,
28 | id: this.tid
29 | });
30 | this.listConf.columns = config.columns;
31 | this.listConf.setting = config.setting;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/platform/electron-browser/i18n.ts:
--------------------------------------------------------------------------------
1 | import { LANGUAGES } from '../../workbench/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/workbench/browser/src/app/layouts/sidebar/sidebar.component.scss:
--------------------------------------------------------------------------------
1 | .eo-sidebar {
2 | background-color: var(--layout-sidebar-background-color);
3 | border-right: 1px solid var(--border-color);
4 | overflow-y: auto;
5 | overflow-x: hidden;
6 | width: var(--layout-sidebar-width);
7 | height: 100%;
8 | transition: width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
9 | }
10 |
11 | .eo-sidebar-shrink {
12 | width: 50px;
13 |
14 | .sidebar-item {
15 | height: 50px;
16 | }
17 | }
18 |
19 | .app-logo {
20 | height: var(--icon-size);
21 | width: var(--icon-size);
22 | }
23 |
24 | .icon {
25 | ::ng-deep .iconpark-icon {
26 | width: 20px;
27 | height: 20px;
28 | }
29 | }
30 |
31 | .sidebar-item {
32 | padding: 10px 0;
33 | color: var(--menu-item-text-color);
34 | height: var(--layout-sidebar-item-height);
35 |
36 | &.tiny {
37 | padding: 4px 0 5px;
38 | }
39 |
40 | &:hover,
41 | &.sidebar-item-active {
42 | font-weight: bold;
43 | background-color: var(--menu-item-active-background-color);
44 | color: var(--menu-item-active-text-color);
45 |
46 | i.icon {
47 | color: inherit;
48 | }
49 | }
50 | }
51 |
52 | ::ng-deep {
53 | .theme-pc-dark .eo-sidebar .app-logo {
54 | filter: invert(100%) sepia(1%) saturate(89%) hue-rotate(300deg) brightness(113%) contrast(100%);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/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 | Send
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 triggle 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/workbench/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/platform/node/i18n.ts:
--------------------------------------------------------------------------------
1 | import { I18nLocale, ExtensionInfo } from 'eo/workbench/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/workbench/browser/src/app/shared/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/share-project/share-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 |
4 | import { ShareComponent } from './share-project.component';
5 |
6 | const routes: Routes = [
7 | {
8 | path: '',
9 | component: ShareComponent,
10 | children: [
11 | {
12 | path: '',
13 | redirectTo: 'http',
14 | pathMatch: 'full'
15 | },
16 | {
17 | path: 'http',
18 | children: [
19 | {
20 | path: 'detail',
21 | loadChildren: () => import('../workspace/project/api/http/detail/api-detail.module').then(m => m.ApiDetailModule)
22 | },
23 | {
24 | path: 'test',
25 | loadChildren: () => import('../workspace/project/api/http/test/api-test.module').then(m => m.ApiTestModule)
26 | }
27 | ]
28 | },
29 | {
30 | path: 'ws',
31 | children: [
32 | {
33 | path: 'test',
34 | loadChildren: () => import('../workspace/project/api/websocket/websocket.module').then(m => m.WebsocketModule)
35 | }
36 | ]
37 | }
38 | ]
39 | }
40 | ];
41 |
42 | @NgModule({
43 | imports: [RouterModule.forChild(routes)],
44 | exports: [RouterModule]
45 | })
46 | export class ShareRoutingModule {}
47 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/member/project-member.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 { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
6 | import { NzAvatarModule } from 'ng-zorro-antd/avatar';
7 | import { NzModalService, NzModalModule } from 'ng-zorro-antd/modal';
8 |
9 | import { MemberListModule } from '../../../../modules/member-list/member-list.module';
10 | import { MemberService } from '../../../../modules/member-list/member.service';
11 | import { ProjectMemberRoutingModule } from './project-member-routing.module';
12 | import { ProjectMemberComponent } from './project-member.component';
13 | import { ProjectMemberService } from './project-member.service';
14 |
15 | @NgModule({
16 | imports: [
17 | ProjectMemberRoutingModule,
18 | MemberListModule,
19 | CommonModule,
20 | NzAvatarModule,
21 | NzModalModule,
22 | FormsModule,
23 | EoNgButtonModule,
24 | SharedModule
25 | ],
26 | declarations: [ProjectMemberComponent],
27 | exports: [],
28 | providers: [
29 | NzModalService,
30 | {
31 | provide: MemberService,
32 | useClass: ProjectMemberService
33 | }
34 | ]
35 | })
36 | export class ProjectMemberModule {}
37 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/shared/services/storage/IndexedDB/schema/env.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-07/schema#",
3 | "description": "环境对象接口",
4 | "definitions": {
5 | "BasicParams": {
6 | "properties": {
7 | "name": {
8 | "description": "参数名",
9 | "type": "string",
10 | "default": ""
11 | },
12 | "value": {
13 | "description": "param value",
14 | "type": "string",
15 | "default": ""
16 | }
17 | },
18 | "type": "object"
19 | }
20 | },
21 | "properties": {
22 | "createdAt": {
23 | "description": "创建时间,可为空",
24 | "type": "string"
25 | },
26 | "hostUri": {
27 | "description": "前置url",
28 | "type": "string",
29 | "default": ""
30 | },
31 | "name": {
32 | "description": "名称",
33 | "type": "string"
34 | },
35 | "parameters": {
36 | "description": "环境变量(可选)",
37 | "type": "string",
38 | "items": {
39 | "$ref": "#/definitions/BasicParams"
40 | },
41 | "default": []
42 | },
43 | "projectID": {
44 | "description": "项目主键ID",
45 | "type": "number"
46 | },
47 | "updatedAt": {
48 | "description": "更新时间,可为空",
49 | "type": "string"
50 | }
51 | },
52 | "required": ["name"],
53 | "additionalProperties": false,
54 | "type": "object"
55 | }
56 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/http/mock/api-mock.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule } from '@angular/router';
3 | import { ApiSharedModule } from 'eo/workbench/browser/src/app/modules/api-shared/api-shared.module';
4 | import { DownloadClientModule } from 'eo/workbench/browser/src/app/modules/download-client/download-client.module';
5 | import { EoMonacoEditorModule } from 'eo/workbench/browser/src/app/modules/eo-ui/monaco-editor/monaco.module';
6 | import { EoTableProModule } from 'eo/workbench/browser/src/app/modules/eo-ui/table-pro/table-pro.module';
7 | import { ApiMockEditComponent } from 'eo/workbench/browser/src/app/pages/workspace/project/api/http/mock/edit/api-mock-edit.component';
8 | import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
9 | import { NzResultModule } from 'ng-zorro-antd/result';
10 |
11 | import { ApiMockComponent } from './api-mock.component';
12 | @NgModule({
13 | declarations: [ApiMockComponent, ApiMockEditComponent],
14 | imports: [
15 | RouterModule.forChild([
16 | {
17 | path: '',
18 | component: ApiMockComponent
19 | }
20 | ]),
21 | NzResultModule,
22 | ApiSharedModule,
23 | EoMonacoEditorModule,
24 | EoTableProModule,
25 | SharedModule,
26 | DownloadClientModule
27 | ],
28 | providers: [],
29 | exports: []
30 | })
31 | export class ApiMockModule {}
32 |
--------------------------------------------------------------------------------
/.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 | "name": "Main",
23 | "type": "node",
24 | "request": "launch",
25 | "protocol": "inspector",
26 | "cwd": "${workspaceFolder}",
27 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
28 | "trace": "verbose",
29 | "runtimeArgs": [
30 | "--serve",
31 | ".",
32 | "--remote-debugging-port=9876"
33 | ],
34 | "windows": {
35 | "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
36 | },
37 | "preLaunchTask": "Build.Main"
38 | }
39 | ],
40 | "compounds": [
41 | {
42 | "name": "Application Debug",
43 | "configurations": [
44 | "Renderer",
45 | "Main"
46 | ]
47 | }
48 | ]
49 | }
50 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/project-routing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { Routes, RouterModule } from '@angular/router';
3 | import { ExtensionAppComponent } from 'eo/workbench/browser/src/app/shared/components/extension-app/extension-app.component';
4 |
5 | import { RedirectProjectID } from '../../services/redirect.services';
6 |
7 | const routes: Routes = [
8 | {
9 | path: '',
10 | redirectTo: 'api',
11 | pathMatch: 'full'
12 | },
13 | {
14 | path: 'api',
15 | canActivate: [RedirectProjectID],
16 | runGuardsAndResolvers: 'always',
17 | loadChildren: () => import('./api/api.module').then(m => m.ApiModule)
18 | },
19 | {
20 | path: 'member',
21 | canActivate: [RedirectProjectID],
22 | runGuardsAndResolvers: 'always',
23 | loadChildren: () => import('./member/project-member.module').then(m => m.ProjectMemberModule)
24 | },
25 |
26 | {
27 | path: 'setting',
28 | canActivate: [RedirectProjectID],
29 | runGuardsAndResolvers: 'always',
30 | loadChildren: () => import('./setting/project-setting.module').then(m => m.ProjectSettingModule)
31 | },
32 | {
33 | path: 'extensionSidebarView/:extName',
34 | component: ExtensionAppComponent
35 | }
36 | ];
37 |
38 | @NgModule({
39 | imports: [RouterModule.forChild(routes)],
40 | providers: [RedirectProjectID],
41 | exports: [RouterModule]
42 | })
43 | export class ProjectRoutingModule {}
44 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/extension/detail/components/extensions-settings.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input, OnInit } from '@angular/core';
2 | import { EoNgFeedbackMessageService } from 'eo-ng-feedback';
3 | import { SettingService } from 'eo/workbench/browser/src/app/modules/system-setting/settings.service';
4 |
5 | @Component({
6 | selector: 'eo-extension-setting',
7 | template: `
8 |
12 | Save
13 |
14 |
15 |
16 | `
17 | })
18 | export class ExtensionSettingComponent implements OnInit {
19 | @Input() configuration = {} as any;
20 | @Input() extName: string;
21 | localSettings = {} as Record;
22 |
23 | constructor(private settingService: SettingService, private message: EoNgFeedbackMessageService) {}
24 |
25 | ngOnInit(): void {
26 | this.init();
27 | }
28 |
29 | private init() {
30 | this.localSettings = this.settingService.settings;
31 | }
32 | handleSave = () => {
33 | this.settingService.saveSetting(this.localSettings);
34 | this.message.success($localize`Save Success`);
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/navbar/navbar.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { NzBreadCrumbModule } from 'ng-zorro-antd/breadcrumb';
3 |
4 | import { DownloadClientModule } from '../../modules/download-client/download-client.module';
5 | import { LogoModule } from '../../modules/logo/logo.module';
6 | import { SharedModule } from '../../shared/shared.module';
7 | import { NavBreadcrumbComponent } from './breadcrumb/nav-breadcrumb.component';
8 | import { SelectWorkspaceComponent } from './breadcrumb/select-workspace/select-workspace.component';
9 | import { BtnUserComponent } from './btn-user/btn-user.component';
10 | import { GetShareLinkComponent } from './get-share-link.component';
11 | import { HelpDropdownComponent } from './help-dropdown/help-dropdown.component';
12 | import { NavOperateComponent } from './nav-operate.component';
13 | import { NavbarComponent } from './navbar.component';
14 | import { ShareNavbarComponent } from './share-navbar/share-navbar.component';
15 |
16 | @NgModule({
17 | imports: [SharedModule, DownloadClientModule, LogoModule, NzBreadCrumbModule],
18 | declarations: [
19 | NavbarComponent,
20 | GetShareLinkComponent,
21 | NavOperateComponent,
22 | SelectWorkspaceComponent,
23 | NavBreadcrumbComponent,
24 | ShareNavbarComponent,
25 | HelpDropdownComponent,
26 | BtnUserComponent
27 | ],
28 | exports: [NavbarComponent, ShareNavbarComponent]
29 | })
30 | export class NavbarModule {}
31 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/workspace.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { RouterModule } from '@angular/router';
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 { NzUploadModule } from 'ng-zorro-antd/upload';
7 |
8 | import { EoSettingModule } from '../../modules/eo-ui/setting/setting.module';
9 | import { SharedModule } from '../../shared/shared.module';
10 | import { WorkspaceComponent } from './workspace.component';
11 |
12 | @NgModule({
13 | declarations: [WorkspaceComponent],
14 | imports: [
15 | RouterModule.forChild([
16 | {
17 | path: '',
18 | redirectTo: 'project',
19 | pathMatch: 'full'
20 | },
21 | {
22 | path: 'overview',
23 | loadChildren: () => import('./overview/workspace-overview.module').then(m => m.WorkspaceOverviewModule)
24 | },
25 | {
26 | path: '',
27 | component: WorkspaceComponent,
28 | children: [
29 | {
30 | path: 'project',
31 | loadChildren: () => import('./project/project.module').then(m => m.ProjectModule)
32 | }
33 | ]
34 | }
35 | ]),
36 | NzCardModule,
37 | NzFormModule,
38 | EoSettingModule,
39 | SharedModule,
40 | NzUploadModule,
41 | NzAvatarModule
42 | ]
43 | })
44 | export class WorkspaceModule {
45 | constructor() {}
46 | }
47 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/api-shared/params-import/params-import.component.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 | Import like this:
10 | {{ eg }}
11 |
12 |
20 |
21 |
22 | Cancel
23 | Replace All
24 | Insert at the end
25 | Replace Changed
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/star-motivation/star-motivation.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 | import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
3 |
4 | import { SharedModule } from '../../shared/shared.module';
5 |
6 | @Component({
7 | selector: 'pc-star-motivation',
8 | template: `
10 | Hi!~ If you like {{ subject }} , please give the Postcat a Star! Your support is our greatest motivation~
12 |
13 |
21 |
22 |
23 |
`,
24 | standalone: true,
25 | imports: [SharedModule],
26 | styles: [
27 | `
28 | .favor-image-link {
29 | background-color: #eee;
30 | border-radius: 50%;
31 | padding: 13px;
32 | }
33 |
34 | .favor-image {
35 | transition: all 0.3s;
36 | }
37 | .favor-image:hover {
38 | transform: scale(1.2);
39 | }
40 | `
41 | ]
42 | })
43 | export class StarMotivationComponent {
44 | @Input() subject: string = 'Postcat';
45 |
46 | readonly APP_CONFIG = APP_CONFIG;
47 | }
48 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Build.Main",
6 | "type": "shell",
7 | "command": "npm run electron:serve-tsc",
8 | "isBackground": false,
9 | "group": {
10 | "kind": "build",
11 | "isDefault": true
12 | },
13 | "problemMatcher": {
14 | "owner": "typescript",
15 | "source": "ts",
16 | "applyTo": "closedDocuments",
17 | "fileLocation": ["relative", "${cwd}"],
18 | "pattern": "$tsc",
19 | "background": {
20 | "activeOnStart": true,
21 | "beginsPattern": "^.*",
22 | "endsPattern": "^.*Terminal will be reused by tasks, press any key to close it.*"
23 | }
24 | }
25 | },
26 | {
27 | "label": "Build.Renderer",
28 | "type": "shell",
29 | "command": "npm run serve",
30 | "isBackground": true,
31 | "group": {
32 | "kind": "build",
33 | "isDefault": true
34 | },
35 | "problemMatcher": {
36 | "owner": "typescript",
37 | "source": "ts",
38 | "applyTo": "closedDocuments",
39 | "fileLocation": ["relative", "${cwd}"],
40 | "pattern": "$tsc",
41 | "background": {
42 | "activeOnStart": true,
43 | "beginsPattern": "^.*",
44 | "endsPattern": "^.*Compiled successfully.*"
45 | }
46 | }
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/layouts/toolbar/toolbar.component.html:
--------------------------------------------------------------------------------
1 |
44 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/pages/workspace/project/api/websocket/websocket.module.ts:
--------------------------------------------------------------------------------
1 | import { CommonModule } from '@angular/common';
2 | import { NgModule } from '@angular/core';
3 | import { EoNgButtonModule } from 'eo-ng-button';
4 | import { ApiSharedModule } from 'eo/workbench/browser/src/app/modules/api-shared/api-shared.module';
5 | import { EoMonacoEditorModule } from 'eo/workbench/browser/src/app/modules/eo-ui/monaco-editor/monaco.module';
6 | import { ApiTestModule } from 'eo/workbench/browser/src/app/pages/workspace/project/api/http/test/api-test.module';
7 | import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
8 | import { NzBadgeModule } from 'ng-zorro-antd/badge';
9 | import { NzResizableModule } from 'ng-zorro-antd/resizable';
10 | import { NzTabsModule } from 'ng-zorro-antd/tabs';
11 |
12 | import { ApiEditUtilService } from '../http/edit/api-edit-util.service';
13 | import { WebsocketComponent } from './websocket.component';
14 | import { WebsocketRoutingModule } from './websocket.routing.module';
15 |
16 | const ANTDS = [EoNgButtonModule, NzTabsModule];
17 |
18 | @NgModule({
19 | imports: [
20 | EoMonacoEditorModule,
21 | WebsocketRoutingModule,
22 | ApiSharedModule,
23 | CommonModule,
24 | SharedModule,
25 | ApiTestModule,
26 | NzResizableModule,
27 | NzBadgeModule,
28 | ...ANTDS
29 | ],
30 | declarations: [WebsocketComponent],
31 | exports: [WebsocketComponent],
32 | providers: [ApiEditUtilService]
33 | })
34 | export class WebsocketModule {}
35 |
--------------------------------------------------------------------------------
/src/workbench/browser/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Build.Main",
6 | "type": "shell",
7 | "command": "npm run electron:serve-tsc",
8 | "isBackground": false,
9 | "group": {
10 | "kind": "build",
11 | "isDefault": true
12 | },
13 | "problemMatcher": {
14 | "owner": "typescript",
15 | "source": "ts",
16 | "applyTo": "closedDocuments",
17 | "fileLocation": ["relative", "${cwd}"],
18 | "pattern": "$tsc",
19 | "background": {
20 | "activeOnStart": true,
21 | "beginsPattern": "^.*",
22 | "endsPattern": "^.*Terminal will be reused by tasks, press any key to close it.*"
23 | }
24 | }
25 | },
26 | {
27 | "label": "Build.Renderer",
28 | "type": "shell",
29 | "command": "npm run serve",
30 | "isBackground": true,
31 | "group": {
32 | "kind": "build",
33 | "isDefault": true
34 | },
35 | "problemMatcher": {
36 | "owner": "typescript",
37 | "source": "ts",
38 | "applyTo": "closedDocuments",
39 | "fileLocation": ["relative", "${cwd}"],
40 | "pattern": "$tsc",
41 | "background": {
42 | "activeOnStart": true,
43 | "beginsPattern": "^.*",
44 | "endsPattern": "^.*Compiled successfully.*"
45 | }
46 | }
47 | }
48 | ]
49 | }
--------------------------------------------------------------------------------
/src/workbench/browser/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/0.13/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-electron'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma')
14 | ],
15 | client:{
16 | clearContext: false // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: [ 'html', 'lcovonly' ],
21 | fixWebpackSourcePaths: true
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | browsers: ['AngularElectron'],
28 | customLaunchers: {
29 | AngularElectron: {
30 | base: 'Electron',
31 | flags: [
32 | '--remote-debugging-port=9222'
33 | ],
34 | browserWindowOptions: {
35 | webPreferences: {
36 | nodeIntegration: true,
37 | nodeIntegrationInSubFrames: true,
38 | allowRunningInsecureContent: true,
39 | contextIsolation: false
40 | }
41 | }
42 | }
43 | }
44 | });
45 | };
46 |
--------------------------------------------------------------------------------
/src/workbench/browser/src/app/modules/nps-mask/component/nps-mask.component.ts:
--------------------------------------------------------------------------------
1 | import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
2 | import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
3 |
4 | import { StoreService } from '../../../shared/store/state.service';
5 | import { NpsPositionDirective } from '../nps-mask-postion.directive';
6 |
7 | @Component({
8 | selector: 'pc-nps-mask',
9 | template: `
10 |
11 |
12 | `,
13 | styleUrls: ['./nps-mask.component.scss'],
14 | hostDirectives: [NpsPositionDirective]
15 | })
16 | export class NpsMaskComponent implements OnInit {
17 | /**
18 | * After durantion,the NPS will show
19 | *
20 | * In development mode,it will show immediately
21 | */
22 | duration = APP_CONFIG.production ? 60 : 0;
23 | constructor(private store: StoreService) {}
24 | ngOnInit(): void {
25 | this.bindUserID();
26 |
27 | //* Trigger NPS after wondering 30 seconds
28 | setTimeout(() => {
29 | this.showNps();
30 | }, this.duration * 1000);
31 | }
32 | bindUserID() {
33 | const userProfile = this.store.getUserProfile;
34 | //@ts-ignore
35 | _howxm('identify', {
36 | uid: userProfile?.id,
37 | email: userProfile?.email
38 | });
39 | }
40 | private showNps() {
41 | //@ts-ignore
42 | _howxm('event', 'show_nps', {});
43 | }
44 | }
45 |
--------------------------------------------------------------------------------