├── .editorconfig ├── .env ├── .env.development ├── .env.example ├── .env.production ├── .env.staging ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── app ├── console │ ├── command │ │ └── demo.go │ └── kernel.go ├── http │ ├── kernel.go │ ├── middleware │ │ └── .gitkeeper │ ├── module │ │ └── demo │ │ │ ├── api.go │ │ │ ├── dto.go │ │ │ ├── mapper.go │ │ │ ├── model.go │ │ │ ├── repository.go │ │ │ └── service.go │ ├── route.go │ ├── swagger.go │ └── swagger │ │ ├── docs.go │ │ ├── swagger.json │ │ └── swagger.yaml └── provider │ ├── .gitkeeper │ ├── demo │ ├── contract.go │ ├── provider.go │ └── service.go │ └── kernel.go ├── babel.config.js ├── config ├── development │ ├── app.yaml │ ├── database.yaml │ ├── deploy.yaml │ ├── gift.yaml │ ├── log.yaml │ └── swagger.yaml ├── production │ └── .gitkeeper └── testing │ └── .gitkeeper ├── coverage.sh ├── docs ├── .vuepress │ ├── config.js │ └── enhanceApp.js ├── README.md ├── guide │ ├── README.md │ ├── app.md │ ├── build.md │ ├── command.md │ ├── cron.md │ ├── dev.md │ ├── env.md │ ├── install.md │ ├── introduce.md │ ├── middleware.md │ ├── provider.md │ ├── structure.md │ ├── swagger.md │ └── todo.md └── provider │ ├── README.md │ ├── app.md │ ├── config.md │ ├── env.md │ └── log.md ├── framework ├── cobra │ ├── LICENSE.txt │ ├── Makefile │ ├── README.md │ ├── args.go │ ├── args_test.go │ ├── bash_completions.go │ ├── bash_completions.md │ ├── bash_completions_test.go │ ├── cobra.go │ ├── cobra │ │ ├── Makefile │ │ ├── README.md │ │ ├── cmd │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── golden_test.go │ │ │ ├── helpers.go │ │ │ ├── init.go │ │ │ ├── init_test.go │ │ │ ├── license_agpl.go │ │ │ ├── license_apache_2.go │ │ │ ├── license_bsd_clause_2.go │ │ │ ├── license_bsd_clause_3.go │ │ │ ├── license_gpl_2.go │ │ │ ├── license_gpl_3.go │ │ │ ├── license_lgpl.go │ │ │ ├── license_mit.go │ │ │ ├── licenses.go │ │ │ ├── project.go │ │ │ ├── project_test.go │ │ │ ├── root.go │ │ │ └── testdata │ │ │ │ ├── LICENSE.golden │ │ │ │ ├── main.go.golden │ │ │ │ ├── root.go.golden │ │ │ │ └── test.go.golden │ │ ├── main.go │ │ └── tpl │ │ │ └── main.go │ ├── cobra_test.go │ ├── command.go │ ├── command_notwin.go │ ├── command_test.go │ ├── command_win.go │ ├── custom_completions.go │ ├── custom_completions_test.go │ ├── doc │ │ ├── cmd_test.go │ │ ├── man_docs.go │ │ ├── man_docs.md │ │ ├── man_docs_test.go │ │ ├── man_examples_test.go │ │ ├── md_docs.go │ │ ├── md_docs.md │ │ ├── md_docs_test.go │ │ ├── rest_docs.go │ │ ├── rest_docs.md │ │ ├── rest_docs_test.go │ │ ├── util.go │ │ ├── yaml_docs.go │ │ ├── yaml_docs.md │ │ └── yaml_docs_test.go │ ├── fish_completions.go │ ├── fish_completions.md │ ├── powershell_completions.go │ ├── powershell_completions.md │ ├── powershell_completions_test.go │ ├── shell_completions.go │ ├── version.go │ ├── zsh_completions.go │ ├── zsh_completions.md │ └── zsh_completions_test.go ├── command │ ├── app.go │ ├── build.go │ ├── cmd.go │ ├── cron.go │ ├── deploy.go │ ├── dev.go │ ├── env.go │ ├── go_cmd.go │ ├── go_cmd_test.go │ ├── help.go │ ├── kernel.go │ ├── kernel_test.go │ ├── middleware.go │ ├── new.go │ ├── npm.go │ ├── provider.go │ ├── provider_test.go │ ├── swagger │ │ ├── gen.go │ │ ├── index.go │ │ └── serve.go │ └── util │ │ └── container.go ├── container.go ├── container_test.go ├── contract │ ├── app.go │ ├── cache.go │ ├── config.go │ ├── env.go │ ├── id.go │ ├── kernel.go │ ├── log.go │ ├── ssh.go │ └── trace.go ├── gin │ ├── AUTHORS.md │ ├── BENCHMARKS.md │ ├── CHANGELOG.md │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── auth.go │ ├── auth_test.go │ ├── benchmarks_test.go │ ├── binding │ │ ├── binding.go │ │ ├── binding_body_test.go │ │ ├── binding_test.go │ │ ├── default_validator.go │ │ ├── form.go │ │ ├── form_mapping.go │ │ ├── form_mapping_benchmark_test.go │ │ ├── form_mapping_test.go │ │ ├── json.go │ │ ├── msgpack.go │ │ ├── protobuf.go │ │ ├── query.go │ │ ├── uri.go │ │ ├── validate_test.go │ │ ├── xml.go │ │ └── yaml.go │ ├── codecov.yml │ ├── context.go │ ├── context_appengine.go │ ├── context_test.go │ ├── debug.go │ ├── debug_test.go │ ├── deprecated.go │ ├── deprecated_test.go │ ├── doc.go │ ├── errors.go │ ├── errors_test.go │ ├── examples │ │ └── README.md │ ├── fs.go │ ├── gin.go │ ├── ginS │ │ ├── README.md │ │ └── gins.go │ ├── gin_integration_test.go │ ├── gin_test.go │ ├── githubapi_test.go │ ├── hade.go │ ├── internal │ │ └── json │ │ │ ├── json.go │ │ │ └── jsoniter.go │ ├── logger.go │ ├── logger_test.go │ ├── middleware_test.go │ ├── mode.go │ ├── mode_test.go │ ├── path.go │ ├── path_test.go │ ├── recovery.go │ ├── recovery_test.go │ ├── render │ │ ├── data.go │ │ ├── html.go │ │ ├── json.go │ │ ├── msgpack.go │ │ ├── protobuf.go │ │ ├── reader.go │ │ ├── redirect.go │ │ ├── render.go │ │ ├── render_test.go │ │ ├── text.go │ │ ├── xml.go │ │ └── yaml.go │ ├── response_writer.go │ ├── response_writer_test.go │ ├── routergroup.go │ ├── routergroup_test.go │ ├── routes_test.go │ ├── test │ ├── test_helpers.go │ ├── testdata │ │ ├── certificate │ │ │ ├── cert.pem │ │ │ └── key.pem │ │ ├── protoexample │ │ │ ├── test.pb.go │ │ │ └── test.proto │ │ └── template │ │ │ ├── hello.tmpl │ │ │ └── raw.tmpl │ ├── tree.go │ ├── tree_test.go │ ├── utils.go │ ├── utils_test.go │ ├── vendor │ │ └── vendor.json │ └── version.go ├── middleware │ └── .gitkeeper ├── provider.go ├── provider │ ├── app │ │ ├── provider.go │ │ ├── provider_test.go │ │ └── service.go │ ├── config │ │ ├── fake_provider.go │ │ ├── fake_service.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ ├── service.go │ │ └── service_test.go │ ├── demo │ │ ├── provider.go │ │ └── service.go │ ├── env │ │ ├── provider.go │ │ ├── provider_test.go │ │ └── service.go │ ├── gorm │ │ ├── provider.go │ │ ├── provider_test.go │ │ └── service.go │ ├── id │ │ ├── provider.go │ │ ├── provier_test.go │ │ └── service.go │ ├── kernel │ │ ├── provider.go │ │ └── service.go │ ├── log │ │ ├── formatter │ │ │ ├── json.go │ │ │ ├── prefix.go │ │ │ └── text.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ └── services │ │ │ ├── console.go │ │ │ ├── log.go │ │ │ ├── rotate.go │ │ │ └── single.go │ ├── ssh │ │ ├── provider.go │ │ └── service.go │ └── trace │ │ ├── provider.go │ │ └── service.go └── util │ ├── console.go │ ├── console_test.go │ ├── exec.go │ ├── file.go │ ├── http.go │ └── zip.go ├── frontend ├── App.vue ├── api │ ├── article.js │ ├── qiniu.js │ ├── remote-search.js │ └── role.js ├── assets │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ ├── custom-theme │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ └── index.css │ └── logo.png ├── components │ ├── BackToTop │ │ └── index.vue │ ├── Breadcrumb │ │ └── index.vue │ ├── Charts │ │ ├── Keyboard.vue │ │ ├── LineMarker.vue │ │ ├── MixChart.vue │ │ └── mixins │ │ │ └── resize.js │ ├── DndList │ │ └── index.vue │ ├── DragSelect │ │ └── index.vue │ ├── Dropzone │ │ └── index.vue │ ├── ErrorLog │ │ └── index.vue │ ├── GithubCorner │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── HeaderSearch │ │ └── index.vue │ ├── HelloWorld.vue │ ├── ImageCropper │ │ ├── index.vue │ │ └── utils │ │ │ ├── data2blob.js │ │ │ ├── effectRipple.js │ │ │ ├── language.js │ │ │ └── mimes.js │ ├── JsonEditor │ │ └── index.vue │ ├── Kanban │ │ └── index.vue │ ├── MDinput │ │ └── index.vue │ ├── MarkdownEditor │ │ ├── default-options.js │ │ └── index.vue │ ├── Pagination │ │ └── index.vue │ ├── PanThumb │ │ └── index.vue │ ├── RightPanel │ │ └── index.vue │ ├── Screenfull │ │ └── index.vue │ ├── Share │ │ └── DropdownMenu.vue │ ├── SizeSelect │ │ └── index.vue │ ├── Sticky │ │ └── index.vue │ ├── SvgIcon │ │ └── index.vue │ ├── TextHoverEffect │ │ └── Mallki.vue │ ├── ThemePicker │ │ └── index.vue │ ├── Tinymce │ │ ├── components │ │ │ └── EditorImage.vue │ │ ├── dynamicLoadScript.js │ │ ├── index.vue │ │ ├── plugins.js │ │ └── toolbar.js │ ├── Upload │ │ ├── SingleImage.vue │ │ ├── SingleImage2.vue │ │ └── SingleImage3.vue │ └── UploadExcel │ │ └── index.vue ├── directive │ ├── clipboard │ │ ├── clipboard.js │ │ └── index.js │ ├── el-drag-dialog │ │ ├── drag.js │ │ └── index.js │ ├── el-table │ │ ├── adaptive.js │ │ └── index.js │ ├── permission │ │ ├── index.js │ │ └── permission.js │ ├── sticky.js │ └── waves │ │ ├── index.js │ │ ├── waves.css │ │ └── waves.js ├── filters │ └── index.js ├── icons │ ├── index.js │ ├── svg │ │ ├── 404.svg │ │ ├── bug.svg │ │ ├── chart.svg │ │ ├── clipboard.svg │ │ ├── component.svg │ │ ├── dashboard.svg │ │ ├── documentation.svg │ │ ├── drag.svg │ │ ├── edit.svg │ │ ├── education.svg │ │ ├── email.svg │ │ ├── example.svg │ │ ├── excel.svg │ │ ├── exit-fullscreen.svg │ │ ├── eye-open.svg │ │ ├── eye.svg │ │ ├── form.svg │ │ ├── fullscreen.svg │ │ ├── guide.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── language.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── lock.svg │ │ ├── message.svg │ │ ├── money.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── pdf.svg │ │ ├── people.svg │ │ ├── peoples.svg │ │ ├── qq.svg │ │ ├── search.svg │ │ ├── shopping.svg │ │ ├── size.svg │ │ ├── skill.svg │ │ ├── star.svg │ │ ├── tab.svg │ │ ├── table.svg │ │ ├── theme.svg │ │ ├── tree-table.svg │ │ ├── tree.svg │ │ ├── user.svg │ │ ├── wechat.svg │ │ └── zip.svg │ └── svgo.yml ├── layout │ ├── components │ │ ├── AppMain.vue │ │ ├── Navbar.vue │ │ ├── Settings │ │ │ └── index.vue │ │ ├── Sidebar │ │ │ ├── FixiOSBug.js │ │ │ ├── Item.vue │ │ │ ├── Link.vue │ │ │ ├── Logo.vue │ │ │ ├── SidebarItem.vue │ │ │ └── index.vue │ │ ├── TagsView │ │ │ ├── ScrollPane.vue │ │ │ └── index.vue │ │ └── index.js │ ├── index.vue │ └── mixin │ │ └── ResizeHandler.js ├── main.js ├── permission.js ├── router │ ├── index.js │ └── modules │ │ ├── charts.js │ │ ├── components.js │ │ ├── nested.js │ │ └── table.js ├── settings.js ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── app.js │ │ ├── errorLog.js │ │ ├── permission.js │ │ ├── settings.js │ │ ├── tagsView.js │ │ └── user.js ├── styles │ ├── btn.scss │ ├── element-ui.scss │ ├── element-variables.scss │ ├── index.scss │ ├── mixin.scss │ ├── sidebar.scss │ ├── transition.scss │ └── variables.scss ├── utils │ ├── auth.js │ ├── clipboard.js │ ├── error-log.js │ ├── get-page-title.js │ ├── index.js │ ├── open-window.js │ ├── permission.js │ ├── scroll-to.js │ └── validate.js ├── vendor │ ├── Export2Excel.js │ └── Export2Zip.js └── views │ ├── charts │ ├── keyboard.vue │ ├── line.vue │ └── mix-chart.vue │ ├── clipboard │ └── index.vue │ ├── components-demo │ ├── avatar-upload.vue │ ├── back-to-top.vue │ ├── count-to.vue │ ├── dnd-list.vue │ ├── drag-dialog.vue │ ├── drag-kanban.vue │ ├── drag-select.vue │ ├── dropzone.vue │ ├── json-editor.vue │ ├── markdown.vue │ ├── mixin.vue │ ├── split-pane.vue │ ├── sticky.vue │ └── tinymce.vue │ ├── dashboard │ ├── admin │ │ ├── components │ │ │ ├── BarChart.vue │ │ │ ├── BoxCard.vue │ │ │ ├── LineChart.vue │ │ │ ├── PanelGroup.vue │ │ │ ├── PieChart.vue │ │ │ ├── RaddarChart.vue │ │ │ ├── TodoList │ │ │ │ ├── Todo.vue │ │ │ │ ├── index.scss │ │ │ │ └── index.vue │ │ │ ├── TransactionTable.vue │ │ │ └── mixins │ │ │ │ └── resize.js │ │ └── index.vue │ ├── editor │ │ └── index.vue │ └── index.vue │ ├── documentation │ └── index.vue │ ├── error-log │ ├── components │ │ ├── ErrorTestA.vue │ │ └── ErrorTestB.vue │ └── index.vue │ ├── error-page │ ├── 401.vue │ └── 404.vue │ ├── example │ ├── components │ │ ├── ArticleDetail.vue │ │ ├── Dropdown │ │ │ ├── Comment.vue │ │ │ ├── Platform.vue │ │ │ ├── SourceUrl.vue │ │ │ └── index.js │ │ └── Warning.vue │ ├── create.vue │ ├── edit.vue │ └── list.vue │ ├── excel │ ├── components │ │ ├── AutoWidthOption.vue │ │ ├── BookTypeOption.vue │ │ └── FilenameOption.vue │ ├── export-excel.vue │ ├── merge-header.vue │ ├── select-excel.vue │ └── upload-excel.vue │ ├── guide │ ├── index.vue │ └── steps.js │ ├── icons │ ├── element-icons.js │ ├── index.vue │ └── svg-icons.js │ ├── login │ ├── auth-redirect.vue │ └── components │ │ └── SocialSignin.vue │ ├── nested │ ├── menu1 │ │ ├── index.vue │ │ ├── menu1-1 │ │ │ └── index.vue │ │ ├── menu1-2 │ │ │ ├── index.vue │ │ │ ├── menu1-2-1 │ │ │ │ └── index.vue │ │ │ └── menu1-2-2 │ │ │ │ └── index.vue │ │ └── menu1-3 │ │ │ └── index.vue │ └── menu2 │ │ └── index.vue │ ├── pdf │ ├── content.js │ ├── download.vue │ └── index.vue │ ├── permission │ ├── components │ │ └── SwitchRoles.vue │ ├── directive.vue │ ├── page.vue │ └── role.vue │ ├── profile │ ├── components │ │ ├── Account.vue │ │ ├── Activity.vue │ │ ├── Timeline.vue │ │ └── UserCard.vue │ └── index.vue │ ├── qiniu │ └── upload.vue │ ├── redirect │ └── index.vue │ ├── tab │ ├── components │ │ └── TabPane.vue │ └── index.vue │ ├── table │ ├── complex-table.vue │ ├── drag-table.vue │ ├── dynamic-table │ │ ├── components │ │ │ ├── FixedThead.vue │ │ │ └── UnfixedThead.vue │ │ └── index.vue │ └── inline-edit-table.vue │ ├── theme │ └── index.vue │ └── zip │ └── index.vue ├── go.mod ├── go.sum ├── jest.config.js ├── jsconfig.json ├── main.go ├── mock ├── article.js ├── index.js ├── mock-server.js ├── remote-search.js ├── role │ ├── index.js │ └── routes.js ├── user.js └── utils.js ├── package.json ├── plop-templates ├── component │ ├── index.hbs │ └── prompt.js ├── store │ ├── index.hbs │ └── prompt.js ├── utils.js └── view │ ├── index.hbs │ └── prompt.js ├── plopfile.js ├── postcss.config.js ├── src ├── App.vue ├── api │ └── user.js ├── main.js ├── utils │ └── request.js └── views │ └── login │ └── index.vue ├── storage ├── cache │ └── .gitkeeper ├── coverage │ ├── .gitkeeper │ ├── hade.out │ └── index.html └── log │ └── .gitkeeper ├── swagger ├── docs.go ├── swagger.json └── swagger.yaml ├── testdata └── storage │ ├── logs │ └── hade.log │ └── pid │ └── app.pid ├── tests ├── .gitkeeper ├── env.go └── unit │ ├── .eslintrc.js │ ├── components │ ├── Hamburger.spec.js │ └── SvgIcon.spec.js │ └── utils │ ├── formatTime.spec.js │ ├── param2Obj.spec.js │ ├── parseTime.spec.js │ └── validate.spec.js └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | APP_ENV=development 2 | APP_URL=0.0.0.0:8989 3 | VUE_APP_BASE_API="http://localhost:8081" -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'development' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/dev-api' 6 | 7 | # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, 8 | # to control whether the babel-plugin-dynamic-import-node plugin is enabled. 9 | # It only does one thing by converting all import() to require(). 10 | # This configuration can significantly increase the speed of hot updates, 11 | # when you have a large number of pages. 12 | # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js 13 | 14 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 15 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Hade 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | 9 | DB_CONNECTION=mysql 10 | DB_HOST=127.0.0.1 11 | DB_PORT=3306 12 | DB_DATABASE=laravel 13 | DB_USERNAME=root 14 | DB_PASSWORD= 15 | 16 | BROADCAST_DRIVER=log 17 | CACHE_DRIVER=file 18 | QUEUE_CONNECTION=sync 19 | SESSION_DRIVER=file 20 | SESSION_LIFETIME=120 21 | 22 | REDIS_HOST=127.0.0.1 23 | REDIS_PASSWORD=null 24 | REDIS_PORT=6379 25 | 26 | MAIL_DRIVER=smtp 27 | MAIL_HOST=smtp.mailtrap.io 28 | MAIL_PORT=2525 29 | MAIL_USERNAME=null 30 | MAIL_PASSWORD=null 31 | MAIL_ENCRYPTION=null 32 | MAIL_FROM_ADDRESS=null 33 | MAIL_FROM_NAME="${APP_NAME}" 34 | 35 | AWS_ACCESS_KEY_ID= 36 | AWS_SECRET_ACCESS_KEY= 37 | AWS_DEFAULT_REGION=us-east-1 38 | AWS_BUCKET= 39 | 40 | PUSHER_APP_ID= 41 | PUSHER_APP_KEY= 42 | PUSHER_APP_SECRET= 43 | PUSHER_APP_CLUSTER=mt1 44 | 45 | MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 46 | MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 47 | 48 | VUE_APP_BASE_API="http://localhost:8080" -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # just a flag 2 | ENV = 'production' 3 | 4 | # base api 5 | VUE_APP_BASE_API = '/prod-api' 6 | 7 | -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | NODE_ENV = production 2 | 3 | # just a flag 4 | ENV = 'staging' 5 | 6 | # base api 7 | VUE_APP_BASE_API = '/stage-api' 8 | 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | storage/pid 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present PanJiaChen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hade框架文件 2 | 3 | 4 | 目前还处于alpha版本,请使用go get -u hade 实时更新 5 | -------------------------------------------------------------------------------- /app/console/command/demo.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "log" 5 | 6 | "hade/framework/cobra" 7 | "hade/framework/command/util" 8 | ) 9 | 10 | var DemoCommand = &cobra.Command{ 11 | Use: "demo", 12 | Short: "demo", 13 | RunE: func(c *cobra.Command, args []string) error { 14 | container := util.GetContainer(c.Root()) 15 | log.Println(container) 16 | return nil 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /app/console/kernel.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/cobra" 6 | hadeCommand "hade/framework/command" 7 | commandUtil "hade/framework/command/util" 8 | ) 9 | 10 | // RunCommand is command 11 | func RunCommand(container framework.Container) error { 12 | var rootCmd = &cobra.Command{ 13 | Use: "hade", 14 | Short: "main", 15 | Long: "hade commands", 16 | } 17 | 18 | ctx := commandUtil.RegiestContainer(container, rootCmd) 19 | 20 | hadeCommand.AddKernelCommands(rootCmd) 21 | 22 | // rootCmd.AddCronCommand("* * * * *", command.DemoCommand) 23 | 24 | return rootCmd.ExecuteContext(ctx) 25 | } 26 | -------------------------------------------------------------------------------- /app/http/kernel.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "hade/framework/gin" 5 | ) 6 | 7 | // NewHttpEngine is command 8 | func NewHttpEngine() (*gin.Engine, error) { 9 | gin.SetMode(gin.ReleaseMode) 10 | r := gin.Default() 11 | 12 | Routes(r) 13 | return r, nil 14 | } 15 | -------------------------------------------------------------------------------- /app/http/middleware/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/app/http/middleware/.gitkeeper -------------------------------------------------------------------------------- /app/http/module/demo/dto.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | type UserDTO struct { 4 | ID int `json:"id"` 5 | Name string `json:"name"` 6 | } 7 | -------------------------------------------------------------------------------- /app/http/module/demo/mapper.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | demoService "hade/app/provider/demo" 5 | ) 6 | 7 | func UserModelsToUserDTOs(models []UserModel) []UserDTO { 8 | ret := []UserDTO{} 9 | for _, model := range models { 10 | t := UserDTO{ 11 | ID: model.UserId, 12 | Name: model.Name, 13 | } 14 | ret = append(ret, t) 15 | } 16 | return ret 17 | } 18 | 19 | func StudentsToUserDTOs(students []demoService.Student) []UserDTO { 20 | ret := []UserDTO{} 21 | for _, student := range students { 22 | t := UserDTO{ 23 | ID: student.ID, 24 | Name: student.Name, 25 | } 26 | ret = append(ret, t) 27 | } 28 | return ret 29 | } 30 | -------------------------------------------------------------------------------- /app/http/module/demo/model.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | type UserModel struct { 4 | UserId int 5 | Name string 6 | Age int 7 | } 8 | -------------------------------------------------------------------------------- /app/http/module/demo/repository.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | type Repository struct { 4 | } 5 | 6 | func NewRepository() *Repository { 7 | return &Repository{} 8 | } 9 | 10 | func (r *Repository) GetUserIds() []int { 11 | return []int{1, 2} 12 | } 13 | 14 | func (r *Repository) GetUserByIds([]int) []UserModel { 15 | return []UserModel{ 16 | { 17 | UserId: 1, 18 | Name: "foo", 19 | Age: 11, 20 | }, 21 | { 22 | UserId: 2, 23 | Name: "bar", 24 | Age: 12, 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/http/module/demo/service.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | type Service struct { 4 | repository *Repository 5 | } 6 | 7 | func NewService() *Service { 8 | repository := NewRepository() 9 | return &Service{ 10 | repository: repository, 11 | } 12 | } 13 | 14 | func (s *Service) GetUsers() []UserModel { 15 | ids := s.repository.GetUserIds() 16 | return s.repository.GetUserByIds(ids) 17 | } 18 | -------------------------------------------------------------------------------- /app/http/route.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "hade/app/http/module/demo" 5 | "hade/framework/gin" 6 | ) 7 | 8 | func Routes(r *gin.Engine) { 9 | 10 | r.Static("/dist/", "./dist/") 11 | 12 | demo.Register(r) 13 | } 14 | -------------------------------------------------------------------------------- /app/http/swagger.go: -------------------------------------------------------------------------------- 1 | // Package http API. 2 | // @title hade 3 | // @version 1.1 4 | // @description hade测试 5 | // @termsOfService https://github.com/swaggo/swag 6 | 7 | // @contact.name yejianfeng 8 | // @contact.email yejianfeng 9 | 10 | // @license.name Apache 2.0 11 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 12 | 13 | // @host 127.0.0.1:8067 14 | // @BasePath / 15 | // @query.collection.format multi 16 | 17 | // @securityDefinitions.basic BasicAuth 18 | 19 | // @securityDefinitions.apikey ApiKeyAuth 20 | // @in header 21 | // @name Authorization 22 | 23 | // @x-extension-openapi {"example": "value on a json format"} 24 | 25 | package http 26 | -------------------------------------------------------------------------------- /app/provider/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/app/provider/.gitkeeper -------------------------------------------------------------------------------- /app/provider/demo/contract.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | const DemoKey = "demo" 4 | 5 | type IService interface { 6 | GetAllStudent() []Student 7 | } 8 | 9 | type Student struct { 10 | ID int 11 | Name string 12 | } 13 | -------------------------------------------------------------------------------- /app/provider/demo/provider.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "hade/framework" 5 | ) 6 | 7 | type DemoProvider struct { 8 | framework.ServiceProvider 9 | 10 | c framework.Container 11 | } 12 | 13 | func (sp *DemoProvider) Name() string { 14 | return DemoKey 15 | } 16 | 17 | func (sp *DemoProvider) Register(c framework.Container) framework.NewInstance { 18 | return NewService 19 | } 20 | 21 | func (sp *DemoProvider) IsDefer() bool { 22 | return false 23 | } 24 | 25 | func (sp *DemoProvider) Params() []interface{} { 26 | return []interface{}{sp.c} 27 | } 28 | 29 | func (sp *DemoProvider) Boot(c framework.Container) error { 30 | sp.c = c 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /app/provider/demo/service.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import "hade/framework" 4 | 5 | type Service struct { 6 | container framework.Container 7 | } 8 | 9 | func NewService(params ...interface{}) (interface{}, error) { 10 | container := params[0].(framework.Container) 11 | return &Service{container: container}, nil 12 | } 13 | 14 | func (s *Service) GetAllStudent() []Student { 15 | return []Student{ 16 | { 17 | ID: 1, 18 | Name: "foo", 19 | }, 20 | { 21 | ID: 2, 22 | Name: "bar", 23 | }, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/provider/kernel.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "hade/app/provider/demo" 5 | "hade/framework" 6 | ) 7 | 8 | // customer provider 9 | func RegisterCustomProvider(c framework.Container) { 10 | c.Bind(&demo.DemoProvider{}, true) 11 | } 12 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /config/development/app.yaml: -------------------------------------------------------------------------------- 1 | url: http://127.0.0.1:8066 2 | 3 | swagger_open: true 4 | 5 | dev_fresh: 1 6 | 7 | path: 8 | storage: "/hade_sts/testdata/storage/" 9 | log: "" 10 | pid: "" 11 | cache: "" -------------------------------------------------------------------------------- /config/development/database.yaml: -------------------------------------------------------------------------------- 1 | mysql: 2 | hostname: 127.0.0.1 3 | username: yejianfeng 4 | password: env(DB_PASSWORD) 5 | timeout: 1 6 | readtime: 2.3 7 | 8 | default: 9 | driver: mysql 10 | hostname: localhost 11 | database: hade 12 | username: root 13 | password: "123456" 14 | charset: utf8mb4 15 | collation: utf8mb4_unicode_ci -------------------------------------------------------------------------------- /config/development/deploy.yaml: -------------------------------------------------------------------------------- 1 | host: 127.0.0.1 2 | user: yejianfeng 3 | password: 123abc 4 | rsa_key: 5 | timeout: 1000 6 | remote_path: "/home/yejianfeng/" 7 | post_shell: 8 | - "./hade serve restart" 9 | - "pwd" 10 | goos: linux 11 | goarch: amd64 -------------------------------------------------------------------------------- /config/development/gift.yaml: -------------------------------------------------------------------------------- 1 | endpoint: "" 2 | access_key: "" 3 | secret_key: "" 4 | use_ssl: true -------------------------------------------------------------------------------- /config/development/log.yaml: -------------------------------------------------------------------------------- 1 | driver: console 2 | level: trace -------------------------------------------------------------------------------- /config/development/swagger.yaml: -------------------------------------------------------------------------------- 1 | url: http://127.0.0.1:8069 -------------------------------------------------------------------------------- /config/production/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/config/production/.gitkeeper -------------------------------------------------------------------------------- /config/testing/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/config/testing/.gitkeeper -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go test ./... -coverprofile=storage/coverage/hade.out 4 | go tool cover -html=storage/coverage/hade.out -o storage/coverage/index.html 5 | open storage/coverage/index.html 6 | -------------------------------------------------------------------------------- /docs/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | 2 | export default ({ Vue }) => { 3 | }; 4 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | actionText: 开始体验 4 | actionLink: /guide/introduce 5 | footer: MIT Licensed | Copyright © 2020-present jianfengye 6 | features: 7 | - title: 基于协议 8 | details: 服务与服务间的协议是基于协议进行交互的。 9 | - title: 前后端协同 10 | details: 前后端协同开发 11 | - title: 命令行 12 | details: 有充分的命令行工具 13 | - title: 集成定时服务 14 | details: 如果你需要启动定时服务,提供命令进行定时服务的启动 15 | - title: 文档丰富 16 | details: 提供丰富的文档说明,提供丰富的文档说明 17 | - title: 开发模式 18 | details: 在开发模式下进行前后端开发,极大提高了开发效率和开发体验 -------------------------------------------------------------------------------- /docs/guide/README.md: -------------------------------------------------------------------------------- 1 | # 指南 2 | 3 | --- 4 | 5 | hade是一个注重协议,注重开发效率的前后端一体化框架。hade框架的目标在于将go+vue模型的应用开发最简化,并且提供统一,一体化的脚手架工具促进业务开发。 -------------------------------------------------------------------------------- /docs/guide/env.md: -------------------------------------------------------------------------------- 1 | # 环境变量 2 | 3 | ## 设置 4 | 5 | hade 支持使用应用默认下的隐藏文件 `.env` 来配置各个机器不同的环境变量。 6 | 7 | ``` 8 | APP_ENV=development 9 | 10 | DB_PASSWORD=mypassword 11 | ``` 12 | 13 | 环境变量的设置可以在配置文件中通过 `env([环境变量])` 来获取到。 14 | 15 | 比如: 16 | 17 | ``` 18 | mysql: 19 | hostname: 127.0.0.1 20 | username: yejianfeng 21 | password: env(DB_PASSWORD) 22 | timeout: 1 23 | readtime: 2.3 24 | ``` 25 | 26 | 27 | ## 应用环境 28 | 29 | hade 启动应用的默认应用环境为 development。 30 | 31 | 你可以通过设置 .env 文件中的 APP_ENV 设置应用环境。 32 | 33 | 应用环境建议选择: 34 | - development // 开发使用 35 | - production // 线上使用 36 | - testing //测试环境 37 | 38 | 应用环境对应配置的文件夹,配置服务会去对应应用环境的文件夹中寻找配置。 39 | 40 | 比如应用环境为 development,在代码中使用 41 | ``` 42 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 43 | url := configService.GetString("app.url") 44 | ``` 45 | 46 | 查找文件为:`config/development/app.yaml` 47 | 48 | 通过命令`./hade env`也可以获取当前应用环境: 49 | 50 | ``` 51 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade env 52 | environment: development 53 | ``` -------------------------------------------------------------------------------- /docs/guide/introduce.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | --- 4 | 5 | hade是一个注重协议,注重开发效率的前后端一体化框架。hade框架的目标在于将go+vue模型的应用开发最简化,并且提供统一,一体化的脚手架工具促进业务开发。 6 | 7 | 我们相信在GO的框架开发中,指定协议比实现更为重要。hade框架对于具体应用开发者来说最便利的功能,在于其制定了一系列的基本协议,在具体的业务逻辑中,可以通过每个协议独特的Key来从全局容器中获取已经注入的服务实例。所有的具体应用开发,在业务逻辑中,都是按照hade约定的协议进行逻辑处理,从而脱离了具体的每个服务所定义的个性化差异。 8 | -------------------------------------------------------------------------------- /docs/guide/middleware.md: -------------------------------------------------------------------------------- 1 | # 中间件 2 | 3 | ## 指南 4 | hade 的 HTTP 路由服务并没有自己开发,而是使用 gin。gin 生态已经有非常完善的(中间件体系)[https://github.com/gin-contrib]。 5 | 6 | 我们没有必要重新开发这些中间件。所以 hade 框架提供了 middleware 命令来获取这些中间件。(不能直接使用go get 的方式来获取,因为 hade 在 gin 基础上做了一些微调) 7 | 8 | ``` 9 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade middleware 10 | hade middleware 11 | 12 | Usage: 13 | hade middleware [flags] 14 | hade middleware [command] 15 | 16 | Available Commands: 17 | add add middleware to app, https://github.com/gin-contrib/[middleware].git 18 | list list all installed middleware 19 | remove remove middleware from app 20 | 21 | Flags: 22 | -h, --help help for middleware 23 | 24 | Use "hade middleware [command] --help" for more information about a command. 25 | ``` 26 | 27 | ## 安装 28 | 29 | 可以安装 https://github.com/gin-contrib/ 项目中的任何中间件,使用命令 `./hade middleware add gzip` 30 | 31 | 命令会从 https://github.com/gin-contrib/gzip.git 项目中下载中间件,并且安装到 `app/http/middleware` 中。 32 | 33 | ## 查询 34 | 35 | 检查目前已经安装了哪些中间件,可以使用命令 `./hade middleware list` 36 | 37 | ## 删除 38 | 39 | 删除某个中间件,可以使用命令 `./hade middleware remove` -------------------------------------------------------------------------------- /docs/guide/todo.md: -------------------------------------------------------------------------------- 1 | # 待做事项 2 | --- 3 | 4 | hade 框架目前还没有到完善地步,这里列一下计划后续做的工作,有兴趣的同学可以一起进行开发。 5 | 6 | git 地址: https://hade 7 | 8 | - 增加traceid机制 9 | - 调试deploy命令 10 | - 增加module相关自动化命令 11 | - 引入rpc机制 12 | - service provider 自动生成 http 和 rpc 的命令 -------------------------------------------------------------------------------- /docs/provider/README.md: -------------------------------------------------------------------------------- 1 | # 服务提供者 2 | 3 | --- 4 | 5 | 这里列出了 hade 框架自带的服务提供者接口和协议说明 -------------------------------------------------------------------------------- /docs/provider/app.md: -------------------------------------------------------------------------------- 1 | # hade:app 2 | 3 | 提供基础的 app 框架目录结构 4 | 5 | ``` golang 6 | package contract 7 | 8 | // AppKey is the key in container 9 | const AppKey = "hade:app" 10 | 11 | // App define application structure 12 | type App interface { 13 | // application version 14 | Version() string 15 | // base path which is the base folder 16 | BasePath() string 17 | // config folder which contains config 18 | ConfigPath() string 19 | // environmentPath which contain .env 20 | EnvironmentPath() string 21 | // storagePath define storage folder 22 | StoragePath() string 23 | // logPath define logPath 24 | LogPath() string 25 | // frameworkPath define frameworkPath 26 | FrameworkPath() string 27 | } 28 | 29 | ``` -------------------------------------------------------------------------------- /docs/provider/env.md: -------------------------------------------------------------------------------- 1 | # hade:env 2 | 3 | 提供环境变量相关方法 4 | 5 | ``` golang 6 | package contract 7 | 8 | const ( 9 | // EnvProduction represent the environment which build for production 10 | EnvProduction = "production" 11 | // EnvTesting represent the environment which build for test 12 | EnvTesting = "testing" 13 | // EnvDevelopment represent the environment which build for development 14 | EnvDevelopment = "development" 15 | 16 | // EnvKey is the key in container 17 | EnvKey = "hade:env" 18 | ) 19 | 20 | // Env define golang run enviornment 21 | // it set some config which want ignored in git 22 | type Env interface { 23 | // AppEnv get current environment 24 | AppEnv() string 25 | 26 | // IsExist check setting is exist 27 | IsExist(string) bool 28 | // Get environment setting, if not exist, return "" 29 | Get(string) string 30 | // All return all settings 31 | All() map[string]string 32 | } 33 | 34 | ``` -------------------------------------------------------------------------------- /docs/provider/log.md: -------------------------------------------------------------------------------- 1 | # hade:log 2 | 3 | 提供日志记录相关操作 4 | 5 | ``` golang 6 | type Log interface { 7 | // Panic will call panic(fields) for debug 8 | Panic(ctx context.Context, msg string, fields []interface{}) 9 | // Fatal will add fatal record which contains msg and fields 10 | Fatal(ctx context.Context, msg string, fields []interface{}) 11 | // Error will add error record which contains msg and fields 12 | Error(ctx context.Context, msg string, fields []interface{}) 13 | // Warn will add warn record which contains msg and fields 14 | Warn(ctx context.Context, msg string, fields []interface{}) 15 | // Info will add info record which contains msg and fields 16 | Info(ctx context.Context, msg string, fields []interface{}) 17 | // Debug will add debug record which contains msg and fields 18 | Debug(ctx context.Context, msg string, fields []interface{}) 19 | // Trace will add trace info which contains msg and fields 20 | Trace(ctx context.Context, msg string, fields []interface{}) 21 | 22 | // SetLevel set log level, and higher level will be recorded 23 | SetLevel(level LogLevel) 24 | // SetCxtFielder will get fields from context 25 | SetCxtFielder(handler CtxFielder) 26 | // SetFormatter will set formatter handler will covert data to string for recording 27 | SetFormatter(formatter Formatter) 28 | } 29 | 30 | ``` -------------------------------------------------------------------------------- /framework/cobra/Makefile: -------------------------------------------------------------------------------- 1 | BIN="./bin" 2 | SRC=$(shell find . -name "*.go") 3 | 4 | ifeq (, $(shell which richgo)) 5 | $(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo") 6 | endif 7 | 8 | .PHONY: fmt vet test cobra_generator install_deps clean 9 | 10 | default: all 11 | 12 | all: fmt vet test cobra_generator 13 | 14 | fmt: 15 | $(info ******************** checking formatting ********************) 16 | @test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1) 17 | 18 | test: install_deps vet 19 | $(info ******************** running tests ********************) 20 | richgo test -v ./... 21 | 22 | cobra_generator: install_deps 23 | $(info ******************** building generator ********************) 24 | mkdir -p $(BIN) 25 | make -C cobra all 26 | 27 | install_deps: 28 | $(info ******************** downloading dependencies ********************) 29 | go get -v ./... 30 | 31 | vet: 32 | $(info ******************** vetting ********************) 33 | go vet ./... 34 | 35 | clean: 36 | rm -rf $(BIN) 37 | -------------------------------------------------------------------------------- /framework/cobra/cobra/Makefile: -------------------------------------------------------------------------------- 1 | XC_OS="linux darwin" 2 | XC_ARCH="amd64" 3 | XC_PARALLEL="2" 4 | BIN="../bin" 5 | SRC=$(shell find . -name "*.go") 6 | 7 | ifeq (, $(shell which gox)) 8 | $(warning "could not find gox in $(PATH), run: go get github.com/mitchellh/gox") 9 | endif 10 | 11 | .PHONY: all build 12 | 13 | default: all 14 | 15 | all: build 16 | 17 | build: 18 | gox \ 19 | -os=$(XC_OS) \ 20 | -arch=$(XC_ARCH) \ 21 | -parallel=$(XC_PARALLEL) \ 22 | -output=$(BIN)/{{.Dir}}_{{.OS}}_{{.Arch}} \ 23 | ; 24 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/add_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestGoldenAddCmd(t *testing.T) { 10 | command := &Command{ 11 | CmdName: "test", 12 | CmdParent: parentName, 13 | Project: getProject(), 14 | } 15 | defer os.RemoveAll(command.AbsolutePath) 16 | 17 | command.Project.Create() 18 | if err := command.Create(); err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName) 23 | goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName) 24 | err := compareFiles(generatedFile, goldenFile) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | } 29 | 30 | func TestValidateCmdName(t *testing.T) { 31 | testCases := []struct { 32 | input string 33 | expected string 34 | }{ 35 | {"cmdName", "cmdName"}, 36 | {"cmd_name", "cmdName"}, 37 | {"cmd-name", "cmdName"}, 38 | {"cmd______Name", "cmdName"}, 39 | {"cmd------Name", "cmdName"}, 40 | {"cmd______name", "cmdName"}, 41 | {"cmd------name", "cmdName"}, 42 | {"cmdName-----", "cmdName"}, 43 | {"cmdname-", "cmdname"}, 44 | } 45 | 46 | for _, testCase := range testCases { 47 | got := validateCmdName(testCase.input) 48 | if testCase.expected != got { 49 | t.Errorf("Expected %q, got %q", testCase.expected, got) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/init_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | ) 9 | 10 | func getProject() *Project { 11 | wd, _ := os.Getwd() 12 | return &Project{ 13 | AbsolutePath: fmt.Sprintf("%s/testproject", wd), 14 | Legal: getLicense(), 15 | Copyright: copyrightLine(), 16 | AppName: "testproject", 17 | PkgName: "github.com/spf13/testproject", 18 | Viper: true, 19 | } 20 | } 21 | 22 | func TestGoldenInitCmd(t *testing.T) { 23 | project := getProject() 24 | defer os.RemoveAll(project.AbsolutePath) 25 | 26 | if err := project.Create(); err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | expectedFiles := []string{"LICENSE", "main.go", "cmd/root.go"} 31 | for _, f := range expectedFiles { 32 | generatedFile := fmt.Sprintf("%s/%s", project.AbsolutePath, f) 33 | goldenFile := fmt.Sprintf("testdata/%s.golden", filepath.Base(f)) 34 | err := compareFiles(generatedFile, goldenFile) 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/project_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | /* todo: write tests */ 4 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/testdata/main.go.golden: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2020 NAME HERE 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | package main 17 | 18 | import "github.com/spf13/testproject/cmd" 19 | 20 | func main() { 21 | cmd.Execute() 22 | } 23 | -------------------------------------------------------------------------------- /framework/cobra/cobra/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2015 Steve Francia . 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | 14 | package main 15 | 16 | import ( 17 | "os" 18 | 19 | "hade/framework/cobra/cobra/cmd" 20 | ) 21 | 22 | func main() { 23 | if err := cmd.Execute(); err != nil { 24 | os.Exit(1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /framework/cobra/cobra_test.go: -------------------------------------------------------------------------------- 1 | package cobra 2 | 3 | import ( 4 | "testing" 5 | "text/template" 6 | ) 7 | 8 | func TestAddTemplateFunctions(t *testing.T) { 9 | AddTemplateFunc("t", func() bool { return true }) 10 | AddTemplateFuncs(template.FuncMap{ 11 | "f": func() bool { return false }, 12 | "h": func() string { return "Hello," }, 13 | "w": func() string { return "world." }}) 14 | 15 | c := &Command{} 16 | c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) 17 | 18 | const expected = "Hello, world." 19 | if got := c.UsageString(); got != expected { 20 | t.Errorf("Expected UsageString: %v\nGot: %v", expected, got) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /framework/cobra/command_notwin.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cobra 4 | 5 | var preExecHookFn func(*Command) 6 | -------------------------------------------------------------------------------- /framework/cobra/command_win.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package cobra 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "time" 9 | 10 | "github.com/inconshreveable/mousetrap" 11 | ) 12 | 13 | var preExecHookFn = preExecHook 14 | 15 | func preExecHook(c *Command) { 16 | if MousetrapHelpText != "" && mousetrap.StartedByExplorer() { 17 | c.Print(MousetrapHelpText) 18 | if MousetrapDisplayDuration > 0 { 19 | time.Sleep(MousetrapDisplayDuration) 20 | } else { 21 | c.Println("Press return to continue...") 22 | fmt.Scanln() 23 | } 24 | os.Exit(1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /framework/cobra/doc/man_docs.md: -------------------------------------------------------------------------------- 1 | # Generating Man Pages For Your Own cobra.Command 2 | 3 | Generating man pages from a cobra command is incredibly easy. An example is as follows: 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "log" 10 | 11 | "hade/framework/cobra" 12 | "hade/framework/cobra/doc" 13 | ) 14 | 15 | func main() { 16 | cmd := &cobra.Command{ 17 | Use: "test", 18 | Short: "my test program", 19 | } 20 | header := &doc.GenManHeader{ 21 | Title: "MINE", 22 | Section: "3", 23 | } 24 | err := doc.GenManTree(cmd, header, "/tmp") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | ``` 30 | 31 | That will get you a man page `/tmp/test.3` 32 | -------------------------------------------------------------------------------- /framework/cobra/doc/man_examples_test.go: -------------------------------------------------------------------------------- 1 | package doc_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "hade/framework/cobra" 8 | "hade/framework/cobra/doc" 9 | ) 10 | 11 | func ExampleGenManTree() { 12 | cmd := &cobra.Command{ 13 | Use: "test", 14 | Short: "my test program", 15 | } 16 | header := &doc.GenManHeader{ 17 | Title: "MINE", 18 | Section: "3", 19 | } 20 | doc.GenManTree(cmd, header, "/tmp") 21 | } 22 | 23 | func ExampleGenMan() { 24 | cmd := &cobra.Command{ 25 | Use: "test", 26 | Short: "my test program", 27 | } 28 | header := &doc.GenManHeader{ 29 | Title: "MINE", 30 | Section: "3", 31 | } 32 | out := new(bytes.Buffer) 33 | doc.GenMan(cmd, header, out) 34 | fmt.Print(out.String()) 35 | } 36 | -------------------------------------------------------------------------------- /framework/cobra/fish_completions.md: -------------------------------------------------------------------------------- 1 | ## Generating Fish Completions for your own cobra.Command 2 | 3 | Cobra supports native Fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. 4 | 5 | ### Limitations 6 | 7 | * Custom completions implemented using the `ValidArgsFunction` and `RegisterFlagCompletionFunc()` are supported automatically but the ones implemented in Bash scripting are not. 8 | -------------------------------------------------------------------------------- /framework/cobra/powershell_completions.md: -------------------------------------------------------------------------------- 1 | # Generating PowerShell Completions For Your Own cobra.Command 2 | 3 | Cobra can generate PowerShell completion scripts. Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles. 4 | 5 | # What's supported 6 | 7 | - Completion for subcommands using their `.Short` description 8 | - Completion for non-hidden flags using their `.Name` and `.Shorthand` 9 | 10 | # What's not yet supported 11 | 12 | - Command aliases 13 | - Required, filename or custom flags (they will work like normal flags) 14 | - Custom completion scripts 15 | -------------------------------------------------------------------------------- /framework/cobra/version.go: -------------------------------------------------------------------------------- 1 | package cobra 2 | 3 | const version = "1.0.0" 4 | -------------------------------------------------------------------------------- /framework/command/env.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | 6 | "hade/framework/cobra" 7 | "hade/framework/command/util" 8 | "hade/framework/contract" 9 | ) 10 | 11 | // envCommand show current envionment 12 | var envCommand = &cobra.Command{ 13 | Use: "env", 14 | Short: "get current environment", 15 | Run: func(c *cobra.Command, args []string) { 16 | container := util.GetContainer(c.Root()) 17 | envService := container.MustMake(contract.EnvKey).(contract.Env) 18 | fmt.Println("environment:", envService.AppEnv()) 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /framework/command/go_cmd.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | 8 | "hade/framework/cobra" 9 | ) 10 | 11 | // go just run local go bin 12 | var goCommand = &cobra.Command{ 13 | Use: "go", 14 | Short: "运行path/go程序,要求go 必须安装", 15 | RunE: func(c *cobra.Command, args []string) error { 16 | path, err := exec.LookPath("go") 17 | if err != nil { 18 | log.Fatalln("hade go: should install go in your PATH") 19 | } 20 | 21 | cmd := exec.Command(path, args...) 22 | cmd.Stdout = os.Stdout 23 | cmd.Stderr = os.Stderr 24 | cmd.Run() 25 | return nil 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /framework/command/go_cmd_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | -------------------------------------------------------------------------------- /framework/command/help.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "hade/framework/cobra" 5 | ) 6 | 7 | // helpCommand show current envionment 8 | var helpCommand = &cobra.Command{ 9 | Use: "help", 10 | Short: "get help info", 11 | Run: func(c *cobra.Command, args []string) { 12 | cmd, _, e := c.Root().Find(args) 13 | if cmd == nil || e != nil { 14 | c.Printf("Unknown help topic %#q\n", args) 15 | c.Root().Usage() 16 | } else { 17 | cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown 18 | cmd.Help() 19 | } 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /framework/command/kernel_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | -------------------------------------------------------------------------------- /framework/command/npm.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | 8 | "hade/framework/cobra" 9 | ) 10 | 11 | // npm just run local go bin 12 | var npmCommand = &cobra.Command{ 13 | Use: "npm", 14 | Short: "运行 PATH/npm 的命令", 15 | RunE: func(c *cobra.Command, args []string) error { 16 | path, err := exec.LookPath("npm") 17 | if err != nil { 18 | log.Fatalln("hade npm: should install npm in your PATH") 19 | } 20 | 21 | cmd := exec.Command(path, args...) 22 | cmd.Stdout = os.Stdout 23 | cmd.Stderr = os.Stderr 24 | cmd.Run() 25 | return nil 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /framework/command/provider_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "hade/app/provider/demo" 8 | "hade/framework/provider/app" 9 | ) 10 | 11 | func Test_hade_providerDoc(t *testing.T) { 12 | appProvider := &app.HadeAppProvider{} 13 | got := providerDoc(appProvider) 14 | fmt.Println(got) 15 | return 16 | } 17 | 18 | func Test_custom_providerDoc(t *testing.T) { 19 | demoProvider := &demo.DemoProvider{} 20 | got2 := providerDoc(demoProvider) 21 | fmt.Println(got2) 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /framework/command/swagger/gen.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "hade/framework/cobra" 8 | commandUtil "hade/framework/command/util" 9 | "hade/framework/contract" 10 | 11 | "github.com/swaggo/swag/gen" 12 | ) 13 | 14 | // envCommand show current envionment 15 | var GenCommand = &cobra.Command{ 16 | Use: "gen", 17 | Short: "generate swagger file, contain swagger.yaml, doc.go", 18 | Run: func(c *cobra.Command, args []string) { 19 | container := commandUtil.GetContainer(c.Root()) 20 | appService := container.MustMake(contract.AppKey).(contract.App) 21 | 22 | outputDir := filepath.Join(appService.BasePath(), "app", "http", "swagger") 23 | 24 | conf := &gen.Config{ 25 | SearchDir: "./app/http/", 26 | Excludes: "", 27 | OutputDir: outputDir, 28 | MainAPIFile: "swagger.go", 29 | PropNamingStrategy: "", 30 | ParseVendor: false, 31 | ParseDependency: false, 32 | ParseInternal: false, 33 | MarkdownFilesDir: "", 34 | GeneratedTime: false, 35 | } 36 | err := gen.New().Build(conf) 37 | if err != nil { 38 | fmt.Println(err) 39 | } 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /framework/command/swagger/index.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import "hade/framework/cobra" 4 | 5 | var IndexCommand = &cobra.Command{ 6 | Use: "swagger", 7 | Short: "swagger operator", 8 | RunE: func(c *cobra.Command, args []string) error { 9 | if len(args) == 0 { 10 | c.Help() 11 | } 12 | return nil 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /framework/command/util/container.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "hade/framework" 8 | "hade/framework/cobra" 9 | ) 10 | 11 | type ContainerKey string 12 | 13 | const containerKey = ContainerKey("container") 14 | 15 | func RegiestContainer(c framework.Container, cmd *cobra.Command) context.Context { 16 | ctx := context.WithValue(context.Background(), containerKey, c) 17 | // cmd.SetContext(ctx) 18 | return ctx 19 | } 20 | 21 | func GetContainer(cmd *cobra.Command) framework.Container { 22 | val := cmd.Context().Value(containerKey) 23 | if val == nil { 24 | fmt.Println("val is nil, register") 25 | container := framework.NewHadeContainer() 26 | RegiestContainer(container, cmd) 27 | return container 28 | } 29 | return val.(framework.Container) 30 | } 31 | -------------------------------------------------------------------------------- /framework/contract/app.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | // AppKey is the key in container 4 | const AppKey = "hade:app" 5 | 6 | // App define application structure 7 | type App interface { 8 | // application version 9 | Version() string 10 | // base path which is the base folder 11 | BasePath() string 12 | // app path which is the app folder 13 | AppPath() string 14 | // app/http 15 | HttpPath() string 16 | // app/http/swagger 17 | SwaggerPath() string 18 | // app/console 19 | ConsolePath() string 20 | // config folder which contains config 21 | ConfigPath() string 22 | // environmentPath which contain .env 23 | EnvironmentPath() string 24 | // storagePath define storage folder 25 | StoragePath() string 26 | // logPath define logPath 27 | LogPath() string 28 | 29 | PidPath() string 30 | 31 | CachePath() string 32 | // load config 33 | LoadAppConfig(map[string]string) 34 | } 35 | -------------------------------------------------------------------------------- /framework/contract/cache.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import "time" 4 | 5 | const CacheKey = "hade:cache" 6 | 7 | type Cache interface { 8 | // Get value by key 9 | Get(key string) []byte 10 | // Pull get and delete 11 | Pull(key string) []byte 12 | // Check key exists 13 | Has(key string) bool 14 | // Set value to key 15 | Put(key string, val []byte, duration time.Duration) error 16 | // Forever Put 17 | Forever(key string, val []byte) error 18 | // Delete key 19 | Delete(key string) error 20 | // increment 21 | Increment(key string) int 22 | // decrement 23 | Decrement(key string) int 24 | } 25 | -------------------------------------------------------------------------------- /framework/contract/env.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | const ( 4 | // EnvProduction represent the environment which build for production 5 | EnvProduction = "production" 6 | // EnvTesting represent the environment which build for test 7 | EnvTesting = "testing" 8 | // EnvDevelopment represent the environment which build for development 9 | EnvDevelopment = "development" 10 | 11 | // EnvKey is the key in container 12 | EnvKey = "hade:env" 13 | ) 14 | 15 | // Env define golang run enviornment 16 | // it set some config which want ignored in git 17 | type Env interface { 18 | // AppEnv get current environment 19 | AppEnv() string 20 | 21 | // IsExist check setting is exist 22 | IsExist(string) bool 23 | // Get environment setting, if not exist, return "" 24 | Get(string) string 25 | // All return all settings 26 | All() map[string]string 27 | } 28 | -------------------------------------------------------------------------------- /framework/contract/id.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | const IDKey = "hade:id" 4 | 5 | type IDService interface { 6 | NewID() string 7 | } 8 | -------------------------------------------------------------------------------- /framework/contract/kernel.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "hade/framework/gin" 5 | ) 6 | 7 | const KernelKey = "hade:kernel" 8 | 9 | type Kernel interface { 10 | HttpEngine() *gin.Engine 11 | } 12 | -------------------------------------------------------------------------------- /framework/contract/ssh.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | const SSHKey = "hade:ssh" 4 | 5 | // SSH 相关接口 6 | type SSH interface { 7 | // 运行shell,并输出结果 8 | Run(shell string) ([]byte, error) 9 | // 将本地文件放到远端 10 | Upload(src, dist string) error 11 | // 将远端文件放到本地 12 | Download(src, dist string) error 13 | // 将本地文件夹放到远端 14 | UploadDir(src, dist string) error 15 | 16 | DownloadDir(src, dist string) error 17 | } 18 | 19 | // SSH的连接配置 20 | // 如果设置了Password,优先走Password模式,否则走RSAKey 21 | // 密码模式和rsa key模式必须至少有一个 22 | type SSHConfig struct { 23 | User string 24 | Password string 25 | Host string 26 | Port string 27 | RsaKey string 28 | Timeout int 29 | } 30 | -------------------------------------------------------------------------------- /framework/gin/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - With issues: 4 | - Use the search tool before opening a new issue. 5 | - Please provide source code and commit sha if you found a bug. 6 | - Review existing issues and provide feedback or react to them. 7 | 8 | - With pull requests: 9 | - Open your pull request against `master` 10 | - Your pull request should have no more than two commits, if not you should squash them. 11 | - It should pass all tests in the available continuous integrations systems such as TravisCI. 12 | - You should add/modify tests to cover your proposed code changes. 13 | - If your pull request contains a new feature, please document it on the README. 14 | -------------------------------------------------------------------------------- /framework/gin/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Manuel Martínez-Almeida 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /framework/gin/binding/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "net/http" 12 | 13 | "hade/framework/gin/internal/json" 14 | ) 15 | 16 | // EnableDecoderUseNumber is used to call the UseNumber method on the JSON 17 | // Decoder instance. UseNumber causes the Decoder to unmarshal a number into an 18 | // interface{} as a Number instead of as a float64. 19 | var EnableDecoderUseNumber = false 20 | 21 | type jsonBinding struct{} 22 | 23 | func (jsonBinding) Name() string { 24 | return "json" 25 | } 26 | 27 | func (jsonBinding) Bind(req *http.Request, obj interface{}) error { 28 | if req == nil || req.Body == nil { 29 | return fmt.Errorf("invalid request") 30 | } 31 | return decodeJSON(req.Body, obj) 32 | } 33 | 34 | func (jsonBinding) BindBody(body []byte, obj interface{}) error { 35 | return decodeJSON(bytes.NewReader(body), obj) 36 | } 37 | 38 | func decodeJSON(r io.Reader, obj interface{}) error { 39 | decoder := json.NewDecoder(r) 40 | if EnableDecoderUseNumber { 41 | decoder.UseNumber() 42 | } 43 | if err := decoder.Decode(obj); err != nil { 44 | return err 45 | } 46 | return validate(obj) 47 | } 48 | -------------------------------------------------------------------------------- /framework/gin/binding/msgpack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "net/http" 11 | 12 | "github.com/ugorji/go/codec" 13 | ) 14 | 15 | type msgpackBinding struct{} 16 | 17 | func (msgpackBinding) Name() string { 18 | return "msgpack" 19 | } 20 | 21 | func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { 22 | return decodeMsgPack(req.Body, obj) 23 | } 24 | 25 | func (msgpackBinding) BindBody(body []byte, obj interface{}) error { 26 | return decodeMsgPack(bytes.NewReader(body), obj) 27 | } 28 | 29 | func decodeMsgPack(r io.Reader, obj interface{}) error { 30 | cdc := new(codec.MsgpackHandle) 31 | if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { 32 | return err 33 | } 34 | return validate(obj) 35 | } 36 | -------------------------------------------------------------------------------- /framework/gin/binding/protobuf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "io/ioutil" 9 | "net/http" 10 | 11 | "github.com/golang/protobuf/proto" 12 | ) 13 | 14 | type protobufBinding struct{} 15 | 16 | func (protobufBinding) Name() string { 17 | return "protobuf" 18 | } 19 | 20 | func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { 21 | buf, err := ioutil.ReadAll(req.Body) 22 | if err != nil { 23 | return err 24 | } 25 | return b.BindBody(buf, obj) 26 | } 27 | 28 | func (protobufBinding) BindBody(body []byte, obj interface{}) error { 29 | if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { 30 | return err 31 | } 32 | // Here it's same to return validate(obj), but util now we can't add 33 | // `binding:""` to the struct which automatically generate by gen-proto 34 | return nil 35 | // return validate(obj) 36 | } 37 | -------------------------------------------------------------------------------- /framework/gin/binding/query.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import "net/http" 8 | 9 | type queryBinding struct{} 10 | 11 | func (queryBinding) Name() string { 12 | return "query" 13 | } 14 | 15 | func (queryBinding) Bind(req *http.Request, obj interface{}) error { 16 | values := req.URL.Query() 17 | if err := mapForm(obj, values); err != nil { 18 | return err 19 | } 20 | return validate(obj) 21 | } 22 | -------------------------------------------------------------------------------- /framework/gin/binding/uri.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | type uriBinding struct{} 8 | 9 | func (uriBinding) Name() string { 10 | return "uri" 11 | } 12 | 13 | func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { 14 | if err := mapUri(obj, m); err != nil { 15 | return err 16 | } 17 | return validate(obj) 18 | } 19 | -------------------------------------------------------------------------------- /framework/gin/binding/xml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "encoding/xml" 10 | "io" 11 | "net/http" 12 | ) 13 | 14 | type xmlBinding struct{} 15 | 16 | func (xmlBinding) Name() string { 17 | return "xml" 18 | } 19 | 20 | func (xmlBinding) Bind(req *http.Request, obj interface{}) error { 21 | return decodeXML(req.Body, obj) 22 | } 23 | 24 | func (xmlBinding) BindBody(body []byte, obj interface{}) error { 25 | return decodeXML(bytes.NewReader(body), obj) 26 | } 27 | func decodeXML(r io.Reader, obj interface{}) error { 28 | decoder := xml.NewDecoder(r) 29 | if err := decoder.Decode(obj); err != nil { 30 | return err 31 | } 32 | return validate(obj) 33 | } 34 | -------------------------------------------------------------------------------- /framework/gin/binding/yaml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "net/http" 11 | 12 | "gopkg.in/yaml.v2" 13 | ) 14 | 15 | type yamlBinding struct{} 16 | 17 | func (yamlBinding) Name() string { 18 | return "yaml" 19 | } 20 | 21 | func (yamlBinding) Bind(req *http.Request, obj interface{}) error { 22 | return decodeYAML(req.Body, obj) 23 | } 24 | 25 | func (yamlBinding) BindBody(body []byte, obj interface{}) error { 26 | return decodeYAML(bytes.NewReader(body), obj) 27 | } 28 | 29 | func decodeYAML(r io.Reader, obj interface{}) error { 30 | decoder := yaml.NewDecoder(r) 31 | if err := decoder.Decode(obj); err != nil { 32 | return err 33 | } 34 | return validate(obj) 35 | } 36 | -------------------------------------------------------------------------------- /framework/gin/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | notify: 3 | gitter: 4 | default: 5 | url: https://webhooks.gitter.im/e/d90dcdeeab2f1e357165 6 | -------------------------------------------------------------------------------- /framework/gin/context_appengine.go: -------------------------------------------------------------------------------- 1 | // +build appengine 2 | 3 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 4 | // Use of this source code is governed by a MIT style 5 | // license that can be found in the LICENSE file. 6 | 7 | package gin 8 | 9 | func init() { 10 | defaultAppEngine = true 11 | } 12 | -------------------------------------------------------------------------------- /framework/gin/deprecated.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | import ( 8 | "log" 9 | 10 | "hade/framework/gin/binding" 11 | ) 12 | 13 | // BindWith binds the passed struct pointer using the specified binding engine. 14 | // See the binding package. 15 | func (c *Context) BindWith(obj interface{}, b binding.Binding) error { 16 | log.Println(`BindWith(\"interface{}, binding.Binding\") error is going to 17 | be deprecated, please check issue #662 and either use MustBindWith() if you 18 | want HTTP 400 to be automatically returned if any error occur, or use 19 | ShouldBindWith() if you need to manage the error.`) 20 | return c.MustBindWith(obj, b) 21 | } 22 | -------------------------------------------------------------------------------- /framework/gin/deprecated_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | import ( 8 | "bytes" 9 | "net/http" 10 | "net/http/httptest" 11 | "testing" 12 | 13 | "hade/framework/gin/binding" 14 | 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func TestBindWith(t *testing.T) { 19 | w := httptest.NewRecorder() 20 | c, _ := CreateTestContext(w) 21 | 22 | c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) 23 | 24 | var obj struct { 25 | Foo string `form:"foo"` 26 | Bar string `form:"bar"` 27 | } 28 | captureOutput(t, func() { 29 | assert.NoError(t, c.BindWith(&obj, binding.Form)) 30 | }) 31 | assert.Equal(t, "foo", obj.Bar) 32 | assert.Equal(t, "bar", obj.Foo) 33 | assert.Equal(t, 0, w.Body.Len()) 34 | } 35 | -------------------------------------------------------------------------------- /framework/gin/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package gin implements a HTTP web framework called gin. 3 | 4 | See https://gin-gonic.com/ for more information about gin. 5 | */ 6 | package gin // import "github.com/gin-gonic/gin" 7 | -------------------------------------------------------------------------------- /framework/gin/examples/README.md: -------------------------------------------------------------------------------- 1 | # Gin Examples 2 | 3 | ⚠️ **NOTICE:** All gin examples have been moved as standalone repository to [here](https://github.com/gin-gonic/examples). 4 | -------------------------------------------------------------------------------- /framework/gin/fs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | import ( 8 | "net/http" 9 | "os" 10 | ) 11 | 12 | type onlyfilesFS struct { 13 | fs http.FileSystem 14 | } 15 | 16 | type neuteredReaddirFile struct { 17 | http.File 18 | } 19 | 20 | // Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally 21 | // in router.Static(). 22 | // if listDirectory == true, then it works the same as http.Dir() otherwise it returns 23 | // a filesystem that prevents http.FileServer() to list the directory files. 24 | func Dir(root string, listDirectory bool) http.FileSystem { 25 | fs := http.Dir(root) 26 | if listDirectory { 27 | return fs 28 | } 29 | return &onlyfilesFS{fs} 30 | } 31 | 32 | // Open conforms to http.Filesystem. 33 | func (fs onlyfilesFS) Open(name string) (http.File, error) { 34 | f, err := fs.fs.Open(name) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return neuteredReaddirFile{f}, nil 39 | } 40 | 41 | // Readdir overrides the http.File default implementation. 42 | func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { 43 | // this disables directory listing 44 | return nil, nil 45 | } 46 | -------------------------------------------------------------------------------- /framework/gin/ginS/README.md: -------------------------------------------------------------------------------- 1 | # Gin Default Server 2 | 3 | This is API experiment for Gin. 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "github.com/gin-gonic/gin" 10 | "hade/framework/gin/ginS" 11 | ) 12 | 13 | func main() { 14 | ginS.GET("/", func(c *gin.Context) { c.String(200, "Hello World") }) 15 | ginS.Run() 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /framework/gin/hade.go: -------------------------------------------------------------------------------- 1 | package gin 2 | 3 | import "hade/framework" 4 | 5 | // Hade framework add functions 6 | 7 | func (engine *Engine) SetContainer(container *framework.HadeContainer) { 8 | engine.container = container 9 | } 10 | 11 | func (engine *Engine) Container() *framework.HadeContainer { 12 | return engine.container 13 | } 14 | -------------------------------------------------------------------------------- /framework/gin/internal/json/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !jsoniter 6 | 7 | package json 8 | 9 | import "encoding/json" 10 | 11 | var ( 12 | // Marshal is exported by gin/json package. 13 | Marshal = json.Marshal 14 | // Unmarshal is exported by gin/json package. 15 | Unmarshal = json.Unmarshal 16 | // MarshalIndent is exported by gin/json package. 17 | MarshalIndent = json.MarshalIndent 18 | // NewDecoder is exported by gin/json package. 19 | NewDecoder = json.NewDecoder 20 | // NewEncoder is exported by gin/json package. 21 | NewEncoder = json.NewEncoder 22 | ) 23 | -------------------------------------------------------------------------------- /framework/gin/internal/json/jsoniter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build jsoniter 6 | 7 | package json 8 | 9 | import "github.com/json-iterator/go" 10 | 11 | var ( 12 | json = jsoniter.ConfigCompatibleWithStandardLibrary 13 | // Marshal is exported by gin/json package. 14 | Marshal = json.Marshal 15 | // Unmarshal is exported by gin/json package. 16 | Unmarshal = json.Unmarshal 17 | // MarshalIndent is exported by gin/json package. 18 | MarshalIndent = json.MarshalIndent 19 | // NewDecoder is exported by gin/json package. 20 | NewDecoder = json.NewDecoder 21 | // NewEncoder is exported by gin/json package. 22 | NewEncoder = json.NewEncoder 23 | ) 24 | -------------------------------------------------------------------------------- /framework/gin/mode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | import ( 8 | "os" 9 | "testing" 10 | 11 | "hade/framework/gin/binding" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func init() { 17 | os.Setenv(EnvGinMode, TestMode) 18 | } 19 | 20 | func TestSetMode(t *testing.T) { 21 | assert.Equal(t, testCode, ginMode) 22 | assert.Equal(t, TestMode, Mode()) 23 | os.Unsetenv(EnvGinMode) 24 | 25 | SetMode("") 26 | assert.Equal(t, debugCode, ginMode) 27 | assert.Equal(t, DebugMode, Mode()) 28 | 29 | SetMode(DebugMode) 30 | assert.Equal(t, debugCode, ginMode) 31 | assert.Equal(t, DebugMode, Mode()) 32 | 33 | SetMode(ReleaseMode) 34 | assert.Equal(t, releaseCode, ginMode) 35 | assert.Equal(t, ReleaseMode, Mode()) 36 | 37 | SetMode(TestMode) 38 | assert.Equal(t, testCode, ginMode) 39 | assert.Equal(t, TestMode, Mode()) 40 | 41 | assert.Panics(t, func() { SetMode("unknown") }) 42 | } 43 | 44 | func TestEnableJsonDecoderUseNumber(t *testing.T) { 45 | assert.False(t, binding.EnableDecoderUseNumber) 46 | EnableJsonDecoderUseNumber() 47 | assert.True(t, binding.EnableDecoderUseNumber) 48 | } 49 | -------------------------------------------------------------------------------- /framework/gin/render/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import "net/http" 8 | 9 | // Data contains ContentType and bytes data. 10 | type Data struct { 11 | ContentType string 12 | Data []byte 13 | } 14 | 15 | // Render (Data) writes data with custom ContentType. 16 | func (r Data) Render(w http.ResponseWriter) (err error) { 17 | r.WriteContentType(w) 18 | _, err = w.Write(r.Data) 19 | return 20 | } 21 | 22 | // WriteContentType (Data) writes custom ContentType. 23 | func (r Data) WriteContentType(w http.ResponseWriter) { 24 | writeContentType(w, []string{r.ContentType}) 25 | } 26 | -------------------------------------------------------------------------------- /framework/gin/render/msgpack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/ugorji/go/codec" 11 | ) 12 | 13 | // MsgPack contains the given interface object. 14 | type MsgPack struct { 15 | Data interface{} 16 | } 17 | 18 | var msgpackContentType = []string{"application/msgpack; charset=utf-8"} 19 | 20 | // WriteContentType (MsgPack) writes MsgPack ContentType. 21 | func (r MsgPack) WriteContentType(w http.ResponseWriter) { 22 | writeContentType(w, msgpackContentType) 23 | } 24 | 25 | // Render (MsgPack) encodes the given interface object and writes data with custom ContentType. 26 | func (r MsgPack) Render(w http.ResponseWriter) error { 27 | return WriteMsgPack(w, r.Data) 28 | } 29 | 30 | // WriteMsgPack writes MsgPack ContentType and encodes the given interface object. 31 | func WriteMsgPack(w http.ResponseWriter, obj interface{}) error { 32 | writeContentType(w, msgpackContentType) 33 | var mh codec.MsgpackHandle 34 | return codec.NewEncoder(w, &mh).Encode(obj) 35 | } 36 | -------------------------------------------------------------------------------- /framework/gin/render/protobuf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | // ProtoBuf contains the given interface object. 14 | type ProtoBuf struct { 15 | Data interface{} 16 | } 17 | 18 | var protobufContentType = []string{"application/x-protobuf"} 19 | 20 | // Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType. 21 | func (r ProtoBuf) Render(w http.ResponseWriter) error { 22 | r.WriteContentType(w) 23 | 24 | bytes, err := proto.Marshal(r.Data.(proto.Message)) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | _, err = w.Write(bytes) 30 | return err 31 | } 32 | 33 | // WriteContentType (ProtoBuf) writes ProtoBuf ContentType. 34 | func (r ProtoBuf) WriteContentType(w http.ResponseWriter) { 35 | writeContentType(w, protobufContentType) 36 | } 37 | -------------------------------------------------------------------------------- /framework/gin/render/reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "io" 9 | "net/http" 10 | "strconv" 11 | ) 12 | 13 | // Reader contains the IO reader and its length, and custom ContentType and other headers. 14 | type Reader struct { 15 | ContentType string 16 | ContentLength int64 17 | Reader io.Reader 18 | Headers map[string]string 19 | } 20 | 21 | // Render (Reader) writes data with custom ContentType and headers. 22 | func (r Reader) Render(w http.ResponseWriter) (err error) { 23 | r.WriteContentType(w) 24 | r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) 25 | r.writeHeaders(w, r.Headers) 26 | _, err = io.Copy(w, r.Reader) 27 | return 28 | } 29 | 30 | // WriteContentType (Reader) writes custom ContentType. 31 | func (r Reader) WriteContentType(w http.ResponseWriter) { 32 | writeContentType(w, []string{r.ContentType}) 33 | } 34 | 35 | // writeHeaders writes custom Header. 36 | func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { 37 | header := w.Header() 38 | for k, v := range headers { 39 | if header.Get(k) == "" { 40 | header.Set(k, v) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /framework/gin/render/redirect.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | ) 11 | 12 | // Redirect contains the http request reference and redirects status code and location. 13 | type Redirect struct { 14 | Code int 15 | Request *http.Request 16 | Location string 17 | } 18 | 19 | // Render (Redirect) redirects the http request to new location and writes redirect response. 20 | func (r Redirect) Render(w http.ResponseWriter) error { 21 | if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated { 22 | panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) 23 | } 24 | http.Redirect(w, r.Request, r.Location, r.Code) 25 | return nil 26 | } 27 | 28 | // WriteContentType (Redirect) don't write any ContentType. 29 | func (r Redirect) WriteContentType(http.ResponseWriter) {} 30 | -------------------------------------------------------------------------------- /framework/gin/render/render.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import "net/http" 8 | 9 | // Render interface is to be implemented by JSON, XML, HTML, YAML and so on. 10 | type Render interface { 11 | // Render writes data with custom ContentType. 12 | Render(http.ResponseWriter) error 13 | // WriteContentType writes custom ContentType. 14 | WriteContentType(w http.ResponseWriter) 15 | } 16 | 17 | var ( 18 | _ Render = JSON{} 19 | _ Render = IndentedJSON{} 20 | _ Render = SecureJSON{} 21 | _ Render = JsonpJSON{} 22 | _ Render = XML{} 23 | _ Render = String{} 24 | _ Render = Redirect{} 25 | _ Render = Data{} 26 | _ Render = HTML{} 27 | _ HTMLRender = HTMLDebug{} 28 | _ HTMLRender = HTMLProduction{} 29 | _ Render = YAML{} 30 | _ Render = MsgPack{} 31 | _ Render = Reader{} 32 | _ Render = AsciiJSON{} 33 | _ Render = ProtoBuf{} 34 | ) 35 | 36 | func writeContentType(w http.ResponseWriter, value []string) { 37 | header := w.Header() 38 | if val := header["Content-Type"]; len(val) == 0 { 39 | header["Content-Type"] = value 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /framework/gin/render/text.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "net/http" 11 | ) 12 | 13 | // String contains the given interface object slice and its format. 14 | type String struct { 15 | Format string 16 | Data []interface{} 17 | } 18 | 19 | var plainContentType = []string{"text/plain; charset=utf-8"} 20 | 21 | // Render (String) writes data with custom ContentType. 22 | func (r String) Render(w http.ResponseWriter) error { 23 | return WriteString(w, r.Format, r.Data) 24 | } 25 | 26 | // WriteContentType (String) writes Plain ContentType. 27 | func (r String) WriteContentType(w http.ResponseWriter) { 28 | writeContentType(w, plainContentType) 29 | } 30 | 31 | // WriteString writes data according to its format and write custom ContentType. 32 | func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) { 33 | writeContentType(w, plainContentType) 34 | if len(data) > 0 { 35 | _, err = fmt.Fprintf(w, format, data...) 36 | return 37 | } 38 | _, err = io.WriteString(w, format) 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /framework/gin/render/xml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "encoding/xml" 9 | "net/http" 10 | ) 11 | 12 | // XML contains the given interface object. 13 | type XML struct { 14 | Data interface{} 15 | } 16 | 17 | var xmlContentType = []string{"application/xml; charset=utf-8"} 18 | 19 | // Render (XML) encodes the given interface object and writes data with custom ContentType. 20 | func (r XML) Render(w http.ResponseWriter) error { 21 | r.WriteContentType(w) 22 | return xml.NewEncoder(w).Encode(r.Data) 23 | } 24 | 25 | // WriteContentType (XML) writes XML ContentType for response. 26 | func (r XML) WriteContentType(w http.ResponseWriter) { 27 | writeContentType(w, xmlContentType) 28 | } 29 | -------------------------------------------------------------------------------- /framework/gin/render/yaml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // YAML contains the given interface object. 14 | type YAML struct { 15 | Data interface{} 16 | } 17 | 18 | var yamlContentType = []string{"application/x-yaml; charset=utf-8"} 19 | 20 | // Render (YAML) marshals the given interface object and writes data with custom ContentType. 21 | func (r YAML) Render(w http.ResponseWriter) error { 22 | r.WriteContentType(w) 23 | 24 | bytes, err := yaml.Marshal(r.Data) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | _, err = w.Write(bytes) 30 | return err 31 | } 32 | 33 | // WriteContentType (YAML) writes YAML ContentType for response. 34 | func (r YAML) WriteContentType(w http.ResponseWriter) { 35 | writeContentType(w, yamlContentType) 36 | } 37 | -------------------------------------------------------------------------------- /framework/gin/test: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /framework/gin/test_helpers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | import "net/http" 8 | 9 | // CreateTestContext returns a fresh engine and context for testing purposes 10 | func CreateTestContext(w http.ResponseWriter) (c *Context, r *Engine) { 11 | r = New() 12 | c = r.allocateContext() 13 | c.reset() 14 | c.writermem.reset(w) 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /framework/gin/testdata/certificate/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC9DCCAdygAwIBAgIQUNSK+OxWHYYFxHVJV0IlpDANBgkqhkiG9w0BAQsFADAS 3 | MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MTExNjEyMDA0N1oXDTE4MTExNjEyMDA0 4 | N1owEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 5 | AQoCggEBAKmyj/YZpD59Bpy4w3qf6VzMw9uUBsWp+IP4kl7z5cmGHYUHn/YopTLH 6 | vR23GAB12p6Km5QWzCBuJF4j61PJXHfg3/rjShZ77JcQ3kzxuy1iKDI+DNKN7Klz 7 | rdjJ49QD0lczZHeBvvCL7JsJFKFjGy62rnStuW8LaIEdtjXT+GUZTxJh6G7yPYfD 8 | MS1IsdMQGOdbGwNa+qogMuQPh0TzHw+C73myKrjY6pREijknMC/rnIMz9dLPt6Kl 9 | xXy4br443dpY6dYGIhDuKhROT+vZ05HKasuuQUFhY7v/KoUpEZMB9rfUSzjQ5fak 10 | eDUAMniXRcd+DmwvboG2TI6ixmuPK+ECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgWg 11 | MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDwYDVR0RBAgwBocE 12 | fwAAATANBgkqhkiG9w0BAQsFAAOCAQEAMXOLvj7BFsxdbcfRPBd0OFrH/8lI7vPV 13 | LRcJ6r5iv0cnNvZXXbIOQLbg4clJAWjoE08nRm1KvNXhKdns0ELEV86YN2S6jThq 14 | rIGrBqKtaJLB3M9BtDSiQ6SGPLYrWvmhj3Avi8PbSGy51bpGbqopd16j6LYU7Cp2 15 | TefMRlOAFtHojpCVon1CMpqcNxS0WNlQ3lUBSrw3HB0o12x++roja2ibF54tSHXB 16 | KUuadoEzN+mMBwenEBychmAGzdiG4GQHRmhigh85+mtW6UMGiqyCZHs0EgE9FCLL 17 | sRrsTI/VOzLz6lluygXkOsXrP+PP0SvmE3eylWjj9e2nj/u/Cy2YKg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /framework/gin/testdata/protoexample/test.proto: -------------------------------------------------------------------------------- 1 | package protoexample; 2 | 3 | enum FOO {X=17;}; 4 | 5 | message Test { 6 | required string label = 1; 7 | optional int32 type = 2[default=77]; 8 | repeated int64 reps = 3; 9 | optional group OptionalGroup = 4{ 10 | required string RequiredField = 5; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /framework/gin/testdata/template/hello.tmpl: -------------------------------------------------------------------------------- 1 |

