├── 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 | 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 | 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 |
2 |
3 | 4 | {{ item.label }} 5 |
6 |
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 |
7 | 8 | Project Name 9 | 10 | 11 | 12 | 13 |
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 | 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 |
2 | 3 | Group Name 4 | 5 | 6 | 7 | 8 |
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: `
8 | 9 | 10 |
`, 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 | 6 | 7 |
8 |
9 |
10 | 11 | 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: ` 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 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
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: `
`, 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 | 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 |
7 | 8 | Mock Name 9 | 10 | 11 | 12 | 13 | 14 | Response 15 | 16 | 24 | 25 | 26 | 27 |
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 | 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 | 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 | 23 | 24 | 25 | 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 |
2 |
3 | 16 | 28 |
29 |
30 | 42 |
43 |
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 | --------------------------------------------------------------------------------