├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── LICENSE ├── README.md ├── app ├── console │ ├── command │ │ └── foo │ │ │ └── foo.go │ └── kernel.go ├── http │ ├── kernel.go │ ├── middleware │ │ ├── .gitkeeper │ │ └── cors │ │ │ ├── .github │ │ │ └── workflows │ │ │ │ └── go.yml │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── config.go │ │ │ ├── cors.go │ │ │ ├── cors_test.go │ │ │ ├── examples │ │ │ └── example.go │ │ │ └── utils.go │ ├── module │ │ └── demo │ │ │ ├── api.go │ │ │ ├── api_cache.go │ │ │ ├── api_orm.go │ │ │ ├── dto.go │ │ │ ├── mapper.go │ │ │ ├── model.go │ │ │ ├── repository.go │ │ │ └── service.go │ ├── route.go │ ├── swagger.go │ └── swagger │ │ ├── docs.go │ │ ├── swagger.json │ │ └── swagger.yaml └── provider │ ├── demo │ ├── contract.go │ ├── provider.go │ └── service.go │ └── user │ ├── contract.go │ ├── provider.go │ └── service.go ├── build ├── build.js ├── check-versions.js ├── logo.png ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── dev.env.js ├── development │ ├── app.yaml │ ├── database.yaml │ ├── deploy.yaml │ ├── log.yaml │ └── ssh.yaml ├── index.js ├── prod.env.js ├── production │ ├── .gitkeeper │ ├── app.yaml │ ├── database.yaml │ ├── deploy.yaml │ ├── gift.yaml │ ├── log.yaml │ └── swagger.yaml ├── test.env.js └── testing │ ├── app.yaml │ ├── cache.yaml │ ├── database.yaml │ ├── deploy.yaml │ ├── log.yaml │ ├── redis.yaml │ ├── ssh.yaml │ └── swagger.yaml ├── 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 │ ├── .gitignore │ ├── .golangci.yml │ ├── .mailmap │ ├── CHANGELOG.md │ ├── CONDUCT.md │ ├── CONTRIBUTING.md │ ├── LICENSE.txt │ ├── Makefile │ ├── README.md │ ├── args.go │ ├── args_test.go │ ├── bash_completions.go │ ├── bash_completions.md │ ├── bash_completionsV2.go │ ├── bash_completions_test.go │ ├── cobra.go │ ├── cobra │ │ ├── Makefile │ │ ├── README.md │ │ ├── cmd │ │ │ ├── add.go │ │ │ ├── add_test.go │ │ │ ├── golden_test.go │ │ │ ├── helpers.go │ │ │ ├── helpers_test.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 │ ├── completions.go │ ├── completions_test.go │ ├── doc │ │ ├── README.md │ │ ├── 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 │ ├── fish_completions_test.go │ ├── hade_command.go │ ├── hade_command_contract.go │ ├── hade_command_distributed.go │ ├── powershell_completions.go │ ├── powershell_completions.md │ ├── projects_using_cobra.md │ ├── shell_completions.go │ ├── shell_completions.md │ ├── user_guide.md │ ├── zsh_completions.go │ └── zsh_completions.md ├── command │ ├── app.go │ ├── build.go │ ├── cmd.go │ ├── config.go │ ├── cron.go │ ├── deploy.go │ ├── dev.go │ ├── env.go │ ├── go_cmd.go │ ├── go_cmd_test.go │ ├── help.go │ ├── kernel.go │ ├── middleware.go │ ├── new.go │ ├── npm.go │ ├── provider.go │ └── swagger.go ├── container.go ├── contract │ ├── app.go │ ├── cache.go │ ├── config.go │ ├── distributed.go │ ├── env.go │ ├── id.go │ ├── kernel.go │ ├── log.go │ ├── orm.go │ ├── redis.go │ ├── ssh.go │ └── trace.go ├── gin │ ├── .github │ │ ├── ISSUE_TEMPLATE.md │ │ └── PULL_REQUEST_TEMPLATE.md │ ├── .gitignore │ ├── .travis.yml │ ├── 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_msgpack_test.go │ │ ├── binding_nomsgpack.go │ │ ├── binding_test.go │ │ ├── default_validator.go │ │ ├── default_validator_test.go │ │ ├── form.go │ │ ├── form_mapping.go │ │ ├── form_mapping_benchmark_test.go │ │ ├── form_mapping_test.go │ │ ├── header.go │ │ ├── json.go │ │ ├── json_test.go │ │ ├── msgpack.go │ │ ├── msgpack_test.go │ │ ├── multipart_form_mapping.go │ │ ├── multipart_form_mapping_test.go │ │ ├── protobuf.go │ │ ├── query.go │ │ ├── uri.go │ │ ├── validate_test.go │ │ ├── xml.go │ │ ├── xml_test.go │ │ ├── yaml.go │ │ └── yaml_test.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_1.13_test.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_context.go │ ├── hade_context_contract.go │ ├── hade_engine.go │ ├── hade_request.go │ ├── hade_response.go │ ├── internal │ │ ├── bytesconv │ │ │ ├── bytesconv.go │ │ │ └── bytesconv_test.go │ │ └── 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 │ │ ├── reader_test.go │ │ ├── redirect.go │ │ ├── render.go │ │ ├── render_msgpack_test.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_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 │ └── version.go ├── middleware │ ├── cost.go │ ├── gin-swagger │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── b0x.yml │ │ ├── swagger.go │ │ ├── swaggerFiles │ │ │ ├── ab0x.go │ │ │ ├── b0xfile__favicon-16x16.png.go │ │ │ ├── b0xfile__favicon-32x32.png.go │ │ │ ├── b0xfile__index.html.go │ │ │ ├── b0xfile__oauth2-redirect.html.go │ │ │ ├── b0xfile__swagger-ui-bundle.js.go │ │ │ ├── b0xfile__swagger-ui-bundle.js.map.go │ │ │ ├── b0xfile__swagger-ui-standalone-preset.js.go │ │ │ ├── b0xfile__swagger-ui-standalone-preset.js.map.go │ │ │ ├── b0xfile__swagger-ui.css.go │ │ │ ├── b0xfile__swagger-ui.css.map.go │ │ │ ├── b0xfile__swagger-ui.js.go │ │ │ └── b0xfile__swagger-ui.js.map.go │ │ └── swagger_test.go │ ├── static │ │ ├── LICENCE.md │ │ └── static.go │ ├── timeout.go │ └── trace.go ├── provider.go ├── provider │ ├── app │ │ ├── provider.go │ │ └── service.go │ ├── cache │ │ ├── provider.go │ │ └── services │ │ │ ├── cache.go │ │ │ ├── memory.go │ │ │ ├── memory_test.go │ │ │ ├── redis.go │ │ │ └── redis_test.go │ ├── config │ │ ├── fake_provider.go │ │ ├── fake_service.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ ├── service.go │ │ └── service_test.go │ ├── distributed │ │ ├── provider_local.go │ │ └── service_local.go │ ├── env │ │ ├── provider.go │ │ ├── service.go │ │ ├── testing_provider.go │ │ └── testing_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 │ │ └── services │ │ │ ├── console.go │ │ │ ├── custom.go │ │ │ ├── log.go │ │ │ ├── rotate.go │ │ │ └── single.go │ ├── orm │ │ ├── config.go │ │ ├── config_test.go │ │ ├── logger.go │ │ ├── provider.go │ │ └── service.go │ ├── redis │ │ ├── config.go │ │ ├── provider.go │ │ ├── service.go │ │ └── service_test.go │ ├── ssh │ │ ├── config.go │ │ ├── provider.go │ │ ├── service.go │ │ └── service_test.go │ └── trace │ │ ├── provider.go │ │ └── service.go └── util │ ├── console.go │ ├── console_test.go │ ├── exec.go │ ├── file.go │ ├── http.go │ └── zip.go ├── go.mod ├── go.sum ├── index.html ├── main.go ├── package-lock.json ├── package.json ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── main.js └── router │ └── index.js ├── static └── .gitkeep ├── storage ├── log │ └── .gitignore └── runtime │ └── .gitignore └── test ├── e2e ├── custom-assertions │ └── elementCount.js ├── nightwatch.conf.js ├── runner.js └── specs │ └── test.js ├── env.go └── unit ├── .eslintrc ├── jest.conf.js ├── setup.js └── specs └── HelloWorld.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | /test/unit/coverage/ 6 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parserOptions: { 6 | parser: 'babel-eslint' 7 | }, 8 | env: { 9 | browser: true, 10 | }, 11 | extends: [ 12 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 13 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 14 | 'plugin:vue/essential', 15 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 16 | 'standard' 17 | ], 18 | // required to lint *.vue files 19 | plugins: [ 20 | 'vue' 21 | ], 22 | // add your custom rules here 23 | rules: { 24 | // allow async-await 25 | 'generator-star-spacing': 'off', 26 | // allow debugger during development 27 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 28 | "space-before-function-paren": 0 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | /test/unit/coverage/ 8 | /test/e2e/reports/ 9 | selenium-debug.log 10 | hade 11 | storage 12 | /deploy/ 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 jianfengye 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 官方网站 2 | 3 | http://hade.funaio.cn/ 4 | 5 | # 框架特色: 6 | 7 | ## 基于协议 8 | 9 | 服务与服务间的协议是基于协议进行交互的。 10 | 11 | ## 前后端协同 12 | 13 | 前后端协同开发 14 | 15 | ## 命令行 16 | 17 | 有充分的命令行工具 18 | 19 | ## 集成定时服务 20 | 21 | 如果你需要启动定时服务,提供命令进行定时服务的启动 22 | 23 | ## 文档丰富 24 | 25 | 提供丰富的文档说明,提供丰富的文档说明 26 | 27 | ## 开发模式 28 | 29 | 在开发模式下进行前后端开发,极大提高了开发效率和开发体验 30 | 31 | ## 使用指南 32 | 33 | [介绍](docs/guide/introduce.md) 34 | 35 | [安装](docs/guide/install.md) 36 | 37 | [编译](docs/guide/build.md) 38 | 39 | [目录结构](docs/guide/structure.md) 40 | 41 | [运行](docs/guide/app.md) 42 | 43 | [环境变量](docs/guide/env.md) 44 | 45 | [调试模式](docs/guide/dev.md) 46 | 47 | [命令](docs/guide/command.md) 48 | 49 | [定时任务](docs/guide/cron.md) 50 | 51 | [中间件](docs/guide/middleware.md) 52 | 53 | [swagger](docs/guide/swagger.md) 54 | 55 | [服务提供者](docs/guide/provider.md) 56 | 57 | [待做事项](docs/guide/todo.md) 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/console/command/foo/foo.go: -------------------------------------------------------------------------------- 1 | package foo 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gohade/hade/framework/cobra" 7 | ) 8 | 9 | var FooCommand = &cobra.Command{ 10 | Use: "foo", 11 | Short: "foo", 12 | RunE: func(c *cobra.Command, args []string) error { 13 | container := c.GetContainer() 14 | fmt.Println(container) 15 | return nil 16 | }, 17 | } 18 | 19 | -------------------------------------------------------------------------------- /app/console/kernel.go: -------------------------------------------------------------------------------- 1 | package console 2 | 3 | import ( 4 | "github.com/gohade/hade/app/console/command/foo" 5 | "github.com/gohade/hade/framework" 6 | "github.com/gohade/hade/framework/cobra" 7 | "github.com/gohade/hade/framework/command" 8 | ) 9 | 10 | // RunCommand 初始化根Command并运行 11 | func RunCommand(container framework.Container) error { 12 | // 根Command 13 | var rootCmd = &cobra.Command{ 14 | // 定义根命令的关键字 15 | Use: "hade", 16 | // 简短介绍 17 | Short: "hade 命令", 18 | // 根命令的详细介绍 19 | Long: "hade 框架提供的命令行工具,使用这个命令行工具能很方便执行框架自带命令,也能很方便编写业务命令", 20 | // 根命令的执行函数 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | cmd.InitDefaultHelpFlag() 23 | return cmd.Help() 24 | }, 25 | // 不需要出现cobra默认的completion子命令 26 | CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true}, 27 | } 28 | 29 | // 为根Command设置服务容器 30 | rootCmd.SetContainer(container) 31 | // 绑定框架的命令 32 | command.AddKernelCommands(rootCmd) 33 | // 绑定业务的命令 34 | AddAppCommand(rootCmd) 35 | 36 | // 执行RootCommand 37 | return rootCmd.Execute() 38 | } 39 | 40 | // 绑定业务的命令 41 | func AddAppCommand(rootCmd *cobra.Command) { 42 | 43 | rootCmd.AddCommand(foo.FooCommand) 44 | // 每秒调用一次Foo命令 45 | //rootCmd.AddCronCommand("* * * * * *", demo.FooCommand) 46 | 47 | // 启动一个分布式任务调度,调度的服务名称为init_func_for_test,每个节点每5s调用一次Foo命令,抢占到了调度任务的节点将抢占锁持续挂载2s才释放 48 | //rootCmd.AddDistributedCronCommand("foo_func_for_test", "*/5 * * * * *", demo.FooCommand, 2*time.Second) 49 | } 50 | -------------------------------------------------------------------------------- /app/http/kernel.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/gin" 6 | ) 7 | 8 | // NewHttpEngine 创建了一个绑定了路由的Web引擎 9 | func NewHttpEngine(container framework.Container) (*gin.Engine, error) { 10 | // 设置为Release,为的是默认在启动中不输出调试信息 11 | gin.SetMode(gin.ReleaseMode) 12 | // 默认启动一个Web引擎 13 | r := gin.New() 14 | // 设置了Engine 15 | r.SetContainer(container) 16 | 17 | // 默认注册recovery中间件 18 | r.Use(gin.Recovery()) 19 | 20 | // 业务绑定路由操作 21 | Routes(r) 22 | // 返回绑定路由后的Web引擎 23 | return r, nil 24 | } 25 | -------------------------------------------------------------------------------- /app/http/middleware/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/app/http/middleware/.gitkeeper -------------------------------------------------------------------------------- /app/http/middleware/cors/.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Setup go 16 | uses: actions/setup-go@v2 17 | with: 18 | go-version: '^1.16' 19 | - name: Checkout repository 20 | uses: actions/checkout@v2 21 | - name: Setup golangci-lint 22 | uses: golangci/golangci-lint-action@v2 23 | with: 24 | version: v1.42.1 25 | args: --verbose 26 | test: 27 | strategy: 28 | matrix: 29 | os: [ubuntu-latest, macos-latest] 30 | go: [1.13, 1.14, 1.15, 1.16, 1.17] 31 | include: 32 | - os: ubuntu-latest 33 | go-build: ~/.cache/go-build 34 | - os: macos-latest 35 | go-build: ~/Library/Caches/go-build 36 | name: ${{ matrix.os }} @ Go ${{ matrix.go }} 37 | runs-on: ${{ matrix.os }} 38 | env: 39 | GO111MODULE: on 40 | GOPROXY: https://proxy.golang.org 41 | steps: 42 | - name: Set up Go ${{ matrix.go }} 43 | uses: actions/setup-go@v2 44 | with: 45 | go-version: ${{ matrix.go }} 46 | 47 | - name: Checkout Code 48 | uses: actions/checkout@v2 49 | with: 50 | ref: ${{ github.ref }} 51 | 52 | - uses: actions/cache@v2 53 | with: 54 | path: | 55 | ${{ matrix.go-build }} 56 | ~/go/pkg/mod 57 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 58 | restore-keys: | 59 | ${{ runner.os }}-go- 60 | - name: Run Tests 61 | run: | 62 | go test -v -covermode=atomic -coverprofile=coverage.out 63 | 64 | - name: Upload coverage to Codecov 65 | uses: codecov/codecov-action@v2 66 | with: 67 | flags: ${{ matrix.os }},go-${{ matrix.go }} 68 | -------------------------------------------------------------------------------- /app/http/middleware/cors/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | 5 | _obj 6 | _test 7 | 8 | *.[568vq] 9 | [568vq].out 10 | 11 | *.cgo1.go 12 | *.cgo2.c 13 | _cgo_defun.c 14 | _cgo_gotypes.go 15 | _cgo_export.* 16 | 17 | _testmain.go 18 | 19 | *.exe 20 | *.test 21 | *.prof 22 | 23 | coverage.out 24 | -------------------------------------------------------------------------------- /app/http/middleware/cors/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Gin-Gonic 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 | -------------------------------------------------------------------------------- /app/http/middleware/cors/examples/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-contrib/cors" 7 | "github.com/gohade/hade/framework/gin" 8 | ) 9 | 10 | func main() { 11 | router := gin.Default() 12 | // CORS for https://foo.com and https://github.com origins, allowing: 13 | // - PUT and PATCH methods 14 | // - Origin header 15 | // - Credentials share 16 | // - Preflight requests cached for 12 hours 17 | router.Use(cors.New(cors.Config{ 18 | AllowOrigins: []string{"https://foo.com"}, 19 | AllowMethods: []string{"PUT", "PATCH"}, 20 | AllowHeaders: []string{"Origin"}, 21 | ExposeHeaders: []string{"Content-Length"}, 22 | AllowCredentials: true, 23 | AllowOriginFunc: func(origin string) bool { 24 | return origin == "https://github.com" 25 | }, 26 | MaxAge: 12 * time.Hour, 27 | })) 28 | router.Run() 29 | } 30 | -------------------------------------------------------------------------------- /app/http/module/demo/api.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | demoService "github.com/gohade/hade/app/provider/demo" 5 | "github.com/gohade/hade/framework/gin" 6 | ) 7 | 8 | type DemoApi struct { 9 | service *Service 10 | } 11 | 12 | func Register(r *gin.Engine) error { 13 | api := NewDemoApi() 14 | r.Bind(&demoService.DemoProvider{}) 15 | 16 | r.GET("/demo/demo", api.Demo) 17 | r.GET("/demo/demo2", api.Demo2) 18 | r.POST("/demo/demo_post", api.DemoPost) 19 | r.GET("/demo/orm", api.DemoOrm) 20 | r.GET("/demo/cache/redis", api.DemoRedis) 21 | return nil 22 | } 23 | 24 | func NewDemoApi() *DemoApi { 25 | service := NewService() 26 | return &DemoApi{service: service} 27 | } 28 | 29 | // Demo godoc 30 | // @Summary 获取所有用户 31 | // @Description 获取所有用户 32 | // @Produce json 33 | // @Tags demo 34 | // @Success 200 array []UserDTO 35 | // @Router /demo/demo [get] 36 | func (api *DemoApi) Demo(c *gin.Context) { 37 | c.JSON(200, "this is demo for dev all") 38 | } 39 | 40 | // Demo2 for godoc 41 | // @Summary 获取所有学生 42 | // @Description 获取所有学生,不进行分页 43 | // @Produce json 44 | // @Tags demo 45 | // @Success 200 {array} UserDTO 46 | // @Router /demo/demo2 [get] 47 | func (api *DemoApi) Demo2(c *gin.Context) { 48 | demoProvider := c.MustMake(demoService.DemoKey).(demoService.IService) 49 | students := demoProvider.GetAllStudent() 50 | usersDTO := StudentsToUserDTOs(students) 51 | c.JSON(200, usersDTO) 52 | } 53 | 54 | func (api *DemoApi) DemoPost(c *gin.Context) { 55 | type Foo struct { 56 | Name string 57 | } 58 | foo := &Foo{} 59 | err := c.BindJSON(&foo) 60 | if err != nil { 61 | c.AbortWithError(500, err) 62 | } 63 | c.JSON(200, nil) 64 | } 65 | -------------------------------------------------------------------------------- /app/http/module/demo/api_cache.go: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/contract" 5 | "github.com/gohade/hade/framework/gin" 6 | "github.com/gohade/hade/framework/provider/redis" 7 | "time" 8 | ) 9 | 10 | // DemoRedis redis的路由方法 11 | func (api *DemoApi) DemoRedis(c *gin.Context) { 12 | logger := c.MustMakeLog() 13 | logger.Info(c, "request start", nil) 14 | 15 | // 初始化一个redis 16 | redisService := c.MustMake(contract.RedisKey).(contract.RedisService) 17 | client, err := redisService.GetClient(redis.WithConfigPath("cache.default"), redis.WithRedisConfig(func(options *contract.RedisConfig) { 18 | options.MaxRetries = 3 19 | })) 20 | if err != nil { 21 | logger.Error(c, err.Error(), nil) 22 | c.AbortWithError(50001, err) 23 | return 24 | } 25 | if err := client.Set(c, "foo", "bar", 1*time.Hour).Err(); err != nil { 26 | c.AbortWithError(500, err) 27 | return 28 | } 29 | val := client.Get(c, "foo").String() 30 | logger.Info(c, "redis get", map[string]interface{}{ 31 | "val": val, 32 | }) 33 | 34 | if err := client.Del(c, "foo").Err(); err != nil { 35 | c.AbortWithError(500, err) 36 | return 37 | } 38 | 39 | c.JSON(200, "ok") 40 | } 41 | 42 | // DemoCache cache的简单例子 43 | func (api *DemoApi) DemoCache(c *gin.Context) { 44 | logger := c.MustMakeLog() 45 | logger.Info(c, "request start", nil) 46 | // 初始化cache服务 47 | cacheService := c.MustMake(contract.CacheKey).(contract.CacheService) 48 | // 设置key为foo 49 | err := cacheService.Set(c, "foo", "bar", 1*time.Hour) 50 | if err != nil { 51 | c.AbortWithError(500, err) 52 | return 53 | } 54 | // 获取key为foo 55 | val, err := cacheService.Get(c, "foo") 56 | if err != nil { 57 | c.AbortWithError(500, err) 58 | return 59 | } 60 | logger.Info(c, "cache get", map[string]interface{}{ 61 | "val": val, 62 | }) 63 | // 删除key为foo 64 | if err := cacheService.Del(c, "foo"); err != nil { 65 | c.AbortWithError(500, err) 66 | return 67 | } 68 | c.JSON(200, "ok") 69 | } 70 | -------------------------------------------------------------------------------- /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 "github.com/gohade/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 | import ( 4 | "database/sql" 5 | "time" 6 | ) 7 | 8 | type UserModel struct { 9 | UserId int 10 | Name string 11 | Age int 12 | } 13 | 14 | // User is gorm model 15 | type User struct { 16 | ID uint 17 | Name string 18 | Email *string 19 | Age uint8 20 | Birthday *time.Time 21 | MemberNumber sql.NullString 22 | ActivatedAt sql.NullTime 23 | CreatedAt time.Time 24 | UpdatedAt time.Time 25 | } 26 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/hade/app/http/middleware/cors" 5 | "github.com/gohade/hade/app/http/module/demo" 6 | "github.com/gohade/hade/framework/contract" 7 | "github.com/gohade/hade/framework/gin" 8 | ginSwagger "github.com/gohade/hade/framework/middleware/gin-swagger" 9 | "github.com/gohade/hade/framework/middleware/gin-swagger/swaggerFiles" 10 | "github.com/gohade/hade/framework/middleware/static" 11 | ) 12 | 13 | // Routes 绑定业务层路由 14 | func Routes(r *gin.Engine) { 15 | container := r.GetContainer() 16 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 17 | 18 | // /路径先去./dist目录下查找文件是否存在,找到使用文件服务提供服务 19 | r.Use(static.Serve("/", static.LocalFile("./dist", false))) 20 | // 使用cors中间件 21 | r.Use(cors.Default()) 22 | 23 | // 如果配置了swagger,则显示swagger的中间件 24 | if configService.GetBool("app.swagger") == true { 25 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 26 | } 27 | 28 | // 动态路由定义 29 | demo.Register(r) 30 | } 31 | -------------------------------------------------------------------------------- /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 yejianfeng1 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 | // @BasePath / 14 | // @query.collection.format multi 15 | 16 | // @securityDefinitions.basic BasicAuth 17 | 18 | // @securityDefinitions.apikey ApiKeyAuth 19 | // @in header 20 | // @name Authorization 21 | 22 | // @x-extension-openapi {"example": "value on a json format"} 23 | 24 | package http 25 | 26 | import ( 27 | _ "github.com/gohade/hade/app/http/swagger" 28 | ) 29 | -------------------------------------------------------------------------------- /app/http/swagger/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: / 2 | definitions: 3 | demo.UserDTO: 4 | properties: 5 | id: 6 | type: integer 7 | name: 8 | type: string 9 | type: object 10 | info: 11 | contact: 12 | email: yejianfeng 13 | name: yejianfeng1 14 | description: hade测试 15 | license: 16 | name: Apache 2.0 17 | url: http://www.apache.org/licenses/LICENSE-2.0.html 18 | termsOfService: https://github.com/swaggo/swag 19 | title: hade 20 | version: "1.1" 21 | paths: 22 | /demo/demo: 23 | get: 24 | description: 获取所有用户 25 | produces: 26 | - application/json 27 | responses: 28 | "200": 29 | description: OK 30 | schema: 31 | items: 32 | items: 33 | $ref: '#/definitions/demo.UserDTO' 34 | type: array 35 | type: array 36 | summary: 获取所有用户 37 | tags: 38 | - demo 39 | /demo/demo2: 40 | get: 41 | description: 获取所有学生,不进行分页 42 | produces: 43 | - application/json 44 | responses: 45 | "200": 46 | description: OK 47 | schema: 48 | items: 49 | $ref: '#/definitions/demo.UserDTO' 50 | type: array 51 | summary: 获取所有学生 52 | tags: 53 | - demo 54 | securityDefinitions: 55 | ApiKeyAuth: 56 | in: header 57 | name: Authorization 58 | type: apiKey 59 | BasicAuth: 60 | type: basic 61 | swagger: "2.0" 62 | x-extension-openapi: 63 | example: value on a json format 64 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/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(c framework.Container) []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 "github.com/gohade/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/user/contract.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | const UserKey = "user" 4 | 5 | type Service interface { 6 | // 请在这里定义你的方法 7 | Foo() string 8 | } 9 | -------------------------------------------------------------------------------- /app/provider/user/provider.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | ) 6 | 7 | type UserProvider struct { 8 | framework.ServiceProvider 9 | 10 | c framework.Container 11 | } 12 | 13 | func (sp *UserProvider) Name() string { 14 | return UserKey 15 | } 16 | 17 | func (sp *UserProvider) Register(c framework.Container) framework.NewInstance { 18 | return NewUserService 19 | } 20 | 21 | func (sp *UserProvider) IsDefer() bool { 22 | return false 23 | } 24 | 25 | func (sp *UserProvider) Params(c framework.Container) []interface{} { 26 | return []interface{}{c} 27 | } 28 | 29 | func (sp *UserProvider) Boot(c framework.Container) error { 30 | return nil 31 | } 32 | 33 | -------------------------------------------------------------------------------- /app/provider/user/service.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "github.com/gohade/hade/framework" 4 | 5 | type UserService struct { 6 | container framework.Container 7 | } 8 | 9 | func NewUserService(params ...interface{}) (interface{}, error) { 10 | container := params[0].(framework.Container) 11 | return &UserService{container: container}, nil 12 | } 13 | 14 | func (s *UserService) Foo() string { 15 | return "" 16 | } 17 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/build/logo.png -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/development/app.yaml: -------------------------------------------------------------------------------- 1 | swagger: true 2 | 3 | path: 4 | # log_folder: "/home/jianfengye/hade/log/" 5 | # runtime_folder: "/home/jianfengye/hade/runtime/" 6 | 7 | dev: # 调试模式 8 | port: 8070 # 调试模式最终监听的端口,默认为8070 9 | backend: # 后端调试模式配置 10 | refresh_time: 3 # 调试模式后端更新时间,如果文件变更,等待3s才进行一次更新,能让频繁保存变更更为顺畅, 默认1s 11 | port: 8072 # 后端监听端口,默认8072 12 | monitor_folder: "" # 监听文件夹地址,为空或者不填默认为AppFolder 13 | frontend: # 前端调试模式配置 14 | port: 8071 # 前端监听端口, 默认8071 15 | -------------------------------------------------------------------------------- /config/development/database.yaml: -------------------------------------------------------------------------------- 1 | conn_max_idle: 10 # 通用配置,连接池最大空闲连接数 2 | conn_max_open: 100 # 通用配置,连接池最大连接数 3 | conn_max_lifetime: 1h # 通用配置,连接数最大生命周期 4 | protocol: tcp # 通用配置,传输协议 5 | loc: Local # 通用配置,时区 6 | 7 | default: 8 | driver: mysql # 连接驱动 9 | dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效 10 | host: localhost # ip地址 11 | port: 3306 # 端口 12 | database: coredemo # 数据库 13 | username: jianfengye # 用户名 14 | password: "123456789" # 密码 15 | charset: utf8mb4 # 字符集 16 | collation: utf8mb4_unicode_ci # 字符序 17 | timeout: 10s # 连接超时 18 | read_timeout: 2s # 读超时 19 | write_timeout: 2s # 写超时 20 | parse_time: true # 是否解析时间 21 | protocol: tcp # 传输协议 22 | loc: Local # 时区 23 | -------------------------------------------------------------------------------- /config/development/deploy.yaml: -------------------------------------------------------------------------------- 1 | connections: # 要自动化部署的连接 2 | - ssh.web-01 3 | 4 | remote_folder: "/home/yejianfeng/coredemo/" # 远端的部署文件夹 5 | 6 | frontend: # 前端部署配置 7 | pre_action: # 部署前置命令 8 | - "pwd" 9 | post_action: # 部署后置命令 10 | - "pwd" 11 | 12 | backend: # 后端部署配置 13 | goos: linux # 部署目标操作系统 14 | goarch: amd64 # 部署目标cpu架构 15 | pre_action: # 部署前置命令 16 | - "rm /home/yejianfeng/coredemo/hade" 17 | post_action: # 部署后置命令 18 | - "chmod 777 /home/yejianfeng/coredemo/hade" 19 | - "/home/yejianfeng/coredemo/hade app restart" 20 | -------------------------------------------------------------------------------- /config/development/log.yaml: -------------------------------------------------------------------------------- 1 | #driver: console 2 | #formatter: text 3 | #level: trace 4 | 5 | #driver: single 6 | #level: trace 7 | #folder: /tmp/ 8 | #file: coredemo.log 9 | 10 | driver: rotate # 切割日志 11 | level: trace # 日志级别 12 | #file: coredemo.log # 保存的日志文件 13 | rotate_count: 10 # 最多日志文件个数 14 | #rotate_size: 120000 15 | #rotate_time: "1m" 16 | max_age: "10d" # 文件保存时间 17 | date_format: "%Y-%m-%d-%H-%M" # 文件后缀格式 18 | -------------------------------------------------------------------------------- /config/development/ssh.yaml: -------------------------------------------------------------------------------- 1 | timeout: 3s 2 | network: tcp 3 | web-01: 4 | host: 111.222.333.444 # ip地址 5 | port: 22 # 端口 6 | username: yejianfeng # 用户名 7 | password: "123456" # 密码 8 | web-02: 9 | network: tcp 10 | host: localhost # ip地址 11 | port: 3306 # 端口 12 | username: jianfengye # 用户名 13 | rsa_key: "/Users/user/.ssh/id_rsa" 14 | known_hosts: "/Users/user/.ssh/known_hosts" 15 | 16 | 17 | #host: localhost # ip地址 18 | #port: 3306 # 端口 19 | #username: jianfengye # 用户名 20 | #password: "123456789" # 密码 21 | #rsa_key: "~/.ssh/id_rsa" 22 | #timeout: 1000 23 | #network: tcp 24 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /config/production/.gitkeeper: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/config/production/.gitkeeper -------------------------------------------------------------------------------- /config/production/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/production/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/production/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/production/gift.yaml: -------------------------------------------------------------------------------- 1 | endpoint: "" 2 | access_key: "" 3 | secret_key: "" 4 | use_ssl: true -------------------------------------------------------------------------------- /config/production/log.yaml: -------------------------------------------------------------------------------- 1 | driver: console 2 | level: trace -------------------------------------------------------------------------------- /config/production/swagger.yaml: -------------------------------------------------------------------------------- 1 | url: http://127.0.0.1:8069 -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/testing/app.yaml: -------------------------------------------------------------------------------- 1 | swagger: true 2 | 3 | path: 4 | # log_folder: "/home/jianfengye/hade/log/" 5 | # runtime_folder: "/home/jianfengye/hade/runtime/" 6 | 7 | dev: # 调试模式 8 | port: 8070 # 调试模式最终监听的端口,默认为8070 9 | backend: # 后端调试模式配置 10 | refresh_time: 3 # 调试模式后端更新时间,如果文件变更,等待3s才进行一次更新,能让频繁保存变更更为顺畅, 默认1s 11 | port: 8072 # 后端监听端口,默认8072 12 | monitor_folder: "" # 监听文件夹地址,为空或者不填默认为AppFolder 13 | frontend: # 前端调试模式配置 14 | port: 8071 # 前端监听端口, 默认8071 15 | -------------------------------------------------------------------------------- /config/testing/cache.yaml: -------------------------------------------------------------------------------- 1 | driver: redis # 连接驱动 2 | host: 127.0.0.1 # ip地址 3 | port: 6379 # 端口 4 | db: 0 #db 5 | timeout: 10s # 连接超时 6 | read_timeout: 2s # 读超时 7 | write_timeout: 2s # 写超时 8 | 9 | #driver: memory # 连接驱动 10 | -------------------------------------------------------------------------------- /config/testing/database.yaml: -------------------------------------------------------------------------------- 1 | conn_max_idle: 10 # 通用配置,连接池最大空闲连接数 2 | conn_max_open: 100 # 通用配置,连接池最大连接数 3 | conn_max_lifetime: 1h # 通用配置,连接数最大生命周期 4 | protocol: tcp # 通用配置,传输协议 5 | loc: Local # 通用配置,时区 6 | 7 | default: 8 | driver: mysql # 连接驱动 9 | dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效 10 | host: localhost # ip地址 11 | port: 3306 # 端口 12 | database: coredemo # 数据库 13 | username: jianfengye # 用户名 14 | password: "123456789" # 密码 15 | charset: utf8mb4 # 字符集 16 | collation: utf8mb4_unicode_ci # 字符序 17 | timeout: 10s # 连接超时 18 | read_timeout: 2s # 读超时 19 | write_timeout: 2s # 写超时 20 | parse_time: true # 是否解析时间 21 | protocol: tcp # 传输协议 22 | loc: Local # 时区 23 | conn_max_idle: 10 # 连接池最大空闲连接数 24 | conn_max_open: 20 # 连接池最大连接数 25 | conn_max_lifetime: 1h # 连接数最大生命周期 26 | 27 | read: 28 | driver: mysql # 连接驱动 29 | dsn: "" # dsn,如果设置了dsn, 以下的所有设置都不生效 30 | host: localhost # ip地址 31 | port: 3306 # 端口 32 | database: coredemo # 数据库 33 | username: jianfengye # 用户名 34 | password: "123456789" # 密码 35 | charset: utf8mb4 # 字符集 36 | collation: utf8mb4_unicode_ci # 字符序 37 | -------------------------------------------------------------------------------- /config/testing/deploy.yaml: -------------------------------------------------------------------------------- 1 | connections: 2 | - ssh.web-01 3 | - ssh.web-02 4 | 5 | remote_path: "/home/jianfengye/hade" 6 | 7 | frontend: 8 | pre_action: 9 | - "pwd" 10 | post_action: 11 | - "pwd" 12 | 13 | backend: 14 | goos: linux 15 | goarch: amd64 16 | pre_action: 17 | - "pwd" 18 | post_action: 19 | - "./hade serve restart" 20 | -------------------------------------------------------------------------------- /config/testing/log.yaml: -------------------------------------------------------------------------------- 1 | #driver: console 2 | #formatter: text 3 | #level: trace 4 | 5 | #driver: single 6 | #level: trace 7 | #folder: /tmp/ 8 | #file: coredemo.log 9 | 10 | driver: rotate # 切割日志 11 | level: trace # 日志级别 12 | #file: coredemo.log # 保存的日志文件 13 | rotate_count: 10 # 最多日志文件个数 14 | #rotate_size: 120000 15 | #rotate_time: "1m" 16 | max_age: "10d" # 文件保存时间 17 | date_format: "%Y-%m-%d-%H-%M" # 文件后缀格式 18 | -------------------------------------------------------------------------------- /config/testing/redis.yaml: -------------------------------------------------------------------------------- 1 | timeout: 10s # 连接超时 2 | read_timeout: 2s # 读超时 3 | write_timeout: 2s # 写超时 4 | 5 | write: 6 | host: 127.0.0.1 # ip地址 7 | port: 6379 # 端口 8 | db: 0 #db 9 | timeout: 10s # 连接超时 10 | read_timeout: 2s # 读超时 11 | write_timeout: 2s # 写超时 12 | -------------------------------------------------------------------------------- /config/testing/ssh.yaml: -------------------------------------------------------------------------------- 1 | timeout: 1s 2 | network: tcp 3 | web-01: 4 | host: 118.190.3.55 # ip地址 5 | port: 22 # 端口 6 | username: yejianfeng # 用户名 7 | password: "123456" # 密码 8 | web-02: 9 | network: tcp 10 | host: localhost # ip地址 11 | port: 3306 # 端口 12 | username: jianfengye # 用户名 13 | rsa_key: "/Users/user/.ssh/id_rsa" 14 | known_hosts: "/Users/user/.ssh/known_hosts" 15 | 16 | 17 | #host: localhost # ip地址 18 | #port: 3306 # 端口 19 | #username: jianfengye # 用户名 20 | #password: "123456789" # 密码 21 | #rsa_key: "~/.ssh/id_rsa" 22 | #timeout: 1000 23 | #network: tcp 24 | -------------------------------------------------------------------------------- /config/testing/swagger.yaml: -------------------------------------------------------------------------------- 1 | url: http://127.0.0.1:8069 -------------------------------------------------------------------------------- /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/app.md: -------------------------------------------------------------------------------- 1 | # 运行 2 | 3 | ## 命令 4 | 5 | 这里的运行是运行整个 app,这个 app 可以只包含后端,也可以只包含前端,但是后端也是隐藏在前端后面运行。具体可以参考 app/http/route.go 6 | 7 | ``` 8 | package http 9 | 10 | import ( 11 | "github.com/gohade/hade/app/http/controller/demo" 12 | "github.com/gohade/hade/framework/gin" 13 | ) 14 | 15 | func Routes(r *gin.Engine) { 16 | r.Static("/dist/", "./dist/") 17 | r.GET("/demo/demo", demo.Demo) 18 | } 19 | 20 | ``` 21 | 22 | 运行相关的命令为 app。 23 | 24 | ``` 25 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade app 26 | start app serve 27 | 28 | Usage: 29 | hade app [flags] 30 | hade app [command] 31 | 32 | Available Commands: 33 | restart restart app server 34 | start start app server 35 | state get app pid 36 | stop stop app server 37 | 38 | Flags: 39 | -h, --help help for app 40 | 41 | Use "hade app [command] --help" for more information about a command. 42 | ``` 43 | 44 | ## 启动 45 | 46 | 可以使用 `./hade app start` 启动一个应用。 47 | 48 | 也可以使用 `./hade app start -d` 使用 deamon 模式启动一个应用。应用名称为 `hade app` 49 | 50 | ``` 51 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade app start -d 52 | app serve started 53 | log file: /Users/Documents/workspace/hade_workspace/demo5/storage/log/app.log 54 | ``` 55 | 56 | app 应用的输出记录在 `/storage/log/app.log` 57 | 58 | 进程 id 记录在 `/storage/pid/app.pid` 59 | 60 | ## 状态 61 | 62 | 当使用 deamon 模式启动的时候,需要查看当前应用是否有启动,如果启动了,进程号是多少,可以使用命令 `./hade app state` 63 | 64 | ``` 65 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade app state 66 | app server started, pid: 28170 67 | ``` 68 | 69 | ## 重启 70 | 71 | 当使用 deamon 模式启动的时候,需要重启应用,可以使用命令 `./hade app restart` 72 | 73 | ::: tip 74 | 如果程序还未启动,调用 restart 命令,效果和 start 命令一样,deamon 模式启动应用 75 | ::: 76 | 77 | ## 停止 78 | 79 | 当使用 deamon 模式启动的时候,需要关闭应用,可以使用命令 `./hade app stop` 80 | -------------------------------------------------------------------------------- /docs/guide/cron.md: -------------------------------------------------------------------------------- 1 | # 定时任务 2 | 3 | ## 指南 4 | 5 | hade 中的定时任务是以命令的形式存在。hade 中也定义了一个命令 `./hade cron` 来对定时任务服务进行管理。 6 | 7 | ``` 8 | about cron command 9 | 10 | Usage: 11 | hade cron [flags] 12 | hade cron [command] 13 | 14 | Available Commands: 15 | list list all cron command 16 | restart restart cron command 17 | start start cron command 18 | state cron serve state 19 | stop stop cron command 20 | 21 | Flags: 22 | -h, --help help for cron 23 | 24 | Use "hade cron [command] --help" for more information about a command. 25 | ``` 26 | 27 | # 创建 28 | 29 | 创建一个定时任务和创建命令(command)是一致的。具体参考[command](/guide/command) 30 | 31 | # 挂载 32 | 33 | 和挂载命令稍微有些不同,使用的方法是 `AddCronCommand` 34 | 35 | ``` 36 | rootCmd.AddCronCommand("* * * * *", command.DemoCommand) 37 | ``` 38 | 39 | # 查询 40 | 41 | 查询哪些定时任务挂载在服务上,使用命令 `./hade cron list` 42 | 43 | ``` 44 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade cron list 45 | * * * * * demo demo 46 | ``` 47 | 48 | # 启动 49 | 50 | 使用命令 `./hade cron start` 启动一个定时服务 51 | ``` 52 | [~/Documents/workspace/hade_workspace/demo5]$ ./hade cron start 53 | start cron job 54 | [PID] 35453 55 | ``` 56 | 57 | 也可以通过 `./hade cron start -d` 使用 deamon 模式启动一个定时服务 58 | 59 | 定时服务的输出记录在 `/storage/log/cron.log` 60 | 61 | 进程 id 记录在 `/storage/pid/app.pid` 62 | 63 | # 状态 64 | 65 | 使用 deamon 模式启动定时服务的时候,可以使用命令 `./hade cron state` 查询定时任务状态 66 | 67 | # 停止 68 | 69 | 使用 deamon 模式启动定时服务的时候,可以使用命令 `./hade cron stop` 停止定时任务 70 | 71 | # 重启 72 | 73 | 使用 deamon 模式启动定时服务的时候,可以使用命令 `./hade cron restart` 重启定时任务 74 | 75 | 76 | ::: tip 77 | 如果程序还未启动,调用 restart 命令,效果和 start 命令一样,deamon 模式启动定时服务 78 | ::: 79 | 80 | 81 | -------------------------------------------------------------------------------- /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/install.md: -------------------------------------------------------------------------------- 1 | # 安装 2 | 3 | --- 4 | ## 可执行文件 5 | 6 | 我们有两种方式来获取可执行的hade文件,第一种是直接下载对应操作系统的hade文件,另外一种是下载源码自己编译 7 | 8 | ### 直接下载 9 | 10 | 下载地址: 11 | xxx 12 | 13 | 将生成的可执行文件 hade 放到 $PATH 目录中: 14 | `cp hade /usr/local/bin/` 15 | 16 | ### 源码编译 17 | 18 | 下载 git 地址:`git@github.com/jianfengye/hade:cloud/hade.git` 到目录 hade 19 | 20 | 在 hade 目录中运行命令 `go run main.go build self` 21 | 22 | 将生成的可执行文件 hade 放到 $PATH 目录中: 23 | `cp hade /usr/local/bin/` 24 | 25 | 26 | ## 初始化项目 27 | 28 | 使用命令 `hade new [app]` 在当前目录创建子项目 29 | 30 | ``` 31 | [~/Documents/workspace/hade_workspace]$ hade new --help 32 | create a new app 33 | 34 | Usage: 35 | hade new [app] [flags] 36 | 37 | Aliases: 38 | new, create, init 39 | 40 | Flags: 41 | -f, --force if app exist, overwrite app, default: false 42 | -h, --help help for new 43 | -m, --mod string go mod name, default: folder name 44 | ``` 45 | 46 | 你可以通过 --mod 来定义项目名字的模块名字,默认项目名,目录名,模块名是相同的 47 | 48 | 接下来,可以通过命令 `go run main.go` 看到如下信息: 49 | 50 | ``` 51 | [~/Documents/workspace/hade_workspace/demo5]$ go run main.go 52 | hade commands 53 | 54 | Usage: 55 | hade [command] 56 | 57 | Available Commands: 58 | app start app serve 59 | build build hade command 60 | command all about commond 61 | cron about cron command 62 | deploy deploy app by ssh 63 | dev dev mode 64 | env get current environment 65 | help get help info 66 | middleware hade middleware 67 | new create a new app 68 | provider about hade service provider 69 | swagger swagger operator 70 | 71 | Flags: 72 | -h, --help help for hade 73 | 74 | Use "hade [command] --help" for more information about a command. 75 | ``` 76 | 77 | 至此,项目安装成功。 78 | -------------------------------------------------------------------------------- /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/config.md: -------------------------------------------------------------------------------- 1 | # hade:config 2 | 3 | 提供基础的配置文件获取方法 4 | 5 | ``` golang 6 | package contract 7 | 8 | import "time" 9 | 10 | const ( 11 | // ConfigKey is config key in container 12 | ConfigKey = "hade:config" 13 | ) 14 | 15 | // Config define setting from files, it support key contains dov。 16 | // for example: 17 | // .Get("user.name") 18 | // suggest use yaml format, https://yaml.org/spec/1.2/spec.html 19 | type Config interface { 20 | // IsExist check setting is exist 21 | IsExist(key string) bool 22 | 23 | // Get a new interface 24 | Get(key string) interface{} 25 | // GetBool get bool type 26 | GetBool(key string) bool 27 | // GetInt get Int type 28 | GetInt(key string) int 29 | // GetFloat64 get float64 30 | GetFloat64(key string) float64 31 | // GetTime get time type 32 | GetTime(key string) time.Time 33 | // GetString get string typen 34 | GetString(key string) string 35 | 36 | // GetIntSlice get int slice type 37 | GetIntSlice(key string) []int 38 | // GetStringSlice get string slice type 39 | GetStringSlice(key string) []string 40 | 41 | // GetStringMap get map which key is string, value is interface 42 | GetStringMap(key string) map[string]interface{} 43 | // GetStringMapString get map which key is string, value is string 44 | GetStringMapString(key string) map[string]string 45 | // GetStringMapStringSlice get map which key is string, value is string slice 46 | GetStringMapStringSlice(key string) map[string][]string 47 | 48 | // Load a config to a struct, val should be an pointer 49 | Load(key string, val interface{}) error 50 | } 51 | 52 | ``` -------------------------------------------------------------------------------- /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/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | # Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore 23 | # swap 24 | [._]*.s[a-w][a-z] 25 | [._]s[a-w][a-z] 26 | # session 27 | Session.vim 28 | # temporary 29 | .netrwhist 30 | *~ 31 | # auto-generated tag files 32 | tags 33 | 34 | *.exe 35 | cobra.test 36 | bin 37 | 38 | .idea/ 39 | *.iml 40 | -------------------------------------------------------------------------------- /framework/cobra/.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | deadline: 5m 3 | 4 | linters: 5 | disable-all: true 6 | enable: 7 | #- bodyclose 8 | - deadcode 9 | #- depguard 10 | #- dogsled 11 | #- dupl 12 | - errcheck 13 | #- exhaustive 14 | #- funlen 15 | - gas 16 | #- gochecknoinits 17 | - goconst 18 | #- gocritic 19 | #- gocyclo 20 | #- gofmt 21 | - goimports 22 | - golint 23 | #- gomnd 24 | #- goprintffuncname 25 | #- gosec 26 | #- gosimple 27 | - govet 28 | - ineffassign 29 | - interfacer 30 | #- lll 31 | - maligned 32 | - megacheck 33 | #- misspell 34 | #- nakedret 35 | #- noctx 36 | #- nolintlint 37 | #- rowserrcheck 38 | #- scopelint 39 | #- staticcheck 40 | - structcheck 41 | #- stylecheck 42 | #- typecheck 43 | - unconvert 44 | #- unparam 45 | #- unused 46 | - varcheck 47 | #- whitespace 48 | fast: false 49 | -------------------------------------------------------------------------------- /framework/cobra/.mailmap: -------------------------------------------------------------------------------- 1 | Steve Francia 2 | Bjørn Erik Pedersen 3 | Fabiano Franz 4 | -------------------------------------------------------------------------------- /framework/cobra/Makefile: -------------------------------------------------------------------------------- 1 | BIN="./bin" 2 | SRC=$(shell find . -name "*.go") 3 | 4 | ifeq (, $(shell which golangci-lint)) 5 | $(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh") 6 | endif 7 | 8 | ifeq (, $(shell which richgo)) 9 | $(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo") 10 | endif 11 | 12 | .PHONY: fmt lint test cobra_generator install_deps clean 13 | 14 | default: all 15 | 16 | all: fmt test cobra_generator 17 | 18 | fmt: 19 | $(info ******************** checking formatting ********************) 20 | @test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1) 21 | 22 | lint: 23 | $(info ******************** running lint tools ********************) 24 | golangci-lint run -v 25 | 26 | test: install_deps lint 27 | $(info ******************** running tests ********************) 28 | richgo test -v ./... 29 | 30 | cobra_generator: install_deps 31 | $(info ******************** building generator ********************) 32 | mkdir -p $(BIN) 33 | make -C cobra all 34 | 35 | install_deps: 36 | $(info ******************** downloading dependencies ********************) 37 | go get -v ./... 38 | 39 | clean: 40 | rm -rf $(BIN) 41 | -------------------------------------------------------------------------------- /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 | assertNoErr(t, command.Project.Create()) 18 | assertNoErr(t, command.Create()) 19 | 20 | generatedFile := fmt.Sprintf("%s/cmd/%s.go", command.AbsolutePath, command.CmdName) 21 | goldenFile := fmt.Sprintf("testdata/%s.go.golden", command.CmdName) 22 | err := compareFiles(generatedFile, goldenFile) 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | } 27 | 28 | func TestValidateCmdName(t *testing.T) { 29 | testCases := []struct { 30 | input string 31 | expected string 32 | }{ 33 | {"cmdName", "cmdName"}, 34 | {"cmd_name", "cmdName"}, 35 | {"cmd-name", "cmdName"}, 36 | {"cmd______Name", "cmdName"}, 37 | {"cmd------Name", "cmdName"}, 38 | {"cmd______name", "cmdName"}, 39 | {"cmd------name", "cmdName"}, 40 | {"cmdName-----", "cmdName"}, 41 | {"cmdname-", "cmdname"}, 42 | } 43 | 44 | for _, testCase := range testCases { 45 | got := validateCmdName(testCase.input) 46 | if testCase.expected != got { 47 | t.Errorf("Expected %q, got %q", testCase.expected, got) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/golden_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os/exec" 9 | ) 10 | 11 | func init() { 12 | // Mute commands. 13 | addCmd.SetOut(new(bytes.Buffer)) 14 | addCmd.SetErr(new(bytes.Buffer)) 15 | initCmd.SetOut(new(bytes.Buffer)) 16 | initCmd.SetErr(new(bytes.Buffer)) 17 | } 18 | 19 | // ensureLF converts any \r\n to \n 20 | func ensureLF(content []byte) []byte { 21 | return bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) 22 | } 23 | 24 | // compareFiles compares the content of files with pathA and pathB. 25 | // If contents are equal, it returns nil. 26 | // If not, it returns which files are not equal 27 | // and diff (if system has diff command) between these files. 28 | func compareFiles(pathA, pathB string) error { 29 | contentA, err := ioutil.ReadFile(pathA) 30 | if err != nil { 31 | return err 32 | } 33 | contentB, err := ioutil.ReadFile(pathB) 34 | if err != nil { 35 | return err 36 | } 37 | if !bytes.Equal(ensureLF(contentA), ensureLF(contentB)) { 38 | output := new(bytes.Buffer) 39 | output.WriteString(fmt.Sprintf("%q and %q are not equal!\n\n", pathA, pathB)) 40 | 41 | diffPath, err := exec.LookPath("diff") 42 | if err != nil { 43 | // Don't execute diff if it can't be found. 44 | return nil 45 | } 46 | diffCmd := exec.Command(diffPath, "-u", pathA, pathB) 47 | diffCmd.Stdout = output 48 | diffCmd.Stderr = output 49 | 50 | output.WriteString("$ diff -u " + pathA + " " + pathB + "\n") 51 | if err := diffCmd.Run(); err != nil { 52 | output.WriteString("\n" + err.Error()) 53 | } 54 | return errors.New(output.String()) 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/helpers.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 cmd 15 | 16 | import ( 17 | "os" 18 | "os/exec" 19 | "path/filepath" 20 | "strings" 21 | 22 | "github.com/spf13/cobra" 23 | ) 24 | 25 | var srcPaths []string 26 | 27 | func init() { 28 | // Initialize srcPaths. 29 | envGoPath := os.Getenv("GOPATH") 30 | goPaths := filepath.SplitList(envGoPath) 31 | if len(goPaths) == 0 { 32 | // Adapted from https://github.com/Masterminds/glide/pull/798/files. 33 | // As of Go 1.8 the GOPATH is no longer required to be set. Instead there 34 | // is a default value. If there is no GOPATH check for the default value. 35 | // Note, checking the GOPATH first to avoid invoking the go toolchain if 36 | // possible. 37 | 38 | goExecutable := os.Getenv("COBRA_GO_EXECUTABLE") 39 | if len(goExecutable) <= 0 { 40 | goExecutable = "go" 41 | } 42 | 43 | out, err := exec.Command(goExecutable, "env", "GOPATH").Output() 44 | cobra.CheckErr(err) 45 | 46 | toolchainGoPath := strings.TrimSpace(string(out)) 47 | goPaths = filepath.SplitList(toolchainGoPath) 48 | if len(goPaths) == 0 { 49 | cobra.CheckErr("$GOPATH is not set") 50 | } 51 | } 52 | srcPaths = make([]string, 0, len(goPaths)) 53 | for _, goPath := range goPaths { 54 | srcPaths = append(srcPaths, filepath.Join(goPath, "src")) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /framework/cobra/cobra/cmd/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import "testing" 4 | 5 | func assertNoErr(t *testing.T, e error) { 6 | if e != nil { 7 | t.Error(e) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /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 © 2021 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/cmd/testdata/test.go.golden: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2021 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 cmd 17 | 18 | import ( 19 | "fmt" 20 | 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | // testCmd represents the test command 25 | var testCmd = &cobra.Command{ 26 | Use: "test", 27 | Short: "A brief description of your command", 28 | Long: `A longer description that spans multiple lines and likely contains examples 29 | and usage of using your command. For example: 30 | 31 | Cobra is a CLI library for Go that empowers applications. 32 | This application is a tool to generate the needed files 33 | to quickly create a Cobra application.`, 34 | Run: func(cmd *cobra.Command, args []string) { 35 | fmt.Println("test called") 36 | }, 37 | } 38 | 39 | func init() { 40 | rootCmd.AddCommand(testCmd) 41 | 42 | // Here you will define your flags and configuration settings. 43 | 44 | // Cobra supports Persistent Flags which will work for this command 45 | // and all subcommands, e.g.: 46 | // testCmd.PersistentFlags().String("foo", "", "A help for foo") 47 | 48 | // Cobra supports local flags which will only run when this command 49 | // is called directly, e.g.: 50 | // testCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 51 | } 52 | -------------------------------------------------------------------------------- /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 | "github.com/spf13/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 assertNoErr(t *testing.T, e error) { 9 | if e != nil { 10 | t.Error(e) 11 | } 12 | } 13 | 14 | func TestAddTemplateFunctions(t *testing.T) { 15 | AddTemplateFunc("t", func() bool { return true }) 16 | AddTemplateFuncs(template.FuncMap{ 17 | "f": func() bool { return false }, 18 | "h": func() string { return "Hello," }, 19 | "w": func() string { return "world." }}) 20 | 21 | c := &Command{} 22 | c.SetUsageTemplate(`{{if t}}{{h}}{{end}}{{if f}}{{h}}{{end}} {{w}}`) 23 | 24 | const expected = "Hello, world." 25 | if got := c.UsageString(); got != expected { 26 | t.Errorf("Expected UsageString: %v\nGot: %v", expected, got) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /framework/cobra/command_notwin.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package cobra 5 | 6 | var preExecHookFn func(*Command) 7 | -------------------------------------------------------------------------------- /framework/cobra/command_win.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package cobra 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "time" 10 | 11 | "github.com/inconshreveable/mousetrap" 12 | ) 13 | 14 | var preExecHookFn = preExecHook 15 | 16 | func preExecHook(c *Command) { 17 | if MousetrapHelpText != "" && mousetrap.StartedByExplorer() { 18 | c.Print(MousetrapHelpText) 19 | if MousetrapDisplayDuration > 0 { 20 | time.Sleep(MousetrapDisplayDuration) 21 | } else { 22 | c.Println("Press return to continue...") 23 | fmt.Scanln() 24 | } 25 | os.Exit(1) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /framework/cobra/doc/README.md: -------------------------------------------------------------------------------- 1 | # Documentation generation 2 | 3 | - [Man page docs](./man_docs.md) 4 | - [Markdown docs](./md_docs.md) 5 | - [Rest docs](./rest_docs.md) 6 | - [Yaml docs](./yaml_docs.md) 7 | 8 | ## Options 9 | ### `DisableAutoGenTag` 10 | You may set `cmd.DisableAutoGenTag = true` 11 | to _entirely_ remove the auto generated string "Auto generated by spf13/cobra..." 12 | from any documentation source. 13 | -------------------------------------------------------------------------------- /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 | "github.com/spf13/cobra" 12 | "github.com/spf13/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 | "github.com/spf13/cobra" 8 | "github.com/spf13/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 | cobra.CheckErr(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 | cobra.CheckErr(doc.GenMan(cmd, header, out)) 34 | fmt.Print(out.String()) 35 | } 36 | -------------------------------------------------------------------------------- /framework/cobra/doc/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Red Hat Inc. All rights reserved. 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 doc 15 | 16 | import ( 17 | "strings" 18 | 19 | "github.com/spf13/cobra" 20 | ) 21 | 22 | // Test to see if we have a reason to print See Also information in docs 23 | // Basically this is a test for a parent command or a subcommand which is 24 | // both not deprecated and not the autogenerated help command. 25 | func hasSeeAlso(cmd *cobra.Command) bool { 26 | if cmd.HasParent() { 27 | return true 28 | } 29 | for _, c := range cmd.Commands() { 30 | if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { 31 | continue 32 | } 33 | return true 34 | } 35 | return false 36 | } 37 | 38 | // Temporary workaround for yaml lib generating incorrect yaml with long strings 39 | // that do not contain \n. 40 | func forceMultiLine(s string) string { 41 | if len(s) > 60 && !strings.Contains(s, "\n") { 42 | s = s + "\n" 43 | } 44 | return s 45 | } 46 | 47 | type byName []*cobra.Command 48 | 49 | func (s byName) Len() int { return len(s) } 50 | func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 51 | func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } 52 | -------------------------------------------------------------------------------- /framework/cobra/fish_completions.md: -------------------------------------------------------------------------------- 1 | ## Generating Fish Completions For Your cobra.Command 2 | 3 | Please refer to [Shell Completions](shell_completions.md) for details. 4 | 5 | -------------------------------------------------------------------------------- /framework/cobra/hade_command.go: -------------------------------------------------------------------------------- 1 | package cobra 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/robfig/cron/v3" 6 | "log" 7 | ) 8 | 9 | // SetContainer 设置服务容器 10 | func (c *Command) SetContainer(container framework.Container) { 11 | c.container = container 12 | } 13 | 14 | // GetContainer 获取容器 15 | func (c *Command) GetContainer() framework.Container { 16 | return c.Root().container 17 | } 18 | 19 | // CronSpec 保存Cron命令的信息,用于展示 20 | type CronSpec struct { 21 | Type string 22 | Cmd *Command 23 | Spec string 24 | ServiceName string 25 | } 26 | 27 | func (c *Command) SetParantNull() { 28 | c.parent = nil 29 | } 30 | 31 | // AddCronCommand 是用来创建一个Cron任务的 32 | func (c *Command) AddCronCommand(spec string, cmd *Command) { 33 | // cron结构是挂载在根Command上的 34 | root := c.Root() 35 | if root.Cron == nil { 36 | // 初始化cron 37 | root.Cron = cron.New(cron.WithParser(cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))) 38 | root.CronSpecs = []CronSpec{} 39 | } 40 | // 增加说明信息 41 | root.CronSpecs = append(root.CronSpecs, CronSpec{ 42 | Type: "normal-cron", 43 | Cmd: cmd, 44 | Spec: spec, 45 | }) 46 | 47 | // 制作一个rootCommand 48 | var cronCmd Command 49 | ctx := root.Context() 50 | cronCmd = *cmd 51 | cronCmd.args = []string{} 52 | cronCmd.SetParantNull() 53 | cronCmd.SetContainer(root.GetContainer()) 54 | 55 | // 增加调用函数 56 | root.Cron.AddFunc(spec, func() { 57 | // 如果后续的command出现panic,这里要捕获 58 | defer func() { 59 | if err := recover(); err != nil { 60 | log.Println(err) 61 | } 62 | }() 63 | 64 | err := cronCmd.ExecuteContext(ctx) 65 | if err != nil { 66 | // 打印出err信息 67 | log.Println(err) 68 | } 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /framework/cobra/hade_command_contract.go: -------------------------------------------------------------------------------- 1 | package cobra 2 | 3 | import "github.com/gohade/hade/framework/contract" 4 | 5 | // MustMakeApp 从容器中获取App服务 6 | func (c *Command) MustMakeApp() contract.App { 7 | return c.GetContainer().MustMake(contract.AppKey).(contract.App) 8 | } 9 | 10 | // MustMakeKernel 从容器中获取Kernel服务 11 | func (c *Command) MustMakeKernel() contract.Kernel { 12 | return c.GetContainer().MustMake(contract.KernelKey).(contract.Kernel) 13 | } 14 | -------------------------------------------------------------------------------- /framework/cobra/hade_command_distributed.go: -------------------------------------------------------------------------------- 1 | package cobra 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/contract" 5 | "github.com/robfig/cron/v3" 6 | "log" 7 | "time" 8 | ) 9 | 10 | // AddDistributedCronCommand 实现一个分布式定时器 11 | // serviceName 这个服务的唯一名字,不允许带有空格 12 | // spec 具体的执行时间 13 | // cmd 具体的执行命令 14 | // holdTime 表示如果我选择上了,这次选择持续的时间,也就是锁释放的时间 15 | func (c *Command) AddDistributedCronCommand(serviceName string, spec string, cmd *Command, holdTime time.Duration) { 16 | root := c.Root() 17 | 18 | // 初始化cron 19 | if root.Cron == nil { 20 | root.Cron = cron.New(cron.WithParser(cron.NewParser(cron.SecondOptional | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor))) 21 | root.CronSpecs = []CronSpec{} 22 | } 23 | 24 | // cron命令的注释,这里注意Type为distributed-cron,ServiceName需要填写 25 | root.CronSpecs = append(root.CronSpecs, CronSpec{ 26 | Type: "distributed-cron", 27 | Cmd: cmd, 28 | Spec: spec, 29 | ServiceName: serviceName, 30 | }) 31 | 32 | appService := root.GetContainer().MustMake(contract.AppKey).(contract.App) 33 | distributeServce := root.GetContainer().MustMake(contract.DistributedKey).(contract.Distributed) 34 | appID := appService.AppID() 35 | 36 | // 复制要执行的command为cronCmd,并且设置为rootCmd 37 | var cronCmd Command 38 | ctx := root.Context() 39 | cronCmd = *cmd 40 | cronCmd.args = []string{} 41 | cronCmd.SetParantNull() 42 | 43 | // cron增加匿名函数 44 | root.Cron.AddFunc(spec, func() { 45 | // 防止panic 46 | defer func() { 47 | if err := recover(); err != nil { 48 | log.Println(err) 49 | } 50 | }() 51 | 52 | // 节点进行选举,返回选举结果 53 | selectedAppID, err := distributeServce.Select(serviceName, appID, holdTime) 54 | if err != nil { 55 | return 56 | } 57 | 58 | // 如果自己没有被选择到,直接返回 59 | if selectedAppID != appID { 60 | return 61 | } 62 | 63 | // 如果自己被选择到了,执行这个定时任务 64 | err = cronCmd.ExecuteContext(ctx) 65 | if err != nil { 66 | log.Println(err) 67 | } 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /framework/cobra/powershell_completions.md: -------------------------------------------------------------------------------- 1 | # Generating PowerShell Completions For Your Own cobra.Command 2 | 3 | Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details. 4 | -------------------------------------------------------------------------------- /framework/cobra/projects_using_cobra.md: -------------------------------------------------------------------------------- 1 | ## Projects using Cobra 2 | 3 | - [Arduino CLI](https://github.com/arduino/arduino-cli) 4 | - [Bleve](http://www.blevesearch.com/) 5 | - [CockroachDB](http://www.cockroachlabs.com/) 6 | - [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) 7 | - [Delve](https://github.com/derekparker/delve) 8 | - [Docker (distribution)](https://github.com/docker/distribution) 9 | - [Etcd](https://etcd.io/) 10 | - [Gardener](https://github.com/gardener/gardenctl) 11 | - [Giant Swarm's gsctl](https://github.com/giantswarm/gsctl) 12 | - [Git Bump](https://github.com/erdaltsksn/git-bump) 13 | - [Github CLI](https://github.com/cli/cli) 14 | - [GitHub Labeler](https://github.com/erdaltsksn/gh-label) 15 | - [Golangci-lint](https://golangci-lint.run) 16 | - [GopherJS](http://www.gopherjs.org/) 17 | - [Helm](https://helm.sh) 18 | - [Hugo](https://gohugo.io) 19 | - [Istio](https://istio.io) 20 | - [Kool](https://github.com/kool-dev/kool) 21 | - [Kubernetes](http://kubernetes.io/) 22 | - [Linkerd](https://linkerd.io/) 23 | - [Mattermost-server](https://github.com/mattermost/mattermost-server) 24 | - [Meroxa CLI](https://github.com/meroxa/cli) 25 | - [Metal Stack CLI](https://github.com/metal-stack/metalctl) 26 | - [Moby (former Docker)](https://github.com/moby/moby) 27 | - [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) 28 | - [OpenShift](https://www.openshift.com/) 29 | - [Ory Hydra](https://github.com/ory/hydra) 30 | - [Ory Kratos](https://github.com/ory/kratos) 31 | - [Pouch](https://github.com/alibaba/pouch) 32 | - [ProjectAtomic (enterprise)](http://www.projectatomic.io/) 33 | - [Prototool](https://github.com/uber/prototool) 34 | - [Random](https://github.com/erdaltsksn/random) 35 | - [Rclone](https://rclone.org/) 36 | - [Skaffold](https://skaffold.dev/) 37 | - [Tendermint](https://github.com/tendermint/tendermint) 38 | - [Twitch CLI](https://github.com/twitchdev/twitch-cli) 39 | - [Werf](https://werf.io/) 40 | -------------------------------------------------------------------------------- /framework/command/config.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gohade/hade/framework/cobra" 6 | "github.com/gohade/hade/framework/contract" 7 | "github.com/kr/pretty" 8 | ) 9 | 10 | // initConfigCommand 获取配置相关的命令 11 | func initConfigCommand() *cobra.Command { 12 | configCommand.AddCommand(configGetCommand) 13 | return configCommand 14 | } 15 | 16 | // envCommand 获取当前的App环境 17 | var configCommand = &cobra.Command{ 18 | Use: "config", 19 | Short: "获取配置相关信息", 20 | RunE: func(c *cobra.Command, args []string) error { 21 | if len(args) == 0 { 22 | c.Help() 23 | } 24 | return nil 25 | }, 26 | } 27 | 28 | // envListCommand 获取所有的App环境变量 29 | var configGetCommand = &cobra.Command{ 30 | Use: "get", 31 | Short: "获取某个配置信息", 32 | RunE: func(c *cobra.Command, args []string) error { 33 | container := c.GetContainer() 34 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 35 | if len(args) != 1 { 36 | fmt.Println("参数错误") 37 | return nil 38 | } 39 | configPath := args[0] 40 | val := configService.Get(configPath) 41 | if val == nil { 42 | fmt.Println("配置路径 ", configPath, " 不存在") 43 | return nil 44 | } 45 | 46 | fmt.Printf("%# v\n", pretty.Formatter(val)) 47 | return nil 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /framework/command/env.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gohade/hade/framework/util" 6 | 7 | "github.com/gohade/hade/framework/cobra" 8 | "github.com/gohade/hade/framework/contract" 9 | ) 10 | 11 | // initEnvCommand 获取env相关的命令 12 | func initEnvCommand() *cobra.Command { 13 | envCommand.AddCommand(envListCommand) 14 | return envCommand 15 | } 16 | 17 | // envCommand 获取当前的App环境 18 | var envCommand = &cobra.Command{ 19 | Use: "env", 20 | Short: "获取当前的App环境", 21 | Run: func(c *cobra.Command, args []string) { 22 | // 获取env环境 23 | container := c.GetContainer() 24 | envService := container.MustMake(contract.EnvKey).(contract.Env) 25 | // 打印环境 26 | fmt.Println("environment:", envService.AppEnv()) 27 | }, 28 | } 29 | 30 | // envListCommand 获取所有的App环境变量 31 | var envListCommand = &cobra.Command{ 32 | Use: "list", 33 | Short: "获取所有的环境变量", 34 | Run: func(c *cobra.Command, args []string) { 35 | // 获取env环境 36 | container := c.GetContainer() 37 | envService := container.MustMake(contract.EnvKey).(contract.Env) 38 | envs := envService.All() 39 | outs := [][]string{} 40 | for k, v := range envs { 41 | outs = append(outs, []string{k, v}) 42 | } 43 | util.PrettyPrint(outs) 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /framework/command/go_cmd.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | 8 | "github.com/gohade/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 | "fmt" 5 | "github.com/gohade/hade/framework/cobra" 6 | "github.com/gohade/hade/framework/contract" 7 | ) 8 | 9 | // helpCommand show current envionment 10 | var DemoCommand = &cobra.Command{ 11 | Use: "demo", 12 | Short: "demo for framework", 13 | Run: func(c *cobra.Command, args []string) { 14 | container := c.GetContainer() 15 | appService := container.MustMake(contract.AppKey).(contract.App) 16 | fmt.Println("app base folder:", appService.BaseFolder()) 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /framework/command/kernel.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/cobra" 5 | ) 6 | 7 | // AddKernelCommands will add all command/* to root command 8 | func AddKernelCommands(root *cobra.Command) { 9 | // app 命令 10 | root.AddCommand(initAppCommand()) 11 | // env 命令 12 | root.AddCommand(initEnvCommand()) 13 | // cron 命令 14 | root.AddCommand(initCronCommand()) 15 | // config 命令 16 | root.AddCommand(initConfigCommand()) 17 | // build 命令 18 | root.AddCommand(initBuildCommand()) 19 | // go build 20 | root.AddCommand(goCommand) 21 | // npm build 22 | root.AddCommand(npmCommand) 23 | // dev 24 | root.AddCommand(initDevCommand()) 25 | // cmd 26 | root.AddCommand(initCmdCommand()) 27 | // provider 28 | root.AddCommand(initProviderCommand()) 29 | // middleware 30 | root.AddCommand(initMiddlewareCommand()) 31 | // new 32 | root.AddCommand(initNewCommand()) 33 | // swagger 34 | root.AddCommand(initSwaggerCommand()) 35 | // deploy 36 | root.AddCommand(initDeployCommand()) 37 | } 38 | -------------------------------------------------------------------------------- /framework/command/npm.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/exec" 7 | 8 | "github.com/gohade/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/swagger.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gohade/hade/framework/cobra" 6 | "github.com/gohade/hade/framework/contract" 7 | "github.com/swaggo/swag/gen" 8 | "path/filepath" 9 | ) 10 | 11 | func initSwaggerCommand() *cobra.Command { 12 | swaggerCommand.AddCommand(swaggerGenCommand) 13 | return swaggerCommand 14 | } 15 | 16 | var swaggerCommand = &cobra.Command{ 17 | Use: "swagger", 18 | Short: "swagger对应命令", 19 | RunE: func(c *cobra.Command, args []string) error { 20 | if len(args) == 0 { 21 | c.Help() 22 | } 23 | return nil 24 | }, 25 | } 26 | 27 | // swaggerGenCommand 生成具体的swagger文档 28 | var swaggerGenCommand = &cobra.Command{ 29 | Use: "gen", 30 | Short: "生成对应的swagger文件, contain swagger.yaml, doc.go", 31 | Run: func(c *cobra.Command, args []string) { 32 | container := c.GetContainer() 33 | appService := container.MustMake(contract.AppKey).(contract.App) 34 | 35 | outputDir := filepath.Join(appService.AppFolder(), "http", "swagger") 36 | httpFolder := filepath.Join(appService.AppFolder(), "http") 37 | 38 | conf := &gen.Config{ 39 | // 遍历需要查询注释的目录 40 | SearchDir: httpFolder, 41 | // 不包含哪些文件 42 | Excludes: "", 43 | // 输出目录 44 | OutputDir: outputDir, 45 | // 整个swagger接口的说明文档注释 46 | MainAPIFile: "swagger.go", 47 | // 名字的显示策略,比如首字母大写等 48 | PropNamingStrategy: "", 49 | // 是否要解析vendor目录 50 | ParseVendor: false, 51 | // 是否要解析外部依赖库的包 52 | ParseDependency: false, 53 | // 是否要解析标准库的包 54 | ParseInternal: false, 55 | // 是否要查找markdown文件,这个markdown文件能用来为tag增加说明格式 56 | MarkdownFilesDir: "", 57 | // 是否应该在docs.go中生成时间戳 58 | GeneratedTime: false, 59 | } 60 | err := gen.New().Build(conf) 61 | if err != nil { 62 | fmt.Println(err) 63 | } 64 | }, 65 | } 66 | -------------------------------------------------------------------------------- /framework/contract/app.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | // AppKey 定义字符串凭证 4 | const AppKey = "hade:app" 5 | 6 | // App 定义接口 7 | type App interface { 8 | // AppID 表示当前这个app的唯一id, 可以用于分布式锁等 9 | AppID() string 10 | // Version 定义当前版本 11 | Version() string 12 | 13 | //BaseFolder 定义项目基础地址 14 | BaseFolder() string 15 | // ConfigFolder 定义了配置文件的路径 16 | ConfigFolder() string 17 | // LogFolder 定义了日志所在路径 18 | LogFolder() string 19 | // ProviderFolder 定义业务自己的服务提供者地址 20 | ProviderFolder() string 21 | // MiddlewareFolder 定义业务自己定义的中间件 22 | MiddlewareFolder() string 23 | // CommandFolder 定义业务定义的命令 24 | CommandFolder() string 25 | // RuntimeFolder 定义业务的运行中间态信息 26 | RuntimeFolder() string 27 | // TestFolder 存放测试所需要的信息 28 | TestFolder() string 29 | // DeployFolder 存放部署的时候创建的文件夹 30 | DeployFolder() string 31 | 32 | // AppFolder 定义业务代码所在的目录,用于监控文件变更使用 33 | AppFolder() string 34 | // LoadAppConfig 加载新的AppConfig,key为对应的函数转为小写下划线,比如ConfigFolder => config_folder 35 | LoadAppConfig(kv map[string]string) 36 | } 37 | -------------------------------------------------------------------------------- /framework/contract/config.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import "time" 4 | 5 | const ( 6 | // ConfigKey is config key in container 7 | ConfigKey = "hade:config" 8 | ) 9 | 10 | // Config 定义了配置文件服务,读取配置文件,支持点分割的路径读取 11 | // 例如: .Get("app.name") 表示从app文件中读取name属性 12 | // 建议使用 yaml 属性, https://yaml.org/spec/1.2/spec.html 13 | type Config interface { 14 | // IsExist 检查一个属性是否存在 15 | IsExist(key string) bool 16 | 17 | // Get 获取一个属性值 18 | Get(key string) interface{} 19 | // GetBool 获取一个bool属性 20 | GetBool(key string) bool 21 | // GetInt 获取一个int属性 22 | GetInt(key string) int 23 | // GetFloat64 获取一个float64属性 24 | GetFloat64(key string) float64 25 | // GetTime 获取一个time属性 26 | GetTime(key string) time.Time 27 | // GetString 获取一个string属性 28 | GetString(key string) string 29 | // GetIntSlice 获取一个int数组属性 30 | GetIntSlice(key string) []int 31 | // GetStringSlice 获取一个string数组 32 | GetStringSlice(key string) []string 33 | // GetStringMap 获取一个string为key,interface为val的map 34 | GetStringMap(key string) map[string]interface{} 35 | // GetStringMapString 获取一个string为key,string为val的map 36 | GetStringMapString(key string) map[string]string 37 | // GetStringMapStringSlice 获取一个string为key,数组string为val的map 38 | GetStringMapStringSlice(key string) map[string][]string 39 | 40 | // Load 加载配置到某个对象 41 | Load(key string, val interface{}) error 42 | } 43 | -------------------------------------------------------------------------------- /framework/contract/distributed.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import "time" 4 | 5 | // DistributedKey 定义字符串凭证 6 | const DistributedKey = "hade:distributed" 7 | 8 | // Distributed 分布式服务 9 | type Distributed interface { 10 | // Select 分布式选择器, 所有节点对某个服务进行抢占,只选择其中一个节点 11 | // ServiceName 服务名字 12 | // appID 当前的AppID 13 | // holdTime 分布式选择器hold住的时间 14 | // 返回值 15 | // selectAppID 分布式选择器最终选择的App 16 | // err 异常才返回,如果没有被选择,不返回err 17 | Select(serviceName string, appID string, holdTime time.Duration) (selectAppID string, err error) 18 | } 19 | -------------------------------------------------------------------------------- /framework/contract/env.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | const ( 4 | // EnvProduction 代表生产环境 5 | EnvProduction = "production" 6 | // EnvTesting 代表测试环境 7 | EnvTesting = "testing" 8 | // EnvDevelopment 代表开发环境 9 | EnvDevelopment = "development" 10 | 11 | // EnvKey 是环境变量服务字符串凭证 12 | EnvKey = "hade:env" 13 | ) 14 | 15 | // Env 定义环境变量的获取服务 16 | type Env interface { 17 | // AppEnv 获取当前的环境,建议分为development/testing/production 18 | AppEnv() string 19 | 20 | // IsExist 判断一个环境变量是否有被设置 21 | IsExist(string) bool 22 | // Get 获取某个环境变量,如果没有设置,返回"" 23 | Get(string) string 24 | // All 获取所有的环境变量,.env和运行环境变量融合后结果 25 | All() map[string]string 26 | } 27 | -------------------------------------------------------------------------------- /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 | "net/http" 5 | ) 6 | 7 | const KernelKey = "hade:kernel" 8 | 9 | // Kernel 接口提供框架最核心的结构 10 | type Kernel interface { 11 | // HttpEngine 提供gin的Engine结构 12 | HttpEngine() http.Handler 13 | } 14 | -------------------------------------------------------------------------------- /framework/contract/log.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "time" 7 | ) 8 | 9 | const LogKey = "hade:log" 10 | 11 | type LogLevel uint32 12 | 13 | const ( 14 | // UnknownLevel 表示未知的日志级别 15 | UnknownLevel LogLevel = iota 16 | // PanicLevel level, panic 表示会导致整个程序出现崩溃的日志信息 17 | PanicLevel 18 | // FatalLevel level. fatal 表示会导致当前这个请求出现提前终止的错误信息 19 | FatalLevel 20 | // ErrorLevel level. error 表示出现错误,但是不一定影响后续请求逻辑的错误信息 21 | ErrorLevel 22 | // WarnLevel level. warn 表示出现错误,但是一定不影响后续请求逻辑的报警信息 23 | WarnLevel 24 | // InfoLevel level. info 表示正常的日志信息输出 25 | InfoLevel 26 | // DebugLevel level. debug 表示在调试状态下打印出来的日志信息 27 | DebugLevel 28 | // TraceLevel level. trace 表示最详细的信息,一般信息量比较大,可能包含调用堆栈等信息 29 | TraceLevel 30 | ) 31 | 32 | // CtxFielder 定义了从context中获取信息的方法 33 | type CtxFielder func(ctx context.Context) map[string]interface{} 34 | 35 | // Formatter 定义了将日志信息组织成字符串的通用方法 36 | type Formatter func(level LogLevel, t time.Time, msg string, fields map[string]interface{}) ([]byte, error) 37 | 38 | // Log 定义了日志服务协议 39 | type Log interface { 40 | // Panic 表示会导致整个程序出现崩溃的日志信息 41 | Panic(ctx context.Context, msg string, fields map[string]interface{}) 42 | // Fatal 表示会导致当前这个请求出现提前终止的错误信息 43 | Fatal(ctx context.Context, msg string, fields map[string]interface{}) 44 | // Error 表示出现错误,但是不一定影响后续请求逻辑的错误信息 45 | Error(ctx context.Context, msg string, fields map[string]interface{}) 46 | // Warn 表示出现错误,但是一定不影响后续请求逻辑的报警信息 47 | Warn(ctx context.Context, msg string, fields map[string]interface{}) 48 | // Info 表示正常的日志信息输出 49 | Info(ctx context.Context, msg string, fields map[string]interface{}) 50 | // Debug 表示在调试状态下打印出来的日志信息 51 | Debug(ctx context.Context, msg string, fields map[string]interface{}) 52 | // Trace 表示最详细的信息,一般信息量比较大,可能包含调用堆栈等信息 53 | Trace(ctx context.Context, msg string, fields map[string]interface{}) 54 | 55 | // SetLevel 设置日志级别 56 | SetLevel(level LogLevel) 57 | // SetCtxFielder 从context中获取上下文字段field 58 | SetCtxFielder(handler CtxFielder) 59 | // SetFormatter 设置输出格式 60 | SetFormatter(formatter Formatter) 61 | // SetOutput 设置输出管道 62 | SetOutput(out io.Writer) 63 | } 64 | -------------------------------------------------------------------------------- /framework/contract/redis.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-redis/redis/v8" 6 | "github.com/gohade/hade/framework" 7 | ) 8 | 9 | const RedisKey = "hade:redis" 10 | 11 | // RedisOption 代表初始化的时候的选项 12 | type RedisOption func(container framework.Container, config *RedisConfig) error 13 | 14 | // RedisService 表示一个redis服务 15 | type RedisService interface { 16 | // GetClient 获取redis连接实例 17 | GetClient(option ...RedisOption) (*redis.Client, error) 18 | } 19 | 20 | // RedisConfig 为hade定义的Redis配置结构 21 | type RedisConfig struct { 22 | *redis.Options 23 | } 24 | 25 | // UniqKey 用来唯一标识一个RedisConfig配置 26 | func (config *RedisConfig) UniqKey() string { 27 | return fmt.Sprintf("%v_%v_%v_%v", config.Addr, config.DB, config.Username, config.Network) 28 | } 29 | -------------------------------------------------------------------------------- /framework/contract/ssh.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gohade/hade/framework" 6 | "golang.org/x/crypto/ssh" 7 | ) 8 | 9 | const SSHKey = "hade:ssh" 10 | 11 | // SSHOption 代表初始化的时候的选项 12 | type SSHOption func(container framework.Container, config *SSHConfig) error 13 | 14 | // SSHService 表示一个ssh服务 15 | type SSHService interface { 16 | // GetClient 获取ssh连接实例 17 | GetClient(option ...SSHOption) (*ssh.Client, error) 18 | } 19 | 20 | // SSHConfig 为hade定义的SSH配置结构 21 | type SSHConfig struct { 22 | NetWork string 23 | Host string 24 | Port string 25 | *ssh.ClientConfig 26 | } 27 | 28 | // UniqKey 用来唯一标识一个SSHConfig配置 29 | func (config *SSHConfig) UniqKey() string { 30 | return fmt.Sprintf("%v_%v_%v", config.Host, config.Port, config.User) 31 | } 32 | -------------------------------------------------------------------------------- /framework/contract/trace.go: -------------------------------------------------------------------------------- 1 | package contract 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | ) 7 | 8 | const TraceKey = "hade:trace" 9 | 10 | const ( 11 | TraceKeyTraceID = "trace_id" 12 | TraceKeySpanID = "span_id" 13 | TraceKeyCspanID = "cspan_id" 14 | TraceKeyParentID = "parent_id" 15 | TraceKeyMethod = "method" 16 | TraceKeyCaller = "caller" 17 | TraceKeyTime = "time" 18 | ) 19 | 20 | // Trace define struct according Google Dapper 21 | type TraceContext struct { 22 | TraceID string // traceID global unique 23 | ParentID string // 父节点SpanID 24 | SpanID string // 当前节点SpanID 25 | CspanID string // 子节点调用的SpanID, 由调用方指定 26 | 27 | Annotation map[string]string // 标记各种信息 28 | } 29 | 30 | type Trace interface { 31 | // // SetTraceIDService set TraceID generator, default hadeIDGenerator 32 | // SetTraceIDService(IDService) 33 | // // SetTraceIDService set SpanID generator, default hadeIDGenerator 34 | // SetSpanIDService(IDService) 35 | 36 | // WithContext register new trace to context 37 | WithTrace(c context.Context, trace *TraceContext) context.Context 38 | // GetTrace From trace context 39 | GetTrace(c context.Context) *TraceContext 40 | // NewTrace generate a new trace 41 | NewTrace() *TraceContext 42 | // StartSpan generate cspan for child call 43 | StartSpan(trace *TraceContext) *TraceContext 44 | 45 | // traceContext to map for logger 46 | ToMap(trace *TraceContext) map[string]string 47 | 48 | // GetTrace By Http 49 | ExtractHTTP(req *http.Request) *TraceContext 50 | // Set Trace to Http 51 | InjectHTTP(req *http.Request, trace *TraceContext) *http.Request 52 | } 53 | -------------------------------------------------------------------------------- /framework/gin/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - With issues: 2 | - Use the search tool before opening a new issue. 3 | - Please provide source code and commit sha if you found a bug. 4 | - Review existing issues and provide feedback or react to them. 5 | 6 | ## Description 7 | 8 | 9 | 10 | ## How to reproduce 11 | 12 | 13 | 14 | ``` 15 | package main 16 | 17 | import ( 18 | "github.com/gohade/hade/framework/gin" 19 | ) 20 | 21 | func main() { 22 | g := gin.Default() 23 | g.GET("/hello/:name", func(c *gin.Context) { 24 | c.String(200, "Hello %s", c.Param("name")) 25 | }) 26 | g.Run(":9000") 27 | } 28 | ``` 29 | 30 | ## Expectations 31 | 32 | 33 | 34 | ``` 35 | $ curl http://localhost:8201/hello/world 36 | Hello world 37 | ``` 38 | 39 | ## Actual result 40 | 41 | 42 | 43 | ``` 44 | $ curl -i http://localhost:8201/hello/world 45 | 46 | ``` 47 | 48 | ## Environment 49 | 50 | - go version: 51 | - gin version (or commit ref): 52 | - operating system: 53 | -------------------------------------------------------------------------------- /framework/gin/.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | - With pull requests: 2 | - Open your pull request against `master` 3 | - Your pull request should have no more than two commits, if not you should squash them. 4 | - It should pass all tests in the available continuous integration systems such as TravisCI. 5 | - You should add/modify tests to cover your proposed code changes. 6 | - If your pull request contains a new feature, please document it on the README. 7 | 8 | -------------------------------------------------------------------------------- /framework/gin/.gitignore: -------------------------------------------------------------------------------- 1 | vendor/* 2 | !vendor/vendor.json 3 | coverage.out 4 | count.out 5 | test 6 | profile.out 7 | tmp.out 8 | -------------------------------------------------------------------------------- /framework/gin/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | matrix: 4 | fast_finish: true 5 | include: 6 | - go: 1.12.x 7 | env: GO111MODULE=on 8 | - go: 1.13.x 9 | - go: 1.13.x 10 | env: 11 | - TESTTAGS=nomsgpack 12 | - go: 1.14.x 13 | - go: 1.14.x 14 | env: 15 | - TESTTAGS=nomsgpack 16 | - go: 1.15.x 17 | - go: 1.15.x 18 | env: 19 | - TESTTAGS=nomsgpack 20 | - go: master 21 | 22 | git: 23 | depth: 10 24 | 25 | before_install: 26 | - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi 27 | 28 | install: 29 | - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi 30 | - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi 31 | - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi 32 | 33 | go_import_path: github.com/gohade/hade/framework/gin 34 | 35 | script: 36 | - make vet 37 | - make fmt-check 38 | - make misspell-check 39 | - make test 40 | 41 | after_success: 42 | - bash <(curl -s https://codecov.io/bash) 43 | 44 | notifications: 45 | webhooks: 46 | urls: 47 | - https://webhooks.gitter.im/e/7f95bf605c4d356372f4 48 | on_success: change # options: [always|never|change] default: always 49 | on_failure: always # options: [always|never|change] default: always 50 | on_start: false # default: false 51 | -------------------------------------------------------------------------------- /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 integration 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/Makefile: -------------------------------------------------------------------------------- 1 | GO ?= go 2 | GOFMT ?= gofmt "-s" 3 | PACKAGES ?= $(shell $(GO) list ./...) 4 | VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/) 5 | GOFILES := $(shell find . -name "*.go") 6 | TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples) 7 | TESTTAGS ?= "" 8 | 9 | .PHONY: test 10 | test: 11 | echo "mode: count" > coverage.out 12 | for d in $(TESTFOLDER); do \ 13 | $(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \ 14 | cat tmp.out; \ 15 | if grep -q "^--- FAIL" tmp.out; then \ 16 | rm tmp.out; \ 17 | exit 1; \ 18 | elif grep -q "build failed" tmp.out; then \ 19 | rm tmp.out; \ 20 | exit 1; \ 21 | elif grep -q "setup failed" tmp.out; then \ 22 | rm tmp.out; \ 23 | exit 1; \ 24 | fi; \ 25 | if [ -f profile.out ]; then \ 26 | cat profile.out | grep -v "mode:" >> coverage.out; \ 27 | rm profile.out; \ 28 | fi; \ 29 | done 30 | 31 | .PHONY: fmt 32 | fmt: 33 | $(GOFMT) -w $(GOFILES) 34 | 35 | .PHONY: fmt-check 36 | fmt-check: 37 | @diff=$$($(GOFMT) -d $(GOFILES)); \ 38 | if [ -n "$$diff" ]; then \ 39 | echo "Please run 'make fmt' and commit the result:"; \ 40 | echo "$${diff}"; \ 41 | exit 1; \ 42 | fi; 43 | 44 | vet: 45 | $(GO) vet $(VETPACKAGES) 46 | 47 | .PHONY: lint 48 | lint: 49 | @hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 50 | $(GO) get -u golang.org/x/lint/golint; \ 51 | fi 52 | for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done; 53 | 54 | .PHONY: misspell-check 55 | misspell-check: 56 | @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 57 | $(GO) get -u github.com/client9/misspell/cmd/misspell; \ 58 | fi 59 | misspell -error $(GOFILES) 60 | 61 | .PHONY: misspell 62 | misspell: 63 | @hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 64 | $(GO) get -u github.com/client9/misspell/cmd/misspell; \ 65 | fi 66 | misspell -w $(GOFILES) 67 | 68 | .PHONY: tools 69 | tools: 70 | go install golang.org/x/lint/golint; \ 71 | go install github.com/client9/misspell/cmd/misspell; 72 | -------------------------------------------------------------------------------- /framework/gin/binding/binding_msgpack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 | //go:build !nomsgpack 6 | // +build !nomsgpack 7 | 8 | package binding 9 | 10 | import ( 11 | "bytes" 12 | "testing" 13 | 14 | "github.com/stretchr/testify/assert" 15 | "github.com/ugorji/go/codec" 16 | ) 17 | 18 | func TestBindingMsgPack(t *testing.T) { 19 | test := FooStruct{ 20 | Foo: "bar", 21 | } 22 | 23 | h := new(codec.MsgpackHandle) 24 | assert.NotNil(t, h) 25 | buf := bytes.NewBuffer([]byte{}) 26 | assert.NotNil(t, buf) 27 | err := codec.NewEncoder(buf, h).Encode(test) 28 | assert.NoError(t, err) 29 | 30 | data := buf.Bytes() 31 | 32 | testMsgPackBodyBinding(t, 33 | MsgPack, "msgpack", 34 | "/", "/", 35 | string(data), string(data[1:])) 36 | } 37 | 38 | func testMsgPackBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { 39 | assert.Equal(t, name, b.Name()) 40 | 41 | obj := FooStruct{} 42 | req := requestWithBody("POST", path, body) 43 | req.Header.Add("Content-Type", MIMEMSGPACK) 44 | err := b.Bind(req, &obj) 45 | assert.NoError(t, err) 46 | assert.Equal(t, "bar", obj.Foo) 47 | 48 | obj = FooStruct{} 49 | req = requestWithBody("POST", badPath, badBody) 50 | req.Header.Add("Content-Type", MIMEMSGPACK) 51 | err = MsgPack.Bind(req, &obj) 52 | assert.Error(t, err) 53 | } 54 | 55 | func TestBindingDefaultMsgPack(t *testing.T) { 56 | assert.Equal(t, MsgPack, Default("POST", MIMEMSGPACK)) 57 | assert.Equal(t, MsgPack, Default("PUT", MIMEMSGPACK2)) 58 | } 59 | -------------------------------------------------------------------------------- /framework/gin/binding/form.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 | "net/http" 9 | ) 10 | 11 | const defaultMemory = 32 << 20 12 | 13 | type formBinding struct{} 14 | type formPostBinding struct{} 15 | type formMultipartBinding struct{} 16 | 17 | func (formBinding) Name() string { 18 | return "form" 19 | } 20 | 21 | func (formBinding) Bind(req *http.Request, obj interface{}) error { 22 | if err := req.ParseForm(); err != nil { 23 | return err 24 | } 25 | if err := req.ParseMultipartForm(defaultMemory); err != nil { 26 | if err != http.ErrNotMultipart { 27 | return err 28 | } 29 | } 30 | if err := mapForm(obj, req.Form); err != nil { 31 | return err 32 | } 33 | return validate(obj) 34 | } 35 | 36 | func (formPostBinding) Name() string { 37 | return "form-urlencoded" 38 | } 39 | 40 | func (formPostBinding) Bind(req *http.Request, obj interface{}) error { 41 | if err := req.ParseForm(); err != nil { 42 | return err 43 | } 44 | if err := mapForm(obj, req.PostForm); err != nil { 45 | return err 46 | } 47 | return validate(obj) 48 | } 49 | 50 | func (formMultipartBinding) Name() string { 51 | return "multipart/form-data" 52 | } 53 | 54 | func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { 55 | if err := req.ParseMultipartForm(defaultMemory); err != nil { 56 | return err 57 | } 58 | if err := mappingByPtr(obj, (*multipartRequest)(req), "form"); err != nil { 59 | return err 60 | } 61 | 62 | return validate(obj) 63 | } 64 | -------------------------------------------------------------------------------- /framework/gin/binding/form_mapping_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | "testing" 9 | "time" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | var form = map[string][]string{ 15 | "name": {"mike"}, 16 | "friends": {"anna", "nicole"}, 17 | "id_number": {"12345678"}, 18 | "id_date": {"2018-01-20"}, 19 | } 20 | 21 | type structFull struct { 22 | Name string `form:"name"` 23 | Age int `form:"age,default=25"` 24 | Friends []string `form:"friends"` 25 | ID *struct { 26 | Number string `form:"id_number"` 27 | DateOfIssue time.Time `form:"id_date" time_format:"2006-01-02" time_utc:"true"` 28 | } 29 | Nationality *string `form:"nationality"` 30 | } 31 | 32 | func BenchmarkMapFormFull(b *testing.B) { 33 | var s structFull 34 | for i := 0; i < b.N; i++ { 35 | err := mapForm(&s, form) 36 | if err != nil { 37 | b.Fatalf("Error on a form mapping") 38 | } 39 | } 40 | b.StopTimer() 41 | 42 | t := b 43 | assert.Equal(t, "mike", s.Name) 44 | assert.Equal(t, 25, s.Age) 45 | assert.Equal(t, []string{"anna", "nicole"}, s.Friends) 46 | assert.Equal(t, "12345678", s.ID.Number) 47 | assert.Equal(t, time.Date(2018, 1, 20, 0, 0, 0, 0, time.UTC), s.ID.DateOfIssue) 48 | assert.Nil(t, s.Nationality) 49 | } 50 | 51 | type structName struct { 52 | Name string `form:"name"` 53 | } 54 | 55 | func BenchmarkMapFormName(b *testing.B) { 56 | var s structName 57 | for i := 0; i < b.N; i++ { 58 | err := mapForm(&s, form) 59 | if err != nil { 60 | b.Fatalf("Error on a form mapping") 61 | } 62 | } 63 | b.StopTimer() 64 | 65 | t := b 66 | assert.Equal(t, "mike", s.Name) 67 | } 68 | -------------------------------------------------------------------------------- /framework/gin/binding/header.go: -------------------------------------------------------------------------------- 1 | package binding 2 | 3 | import ( 4 | "net/http" 5 | "net/textproto" 6 | "reflect" 7 | ) 8 | 9 | type headerBinding struct{} 10 | 11 | func (headerBinding) Name() string { 12 | return "header" 13 | } 14 | 15 | func (headerBinding) Bind(req *http.Request, obj interface{}) error { 16 | 17 | if err := mapHeader(obj, req.Header); err != nil { 18 | return err 19 | } 20 | 21 | return validate(obj) 22 | } 23 | 24 | func mapHeader(ptr interface{}, h map[string][]string) error { 25 | return mappingByPtr(ptr, headerSource(h), "header") 26 | } 27 | 28 | type headerSource map[string][]string 29 | 30 | var _ setter = headerSource(nil) 31 | 32 | func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) { 33 | return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt) 34 | } 35 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/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 | // EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method 22 | // on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to 23 | // return an error when the destination is a struct and the input contains object 24 | // keys which do not match any non-ignored, exported fields in the destination. 25 | var EnableDecoderDisallowUnknownFields = false 26 | 27 | type jsonBinding struct{} 28 | 29 | func (jsonBinding) Name() string { 30 | return "json" 31 | } 32 | 33 | func (jsonBinding) Bind(req *http.Request, obj interface{}) error { 34 | if req == nil || req.Body == nil { 35 | return fmt.Errorf("invalid request") 36 | } 37 | return decodeJSON(req.Body, obj) 38 | } 39 | 40 | func (jsonBinding) BindBody(body []byte, obj interface{}) error { 41 | return decodeJSON(bytes.NewReader(body), obj) 42 | } 43 | 44 | func decodeJSON(r io.Reader, obj interface{}) error { 45 | decoder := json.NewDecoder(r) 46 | if EnableDecoderUseNumber { 47 | decoder.UseNumber() 48 | } 49 | if EnableDecoderDisallowUnknownFields { 50 | decoder.DisallowUnknownFields() 51 | } 52 | if err := decoder.Decode(obj); err != nil { 53 | return err 54 | } 55 | return validate(obj) 56 | } 57 | -------------------------------------------------------------------------------- /framework/gin/binding/json_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestJSONBindingBindBody(t *testing.T) { 15 | var s struct { 16 | Foo string `json:"foo"` 17 | } 18 | err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO"}`), &s) 19 | require.NoError(t, err) 20 | assert.Equal(t, "FOO", s.Foo) 21 | } 22 | 23 | func TestJSONBindingBindBodyMap(t *testing.T) { 24 | s := make(map[string]string) 25 | err := jsonBinding{}.BindBody([]byte(`{"foo": "FOO","hello":"world"}`), &s) 26 | require.NoError(t, err) 27 | assert.Len(t, s, 2) 28 | assert.Equal(t, "FOO", s["foo"]) 29 | assert.Equal(t, "world", s["hello"]) 30 | } 31 | -------------------------------------------------------------------------------- /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 | //go:build !nomsgpack 6 | // +build !nomsgpack 7 | 8 | package binding 9 | 10 | import ( 11 | "bytes" 12 | "io" 13 | "net/http" 14 | 15 | "github.com/ugorji/go/codec" 16 | ) 17 | 18 | type msgpackBinding struct{} 19 | 20 | func (msgpackBinding) Name() string { 21 | return "msgpack" 22 | } 23 | 24 | func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { 25 | return decodeMsgPack(req.Body, obj) 26 | } 27 | 28 | func (msgpackBinding) BindBody(body []byte, obj interface{}) error { 29 | return decodeMsgPack(bytes.NewReader(body), obj) 30 | } 31 | 32 | func decodeMsgPack(r io.Reader, obj interface{}) error { 33 | cdc := new(codec.MsgpackHandle) 34 | if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { 35 | return err 36 | } 37 | return validate(obj) 38 | } 39 | -------------------------------------------------------------------------------- /framework/gin/binding/msgpack_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | //go:build !nomsgpack 6 | // +build !nomsgpack 7 | 8 | package binding 9 | 10 | import ( 11 | "bytes" 12 | "testing" 13 | 14 | "github.com/stretchr/testify/assert" 15 | "github.com/stretchr/testify/require" 16 | "github.com/ugorji/go/codec" 17 | ) 18 | 19 | func TestMsgpackBindingBindBody(t *testing.T) { 20 | type teststruct struct { 21 | Foo string `msgpack:"foo"` 22 | } 23 | var s teststruct 24 | err := msgpackBinding{}.BindBody(msgpackBody(t, teststruct{"FOO"}), &s) 25 | require.NoError(t, err) 26 | assert.Equal(t, "FOO", s.Foo) 27 | } 28 | 29 | func msgpackBody(t *testing.T, obj interface{}) []byte { 30 | var bs bytes.Buffer 31 | h := &codec.MsgpackHandle{} 32 | err := codec.NewEncoder(&bs, h).Encode(obj) 33 | require.NoError(t, err) 34 | return bs.Bytes() 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/xml_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestXMLBindingBindBody(t *testing.T) { 15 | var s struct { 16 | Foo string `xml:"foo"` 17 | } 18 | xmlBody := ` 19 | 20 | FOO 21 | ` 22 | err := xmlBinding{}.BindBody([]byte(xmlBody), &s) 23 | require.NoError(t, err) 24 | assert.Equal(t, "FOO", s.Foo) 25 | } 26 | -------------------------------------------------------------------------------- /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/binding/yaml_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestYAMLBindingBindBody(t *testing.T) { 15 | var s struct { 16 | Foo string `yaml:"foo"` 17 | } 18 | err := yamlBinding{}.BindBody([]byte("foo: FOO"), &s) 19 | require.NoError(t, err) 20 | assert.Equal(t, "FOO", s.Foo) 21 | } 22 | -------------------------------------------------------------------------------- /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 | // 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 | //go:build appengine 6 | // +build appengine 7 | 8 | package gin 9 | 10 | func init() { 11 | defaultAppEngine = true 12 | } 13 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/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 | "github.com/gohade/hade/framework/gin/binding" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestBindWith(t *testing.T) { 18 | w := httptest.NewRecorder() 19 | c, _ := CreateTestContext(w) 20 | 21 | c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused")) 22 | 23 | var obj struct { 24 | Foo string `form:"foo"` 25 | Bar string `form:"bar"` 26 | } 27 | captureOutput(t, func() { 28 | assert.NoError(t, c.BindWith(&obj, binding.Form)) 29 | }) 30 | assert.Equal(t, "foo", obj.Bar) 31 | assert.Equal(t, "bar", obj.Foo) 32 | assert.Equal(t, 0, w.Body.Len()) 33 | } 34 | -------------------------------------------------------------------------------- /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/gohade/hade/framework/gin" 7 | -------------------------------------------------------------------------------- /framework/gin/errors_1.13_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.13 2 | // +build go1.13 3 | 4 | package gin 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | type TestErr string 15 | 16 | func (e TestErr) Error() string { return string(e) } 17 | 18 | // TestErrorUnwrap tests the behavior of gin.Error with "errors.Is()" and "errors.As()". 19 | // "errors.Is()" and "errors.As()" have been added to the standard library in go 1.13, 20 | // hence the "// +build go1.13" directive at the beginning of this file. 21 | func TestErrorUnwrap(t *testing.T) { 22 | innerErr := TestErr("somme error") 23 | 24 | // 2 layers of wrapping : use 'fmt.Errorf("%w")' to wrap a gin.Error{}, which itself wraps innerErr 25 | err := fmt.Errorf("wrapped: %w", &Error{ 26 | Err: innerErr, 27 | Type: ErrorTypeAny, 28 | }) 29 | 30 | // check that 'errors.Is()' and 'errors.As()' behave as expected : 31 | assert.True(t, errors.Is(err, innerErr)) 32 | var testErr TestErr 33 | assert.True(t, errors.As(err, &testErr)) 34 | } 35 | -------------------------------------------------------------------------------- /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/gohade/hade/framework/gin" 10 | "github.com/gohade/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_context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 jianfengye. 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 | package gin 5 | 6 | import ( 7 | "context" 8 | ) 9 | 10 | func (ctx *Context) BaseContext() context.Context { 11 | return ctx.Request.Context() 12 | } 13 | 14 | // context 实现container的几个封装 15 | 16 | // 实现make的封装 17 | func (ctx *Context) Make(key string) (interface{}, error) { 18 | return ctx.container.Make(key) 19 | } 20 | 21 | // 实现mustMake的封装 22 | func (ctx *Context) MustMake(key string) interface{} { 23 | return ctx.container.MustMake(key) 24 | } 25 | 26 | // 实现makenew的封装 27 | func (ctx *Context) MakeNew(key string, params []interface{}) (interface{}, error) { 28 | return ctx.container.MakeNew(key, params) 29 | } 30 | -------------------------------------------------------------------------------- /framework/gin/hade_context_contract.go: -------------------------------------------------------------------------------- 1 | package gin 2 | 3 | import "github.com/gohade/hade/framework/contract" 4 | 5 | // MustMakeApp 从容器中获取App服务 6 | func (c *Context) MustMakeApp() contract.App { 7 | return c.MustMake(contract.AppKey).(contract.App) 8 | } 9 | 10 | // MustMakeKernel 从容器中获取Kernel服务 11 | func (c *Context) MustMakeKernel() contract.Kernel { 12 | return c.MustMake(contract.KernelKey).(contract.Kernel) 13 | } 14 | 15 | // MustMakeConfig 从容器中获取配置服务 16 | func (c *Context) MustMakeConfig() contract.Config { 17 | return c.MustMake(contract.ConfigKey).(contract.Config) 18 | } 19 | 20 | // MustMakeLog 从容器中获取日志服务 21 | func (c *Context) MustMakeLog() contract.Log { 22 | return c.MustMake(contract.LogKey).(contract.Log) 23 | } 24 | -------------------------------------------------------------------------------- /framework/gin/hade_engine.go: -------------------------------------------------------------------------------- 1 | package gin 2 | 3 | import "github.com/gohade/hade/framework" 4 | 5 | // SetContainer 为Engine设置container 6 | func (engine *Engine) SetContainer(container framework.Container) { 7 | engine.container = container 8 | } 9 | 10 | // GetContainer 从Engine中获取container 11 | func (engine *Engine) GetContainer() framework.Container { 12 | return engine.container 13 | } 14 | 15 | // engine实现container的绑定封装 16 | func (engine *Engine) Bind(provider framework.ServiceProvider) error { 17 | return engine.container.Bind(provider) 18 | } 19 | 20 | // IsBind 关键字凭证是否已经绑定服务提供者 21 | func (engine *Engine) IsBind(key string) bool { 22 | return engine.container.IsBind(key) 23 | } 24 | -------------------------------------------------------------------------------- /framework/gin/internal/bytesconv/bytesconv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 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 bytesconv 6 | 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // StringToBytes converts string to byte slice without a memory allocation. 12 | func StringToBytes(s string) []byte { 13 | return *(*[]byte)(unsafe.Pointer( 14 | &struct { 15 | string 16 | Cap int 17 | }{s, len(s)}, 18 | )) 19 | } 20 | 21 | // BytesToString converts byte slice to string without a memory allocation. 22 | func BytesToString(b []byte) string { 23 | return *(*string)(unsafe.Pointer(&b)) 24 | } 25 | -------------------------------------------------------------------------------- /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 | //go:build !jsoniter 6 | // +build !jsoniter 7 | 8 | package json 9 | 10 | import "encoding/json" 11 | 12 | var ( 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/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 | //go:build jsoniter 6 | // +build jsoniter 7 | 8 | package json 9 | 10 | import jsoniter "github.com/json-iterator/go" 11 | 12 | var ( 13 | json = jsoniter.ConfigCompatibleWithStandardLibrary 14 | // Marshal is exported by gin/json package. 15 | Marshal = json.Marshal 16 | // Unmarshal is exported by gin/json package. 17 | Unmarshal = json.Unmarshal 18 | // MarshalIndent is exported by gin/json package. 19 | MarshalIndent = json.MarshalIndent 20 | // NewDecoder is exported by gin/json package. 21 | NewDecoder = json.NewDecoder 22 | // NewEncoder is exported by gin/json package. 23 | NewEncoder = json.NewEncoder 24 | ) 25 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/hade/framework/gin/binding" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func init() { 16 | os.Setenv(EnvGinMode, TestMode) 17 | } 18 | 19 | func TestSetMode(t *testing.T) { 20 | assert.Equal(t, testCode, ginMode) 21 | assert.Equal(t, TestMode, Mode()) 22 | os.Unsetenv(EnvGinMode) 23 | 24 | SetMode("") 25 | assert.Equal(t, debugCode, ginMode) 26 | assert.Equal(t, DebugMode, Mode()) 27 | 28 | SetMode(DebugMode) 29 | assert.Equal(t, debugCode, ginMode) 30 | assert.Equal(t, DebugMode, Mode()) 31 | 32 | SetMode(ReleaseMode) 33 | assert.Equal(t, releaseCode, ginMode) 34 | assert.Equal(t, ReleaseMode, Mode()) 35 | 36 | SetMode(TestMode) 37 | assert.Equal(t, testCode, ginMode) 38 | assert.Equal(t, TestMode, Mode()) 39 | 40 | assert.Panics(t, func() { SetMode("unknown") }) 41 | } 42 | 43 | func TestDisableBindValidation(t *testing.T) { 44 | v := binding.Validator 45 | assert.NotNil(t, binding.Validator) 46 | DisableBindValidation() 47 | assert.Nil(t, binding.Validator) 48 | binding.Validator = v 49 | } 50 | 51 | func TestEnableJsonDecoderUseNumber(t *testing.T) { 52 | assert.False(t, binding.EnableDecoderUseNumber) 53 | EnableJsonDecoderUseNumber() 54 | assert.True(t, binding.EnableDecoderUseNumber) 55 | } 56 | 57 | func TestEnableJsonDecoderDisallowUnknownFields(t *testing.T) { 58 | assert.False(t, binding.EnableDecoderDisallowUnknownFields) 59 | EnableJsonDecoderDisallowUnknownFields() 60 | assert.True(t, binding.EnableDecoderDisallowUnknownFields) 61 | } 62 | -------------------------------------------------------------------------------- /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 | //go:build !nomsgpack 6 | // +build !nomsgpack 7 | 8 | package render 9 | 10 | import ( 11 | "net/http" 12 | 13 | "github.com/ugorji/go/codec" 14 | ) 15 | 16 | var ( 17 | _ Render = MsgPack{} 18 | ) 19 | 20 | // MsgPack contains the given interface object. 21 | type MsgPack struct { 22 | Data interface{} 23 | } 24 | 25 | var msgpackContentType = []string{"application/msgpack; charset=utf-8"} 26 | 27 | // WriteContentType (MsgPack) writes MsgPack ContentType. 28 | func (r MsgPack) WriteContentType(w http.ResponseWriter) { 29 | writeContentType(w, msgpackContentType) 30 | } 31 | 32 | // Render (MsgPack) encodes the given interface object and writes data with custom ContentType. 33 | func (r MsgPack) Render(w http.ResponseWriter) error { 34 | return WriteMsgPack(w, r.Data) 35 | } 36 | 37 | // WriteMsgPack writes MsgPack ContentType and encodes the given interface object. 38 | func WriteMsgPack(w http.ResponseWriter, obj interface{}) error { 39 | writeContentType(w, msgpackContentType) 40 | var mh codec.MsgpackHandle 41 | return codec.NewEncoder(w, &mh).Encode(obj) 42 | } 43 | -------------------------------------------------------------------------------- /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 | if r.ContentLength >= 0 { 25 | if r.Headers == nil { 26 | r.Headers = map[string]string{} 27 | } 28 | r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) 29 | } 30 | r.writeHeaders(w, r.Headers) 31 | _, err = io.Copy(w, r.Reader) 32 | return 33 | } 34 | 35 | // WriteContentType (Reader) writes custom ContentType. 36 | func (r Reader) WriteContentType(w http.ResponseWriter) { 37 | writeContentType(w, []string{r.ContentType}) 38 | } 39 | 40 | // writeHeaders writes custom Header. 41 | func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { 42 | header := w.Header() 43 | for k, v := range headers { 44 | if header.Get(k) == "" { 45 | header.Set(k, v) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /framework/gin/render/reader_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 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/httptest" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestReaderRenderNoHeaders(t *testing.T) { 16 | content := "test" 17 | r := Reader{ 18 | ContentLength: int64(len(content)), 19 | Reader: strings.NewReader(content), 20 | } 21 | err := r.Render(httptest.NewRecorder()) 22 | require.NoError(t, err) 23 | } 24 | -------------------------------------------------------------------------------- /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 = Reader{} 31 | _ Render = AsciiJSON{} 32 | _ Render = ProtoBuf{} 33 | ) 34 | 35 | func writeContentType(w http.ResponseWriter, value []string) { 36 | header := w.Header() 37 | if val := header["Content-Type"]; len(val) == 0 { 38 | header["Content-Type"] = value 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /framework/gin/render/render_msgpack_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 | //go:build !nomsgpack 6 | // +build !nomsgpack 7 | 8 | package render 9 | 10 | import ( 11 | "bytes" 12 | "net/http/httptest" 13 | "testing" 14 | 15 | "github.com/stretchr/testify/assert" 16 | "github.com/ugorji/go/codec" 17 | ) 18 | 19 | // TODO unit tests 20 | // test errors 21 | 22 | func TestRenderMsgPack(t *testing.T) { 23 | w := httptest.NewRecorder() 24 | data := map[string]interface{}{ 25 | "foo": "bar", 26 | } 27 | 28 | (MsgPack{data}).WriteContentType(w) 29 | assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) 30 | 31 | err := (MsgPack{data}).Render(w) 32 | 33 | assert.NoError(t, err) 34 | 35 | h := new(codec.MsgpackHandle) 36 | assert.NotNil(t, h) 37 | buf := bytes.NewBuffer([]byte{}) 38 | assert.NotNil(t, buf) 39 | err = codec.NewEncoder(buf, h).Encode(data) 40 | 41 | assert.NoError(t, err) 42 | assert.Equal(t, w.Body.String(), string(buf.Bytes())) 43 | assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type")) 44 | } 45 | -------------------------------------------------------------------------------- /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 | "net/http" 10 | 11 | "github.com/gohade/hade/framework/gin/internal/bytesconv" 12 | ) 13 | 14 | // String contains the given interface object slice and its format. 15 | type String struct { 16 | Format string 17 | Data []interface{} 18 | } 19 | 20 | var plainContentType = []string{"text/plain; charset=utf-8"} 21 | 22 | // Render (String) writes data with custom ContentType. 23 | func (r String) Render(w http.ResponseWriter) error { 24 | return WriteString(w, r.Format, r.Data) 25 | } 26 | 27 | // WriteContentType (String) writes Plain ContentType. 28 | func (r String) WriteContentType(w http.ResponseWriter) { 29 | writeContentType(w, plainContentType) 30 | } 31 | 32 | // WriteString writes data according to its format and write custom ContentType. 33 | func WriteString(w http.ResponseWriter, format string, data []interface{}) (err error) { 34 | writeContentType(w, plainContentType) 35 | if len(data) > 0 { 36 | _, err = fmt.Fprintf(w, format, data...) 37 | return 38 | } 39 | _, err = w.Write(bytesconv.StringToBytes(format)) 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /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_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/certificate/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAqbKP9hmkPn0GnLjDep/pXMzD25QGxan4g/iSXvPlyYYdhQef 3 | 9iilMse9HbcYAHXanoqblBbMIG4kXiPrU8lcd+Df+uNKFnvslxDeTPG7LWIoMj4M 4 | 0o3sqXOt2Mnj1APSVzNkd4G+8IvsmwkUoWMbLraudK25bwtogR22NdP4ZRlPEmHo 5 | bvI9h8MxLUix0xAY51sbA1r6qiAy5A+HRPMfD4LvebIquNjqlESKOScwL+ucgzP1 6 | 0s+3oqXFfLhuvjjd2ljp1gYiEO4qFE5P69nTkcpqy65BQWFju/8qhSkRkwH2t9RL 7 | ONDl9qR4NQAyeJdFx34ObC9ugbZMjqLGa48r4QIDAQABAoIBAD5mhd+GMEo2KU9J 8 | 9b/Ku8I/HapJtW/L/7Fvn0tBPncrVQGM+zpGWfDhV95sbGwG6lwwNeNvuqIWPlNL 9 | vAY0XkdKrrIQEDdSXH50WnpKzXxzwrou7QIj5Cmvevbjzl4xBZDBOilj0XWczmV4 10 | IljyG5XC4UXQeAaoWEZaSZ1jk8yAt2Zq1Hgg7HqhHsK/arWXBgax+4K5nV/s9gZx 11 | yjKU9mXTIs7k/aNnZqwQKqcZF+l3mvbZttOaFwsP14H0I8OFWhnM9hie54Dejqxi 12 | f4/llNxDqUs6lqJfP3qNxtORLcFe75M+Yl8v7g2hkjtLdZBakPzSTEx3TAK/UHgi 13 | aM8DdxECgYEA3fmg/PI4EgUEj0C3SCmQXR/CnQLMUQgb54s0asp4akvp+M7YCcr1 14 | pQd3HFUpBwhBcJg5LeSe87vLupY7pHCKk56cl9WY6hse0b9sP/7DWJuGiO62m0E0 15 | vNjQ2jpG99oR2ROIHHeWsGCpGLmrRT/kY+vR3M+AOLZniXlOCw8k0aUCgYEAw7WL 16 | XFWLxgZYQYilywqrQmfv1MBfaUCvykO6oWB+f6mmnihSFjecI+nDw/b3yXVYGEgy 17 | 0ebkuw0jP8suC8wBqX9WuXj+9nZNomJRssJyOMiEhDEqUiTztFPSp9pdruoakLTh 18 | Wk1p9NralOqGPUmxpXlFKVmYRTUbluikVxDypI0CgYBn6sqEQH0hann0+o4TWWn9 19 | PrYkPUAbm1k8771tVTZERR/W3Dbldr/DL5iCihe39BR2urziEEqdvkglJNntJMar 20 | TzDuIBADYQjvltb9qq4XGFBGYMLaMg+XbUVxNKEuvUdnwa4R7aZ9EfN34MwekkfA 21 | w5Cu9/GGG1ajVEfGA6PwBQKBgA3o71jGs8KFXOx7e90sivOTU5Z5fc6LTHNB0Rf7 22 | NcJ5GmCPWRY/KZfb25AoE4B8GKDRMNt+X69zxZeZJ1KrU0rqxA02rlhyHB54gnoE 23 | G/4xMkn6/JkOC0w70PMhMBtohC7YzFOQwQEoNPT0nkno3Pl33xSLS6lPlwBo1JVj 24 | nPtZAoGACXNLXYkR5vexE+w6FGl59r4RQhu1XU8Mr5DIHeB7kXPN3RKbS201M+Tb 25 | SB5jbu0iDV477XkzSNmhaksFf2wM9MT6CaE+8n3UU5tMa+MmBGgwYTp/i9HkqVh5 26 | jjpJifn1VWBINd4cpNzwCg9LXoo0tbtUPWwGzqVeyo/YE5GIHGo= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /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.7.3" 9 | -------------------------------------------------------------------------------- /framework/middleware/cost.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 jianfengye. 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 | package middleware 5 | 6 | import ( 7 | "log" 8 | "time" 9 | 10 | "github.com/gohade/hade/framework/gin" 11 | ) 12 | 13 | // recovery机制,将协程中的函数异常进行捕获 14 | func Cost() gin.HandlerFunc { 15 | // 使用函数回调 16 | return func(c *gin.Context) { 17 | // 记录开始时间 18 | start := time.Now() 19 | 20 | log.Printf("api uri start: %v", c.Request.RequestURI) 21 | // 使用next执行具体的业务逻辑 22 | c.Next() 23 | 24 | // 记录结束时间 25 | end := time.Now() 26 | cost := end.Sub(start) 27 | log.Printf("api uri end: %v, cost: %v", c.Request.RequestURI, cost.Seconds()) 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /framework/middleware/gin-swagger/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | dist 13 | 14 | .idea 15 | vendor 16 | .envrc 17 | -------------------------------------------------------------------------------- /framework/middleware/gin-swagger/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.13.x 5 | - 1.14.x 6 | 7 | matrix: 8 | fast_finish: true 9 | include: 10 | - go: 1.11.x 11 | env: GO111MODULE=on 12 | 13 | script: 14 | - go test -coverprofile=coverage.txt -covermode=atomic 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | -------------------------------------------------------------------------------- /framework/middleware/gin-swagger/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Swaggo 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 | -------------------------------------------------------------------------------- /framework/middleware/gin-swagger/swaggerFiles/b0xfile__swagger-ui.css.map.go: -------------------------------------------------------------------------------- 1 | // Code generaTed by fileb0x at "2017-11-26 17:57:18.489607614 +0600 +06 m=+4.245926076" from config file "b0x.yml" DO NOT EDIT. 2 | 3 | package swaggerFiles 4 | 5 | import ( 6 | "log" 7 | "os" 8 | ) 9 | 10 | // FileSwaggerUICSSMap is "/swagger-ui.css.map" 11 | var FileSwaggerUICSSMap = []byte("\x7b\x22\x76\x65\x72\x73\x69\x6f\x6e\x22\x3a\x33\x2c\x22\x73\x6f\x75\x72\x63\x65\x73\x22\x3a\x5b\x5d\x2c\x22\x6e\x61\x6d\x65\x73\x22\x3a\x5b\x5d\x2c\x22\x6d\x61\x70\x70\x69\x6e\x67\x73\x22\x3a\x22\x22\x2c\x22\x66\x69\x6c\x65\x22\x3a\x22\x73\x77\x61\x67\x67\x65\x72\x2d\x75\x69\x2e\x63\x73\x73\x22\x2c\x22\x73\x6f\x75\x72\x63\x65\x52\x6f\x6f\x74\x22\x3a\x22\x22\x7d") 12 | 13 | func init() { 14 | 15 | f, err := FS.OpenFile(CTX, "/swagger-ui.css.map", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | _, err = f.Write(FileSwaggerUICSSMap) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | err = f.Close() 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /framework/middleware/static/LICENCE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 gin-contrib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 8 | persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 11 | Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 14 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 15 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 16 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /framework/middleware/static/static.go: -------------------------------------------------------------------------------- 1 | package static 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | "path" 7 | "strings" 8 | 9 | "github.com/gohade/hade/framework/gin" 10 | ) 11 | 12 | const INDEX = "index.html" 13 | 14 | type ServeFileSystem interface { 15 | http.FileSystem 16 | Exists(prefix string, path string) bool 17 | } 18 | 19 | type localFileSystem struct { 20 | http.FileSystem 21 | root string 22 | indexes bool 23 | } 24 | 25 | func LocalFile(root string, indexes bool) *localFileSystem { 26 | return &localFileSystem{ 27 | FileSystem: gin.Dir(root, indexes), 28 | root: root, 29 | indexes: indexes, 30 | } 31 | } 32 | 33 | func (l *localFileSystem) Exists(prefix string, filepath string) bool { 34 | if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) { 35 | name := path.Join(l.root, p) 36 | stats, err := os.Stat(name) 37 | if err != nil { 38 | return false 39 | } 40 | if stats.IsDir() { 41 | if !l.indexes { 42 | index := path.Join(name, INDEX) 43 | _, err := os.Stat(index) 44 | if err != nil { 45 | return false 46 | } 47 | } 48 | } 49 | return true 50 | } 51 | return false 52 | } 53 | 54 | func ServeRoot(urlPrefix, root string) gin.HandlerFunc { 55 | return Serve(urlPrefix, LocalFile(root, false)) 56 | } 57 | 58 | // Static returns a middleware handler that serves static files in the given directory. 59 | func Serve(urlPrefix string, fs ServeFileSystem) gin.HandlerFunc { 60 | fileserver := http.FileServer(fs) 61 | if urlPrefix != "" { 62 | fileserver = http.StripPrefix(urlPrefix, fileserver) 63 | } 64 | return func(c *gin.Context) { 65 | if fs.Exists(urlPrefix, c.Request.URL.Path) { 66 | fileserver.ServeHTTP(c.Writer, c.Request) 67 | c.Abort() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /framework/middleware/timeout.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 jianfengye. 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 | package middleware 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "log" 10 | "time" 11 | 12 | "github.com/gohade/hade/framework/gin" 13 | ) 14 | 15 | func Timeout(d time.Duration) gin.HandlerFunc { 16 | // 使用函数回调 17 | return func(c *gin.Context) { 18 | finish := make(chan struct{}, 1) 19 | panicChan := make(chan interface{}, 1) 20 | // 执行业务逻辑前预操作:初始化超时context 21 | durationCtx, cancel := context.WithTimeout(c.BaseContext(), d) 22 | defer cancel() 23 | 24 | go func() { 25 | defer func() { 26 | if p := recover(); p != nil { 27 | panicChan <- p 28 | } 29 | }() 30 | // 使用next执行具体的业务逻辑 31 | c.Next() 32 | 33 | finish <- struct{}{} 34 | }() 35 | // 执行业务逻辑后操作 36 | select { 37 | case p := <-panicChan: 38 | c.ISetStatus(500).IJson("panic") 39 | log.Println(p) 40 | case <-finish: 41 | fmt.Println("finish") 42 | case <-durationCtx.Done(): 43 | c.ISetStatus(502).IJson("time out") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /framework/middleware/trace.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/contract" 5 | "github.com/gohade/hade/framework/gin" 6 | ) 7 | 8 | // recovery机制,将协程中的函数异常进行捕获 9 | func Trace() gin.HandlerFunc { 10 | // 使用函数回调 11 | return func(c *gin.Context) { 12 | // 记录开始时间 13 | tracer := c.MustMake(contract.TraceKey).(contract.Trace) 14 | traceCtx := tracer.ExtractHTTP(c.Request) 15 | 16 | tracer.WithTrace(c, traceCtx) 17 | 18 | // 使用next执行具体的业务逻辑 19 | c.Next() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /framework/provider.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | // NewInstance 定义了如何创建一个新实例,所有服务容器的创建服务 4 | type NewInstance func(...interface{}) (interface{}, error) 5 | 6 | // ServiceProvider 定义一个服务提供者需要实现的接口 7 | type ServiceProvider interface { 8 | // Register 在服务容器中注册了一个实例化服务的方法,是否在注册的时候就实例化这个服务,需要参考IsDefer接口。 9 | Register(Container) NewInstance 10 | // Boot 在调用实例化服务的时候会调用,可以把一些准备工作:基础配置,初始化参数的操作放在这个里面。 11 | // 如果Boot返回error,整个服务实例化就会实例化失败,返回错误 12 | Boot(Container) error 13 | // IsDefer 决定是否在注册的时候实例化这个服务,如果不是注册的时候实例化,那就是在第一次make的时候进行实例化操作 14 | // false表示不需要延迟实例化,在注册的时候就实例化。true表示延迟实例化 15 | IsDefer() bool 16 | // Params params定义传递给NewInstance的参数,可以自定义多个,建议将container作为第一个参数 17 | Params(Container) []interface{} 18 | // Name 代表了这个服务提供者的凭证 19 | Name() string 20 | } 21 | -------------------------------------------------------------------------------- /framework/provider/app/provider.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | // HadeAppProvider 提供App的具体实现方法 9 | type HadeAppProvider struct { 10 | BaseFolder string 11 | } 12 | 13 | // Register 注册HadeApp方法 14 | func (h *HadeAppProvider) Register(container framework.Container) framework.NewInstance { 15 | return NewHadeApp 16 | } 17 | 18 | // Boot 启动调用 19 | func (h *HadeAppProvider) Boot(container framework.Container) error { 20 | return nil 21 | } 22 | 23 | // IsDefer 是否延迟初始化 24 | func (h *HadeAppProvider) IsDefer() bool { 25 | return false 26 | } 27 | 28 | // Params 获取初始化参数 29 | func (h *HadeAppProvider) Params(container framework.Container) []interface{} { 30 | return []interface{}{container, h.BaseFolder} 31 | } 32 | 33 | // Name 获取字符串凭证 34 | func (h *HadeAppProvider) Name() string { 35 | return contract.AppKey 36 | } 37 | -------------------------------------------------------------------------------- /framework/provider/cache/provider.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/gohade/hade/framework" 7 | "github.com/gohade/hade/framework/contract" 8 | "github.com/gohade/hade/framework/provider/cache/services" 9 | ) 10 | 11 | // HadeCacheProvider 服务提供者 12 | type HadeCacheProvider struct { 13 | framework.ServiceProvider 14 | 15 | Driver string // Driver 16 | } 17 | 18 | // Register 注册一个服务实例 19 | func (l *HadeCacheProvider) Register(c framework.Container) framework.NewInstance { 20 | if l.Driver == "" { 21 | tcs, err := c.Make(contract.ConfigKey) 22 | if err != nil { 23 | // 默认使用console 24 | return services.NewMemoryCache 25 | } 26 | 27 | cs := tcs.(contract.Config) 28 | l.Driver = strings.ToLower(cs.GetString("cache.driver")) 29 | } 30 | 31 | // 根据driver的配置项确定 32 | switch l.Driver { 33 | case "redis": 34 | return services.NewRedisCache 35 | case "memory": 36 | return services.NewMemoryCache 37 | default: 38 | return services.NewMemoryCache 39 | } 40 | } 41 | 42 | // Boot 启动的时候注入 43 | func (l *HadeCacheProvider) Boot(c framework.Container) error { 44 | return nil 45 | } 46 | 47 | // IsDefer 是否延迟加载 48 | func (l *HadeCacheProvider) IsDefer() bool { 49 | return true 50 | } 51 | 52 | // Params 定义要传递给实例化方法的参数 53 | func (l *HadeCacheProvider) Params(c framework.Container) []interface{} { 54 | return []interface{}{c} 55 | } 56 | 57 | // Name 定义对应的服务字符串凭证 58 | func (l *HadeCacheProvider) Name() string { 59 | return contract.CacheKey 60 | } 61 | -------------------------------------------------------------------------------- /framework/provider/cache/services/cache.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "time" 6 | ) 7 | 8 | const ( 9 | NoneDuration = time.Duration(-1) 10 | ) 11 | 12 | var ErrKeyNotFound = errors.New("key not found") 13 | var ErrTypeNotOk = errors.New("val type not ok") 14 | -------------------------------------------------------------------------------- /framework/provider/config/fake_provider.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/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(c framework.Container) []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 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | "path/filepath" 7 | ) 8 | 9 | type HadeConfigProvider struct{} 10 | 11 | // Register registe a new function for make a service instance 12 | func (provider *HadeConfigProvider) Register(c framework.Container) framework.NewInstance { 13 | return NewHadeConfig 14 | } 15 | 16 | // Boot will called when the service instantiate 17 | func (provider *HadeConfigProvider) 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 *HadeConfigProvider) IsDefer() bool { 23 | return false 24 | } 25 | 26 | // Params define the necessary params for NewInstance 27 | func (provider *HadeConfigProvider) Params(c framework.Container) []interface{} { 28 | appService := c.MustMake(contract.AppKey).(contract.App) 29 | envService := c.MustMake(contract.EnvKey).(contract.Env) 30 | env := envService.AppEnv() 31 | // 配置文件夹地址 32 | configFolder := appService.ConfigFolder() 33 | envFolder := filepath.Join(configFolder, env) 34 | return []interface{}{c, envFolder, envService.All()} 35 | } 36 | 37 | /// Name define the name for this service 38 | func (provider *HadeConfigProvider) Name() string { 39 | return contract.ConfigKey 40 | } 41 | -------------------------------------------------------------------------------- /framework/provider/config/provider_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/gohade/hade/framework" 7 | "github.com/gohade/hade/framework/contract" 8 | "github.com/gohade/hade/framework/provider/app" 9 | "github.com/gohade/hade/framework/provider/env" 10 | tests "github.com/gohade/hade/test" 11 | 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | func TestHadeConfig_Normal(t *testing.T) { 16 | Convey("test hade config normal case", t, func() { 17 | basePath := tests.BasePath 18 | c := framework.NewHadeContainer() 19 | c.Bind(&app.HadeAppProvider{BaseFolder: basePath}) 20 | c.Bind(&env.HadeEnvProvider{}) 21 | 22 | err := c.Bind(&HadeConfigProvider{}) 23 | So(err, ShouldBeNil) 24 | 25 | conf := c.MustMake(contract.ConfigKey).(contract.Config) 26 | So(conf.GetString("database.default.host"), ShouldEqual, "localhost") 27 | So(conf.GetInt("database.default.port"), ShouldEqual, 3306) 28 | //So(conf.GetFloat64("database.default.readtime"), ShouldEqual, 2.3) 29 | // So(conf.GetString("database.mysql.password"), ShouldEqual, "mypassword") 30 | 31 | maps := conf.GetStringMap("database.default") 32 | So(maps, ShouldContainKey, "host") 33 | So(maps["host"], ShouldEqual, "localhost") 34 | 35 | maps2 := conf.GetStringMapString("database.default") 36 | So(maps2["host"], ShouldEqual, "localhost") 37 | 38 | type Mysql struct { 39 | Host string `yaml:"host"` 40 | } 41 | ms := &Mysql{} 42 | err = conf.Load("database.default", ms) 43 | So(err, ShouldBeNil) 44 | So(ms.Host, ShouldEqual, "localhost") 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /framework/provider/config/service_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/gohade/hade/framework/contract" 8 | tests "github.com/gohade/hade/test" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestHadeConfig_GetInt(t *testing.T) { 14 | container := tests.InitBaseContainer() 15 | 16 | Convey("test hade env normal case", t, func() { 17 | appService := container.MustMake(contract.AppKey).(contract.App) 18 | envService := container.MustMake(contract.EnvKey).(contract.Env) 19 | folder := filepath.Join(appService.ConfigFolder(), envService.AppEnv()) 20 | 21 | serv, err := NewHadeConfig(container, folder, map[string]string{}) 22 | So(err, ShouldBeNil) 23 | conf := serv.(*HadeConfig) 24 | timeout := conf.GetString("database.default.timeout") 25 | So(timeout, ShouldEqual, "10s") 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /framework/provider/distributed/provider_local.go: -------------------------------------------------------------------------------- 1 | package distributed 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | // LocalDistributedProvider 提供App的具体实现方法 9 | type LocalDistributedProvider struct { 10 | } 11 | 12 | // Register 注册HadeApp方法 13 | func (h *LocalDistributedProvider) Register(container framework.Container) framework.NewInstance { 14 | return NewLocalDistributedService 15 | } 16 | 17 | // Boot 启动调用 18 | func (h *LocalDistributedProvider) Boot(container framework.Container) error { 19 | return nil 20 | } 21 | 22 | // IsDefer 是否延迟初始化 23 | func (h *LocalDistributedProvider) IsDefer() bool { 24 | return false 25 | } 26 | 27 | // Params 获取初始化参数 28 | func (h *LocalDistributedProvider) Params(container framework.Container) []interface{} { 29 | return []interface{}{container} 30 | } 31 | 32 | // Name 获取字符串凭证 33 | func (h *LocalDistributedProvider) Name() string { 34 | return contract.DistributedKey 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/env/provider.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/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.BaseFolder() 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(c framework.Container) []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/testing_provider.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | type HadeTestingEnvProvider struct { 9 | Folder string 10 | } 11 | 12 | // Register registe a new function for make a service instance 13 | func (provider *HadeTestingEnvProvider) Register(c framework.Container) framework.NewInstance { 14 | return NewHadeTestingEnv 15 | } 16 | 17 | // Boot will called when the service instantiate 18 | func (provider *HadeTestingEnvProvider) Boot(c framework.Container) error { 19 | return nil 20 | } 21 | 22 | // IsDefer define whether the service instantiate when first make or register 23 | func (provider *HadeTestingEnvProvider) IsDefer() bool { 24 | return false 25 | } 26 | 27 | // Params define the necessary params for NewInstance 28 | func (provider *HadeTestingEnvProvider) Params(c framework.Container) []interface{} { 29 | return []interface{}{} 30 | } 31 | 32 | /// Name define the name for this service 33 | func (provider *HadeTestingEnvProvider) Name() string { 34 | return contract.EnvKey 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/env/testing_service.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | // HadeEnv 是 Env 的具体实现 4 | type HadeTestingEnv struct { 5 | folder string // 代表.env所在的目录 6 | maps map[string]string // 保存所有的环境变量 7 | } 8 | 9 | // NewHadeEnv 有一个参数,.env文件所在的目录 10 | // example: NewHadeEnv("/envfolder/") 会读取文件: /envfolder/.env 11 | // .env的文件格式 FOO_ENV=BAR 12 | func NewHadeTestingEnv(params ...interface{}) (interface{}, error) { 13 | 14 | // 返回实例 15 | return &HadeTestingEnv{}, nil 16 | } 17 | 18 | // AppEnv 获取表示当前APP环境的变量APP_ENV 19 | func (en *HadeTestingEnv) AppEnv() string { 20 | return "testing" 21 | } 22 | 23 | // IsExist 判断一个环境变量是否有被设置 24 | func (en *HadeTestingEnv) IsExist(key string) bool { 25 | _, ok := en.maps[key] 26 | return ok 27 | } 28 | 29 | // Get 获取某个环境变量,如果没有设置,返回"" 30 | func (en *HadeTestingEnv) Get(key string) string { 31 | if val, ok := en.maps[key]; ok { 32 | return val 33 | } 34 | return "" 35 | } 36 | 37 | // All 获取所有的环境变量,.env和运行环境变量融合后结果 38 | func (en *HadeTestingEnv) All() map[string]string { 39 | return en.maps 40 | } 41 | -------------------------------------------------------------------------------- /framework/provider/id/provider.go: -------------------------------------------------------------------------------- 1 | package id 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/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(c framework.Container) []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 | tests "github.com/gohade/hade/test" 5 | "testing" 6 | 7 | "github.com/gohade/hade/framework/contract" 8 | "github.com/gohade/hade/framework/provider/config" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestConsoleLog_Normal(t *testing.T) { 13 | Convey("test hade console log normal case", t, func() { 14 | c := tests.InitBaseContainer() 15 | c.Bind(&config.HadeConfigProvider{}) 16 | 17 | err := c.Bind(&HadeIDProvider{}) 18 | So(err, ShouldBeNil) 19 | 20 | idService := c.MustMake(contract.IDKey).(contract.IDService) 21 | xid := idService.NewID() 22 | So(xid, ShouldNotBeEmpty) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /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 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | "github.com/gohade/hade/framework/gin" 7 | ) 8 | 9 | // HadeKernelProvider 提供web引擎 10 | type HadeKernelProvider struct { 11 | HttpEngine *gin.Engine 12 | } 13 | 14 | // Register 注册服务提供者 15 | func (provider *HadeKernelProvider) Register(c framework.Container) framework.NewInstance { 16 | return NewHadeKernelService 17 | } 18 | 19 | // Boot 启动的时候判断是否由外界注入了Engine,如果注入的化,用注入的,如果没有,重新实例化 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) 25 | return nil 26 | } 27 | 28 | // IsDefer 引擎的初始化我们希望开始就进行初始化 29 | func (provider *HadeKernelProvider) IsDefer() bool { 30 | return false 31 | } 32 | 33 | // Params 参数就是一个HttpEngine 34 | func (provider *HadeKernelProvider) Params(c framework.Container) []interface{} { 35 | return []interface{}{provider.HttpEngine} 36 | } 37 | 38 | // Name 提供凭证 39 | func (provider *HadeKernelProvider) Name() string { 40 | return contract.KernelKey 41 | } 42 | -------------------------------------------------------------------------------- /framework/provider/kernel/service.go: -------------------------------------------------------------------------------- 1 | package kernel 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/gin" 5 | "net/http" 6 | ) 7 | 8 | // 引擎服务 9 | type HadeKernelService struct { 10 | engine *gin.Engine 11 | } 12 | 13 | // 初始化web引擎服务实例 14 | func NewHadeKernelService(params ...interface{}) (interface{}, error) { 15 | httpEngine := params[0].(*gin.Engine) 16 | return &HadeKernelService{engine: httpEngine}, nil 17 | } 18 | 19 | // 返回web引擎 20 | func (s *HadeKernelService) HttpEngine() http.Handler { 21 | return s.engine 22 | } 23 | -------------------------------------------------------------------------------- /framework/provider/log/formatter/json.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "time" 7 | 8 | "github.com/gohade/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{}) 15 | fields["msg"] = msg 16 | fields["level"] = level 17 | fields["timestamp"] = t.Format(time.RFC3339) 18 | c, err := json.Marshal(fields) 19 | if err != nil { 20 | return bf.Bytes(), errors.Wrap(err, "json format error") 21 | } 22 | 23 | bf.Write(c) 24 | return bf.Bytes(), nil 25 | } 26 | -------------------------------------------------------------------------------- /framework/provider/log/formatter/prefix.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import "github.com/gohade/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 | "github.com/gohade/hade/framework/contract" 9 | ) 10 | 11 | // TextFormatter 表示文本格式输出 12 | func TextFormatter(level contract.LogLevel, t time.Time, msg string, fields map[string]interface{}) ([]byte, error) { 13 | bf := bytes.NewBuffer([]byte{}) 14 | Separator := "\t" 15 | 16 | // 先输出日志级别 17 | prefix := Prefix(level) 18 | 19 | bf.WriteString(prefix) 20 | bf.WriteString(Separator) 21 | 22 | // 输出时间 23 | ts := t.Format(time.RFC3339) 24 | bf.WriteString(ts) 25 | bf.WriteString(Separator) 26 | 27 | // 输出msg 28 | bf.WriteString("\"") 29 | bf.WriteString(msg) 30 | bf.WriteString("\"") 31 | bf.WriteString(Separator) 32 | 33 | // 输出map 34 | bf.WriteString(fmt.Sprint(fields)) 35 | return bf.Bytes(), nil 36 | } 37 | -------------------------------------------------------------------------------- /framework/provider/log/services/console.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/gohade/hade/framework" 7 | "github.com/gohade/hade/framework/contract" 8 | ) 9 | 10 | // HadeConsoleLog 代表控制台输出 11 | type HadeConsoleLog struct { 12 | HadeLog 13 | } 14 | 15 | // NewHadeConsoleLog 实例化HadeConsoleLog 16 | func NewHadeConsoleLog(params ...interface{}) (interface{}, error) { 17 | c := params[0].(framework.Container) 18 | level := params[1].(contract.LogLevel) 19 | ctxFielder := params[2].(contract.CtxFielder) 20 | formatter := params[3].(contract.Formatter) 21 | 22 | log := &HadeConsoleLog{} 23 | 24 | log.SetLevel(level) 25 | log.SetCtxFielder(ctxFielder) 26 | log.SetFormatter(formatter) 27 | 28 | // 最重要的将内容输出到控制台 29 | log.SetOutput(os.Stdout) 30 | log.c = c 31 | return log, nil 32 | } 33 | -------------------------------------------------------------------------------- /framework/provider/log/services/custom.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | "io" 7 | ) 8 | 9 | type HadeCustomLog struct { 10 | HadeLog 11 | } 12 | 13 | func NewHadeCustomLog(params ...interface{}) (interface{}, error) { 14 | c := params[0].(framework.Container) 15 | level := params[1].(contract.LogLevel) 16 | ctxFielder := params[2].(contract.CtxFielder) 17 | formatter := params[3].(contract.Formatter) 18 | output := params[4].(io.Writer) 19 | 20 | log := &HadeConsoleLog{} 21 | 22 | log.SetLevel(level) 23 | log.SetCtxFielder(ctxFielder) 24 | log.SetFormatter(formatter) 25 | 26 | log.SetOutput(output) 27 | log.c = c 28 | return log, nil 29 | } 30 | -------------------------------------------------------------------------------- /framework/provider/log/services/single.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/util" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/gohade/hade/framework" 9 | "github.com/gohade/hade/framework/contract" 10 | 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | type HadeSingleLog struct { 15 | HadeLog 16 | 17 | folder string 18 | file string 19 | fd *os.File 20 | } 21 | 22 | // NewHadeSingleLog params sequence: level, ctxFielder, Formatter, map[string]interface(folder/file) 23 | func NewHadeSingleLog(params ...interface{}) (interface{}, error) { 24 | c := params[0].(framework.Container) 25 | level := params[1].(contract.LogLevel) 26 | ctxFielder := params[2].(contract.CtxFielder) 27 | formatter := params[3].(contract.Formatter) 28 | 29 | appService := c.MustMake(contract.AppKey).(contract.App) 30 | configService := c.MustMake(contract.ConfigKey).(contract.Config) 31 | 32 | log := &HadeSingleLog{} 33 | log.SetLevel(level) 34 | log.SetCtxFielder(ctxFielder) 35 | log.SetFormatter(formatter) 36 | 37 | folder := appService.LogFolder() 38 | if configService.IsExist("log.folder") { 39 | folder = configService.GetString("log.folder") 40 | } 41 | log.folder = folder 42 | if !util.Exists(folder) { 43 | os.MkdirAll(folder, os.ModePerm) 44 | } 45 | 46 | log.file = "hade.log" 47 | if configService.IsExist("log.file") { 48 | log.file = configService.GetString("log.file") 49 | } 50 | 51 | fd, err := os.OpenFile(filepath.Join(log.folder, log.file), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 52 | if err != nil { 53 | return nil, errors.Wrap(err, "open log file err") 54 | } 55 | 56 | log.SetOutput(fd) 57 | log.c = c 58 | 59 | return log, nil 60 | } 61 | -------------------------------------------------------------------------------- /framework/provider/orm/config.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "context" 5 | "github.com/gohade/hade/framework" 6 | "github.com/gohade/hade/framework/contract" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // GetBaseConfig 读取database.yaml根目录结构 11 | func GetBaseConfig(c framework.Container) *contract.DBConfig { 12 | configService := c.MustMake(contract.ConfigKey).(contract.Config) 13 | logService := c.MustMake(contract.LogKey).(contract.Log) 14 | config := &contract.DBConfig{} 15 | // 直接使用配置服务的load方法读取,yaml文件 16 | err := configService.Load("database", config) 17 | if err != nil { 18 | // 直接使用logService来打印错误信息 19 | logService.Error(context.Background(), "parse database config error", nil) 20 | return nil 21 | } 22 | return config 23 | } 24 | 25 | // WithConfigPath 加载配置文件地址 26 | func WithConfigPath(configPath string) contract.DBOption { 27 | return func(container framework.Container, config *contract.DBConfig) error { 28 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 29 | // 加载configPath配置路径 30 | if err := configService.Load(configPath, config); err != nil { 31 | return err 32 | } 33 | return nil 34 | } 35 | } 36 | 37 | // WithGormConfig 表示自行配置Gorm的配置信息 38 | func WithGormConfig(gormConfig *gorm.Config) contract.DBOption { 39 | return func(container framework.Container, config *contract.DBConfig) error { 40 | if gormConfig.Logger == nil { 41 | gormConfig.Logger = config.Logger 42 | } 43 | config.Config = gormConfig 44 | return nil 45 | } 46 | } 47 | 48 | // WithDryRun 设置空跑模式 49 | func WithDryRun() contract.DBOption { 50 | return func(container framework.Container, config *contract.DBConfig) error { 51 | config.DryRun = true 52 | return nil 53 | } 54 | } 55 | 56 | // WithFullSaveAssociations 设置保存时候关联 57 | func WithFullSaveAssociations() contract.DBOption { 58 | return func(container framework.Container, config *contract.DBConfig) error { 59 | config.FullSaveAssociations = true 60 | return nil 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /framework/provider/orm/config_test.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/contract" 5 | "github.com/gohade/hade/framework/provider/config" 6 | tests "github.com/gohade/hade/test" 7 | . "github.com/smartystreets/goconvey/convey" 8 | "testing" 9 | ) 10 | 11 | func TestHadeConfig_Load(t *testing.T) { 12 | container := tests.InitBaseContainer() 13 | container.Bind(&config.HadeConfigProvider{}) 14 | 15 | Convey("test config", t, func() { 16 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 17 | config := &contract.DBConfig{} 18 | err := configService.Load("database.default", config) 19 | So(err, ShouldBeNil) 20 | }) 21 | 22 | Convey("test default config", t, func() { 23 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 24 | config := &contract.DBConfig{ 25 | ConnMaxIdle: 10, 26 | } 27 | err := configService.Load("database.read", config) 28 | So(err, ShouldBeNil) 29 | So(config.ConnMaxIdle, ShouldEqual, 10) 30 | }) 31 | 32 | Convey("test base config", t, func() { 33 | configService := container.MustMake(contract.ConfigKey).(contract.Config) 34 | config := &contract.DBConfig{ 35 | ConnMaxOpen: 200, 36 | } 37 | err := configService.Load("database", config) 38 | So(err, ShouldBeNil) 39 | So(config.ConnMaxOpen, ShouldEqual, 100) 40 | }) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /framework/provider/orm/logger.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "context" 5 | "github.com/gohade/hade/framework/contract" 6 | "gorm.io/gorm/logger" 7 | "time" 8 | ) 9 | 10 | // OrmLogger orm的日志实现类, 实现了gorm.Logger.Interface 11 | type OrmLogger struct { 12 | logger contract.Log // 有一个logger对象存放hade的log服务 13 | } 14 | 15 | // NewOrmLogger 初始化一个ormLogger, 16 | func NewOrmLogger(logger contract.Log) *OrmLogger { 17 | return &OrmLogger{logger: logger} 18 | } 19 | 20 | // LogMode 什么都不实现,日志级别完全依赖hade的日志定义 21 | func (o *OrmLogger) LogMode(level logger.LogLevel) logger.Interface { 22 | return o 23 | } 24 | 25 | // Info 对接hade的info输出 26 | func (o *OrmLogger) Info(ctx context.Context, s string, i ...interface{}) { 27 | fields := map[string]interface{}{ 28 | "fields": i, 29 | } 30 | o.logger.Info(ctx, s, fields) 31 | } 32 | 33 | // Warn 对接hade的Warn输出 34 | func (o *OrmLogger) Warn(ctx context.Context, s string, i ...interface{}) { 35 | fields := map[string]interface{}{ 36 | "fields": i, 37 | } 38 | o.logger.Warn(ctx, s, fields) 39 | } 40 | 41 | // Error 对接hade的Error输出 42 | func (o *OrmLogger) Error(ctx context.Context, s string, i ...interface{}) { 43 | fields := map[string]interface{}{ 44 | "fields": i, 45 | } 46 | o.logger.Error(ctx, s, fields) 47 | } 48 | 49 | // Trace 对接hade的Trace输出 50 | func (o *OrmLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { 51 | sql, rows := fc() 52 | elapsed := time.Since(begin) 53 | fields := map[string]interface{}{ 54 | "begin": begin, 55 | "error": err, 56 | "sql": sql, 57 | "rows": rows, 58 | "time": elapsed, 59 | } 60 | 61 | s := "orm trace sql" 62 | o.logger.Trace(ctx, s, fields) 63 | } 64 | -------------------------------------------------------------------------------- /framework/provider/orm/provider.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | // GormProvider 提供App的具体实现方法 9 | type GormProvider struct { 10 | } 11 | 12 | // Register 注册方法 13 | func (h *GormProvider) Register(container framework.Container) framework.NewInstance { 14 | return NewHadeGorm 15 | } 16 | 17 | // Boot 启动调用 18 | func (h *GormProvider) Boot(container framework.Container) error { 19 | return nil 20 | } 21 | 22 | // IsDefer 是否延迟初始化 23 | func (h *GormProvider) IsDefer() bool { 24 | return true 25 | } 26 | 27 | // Params 获取初始化参数 28 | func (h *GormProvider) Params(container framework.Container) []interface{} { 29 | return []interface{}{container} 30 | } 31 | 32 | // Name 获取字符串凭证 33 | func (h *GormProvider) Name() string { 34 | return contract.ORMKey 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/redis/provider.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | // RedisProvider 提供App的具体实现方法 9 | type RedisProvider struct { 10 | } 11 | 12 | // Register 注册方法 13 | func (h *RedisProvider) Register(container framework.Container) framework.NewInstance { 14 | return NewHadeRedis 15 | } 16 | 17 | // Boot 启动调用 18 | func (h *RedisProvider) Boot(container framework.Container) error { 19 | return nil 20 | } 21 | 22 | // IsDefer 是否延迟初始化 23 | func (h *RedisProvider) IsDefer() bool { 24 | return true 25 | } 26 | 27 | // Params 获取初始化参数 28 | func (h *RedisProvider) Params(container framework.Container) []interface{} { 29 | return []interface{}{container} 30 | } 31 | 32 | // Name 获取字符串凭证 33 | func (h *RedisProvider) Name() string { 34 | return contract.RedisKey 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/redis/service.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "github.com/go-redis/redis/v8" 5 | "github.com/gohade/hade/framework" 6 | "github.com/gohade/hade/framework/contract" 7 | "sync" 8 | ) 9 | 10 | // HadeRedis 代表hade框架的redis实现 11 | type HadeRedis struct { 12 | container framework.Container // 服务容器 13 | clients map[string]*redis.Client // key为uniqKey, value为redis.Client (连接池) 14 | 15 | lock *sync.RWMutex 16 | } 17 | 18 | // NewHadeRedis 代表实例化Client 19 | func NewHadeRedis(params ...interface{}) (interface{}, error) { 20 | container := params[0].(framework.Container) 21 | clients := make(map[string]*redis.Client) 22 | lock := &sync.RWMutex{} 23 | return &HadeRedis{ 24 | container: container, 25 | clients: clients, 26 | lock: lock, 27 | }, nil 28 | } 29 | 30 | // GetClient 获取Client实例 31 | func (app *HadeRedis) GetClient(option ...contract.RedisOption) (*redis.Client, error) { 32 | // 读取默认配置 33 | config := GetBaseConfig(app.container) 34 | 35 | // option对opt进行修改 36 | for _, opt := range option { 37 | if err := opt(app.container, config); err != nil { 38 | return nil, err 39 | } 40 | } 41 | 42 | // 如果最终的config没有设置dsn,就生成dsn 43 | key := config.UniqKey() 44 | 45 | // 判断是否已经实例化了redis.Client 46 | app.lock.RLock() 47 | if db, ok := app.clients[key]; ok { 48 | app.lock.RUnlock() 49 | return db, nil 50 | } 51 | app.lock.RUnlock() 52 | 53 | // 没有实例化gorm.DB,那么就要进行实例化操作 54 | app.lock.Lock() 55 | defer app.lock.Unlock() 56 | 57 | // 实例化gorm.DB 58 | client := redis.NewClient(config.Options) 59 | 60 | // 挂载到map中,结束配置 61 | app.clients[key] = client 62 | 63 | return client, nil 64 | } 65 | -------------------------------------------------------------------------------- /framework/provider/redis/service_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "github.com/gohade/hade/framework/provider/config" 6 | "github.com/gohade/hade/framework/provider/log" 7 | tests "github.com/gohade/hade/test" 8 | . "github.com/smartystreets/goconvey/convey" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | func TestHadeService_Load(t *testing.T) { 14 | container := tests.InitBaseContainer() 15 | container.Bind(&config.HadeConfigProvider{}) 16 | container.Bind(&log.HadeLogServiceProvider{}) 17 | 18 | Convey("test get client", t, func() { 19 | hadeRedis, err := NewHadeRedis(container) 20 | So(err, ShouldBeNil) 21 | service, ok := hadeRedis.(*HadeRedis) 22 | So(ok, ShouldBeTrue) 23 | client, err := service.GetClient(WithConfigPath("redis.write")) 24 | So(err, ShouldBeNil) 25 | So(client, ShouldNotBeNil) 26 | ctx := context.Background() 27 | err = client.Set(ctx, "foo", "bar", 1*time.Hour).Err() 28 | So(err, ShouldBeNil) 29 | val, err := client.Get(ctx, "foo").Result() 30 | So(err, ShouldBeNil) 31 | So(val, ShouldEqual, "bar") 32 | err = client.Del(ctx, "foo").Err() 33 | So(err, ShouldBeNil) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/ssh/provider.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/contract" 6 | ) 7 | 8 | // SSHProvider 提供App的具体实现方法 9 | type SSHProvider struct { 10 | } 11 | 12 | // Register 注册方法 13 | func (h *SSHProvider) Register(container framework.Container) framework.NewInstance { 14 | return NewHadeSSH 15 | } 16 | 17 | // Boot 启动调用 18 | func (h *SSHProvider) Boot(container framework.Container) error { 19 | return nil 20 | } 21 | 22 | // IsDefer 是否延迟初始化 23 | func (h *SSHProvider) IsDefer() bool { 24 | return true 25 | } 26 | 27 | // Params 获取初始化参数 28 | func (h *SSHProvider) Params(container framework.Container) []interface{} { 29 | return []interface{}{container} 30 | } 31 | 32 | // Name 获取字符串凭证 33 | func (h *SSHProvider) Name() string { 34 | return contract.SSHKey 35 | } 36 | -------------------------------------------------------------------------------- /framework/provider/ssh/service.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | import ( 4 | "context" 5 | "github.com/gohade/hade/framework" 6 | "github.com/gohade/hade/framework/contract" 7 | "golang.org/x/crypto/ssh" 8 | "sync" 9 | ) 10 | 11 | // HadeSSH 代表hade框架的ssh实现 12 | type HadeSSH struct { 13 | container framework.Container // 服务容器 14 | clients map[string]*ssh.Client // key为uniqKey, value为ssh.Client(连接池) 15 | 16 | lock *sync.RWMutex 17 | } 18 | 19 | // NewHadeSSH 代表实例化Client 20 | func NewHadeSSH(params ...interface{}) (interface{}, error) { 21 | container := params[0].(framework.Container) 22 | clients := make(map[string]*ssh.Client) 23 | lock := &sync.RWMutex{} 24 | return &HadeSSH{ 25 | container: container, 26 | clients: clients, 27 | lock: lock, 28 | }, nil 29 | } 30 | 31 | // GetClient 获取Client实例 32 | func (app *HadeSSH) GetClient(option ...contract.SSHOption) (*ssh.Client, error) { 33 | logService := app.container.MustMake(contract.LogKey).(contract.Log) 34 | // 读取默认配置 35 | config := GetBaseConfig(app.container) 36 | 37 | // option对opt进行修改 38 | for _, opt := range option { 39 | if err := opt(app.container, config); err != nil { 40 | return nil, err 41 | } 42 | } 43 | 44 | // 如果最终的config没有设置dsn,就生成dsn 45 | key := config.UniqKey() 46 | 47 | // 判断是否已经实例化了 48 | app.lock.RLock() 49 | if db, ok := app.clients[key]; ok { 50 | app.lock.RUnlock() 51 | return db, nil 52 | } 53 | app.lock.RUnlock() 54 | 55 | // 没有实例化,那么就要进行实例化操作 56 | app.lock.Lock() 57 | defer app.lock.Unlock() 58 | 59 | // 实例化 60 | addr := config.Host + ":" + config.Port 61 | client, err := ssh.Dial(config.NetWork, addr, config.ClientConfig) 62 | if err != nil { 63 | logService.Error(context.Background(), "ssh dial error", map[string]interface{}{ 64 | "err": err, 65 | "addr": addr, 66 | }) 67 | } 68 | 69 | // 挂载到map中,结束配置 70 | app.clients[key] = client 71 | 72 | return client, nil 73 | } 74 | -------------------------------------------------------------------------------- /framework/provider/ssh/service_test.go: -------------------------------------------------------------------------------- 1 | package ssh 2 | 3 | import ( 4 | "github.com/gohade/hade/framework/provider/config" 5 | "github.com/gohade/hade/framework/provider/log" 6 | tests "github.com/gohade/hade/test" 7 | . "github.com/smartystreets/goconvey/convey" 8 | "testing" 9 | ) 10 | 11 | func TestHadeSSHService_Load(t *testing.T) { 12 | container := tests.InitBaseContainer() 13 | container.Bind(&config.HadeConfigProvider{}) 14 | container.Bind(&log.HadeLogServiceProvider{}) 15 | 16 | Convey("test get client", t, func() { 17 | hadeRedis, err := NewHadeSSH(container) 18 | So(err, ShouldBeNil) 19 | service, ok := hadeRedis.(*HadeSSH) 20 | So(ok, ShouldBeTrue) 21 | client, err := service.GetClient(WithConfigPath("ssh.web-01")) 22 | So(err, ShouldBeNil) 23 | So(client, ShouldNotBeNil) 24 | session, err := client.NewSession() 25 | So(err, ShouldBeNil) 26 | out, err := session.Output("pwd") 27 | So(err, ShouldBeNil) 28 | So(out, ShouldNotBeNil) 29 | session.Close() 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /framework/provider/trace/provider.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/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(c framework.Container) []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 | // PrettyPrint 美观输出数组 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 | // GetExecDirectory 获取当前执行程序目录 9 | func GetExecDirectory() string { 10 | file, err := os.Getwd() 11 | if err == nil { 12 | return file + "/" 13 | } 14 | return "" 15 | } 16 | 17 | // CheckProcessExist 检查进程pid是否存在,如果存在的话,返回true 18 | func CheckProcessExist(pid int) bool { 19 | // 查询这个pid 20 | process, err := os.FindProcess(pid) 21 | if err != nil { 22 | return false 23 | } 24 | 25 | // 给进程发送signal 0, 如果返回nil,代表进程存在, 否则进程不存在 26 | err = process.Signal(syscall.Signal(0)) 27 | if err != nil { 28 | return false 29 | } 30 | return true 31 | } 32 | -------------------------------------------------------------------------------- /framework/util/http.go: -------------------------------------------------------------------------------- 1 | package util 2 | -------------------------------------------------------------------------------- /framework/util/zip.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "archive/zip" 5 | "fmt" 6 | "io" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // Unzip will decompress a zip archive, moving all files and folders 13 | // within the zip file (parameter 1) to an output directory (parameter 2). 14 | func Unzip(src string, dest string) ([]string, error) { 15 | 16 | var filenames []string 17 | 18 | r, err := zip.OpenReader(src) 19 | if err != nil { 20 | return filenames, err 21 | } 22 | defer r.Close() 23 | 24 | for _, f := range r.File { 25 | 26 | // Store filename/path for returning and using later on 27 | fpath := filepath.Join(dest, f.Name) 28 | 29 | // Check for ZipSlip. More Info: http://bit.ly/2MsjAWE 30 | if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) { 31 | return filenames, fmt.Errorf("%s: illegal file path", fpath) 32 | } 33 | 34 | filenames = append(filenames, fpath) 35 | 36 | if f.FileInfo().IsDir() { 37 | // Make Folder 38 | os.MkdirAll(fpath, os.ModePerm) 39 | continue 40 | } 41 | 42 | // Make File 43 | if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil { 44 | return filenames, err 45 | } 46 | 47 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) 48 | if err != nil { 49 | return filenames, err 50 | } 51 | 52 | rc, err := f.Open() 53 | if err != nil { 54 | return filenames, err 55 | } 56 | 57 | _, err = io.Copy(outFile, rc) 58 | 59 | // Close the file without defer to close before next iteration of loop 60 | outFile.Close() 61 | rc.Close() 62 | 63 | if err != nil { 64 | return filenames, err 65 | } 66 | } 67 | return filenames, nil 68 | } 69 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gohade/hade 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.3.2 7 | github.com/cpuguy83/go-md2man/v2 v2.0.0 8 | github.com/erikdubbelboer/gspt v0.0.0-20210805194459-ce36a5128377 9 | github.com/fsnotify/fsnotify v1.4.9 10 | github.com/gin-contrib/cors v1.3.1 11 | github.com/gin-contrib/sse v0.1.0 12 | github.com/go-git/go-git/v5 v5.4.2 13 | github.com/go-playground/validator/v10 v10.4.1 14 | github.com/go-redis/redis/v8 v8.11.4 15 | github.com/go-sql-driver/mysql v1.6.0 16 | github.com/golang/protobuf v1.5.2 17 | github.com/google/go-github/v39 v39.1.0 18 | github.com/google/uuid v1.3.0 19 | github.com/inconshreveable/mousetrap v1.0.0 20 | github.com/jianfengye/collection v1.3.2 21 | github.com/jonboulle/clockwork v0.2.2 // indirect 22 | github.com/json-iterator/go v1.1.11 23 | github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect 24 | github.com/kr/pretty v0.3.0 25 | github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible 26 | github.com/lestrrat-go/strftime v1.0.5 // indirect 27 | github.com/mattn/go-isatty v0.0.12 28 | github.com/mitchellh/mapstructure v1.4.1 29 | github.com/pkg/errors v0.9.1 30 | github.com/pkg/sftp v1.13.4 31 | github.com/robfig/cron/v3 v3.0.0 32 | github.com/rs/xid v1.3.0 33 | github.com/sevlyar/go-daemon v0.1.5 34 | github.com/smartystreets/goconvey v1.6.4 35 | github.com/spf13/cast v1.4.0 36 | github.com/spf13/cobra v1.2.1 37 | github.com/spf13/pflag v1.0.5 38 | github.com/spf13/viper v1.8.1 39 | github.com/stretchr/testify v1.7.0 40 | github.com/swaggo/gin-swagger v1.3.2 41 | github.com/swaggo/swag v1.7.3 42 | github.com/ugorji/go/codec v1.1.7 43 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 44 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 45 | gopkg.in/yaml.v2 v2.4.0 46 | gorm.io/driver/clickhouse v0.2.1 47 | gorm.io/driver/mysql v1.1.2 48 | gorm.io/driver/postgres v1.1.2 49 | gorm.io/driver/sqlite v1.1.6 50 | gorm.io/driver/sqlserver v1.1.0 51 | gorm.io/gorm v1.21.16 52 | ) 53 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | hade 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 jianfengye. 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 | package main 5 | 6 | import ( 7 | "github.com/gohade/hade/app/console" 8 | "github.com/gohade/hade/app/http" 9 | "github.com/gohade/hade/framework" 10 | "github.com/gohade/hade/framework/provider/app" 11 | "github.com/gohade/hade/framework/provider/cache" 12 | "github.com/gohade/hade/framework/provider/config" 13 | "github.com/gohade/hade/framework/provider/distributed" 14 | "github.com/gohade/hade/framework/provider/env" 15 | "github.com/gohade/hade/framework/provider/id" 16 | "github.com/gohade/hade/framework/provider/kernel" 17 | "github.com/gohade/hade/framework/provider/log" 18 | "github.com/gohade/hade/framework/provider/orm" 19 | "github.com/gohade/hade/framework/provider/redis" 20 | "github.com/gohade/hade/framework/provider/ssh" 21 | "github.com/gohade/hade/framework/provider/trace" 22 | ) 23 | 24 | func main() { 25 | // 初始化服务容器 26 | container := framework.NewHadeContainer() 27 | // 绑定App服务提供者 28 | container.Bind(&app.HadeAppProvider{}) 29 | // 后续初始化需要绑定的服务提供者... 30 | container.Bind(&env.HadeEnvProvider{}) 31 | container.Bind(&distributed.LocalDistributedProvider{}) 32 | container.Bind(&config.HadeConfigProvider{}) 33 | container.Bind(&id.HadeIDProvider{}) 34 | container.Bind(&trace.HadeTraceProvider{}) 35 | container.Bind(&log.HadeLogServiceProvider{}) 36 | container.Bind(&orm.GormProvider{}) 37 | container.Bind(&redis.RedisProvider{}) 38 | container.Bind(&cache.HadeCacheProvider{}) 39 | container.Bind(&ssh.SSHProvider{}) 40 | 41 | // 将HTTP引擎初始化,并且作为服务提供者绑定到服务容器中 42 | if engine, err := http.NewHttpEngine(container); err == nil { 43 | container.Bind(&kernel.HadeKernelProvider{HttpEngine: engine}) 44 | } 45 | 46 | // 运行root命令 47 | console.RunCommand(container) 48 | } 49 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/src/assets/logo.png -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | 7 | Vue.config.productionTip = false 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | router, 13 | components: { App }, 14 | template: '' 15 | }) 16 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import HelloWorld from '@/components/HelloWorld' 4 | 5 | Vue.use(Router) 6 | 7 | export default new Router({ 8 | routes: [ 9 | { 10 | path: '/', 11 | name: 'HelloWorld', 12 | component: HelloWorld 13 | } 14 | ] 15 | }) 16 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/static/.gitkeep -------------------------------------------------------------------------------- /storage/log/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/storage/log/.gitignore -------------------------------------------------------------------------------- /storage/runtime/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gohade/coredemo/efdbf2b0eec68149092dbb65714af62c307c582d/storage/runtime/.gitignore -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // The assertion name is the filename. 3 | // Example usage: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // For more information on custom assertions see: 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | 10 | exports.assertion = function (selector, count) { 11 | this.message = 'Testing if element <' + selector + '> has count: ' + count 12 | this.expected = count 13 | this.pass = function (val) { 14 | return val === this.expected 15 | } 16 | this.value = function (res) { 17 | return res.value 18 | } 19 | this.command = function (cb) { 20 | var self = this 21 | return this.api.execute(function (selector) { 22 | return document.querySelectorAll(selector).length 23 | }, [selector], function (res) { 24 | cb.call(self, res) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | 4 | const webpack = require('webpack') 5 | const DevServer = require('webpack-dev-server') 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf') 8 | const devConfigPromise = require('../../build/webpack.dev.conf') 9 | 10 | let server 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer 14 | const compiler = webpack(webpackConfig) 15 | server = new DevServer(compiler, devServerOptions) 16 | const port = devServerOptions.port 17 | const host = devServerOptions.host 18 | return server.listen(port, host) 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2) 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']) 34 | } 35 | 36 | const spawn = require('cross-spawn') 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 38 | 39 | runner.on('exit', function (code) { 40 | server.close() 41 | process.exit(code) 42 | }) 43 | 44 | runner.on('error', function (err) { 45 | server.close() 46 | throw err 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/env.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/gohade/hade/framework" 5 | "github.com/gohade/hade/framework/provider/app" 6 | "github.com/gohade/hade/framework/provider/env" 7 | ) 8 | 9 | const ( 10 | BasePath = "/Users/yejianfeng/Documents/UGit/coredemo/" 11 | ) 12 | 13 | func InitBaseContainer() framework.Container { 14 | // 初始化服务容器 15 | container := framework.NewHadeContainer() 16 | // 绑定App服务提供者 17 | container.Bind(&app.HadeAppProvider{BaseFolder: BasePath}) 18 | // 后续初始化需要绑定的服务提供者... 19 | container.Bind(&env.HadeTestingEnvProvider{}) 20 | return container 21 | } 22 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | rootDir: path.resolve(__dirname, '../../'), 5 | moduleFileExtensions: [ 6 | 'js', 7 | 'json', 8 | 'vue' 9 | ], 10 | moduleNameMapper: { 11 | '^@/(.*)$': '/src/$1' 12 | }, 13 | transform: { 14 | '^.+\\.js$': '/node_modules/babel-jest', 15 | '.*\\.(vue)$': '/node_modules/vue-jest' 16 | }, 17 | testPathIgnorePatterns: [ 18 | '/test/e2e' 19 | ], 20 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 21 | setupFiles: ['/test/unit/setup'], 22 | mapCoverage: true, 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!src/router/index.js', 28 | '!**/node_modules/**' 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | -------------------------------------------------------------------------------- /test/unit/specs/HelloWorld.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .toEqual('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------