Hello {[{.name}]}

-------------------------------------------------------------------------------- /framework/gin/testdata/template/raw.tmpl: -------------------------------------------------------------------------------- 1 | Date: {[{.now | formatAsDate}]} 2 | -------------------------------------------------------------------------------- /framework/gin/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gin 6 | 7 | // Version is the current gin framework's version. 8 | const Version = "v1.4.0" 9 | -------------------------------------------------------------------------------- /framework/middleware/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/framework/middleware/.gitkeeper -------------------------------------------------------------------------------- /framework/provider.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | // NewInstance define a instantiate function for making a new service 4 | type NewInstance func(...interface{}) (interface{}, error) 5 | 6 | // ServiceProvider define a service provider should implement 7 | type ServiceProvider interface { 8 | // Register registe a new function for make a service instance 9 | Register(Container) NewInstance 10 | // Boot will called when the service instantiate 11 | Boot(Container) error 12 | // IsDefer define whether the service instantiate when first make or register 13 | IsDefer() bool 14 | // Params define the necessary params for NewInstance 15 | Params() []interface{} 16 | /// Name define the name for this service 17 | Name() string 18 | } 19 | -------------------------------------------------------------------------------- /framework/provider/app/provider.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | // HadeAppProvider provide a App service, it must be singlton, and not delay 9 | type HadeAppProvider struct { 10 | app *HadeApp 11 | 12 | BasePath string 13 | } 14 | 15 | // Register registe a new function for make a service instance 16 | func (provider *HadeAppProvider) Register(c framework.Container) framework.NewInstance { 17 | return NewHadeApp 18 | } 19 | 20 | // Boot will called when the service instantiate 21 | func (provider *HadeAppProvider) Boot(c framework.Container) error { 22 | return nil 23 | } 24 | 25 | // IsDefer define whether the service instantiate when first make or register 26 | func (provider *HadeAppProvider) IsDefer() bool { 27 | return false 28 | } 29 | 30 | // Params define the necessary params for NewInstance 31 | func (provider *HadeAppProvider) Params() []interface{} { 32 | return []interface{}{provider.BasePath} 33 | } 34 | 35 | /// Name define the name for this service 36 | func (provider *HadeAppProvider) Name() string { 37 | return contract.AppKey 38 | } 39 | -------------------------------------------------------------------------------- /framework/provider/app/provider_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "testing" 5 | 6 | "hade/framework" 7 | "hade/framework/contract" 8 | "hade/tests" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestHadeAppProvider(t *testing.T) { 14 | Convey("test normal case", t, func() { 15 | basePath := tests.BasePath 16 | c := framework.NewHadeContainer() 17 | sp := &HadeAppProvider{BasePath: basePath} 18 | 19 | err := c.Singleton(sp) 20 | So(err, ShouldBeNil) 21 | 22 | app, err := c.Make(contract.AppKey) 23 | So(err, ShouldBeNil) 24 | var iapp *contract.App 25 | So(app, ShouldImplement, iapp) 26 | hade := app.(contract.App) 27 | logPath := hade.LogPath() 28 | So(logPath, ShouldEqual, basePath+"storage/log/") 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /framework/provider/config/fake_provider.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type FakeConfigProvider struct { 9 | FileName string 10 | Content []byte 11 | } 12 | 13 | // Register registe a new function for make a service instance 14 | func (provider *FakeConfigProvider) Register(c framework.Container) framework.NewInstance { 15 | return NewFakeConfig 16 | } 17 | 18 | // Boot will called when the service instantiate 19 | func (provider *FakeConfigProvider) Boot(c framework.Container) error { 20 | return nil 21 | } 22 | 23 | // IsDefer define whether the service instantiate when first make or register 24 | func (provider *FakeConfigProvider) IsDefer() bool { 25 | return false 26 | } 27 | 28 | // Params define the necessary params for NewInstance 29 | func (provider *FakeConfigProvider) Params() []interface{} { 30 | return []interface{}{provider.FileName, provider.Content} 31 | } 32 | 33 | // Name define the name for this service 34 | func (provider *FakeConfigProvider) Name() string { 35 | return contract.ConfigKey 36 | } 37 | -------------------------------------------------------------------------------- /framework/provider/config/provider.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type HadeConfigProvider struct { 9 | c framework.Container 10 | folder string 11 | env string 12 | 13 | envMaps map[string]string 14 | } 15 | 16 | // Register registe a new function for make a service instance 17 | func (provider *HadeConfigProvider) Register(c framework.Container) framework.NewInstance { 18 | return NewHadeConfig 19 | } 20 | 21 | // Boot will called when the service instantiate 22 | func (provider *HadeConfigProvider) Boot(c framework.Container) error { 23 | provider.folder = c.MustMake(contract.AppKey).(contract.App).ConfigPath() 24 | provider.envMaps = c.MustMake(contract.EnvKey).(contract.Env).All() 25 | provider.env = c.MustMake(contract.EnvKey).(contract.Env).AppEnv() 26 | provider.c = c 27 | return nil 28 | } 29 | 30 | // IsDefer define whether the service instantiate when first make or register 31 | func (provider *HadeConfigProvider) IsDefer() bool { 32 | return true 33 | } 34 | 35 | // Params define the necessary params for NewInstance 36 | func (provider *HadeConfigProvider) Params() []interface{} { 37 | return []interface{}{provider.folder, provider.envMaps, provider.env, provider.c} 38 | } 39 | 40 | /// Name define the name for this service 41 | func (provider *HadeConfigProvider) Name() string { 42 | return contract.ConfigKey 43 | } 44 | -------------------------------------------------------------------------------- /framework/provider/config/service_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "hade/framework/contract" 8 | "hade/tests" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestHadeConfig_GetInt(t *testing.T) { 14 | Convey("test hade env normal case", t, func() { 15 | basePath := tests.BasePath 16 | folder := filepath.Join(basePath, "config") 17 | serv, err := NewHadeConfig(folder, map[string]string{}, contract.EnvDevelopment) 18 | So(err, ShouldBeNil) 19 | conf := serv.(*HadeConfig) 20 | timeout := conf.GetInt("database.mysql.timeout") 21 | So(timeout, ShouldEqual, 1) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /framework/provider/demo/provider.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "fmt" 5 | 6 | "hade/framework" 7 | ) 8 | 9 | type DemoServiceProvider struct { 10 | C map[string]string 11 | framework.ServiceProvider 12 | } 13 | 14 | func (sp *DemoServiceProvider) Name() string { 15 | return "demo" 16 | } 17 | 18 | func (sp *DemoServiceProvider) Register(c framework.Container) framework.NewInstance { 19 | return NewDemoService 20 | } 21 | 22 | func (sp *DemoServiceProvider) IsDefer() bool { 23 | return true 24 | } 25 | 26 | func (sp *DemoServiceProvider) Params() []interface{} { 27 | return []interface{}{sp.C} 28 | } 29 | 30 | func (sp *DemoServiceProvider) Boot(c framework.Container) error { 31 | fmt.Println("demo service boot") 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /framework/provider/demo/service.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type DemoService struct { 9 | c map[string]string 10 | } 11 | 12 | func NewDemoService(params ...interface{}) (interface{}, error) { 13 | c := params[0].(map[string]string) 14 | fmt.Println("new demo service") 15 | return &DemoService{c: c}, errors.New("new demo service error") 16 | } 17 | 18 | func (s *DemoService) Get(key string) string { 19 | if v, ok := s.c[key]; ok { 20 | return v 21 | } 22 | return "" 23 | } 24 | -------------------------------------------------------------------------------- /framework/provider/env/provider.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type HadeEnvProvider struct { 9 | Folder string 10 | } 11 | 12 | // Register registe a new function for make a service instance 13 | func (provider *HadeEnvProvider) Register(c framework.Container) framework.NewInstance { 14 | return NewHadeEnv 15 | } 16 | 17 | // Boot will called when the service instantiate 18 | func (provider *HadeEnvProvider) Boot(c framework.Container) error { 19 | app := c.MustMake(contract.AppKey).(contract.App) 20 | provider.Folder = app.EnvironmentPath() 21 | return nil 22 | } 23 | 24 | // IsDefer define whether the service instantiate when first make or register 25 | func (provider *HadeEnvProvider) IsDefer() bool { 26 | return false 27 | } 28 | 29 | // Params define the necessary params for NewInstance 30 | func (provider *HadeEnvProvider) Params() []interface{} { 31 | return []interface{}{provider.Folder} 32 | } 33 | 34 | /// Name define the name for this service 35 | func (provider *HadeEnvProvider) Name() string { 36 | return contract.EnvKey 37 | } 38 | -------------------------------------------------------------------------------- /framework/provider/env/provider_test.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "testing" 5 | 6 | "hade/framework" 7 | "hade/framework/contract" 8 | "hade/framework/provider/app" 9 | "hade/tests" 10 | 11 | . "github.com/smartystreets/goconvey/convey" 12 | ) 13 | 14 | func TestHadeEnvProvider(t *testing.T) { 15 | Convey("test hade env normal case", t, func() { 16 | basePath := tests.BasePath 17 | c := framework.NewHadeContainer() 18 | sp := &app.HadeAppProvider{BasePath: basePath} 19 | 20 | err := c.Singleton(sp) 21 | So(err, ShouldBeNil) 22 | 23 | sp2 := &HadeEnvProvider{} 24 | err = c.Singleton(sp2) 25 | So(err, ShouldBeNil) 26 | 27 | envServ := c.MustMake(contract.EnvKey).(contract.Env) 28 | So(envServ.AppEnv(), ShouldEqual, "development") 29 | // So(envServ.Get("DB_HOST"), ShouldEqual, "127.0.0.1") 30 | // So(envServ.AppDebug(), ShouldBeTrue) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /framework/provider/gorm/provider.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type GormServiceProvider struct { 9 | Config map[string]string 10 | framework.ServiceProvider 11 | } 12 | 13 | func (sp *GormServiceProvider) Name() string { 14 | return "gorm" 15 | } 16 | 17 | func (sp *GormServiceProvider) Register(c framework.Container) framework.NewInstance { 18 | return NewGormDB 19 | } 20 | 21 | func (sp *GormServiceProvider) IsDefer() bool { 22 | return true 23 | } 24 | 25 | func (sp *GormServiceProvider) Params() []interface{} { 26 | return []interface{}{sp.Config} 27 | } 28 | 29 | func (sp *GormServiceProvider) Boot(c framework.Container) error { 30 | if sp.Config == nil { 31 | if c.IsBind(contract.ConfigKey) { 32 | config := c.MustMake(contract.ConfigKey).(contract.Config) 33 | if config.IsExist("database.default") { 34 | sp.Config = config.GetStringMapString("database.default") 35 | } 36 | } 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /framework/provider/gorm/provider_test.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | -------------------------------------------------------------------------------- /framework/provider/gorm/service.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/jinzhu/gorm" 7 | _ "github.com/jinzhu/gorm/dialects/mysql" 8 | 9 | _ "github.com/jinzhu/gorm/dialects/mssql" 10 | _ "github.com/jinzhu/gorm/dialects/postgres" 11 | _ "github.com/jinzhu/gorm/dialects/sqlite" 12 | "github.com/pkg/errors" 13 | ) 14 | 15 | func NewGormDB(params ...interface{}) (interface{}, error) { 16 | c := params[0].(map[string]string) 17 | if c == nil { 18 | return nil, errors.New("config is empty") 19 | } 20 | s := fmt.Sprintf("%s:%s@(%s)/%s?charset=%s&parseTime=True&loc=Local", c["username"], c["password"], c["hostname"], c["database"], c["charset"]) 21 | db, err := gorm.Open(c["driver"], s) 22 | if err != nil { 23 | return nil, errors.Wrap(err, "new gorm error") 24 | } 25 | return db, nil 26 | } 27 | -------------------------------------------------------------------------------- /framework/provider/id/provider.go: -------------------------------------------------------------------------------- 1 | package id 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type HadeIDProvider struct { 9 | } 10 | 11 | // Register registe a new function for make a service instance 12 | func (provider *HadeIDProvider) Register(c framework.Container) framework.NewInstance { 13 | return NewHadeIDService 14 | } 15 | 16 | // Boot will called when the service instantiate 17 | func (provider *HadeIDProvider) Boot(c framework.Container) error { 18 | return nil 19 | } 20 | 21 | // IsDefer define whether the service instantiate when first make or register 22 | func (provider *HadeIDProvider) IsDefer() bool { 23 | return false 24 | } 25 | 26 | // Params define the necessary params for NewInstance 27 | func (provider *HadeIDProvider) Params() []interface{} { 28 | return []interface{}{} 29 | } 30 | 31 | /// Name define the name for this service 32 | func (provider *HadeIDProvider) Name() string { 33 | return contract.IDKey 34 | } 35 | -------------------------------------------------------------------------------- /framework/provider/id/provier_test.go: -------------------------------------------------------------------------------- 1 | package id 2 | 3 | import ( 4 | "testing" 5 | 6 | "hade/framework" 7 | "hade/framework/contract" 8 | "hade/framework/provider/app" 9 | "hade/framework/provider/config" 10 | "hade/framework/provider/env" 11 | "hade/framework/util" 12 | 13 | . "github.com/smartystreets/goconvey/convey" 14 | ) 15 | 16 | func TestConsoleLog_Normal(t *testing.T) { 17 | Convey("test hade console log normal case", t, func() { 18 | basePath := util.GetExecDirectory() 19 | c := framework.NewHadeContainer() 20 | c.Singleton(&app.HadeAppProvider{BasePath: basePath}) 21 | c.Singleton(&env.HadeEnvProvider{}) 22 | c.Singleton(&config.HadeConfigProvider{}) 23 | 24 | err := c.Singleton(&HadeIDProvider{}) 25 | So(err, ShouldBeNil) 26 | 27 | idService := c.MustMake(contract.IDKey).(contract.IDService) 28 | xid := idService.NewID() 29 | t.Log(xid) 30 | So(xid, ShouldNotBeEmpty) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /framework/provider/id/service.go: -------------------------------------------------------------------------------- 1 | package id 2 | 3 | import ( 4 | "github.com/rs/xid" 5 | ) 6 | 7 | type HadeIDService struct { 8 | } 9 | 10 | func NewHadeIDService(params ...interface{}) (interface{}, error) { 11 | return &HadeIDService{}, nil 12 | } 13 | 14 | func (s *HadeIDService) NewID() string { 15 | return xid.New().String() 16 | } 17 | -------------------------------------------------------------------------------- /framework/provider/kernel/provider.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | "hade/framework/gin" 7 | ) 8 | 9 | // HadeAppProvider provide a App service, it must be singlton, and not delay 10 | type HadeKernelProvider struct { 11 | HttpEngine *gin.Engine 12 | } 13 | 14 | // Register registe a new function for make a service instance 15 | func (provider *HadeKernelProvider) Register(c framework.Container) framework.NewInstance { 16 | return NewHadeKernelService 17 | } 18 | 19 | // Boot will called when the service instantiate 20 | func (provider *HadeKernelProvider) Boot(c framework.Container) error { 21 | if provider.HttpEngine == nil { 22 | provider.HttpEngine = gin.Default() 23 | } 24 | provider.HttpEngine.SetContainer(c.(*framework.HadeContainer)) 25 | return nil 26 | } 27 | 28 | // IsDefer define whether the service instantiate when first make or register 29 | func (provider *HadeKernelProvider) IsDefer() bool { 30 | return false 31 | } 32 | 33 | // Params define the necessary params for NewInstance 34 | func (provider *HadeKernelProvider) Params() []interface{} { 35 | return []interface{}{provider.HttpEngine} 36 | } 37 | 38 | /// Name define the name for this service 39 | func (provider *HadeKernelProvider) Name() string { 40 | return contract.KernelKey 41 | } 42 | -------------------------------------------------------------------------------- /framework/provider/kernel/service.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "hade/framework/gin" 5 | ) 6 | 7 | type HadeKernelService struct { 8 | engine *gin.Engine 9 | } 10 | 11 | func NewHadeKernelService(params ...interface{}) (interface{}, error) { 12 | httpEngine := params[0].(*gin.Engine) 13 | return &HadeKernelService{engine: httpEngine}, nil 14 | } 15 | 16 | func (s *HadeKernelService) HttpEngine() *gin.Engine { 17 | return s.engine 18 | } 19 | -------------------------------------------------------------------------------- /framework/provider/log/formatter/json.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "time" 7 | 8 | "hade/framework/contract" 9 | 10 | "github.com/pkg/errors" 11 | ) 12 | 13 | func JsonFormatter(level contract.LogLevel, t time.Time, msg string, fields map[string]interface{}) ([]byte, error) { 14 | bf := bytes.NewBuffer([]byte(msg)) 15 | bf.Write([]byte{':'}) 16 | c, err := json.Marshal(fields) 17 | if err != nil { 18 | return bf.Bytes(), errors.Wrap(err, "json format error") 19 | } 20 | 21 | bf.Write(c) 22 | return bf.Bytes(), nil 23 | } 24 | -------------------------------------------------------------------------------- /framework/provider/log/formatter/prefix.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import "hade/framework/contract" 4 | 5 | func Prefix(level contract.LogLevel) string { 6 | prefix := "" 7 | switch level { 8 | case contract.PanicLevel: 9 | prefix = "[Panic]" 10 | case contract.FatalLevel: 11 | prefix = "[Fatal]" 12 | case contract.ErrorLevel: 13 | prefix = "[Error]" 14 | case contract.WarnLevel: 15 | prefix = "[Warn]" 16 | case contract.InfoLevel: 17 | prefix = "[Info]" 18 | case contract.DebugLevel: 19 | prefix = "[Debug]" 20 | case contract.TraceLevel: 21 | prefix = "[Trace]" 22 | } 23 | return prefix 24 | } 25 | -------------------------------------------------------------------------------- /framework/provider/log/formatter/text.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "time" 7 | 8 | "hade/framework/contract" 9 | ) 10 | 11 | func TextFormatter(level contract.LogLevel, t time.Time, msg string, fields map[string]interface{}) ([]byte, error) { 12 | bf := bytes.NewBuffer([]byte(msg)) 13 | 14 | prefix := Prefix(level) 15 | 16 | bf.WriteString(prefix) 17 | bf.WriteByte(' ') 18 | 19 | ts := t.Format(time.RFC3339) 20 | bf.WriteString(ts) 21 | bf.WriteByte(' ') 22 | 23 | bf.WriteString(msg) 24 | bf.WriteByte(' ') 25 | 26 | bf.WriteString(fmt.Sprint(fields)) 27 | return bf.Bytes(), nil 28 | } 29 | -------------------------------------------------------------------------------- /framework/provider/log/services/console.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "os" 5 | 6 | "hade/framework" 7 | "hade/framework/contract" 8 | ) 9 | 10 | type HadeConsoleLog struct { 11 | HadeLog 12 | } 13 | 14 | func NewHadeConsoleLog(params ...interface{}) (interface{}, error) { 15 | level := params[0].(contract.LogLevel) 16 | ctxFielder := params[1].(contract.CtxFielder) 17 | formatter := params[2].(contract.Formatter) 18 | c := params[4].(framework.Container) 19 | 20 | log := &HadeConsoleLog{} 21 | 22 | log.SetLevel(level) 23 | log.SetCxtFielder(ctxFielder) 24 | log.SetFormatter(formatter) 25 | 26 | log.SetOutput(os.Stdout) 27 | log.c = c 28 | return log, nil 29 | } 30 | -------------------------------------------------------------------------------- /framework/provider/trace/provider.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "hade/framework" 5 | "hade/framework/contract" 6 | ) 7 | 8 | type HadeTraceProvider struct { 9 | c framework.Container 10 | } 11 | 12 | // Register registe a new function for make a service instance 13 | func (provider *HadeTraceProvider) Register(c framework.Container) framework.NewInstance { 14 | return NewHadeTraceService 15 | } 16 | 17 | // Boot will called when the service instantiate 18 | func (provider *HadeTraceProvider) Boot(c framework.Container) error { 19 | provider.c = c 20 | return nil 21 | } 22 | 23 | // IsDefer define whether the service instantiate when first make or register 24 | func (provider *HadeTraceProvider) IsDefer() bool { 25 | return false 26 | } 27 | 28 | // Params define the necessary params for NewInstance 29 | func (provider *HadeTraceProvider) Params() []interface{} { 30 | return []interface{}{provider.c} 31 | } 32 | 33 | /// Name define the name for this service 34 | func (provider *HadeTraceProvider) Name() string { 35 | return contract.TraceKey 36 | } 37 | -------------------------------------------------------------------------------- /framework/util/console.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 美观输出数组 8 | func PrettyPrint(arr [][]string) { 9 | if len(arr) == 0 { 10 | return 11 | } 12 | rows := len(arr) 13 | cols := len(arr[0]) 14 | 15 | lens := make([][]int, rows) 16 | for i := 0; i < rows; i++ { 17 | lens[i] = make([]int, cols) 18 | for j := 0; j < cols; j++ { 19 | lens[i][j] = len(arr[i][j]) 20 | } 21 | } 22 | 23 | colMaxs := make([]int, cols) 24 | for j := 0; j < cols; j++ { 25 | for i := 0; i < rows; i++ { 26 | if colMaxs[j] < lens[i][j] { 27 | colMaxs[j] = lens[i][j] 28 | } 29 | } 30 | } 31 | 32 | for i := 0; i < rows; i++ { 33 | for j := 0; j < cols; j++ { 34 | fmt.Print(arr[i][j]) 35 | padding := colMaxs[j] - lens[i][j] + 2 36 | for p := 0; p < padding; p++ { 37 | fmt.Print(" ") 38 | } 39 | } 40 | fmt.Print("\n") 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /framework/util/console_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "testing" 4 | 5 | func TestPrettyPrint(t *testing.T) { 6 | type args struct { 7 | arr [][]string 8 | } 9 | tests := []struct { 10 | name string 11 | args args 12 | }{ 13 | { 14 | name: "normal", 15 | args: args{ 16 | arr: [][]string{ 17 | {"te", "test", "sdf"}, 18 | {"te11232", "test123123", "1232123"}, 19 | }, 20 | }, 21 | }, 22 | } 23 | for _, tt := range tests { 24 | t.Run(tt.name, func(t *testing.T) { 25 | PrettyPrint(tt.args.arr) 26 | }) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /framework/util/exec.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // 获取当前执行程序目录 9 | func GetExecDirectory() string { 10 | file, err := os.Getwd() 11 | if err == nil { 12 | return file + "/" 13 | } 14 | return "" 15 | } 16 | 17 | // Will return true if the process with PID exists. 18 | func CheckProcessExist(pid int) bool { 19 | process, err := os.FindProcess(pid) 20 | if err != nil { 21 | return false 22 | } 23 | 24 | err = process.Signal(syscall.Signal(0)) 25 | if err != nil { 26 | return false 27 | } 28 | return true 29 | } 30 | -------------------------------------------------------------------------------- /framework/util/http.go: -------------------------------------------------------------------------------- 1 | package util 2 | -------------------------------------------------------------------------------- /frontend/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /frontend/api/article.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function fetchList(query) { 4 | return request({ 5 | url: '/vue-element-admin/article/list', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function fetchArticle(id) { 12 | return request({ 13 | url: '/vue-element-admin/article/detail', 14 | method: 'get', 15 | params: { id } 16 | }) 17 | } 18 | 19 | export function fetchPv(pv) { 20 | return request({ 21 | url: '/vue-element-admin/article/pv', 22 | method: 'get', 23 | params: { pv } 24 | }) 25 | } 26 | 27 | export function createArticle(data) { 28 | return request({ 29 | url: '/vue-element-admin/article/create', 30 | method: 'post', 31 | data 32 | }) 33 | } 34 | 35 | export function updateArticle(data) { 36 | return request({ 37 | url: '/vue-element-admin/article/update', 38 | method: 'post', 39 | data 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /frontend/api/qiniu.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getToken() { 4 | return request({ 5 | url: '/qiniu/upload/token', // 假地址 自行替换 6 | method: 'get' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /frontend/api/remote-search.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function searchUser(name) { 4 | return request({ 5 | url: '/vue-element-admin/search/user', 6 | method: 'get', 7 | params: { name } 8 | }) 9 | } 10 | 11 | export function transactionList(query) { 12 | return request({ 13 | url: '/vue-element-admin/transaction/list', 14 | method: 'get', 15 | params: query 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /frontend/api/role.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getRoutes() { 4 | return request({ 5 | url: '/vue-element-admin/routes', 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export function getRoles() { 11 | return request({ 12 | url: '/vue-element-admin/roles', 13 | method: 'get' 14 | }) 15 | } 16 | 17 | export function addRole(data) { 18 | return request({ 19 | url: '/vue-element-admin/role', 20 | method: 'post', 21 | data 22 | }) 23 | } 24 | 25 | export function updateRole(id, data) { 26 | return request({ 27 | url: `/vue-element-admin/role/${id}`, 28 | method: 'put', 29 | data 30 | }) 31 | } 32 | 33 | export function deleteRole(id) { 34 | return request({ 35 | url: `/vue-element-admin/role/${id}`, 36 | method: 'delete' 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /frontend/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/401_images/401.gif -------------------------------------------------------------------------------- /frontend/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/404_images/404.png -------------------------------------------------------------------------------- /frontend/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /frontend/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /frontend/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /frontend/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jianfengye/hade/b28379212e5a77314bb74cd3cce89c97b5688e11/frontend/assets/logo.png -------------------------------------------------------------------------------- /frontend/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 45 | -------------------------------------------------------------------------------- /frontend/components/ImageCropper/utils/data2blob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * database64文件格式转换为2进制 3 | * 4 | * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 5 | * @param {[String]} mime [description] 6 | * @return {[blob]} [description] 7 | */ 8 | export default function(data, mime) { 9 | data = data.split(',')[1] 10 | data = window.atob(data) 11 | var ia = new Uint8Array(data.length) 12 | for (var i = 0; i < data.length; i++) { 13 | ia[i] = data.charCodeAt(i) 14 | } 15 | // canvas.toDataURL 返回的默认格式就是 image/png 16 | return new Blob([ia], { 17 | type: mime 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /frontend/components/ImageCropper/utils/mimes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | } 8 | -------------------------------------------------------------------------------- /frontend/components/MarkdownEditor/default-options.js: -------------------------------------------------------------------------------- 1 | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor 2 | export default { 3 | minHeight: '200px', 4 | previewStyle: 'vertical', 5 | useCommandShortcut: true, 6 | useDefaultHTMLSanitizer: true, 7 | usageStatistics: false, 8 | hideModeSwitch: false, 9 | toolbarItems: [ 10 | 'heading', 11 | 'bold', 12 | 'italic', 13 | 'strike', 14 | 'divider', 15 | 'hr', 16 | 'quote', 17 | 'divider', 18 | 'ul', 19 | 'ol', 20 | 'task', 21 | 'indent', 22 | 'outdent', 23 | 'divider', 24 | 'table', 25 | 'image', 26 | 'link', 27 | 'divider', 28 | 'code', 29 | 'codeblock' 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /frontend/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 61 | -------------------------------------------------------------------------------- /frontend/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /frontend/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /frontend/directive/clipboard/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('Clipboard', Clipboard) 5 | } 6 | 7 | if (window.Vue) { 8 | window.clipboard = Clipboard 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | Clipboard.install = install 13 | export default Clipboard 14 | -------------------------------------------------------------------------------- /frontend/directive/el-drag-dialog/index.js: -------------------------------------------------------------------------------- 1 | import drag from './drag' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-drag-dialog', drag) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-drag-dialog'] = drag 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | drag.install = install 13 | export default drag 14 | -------------------------------------------------------------------------------- /frontend/directive/el-table/adaptive.js: -------------------------------------------------------------------------------- 1 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event' 2 | 3 | /** 4 | * How to use 5 | * ... 6 | * el-table height is must be set 7 | * bottomOffset: 30(default) // The height of the table from the bottom of the page. 8 | */ 9 | 10 | const doResize = (el, binding, vnode) => { 11 | const { componentInstance: $table } = vnode 12 | 13 | const { value } = binding 14 | 15 | if (!$table.height) { 16 | throw new Error(`el-$table must set the height. Such as height='100px'`) 17 | } 18 | const bottomOffset = (value && value.bottomOffset) || 30 19 | 20 | if (!$table) return 21 | 22 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset 23 | $table.layout.setHeight(height) 24 | $table.doLayout() 25 | } 26 | 27 | export default { 28 | bind(el, binding, vnode) { 29 | el.resizeListener = () => { 30 | doResize(el, binding, vnode) 31 | } 32 | // parameter 1 is must be "Element" type 33 | addResizeListener(window.document.body, el.resizeListener) 34 | }, 35 | inserted(el, binding, vnode) { 36 | doResize(el, binding, vnode) 37 | }, 38 | unbind(el) { 39 | removeResizeListener(window.document.body, el.resizeListener) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /frontend/directive/el-table/index.js: -------------------------------------------------------------------------------- 1 | import adaptive from './adaptive' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-height-adaptive-table', adaptive) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-height-adaptive-table'] = adaptive 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | adaptive.install = install 13 | export default adaptive 14 | -------------------------------------------------------------------------------- /frontend/directive/permission/index.js: -------------------------------------------------------------------------------- 1 | import permission from './permission' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('permission', permission) 5 | } 6 | 7 | if (window.Vue) { 8 | window['permission'] = permission 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | permission.install = install 13 | export default permission 14 | -------------------------------------------------------------------------------- /frontend/directive/permission/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | function checkPermission(el, binding) { 4 | const { value } = binding 5 | const roles = store.getters && store.getters.roles 6 | 7 | if (value && value instanceof Array) { 8 | if (value.length > 0) { 9 | const permissionRoles = value 10 | 11 | const hasPermission = roles.some(role => { 12 | return permissionRoles.includes(role) 13 | }) 14 | 15 | if (!hasPermission) { 16 | el.parentNode && el.parentNode.removeChild(el) 17 | } 18 | } 19 | } else { 20 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 21 | } 22 | } 23 | 24 | export default { 25 | inserted(el, binding) { 26 | checkPermission(el, binding) 27 | }, 28 | update(el, binding) { 29 | checkPermission(el, binding) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /frontend/directive/waves/index.js: -------------------------------------------------------------------------------- 1 | import waves from './waves' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('waves', waves) 5 | } 6 | 7 | if (window.Vue) { 8 | window.waves = waves 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | waves.install = install 13 | export default waves 14 | -------------------------------------------------------------------------------- /frontend/directive/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } -------------------------------------------------------------------------------- /frontend/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /frontend/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /frontend/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 49 | 50 | 58 | -------------------------------------------------------------------------------- /frontend/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $subMenu = this.$refs.subMenu 15 | if ($subMenu) { 16 | const handleMouseleave = $subMenu.handleMouseleave 17 | $subMenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 42 | -------------------------------------------------------------------------------- /frontend/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 44 | -------------------------------------------------------------------------------- /frontend/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as AppMain } from './AppMain' 2 | export { default as Navbar } from './Navbar' 3 | export { default as Settings } from './Settings' 4 | export { default as Sidebar } from './Sidebar/index.vue' 5 | export { default as TagsView } from './TagsView/index.vue' 6 | -------------------------------------------------------------------------------- /frontend/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /frontend/router/modules/charts.js: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules**/ 2 | 3 | import Layout from '@/layout' 4 | 5 | const chartsRouter = { 6 | path: '/charts', 7 | component: Layout, 8 | redirect: 'noRedirect', 9 | name: 'Charts', 10 | meta: { 11 | title: 'Charts', 12 | icon: 'chart' 13 | }, 14 | children: [ 15 | { 16 | path: 'keyboard', 17 | component: () => import('@/views/charts/keyboard'), 18 | name: 'KeyboardChart', 19 | meta: { title: 'Keyboard Chart', noCache: true } 20 | }, 21 | { 22 | path: 'line', 23 | component: () => import('@/views/charts/line'), 24 | name: 'LineChart', 25 | meta: { title: 'Line Chart', noCache: true } 26 | }, 27 | { 28 | path: 'mix-chart', 29 | component: () => import('@/views/charts/mix-chart'), 30 | name: 'MixChart', 31 | meta: { title: 'Mix Chart', noCache: true } 32 | } 33 | ] 34 | } 35 | 36 | export default chartsRouter 37 | -------------------------------------------------------------------------------- /frontend/router/modules/table.js: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules **/ 2 | 3 | import Layout from '@/layout' 4 | 5 | const tableRouter = { 6 | path: '/table', 7 | component: Layout, 8 | redirect: '/table/complex-table', 9 | name: 'Table', 10 | meta: { 11 | title: 'Table', 12 | icon: 'table' 13 | }, 14 | children: [ 15 | { 16 | path: 'dynamic-table', 17 | component: () => import('@/views/table/dynamic-table/index'), 18 | name: 'DynamicTable', 19 | meta: { title: 'Dynamic Table' } 20 | }, 21 | { 22 | path: 'drag-table', 23 | component: () => import('@/views/table/drag-table'), 24 | name: 'DragTable', 25 | meta: { title: 'Drag Table' } 26 | }, 27 | { 28 | path: 'inline-edit-table', 29 | component: () => import('@/views/table/inline-edit-table'), 30 | name: 'InlineEditTable', 31 | meta: { title: 'Inline Edit' } 32 | }, 33 | { 34 | path: 'complex-table', 35 | component: () => import('@/views/table/complex-table'), 36 | name: 'ComplexTable', 37 | meta: { title: 'Complex Table' } 38 | } 39 | ] 40 | } 41 | export default tableRouter 42 | -------------------------------------------------------------------------------- /frontend/settings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'Vue Element Admin', 3 | 4 | /** 5 | * @type {boolean} true | false 6 | * @description Whether show the settings right-panel 7 | */ 8 | showSettings: true, 9 | 10 | /** 11 | * @type {boolean} true | false 12 | * @description Whether need tagsView 13 | */ 14 | tagsView: true, 15 | 16 | /** 17 | * @type {boolean} true | false 18 | * @description Whether fix the header 19 | */ 20 | fixedHeader: false, 21 | 22 | /** 23 | * @type {boolean} true | false 24 | * @description Whether show the logo in sidebar 25 | */ 26 | sidebarLogo: false, 27 | 28 | /** 29 | * @type {string | array} 'production' | ['production', 'development'] 30 | * @description Need show err logs component. 31 | * The default is only used in the production env 32 | * If you want to also use it in dev, you can pass ['production', 'development'] 33 | */ 34 | errorLog: 'production' 35 | } 36 | -------------------------------------------------------------------------------- /frontend/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | size: state => state.app.size, 4 | device: state => state.app.device, 5 | visitedViews: state => state.tagsView.visitedViews, 6 | cachedViews: state => state.tagsView.cachedViews, 7 | token: state => state.user.token, 8 | avatar: state => state.user.avatar, 9 | name: state => state.user.name, 10 | introduction: state => state.user.introduction, 11 | roles: state => state.user.roles, 12 | permission_routes: state => state.permission.routes, 13 | errorLogs: state => state.errorLog.logs 14 | } 15 | export default getters 16 | -------------------------------------------------------------------------------- /frontend/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import getters from './getters' 4 | 5 | Vue.use(Vuex) 6 | 7 | // https://webpack.js.org/guides/dependency-management/#requirecontext 8 | const modulesFiles = require.context('./modules', true, /\.js$/) 9 | 10 | // you do not need `import app from './modules/app'` 11 | // it will auto require all vuex module from modules file 12 | const modules = modulesFiles.keys().reduce((modules, modulePath) => { 13 | // set './app.js' => 'app' 14 | const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') 15 | const value = modulesFiles(modulePath) 16 | modules[moduleName] = value.default 17 | return modules 18 | }, {}) 19 | 20 | const store = new Vuex.Store({ 21 | modules, 22 | getters 23 | }) 24 | 25 | export default store 26 | -------------------------------------------------------------------------------- /frontend/store/modules/errorLog.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | logs: [] 3 | } 4 | 5 | const mutations = { 6 | ADD_ERROR_LOG: (state, log) => { 7 | state.logs.push(log) 8 | }, 9 | CLEAR_ERROR_LOG: (state) => { 10 | state.logs.splice(0) 11 | } 12 | } 13 | 14 | const actions = { 15 | addErrorLog({ commit }, log) { 16 | commit('ADD_ERROR_LOG', log) 17 | }, 18 | clearErrorLog({ commit }) { 19 | commit('CLEAR_ERROR_LOG') 20 | } 21 | } 22 | 23 | export default { 24 | namespaced: true, 25 | state, 26 | mutations, 27 | actions 28 | } 29 | -------------------------------------------------------------------------------- /frontend/store/modules/settings.js: -------------------------------------------------------------------------------- 1 | import variables from '@/styles/element-variables.scss' 2 | import defaultSettings from '@/settings' 3 | 4 | const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings 5 | 6 | const state = { 7 | theme: variables.theme, 8 | showSettings: showSettings, 9 | tagsView: tagsView, 10 | fixedHeader: fixedHeader, 11 | sidebarLogo: sidebarLogo 12 | } 13 | 14 | const mutations = { 15 | CHANGE_SETTING: (state, { key, value }) => { 16 | if (state.hasOwnProperty(key)) { 17 | state[key] = value 18 | } 19 | } 20 | } 21 | 22 | const actions = { 23 | changeSetting({ commit }, data) { 24 | commit('CHANGE_SETTING', data) 25 | } 26 | } 27 | 28 | export default { 29 | namespaced: true, 30 | state, 31 | mutations, 32 | actions 33 | } 34 | 35 | -------------------------------------------------------------------------------- /frontend/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #1890ff; 8 | $--color-success: #13ce66; 9 | $--color-warning: #ffba00; 10 | $--color-danger: #ff4949; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border: 1px solid #dfe6ec; 21 | 22 | /* icon font path, required */ 23 | $--font-path: "~element-ui/lib/theme-chalk/fonts"; 24 | 25 | @import "~element-ui/packages/theme-chalk/src/index"; 26 | 27 | // the :export directive is the magic sauce for webpack 28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 29 | :export { 30 | theme: $--color-primary; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // global transition css 2 | 3 | /* fade */ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /* fade-transform */ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /frontend/styles/variables.scss: -------------------------------------------------------------------------------- 1 | // base color 2 | $blue:#324157; 3 | $light-blue:#3A71A8; 4 | $red:#C03639; 5 | $pink: #E65D6E; 6 | $green: #30B08F; 7 | $tiffany: #4AB7BD; 8 | $yellow:#FEC171; 9 | $panGreen: #30B08F; 10 | 11 | // sidebar 12 | $menuText:#bfcbd9; 13 | $menuActiveText:#409EFF; 14 | $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951 15 | 16 | $menuBg:#304156; 17 | $menuHover:#263445; 18 | 19 | $subMenuBg:#1f2d3d; 20 | $subMenuHover:#001528; 21 | 22 | $sideBarWidth: 210px; 23 | 24 | // the :export directive is the magic sauce for webpack 25 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 26 | :export { 27 | menuText: $menuText; 28 | menuActiveText: $menuActiveText; 29 | subMenuActiveText: $subMenuActiveText; 30 | menuBg: $menuBg; 31 | menuHover: $menuHover; 32 | subMenuBg: $subMenuBg; 33 | subMenuHover: $subMenuHover; 34 | sideBarWidth: $sideBarWidth; 35 | } 36 | -------------------------------------------------------------------------------- /frontend/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | 3 | const TokenKey = 'Admin-Token' 4 | 5 | export function getToken() { 6 | return Cookies.get(TokenKey) 7 | } 8 | 9 | export function setToken(token) { 10 | return Cookies.set(TokenKey, token) 11 | } 12 | 13 | export function removeToken() { 14 | return Cookies.remove(TokenKey) 15 | } 16 | -------------------------------------------------------------------------------- /frontend/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.destroy() 26 | }) 27 | clipboard.on('error', () => { 28 | clipboardError() 29 | clipboard.destroy() 30 | }) 31 | clipboard.onClick(event) 32 | } 33 | -------------------------------------------------------------------------------- /frontend/utils/error-log.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '@/store' 3 | import { isString, isArray } from '@/utils/validate' 4 | import settings from '@/settings' 5 | 6 | // you can set in settings.js 7 | // errorLog:'production' | ['production', 'development'] 8 | const { errorLog: needErrorLog } = settings 9 | 10 | function checkNeed() { 11 | const env = process.env.NODE_ENV 12 | if (isString(needErrorLog)) { 13 | return env === needErrorLog 14 | } 15 | if (isArray(needErrorLog)) { 16 | return needErrorLog.includes(env) 17 | } 18 | return false 19 | } 20 | 21 | if (checkNeed()) { 22 | Vue.config.errorHandler = function(err, vm, info, a) { 23 | // Don't ask me why I use Vue.nextTick, it just a hack. 24 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500 25 | Vue.nextTick(() => { 26 | store.dispatch('errorLog/addErrorLog', { 27 | err, 28 | vm, 29 | info, 30 | url: window.location.href 31 | }) 32 | console.error(err, info) 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/utils/get-page-title.js: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title || 'Vue Element Admin' 4 | 5 | export default function getPageTitle(pageTitle) { 6 | if (pageTitle) { 7 | return `${pageTitle} - ${title}` 8 | } 9 | return `${title}` 10 | } 11 | -------------------------------------------------------------------------------- /frontend/utils/open-window.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by PanJiaChen on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | export default function openWindow(url, title, w, h) { 9 | // Fixes dual-screen position Most browsers Firefox 10 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left 11 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top 12 | 13 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width 14 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height 15 | 16 | const left = ((width / 2) - (w / 2)) + dualScreenLeft 17 | const top = ((height / 2) - (h / 2)) + dualScreenTop 18 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) 19 | 20 | // Puts focus on the newWindow 21 | if (window.focus) { 22 | newWindow.focus() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /frontend/utils/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | /** 4 | * @param {Array} value 5 | * @returns {Boolean} 6 | * @example see @/views/permission/directive.vue 7 | */ 8 | export default function checkPermission(value) { 9 | if (value && value instanceof Array && value.length > 0) { 10 | const roles = store.getters && store.getters.roles 11 | const permissionRoles = value 12 | 13 | const hasPermission = roles.some(role => { 14 | return permissionRoles.includes(role) 15 | }) 16 | 17 | if (!hasPermission) { 18 | return false 19 | } 20 | return true 21 | } else { 22 | console.error(`need roles! Like v-permission="['admin','editor']"`) 23 | return false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/vendor/Export2Zip.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { saveAs } from 'file-saver' 3 | import JSZip from 'jszip' 4 | 5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) { 6 | const zip = new JSZip() 7 | const txt_name = txtName || 'file' 8 | const zip_name = zipName || 'file' 9 | const data = jsonData 10 | let txtData = `${th}\r\n` 11 | data.forEach((row) => { 12 | let tempStr = '' 13 | tempStr = row.toString() 14 | txtData += `${tempStr}\r\n` 15 | }) 16 | zip.file(`${txt_name}.txt`, txtData) 17 | zip.generateAsync({ 18 | type: "blob" 19 | }).then((blob) => { 20 | saveAs(blob, `${zip_name}.zip`) 21 | }, (err) => { 22 | alert('导出失败') 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /frontend/views/charts/keyboard.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/views/charts/line.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/views/charts/mix-chart.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /frontend/views/components-demo/dnd-list.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 39 | 40 | -------------------------------------------------------------------------------- /frontend/views/components-demo/drag-select.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 44 | -------------------------------------------------------------------------------- /frontend/views/components-demo/dropzone.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 31 | 32 | -------------------------------------------------------------------------------- /frontend/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /frontend/views/error-log/components/ErrorTestA.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /frontend/views/error-log/components/ErrorTestB.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /frontend/views/error-log/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /frontend/views/example/components/Dropdown/Comment.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 42 | -------------------------------------------------------------------------------- /frontend/views/example/components/Dropdown/Platform.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 47 | -------------------------------------------------------------------------------- /frontend/views/example/components/Dropdown/SourceUrl.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 39 | -------------------------------------------------------------------------------- /frontend/views/example/components/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | export { default as CommentDropdown } from './Comment' 2 | export { default as PlatformDropdown } from './Platform' 3 | export { default as SourceUrlDropdown } from './SourceUrl' 4 | -------------------------------------------------------------------------------- /frontend/views/example/components/Warning.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/views/example/create.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/views/example/edit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/views/excel/components/AutoWidthOption.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 35 | -------------------------------------------------------------------------------- /frontend/views/excel/components/BookTypeOption.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 40 | -------------------------------------------------------------------------------- /frontend/views/excel/components/FilenameOption.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | -------------------------------------------------------------------------------- /frontend/views/excel/upload-excel.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 43 | -------------------------------------------------------------------------------- /frontend/views/guide/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 37 | -------------------------------------------------------------------------------- /frontend/views/guide/steps.js: -------------------------------------------------------------------------------- 1 | const steps = [ 2 | { 3 | element: '#hamburger-container', 4 | popover: { 5 | title: 'Hamburger', 6 | description: 'Open && Close sidebar', 7 | position: 'bottom' 8 | } 9 | }, 10 | { 11 | element: '#breadcrumb-container', 12 | popover: { 13 | title: 'Breadcrumb', 14 | description: 'Indicate the current page location', 15 | position: 'bottom' 16 | } 17 | }, 18 | { 19 | element: '#header-search', 20 | popover: { 21 | title: 'Page Search', 22 | description: 'Page search, quick navigation', 23 | position: 'left' 24 | } 25 | }, 26 | { 27 | element: '#screenfull', 28 | popover: { 29 | title: 'Screenfull', 30 | description: 'Set the page into fullscreen', 31 | position: 'left' 32 | } 33 | }, 34 | { 35 | element: '#size-select', 36 | popover: { 37 | title: 'Switch Size', 38 | description: 'Switch the system size', 39 | position: 'left' 40 | } 41 | }, 42 | { 43 | element: '#tags-view-container', 44 | popover: { 45 | title: 'Tags view', 46 | description: 'The history of the page you visited', 47 | position: 'bottom' 48 | }, 49 | padding: 0 50 | } 51 | ] 52 | 53 | export default steps 54 | -------------------------------------------------------------------------------- /frontend/views/icons/svg-icons.js: -------------------------------------------------------------------------------- 1 | const req = require.context('../../icons/svg', false, /\.svg$/) 2 | const requireAll = requireContext => requireContext.keys() 3 | 4 | const re = /\.\/(.*)\.svg/ 5 | 6 | const svgIcons = requireAll(req).map(i => { 7 | return i.match(re)[1] 8 | }) 9 | 10 | export default svgIcons 11 | -------------------------------------------------------------------------------- /frontend/views/login/auth-redirect.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /frontend/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /frontend/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /frontend/views/pdf/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/views/permission/components/SwitchRoles.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 33 | -------------------------------------------------------------------------------- /frontend/views/permission/page.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /frontend/views/profile/components/Account.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 39 | -------------------------------------------------------------------------------- /frontend/views/profile/components/Timeline.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 44 | -------------------------------------------------------------------------------- /frontend/views/qiniu/upload.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 42 | -------------------------------------------------------------------------------- /frontend/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /frontend/views/table/dynamic-table/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | 25 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 6 | 'jest-transform-stub', 7 | '^.+\\.jsx?$': 'babel-jest' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 15 | ], 16 | collectCoverageFrom: ['src/utils/validate.js', 'src/components/**/*.{js|vue}'], 17 | coverageDirectory: '/tests/unit/coverage', 18 | collectCoverage: true, 19 | coverageReporters: [ 20 | 'html', 21 | 'text' 22 | ], 23 | testURL: 'http://localhost/' 24 | } 25 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["src/*"] 6 | } 7 | }, 8 | "exclude": ["node_modules", "dist"] 9 | } -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "hade/app/console" 5 | "hade/app/http" 6 | "hade/app/provider" 7 | "hade/framework" 8 | "hade/framework/provider/app" 9 | "hade/framework/provider/config" 10 | "hade/framework/provider/env" 11 | "hade/framework/provider/id" 12 | "hade/framework/provider/kernel" 13 | "hade/framework/provider/log" 14 | "hade/framework/provider/ssh" 15 | "hade/framework/util" 16 | ) 17 | 18 | func main() { 19 | container := framework.NewHadeContainer() 20 | 21 | basePath := util.GetExecDirectory() 22 | container.Singleton(&app.HadeAppProvider{BasePath: basePath}) 23 | container.Singleton(&env.HadeEnvProvider{}) 24 | container.Singleton(&config.HadeConfigProvider{}) 25 | container.Singleton(&log.HadeLogServiceProvider{}) 26 | container.Singleton(&id.HadeIDProvider{}) 27 | container.Singleton(&ssh.HadeSSHProvider{}) 28 | 29 | if engine, err := http.NewHttpEngine(); err == nil { 30 | container.Singleton(&kernel.HadeKernelProvider{HttpEngine: engine}) 31 | } 32 | 33 | // custom register 34 | provider.RegisterCustomProvider(container) 35 | 36 | console.RunCommand(container) 37 | } 38 | -------------------------------------------------------------------------------- /mock/remote-search.js: -------------------------------------------------------------------------------- 1 | const Mock = require('mockjs') 2 | 3 | const NameList = [] 4 | const count = 100 5 | 6 | for (let i = 0; i < count; i++) { 7 | NameList.push(Mock.mock({ 8 | name: '@first' 9 | })) 10 | } 11 | NameList.push({ name: 'mock-Pan' }) 12 | 13 | module.exports = [ 14 | // username search 15 | { 16 | url: '/vue-element-admin/search/user', 17 | type: 'get', 18 | response: config => { 19 | const { name } = config.query 20 | const mockNameList = NameList.filter(item => { 21 | const lowerCaseName = item.name.toLowerCase() 22 | return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0) 23 | }) 24 | return { 25 | code: 20000, 26 | data: { items: mockNameList } 27 | } 28 | } 29 | }, 30 | 31 | // transaction list 32 | { 33 | url: '/vue-element-admin/transaction/list', 34 | type: 'get', 35 | response: _ => { 36 | return { 37 | code: 20000, 38 | data: { 39 | total: 20, 40 | 'items|20': [{ 41 | order_no: '@guid()', 42 | timestamp: +Mock.Random.date('T'), 43 | username: '@name()', 44 | price: '@float(1000, 15000, 0, 2)', 45 | 'status|1': ['success', 'pending'] 46 | }] 47 | } 48 | } 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /mock/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @param {string} url 3 | * @returns {Object} 4 | */ 5 | function param2Obj(url) { 6 | const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') 7 | if (!search) { 8 | return {} 9 | } 10 | const obj = {} 11 | const searchArr = search.split('&') 12 | searchArr.forEach(v => { 13 | const index = v.indexOf('=') 14 | if (index !== -1) { 15 | const name = v.substring(0, index) 16 | const val = v.substring(index + 1, v.length) 17 | obj[name] = val 18 | } 19 | }) 20 | return obj 21 | } 22 | 23 | /** 24 | * This is just a simple version of deep copy 25 | * Has a lot of edge cases bug 26 | * If you want to use a perfect deep copy, use lodash's _.cloneDeep 27 | * @param {Object} source 28 | * @returns {Object} 29 | */ 30 | function deepClone(source) { 31 | if (!source && typeof source !== 'object') { 32 | throw new Error('error arguments', 'deepClone') 33 | } 34 | const targetObj = source.constructor === Array ? [] : {} 35 | Object.keys(source).forEach(keys => { 36 | if (source[keys] && typeof source[keys] === 'object') { 37 | targetObj[keys] = deepClone(source[keys]) 38 | } else { 39 | targetObj[keys] = source[keys] 40 | } 41 | }) 42 | return targetObj 43 | } 44 | 45 | module.exports = { 46 | param2Obj, 47 | deepClone 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hade", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "docs:build": "vuepress build docs --dest=./dist", 10 | "docs:dev": "vuepress dev docs --temp .temp" 11 | }, 12 | "dependencies": { 13 | "@vuepress/plugin-back-to-top": "^1.5.4", 14 | "@vuepress/plugin-last-updated": "^1.5.4", 15 | "@vuepress/plugin-medium-zoom": "^1.5.4", 16 | "@vuepress/shared-utils": "^1.5.4", 17 | "@vuepress/theme-vue": "^1.5.4", 18 | "core-js": "^3.6.4", 19 | "vue": "^2.6.11", 20 | "vuepress": "^1.5.4" 21 | }, 22 | "devDependencies": { 23 | "@vue/cli-plugin-babel": "~4.3.0", 24 | "@vue/cli-plugin-eslint": "~4.3.0", 25 | "@vue/cli-service": "~4.3.0", 26 | "babel-eslint": "^10.1.0", 27 | "eslint": "^6.7.2", 28 | "eslint-plugin-vue": "^6.2.2", 29 | "vue-template-compiler": "^2.6.11" 30 | }, 31 | "eslintConfig": { 32 | "root": true, 33 | "env": { 34 | "node": true 35 | }, 36 | "extends": [ 37 | "plugin:vue/essential", 38 | "eslint:recommended" 39 | ], 40 | "parserOptions": { 41 | "parser": "babel-eslint" 42 | }, 43 | "rules": {} 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions", 48 | "not dead" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /plop-templates/component/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /plop-templates/store/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if state}} 2 | const state = {} 3 | {{/if}} 4 | 5 | {{#if mutations}} 6 | const mutations = {} 7 | {{/if}} 8 | 9 | {{#if actions}} 10 | const actions = {} 11 | {{/if}} 12 | 13 | export default { 14 | namespaced: true, 15 | {{options}} 16 | } 17 | -------------------------------------------------------------------------------- /plop-templates/utils.js: -------------------------------------------------------------------------------- 1 | exports.notEmpty = name => v => 2 | !v || v.trim() === '' ? `${name} is required` : true 3 | -------------------------------------------------------------------------------- /plop-templates/view/index.hbs: -------------------------------------------------------------------------------- 1 | {{#if template}} 2 | 5 | {{/if}} 6 | 7 | {{#if script}} 8 | 20 | {{/if}} 21 | 22 | {{#if style}} 23 | 26 | {{/if}} 27 | -------------------------------------------------------------------------------- /plop-templates/view/prompt.js: -------------------------------------------------------------------------------- 1 | const { notEmpty } = require('../utils.js') 2 | 3 | module.exports = { 4 | description: 'generate a view', 5 | prompts: [{ 6 | type: 'input', 7 | name: 'name', 8 | message: 'view name please', 9 | validate: notEmpty('name') 10 | }, 11 | { 12 | type: 'checkbox', 13 | name: 'blocks', 14 | message: 'Blocks:', 15 | choices: [{ 16 | name: '