├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ ├── config.yml │ └── feature_request.yaml └── workflows │ ├── build_test.yml │ ├── devops-test.yaml │ ├── devops.yml │ └── docker_build.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README-en.md ├── README.md ├── SECURITY.md ├── deployment ├── server │ ├── gva-server-configmap.yaml │ ├── gva-server-deployment.yaml │ └── gva-server-service.yaml └── web │ ├── gva-web-configmap.yaml │ ├── gva-web-deploymemt.yaml │ └── gva-web-service.yaml ├── docker-compose-dev.yaml ├── docker-compose.yaml ├── docs ├── .gitignore ├── README.md ├── babel.config.js ├── blog │ ├── 2021-08-26-welcome │ │ ├── docusaurus-plushie-banner.jpeg │ │ └── index.md │ ├── 2022-06-27-first.md │ ├── 2022-07-25-introduce │ │ ├── architecture-en.png │ │ ├── introduce.md │ │ ├── rollback.png │ │ └── rules.png │ └── authors.yml ├── develop │ ├── community │ │ ├── community.md │ │ └── owls-wechat.jpg │ ├── contribution.md │ ├── db-tables-and-init.md │ ├── design │ │ ├── _category_.json │ │ ├── db-auth.md │ │ ├── db-data-read.md │ │ ├── redis-write.md │ │ ├── template.md │ │ └── user-login.md │ ├── develop-env-build.md │ ├── intro.md │ └── roadmap.md ├── docs │ ├── intro.md │ ├── tutorial-basics │ │ ├── _category_.json │ │ ├── congratulations.md │ │ ├── create-a-blog-post.md │ │ ├── create-a-document.md │ │ ├── create-a-page.md │ │ ├── deploy-your-site.md │ │ └── markdown-features.mdx │ └── tutorial-extras │ │ ├── _category_.json │ │ ├── img │ │ ├── docsVersionDropdown.png │ │ └── localeDropdown.png │ │ ├── manage-docs-versions.md │ │ └── translate-your-site.md ├── docusaurus.config.js ├── package-lock.json ├── package.json ├── sidebars.js ├── src │ ├── components │ │ └── HomepageFeatures │ │ │ ├── index.js │ │ │ └── styles.module.css │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ ├── index.module.css │ │ └── markdown-page.md ├── static │ ├── .nojekyll │ └── img │ │ ├── docusaurus.png │ │ ├── favicon-owl.ico │ │ ├── favicon.ico │ │ ├── undraw_docusaurus_mountain.svg │ │ ├── undraw_docusaurus_react.svg │ │ └── undraw_docusaurus_tree.svg └── user_guide │ ├── auth.md │ ├── db │ ├── _category_.json │ ├── intro.md │ ├── read-sql.md │ └── write-sql.md │ ├── deployment.yaml │ ├── intro.md │ ├── quick-start.md │ ├── sidebarsCommunity.js │ └── user.md ├── go ├── Dockerfile ├── README.md ├── api │ └── v1 │ │ ├── auth │ │ └── auth.go │ │ ├── autocode │ │ ├── auto_code_example.go │ │ └── enter.go │ │ ├── enter.go │ │ ├── example │ │ ├── enter.go │ │ ├── exa_customer.go │ │ └── exa_excel.go │ │ ├── redis │ │ ├── enter.go │ │ └── read.go │ │ ├── system │ │ ├── enter.go │ │ ├── sys_api.go │ │ ├── sys_authority.go │ │ ├── sys_authority_btn.go │ │ ├── sys_auto_code.go │ │ ├── sys_auto_code_history.go │ │ ├── sys_captcha.go │ │ ├── sys_casbin.go │ │ ├── sys_dictionary.go │ │ ├── sys_dictionary_detail.go │ │ ├── sys_initdb.go │ │ ├── sys_jwt_blacklist.go │ │ ├── sys_menu.go │ │ ├── sys_operation_record.go │ │ ├── sys_system.go │ │ └── sys_user.go │ │ ├── task │ │ └── task.go │ │ └── tidb_or_mysql │ │ ├── admin.go │ │ ├── backup.go │ │ ├── cluster.go │ │ ├── common.go │ │ ├── enter.go │ │ ├── login.go │ │ ├── read.go │ │ ├── rule.go │ │ └── task.go ├── cmd │ └── owls │ │ └── main.go ├── config.docker.yaml ├── config.example.yaml ├── config.yaml ├── config │ ├── auto_code.go │ ├── captcha.go │ ├── casbin.go │ ├── config.go │ ├── cors.go │ ├── db_list.go │ ├── email.go │ ├── excel.go │ ├── gorm_mysql.go │ ├── gorm_pgsql.go │ ├── jwt.go │ ├── oss_aliyun.go │ ├── oss_aws.go │ ├── oss_huawei.go │ ├── oss_local.go │ ├── oss_qiniu.go │ ├── oss_tencent.go │ ├── redis.go │ ├── system.go │ ├── timer.go │ └── zap.go ├── core │ ├── server.go │ ├── server_other.go │ ├── server_win.go │ ├── viper.go │ └── zap.go ├── docs │ ├── docs.go │ ├── swagger.json │ └── swagger.yaml ├── global │ ├── global.go │ └── model.go ├── go.mod ├── go.sum ├── initialize │ ├── gorm.go │ ├── gorm_mysql.go │ ├── gorm_pgsql.go │ ├── internal │ │ ├── gorm.go │ │ └── logger.go │ ├── plugin.go │ ├── router.go │ └── validator.go ├── middleware │ ├── casbin_rbac.go │ ├── cors.go │ ├── email.go │ ├── error.go │ ├── jwt.go │ ├── limit_ip.go │ ├── loadtls.go │ ├── logger.go │ ├── need_init.go │ └── operation.go ├── model │ ├── autocode │ │ ├── autocodeExample.go │ │ └── request │ │ │ └── autocodeExample.go │ ├── common │ │ ├── request │ │ │ └── common.go │ │ └── response │ │ │ ├── common.go │ │ │ └── response.go │ ├── example │ │ ├── exa_breakpoint_continue.go │ │ ├── exa_customer.go │ │ ├── exa_excel.go │ │ ├── exa_file_upload_download.go │ │ └── response │ │ │ ├── exa_breakpoint_continue.go │ │ │ ├── exa_customer.go │ │ │ └── exa_file_upload_download.go │ └── system │ │ ├── request │ │ ├── jwt.go │ │ ├── sys_api.go │ │ ├── sys_authority_btn.go │ │ ├── sys_auto_history.go │ │ ├── sys_casbin.go │ │ ├── sys_dictionary.go │ │ ├── sys_dictionary_detail.go │ │ ├── sys_menu.go │ │ ├── sys_operation_record.go │ │ └── sys_user.go │ │ ├── response │ │ ├── sys_api.go │ │ ├── sys_authority.go │ │ ├── sys_authority_btn.go │ │ ├── sys_auto_code.go │ │ ├── sys_auto_code_history.go │ │ ├── sys_captcha.go │ │ ├── sys_casbin.go │ │ ├── sys_menu.go │ │ ├── sys_system.go │ │ └── sys_user.go │ │ ├── sys_api.go │ │ ├── sys_authority.go │ │ ├── sys_authority_btn.go │ │ ├── sys_authority_menu.go │ │ ├── sys_auto_code.go │ │ ├── sys_autocode_history.go │ │ ├── sys_base_menu.go │ │ ├── sys_dictionary.go │ │ ├── sys_dictionary_detail.go │ │ ├── sys_init.go │ │ ├── sys_initdb.go │ │ ├── sys_jwt_blacklist.go │ │ ├── sys_menu_btn.go │ │ ├── sys_operation_record.go │ │ ├── sys_system.go │ │ ├── sys_user.go │ │ └── sys_user_authority.go ├── packfile │ ├── notUsePackFile.go │ └── usePackFile.go ├── plugin │ ├── email │ │ ├── README.MD │ │ ├── api │ │ │ ├── enter.go │ │ │ └── sys_email.go │ │ ├── config │ │ │ └── email.go │ │ ├── global │ │ │ └── gloabl.go │ │ ├── main.go │ │ ├── model │ │ │ └── response │ │ │ │ └── email.go │ │ ├── router │ │ │ ├── enter.go │ │ │ └── sys_email.go │ │ ├── service │ │ │ ├── enter.go │ │ │ └── sys_email.go │ │ └── utils │ │ │ └── email.go │ ├── example_plugin │ │ └── main.go │ └── ws │ │ └── ws.go ├── resource │ ├── excel │ │ ├── ExcelExport.xlsx │ │ ├── ExcelImport.xlsx │ │ └── ExcelTemplate.xlsx │ ├── page │ │ ├── css │ │ │ ├── index.f05c41c6.css │ │ │ └── parser-example.69e16e51.css │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── js │ │ │ ├── chunk-vendors.9ae0d8fb.js │ │ │ ├── index.94d8e405.js │ │ │ ├── parser-example.c1051310.js │ │ │ ├── preview.d8d06dfd.js │ │ │ └── tinymce-example.5a756246.js │ │ ├── libs │ │ │ └── monaco-editor │ │ │ │ └── vs │ │ │ │ ├── base │ │ │ │ ├── browser │ │ │ │ │ └── ui │ │ │ │ │ │ └── codicons │ │ │ │ │ │ └── codicon │ │ │ │ │ │ └── codicon.ttf │ │ │ │ └── worker │ │ │ │ │ └── workerMain.js │ │ │ │ ├── basic-languages │ │ │ │ ├── abap │ │ │ │ │ └── abap.js │ │ │ │ ├── apex │ │ │ │ │ └── apex.js │ │ │ │ ├── azcli │ │ │ │ │ └── azcli.js │ │ │ │ ├── bat │ │ │ │ │ └── bat.js │ │ │ │ ├── cameligo │ │ │ │ │ └── cameligo.js │ │ │ │ ├── clojure │ │ │ │ │ └── clojure.js │ │ │ │ ├── coffee │ │ │ │ │ └── coffee.js │ │ │ │ ├── cpp │ │ │ │ │ └── cpp.js │ │ │ │ ├── csharp │ │ │ │ │ └── csharp.js │ │ │ │ ├── csp │ │ │ │ │ └── csp.js │ │ │ │ ├── css │ │ │ │ │ └── css.js │ │ │ │ ├── dart │ │ │ │ │ └── dart.js │ │ │ │ ├── dockerfile │ │ │ │ │ └── dockerfile.js │ │ │ │ ├── ecl │ │ │ │ │ └── ecl.js │ │ │ │ ├── fsharp │ │ │ │ │ └── fsharp.js │ │ │ │ ├── go │ │ │ │ │ └── go.js │ │ │ │ ├── graphql │ │ │ │ │ └── graphql.js │ │ │ │ ├── handlebars │ │ │ │ │ └── handlebars.js │ │ │ │ ├── hcl │ │ │ │ │ └── hcl.js │ │ │ │ ├── html │ │ │ │ │ └── html.js │ │ │ │ ├── ini │ │ │ │ │ └── ini.js │ │ │ │ ├── java │ │ │ │ │ └── java.js │ │ │ │ ├── javascript │ │ │ │ │ └── javascript.js │ │ │ │ ├── julia │ │ │ │ │ └── julia.js │ │ │ │ ├── kotlin │ │ │ │ │ └── kotlin.js │ │ │ │ ├── less │ │ │ │ │ └── less.js │ │ │ │ ├── lexon │ │ │ │ │ └── lexon.js │ │ │ │ ├── lua │ │ │ │ │ └── lua.js │ │ │ │ ├── m3 │ │ │ │ │ └── m3.js │ │ │ │ ├── markdown │ │ │ │ │ └── markdown.js │ │ │ │ ├── mips │ │ │ │ │ └── mips.js │ │ │ │ ├── msdax │ │ │ │ │ └── msdax.js │ │ │ │ ├── mysql │ │ │ │ │ └── mysql.js │ │ │ │ ├── objective-c │ │ │ │ │ └── objective-c.js │ │ │ │ ├── pascal │ │ │ │ │ └── pascal.js │ │ │ │ ├── pascaligo │ │ │ │ │ └── pascaligo.js │ │ │ │ ├── perl │ │ │ │ │ └── perl.js │ │ │ │ ├── pgsql │ │ │ │ │ └── pgsql.js │ │ │ │ ├── php │ │ │ │ │ └── php.js │ │ │ │ ├── postiats │ │ │ │ │ └── postiats.js │ │ │ │ ├── powerquery │ │ │ │ │ └── powerquery.js │ │ │ │ ├── powershell │ │ │ │ │ └── powershell.js │ │ │ │ ├── pug │ │ │ │ │ └── pug.js │ │ │ │ ├── python │ │ │ │ │ └── python.js │ │ │ │ ├── r │ │ │ │ │ └── r.js │ │ │ │ ├── razor │ │ │ │ │ └── razor.js │ │ │ │ ├── redis │ │ │ │ │ └── redis.js │ │ │ │ ├── redshift │ │ │ │ │ └── redshift.js │ │ │ │ ├── restructuredtext │ │ │ │ │ └── restructuredtext.js │ │ │ │ ├── ruby │ │ │ │ │ └── ruby.js │ │ │ │ ├── rust │ │ │ │ │ └── rust.js │ │ │ │ ├── sb │ │ │ │ │ └── sb.js │ │ │ │ ├── scala │ │ │ │ │ └── scala.js │ │ │ │ ├── scheme │ │ │ │ │ └── scheme.js │ │ │ │ ├── scss │ │ │ │ │ └── scss.js │ │ │ │ ├── shell │ │ │ │ │ └── shell.js │ │ │ │ ├── solidity │ │ │ │ │ └── solidity.js │ │ │ │ ├── sophia │ │ │ │ │ └── sophia.js │ │ │ │ ├── sql │ │ │ │ │ └── sql.js │ │ │ │ ├── st │ │ │ │ │ └── st.js │ │ │ │ ├── swift │ │ │ │ │ └── swift.js │ │ │ │ ├── systemverilog │ │ │ │ │ └── systemverilog.js │ │ │ │ ├── tcl │ │ │ │ │ └── tcl.js │ │ │ │ ├── twig │ │ │ │ │ └── twig.js │ │ │ │ ├── typescript │ │ │ │ │ └── typescript.js │ │ │ │ ├── vb │ │ │ │ │ └── vb.js │ │ │ │ ├── xml │ │ │ │ │ └── xml.js │ │ │ │ └── yaml │ │ │ │ │ └── yaml.js │ │ │ │ ├── editor │ │ │ │ ├── editor.main.css │ │ │ │ ├── editor.main.js │ │ │ │ ├── editor.main.nls.de.js │ │ │ │ ├── editor.main.nls.es.js │ │ │ │ ├── editor.main.nls.fr.js │ │ │ │ ├── editor.main.nls.it.js │ │ │ │ ├── editor.main.nls.ja.js │ │ │ │ ├── editor.main.nls.js │ │ │ │ ├── editor.main.nls.ko.js │ │ │ │ ├── editor.main.nls.ru.js │ │ │ │ ├── editor.main.nls.zh-cn.js │ │ │ │ └── editor.main.nls.zh-tw.js │ │ │ │ ├── language │ │ │ │ ├── css │ │ │ │ │ ├── cssMode.js │ │ │ │ │ └── cssWorker.js │ │ │ │ ├── html │ │ │ │ │ ├── htmlMode.js │ │ │ │ │ └── htmlWorker.js │ │ │ │ ├── json │ │ │ │ │ ├── jsonMode.js │ │ │ │ │ └── jsonWorker.js │ │ │ │ └── typescript │ │ │ │ │ ├── tsMode.js │ │ │ │ │ └── tsWorker.js │ │ │ │ └── loader.js │ │ └── preview.html │ ├── rbac_model.conf │ └── template │ │ ├── readme.txt.tpl │ │ ├── server │ │ ├── api.go.tpl │ │ ├── model.go.tpl │ │ ├── request.go.tpl │ │ ├── router.go.tpl │ │ └── service.go.tpl │ │ └── web │ │ ├── api.js.tpl │ │ ├── form.vue.tpl │ │ └── table.vue.tpl ├── router │ ├── autocode │ │ ├── auto_code_example.go │ │ └── enter.go │ ├── enter.go │ ├── example │ │ ├── enter.go │ │ ├── exa_customer.go │ │ └── exa_excel.go │ ├── redis │ │ ├── enter.go │ │ └── redis.go │ ├── routers │ │ ├── auth.go │ │ ├── enter.go │ │ └── task.go │ ├── system │ │ ├── enter.go │ │ ├── sys_api.go │ │ ├── sys_authority.go │ │ ├── sys_authority_btn.go │ │ ├── sys_auto_code.go │ │ ├── sys_auto_code_history.go │ │ ├── sys_base.go │ │ ├── sys_casbin.go │ │ ├── sys_dictionary.go │ │ ├── sys_dictionary_detail.go │ │ ├── sys_initdb.go │ │ ├── sys_jwt.go │ │ ├── sys_menu.go │ │ ├── sys_operation_record.go │ │ ├── sys_system.go │ │ └── sys_user.go │ └── tidb_or_mysql │ │ ├── enter.go │ │ └── tidb_or_mysql.go ├── service │ ├── auth │ │ └── auth │ │ │ ├── auth.go │ │ │ ├── auth_dao.go │ │ │ ├── auth_task.go │ │ │ ├── filter.go │ │ │ └── type.go │ ├── autocode │ │ ├── auto_code_example.go │ │ └── enter.go │ ├── enter.go │ ├── example │ │ ├── enter.go │ │ ├── exa_breakpoint_continue.go │ │ ├── exa_customer.go │ │ └── exa_excel_parse.go │ ├── redis │ │ ├── cmd_whitelist.go │ │ ├── connection.go │ │ ├── exec.go │ │ ├── init.go │ │ ├── query.go │ │ ├── redis_dao │ │ │ ├── task.go │ │ │ └── white_list_dao.go │ │ ├── task.go │ │ └── white_list.go │ ├── system │ │ ├── enter.go │ │ ├── jwt_black_list.go │ │ ├── login │ │ │ ├── ldap.go │ │ │ └── registry.go │ │ ├── sys_api.go │ │ ├── sys_authority.go │ │ ├── sys_authority_btn.go │ │ ├── sys_auto_code.go │ │ ├── sys_auto_code_interface.go │ │ ├── sys_auto_code_mysql.go │ │ ├── sys_auto_code_pgsql.go │ │ ├── sys_autocode_history.go │ │ ├── sys_base_menu.go │ │ ├── sys_casbin.go │ │ ├── sys_dictionary.go │ │ ├── sys_dictionary_detail.go │ │ ├── sys_initdb.go │ │ ├── sys_initdb_mysql.go │ │ ├── sys_initdb_pgsql.go │ │ ├── sys_menu.go │ │ ├── sys_operation_record.go │ │ ├── sys_system.go │ │ └── sys_user.go │ ├── task │ │ ├── status.go │ │ ├── task.go │ │ ├── task_dao.go │ │ └── type.go │ └── tidb_or_mysql │ │ ├── admin │ │ └── admin.go │ │ ├── auth │ │ ├── admin.go │ │ ├── auth_mock.go │ │ ├── config_auth_tool.go │ │ ├── login.go │ │ ├── login_check.go │ │ ├── login_check │ │ │ └── ldap.go │ │ └── net_auth_tool.go │ │ ├── checker │ │ ├── audit.go │ │ ├── heuristic.go │ │ ├── indexAndAffectRow.go │ │ ├── indexAndAffectRow_test.go │ │ ├── rule_status.go │ │ ├── rules.go │ │ └── unsupportedModify.go │ │ ├── common.go │ │ ├── dao │ │ ├── admin.go │ │ ├── backup.go │ │ ├── build_table.sql │ │ ├── cluster_info.go │ │ ├── init.go │ │ ├── rule.go │ │ ├── sub_task.go │ │ └── task.go │ │ ├── db_info │ │ ├── cluster.go │ │ └── db.go │ │ ├── injection │ │ └── injection.go │ │ ├── sql_util │ │ ├── amend_sql.go │ │ ├── sql_util.go │ │ └── test │ │ │ ├── amend_sql_test.go │ │ │ └── sqlUtil_test.go │ │ └── task │ │ ├── auth.go │ │ ├── backup.go │ │ ├── checker.go │ │ ├── db.go │ │ ├── exec.go │ │ ├── modle.go │ │ ├── read.go │ │ ├── rollback.go │ │ └── task.go ├── source │ ├── example │ │ ├── file_mysql.go │ │ └── file_pgsql.go │ ├── system │ │ ├── api.go │ │ ├── authorities_menus.go │ │ ├── authority.go │ │ ├── casbin.go │ │ ├── data_authorities.go │ │ ├── dictionary.go │ │ ├── dictionary_detail.go │ │ ├── menu.go │ │ ├── user.go │ │ ├── user_authority.go │ │ ├── view_authority_menu_mysql.go │ │ └── view_authority_menu_postgres.go │ └── tidb_or_mysql │ │ └── cluster.go └── utils │ ├── breakpoint_continue.go │ ├── captcha │ └── redis.go │ ├── clamis.go │ ├── constant.go │ ├── crypto.go │ ├── crypto_test.go │ ├── db_automation.go │ ├── directory.go │ ├── file_operations.go │ ├── fmt_plus.go │ ├── http.go │ ├── injectionCode.go │ ├── jwt.go │ ├── logger │ └── log.go │ ├── md5.go │ ├── numbers.go │ ├── plugin │ ├── plugin.go │ └── plugin_uinx.go │ ├── reload.go │ ├── rotatelogs.go │ ├── server.go │ ├── set.go │ ├── string.go │ ├── time_convert.go │ ├── timer │ ├── timed_task.go │ └── timed_task_test.go │ ├── util_test.go │ ├── validator.go │ ├── verify.go │ └── zipfiles.go ├── issue.list └── web ├── .docker-compose └── nginx │ └── conf.d │ └── my.conf ├── .dockerignore ├── .env.development ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── Dockerfile ├── README.md ├── babel.config.js ├── favicon.ico ├── index.html ├── limit.js ├── openDocument.js ├── package.json ├── src ├── App.vue ├── api │ ├── api.js │ ├── auth │ │ └── auth.js │ ├── authority.js │ ├── authorityBtn.js │ ├── autoCode.js │ ├── breakpoint.js │ ├── casbin.js │ ├── customer.js │ ├── db │ │ ├── cluster.js │ │ ├── read.js │ │ ├── rule.js │ │ └── task.js │ ├── email.js │ ├── excel.js │ ├── fileUploadAndDownload.js │ ├── github.js │ ├── initdb.js │ ├── jwt.js │ ├── menu.js │ ├── sysDictionary.js │ ├── sysDictionaryDetail.js │ ├── sysOperationRecord.js │ ├── system.js │ ├── task │ │ └── task.js │ └── user.js ├── assets │ ├── background.svg │ ├── dashboard.png │ ├── docs.png │ ├── flipped-aurora.png │ ├── github.png │ ├── kefu.png │ ├── login_background.jpg │ ├── login_background.svg │ ├── login_left.svg │ ├── logo.jpg │ ├── logo.png │ ├── logo_login.png │ ├── nav_logo.png │ ├── noBody.png │ ├── notFound.png │ ├── qm.png │ └── video.png ├── components │ ├── chooseImg │ │ └── index.vue │ ├── customPic │ │ └── index.vue │ ├── upload │ │ └── image.vue │ └── warningBar │ │ └── warningBar.vue ├── core │ ├── config.js │ ├── gin-vue-admin.js │ └── global.js ├── directive │ └── auth.js ├── main.js ├── permission.js ├── pinia │ ├── index.js │ └── modules │ │ ├── dictionary.js │ │ ├── router.js │ │ └── user.js ├── router │ └── index.js ├── style │ ├── base.scss │ ├── basics.scss │ ├── element_visiable.scss │ ├── init.sass │ ├── main.scss │ ├── mobile.scss │ └── newLogin.scss ├── utils │ ├── asyncRouter.js │ ├── btnAuth.js │ ├── bus.js │ ├── date.js │ ├── dictionary.js │ ├── downloadImg.js │ ├── format.js │ ├── image.js │ ├── page.js │ ├── request.js │ └── stringFun.js └── view │ ├── about │ └── index.vue │ ├── auth │ ├── apply.vue │ ├── approval.vue │ ├── auths.vue │ └── index.vue │ ├── dashboard │ ├── dashboardCharts │ │ └── echartsLine.vue │ ├── dashboardTable │ │ └── dashboardTable.vue │ └── index.vue │ ├── error │ ├── index.vue │ └── reload.vue │ ├── example │ ├── breakpoint │ │ └── breakpoint.vue │ ├── customer │ │ └── customer.vue │ ├── excel │ │ └── excel.vue │ ├── index.vue │ └── upload │ │ └── upload.vue │ ├── init │ └── index.vue │ ├── layout │ ├── aside │ │ ├── asideComponent │ │ │ ├── asyncSubmenu.vue │ │ │ ├── index.vue │ │ │ └── menuItem.vue │ │ ├── historyComponent │ │ │ └── history.vue │ │ └── index.vue │ ├── bottomInfo │ │ └── bottomInfo.vue │ ├── index.vue │ ├── screenfull │ │ └── index.vue │ ├── search │ │ └── search.vue │ └── setting │ │ └── index.vue │ ├── login │ └── index.vue │ ├── person │ └── person.vue │ ├── redis │ ├── cluster │ │ └── cluster.vue │ ├── exec │ │ └── exec.vue │ ├── history │ │ └── history.vue │ ├── index.vue │ ├── read.vue │ ├── review │ │ └── review.vue │ ├── rule │ │ └── rule.vue │ └── submit │ │ └── submit.vue │ ├── routerHolder.vue │ ├── superAdmin │ ├── api │ │ └── api.vue │ ├── authority │ │ ├── authority.vue │ │ └── components │ │ │ ├── apis.vue │ │ │ ├── datas.vue │ │ │ └── menus.vue │ ├── dictionary │ │ ├── sysDictionary.vue │ │ └── sysDictionaryDetail.vue │ ├── index.vue │ ├── menu │ │ ├── icon.vue │ │ └── menu.vue │ ├── operation │ │ └── sysOperationRecord.vue │ └── user │ │ └── user.vue │ ├── system │ └── state.vue │ ├── systemTools │ ├── autoCode │ │ ├── component │ │ │ ├── fieldDialog.vue │ │ │ └── previewCodeDialg.vue │ │ └── index.vue │ ├── autoCodeAdmin │ │ └── index.vue │ ├── formCreate │ │ └── index.vue │ ├── index.vue │ └── system │ │ └── system.vue │ └── tidbOrMysql │ ├── cluster │ └── cluster.vue │ ├── exec │ └── exec.vue │ ├── history │ └── history.vue │ ├── index.vue │ ├── read.vue │ ├── review │ └── review.vue │ ├── rule │ └── rule.vue │ └── submit │ └── submit.vue └── vite.config.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sql linguist-language=GO -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Document 4 | url: https://www.owls.nooncall.cn/doc 5 | about: If you have any questions about the use, you can check our official documents first 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature request 2 | description: Suggest an idea for Owls 3 | title: "[Feature]: " 4 | labels: [feature] 5 | assignees: 6 | - piexlmax 7 | body: 8 | - type: textarea 9 | id: desc 10 | attributes: 11 | label: 功能描述以及必要性描述 12 | description: 您觉得此新功能会为框架带来什么便利. 13 | placeholder: | 14 | 1. 首先... 15 | 2. 然后... 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: advise 20 | attributes: 21 | label: 建议和方案 22 | description: 您有好的建议或者修改方案可以提供给我们。 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *__debug_bin 2 | .idea/ 3 | /web/node_modules 4 | /web/dist 5 | 6 | .DS_Store 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | /server/log/ 27 | /server/gva 28 | /server/latest_log 29 | 30 | *.iml 31 | *.log 32 | bin 33 | 34 | # doc 35 | # Dependencies 36 | /docs/node_modules 37 | 38 | # Production 39 | /docs/build 40 | 41 | # Generated files 42 | /docs/.docusaurus 43 | /docs/.cache-loader 44 | 45 | # Misc 46 | /docs/.DS_Store 47 | /docs/.env.local 48 | /docs/.env.development.local 49 | /docs/.env.test.local 50 | /docs/.env.production.local 51 | 52 | /docs/npm-debug.log* 53 | /docs/yarn-debug.log* 54 | /docs/yarn-error.log* 55 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | ### Contributing Guide 3 | #### 1 Issue Guidelines 4 | 5 | - Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. If any questions come up when you are using Element, please hit [Gitter](https://gitter.im/element-en/Lobby) for help. 6 | 7 | - Before submitting an issue, please check if similar problems have already been issued. 8 | 9 | #### 2 Pull Request Guidelines 10 | 11 | - Fork this repository to your own account. Do not create branches here. 12 | 13 | - Commit info should be formatted as `[File Name]: Info about commit.` (e.g. `README.md: Fix xxx bug`) 14 | 15 | - Make sure PRs are created to `develop` branch instead of `master` branch. 16 | 17 | - If your PR fixes a bug, please provide a description about the related bug. 18 | 19 | - Merging a PR takes two maintainers: one approves the changes after reviewing, and then the other reviews and merges. 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:latest 2 | 3 | LABEL MAINTAINER=developer@owls.nooncall.cn 4 | 5 | COPY ./bin /service/ 6 | 7 | WORKDIR /service 8 | 9 | ENTRYPOINT ["./owls"] 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: fmt 2 | # go test -race ./controller/test/... 3 | # go test -race ./service/checker/... 4 | # go test -race ./service/sql_util/... 5 | # go test -race ./util/... 6 | 7 | GODIR=`pwd`/go 8 | 9 | config: 10 | mkdir -p ./bin/resource 11 | cp ${GODIR}/config.yaml ./bin/ 12 | cp ${GODIR}/resource/rbac_model.conf ./bin/resource/rbac_model.conf 13 | 14 | build: fmt config 15 | cd ${GODIR} && \ 16 | go build -o ../bin/owls ./cmd/owls/ &&\ 17 | cd .. 18 | 19 | build-linux: config 20 | cd ${GODIR} && \ 21 | CGO_ENABLED=0 GOOS=linux go build -o ../bin/owls -a -ldflags '-extldflags "-static"' ./cmd/owls/ 22 | cd .. 23 | fmt: 24 | cd ${GODIR} && go fmt ./... && cd .. 25 | 26 | run: config build-front build build-docs 27 | cd ./bin && ./owls 28 | 29 | .ONESHELL: 30 | build-front: 31 | mkdir -p bin 32 | rm -rf ./bin/static 33 | cd web/ && npm run build && cp -r ./dist ../bin/static 34 | cd .. 35 | 36 | build-docs: 37 | mkdir -p bin 38 | rm -rf ./bin/docs-static 39 | cd docs/ && npm run build && cp -r ./build ../bin/docs-static 40 | cd .. 41 | 42 | build-docker: build-front build-linux build-docs 43 | docker build -t mingbai/owls:v0.5.0 . 44 | 45 | run-docker: build-docker 46 | docker run -p 80:80 -d mingbai/owls:v0.5.0 47 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please report security issues to qimiaojiangjizhao@gmail.com 6 | -------------------------------------------------------------------------------- /deployment/server/gva-server-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gva-server 5 | annotations: 6 | flipped-aurora/gin-vue-admin: backend 7 | github: "https://github.com/flipped-aurora/gin-vue-admin.git" 8 | app.kubernetes.io/version: 0.0.1 9 | labels: 10 | app: gva-server 11 | version: gva-vue3 12 | spec: 13 | selector: 14 | app: gva-server 15 | version: gva-vue3 16 | ports: 17 | - port: 8888 18 | name: http 19 | targetPort: 8888 20 | type: ClusterIP 21 | # type: NodePort 22 | -------------------------------------------------------------------------------- /deployment/web/gva-web-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: my.conf 5 | data: 6 | my.conf: | 7 | server { 8 | listen 8080; 9 | server_name localhost; 10 | 11 | #charset koi8-r; 12 | #access_log logs/host.access.log main; 13 | 14 | location / { 15 | root /usr/share/nginx/html; 16 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 17 | try_files $uri $uri/ /index.html; 18 | } 19 | 20 | location /api { 21 | proxy_set_header Host $http_host; 22 | proxy_set_header X-Real-IP $remote_addr; 23 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | proxy_set_header X-Forwarded-Proto $scheme; 25 | rewrite ^/api/(.*)$ /$1 break; #重写 26 | proxy_pass http://gva-server:8888; # 设置代理服务器的协议和地址 27 | } 28 | 29 | location /api/swagger/index.html { 30 | proxy_pass http://gva-server:8888/swagger/index.html; 31 | } 32 | } -------------------------------------------------------------------------------- /deployment/web/gva-web-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gva-web 5 | annotations: 6 | flipped-aurora/gin-vue-admin: ui 7 | github: "https://github.com/flipped-aurora/gin-vue-admin.git" 8 | app.kubernetes.io/version: 0.0.1 9 | labels: 10 | app: gva-web 11 | version: gva-vue3 12 | spec: 13 | type: NodePort 14 | # type: ClusterIP 15 | ports: 16 | - name: http 17 | port: 8080 18 | targetPort: 8080 19 | selector: 20 | app: gva-web 21 | version: gva-vue3 22 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg -------------------------------------------------------------------------------- /docs/blog/2021-08-26-welcome/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | authors: [slorber, yangshun] 5 | tags: [facebook, hello, docusaurus] 6 | --- 7 | 8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). 9 | 10 | Simply add Markdown files (or folders) to the `blog` directory. 11 | 12 | Regular blog authors can be added to `authors.yml`. 13 | 14 | The blog post date can be extracted from filenames, such as: 15 | 16 | - `2019-05-30-welcome.md` 17 | - `2019-05-30-welcome/index.md` 18 | 19 | A blog post folder can be convenient to co-locate blog post images: 20 | 21 | ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) 22 | 23 | The blog supports tags as well! 24 | 25 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. 26 | -------------------------------------------------------------------------------- /docs/blog/2022-06-27-first.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: 首篇 3 | title: Hello World 4 | authors: 5 | name: Owls 6 | title: Owls研发团队 7 | # url: https://github.com/wgao19 8 | # image_url: https://github.com/wgao19.png 9 | tags: [hello, world] 10 | --- 11 | 12 | #### Hello World 13 | 欢迎光临! 14 | -------------------------------------------------------------------------------- /docs/blog/2022-07-25-introduce/architecture-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/blog/2022-07-25-introduce/architecture-en.png -------------------------------------------------------------------------------- /docs/blog/2022-07-25-introduce/rollback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/blog/2022-07-25-introduce/rollback.png -------------------------------------------------------------------------------- /docs/blog/2022-07-25-introduce/rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/blog/2022-07-25-introduce/rules.png -------------------------------------------------------------------------------- /docs/blog/authors.yml: -------------------------------------------------------------------------------- 1 | qingfeng: 2 | name: qingfeng,mingbai 3 | title: Maintainer of owls 4 | url: https://github.com/nooncall/owls 5 | image_url: https://github.com/qingfeng777.png 6 | 7 | endi: 8 | name: Endilie Yacop Sucipto 9 | title: Maintainer of Docusaurus 10 | url: https://github.com/endiliey 11 | image_url: https://github.com/endiliey.png 12 | 13 | yangshun: 14 | name: Yangshun Tay 15 | title: Front End Engineer @ Facebook 16 | url: https://github.com/yangshun 17 | image_url: https://github.com/yangshun.png 18 | 19 | slorber: 20 | name: Sébastien Lorber 21 | title: Docusaurus maintainer 22 | url: https://sebastienlorber.com 23 | image_url: https://github.com/slorber.png 24 | -------------------------------------------------------------------------------- /docs/develop/community/community.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '社区' 3 | sidebar_position: 6 4 | --- 5 | 6 | ### 微信群 7 | 目前主要在微信群中沟通。 8 | 9 | #### 开发者微信群: 10 | 11 | ![wechat](./owls-wechat.jpg) 12 | 13 | ### 其他 14 | 或者添加开发者微信:grsixk 15 | -------------------------------------------------------------------------------- /docs/develop/community/owls-wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/develop/community/owls-wechat.jpg -------------------------------------------------------------------------------- /docs/develop/contribution.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '贡献指南' 3 | title: 贡献指南 4 | sidebar_position: 5 5 | --- 6 | 7 | Hi! 首先感谢您对Owls的兴趣。 8 | 9 | Owls 是一套为快安全、规范准备的一整套前后端分离架构式的后端系统管理平台, 旨在简化、标准化DBA、研发等同学对数据库、MQ、缓存等系统的使用和管理。 10 | 11 | Owls 的成长离不开大家的支持,如果你愿意为 Owls 贡献代码或提供建议,请阅读以下内容。 12 | 13 | #### Issue 规范 14 | - 稍大的功能模块设计需要在文档目录(server/docs/design)中,参照[模板](../design/template)添加设计说明,并进行review。 15 | 16 | - issue 仅用于提交 Bug 或 Feature 以及设计相关的内容。 17 | 18 | - 在提交 issue 之前,请搜索相关内容是否已被提出。 19 | 20 | #### Pull Request 规范 21 | - 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。 22 | 23 | - commit 信息要以`[模块名]: 描述信息` 的形式填写,例如 `[readme] update xxx msg`。 24 | 25 | - 如果是修复 bug,请在 PR 中给出描述信息。 26 | 27 | - 合并代码需要一人进行 review 后 approve,方可合并。 28 | -------------------------------------------------------------------------------- /docs/develop/db-tables-and-init.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '库表使用' 3 | title: 库表使用 4 | sidebar_position: 6 5 | --- 6 | 7 | 1, 数据库 8 | 9 | 数据库目前主要是使用的是mysql,如果是一个新库,会在初始化的时候进行选择、输入相关数据库连接信息。 10 | 11 | 如果读取配置文件,发现是一个已经有数据的库,则无法再进行初始化,已有用户可直接登录。 12 | 13 | 2, 表 14 | 15 | 一般,表在数据库初始化的时候自动创建,无需手动建表。 16 | 17 | 如果是开发时,需要临时增加一个表 18 | 19 | a. 可以清除数据库重新初始化 20 | b. 可以临时手动建表,不影响后续重新部署时的自动初始化。 21 | 22 | 3, 表中数据的初始化 23 | 24 | 表中数据的初始化在`source`目录下,初始化数据库时,会将一些基础数据写入到DB。 25 | 26 | 比如UI上菜单相关数据的初始化在这里: 27 | `source/system/menu.go` -------------------------------------------------------------------------------- /docs/develop/design/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "设计文档", 3 | "position": 4, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "设计文档列表。" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/develop/design/db-auth.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '数据权限' 3 | title: 数据权限 4 | sidebar_position: 4 5 | --- 6 | 7 | ## 数据权限管理 8 | 9 | #### 版本记录 10 | 11 | 日期 |内容|撰写人 12 | |---|---|---| 13 | 2022/04/26| V1.0 初版| 刘胜杰 14 | 15 | ## 人员 16 | - 需求提出: 刘胜杰 17 | - 研发: 刘胜杰 18 | - 抄送: 19 | 20 | ## 问题 21 | 查询数据需要权限控制,以保护数据安全 22 | 23 | ## 目标 24 | - 实现权限申请及审批流程 25 | - 数据查询及任务提交添加权限控制 26 | - 支持后续可能出现的,更多的数据系统的权限管理 27 | 28 | ## 设计 29 | 30 | ### 整体设计 31 | 普通用户提交请求对库-表的权限申请,管理员进行审批及普通用户的权限删除 32 | 33 | ### 详细设计 34 | 35 | #### 页面 36 | - 权限申请及审批列表。 37 | - 人员权限列表-库类型-所拥有的db列表等。 38 | 39 | #### 表 40 | 审批task表 41 | 42 | 是否可与sql审核用同一个表?是否所有的审批流程用同样的表及类似的流程? 43 | 用同一个表,逻辑会交织到一起,不便于处理不同情况。 44 | 用不同的表,可能会同样的逻辑写多遍,浪费。 45 | 46 | 可以优先考虑用同一个表。 47 | 48 | taskID 49 | UserId 50 | DataType 51 | Cluster 52 | DB 53 | 54 | 权限表 55 | 56 | UserId 57 | DataType 58 | Cluster 59 | DB []string 60 | 61 | ## 排期 62 | 63 | - [x] 后端接口ready @刘胜杰 2020-5-3 64 | - [x] 前端页面ready 并调通管理 2020-5-5 65 | - [x] sql审核db list经权限过滤 2022-5-7 -------------------------------------------------------------------------------- /docs/develop/design/db-data-read.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '数据读取' 3 | title: 数据读取 4 | sidebar_position: 3 5 | --- 6 | 7 | ## 数据权限管理 8 | 9 | #### 版本记录 10 | 11 | 日期 |内容|撰写人 12 | |---|---|---| 13 | 2022/05/09| V1.0 初版| 刘胜杰 14 | 15 | ## 人员 16 | - 需求提出: 刘胜杰 17 | - 研发: 刘胜杰 18 | - 抄送: 19 | 20 | ## 问题 21 | 需提供数据查询功能 22 | 23 | ## 目标 24 | - 提供数据查询功能 25 | - 数据查询受权限控制 26 | 27 | ## 设计 28 | 29 | ### 整体设计 30 | 用户选择集群,数据库; 31 | 展示提示性的sql语句,提示表结构信息; 32 | 而后由用户在文本框中输入sql进行查询; 33 | 结果以动态表格展示。 34 | 35 | ### 详细设计 36 | 37 | #### 页面 38 | - 数据查询页,第一行为信息选择,集群,数据库选择;第二行展示提示性sql及输入文本框;第三行展示返回的数据表。 39 | - 集群、数据库的选择受权限控制。 40 | - 数据查询如无limit,自动添加20的limit。 41 | - 数据查询提前验证是否匹配索引,如不完全匹配,则警告并询问是否继续。 42 | - 数据查询行为,记录到操作记录。 43 | 44 | #### 表 45 | 无新增表 46 | 47 | #### 后端 48 | - 一个执行用户输入的接口。 49 | - 确保执行的是读sql。 50 | - 返回数据,二维数组? 51 | 52 | 53 | ## 排期 54 | 55 | - [x] 后端接口ready @刘胜杰 2020-5-13 56 | - [x] 前端页面ready 并调通管理 2020-5-15 57 | - [x] db list经权限过滤 2022-5-17 -------------------------------------------------------------------------------- /docs/develop/design/redis-write.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: 'redis-write' 3 | title: redis-write 4 | sidebar_position: 5 5 | --- 6 | ## redis读写 7 | 8 | #### 版本记录 9 | 10 | 日期 |内容|撰写人 11 | |---|---|---| 12 | 2023/02/09| V1.0 初版| 刘胜杰 13 | 14 | ## 人员 15 | - 需求提出:刘胜杰 16 | - 研发: 刘胜杰 17 | - 抄送: 18 | 19 | ## 问题 20 | 21 | 支持redis的数据查询、命令执行 22 | 23 | ## 目标 24 | 25 | - 支持数据查询 26 | - 支持写命令审批、执行 27 | - 读写命令,支持白名单、影响范围限制等 28 | 29 | ## 设计 30 | ### 整体设计 31 | 32 | - 读数据 33 | - 限制范围内,直接执行读取并返回结果 34 | - 写数据 35 | - 生成任务、通过审批后可执行 36 | - 一个任务内科包含多条命令 37 | - 集群管理复用DB集群管理,增加Type字段,支持UI检索 38 | 39 | 40 | ### 详细设计 41 | 42 | - 在白名单的基础上,支持范围限制 43 | - 读白名单 44 | 45 | 命令|说明|范围 46 | |---|---|---| 47 | get | 48 | mget | 49 | hget | 50 | hmget | 51 | lrange |限制影响数据量|1000 52 | zrange |限制影响数据量|1000 53 | sismember || 54 | scard || 55 | zcard || 56 | hscan || 57 | ttl || 58 | type || 59 | hlen || 60 | exists || 61 | sscan || 62 | 63 | - 写白名单 64 | 65 | 命令|说明|范围 66 | |---|---|---| 67 | set || 68 | mset || 69 | hset || 70 | hmset || 71 | hdel || 72 | del || 73 | zrem || 74 | srem || 75 | sadd || 76 | zadd || 77 | incrby || 78 | - …… 79 | 80 | 排期 81 | 82 | - [x] 暂无[人员][日期] 83 | - [ ] Milestone 2 84 | - [ ] …… -------------------------------------------------------------------------------- /docs/develop/design/template.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '模板' 3 | title: 模板 4 | sidebar_position: 6 5 | --- 6 | ## 标题 7 | 8 | #### 版本记录 9 | 10 | 日期 |内容|撰写人 11 | |---|---|---| 12 | 2022/01/04| V1.0 初版| 李不凡 13 | 14 | ## 人员 15 | - 需求提出: 16 | - 研发: 17 | - 抄送: 18 | 19 | ## 问题 20 | (请描述遇到的问题、对业务的影响等) 21 | 22 | ## 目标 23 | (请描述期望达到的状态、解决的问题、创造的价值等) 24 | 25 | ## 设计 26 | ### 整体设计 27 | ### 详细设计 28 | 29 | - 子任务A 30 | - 子任务B 31 | - …… 32 | 33 | 排期 34 | 35 | - [x] Milestone 1 (描述达到状态)[人员][日期] 36 | - [ ] Milestone 2 37 | - [ ] …… -------------------------------------------------------------------------------- /docs/develop/design/user-login.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '登录' 3 | title: 登录 4 | sidebar_position: 2 5 | --- 6 | ## 标题 7 | 8 | #### 版本记录 9 | 10 | 日期 |内容|撰写人 11 | |---|---|---| 12 | 2022/06/05| V1.0 初版| 刘胜杰 13 | 14 | ## 人员 15 | - 需求提出: 16 | - 研发: 刘胜杰 & 17 | - 抄送: 18 | 19 | ## 问题 20 | 解决不用场景下的用户接入,用户登录问题 21 | 22 | ## 目标 23 | 提供多种方式,支持不同场景下用户接入需求 24 | 1,支持ldap接入方式 25 | 2,支持注册登录方式 26 | 3,设计好用户接口,使得方便接入企业内部用户系统 27 | 28 | ## 设计 29 | - 以配置切换不同的用户模式 30 | 根据配置值,注入不同的用户登录实现, 31 | 并根据配置值展示登陆页面的是否有‘注册’按钮 32 | - 梳理现有的用户系统,设计登陆相关的接口 33 | 需改造现有的登陆实现, 34 | - 支持ldap接入用户登录 35 | 现有部分ldap代码,需要接入改造并测试 36 | - 支持注册登录方式 37 | 登陆已经存在,需添加注册功能 38 | 目前管理员可以修改用户密码,需支持用户修改自己的密码 39 | 40 | ## 排期 41 | 42 | - [x] 支持ldap 2022-06中 43 | - [x] 支持注册登录方式 2022-06下 44 | - [ ] 整理接入内部用户系统的文档 -------------------------------------------------------------------------------- /docs/develop/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '介绍' 3 | title: 介绍 4 | sidebar_position: 1 5 | --- 6 | 7 | ### 模块说明 8 | 9 | 此模块为研发相关的文档,包括项目本地启动、编译、部署更新、设计文档等内容。 10 | 11 | 用户文档请查阅其他部分。 -------------------------------------------------------------------------------- /docs/develop/roadmap.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | title: Roadmap 4 | --- 5 | 6 | ## 说明 7 | 开发计划会优先考虑用户提出的需求,欢迎在issue或者社区微信群里提出需求。 8 | 9 | ### 中短期计划 10 | 11 | * 完善文档 12 | * 支持mysql使用ghost改表 13 | * 收集展示库表状态信息 14 | * redis缓存管理 15 | 16 | 17 | ### 长期计划 18 | 19 | * MQ管理 20 | * ...... 21 | 22 | ### 需求 23 | 24 | * 支持SqlServer,PG 25 | * 支持ClickHouse -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Basics", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "5 minutes to learn the most important Docusaurus concepts." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/congratulations.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Congratulations! 6 | 7 | You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. 8 | 9 | Docusaurus has **much more to offer**! 10 | 11 | Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. 12 | 13 | Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) 14 | 15 | ## What's next? 16 | 17 | - Read the [official documentation](https://docusaurus.io/). 18 | - Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) 19 | - Add a [search bar](https://docusaurus.io/docs/search) 20 | - Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) 21 | - Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) 22 | -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/create-a-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Create a Blog Post 6 | 7 | Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... 8 | 9 | ## Create your first Post 10 | 11 | Create a file at `blog/2021-02-28-greetings.md`: 12 | 13 | ```md title="blog/2021-02-28-greetings.md" 14 | --- 15 | slug: greetings 16 | title: Greetings! 17 | authors: 18 | - name: Joel Marcey 19 | title: Co-creator of Docusaurus 1 20 | url: https://github.com/JoelMarcey 21 | image_url: https://github.com/JoelMarcey.png 22 | - name: Sébastien Lorber 23 | title: Docusaurus maintainer 24 | url: https://sebastienlorber.com 25 | image_url: https://github.com/slorber.png 26 | tags: [greetings] 27 | --- 28 | 29 | Congratulations, you have made your first post! 30 | 31 | Feel free to play around and edit this post as much you like. 32 | ``` 33 | 34 | A new blog post is now available at `http://localhost:3000/blog/greetings`. 35 | -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/create-a-document.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Create a Document 6 | 7 | Documents are **groups of pages** connected through: 8 | 9 | - a **sidebar** 10 | - **previous/next navigation** 11 | - **versioning** 12 | 13 | ## Create your first Doc 14 | 15 | Create a markdown file at `docs/hello.md`: 16 | 17 | ```md title="docs/hello.md" 18 | # Hello 19 | 20 | This is my **first Docusaurus document**! 21 | ``` 22 | 23 | A new document is now available at `http://localhost:3000/docs/hello`. 24 | 25 | ## Configure the Sidebar 26 | 27 | Docusaurus automatically **creates a sidebar** from the `docs` folder. 28 | 29 | Add metadata to customize the sidebar label and position: 30 | 31 | ```md title="docs/hello.md" {1-4} 32 | --- 33 | sidebar_label: 'Hi!' 34 | sidebar_position: 3 35 | --- 36 | 37 | # Hello 38 | 39 | This is my **first Docusaurus document**! 40 | ``` 41 | 42 | It is also possible to create your sidebar explicitly in `sidebars.js`: 43 | 44 | ```js title="sidebars.js" 45 | module.exports = { 46 | tutorialSidebar: [ 47 | { 48 | type: 'category', 49 | label: 'Tutorial', 50 | // highlight-next-line 51 | items: ['hello'], 52 | }, 53 | ], 54 | }; 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/create-a-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Create a Page 6 | 7 | Add **Markdown or React** files to `src/pages` to create a **standalone page**: 8 | 9 | - `src/pages/index.js` -> `localhost:3000/` 10 | - `src/pages/foo.md` -> `localhost:3000/foo` 11 | - `src/pages/foo/bar.js` -> `localhost:3000/foo/bar` 12 | 13 | ## Create your first React Page 14 | 15 | Create a file at `src/pages/my-react-page.js`: 16 | 17 | ```jsx title="src/pages/my-react-page.js" 18 | import React from 'react'; 19 | import Layout from '@theme/Layout'; 20 | 21 | export default function MyReactPage() { 22 | return ( 23 | 24 |

My React page

25 |

This is a React page

26 |
27 | ); 28 | } 29 | ``` 30 | 31 | A new page is now available at `http://localhost:3000/my-react-page`. 32 | 33 | ## Create your first Markdown Page 34 | 35 | Create a file at `src/pages/my-markdown-page.md`: 36 | 37 | ```mdx title="src/pages/my-markdown-page.md" 38 | # My Markdown page 39 | 40 | This is a Markdown page 41 | ``` 42 | 43 | A new page is now available at `http://localhost:3000/my-markdown-page`. 44 | -------------------------------------------------------------------------------- /docs/docs/tutorial-basics/deploy-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Deploy your site 6 | 7 | Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). 8 | 9 | It builds your site as simple **static HTML, JavaScript and CSS files**. 10 | 11 | ## Build your site 12 | 13 | Build your site **for production**: 14 | 15 | ```bash 16 | npm run build 17 | ``` 18 | 19 | The static files are generated in the `build` folder. 20 | 21 | ## Deploy your site 22 | 23 | Test your production build locally: 24 | 25 | ```bash 26 | npm run serve 27 | ``` 28 | 29 | The `build` folder is now served at `http://localhost:3000/`. 30 | 31 | You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). 32 | -------------------------------------------------------------------------------- /docs/docs/tutorial-extras/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Extras", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /docs/docs/tutorial-extras/img/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/docs/tutorial-extras/img/docsVersionDropdown.png -------------------------------------------------------------------------------- /docs/docs/tutorial-extras/img/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/docs/tutorial-extras/img/localeDropdown.png -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owls", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.21", 18 | "@docusaurus/preset-classic": "2.0.0-beta.21", 19 | "@mdx-js/react": "^1.6.22", 20 | "clsx": "^1.1.1", 21 | "prism-react-renderer": "^1.3.3", 22 | "react": "^17.0.2", 23 | "react-dom": "^17.0.2" 24 | }, 25 | "devDependencies": { 26 | "@docusaurus/module-type-aliases": "2.0.0-beta.21" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /docs/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | 8 | import styles from './index.module.css'; 9 | 10 | function HomepageHeader() { 11 | const {siteConfig} = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 |

{siteConfig.title}

16 |

{siteConfig.tagline}

17 |
18 | 21 | 5min ⏱️ 上手Owls 22 | 23 |
24 |
25 |
26 | ); 27 | } 28 | 29 | export default function Home() { 30 | const {siteConfig} = useDocusaurusContext(); 31 | return ( 32 | 35 | 36 |
37 | 38 |
39 |
40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/favicon-owl.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/static/img/favicon-owl.ico -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /docs/user_guide/auth.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '权限' 3 | title: 权限管理 4 | sidebar_position: 3 5 | --- 6 | 7 | ### 权限实现 8 | 9 | 权限控制采用[Casbin](https://casbin.org/docs/zh-CN/overview)框架,使用的RBAC模型,默认会初始化一个admin角色和一个user角色,分别对应于[用户](./%E7%94%A8%E6%88%B7)中的admin用户和普通用户。 10 | 11 | 如需创建新的角色,可直接在`超级管理员/角色管理`页面创建新的角色并勾选赋予其权限即可。 -------------------------------------------------------------------------------- /docs/user_guide/db/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "TiDB/Mysql", 3 | "position": 4, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "database about。" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/user_guide/db/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '介绍' 3 | title: 介绍 4 | sidebar_position: 1 5 | --- 6 | 7 | ### 对应功能模块 8 | 此模块介绍DB工单提交、数据读取相关的内容。对应的菜单是TiDB/Mysql。 9 | 10 | 如需查看安装、用户、权限等相关的文档介绍,请查阅[用户手册](../intro)文档。 -------------------------------------------------------------------------------- /docs/user_guide/db/read-sql.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '读数据' 3 | title: 数据读取 4 | sidebar_position: 1 5 | --- 6 | 7 | ### 前置依赖 8 | 9 | DB模块查询的数据源来自于此模块下`集群管理`中管理的集群,需要首先添加正确的集群信息,然后才能查询到数据。 10 | 11 | ### 权限控制 12 | 13 | 如不开启权限控制,则所有用户都可以查询所有集群中的数据。如果开启权限控制,则需要首先申请获取对应的DB权限,才能查询其数据。数据库权限的申请、审批都是在`权限`菜单中完成。 14 | 15 | **权限控制是否开启由`server/config.yaml`配置文件中的`db-filter/read-need-auth`字段控制,默认为false,即不开启。** 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/user_guide/db/write-sql.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '写工单' 3 | title: 提交写sql 4 | sidebar_position: 2 5 | --- 6 | 7 | ### 前置依赖 8 | 9 | 写工单的提交和执行,同样依赖于此模块下`集群管理`中管理的集群,需要首先添加正确的集群信息,然后才能使用工单流程。 10 | 11 | ### 审批流程 12 | 13 | 用户提交写请求工单后,可由拥有审批、执行页面权限(比如说admin)的用户进行审批和执行。 14 | 15 | 提交工单时,可以把多条sql复制到输入框中,以分号分隔。系统会自动拆分为多条子任务。 16 | 17 | 单次提交sql数量有限制,默认值为100,可以修改server/config.yaml配置文件中db-filter/num-once-limit值来控制。 18 | 19 | 执行DML语句会触发数据备份,默认备份失败也会继续执行,可以修改server/config.yaml配置文件中db-filter/exec-no-backup值来控制是否继续。 20 | 21 | **注意,大批量的写请求建议通过DBA手动备份后执行**,通过系统执行大批量的写请求,可能会备份失败。 22 | 23 | -------------------------------------------------------------------------------- /docs/user_guide/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '介绍' 3 | title: 介绍 4 | sidebar_position: 0 5 | --- 6 | 7 | ## 对应功能模块 8 | 此模块介绍安装、部署、用户、权限相关的内容。 9 | 10 | 如需查看数据库工单提交、数据查询相关的文档介绍,请查阅[DB文档](db/intro)。 11 | ## 软件介绍 12 | 13 | Owls是一个基于 [vue](https://vuejs.org) 和 [go](https://go.dev/) 开发的全栈前后端分离的数据交互管理平台,集成jwt鉴权,sql审批(sql查询,mq管理、redis使用管理、etcd管理等)。帮助您更方便、更规范的管理中间件系统,守护数据系统,提高系统稳定性,避免人为操作失误导致的故障,提高效率,解放创造力。 14 | 15 | ## 在线示例 16 | 17 | [在线预览](http://owls.nooncall.cn:8778/owls): http://owls.nooncall.cn:8778/owls 18 | 19 | 测试用户名:admin 20 | 21 | 密码:aaaaaa 22 | 23 | ## 仓库地址 24 | 25 | Github(科学上网):https://github.com/nooncall/owls 26 | Gitee(国内访问): https://gitee.com/nooncall/owls 27 | -------------------------------------------------------------------------------- /docs/user_guide/quick-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '快速开始' 3 | title: 快速开始 4 | sidebar_position: 1 5 | --- 6 | 7 | ### Docker安装 8 | 9 | 如果使用准备好的DB,则可以直接执行: 10 | 11 | docker run -p 8778:8778 -d mingbai/owls:latest 12 | 13 | 如无,则创建docker网桥,然后分别docker启动mysql、owls的容器: 14 | 15 | docker network create owls 16 | 17 | docker run -d --network=owls --name=mysql -e MYSQL_ROOT_PASSWORD=aaaaaa mysql:5.7 18 | 19 | docker run -p 8778:8778 -d --network=owls mingbai/owls:latest 20 | 21 | ### 集群内安装 22 | 23 | kubectl apply -n argo -f https://github.com/nooncall/owls/tree/master/docs/user_guide/deployment.yaml 24 | 25 | 26 | ### 初始化 27 | 28 | 登陆页面点击初始化按钮,根据我们上面的安装步骤,数据库的地址应该写`mysql`(或自行准备的DB地址),密码`aaaaaa`,其他默认即可。 29 | 30 | ### 本地文档 31 | 32 | 访问: `http://localhost:8778/docs` 33 | 34 | ### Enjoy 35 | 现在访问`http://localhost:8778`即可尝试使用系统提供的功能了 。 36 | 37 | 默认创建的用户有两个,`admin`和`user` 密码都是`aaaaaa` 。 38 | 39 | 正式使用的系统,还是建议使用独立的、持久化的数据库,可以选择Mysql或者TiDB。 -------------------------------------------------------------------------------- /docs/user_guide/sidebarsCommunity.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | community: [ 10 | { 11 | type: 'autogenerated', 12 | dirName: '.', 13 | }, 14 | { 15 | type: 'link', 16 | href: '/develop/community', 17 | label: '社区', 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /docs/user_guide/user.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_label: '用户' 3 | title: 用户管理 4 | sidebar_position: 2 5 | --- 6 | 7 | ### 初始化用户 8 | 9 | 执行初始化时,会初始化一个`admin`用户,一个`user`用户,其密码是在配置文件server/config.yaml中配置的,默认为`aaaaaa` 10 | 11 | ### 新用户接入方式 12 | 13 | 新用户接入支持两种方式,由server/config.yaml配置文件中的login/model控制,registry为注册登录模式,ldap为接入LDAP的方式。默认值为registry。 14 | 15 | #### 注册-登陆 16 | 17 | 配置为注册登录模式,会在用户页面显示`注册`按钮,点击即可进行注册,新注册的用户默认为普通用户权限,如需调整调整权限,可由admin用户创建并赋予其新的角色。 18 | 19 | #### LDAP接入用户 20 | 21 | LDAP模式需要正确填写server/config.yaml配置文件中login/ldap下的配置。配置正确后可接入LDAP系统,使用LDAP账号密码登陆系统。新登陆的用户默认同样为普通用户权限。 -------------------------------------------------------------------------------- /go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine as builder 2 | 3 | WORKDIR /go/src/github.com/nooncall/owls/go 4 | COPY . . 5 | 6 | RUN go env -w GO111MODULE=on \ 7 | && go env -w GOPROXY=https://goproxy.cn,direct \ 8 | && go env -w CGO_ENABLED=0 \ 9 | && go env \ 10 | && go mod tidy \ 11 | && go build -o server . 12 | 13 | FROM alpine:latest 14 | 15 | LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com" 16 | 17 | WORKDIR /go/src/github.com/nooncall/owls/go 18 | 19 | COPY --from=0 /go/src/github.com/nooncall/owls/go/server ./ 20 | COPY --from=0 /go/src/github.com/nooncall/owls/go/resource ./resource/ 21 | COPY --from=0 /go/src/github.com/nooncall/owls/go/config.docker.yaml ./ 22 | 23 | EXPOSE 8888 24 | ENTRYPOINT ./server -c config.docker.yaml 25 | -------------------------------------------------------------------------------- /go/api/v1/autocode/enter.go: -------------------------------------------------------------------------------- 1 | package autocode 2 | 3 | type ApiGroup struct { 4 | // Code generated by github.com/nooncall/owls/go Begin; DO NOT EDIT. 5 | AutoCodeExampleApi 6 | // Code generated by github.com/nooncall/owls/go End; DO NOT EDIT. 7 | } 8 | -------------------------------------------------------------------------------- /go/api/v1/enter.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/api/v1/auth" 5 | "github.com/nooncall/owls/go/api/v1/autocode" 6 | "github.com/nooncall/owls/go/api/v1/example" 7 | "github.com/nooncall/owls/go/api/v1/redis" 8 | "github.com/nooncall/owls/go/api/v1/system" 9 | "github.com/nooncall/owls/go/api/v1/task" 10 | "github.com/nooncall/owls/go/api/v1/tidb_or_mysql" 11 | ) 12 | 13 | type ApiGroup struct { 14 | SystemApiGroup system.ApiGroup 15 | ExampleApiGroup example.ApiGroup 16 | AutoCodeApiGroup autocode.ApiGroup 17 | TiDBOrMysqlGroup tidb_or_mysql.ApiGroup 18 | Redis redis.ApiGroup 19 | Task task.TaskApi 20 | Auth auth.AuthApi 21 | } 22 | 23 | var ApiGroupApp = new(ApiGroup) 24 | 25 | type ListData struct { 26 | Total int64 `json:"total"` 27 | List interface{} `json:"list"` 28 | Page int `json:"page"` 29 | PageSize int `json:"pageSize"` 30 | } 31 | -------------------------------------------------------------------------------- /go/api/v1/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "github.com/nooncall/owls/go/service" 4 | 5 | type ApiGroup struct { 6 | ExcelApi 7 | CustomerApi 8 | } 9 | 10 | var ( 11 | excelService = service.ServiceGroupApp.ExampleServiceGroup.ExcelService 12 | customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService 13 | fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService 14 | ) 15 | -------------------------------------------------------------------------------- /go/api/v1/redis/enter.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | type ApiGroup struct { 4 | ReadApi 5 | } 6 | -------------------------------------------------------------------------------- /go/api/v1/system/sys_jwt_blacklist.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/nooncall/owls/go/global" 6 | "github.com/nooncall/owls/go/model/common/response" 7 | "github.com/nooncall/owls/go/model/system" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | type JwtApi struct{} 12 | 13 | // @Tags Jwt 14 | // @Summary jwt加入黑名单 15 | // @Security ApiKeyAuth 16 | // @accept application/json 17 | // @Produce application/json 18 | // @Success 200 {object} response.Response{msg=string} "jwt加入黑名单" 19 | // @Router /jwt/jsonInBlacklist [post] 20 | func (j *JwtApi) JsonInBlacklist(c *gin.Context) { 21 | token := c.Request.Header.Get("x-token") 22 | jwt := system.JwtBlacklist{Jwt: token} 23 | if err := jwtService.JsonInBlacklist(jwt); err != nil { 24 | global.GVA_LOG.Error("jwt作废失败!", zap.Error(err)) 25 | response.FailWithMessage("jwt作废失败", c) 26 | } else { 27 | response.OkWithMessage("jwt作废成功", c) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /go/api/v1/tidb_or_mysql/common.go: -------------------------------------------------------------------------------- 1 | package tidb_or_mysql 2 | 3 | type Resp struct { 4 | Code int `json:"code"` 5 | Message string `json:"message"` 6 | Data interface{} `json:"data"` 7 | } 8 | 9 | type ListData struct { 10 | Total int64 `json:"total"` 11 | List interface{} `json:"list"` 12 | More bool `json:"more"` 13 | Offset int `json:"offset"` 14 | Page int `json:"page"` 15 | PageSize int `json:"pageSize"` 16 | } 17 | -------------------------------------------------------------------------------- /go/api/v1/tidb_or_mysql/enter.go: -------------------------------------------------------------------------------- 1 | package tidb_or_mysql 2 | 3 | type ApiGroup struct { 4 | AdminApi 5 | BackupApi 6 | ClusterApi 7 | RuleApi 8 | TaskApi 9 | ReadApi 10 | } 11 | -------------------------------------------------------------------------------- /go/api/v1/tidb_or_mysql/read.go: -------------------------------------------------------------------------------- 1 | package tidb_or_mysql 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gin-gonic/gin" 7 | 8 | "github.com/nooncall/owls/go/model/common/response" 9 | "github.com/nooncall/owls/go/service/tidb_or_mysql/task" 10 | ) 11 | 12 | type ReadApi struct{} 13 | 14 | func (readApi *ReadApi) ReadData(ctx *gin.Context) { 15 | f := "ReadData() -->" 16 | 17 | var req task.SqlParam 18 | if err := ctx.BindJSON(&req); err != nil { 19 | response.FailWithMessage(fmt.Sprintf("%s, parse param failed :%s ", f, err.Error()), ctx) 20 | return 21 | } 22 | 23 | rollBackData, err := task.ReadData(&req) 24 | if err != nil { 25 | response.FailWithMessage(fmt.Sprintf("%s: read failed, err: %s", f, err.Error()), ctx) 26 | return 27 | } 28 | 29 | response.OkWithData(rollBackData, ctx) 30 | } 31 | 32 | func (readApi *ReadApi) GetTableInfo(ctx *gin.Context) { 33 | f := "GetTableInfo()-->" 34 | var req task.SqlParam 35 | if err := ctx.BindJSON(&req); err != nil { 36 | response.FailWithMessage(fmt.Sprintf("%s, parse param failed :%s ", f, err.Error()), ctx) 37 | return 38 | } 39 | 40 | info, err := task.GetTableInfo(&req) 41 | if err != nil { 42 | response.FailWithMessage(fmt.Sprintf("%s: get table info failed, err: %s", f, err.Error()), ctx) 43 | return 44 | } 45 | response.OkWithData(info, ctx) 46 | } 47 | -------------------------------------------------------------------------------- /go/cmd/owls/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/core" 5 | "github.com/nooncall/owls/go/global" 6 | "github.com/nooncall/owls/go/initialize" 7 | "github.com/nooncall/owls/go/service/tidb_or_mysql/injection" 8 | "github.com/nooncall/owls/go/utils/logger" 9 | "go.uber.org/zap" 10 | ) 11 | 12 | //go:generate go env -w GO111MODULE=on 13 | //go:generate go env -w GOPROXY=https://goproxy.cn,direct 14 | //go:generate go mod tidy 15 | //go:generate go mod download 16 | 17 | // @title Swagger Example API 18 | // @version 0.0.1 19 | // @description This is a sample Server pets 20 | // @securityDefinitions.apikey ApiKeyAuth 21 | // @in header 22 | // @name x-token 23 | // @BasePath / 24 | func main() { 25 | global.GVA_VP = core.Viper() // 初始化Viper 26 | global.GVA_LOG = core.Zap() // 初始化zap日志库 27 | zap.ReplaceGlobals(global.GVA_LOG) 28 | global.GVA_DB = initialize.Gorm() // gorm连接数据库 29 | if global.GVA_DB != nil { 30 | initialize.RegisterTables(global.GVA_DB) // 初始化表 31 | // 程序结束前关闭数据库链接 32 | db, _ := global.GVA_DB.DB() 33 | defer db.Close() 34 | } 35 | 36 | injection.Injection() 37 | logger.InitLog(global.GVA_CONFIG.DBFilter.LogDir, "owl.log", global.GVA_CONFIG.DBFilter.LogLevel) 38 | 39 | core.RunWindowsServer() 40 | } 41 | -------------------------------------------------------------------------------- /go/config/auto_code.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Autocode struct { 4 | TransferRestart bool `mapstructure:"transfer-restart" json:"transferRestart" yaml:"transfer-restart"` 5 | Root string `mapstructure:"root" json:"root" yaml:"root"` 6 | Server string `mapstructure:"server" json:"server" yaml:"server"` 7 | SApi string `mapstructure:"server-api" json:"serverApi" yaml:"server-api"` 8 | SInitialize string `mapstructure:"server-initialize" json:"serverInitialize" yaml:"server-initialize"` 9 | SModel string `mapstructure:"server-model" json:"serverModel" yaml:"server-model"` 10 | SRequest string `mapstructure:"server-request" json:"serverRequest" yaml:"server-request"` 11 | SRouter string `mapstructure:"server-router" json:"serverRouter" yaml:"server-router"` 12 | SService string `mapstructure:"server-service" json:"serverService" yaml:"server-service"` 13 | Web string `mapstructure:"web" json:"web" yaml:"web"` 14 | WApi string `mapstructure:"web-api" json:"webApi" yaml:"web-api"` 15 | WForm string `mapstructure:"web-form" json:"webForm" yaml:"web-form"` 16 | WTable string `mapstructure:"web-table" json:"webTable" yaml:"web-table"` 17 | } 18 | -------------------------------------------------------------------------------- /go/config/captcha.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Captcha struct { 4 | KeyLong int `mapstructure:"key-long" json:"keyLong" yaml:"key-long"` // 验证码长度 5 | ImgWidth int `mapstructure:"img-width" json:"imgWidth" yaml:"img-width"` // 验证码宽度 6 | ImgHeight int `mapstructure:"img-height" json:"imgHeight" yaml:"img-height"` // 验证码高度 7 | } 8 | -------------------------------------------------------------------------------- /go/config/casbin.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Casbin struct { 4 | ModelPath string `mapstructure:"model-path" json:"modelPath" yaml:"model-path"` // 存放casbin模型的相对路径 5 | } 6 | -------------------------------------------------------------------------------- /go/config/cors.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type CORS struct { 4 | Mode string `mapstructure:"mode" json:"mode" yaml:"mode"` 5 | Whitelist []CORSWhitelist `mapstructure:"whitelist" json:"whitelist" yaml:"whitelist"` 6 | } 7 | 8 | type CORSWhitelist struct { 9 | AllowOrigin string `mapstructure:"allow-origin" json:"allow-origin" yaml:"allow-origin"` 10 | AllowMethods string `mapstructure:"allow-methods" json:"allow-methods" yaml:"allow-methods"` 11 | AllowHeaders string `mapstructure:"allow-headers" json:"allow-headers" yaml:"allow-headers"` 12 | ExposeHeaders string `mapstructure:"expose-headers" json:"expose-headers" yaml:"expose-headers"` 13 | AllowCredentials bool `mapstructure:"allow-credentials" json:"allow-credentials" yaml:"allow-credentials"` 14 | } 15 | -------------------------------------------------------------------------------- /go/config/email.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Email struct { 4 | To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 5 | Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 6 | From string `mapstructure:"from" json:"from" yaml:"from"` // 收件人 7 | Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 8 | IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 9 | Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 10 | Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 11 | } 12 | -------------------------------------------------------------------------------- /go/config/excel.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Excel struct { 4 | Dir string `mapstructure:"dir" json:"dir" yaml:"dir"` 5 | } 6 | -------------------------------------------------------------------------------- /go/config/gorm_mysql.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Mysql struct { 4 | Path string `mapstructure:"path" json:"path" yaml:"path"` // 服务器地址 5 | Port string `mapstructure:"port" json:"port" yaml:"port"` // 端口 6 | Config string `mapstructure:"config" json:"config" yaml:"config"` // 高级配置 7 | Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"` // 数据库名 8 | Username string `mapstructure:"username" json:"username" yaml:"username"` // 数据库用户名 9 | Password string `mapstructure:"password" json:"password" yaml:"password"` // 数据库密码 10 | MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数 11 | MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数 12 | LogMode string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"` // 是否开启Gorm全局日志 13 | LogZap bool `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"` // 是否通过zap写入日志文件 14 | } 15 | 16 | func (m *Mysql) Dsn() string { 17 | return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config 18 | } 19 | 20 | func (m *Mysql) GetLogMode() string { 21 | return m.LogMode 22 | } 23 | -------------------------------------------------------------------------------- /go/config/jwt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type JWT struct { 4 | SigningKey string `mapstructure:"signing-key" json:"signingKey" yaml:"signing-key"` // jwt签名 5 | ExpiresTime int64 `mapstructure:"expires-time" json:"expiresTime" yaml:"expires-time"` // 过期时间 6 | BufferTime int64 `mapstructure:"buffer-time" json:"bufferTime" yaml:"buffer-time"` // 缓冲时间 7 | Issuer string `mapstructure:"issuer" json:"issuer" yaml:"issuer"` // 签发者 8 | } 9 | -------------------------------------------------------------------------------- /go/config/oss_aliyun.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type AliyunOSS struct { 4 | Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` 5 | AccessKeyId string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"` 6 | AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"` 7 | BucketName string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"` 8 | BucketUrl string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"` 9 | BasePath string `mapstructure:"base-path" json:"basePath" yaml:"base-path"` 10 | } 11 | -------------------------------------------------------------------------------- /go/config/oss_aws.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type AwsS3 struct { 4 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` 5 | Region string `mapstructure:"region" json:"region" yaml:"region"` 6 | Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` 7 | S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3ForcePathStyle" yaml:"s3-force-path-style"` 8 | DisableSSL bool `mapstructure:"disable-ssl" json:"disableSSL" yaml:"disable-ssl"` 9 | SecretID string `mapstructure:"secret-id" json:"secretID" yaml:"secret-id"` 10 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` 11 | BaseURL string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"` 12 | PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"` 13 | } 14 | -------------------------------------------------------------------------------- /go/config/oss_huawei.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type HuaWeiObs struct { 4 | Path string `mapstructure:"path" json:"path" yaml:"path"` 5 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` 6 | Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` 7 | AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"` 8 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` 9 | } 10 | -------------------------------------------------------------------------------- /go/config/oss_local.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Local struct { 4 | Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件路径 5 | } 6 | -------------------------------------------------------------------------------- /go/config/oss_qiniu.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Qiniu struct { 4 | Zone string `mapstructure:"zone" json:"zone" yaml:"zone"` // 存储区域 5 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` // 空间名称 6 | ImgPath string `mapstructure:"img-path" json:"imgPath" yaml:"img-path"` // CDN加速域名 7 | UseHTTPS bool `mapstructure:"use-https" json:"useHttps" yaml:"use-https"` // 是否使用https 8 | AccessKey string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"` // 秘钥AK 9 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` // 秘钥SK 10 | UseCdnDomains bool `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速 11 | } 12 | -------------------------------------------------------------------------------- /go/config/oss_tencent.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type TencentCOS struct { 4 | Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"` 5 | Region string `mapstructure:"region" json:"region" yaml:"region"` 6 | SecretID string `mapstructure:"secret-id" json:"secretID" yaml:"secret-id"` 7 | SecretKey string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` 8 | BaseURL string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"` 9 | PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"` 10 | } 11 | -------------------------------------------------------------------------------- /go/config/redis.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Redis struct { 4 | DB int `mapstructure:"db" json:"db" yaml:"db"` // redis的哪个数据库 5 | Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` // 服务器地址:端口 6 | Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码 7 | } 8 | -------------------------------------------------------------------------------- /go/config/system.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type System struct { 4 | Env string `mapstructure:"env" json:"env" yaml:"env"` // 环境值 5 | Addr int `mapstructure:"addr" json:"addr" yaml:"addr"` // 端口值 6 | DbType string `mapstructure:"db-type" json:"dbType" yaml:"db-type"` // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql 7 | OssType string `mapstructure:"oss-type" json:"ossType" yaml:"oss-type"` // Oss类型 8 | UseMultipoint bool `mapstructure:"use-multipoint" json:"useMultipoint" yaml:"use-multipoint"` // 多点登录拦截 9 | UseRedis bool `mapstructure:"use-redis" json:"useRedis" yaml:"use-redis"` // 使用redis 10 | LimitCountIP int `mapstructure:"iplimit-count" json:"iplimitCount" yaml:"iplimit-count"` 11 | LimitTimeIP int `mapstructure:"iplimit-time" json:"iplimitTime" yaml:"iplimit-time"` 12 | ShowSql bool `json:"show_sql"` 13 | } 14 | -------------------------------------------------------------------------------- /go/config/timer.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Timer struct { 4 | Start bool `mapstructure:"start" json:"start" yaml:"start"` // 是否启用 5 | Spec string `mapstructure:"spec" json:"spec" yaml:"spec"` // CRON表达式 6 | Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"` 7 | } 8 | 9 | type Detail struct { 10 | TableName string `mapstructure:"tableName" json:"tableName" yaml:"tableName"` // 需要清理的表名 11 | CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"` // 需要比较时间的字段 12 | Interval string `mapstructure:"interval" json:"interval" yaml:"interval"` // 时间间隔 13 | } 14 | -------------------------------------------------------------------------------- /go/config/zap.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Zap struct { 4 | Level string `mapstructure:"level" json:"level" yaml:"level"` // 级别 5 | Format string `mapstructure:"format" json:"format" yaml:"format"` // 输出 6 | Prefix string `mapstructure:"prefix" json:"prefix" yaml:"prefix"` // 日志前缀 7 | Director string `mapstructure:"director" json:"director" yaml:"director"` // 日志文件夹 8 | ShowLine bool `mapstructure:"show-line" json:"showLine" yaml:"showLine"` // 显示行 9 | EncodeLevel string `mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"` // 编码级 10 | StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key"` // 栈名 11 | LogInConsole bool `mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"` // 输出控制台 12 | } 13 | -------------------------------------------------------------------------------- /go/core/server_other.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package core 5 | 6 | import ( 7 | "time" 8 | 9 | "github.com/fvbock/endless" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func initServer(address string, router *gin.Engine) server { 14 | s := endless.NewServer(address, router) 15 | s.ReadHeaderTimeout = 20 * time.Second 16 | s.WriteTimeout = 20 * time.Second 17 | s.MaxHeaderBytes = 1 << 20 18 | return s 19 | } 20 | -------------------------------------------------------------------------------- /go/core/server_win.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package core 5 | 6 | import ( 7 | "net/http" 8 | "time" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func initServer(address string, router *gin.Engine) server { 14 | return &http.Server{ 15 | Addr: address, 16 | Handler: router, 17 | ReadTimeout: 20 * time.Second, 18 | WriteTimeout: 20 * time.Second, 19 | MaxHeaderBytes: 1 << 20, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/global/model.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "time" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type GVA_MODEL struct { 10 | ID uint `gorm:"primarykey"` // 主键ID 11 | CreatedAt time.Time // 创建时间 12 | UpdatedAt time.Time // 更新时间 13 | DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间 14 | } 15 | -------------------------------------------------------------------------------- /go/initialize/internal/logger.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/nooncall/owls/go/global" 7 | "gorm.io/gorm/logger" 8 | ) 9 | 10 | type writer struct { 11 | logger.Writer 12 | } 13 | 14 | // NewWriter writer 构造函数 15 | // Author [SliverHorn](https://github.com/SliverHorn) 16 | func NewWriter(w logger.Writer) *writer { 17 | return &writer{Writer: w} 18 | } 19 | 20 | // Printf 格式化打印日志 21 | // Author [SliverHorn](https://github.com/SliverHorn) 22 | func (w *writer) Printf(message string, data ...interface{}) { 23 | var logZap bool 24 | switch global.GVA_CONFIG.System.DbType { 25 | case "mysql": 26 | logZap = global.GVA_CONFIG.Mysql.LogZap 27 | case "pgsql": 28 | logZap = global.GVA_CONFIG.Pgsql.LogZap 29 | } 30 | if logZap { 31 | global.GVA_LOG.Info(fmt.Sprintf(message+"\n", data...)) 32 | } else { 33 | w.Writer.Printf(message, data...) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /go/initialize/plugin.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | //"github.com/nooncall/owls/go/plugin/email" // 本地插件仓库地址模式 5 | "github.com/gin-gonic/gin" 6 | "github.com/nooncall/owls/go/plugin/example_plugin" 7 | "github.com/nooncall/owls/go/utils/plugin" 8 | ) 9 | 10 | func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) { 11 | for i := range Plugin { 12 | PluginGroup := group.Group(Plugin[i].RouterPath()) 13 | Plugin[i].Register(PluginGroup) 14 | } 15 | } 16 | 17 | func InstallPlugin(PublicGroup *gin.RouterGroup, PrivateGroup *gin.RouterGroup) { 18 | // 添加开放权限的插件 示例 19 | PluginInit(PublicGroup, example_plugin.ExamplePlugin) 20 | } 21 | -------------------------------------------------------------------------------- /go/initialize/validator.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import "github.com/nooncall/owls/go/utils" 4 | 5 | func init() { 6 | _ = utils.RegisterRule("PageVerify", 7 | utils.Rules{ 8 | "Page": {utils.NotEmpty()}, 9 | "PageSize": {utils.NotEmpty()}, 10 | }, 11 | ) 12 | _ = utils.RegisterRule("IdVerify", 13 | utils.Rules{ 14 | "Id": {utils.NotEmpty()}, 15 | }, 16 | ) 17 | _ = utils.RegisterRule("AuthorityIdVerify", 18 | utils.Rules{ 19 | "AuthorityId": {utils.NotEmpty()}, 20 | }, 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /go/middleware/casbin_rbac.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/nooncall/owls/go/global" 6 | "github.com/nooncall/owls/go/model/common/response" 7 | "github.com/nooncall/owls/go/service" 8 | "github.com/nooncall/owls/go/utils" 9 | ) 10 | 11 | var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService 12 | 13 | // 拦截器 14 | func CasbinHandler() gin.HandlerFunc { 15 | return func(c *gin.Context) { 16 | waitUse, _ := utils.GetClaims(c) 17 | // 获取请求的PATH 18 | obj := c.Request.URL.Path 19 | // 获取请求方法 20 | act := c.Request.Method 21 | // 获取用户的角色 22 | sub := waitUse.AuthorityId 23 | e := casbinService.Casbin() 24 | // 判断策略中是否存在 25 | success, _ := e.Enforce(sub, obj, act) 26 | if global.GVA_CONFIG.System.Env == "develop" || success { 27 | c.Next() 28 | } else { 29 | c.Next() 30 | return 31 | // todo, handle this. 32 | response.FailWithDetailed(gin.H{}, "权限不足", c) 33 | c.Abort() 34 | return 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /go/middleware/loadtls.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/unrolled/secure" 8 | ) 9 | 10 | // 用https把这个中间件在router里面use一下就好 11 | 12 | func LoadTls() gin.HandlerFunc { 13 | return func(c *gin.Context) { 14 | middleware := secure.New(secure.Options{ 15 | SSLRedirect: true, 16 | SSLHost: "localhost:443", 17 | }) 18 | err := middleware.Process(c.Writer, c.Request) 19 | if err != nil { 20 | // 如果出现错误,请不要继续 21 | fmt.Println(err) 22 | return 23 | } 24 | // 继续往下处理 25 | c.Next() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /go/middleware/need_init.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/nooncall/owls/go/global" 6 | "github.com/nooncall/owls/go/model/common/response" 7 | ) 8 | 9 | // 处理跨域请求,支持options访问 10 | func NeedInit() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | if global.GVA_DB == nil { 13 | response.OkWithDetailed(gin.H{ 14 | "needInit": true, 15 | }, "前往初始化数据库", c) 16 | c.Abort() 17 | } else { 18 | c.Next() 19 | } 20 | // 处理请求 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go/model/autocode/autocodeExample.go: -------------------------------------------------------------------------------- 1 | // 自动生成模板SysDictionaryDetail 2 | package autocode 3 | 4 | import ( 5 | "github.com/nooncall/owls/go/global" 6 | ) 7 | 8 | // 如果含有time.Time 请自行import time包 9 | type AutoCodeExample struct { 10 | global.GVA_MODEL 11 | AutoCodeExampleField string `json:"autoCodeExampleField" form:"autoCodeExampleField" gorm:"column:auto_code_example_field;comment:仅作示例条目无实际作用"` // 展示值 12 | } 13 | -------------------------------------------------------------------------------- /go/model/autocode/request/autocodeExample.go: -------------------------------------------------------------------------------- 1 | // 自动生成模板SysDictionaryDetail 2 | package request 3 | 4 | import ( 5 | "github.com/nooncall/owls/go/model/autocode" 6 | "github.com/nooncall/owls/go/model/common/request" 7 | ) 8 | 9 | // 如果含有time.Time 请自行import time包 10 | type AutoCodeExampleSearch struct { 11 | autocode.AutoCodeExample 12 | request.PageInfo 13 | } 14 | -------------------------------------------------------------------------------- /go/model/common/request/common.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // PageInfo Paging common input parameter structure 4 | type PageInfo struct { 5 | Page int `json:"page" form:"page"` // 页码 6 | PageSize int `json:"pageSize" form:"pageSize"` // 每页大小 7 | } 8 | 9 | type SortPageInfo struct { 10 | PageInfo 11 | OrderKey string `json:"orderKey"` // 排序 12 | Key string `json:"key"` 13 | Desc bool `json:"desc"` // 排序方式:升序false(默认)|降序true 14 | Operator string `json:"operator"` 15 | Type string `json:"type"` 16 | } 17 | 18 | // GetById Find by id structure 19 | type GetById struct { 20 | ID int `json:"id" form:"id"` // 主键ID 21 | } 22 | 23 | func (r *GetById) Uint() uint { 24 | return uint(r.ID) 25 | } 26 | 27 | type IdsReq struct { 28 | Ids []int `json:"ids" form:"ids"` 29 | } 30 | 31 | // GetAuthorityId Get role by id structure 32 | type GetAuthorityId struct { 33 | AuthorityId string `json:"authorityId" form:"authorityId"` // 角色ID 34 | } 35 | 36 | type Empty struct{} 37 | -------------------------------------------------------------------------------- /go/model/common/response/common.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type PageResult struct { 4 | List interface{} `json:"list"` 5 | Total int64 `json:"total"` 6 | Page int `json:"page"` 7 | PageSize int `json:"pageSize"` 8 | } 9 | -------------------------------------------------------------------------------- /go/model/common/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | type Response struct { 10 | Code int `json:"code"` 11 | Data interface{} `json:"data"` 12 | Msg string `json:"msg"` 13 | } 14 | 15 | const ( 16 | ERROR = 7 17 | SUCCESS = 0 18 | ) 19 | 20 | func Result(code int, data interface{}, msg string, c *gin.Context) { 21 | // 开始时间 22 | c.JSON(http.StatusOK, Response{ 23 | code, 24 | data, 25 | msg, 26 | }) 27 | } 28 | 29 | func Ok(c *gin.Context) { 30 | Result(SUCCESS, map[string]interface{}{}, "操作成功", c) 31 | } 32 | 33 | func OkWithMessage(message string, c *gin.Context) { 34 | Result(SUCCESS, map[string]interface{}{}, message, c) 35 | } 36 | 37 | func OkWithData(data interface{}, c *gin.Context) { 38 | Result(SUCCESS, data, "操作成功", c) 39 | } 40 | 41 | func OkWithDetailed(data interface{}, message string, c *gin.Context) { 42 | Result(SUCCESS, data, message, c) 43 | } 44 | 45 | func Fail(c *gin.Context) { 46 | Result(ERROR, map[string]interface{}{}, "操作失败", c) 47 | } 48 | 49 | func FailWithMessage(message string, c *gin.Context) { 50 | Result(ERROR, map[string]interface{}{}, message, c) 51 | } 52 | 53 | func FailWithDetailed(data interface{}, message string, c *gin.Context) { 54 | Result(ERROR, data, message, c) 55 | } 56 | -------------------------------------------------------------------------------- /go/model/example/exa_breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | ) 6 | 7 | // file struct, 文件结构体 8 | type ExaFile struct { 9 | global.GVA_MODEL 10 | FileName string 11 | FileMd5 string 12 | FilePath string 13 | ExaFileChunk []ExaFileChunk 14 | ChunkTotal int 15 | IsFinish bool 16 | } 17 | 18 | // file chunk struct, 切片结构体 19 | type ExaFileChunk struct { 20 | global.GVA_MODEL 21 | ExaFileID uint 22 | FileChunkNumber int 23 | FileChunkPath string 24 | } 25 | -------------------------------------------------------------------------------- /go/model/example/exa_customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | type ExaCustomer struct { 9 | global.GVA_MODEL 10 | CustomerName string `json:"customerName" form:"customerName" gorm:"comment:客户名"` // 客户名 11 | CustomerPhoneData string `json:"customerPhoneData" form:"customerPhoneData" gorm:"comment:客户手机号"` // 客户手机号 12 | SysUserID uint `json:"sysUserId" form:"sysUserId" gorm:"comment:管理ID"` // 管理ID 13 | SysUserAuthorityID string `json:"sysUserAuthorityID" form:"sysUserAuthorityID" gorm:"comment:管理角色ID"` // 管理角色ID 14 | SysUser system.SysUser `json:"sysUser" form:"sysUser" gorm:"comment:管理详情"` // 管理详情 15 | } 16 | -------------------------------------------------------------------------------- /go/model/example/exa_excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "github.com/nooncall/owls/go/model/system" 4 | 5 | type ExcelInfo struct { 6 | FileName string `json:"fileName"` // 文件名 7 | InfoList []system.SysBaseMenu `json:"infoList"` 8 | } 9 | -------------------------------------------------------------------------------- /go/model/example/exa_file_upload_download.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | ) 6 | 7 | type ExaFileUploadAndDownload struct { 8 | global.GVA_MODEL 9 | Name string `json:"name" gorm:"comment:文件名"` // 文件名 10 | Url string `json:"url" gorm:"comment:文件地址"` // 文件地址 11 | Tag string `json:"tag" gorm:"comment:文件标签"` // 文件标签 12 | Key string `json:"key" gorm:"comment:编号"` // 编号 13 | } 14 | -------------------------------------------------------------------------------- /go/model/example/response/exa_breakpoint_continue.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/example" 4 | 5 | type FilePathResponse struct { 6 | FilePath string `json:"filePath"` 7 | } 8 | 9 | type FileResponse struct { 10 | File example.ExaFile `json:"file"` 11 | } 12 | -------------------------------------------------------------------------------- /go/model/example/response/exa_customer.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/example" 4 | 5 | type ExaCustomerResponse struct { 6 | Customer example.ExaCustomer `json:"customer"` 7 | } 8 | -------------------------------------------------------------------------------- /go/model/example/response/exa_file_upload_download.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/example" 4 | 5 | type ExaFileResponse struct { 6 | File example.ExaFileUploadAndDownload `json:"file"` 7 | } 8 | -------------------------------------------------------------------------------- /go/model/system/request/jwt.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/golang-jwt/jwt/v4" 5 | uuid "github.com/satori/go.uuid" 6 | ) 7 | 8 | // Custom claims structure 9 | type CustomClaims struct { 10 | BaseClaims 11 | BufferTime int64 12 | jwt.StandardClaims 13 | } 14 | 15 | type BaseClaims struct { 16 | UUID uuid.UUID 17 | ID uint 18 | Username string 19 | NickName string 20 | AuthorityId string 21 | } 22 | -------------------------------------------------------------------------------- /go/model/system/request/sys_api.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/common/request" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | // api分页条件查询及排序结构体 9 | type SearchApiParams struct { 10 | system.SysApi 11 | request.PageInfo 12 | OrderKey string `json:"orderKey"` // 排序 13 | Desc bool `json:"desc"` // 排序方式:升序false(默认)|降序true 14 | } 15 | -------------------------------------------------------------------------------- /go/model/system/request/sys_authority_btn.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | type SysAuthorityBtnReq struct { 4 | MenuID uint `json:"menuID"` 5 | AuthorityId string `json:"authorityId"` 6 | Selected []uint `json:"selected"` 7 | } 8 | -------------------------------------------------------------------------------- /go/model/system/request/sys_auto_history.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import "github.com/nooncall/owls/go/model/common/request" 4 | 5 | type SysAutoHistory struct { 6 | request.PageInfo 7 | } 8 | -------------------------------------------------------------------------------- /go/model/system/request/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | // Casbin info structure 4 | type CasbinInfo struct { 5 | Path string `json:"path"` // 路径 6 | Method string `json:"method"` // 方法 7 | } 8 | 9 | // Casbin structure for input parameters 10 | type CasbinInReceive struct { 11 | AuthorityId string `json:"authorityId"` // 权限id 12 | CasbinInfos []CasbinInfo `json:"casbinInfos"` 13 | } 14 | 15 | func DefaultCasbin() []CasbinInfo { 16 | return []CasbinInfo{ 17 | {Path: "/menu/getMenu", Method: "POST"}, 18 | {Path: "/jwt/jsonInBlacklist", Method: "POST"}, 19 | {Path: "/base/login", Method: "POST"}, 20 | {Path: "/user/admin_register", Method: "POST"}, 21 | {Path: "/user/changePassword", Method: "POST"}, 22 | {Path: "/user/setUserAuthority", Method: "POST"}, 23 | {Path: "/user/setUserInfo", Method: "PUT"}, 24 | {Path: "/user/getUserInfo", Method: "GET"}, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /go/model/system/request/sys_dictionary.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/common/request" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | type SysDictionarySearch struct { 9 | system.SysDictionary 10 | request.PageInfo 11 | } 12 | -------------------------------------------------------------------------------- /go/model/system/request/sys_dictionary_detail.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/common/request" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | type SysDictionaryDetailSearch struct { 9 | system.SysDictionaryDetail 10 | request.PageInfo 11 | } 12 | -------------------------------------------------------------------------------- /go/model/system/request/sys_menu.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | // Add menu authority info structure 9 | type AddMenuAuthorityInfo struct { 10 | Menus []system.SysBaseMenu `json:"menus"` 11 | AuthorityId string `json:"authorityId"` // 角色ID 12 | } 13 | 14 | func DefaultMenu() []system.SysBaseMenu { 15 | return []system.SysBaseMenu{{ 16 | GVA_MODEL: global.GVA_MODEL{ID: 1}, 17 | ParentId: "0", 18 | Path: "dashboard", 19 | Name: "dashboard", 20 | Component: "view/dashboard/index.vue", 21 | Sort: 1, 22 | Meta: system.Meta{ 23 | Title: "仪表盘", 24 | Icon: "setting", 25 | }, 26 | }} 27 | } 28 | -------------------------------------------------------------------------------- /go/model/system/request/sys_operation_record.go: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/common/request" 5 | "github.com/nooncall/owls/go/model/system" 6 | ) 7 | 8 | type SysOperationRecordSearch struct { 9 | system.SysOperationRecord 10 | request.PageInfo 11 | } 12 | -------------------------------------------------------------------------------- /go/model/system/response/sys_api.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/system" 4 | 5 | type SysAPIResponse struct { 6 | Api system.SysApi `json:"api"` 7 | } 8 | 9 | type SysAPIListResponse struct { 10 | Apis []system.SysApi `json:"apis"` 11 | } 12 | -------------------------------------------------------------------------------- /go/model/system/response/sys_authority.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/system" 4 | 5 | type SysAuthorityResponse struct { 6 | Authority system.SysAuthority `json:"authority"` 7 | } 8 | 9 | type SysAuthorityCopyResponse struct { 10 | Authority system.SysAuthority `json:"authority"` 11 | OldAuthorityId string `json:"oldAuthorityId"` // 旧角色ID 12 | } 13 | -------------------------------------------------------------------------------- /go/model/system/response/sys_authority_btn.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type SysAuthorityBtnRes struct { 4 | Selected []uint `json:"selected"` 5 | } 6 | -------------------------------------------------------------------------------- /go/model/system/response/sys_auto_code.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type Db struct { 4 | Database string `json:"database" gorm:"column:database"` 5 | } 6 | 7 | type Table struct { 8 | TableName string `json:"tableName" gorm:"column:table_name"` 9 | } 10 | 11 | type Column struct { 12 | DataType string `json:"dataType" gorm:"column:data_type"` 13 | ColumnName string `json:"columnName" gorm:"column:column_name"` 14 | DataTypeLong string `json:"dataTypeLong" gorm:"column:data_type_long"` 15 | ColumnComment string `json:"columnComment" gorm:"column:column_comment"` 16 | } 17 | -------------------------------------------------------------------------------- /go/model/system/response/sys_auto_code_history.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "time" 4 | 5 | type AutoCodeHistory struct { 6 | ID uint `json:"ID" gorm:"column:id"` 7 | CreatedAt time.Time `json:"CreatedAt" gorm:"column:created_at"` 8 | UpdatedAt time.Time `json:"UpdatedAt" gorm:"column:updated_at"` 9 | TableName string `json:"tableName" gorm:"column:table_name"` 10 | StructName string `json:"structName" gorm:"column:struct_name"` 11 | StructCNName string `json:"structCNName" gorm:"column:struct_cn_name"` 12 | Flag int `json:"flag" gorm:"column:flag"` 13 | } 14 | -------------------------------------------------------------------------------- /go/model/system/response/sys_captcha.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type SysCaptchaResponse struct { 4 | CaptchaId string `json:"captchaId"` 5 | PicPath string `json:"picPath"` 6 | CaptchaLength int `json:"captchaLength""` 7 | } 8 | -------------------------------------------------------------------------------- /go/model/system/response/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/system/request" 5 | ) 6 | 7 | type PolicyPathResponse struct { 8 | Paths []request.CasbinInfo `json:"paths"` 9 | } 10 | -------------------------------------------------------------------------------- /go/model/system/response/sys_menu.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/model/system" 4 | 5 | type SysMenusResponse struct { 6 | Menus []system.SysMenu `json:"menus"` 7 | } 8 | 9 | type SysBaseMenusResponse struct { 10 | Menus []system.SysBaseMenu `json:"menus"` 11 | } 12 | 13 | type SysBaseMenuResponse struct { 14 | Menu system.SysBaseMenu `json:"menu"` 15 | } 16 | -------------------------------------------------------------------------------- /go/model/system/response/sys_system.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import "github.com/nooncall/owls/go/config" 4 | 5 | type SysConfigResponse struct { 6 | Config config.Server `json:"config"` 7 | } 8 | -------------------------------------------------------------------------------- /go/model/system/response/sys_user.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/system" 5 | ) 6 | 7 | type SysUserResponse struct { 8 | User system.SysUser `json:"user"` 9 | } 10 | 11 | type LoginResponse struct { 12 | User system.SysUser `json:"user"` 13 | Token string `json:"token"` 14 | ExpiresAt int64 `json:"expiresAt"` 15 | } 16 | -------------------------------------------------------------------------------- /go/model/system/sys_api.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | ) 6 | 7 | type SysApi struct { 8 | global.GVA_MODEL 9 | Path string `json:"path" gorm:"comment:api路径"` // api路径 10 | Description string `json:"description" gorm:"comment:api中文描述"` // api中文描述 11 | ApiGroup string `json:"apiGroup" gorm:"comment:api组"` // api组 12 | Method string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE 13 | } 14 | -------------------------------------------------------------------------------- /go/model/system/sys_authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type SysAuthority struct { 8 | CreatedAt time.Time // 创建时间 9 | UpdatedAt time.Time // 更新时间 10 | DeletedAt *time.Time `sql:"index"` 11 | AuthorityId string `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID 12 | AuthorityName string `json:"authorityName" gorm:"comment:角色名"` // 角色名 13 | ParentId string `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID 14 | DataAuthorityId []SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id"` 15 | Children []SysAuthority `json:"children" gorm:"-"` 16 | SysBaseMenus []SysBaseMenu `json:"menus" gorm:"many2many:sys_authority_menus;"` 17 | Users []SysUser `json:"-" gorm:"many2many:sys_user_authority;"` 18 | DefaultRouter string `json:"defaultRouter" gorm:"comment:默认菜单;default:dashboard"` // 默认菜单(默认dashboard) 19 | } 20 | -------------------------------------------------------------------------------- /go/model/system/sys_authority_btn.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type SysAuthorityBtn struct { 4 | AuthorityId string `gorm:"comment:角色ID"` 5 | SysMenuID uint `gorm:"comment:菜单ID"` 6 | SysBaseMenuBtnID uint `gorm:"comment:菜单按钮ID"` 7 | SysBaseMenuBtn SysBaseMenuBtn ` gorm:"comment:按钮详情"` 8 | } 9 | -------------------------------------------------------------------------------- /go/model/system/sys_authority_menu.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type SysMenu struct { 4 | SysBaseMenu 5 | MenuId string `json:"menuId" gorm:"comment:菜单ID"` 6 | AuthorityId string `json:"-" gorm:"comment:角色ID"` 7 | Children []SysMenu `json:"children" gorm:"-"` 8 | Parameters []SysBaseMenuParameter `json:"parameters" gorm:"foreignKey:SysBaseMenuID;references:MenuId"` 9 | Btns map[string]string `json:"btns" gorm:"-"` 10 | } 11 | 12 | func (s SysMenu) TableName() string { 13 | return "authority_menu" 14 | } 15 | -------------------------------------------------------------------------------- /go/model/system/sys_dictionary.go: -------------------------------------------------------------------------------- 1 | // 自动生成模板SysDictionary 2 | package system 3 | 4 | import ( 5 | "github.com/nooncall/owls/go/global" 6 | ) 7 | 8 | // 如果含有time.Time 请自行import time包 9 | type SysDictionary struct { 10 | global.GVA_MODEL 11 | Name string `json:"name" form:"name" gorm:"column:name;comment:字典名(中)"` // 字典名(中) 12 | Type string `json:"type" form:"type" gorm:"column:type;comment:字典名(英)"` // 字典名(英) 13 | Status *bool `json:"status" form:"status" gorm:"column:status;comment:状态"` // 状态 14 | Desc string `json:"desc" form:"desc" gorm:"column:desc;comment:描述"` // 描述 15 | SysDictionaryDetails []SysDictionaryDetail `json:"sysDictionaryDetails" form:"sysDictionaryDetails"` 16 | } 17 | -------------------------------------------------------------------------------- /go/model/system/sys_dictionary_detail.go: -------------------------------------------------------------------------------- 1 | // 自动生成模板SysDictionaryDetail 2 | package system 3 | 4 | import ( 5 | "github.com/nooncall/owls/go/global" 6 | ) 7 | 8 | // 如果含有time.Time 请自行import time包 9 | type SysDictionaryDetail struct { 10 | global.GVA_MODEL 11 | Label string `json:"label" form:"label" gorm:"column:label;comment:展示值"` // 展示值 12 | Value int `json:"value" form:"value" gorm:"column:value;comment:字典值"` // 字典值 13 | Status *bool `json:"status" form:"status" gorm:"column:status;comment:启用状态"` // 启用状态 14 | Sort int `json:"sort" form:"sort" gorm:"column:sort;comment:排序标记"` // 排序标记 15 | SysDictionaryID int `json:"sysDictionaryID" form:"sysDictionaryID" gorm:"column:sys_dictionary_id;comment:关联标记"` // 关联标记 16 | } 17 | -------------------------------------------------------------------------------- /go/model/system/sys_jwt_blacklist.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | ) 6 | 7 | type JwtBlacklist struct { 8 | global.GVA_MODEL 9 | Jwt string `gorm:"type:text;comment:jwt"` 10 | } 11 | -------------------------------------------------------------------------------- /go/model/system/sys_menu_btn.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import "github.com/nooncall/owls/go/global" 4 | 5 | type SysBaseMenuBtn struct { 6 | global.GVA_MODEL 7 | Name string `json:"name" gorm:"comment:按钮关键key"` 8 | Desc string `json:"desc" gorm:"按钮备注"` 9 | SysBaseMenuID uint `json:"sysBaseMenuID" gorm:"comment:菜单ID"` 10 | } 11 | -------------------------------------------------------------------------------- /go/model/system/sys_system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/config" 5 | ) 6 | 7 | // 配置文件结构体 8 | type System struct { 9 | Config config.Server `json:"config"` 10 | } 11 | -------------------------------------------------------------------------------- /go/model/system/sys_user_authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type SysUseAuthority struct { 4 | SysUserId uint `gorm:"column:sys_user_id"` 5 | SysAuthorityAuthorityId string `gorm:"column:sys_authority_authority_id"` 6 | } 7 | 8 | func (s *SysUseAuthority) TableName() string { 9 | return "sys_user_authority" 10 | } 11 | -------------------------------------------------------------------------------- /go/packfile/notUsePackFile.go: -------------------------------------------------------------------------------- 1 | //go:build !packfile 2 | // +build !packfile 3 | 4 | package packfile 5 | -------------------------------------------------------------------------------- /go/packfile/usePackFile.go: -------------------------------------------------------------------------------- 1 | //go:build packfile 2 | // +build packfile 3 | 4 | package packfile 5 | 6 | import ( 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | //go:generate go-bindata -o=staticFile.go -pkg=packfile -tags=packfile ../resource/... ../config.yaml 15 | 16 | func writeFile(path string, data []byte) { 17 | // 如果文件夹不存在,预先创建文件夹 18 | if lastSeparator := strings.LastIndex(path, "/"); lastSeparator != -1 { 19 | dirPath := path[:lastSeparator] 20 | if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) { 21 | os.MkdirAll(dirPath, os.ModePerm) 22 | } 23 | } 24 | 25 | // 已存在的文件,不应该覆盖重写,可能在前端更改了配置文件等 26 | if _, err := os.Stat(path); os.IsNotExist(err) { 27 | if err2 := ioutil.WriteFile(path, data, os.ModePerm); err2 != nil { 28 | fmt.Printf("Write file failed: %s\n", path) 29 | } 30 | } else { 31 | fmt.Printf("File exist, skip: %s\n", path) 32 | } 33 | } 34 | 35 | func init() { 36 | for key := range _bindata { 37 | filePath, _ := filepath.Abs(strings.TrimPrefix(key, ".")) 38 | data, err := Asset(key) 39 | if err != nil { 40 | // Asset was not found. 41 | fmt.Printf("Fail to find: %s\n", filePath) 42 | } else { 43 | writeFile(filePath, data) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /go/plugin/email/api/enter.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type ApiGroup struct { 4 | EmailApi 5 | } 6 | 7 | var ApiGroupApp = new(ApiGroup) 8 | -------------------------------------------------------------------------------- /go/plugin/email/config/email.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Email struct { 4 | To string `mapstructure:"to" json:"to" yaml:"to"` // 收件人:多个以英文逗号分隔 例:a@qq.com b@qq.com 正式开发中请把此项目作为参数使用 5 | From string `mapstructure:"from" json:"from" yaml:"from"` // 发件人 你自己要发邮件的邮箱 6 | Host string `mapstructure:"host" json:"host" yaml:"host"` // 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议 7 | Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥 用于登录的密钥 最好不要用邮箱密码 去邮箱smtp申请一个用于登录的密钥 8 | Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称 发件人昵称 通常为自己的邮箱 9 | Port int `mapstructure:"port" json:"port" yaml:"port"` // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465 10 | IsSSL bool `mapstructure:"is-ssl" json:"isSSL" yaml:"is-ssl"` // 是否SSL 是否开启SSL 11 | } 12 | -------------------------------------------------------------------------------- /go/plugin/email/global/gloabl.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import "github.com/nooncall/owls/go/plugin/email/config" 4 | 5 | var GlobalConfig = new(config.Email) 6 | -------------------------------------------------------------------------------- /go/plugin/email/main.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/nooncall/owls/go/plugin/email/global" 6 | "github.com/nooncall/owls/go/plugin/email/router" 7 | ) 8 | 9 | type emailPlugin struct{} 10 | 11 | func CreateEmailPlug(To, From, Host, Secret, Nickname string, Port int, IsSSL bool) *emailPlugin { 12 | global.GlobalConfig.To = To 13 | global.GlobalConfig.From = From 14 | global.GlobalConfig.Host = Host 15 | global.GlobalConfig.Secret = Secret 16 | global.GlobalConfig.Nickname = Nickname 17 | global.GlobalConfig.Port = Port 18 | global.GlobalConfig.IsSSL = IsSSL 19 | return &emailPlugin{} 20 | } 21 | 22 | func (*emailPlugin) Register(group *gin.RouterGroup) { 23 | router.RouterGroupApp.InitEmailRouter(group) 24 | } 25 | 26 | func (*emailPlugin) RouterPath() string { 27 | return "email" 28 | } 29 | -------------------------------------------------------------------------------- /go/plugin/email/model/response/email.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | type Email struct { 4 | To string `json:"to"` // 邮件发送给谁 5 | Subject string `json:"subject"` // 邮件标题 6 | Body string `json:"body"` // 邮件内容 7 | } 8 | -------------------------------------------------------------------------------- /go/plugin/email/router/enter.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | type RouterGroup struct { 4 | EmailRouter 5 | } 6 | 7 | var RouterGroupApp = new(RouterGroup) 8 | -------------------------------------------------------------------------------- /go/plugin/email/router/sys_email.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/nooncall/owls/go/middleware" 6 | "github.com/nooncall/owls/go/plugin/email/api" 7 | ) 8 | 9 | type EmailRouter struct{} 10 | 11 | func (s *EmailRouter) InitEmailRouter(Router *gin.RouterGroup) { 12 | emailRouter := Router.Use(middleware.OperationRecord()) 13 | EmailApi := api.ApiGroupApp.EmailApi.EmailTest 14 | SendEmail := api.ApiGroupApp.EmailApi.SendEmail 15 | { 16 | emailRouter.POST("emailTest", EmailApi) // 发送测试邮件 17 | emailRouter.POST("sendEmail", SendEmail) // 发送邮件 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go/plugin/email/service/enter.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type ServiceGroup struct { 4 | EmailService 5 | } 6 | 7 | var ServiceGroupApp = new(ServiceGroup) 8 | -------------------------------------------------------------------------------- /go/plugin/email/service/sys_email.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/plugin/email/utils" 5 | ) 6 | 7 | type EmailService struct{} 8 | 9 | //@author: [maplepie](https://github.com/maplepie) 10 | //@function: EmailTest 11 | //@description: 发送邮件测试 12 | //@return: err error 13 | 14 | func (e *EmailService) EmailTest() (err error) { 15 | subject := "test" 16 | body := "test" 17 | err = utils.EmailTest(subject, body) 18 | return err 19 | } 20 | 21 | //@author: [maplepie](https://github.com/maplepie) 22 | //@function: EmailTest 23 | //@description: 发送邮件测试 24 | //@return: err error 25 | //@params to string 收件人 26 | //@params subject string 标题(主题) 27 | //@params body string 邮件内容 28 | 29 | func (e *EmailService) SendEmail(to, subject, body string) (err error) { 30 | err = utils.Email(to, subject, body) 31 | return err 32 | } 33 | -------------------------------------------------------------------------------- /go/plugin/example_plugin/main.go: -------------------------------------------------------------------------------- 1 | package example_plugin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | var ExamplePlugin = new(pluginExample) 8 | 9 | type pluginExample struct{} 10 | 11 | func (*pluginExample) Register(group *gin.RouterGroup) { 12 | //如需细分权限 可以在此处use中间件 gva项目包名已改为github模式 13 | //所以整个plugin可以直接独立到外层开启为新的项目 然后用包的形式导入也是可以完整运行的 14 | // 例: 15 | /* 16 | group.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()).GET("hello", func(context *gin.Context) { 17 | context.JSON(200, "hello world") 18 | }) 19 | */ 20 | group.GET("hello", func(context *gin.Context) { 21 | context.JSON(200, "hello world") 22 | }) 23 | } 24 | 25 | func (*pluginExample) RouterPath() string { 26 | return "group" 27 | } 28 | -------------------------------------------------------------------------------- /go/resource/excel/ExcelExport.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/go/resource/excel/ExcelExport.xlsx -------------------------------------------------------------------------------- /go/resource/excel/ExcelImport.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/go/resource/excel/ExcelImport.xlsx -------------------------------------------------------------------------------- /go/resource/excel/ExcelTemplate.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/go/resource/excel/ExcelTemplate.xlsx -------------------------------------------------------------------------------- /go/resource/page/css/parser-example.69e16e51.css: -------------------------------------------------------------------------------- 1 | .test-form[data-v-77b1aafa]{margin:15px auto;width:800px;padding:15px} -------------------------------------------------------------------------------- /go/resource/page/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/go/resource/page/favicon.ico -------------------------------------------------------------------------------- /go/resource/page/js/tinymce-example.5a756246.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["tinymce-example"],{a5aa:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",[n("Tinymce",{attrs:{height:300,placeholder:"在这里输入文字"},model:{value:e.defaultValue,callback:function(t){e.defaultValue=t},expression:"defaultValue"}})],1)},c=[],u=n("31c6"),l={components:{Tinymce:u["a"]},props:{},data:function(){return{defaultValue:"

配置文档参阅:http://tinymce.ax-z.cn

"}},computed:{},watch:{},created:function(){},mounted:function(){},methods:{}},o=l,i=n("5d22"),d=Object(i["a"])(o,a,c,!1,null,null,null);t["default"]=d.exports}}]); -------------------------------------------------------------------------------- /go/resource/page/libs/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/go/resource/page/libs/monaco-editor/vs/base/browser/ui/codicons/codicon/codicon.ttf -------------------------------------------------------------------------------- /go/resource/page/libs/monaco-editor/vs/basic-languages/azcli/azcli.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * monaco-languages version: 2.3.0(57af10ae0184db4e0f7f9a92ff972629c39ccb53) 4 | * Released under the MIT license 5 | * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md 6 | *-----------------------------------------------------------------------------*/ 7 | define("vs/basic-languages/azcli/azcli",["require","exports"],(function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.language=t.conf=void 0,t.conf={comments:{lineComment:"#"}},t.language={defaultToken:"keyword",ignoreCase:!0,tokenPostfix:".azcli",str:/[^#\s]/,tokenizer:{root:[{include:"@comment"},[/\s-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}],[/^-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":{token:"key.identifier",next:"@type"}}}]],type:[{include:"@comment"},[/-+@str*\s*/,{cases:{"@eos":{token:"key.identifier",next:"@popall"},"@default":"key.identifier"}}],[/@str+\s*/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}]],comment:[[/#.*$/,{cases:{"@eos":{token:"comment",next:"@popall"}}}]]}}})); -------------------------------------------------------------------------------- /go/resource/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act 15 | -------------------------------------------------------------------------------- /go/resource/template/readme.txt.tpl: -------------------------------------------------------------------------------- 1 | 代码解压后把fe的api文件内容粘贴进前端api文件夹下并修改为自己想要的名字即可 2 | 3 | 后端代码解压后同理,放到自己想要的 mvc对应路径 并且到 initRouter中注册自动生成的路由 到registerTable中注册自动生成的model 4 | 5 | 项目github:"https://github.com/piexlmax/github.com/nooncall/owls/go" 6 | 7 | 希望大家给个star多多鼓励 8 | -------------------------------------------------------------------------------- /go/resource/template/server/model.go.tpl: -------------------------------------------------------------------------------- 1 | // 自动生成模板{{.StructName}} 2 | package autocode 3 | 4 | import ( 5 | "github.com/nooncall/owls/go/global" 6 | ) 7 | 8 | // {{.StructName}} 结构体 9 | // 如果含有time.Time 请自行import time包 10 | type {{.StructName}} struct { 11 | global.GVA_MODEL {{- range .Fields}} 12 | {{- if ne .FieldType "string" }} 13 | {{.FieldName}} *{{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"` 14 | {{- else }} 15 | {{.FieldName}} {{.FieldType}} `json:"{{.FieldJson}}" form:"{{.FieldJson}}" gorm:"column:{{.ColumnName}};comment:{{.Comment}};{{- if .DataTypeLong -}}size:{{.DataTypeLong}};{{- end -}}"` 16 | {{- end }} {{- end }} 17 | } 18 | 19 | {{ if .TableName }} 20 | // TableName {{.StructName}} 表名 21 | func ({{.StructName}}) TableName() string { 22 | return "{{.TableName}}" 23 | } 24 | {{ end }} 25 | -------------------------------------------------------------------------------- /go/resource/template/server/request.go.tpl: -------------------------------------------------------------------------------- 1 | package request 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/model/autocode" 5 | "github.com/nooncall/owls/go/model/common/request" 6 | ) 7 | 8 | type {{.StructName}}Search struct{ 9 | autocode.{{.StructName}} 10 | request.PageInfo 11 | } -------------------------------------------------------------------------------- /go/router/autocode/auto_code_example.go: -------------------------------------------------------------------------------- 1 | package autocode 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type AutoCodeExampleRouter struct{} 10 | 11 | func (s *AutoCodeExampleRouter) InitSysAutoCodeExampleRouter(Router *gin.RouterGroup) { 12 | autoCodeExampleRouter := Router.Group("autoCodeExample").Use(middleware.OperationRecord()) 13 | autoCodeExampleRouterWithoutRecord := Router.Group("autoCodeExample") 14 | autoCodeExampleApi := v1.ApiGroupApp.AutoCodeApiGroup.AutoCodeExampleApi 15 | { 16 | autoCodeExampleRouter.POST("createSysAutoCodeExample", autoCodeExampleApi.CreateAutoCodeExample) // 新建AutoCodeExample 17 | autoCodeExampleRouter.DELETE("deleteSysAutoCodeExample", autoCodeExampleApi.DeleteAutoCodeExample) // 删除AutoCodeExample 18 | autoCodeExampleRouter.PUT("updateSysAutoCodeExample", autoCodeExampleApi.UpdateAutoCodeExample) // 更新AutoCodeExample 19 | } 20 | { 21 | autoCodeExampleRouterWithoutRecord.GET("findSysAutoCodeExample", autoCodeExampleApi.FindAutoCodeExample) // 根据ID获取AutoCodeExample 22 | autoCodeExampleRouterWithoutRecord.GET("getSysAutoCodeExampleList", autoCodeExampleApi.GetAutoCodeExampleList) // 获取AutoCodeExample列表 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go/router/autocode/enter.go: -------------------------------------------------------------------------------- 1 | package autocode 2 | 3 | type RouterGroup struct { 4 | // Code generated by github.com/nooncall/owls/go Begin; DO NOT EDIT. 5 | AutoCodeExampleRouter 6 | // Code generated by github.com/nooncall/owls/go End; DO NOT EDIT. 7 | } 8 | -------------------------------------------------------------------------------- /go/router/enter.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/router/autocode" 5 | "github.com/nooncall/owls/go/router/example" 6 | "github.com/nooncall/owls/go/router/redis" 7 | "github.com/nooncall/owls/go/router/routers" 8 | "github.com/nooncall/owls/go/router/system" 9 | "github.com/nooncall/owls/go/router/tidb_or_mysql" 10 | ) 11 | 12 | type RouterGroup struct { 13 | System system.RouterGroup 14 | Example example.RouterGroup 15 | Autocode autocode.RouterGroup 16 | Redis redis.RouterGroup 17 | TidbOrMysql tidb_or_mysql.RouterGroup 18 | Routers routers.Routers 19 | } 20 | 21 | var RouterGroupApp = new(RouterGroup) 22 | -------------------------------------------------------------------------------- /go/router/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type RouterGroup struct { 4 | ExcelRouter 5 | CustomerRouter 6 | } 7 | -------------------------------------------------------------------------------- /go/router/example/exa_customer.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type CustomerRouter struct{} 10 | 11 | func (e *CustomerRouter) InitCustomerRouter(Router *gin.RouterGroup) { 12 | customerRouter := Router.Group("customer").Use(middleware.OperationRecord()) 13 | customerRouterWithoutRecord := Router.Group("customer") 14 | exaCustomerApi := v1.ApiGroupApp.ExampleApiGroup.CustomerApi 15 | { 16 | customerRouter.POST("customer", exaCustomerApi.CreateExaCustomer) // 创建客户 17 | customerRouter.PUT("customer", exaCustomerApi.UpdateExaCustomer) // 更新客户 18 | customerRouter.DELETE("customer", exaCustomerApi.DeleteExaCustomer) // 删除客户 19 | } 20 | { 21 | customerRouterWithoutRecord.GET("customer", exaCustomerApi.GetExaCustomer) // 获取单一客户信息 22 | customerRouterWithoutRecord.GET("customerList", exaCustomerApi.GetExaCustomerList) // 获取客户列表 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go/router/example/exa_excel.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type ExcelRouter struct{} 9 | 10 | func (e *ExcelRouter) InitExcelRouter(Router *gin.RouterGroup) { 11 | excelRouter := Router.Group("excel") 12 | exaExcelApi := v1.ApiGroupApp.ExampleApiGroup.ExcelApi 13 | { 14 | excelRouter.POST("importExcel", exaExcelApi.ImportExcel) // 导入Excel 15 | excelRouter.GET("loadExcel", exaExcelApi.LoadExcel) // 加载Excel数据 16 | excelRouter.POST("exportExcel", exaExcelApi.ExportExcel) // 导出Excel 17 | excelRouter.GET("downloadTemplate", exaExcelApi.DownloadTemplate) // 下载模板文件 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go/router/redis/enter.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | type RouterGroup struct { 4 | RedisRouter 5 | } 6 | -------------------------------------------------------------------------------- /go/router/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type RedisRouter struct{} 10 | 11 | func (s *RedisRouter) InitApiRouter(Router *gin.RouterGroup) { 12 | apiRouter := Router.Group("redis").Use(middleware.OperationRecord()) 13 | apiRouterWithoutRecord := Router.Group("redis") 14 | 15 | { 16 | readApi := v1.ApiGroupApp.Redis.ReadApi 17 | 18 | apiRouter.POST("/read", readApi.ReadData) 19 | } 20 | 21 | // task 接口复用,task包含写接口 22 | 23 | // admin, cluster 接口复用 24 | { 25 | ruleApi := v1.ApiGroupApp.Redis.ReadApi 26 | 27 | apiRouterWithoutRecord.GET("/rule/list", ruleApi.ListRule) 28 | apiRouter.PUT("/rule/status", ruleApi.UpdateRuleStatus) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /go/router/routers/auth.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | func (s *Routers) InitAuthRouter(Router *gin.RouterGroup) { 10 | recordRouter := Router.Use(middleware.OperationRecord()) 11 | 12 | { 13 | auth := v1.ApiGroupApp.Auth 14 | 15 | recordRouter.POST("/auth", auth.AddAuth) 16 | recordRouter.DELETE("/auth", auth.DelAuth) 17 | Router.POST("/auth/list", auth.ListAuth) 18 | Router.POST("/data-type/list", auth.ListDataType) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /go/router/routers/enter.go: -------------------------------------------------------------------------------- 1 | // all follow routers will be added here 2 | 3 | package routers 4 | 5 | import "github.com/gin-gonic/gin" 6 | 7 | type Routers struct{} 8 | 9 | func (s *Routers) InitApiRouter(Router *gin.RouterGroup) { 10 | s.InitAuthRouter(Router) 11 | s.InitTaskRouter(Router) 12 | } 13 | 14 | var Router Routers 15 | -------------------------------------------------------------------------------- /go/router/routers/task.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | func (s *Routers) InitTaskRouter(Router *gin.RouterGroup) { 10 | recordRouter := Router.Use(middleware.OperationRecord()) 11 | 12 | { 13 | taskApi := v1.ApiGroupApp.Task 14 | 15 | recordRouter.POST("/task", taskApi.AddTask) 16 | recordRouter.PUT("/task", taskApi.UpdateTask) 17 | Router.GET("/task", taskApi.GetTask) 18 | Router.POST("/task/list", taskApi.ListTask) 19 | Router.POST("/task/review", taskApi.ListReviewTask) 20 | Router.POST("/task/history", taskApi.ListHistoryTask) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /go/router/system/enter.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type RouterGroup struct { 4 | ApiRouter 5 | JwtRouter 6 | SysRouter 7 | BaseRouter 8 | InitRouter 9 | MenuRouter 10 | UserRouter 11 | CasbinRouter 12 | AutoCodeRouter 13 | AuthorityRouter 14 | DictionaryRouter 15 | OperationRecordRouter 16 | DictionaryDetailRouter 17 | AuthorityBtnRouter 18 | } 19 | -------------------------------------------------------------------------------- /go/router/system/sys_api.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type ApiRouter struct{} 10 | 11 | func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup) { 12 | apiRouter := Router.Group("api").Use(middleware.OperationRecord()) 13 | apiRouterWithoutRecord := Router.Group("api") 14 | apiRouterApi := v1.ApiGroupApp.SystemApiGroup.SystemApiApi 15 | { 16 | apiRouter.POST("createApi", apiRouterApi.CreateApi) // 创建Api 17 | apiRouter.POST("deleteApi", apiRouterApi.DeleteApi) // 删除Api 18 | apiRouter.POST("getApiById", apiRouterApi.GetApiById) // 获取单条Api消息 19 | apiRouter.POST("updateApi", apiRouterApi.UpdateApi) // 更新api 20 | apiRouter.DELETE("deleteApisByIds", apiRouterApi.DeleteApisByIds) // 删除选中api 21 | } 22 | { 23 | apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api 24 | apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /go/router/system/sys_authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type AuthorityRouter struct{} 10 | 11 | func (s *AuthorityRouter) InitAuthorityRouter(Router *gin.RouterGroup) { 12 | authorityRouter := Router.Group("authority").Use(middleware.OperationRecord()) 13 | authorityRouterWithoutRecord := Router.Group("authority") 14 | authorityApi := v1.ApiGroupApp.SystemApiGroup.AuthorityApi 15 | { 16 | authorityRouter.POST("createAuthority", authorityApi.CreateAuthority) // 创建角色 17 | authorityRouter.POST("deleteAuthority", authorityApi.DeleteAuthority) // 删除角色 18 | authorityRouter.PUT("updateAuthority", authorityApi.UpdateAuthority) // 更新角色 19 | authorityRouter.POST("copyAuthority", authorityApi.CopyAuthority) // 拷贝角色 20 | authorityRouter.POST("setDataAuthority", authorityApi.SetDataAuthority) // 设置角色资源权限 21 | } 22 | { 23 | authorityRouterWithoutRecord.POST("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /go/router/system/sys_authority_btn.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type AuthorityBtnRouter struct{} 9 | 10 | func (s *AuthorityBtnRouter) InitAuthorityBtnRouterRouter(Router *gin.RouterGroup) { 11 | //authorityRouter := Router.Group("authorityBtn").Use(middleware.OperationRecord()) 12 | authorityRouterWithoutRecord := Router.Group("authorityBtn") 13 | authorityBtnApi := v1.ApiGroupApp.SystemApiGroup.AuthorityBtnApi 14 | { 15 | authorityRouterWithoutRecord.POST("getAuthorityBtn", authorityBtnApi.GetAuthorityBtn) 16 | authorityRouterWithoutRecord.POST("setAuthorityBtn", authorityBtnApi.SetAuthorityBtn) 17 | authorityRouterWithoutRecord.POST("canRemoveAuthorityBtn", authorityBtnApi.CanRemoveAuthorityBtn) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go/router/system/sys_auto_code.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type AutoCodeRouter struct{} 9 | 10 | func (s *AutoCodeRouter) InitAutoCodeRouter(Router *gin.RouterGroup) { 11 | autoCodeRouter := Router.Group("autoCode") 12 | autoCodeApi := v1.ApiGroupApp.SystemApiGroup.AutoCodeApi 13 | { 14 | autoCodeRouter.GET("getDB", autoCodeApi.GetDB) // 获取数据库 15 | autoCodeRouter.GET("getTables", autoCodeApi.GetTables) // 获取对应数据库的表 16 | autoCodeRouter.GET("getColumn", autoCodeApi.GetColumn) // 获取指定表所有字段信息 17 | autoCodeRouter.POST("preview", autoCodeApi.PreviewTemp) // 获取自动创建代码预览 18 | autoCodeRouter.POST("createTemp", autoCodeApi.CreateTemp) // 创建自动化代码 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /go/router/system/sys_auto_code_history.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type AutoCodeHistoryRouter struct{} 9 | 10 | func (s *AutoCodeRouter) InitAutoCodeHistoryRouter(Router *gin.RouterGroup) { 11 | autoCodeHistoryRouter := Router.Group("autoCode") 12 | autoCodeHistoryApi := v1.ApiGroupApp.SystemApiGroup.AutoCodeHistoryApi 13 | { 14 | autoCodeHistoryRouter.POST("getMeta", autoCodeHistoryApi.First) // 根据id获取meta信息 15 | autoCodeHistoryRouter.POST("rollback", autoCodeHistoryApi.RollBack) // 回滚 16 | autoCodeHistoryRouter.POST("delSysHistory", autoCodeHistoryApi.Delete) // 删除回滚记录 17 | autoCodeHistoryRouter.POST("getSysHistory", autoCodeHistoryApi.GetList) // 获取回滚记录分页 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /go/router/system/sys_base.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type BaseRouter struct{} 9 | 10 | func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { 11 | baseRouter := Router.Group("base") 12 | baseApi := v1.ApiGroupApp.SystemApiGroup.BaseApi 13 | { 14 | baseRouter.GET("login/model", baseApi.LoginModel) 15 | baseRouter.POST("register", baseApi.Register) 16 | baseRouter.POST("login", baseApi.Login) 17 | baseRouter.POST("captcha", baseApi.Captcha) 18 | } 19 | return baseRouter 20 | } 21 | -------------------------------------------------------------------------------- /go/router/system/sys_casbin.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type CasbinRouter struct{} 10 | 11 | func (s *CasbinRouter) InitCasbinRouter(Router *gin.RouterGroup) { 12 | casbinRouter := Router.Group("casbin").Use(middleware.OperationRecord()) 13 | casbinRouterWithoutRecord := Router.Group("casbin") 14 | casbinApi := v1.ApiGroupApp.SystemApiGroup.CasbinApi 15 | { 16 | casbinRouter.POST("updateCasbin", casbinApi.UpdateCasbin) 17 | } 18 | { 19 | casbinRouterWithoutRecord.POST("getPolicyPathByAuthorityId", casbinApi.GetPolicyPathByAuthorityId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/router/system/sys_dictionary.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type DictionaryRouter struct{} 10 | 11 | func (s *DictionaryRouter) InitSysDictionaryRouter(Router *gin.RouterGroup) { 12 | sysDictionaryRouter := Router.Group("sysDictionary").Use(middleware.OperationRecord()) 13 | sysDictionaryRouterWithoutRecord := Router.Group("sysDictionary") 14 | sysDictionaryApi := v1.ApiGroupApp.SystemApiGroup.DictionaryApi 15 | { 16 | sysDictionaryRouter.POST("createSysDictionary", sysDictionaryApi.CreateSysDictionary) // 新建SysDictionary 17 | sysDictionaryRouter.DELETE("deleteSysDictionary", sysDictionaryApi.DeleteSysDictionary) // 删除SysDictionary 18 | sysDictionaryRouter.PUT("updateSysDictionary", sysDictionaryApi.UpdateSysDictionary) // 更新SysDictionary 19 | } 20 | { 21 | sysDictionaryRouterWithoutRecord.GET("findSysDictionary", sysDictionaryApi.FindSysDictionary) // 根据ID获取SysDictionary 22 | sysDictionaryRouterWithoutRecord.GET("getSysDictionaryList", sysDictionaryApi.GetSysDictionaryList) // 获取SysDictionary列表 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go/router/system/sys_dictionary_detail.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type DictionaryDetailRouter struct{} 10 | 11 | func (s *DictionaryDetailRouter) InitSysDictionaryDetailRouter(Router *gin.RouterGroup) { 12 | dictionaryDetailRouter := Router.Group("sysDictionaryDetail").Use(middleware.OperationRecord()) 13 | dictionaryDetailRouterWithoutRecord := Router.Group("sysDictionaryDetail") 14 | sysDictionaryDetailApi := v1.ApiGroupApp.SystemApiGroup.DictionaryDetailApi 15 | { 16 | dictionaryDetailRouter.POST("createSysDictionaryDetail", sysDictionaryDetailApi.CreateSysDictionaryDetail) // 新建SysDictionaryDetail 17 | dictionaryDetailRouter.DELETE("deleteSysDictionaryDetail", sysDictionaryDetailApi.DeleteSysDictionaryDetail) // 删除SysDictionaryDetail 18 | dictionaryDetailRouter.PUT("updateSysDictionaryDetail", sysDictionaryDetailApi.UpdateSysDictionaryDetail) // 更新SysDictionaryDetail 19 | } 20 | { 21 | dictionaryDetailRouterWithoutRecord.GET("findSysDictionaryDetail", sysDictionaryDetailApi.FindSysDictionaryDetail) // 根据ID获取SysDictionaryDetail 22 | dictionaryDetailRouterWithoutRecord.GET("getSysDictionaryDetailList", sysDictionaryDetailApi.GetSysDictionaryDetailList) // 获取SysDictionaryDetail列表 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /go/router/system/sys_initdb.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type InitRouter struct{} 9 | 10 | func (s *InitRouter) InitInitRouter(Router *gin.RouterGroup) { 11 | initRouter := Router.Group("init") 12 | dbApi := v1.ApiGroupApp.SystemApiGroup.DBApi 13 | { 14 | initRouter.POST("initdb", dbApi.InitDB) // 创建Api 15 | initRouter.POST("checkdb", dbApi.CheckDB) // 创建Api 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /go/router/system/sys_jwt.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type JwtRouter struct{} 9 | 10 | func (s *JwtRouter) InitJwtRouter(Router *gin.RouterGroup) { 11 | jwtRouter := Router.Group("jwt") 12 | jwtApi := v1.ApiGroupApp.SystemApiGroup.JwtApi 13 | { 14 | jwtRouter.POST("jsonInBlacklist", jwtApi.JsonInBlacklist) // jwt加入黑名单 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /go/router/system/sys_operation_record.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | ) 7 | 8 | type OperationRecordRouter struct{} 9 | 10 | func (s *OperationRecordRouter) InitSysOperationRecordRouter(Router *gin.RouterGroup) { 11 | operationRecordRouter := Router.Group("sysOperationRecord") 12 | authorityMenuApi := v1.ApiGroupApp.SystemApiGroup.OperationRecordApi 13 | { 14 | operationRecordRouter.POST("createSysOperationRecord", authorityMenuApi.CreateSysOperationRecord) // 新建SysOperationRecord 15 | operationRecordRouter.DELETE("deleteSysOperationRecord", authorityMenuApi.DeleteSysOperationRecord) // 删除SysOperationRecord 16 | operationRecordRouter.DELETE("deleteSysOperationRecordByIds", authorityMenuApi.DeleteSysOperationRecordByIds) // 批量删除SysOperationRecord 17 | operationRecordRouter.GET("findSysOperationRecord", authorityMenuApi.FindSysOperationRecord) // 根据ID获取SysOperationRecord 18 | operationRecordRouter.GET("getSysOperationRecordList", authorityMenuApi.GetSysOperationRecordList) // 获取SysOperationRecord列表 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /go/router/system/sys_system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type SysRouter struct{} 10 | 11 | func (s *SysRouter) InitSystemRouter(Router *gin.RouterGroup) { 12 | sysRouter := Router.Group("system").Use(middleware.OperationRecord()) 13 | systemApi := v1.ApiGroupApp.SystemApiGroup.SystemApi 14 | { 15 | sysRouter.POST("getSystemConfig", systemApi.GetSystemConfig) // 获取配置文件内容 16 | sysRouter.POST("setSystemConfig", systemApi.SetSystemConfig) // 设置配置文件内容 17 | sysRouter.POST("getServerInfo", systemApi.GetServerInfo) // 获取服务器信息 18 | sysRouter.POST("reloadSystem", systemApi.ReloadSystem) // 重启服务 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /go/router/system/sys_user.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | v1 "github.com/nooncall/owls/go/api/v1" 6 | "github.com/nooncall/owls/go/middleware" 7 | ) 8 | 9 | type UserRouter struct{} 10 | 11 | func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { 12 | userRouter := Router.Group("user").Use(middleware.OperationRecord()) 13 | userRouterWithoutRecord := Router.Group("user") 14 | baseApi := v1.ApiGroupApp.SystemApiGroup.BaseApi 15 | { 16 | userRouter.POST("register", baseApi.Register) // 管理员注册账号 17 | userRouter.POST("changePassword", baseApi.ChangePassword) // 用户修改密码 18 | userRouter.POST("setUserAuthority", baseApi.SetUserAuthority) // 设置用户权限 19 | userRouter.DELETE("deleteUser", baseApi.DeleteUser) // 删除用户 20 | userRouter.PUT("setUserInfo", baseApi.SetUserInfo) // 设置用户信息 21 | userRouter.PUT("setSelfInfo", baseApi.SetSelfInfo) // 设置自身信息 22 | userRouter.POST("setUserAuthorities", baseApi.SetUserAuthorities) // 设置用户权限组 23 | userRouter.POST("resetPassword", baseApi.ResetPassword) // 设置用户权限组 24 | } 25 | { 26 | userRouterWithoutRecord.POST("getUserList", baseApi.GetUserList) // 分页获取用户列表 27 | userRouterWithoutRecord.GET("getUserInfo", baseApi.GetUserInfo) // 获取自身信息 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /go/router/tidb_or_mysql/enter.go: -------------------------------------------------------------------------------- 1 | package tidb_or_mysql 2 | 3 | type RouterGroup struct { 4 | TidbOrMysqlRouter 5 | } 6 | -------------------------------------------------------------------------------- /go/service/auth/auth/auth_task.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/nooncall/owls/go/service/task" 7 | ) 8 | 9 | func (a Auth) AddTask(ctx context.Context, cluster string, db int, parentTaskID int64) (int64, bool, error) { 10 | // a.ParentTaskID = parentTaskID 11 | count, err := authDao.AddAuth(&a) 12 | return count, true, err 13 | } 14 | 15 | // give auth by set status 16 | func (a Auth) ExecTask(ctx context.Context, startSubTaskId, taskId int64, cluster, db string) error { 17 | a.Status = StatusPass 18 | return authDao.UpdateAuth(&a) 19 | } 20 | 21 | func (a Auth) UpdateTask(action string) error { 22 | switch action { 23 | case task.ActionCancel: 24 | a.Status = StatusCancelApply 25 | case task.ActionApproval: 26 | a.Status = StatusPass 27 | case task.Reject: 28 | a.Status = StatusReject 29 | case task.ActionUpdate: 30 | } 31 | return authDao.UpdateAuth(&a) 32 | } 33 | 34 | func (a Auth) ListTask(parentTaskID int64) (interface{}, error) { 35 | panic("implement me") 36 | } 37 | 38 | func (a Auth) GetTask(id int64) (interface{}, error) { 39 | return authDao.GetAuth(id) 40 | } 41 | 42 | func (a Auth) GetExecWaitTask() ([]interface{}, int64, error) { 43 | panic("implement me") 44 | } 45 | -------------------------------------------------------------------------------- /go/service/auth/auth/filter.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/nooncall/owls/go/utils/logger" 4 | 5 | func FilterDB(dbs []string, userId uint, dataType, cluster string) []string { 6 | f := "FilterDB()-->: " 7 | auths, err := authDao.ListAuthForFilter(userId, StatusPass, DB) 8 | if err != nil { 9 | logger.Errorf("%s get auth err: %v", f, err) 10 | return dbs 11 | } 12 | 13 | var result []string 14 | for _, db := range dbs { 15 | for _, auth := range auths { 16 | if auth.Cluster == cluster && auth.DB == db && auth.DataType == dataType { 17 | result = append(result, db) 18 | break 19 | } 20 | } 21 | } 22 | 23 | return result 24 | } 25 | 26 | func FilterCluster(clusters []string, userId uint, dataType string) []string { 27 | f := "FilterCluster()-->: " 28 | auths, err := authDao.ListAuthForFilter(userId, StatusPass, DB) 29 | if err != nil { 30 | logger.Errorf("%s get auth err: %v", f, err) 31 | return clusters 32 | } 33 | 34 | var result []string 35 | for _, cluster := range clusters { 36 | for _, auth := range auths { 37 | if auth.Cluster == cluster && auth.DataType == dataType { 38 | result = append(result, cluster) 39 | break 40 | } 41 | } 42 | } 43 | 44 | return result 45 | } 46 | -------------------------------------------------------------------------------- /go/service/auth/auth/type.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | const ( 4 | DB = "db" 5 | ) 6 | 7 | const ( 8 | StatusCancelApply = "cancel_apply" 9 | StatusPass = "paas" 10 | StatusReject = "reject" 11 | ) 12 | 13 | func Types() []string { 14 | return []string{DB} 15 | } 16 | -------------------------------------------------------------------------------- /go/service/autocode/enter.go: -------------------------------------------------------------------------------- 1 | package autocode 2 | 3 | type ServiceGroup struct { 4 | // Code generated by github.com/nooncall/owls/go Begin; DO NOT EDIT. 5 | AutoCodeExampleService 6 | // Code generated by github.com/nooncall/owls/go End; DO NOT EDIT. 7 | } 8 | -------------------------------------------------------------------------------- /go/service/enter.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/service/autocode" 5 | "github.com/nooncall/owls/go/service/example" 6 | "github.com/nooncall/owls/go/service/system" 7 | ) 8 | 9 | type ServiceGroup struct { 10 | SystemServiceGroup system.ServiceGroup 11 | ExampleServiceGroup example.ServiceGroup 12 | AutoCodeServiceGroup autocode.ServiceGroup 13 | } 14 | 15 | var ServiceGroupApp = new(ServiceGroup) 16 | -------------------------------------------------------------------------------- /go/service/example/enter.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | type ServiceGroup struct { 4 | ExcelService 5 | CustomerService 6 | FileUploadAndDownloadService 7 | } 8 | -------------------------------------------------------------------------------- /go/service/redis/connection.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/go-redis/redis/v8" 8 | "github.com/nooncall/owls/go/service/tidb_or_mysql/db_info" 9 | "github.com/nooncall/owls/go/utils/logger" 10 | ) 11 | 12 | func NewRedisCli(clusterName string, database int) (r *redis.Client, err error) { 13 | cluster, err := db_info.GetClusterByName(clusterName) 14 | if err != nil { 15 | return nil, fmt.Errorf("get cluster info err: %s", err.Error()) 16 | } 17 | 18 | // new,and init 19 | redisCli := redis.NewClient(&redis.Options{ 20 | Addr: cluster.Addr, 21 | Password: cluster.Pwd, // redis的认证密码 22 | DB: database, // 连接的database库 23 | IdleTimeout: 300, // 默认Idle超时时间 24 | PoolSize: 10, // 连接池 25 | }) 26 | 27 | res, err := redisCli.Ping(context.Background()).Result() 28 | if err != nil { 29 | logger.Errorf("Connect Failed! Err: %v", err) 30 | return nil, err 31 | } 32 | logger.Errorf("Connect Successful! Ping => %v", res) 33 | return redisCli, nil 34 | } 35 | -------------------------------------------------------------------------------- /go/service/redis/init.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | _ "github.com/go-sql-driver/mysql" 5 | "gorm.io/gorm" 6 | 7 | "github.com/nooncall/owls/go/global" 8 | ) 9 | 10 | var DB *gorm.DB 11 | 12 | func GetDB() *gorm.DB { 13 | // todo, refactor to config~~ 14 | return global.GVA_DB.Debug() 15 | } 16 | -------------------------------------------------------------------------------- /go/service/redis/query.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Params struct { 8 | Cmd, Cluster string 9 | DB int 10 | } 11 | 12 | // ExecQuery ... 13 | func ExecQuery(ctx context.Context, req *Params) interface{} { 14 | resp, err := ExecReadTask(ctx, req) 15 | if err != nil { 16 | return err.Error() 17 | 18 | } 19 | return resp 20 | } 21 | -------------------------------------------------------------------------------- /go/service/redis/redis_dao/white_list_dao.go: -------------------------------------------------------------------------------- 1 | package redis_dao 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/nooncall/owls/go/service/redis" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type WhiteListDao struct{} 11 | 12 | func NewWhiteListDao() redis.WhiteListDao { 13 | return &WhiteListDao{} 14 | } 15 | 16 | func (dao *WhiteListDao) AddWhiteList(ctx context.Context, db *gorm.DB, WhiteList *redis.WhiteList) (int64, error) { 17 | result := db.Create(WhiteList) 18 | if result.Error != nil { 19 | return 0, result.Error 20 | } 21 | return WhiteList.ID, nil 22 | } 23 | 24 | func (dao *WhiteListDao) DelWhiteList(ctx context.Context, db *gorm.DB, id int64) error { 25 | result := db.Delete(&redis.WhiteList{}, id) 26 | return result.Error 27 | } 28 | 29 | func (dao *WhiteListDao) ListWhiteList(ctx context.Context, db *gorm.DB, cmdType string) ([]redis.WhiteList, error) { 30 | var whiteLists []redis.WhiteList 31 | result := db.Where("cmd_type = ?", cmdType).Find(&whiteLists) 32 | if result.Error != nil { 33 | return nil, result.Error 34 | } 35 | return whiteLists, nil 36 | } 37 | -------------------------------------------------------------------------------- /go/service/system/enter.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | type ServiceGroup struct { 4 | JwtService 5 | ApiService 6 | MenuService 7 | UserService 8 | CasbinService 9 | InitDBService 10 | AutoCodeService 11 | BaseMenuService 12 | AuthorityService 13 | DictionaryService 14 | SystemConfigService 15 | AutoCodeHistoryService 16 | OperationRecordService 17 | DictionaryDetailService 18 | AuthorityBtnService 19 | } 20 | -------------------------------------------------------------------------------- /go/service/system/sys_auto_code_interface.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/system/response" 6 | ) 7 | 8 | type Database interface { 9 | GetDB() (data []response.Db, err error) 10 | GetTables(dbName string) (data []response.Table, err error) 11 | GetColumn(tableName string, dbName string) (data []response.Column, err error) 12 | } 13 | 14 | func (autoCodeService *AutoCodeService) Database() Database { 15 | switch global.GVA_CONFIG.System.DbType { 16 | case "mysql": 17 | return AutoCodeMysql 18 | case "pgsql": 19 | return AutoCodePgsql 20 | default: 21 | return AutoCodeMysql 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /go/service/task/status.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | const ( 4 | WaitApproval = "wait_approval" 5 | Pass = "pass" 6 | Failed = "failed" 7 | Cancel = "cancel" 8 | Reject = "reject" 9 | ) 10 | 11 | func ExecStatus() []string { 12 | return []string{WaitApproval} 13 | } 14 | 15 | func SubmitStatus() []string { 16 | return []string{WaitApproval, Reject} 17 | } 18 | 19 | func HistoryStatus() []string { 20 | return []string{Pass, Failed, Cancel} 21 | } 22 | -------------------------------------------------------------------------------- /go/service/task/type.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | const ( 4 | Auth = "auth" 5 | Redis = "redis" 6 | 7 | ActionCancel = "cancel" 8 | ActionUpdate = "update" 9 | ActionApproval = "approval" 10 | ActionResubmit = "resubmit" 11 | ActionReject = "reject" 12 | ActionExec = "exec" 13 | ) 14 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/auth/admin.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/service/tidb_or_mysql" 5 | "github.com/nooncall/owls/go/service/tidb_or_mysql/admin" 6 | ) 7 | 8 | type AdminAuthToolImpl struct { 9 | } 10 | 11 | var AdminAuthService AdminAuthToolImpl 12 | 13 | func (AdminAuthToolImpl) GetReviewer(userName string) (reviewerName string, err error) { 14 | admins, _, err := admin.ListAdmin(&tidb_or_mysql.Pagination{Limit: 10}) 15 | if err != nil { 16 | return "", err 17 | } 18 | 19 | var resp string 20 | for i, v := range admins { 21 | if i == 0 { 22 | resp += v.Username 23 | } else { 24 | resp += "," + v.Username 25 | } 26 | } 27 | return resp, nil 28 | } 29 | 30 | func (AdminAuthToolImpl) IsDba(userName string) (isDba bool, err error) { 31 | return true, nil 32 | // return admin.IsAdmin(userName) 33 | } 34 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/auth/auth_mock.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type MockAuth struct { 4 | } 5 | 6 | var MockAuthService MockAuth 7 | 8 | func (MockAuth) GetReviewer(userName string) (reviewerName string, err error) { 9 | return "nobody", nil 10 | } 11 | 12 | func (MockAuth) IsDba(userName string) (isDba bool, err error) { 13 | return true, nil 14 | } 15 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/auth/config_auth_tool.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | 7 | "github.com/nooncall/owls/go/global" 8 | ) 9 | 10 | type ConfAuthToolImpl struct { 11 | } 12 | 13 | var ConfAuthService ConfAuthToolImpl 14 | 15 | func (ConfAuthToolImpl) GetReviewer(userName string) (reviewerName string, err error) { 16 | return strings.Join(global.GVA_CONFIG.DBFilter.Reviewers, ","), nil 17 | } 18 | 19 | func (ConfAuthToolImpl) IsDba(userName string) (isDba bool, err error) { 20 | if len(global.GVA_CONFIG.DBFilter.Reviewers) < 1 { 21 | return false, errors.New("dba members not config") 22 | } 23 | for _, v := range global.GVA_CONFIG.DBFilter.Reviewers { 24 | if v == userName { 25 | return true, nil 26 | } 27 | } 28 | 29 | return false, nil 30 | } 31 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/auth/login_check.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type LoginChecker interface { 4 | Login(userName, pwd string) error 5 | } 6 | 7 | var loginService LoginChecker 8 | 9 | func SetLoginService(impl LoginChecker) { 10 | loginService = impl 11 | } 12 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/auth/login_check/ldap.go: -------------------------------------------------------------------------------- 1 | package login_check 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | 7 | ldap "github.com/jtblin/go-ldap-client" 8 | "github.com/nooncall/owls/go/global" 9 | "github.com/nooncall/owls/go/utils/logger" 10 | ) 11 | 12 | type LoginServiceImpl struct { 13 | } 14 | 15 | var LoginService LoginServiceImpl 16 | 17 | func (LoginServiceImpl) Login(userName, pwd string) error { 18 | ok, _, err := getLdapConn().Authenticate(userName, pwd) 19 | if err != nil { 20 | return fmt.Errorf("authenticating user err %s: %+v ", userName, err) 21 | } 22 | if !ok { 23 | return fmt.Errorf("authenticating failed for user %s", "username") 24 | } 25 | logger.Infof("user: %s Login", userName) 26 | return nil 27 | } 28 | 29 | var ldapConn *ldap.LDAPClient 30 | 31 | func getLdapConn() *ldap.LDAPClient { 32 | once.Do(setLdapConn) 33 | return ldapConn 34 | } 35 | 36 | var once sync.Once 37 | 38 | func setLdapConn() { 39 | login := global.GVA_CONFIG.Login 40 | ldapConn = &ldap.LDAPClient{ 41 | Base: login.Ldap.BaseDn, 42 | Host: login.Ldap.Host, 43 | Port: login.Ldap.Port, 44 | UseSSL: login.Ldap.UseSll, 45 | BindDN: login.Ldap.BaseDn, 46 | BindPassword: login.Ldap.BindPwd, 47 | UserFilter: "(uid=%s)", 48 | GroupFilter: "(memberUid=%s)", 49 | Attributes: []string{"givenName", "sn", "mail", "uid"}, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/common.go: -------------------------------------------------------------------------------- 1 | package tidb_or_mysql 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Pagination struct { 8 | Offset int `json:"offset"` 9 | Limit int `json:"limit"` 10 | Key string `json:"key"` 11 | 12 | Operator string `json:"operator"` 13 | } 14 | 15 | type ClockInf interface { 16 | Now() time.Time 17 | NowUnix() int64 18 | } 19 | 20 | var Clock ClockInf 21 | 22 | func SetClock(impl ClockInf) { 23 | Clock = impl 24 | } 25 | 26 | type RealClock struct{} 27 | 28 | func (RealClock) Now() time.Time { return time.Now() } 29 | func (RealClock) NowUnix() int64 { return time.Now().Unix() } 30 | func (RealClock) After(d time.Duration) <-chan time.Time { return time.After(d) } 31 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/dao/backup.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/service/tidb_or_mysql/task" 5 | ) 6 | 7 | type BackupImpl struct { 8 | } 9 | 10 | var BackupDAO BackupImpl 11 | 12 | func (BackupImpl) AddBackup(backup *task.OwlBackup) (int64, error) { 13 | err := GetDB().Create(backup).Error 14 | return backup.ID, err 15 | } 16 | 17 | func (BackupImpl) UpdateBackup(backup *task.OwlBackup) error { 18 | return GetDB().Model(backup).Where("id = ?", backup.ID).Updates(backup).Error 19 | } 20 | 21 | func (BackupImpl) GetBackupInfoById(id int64) (*task.OwlBackup, error) { 22 | var backup task.OwlBackup 23 | return &backup, GetDB().Where("id = ?", id).First(&backup).Error 24 | } 25 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/dao/init.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | _ "github.com/go-sql-driver/mysql" 5 | "gorm.io/gorm" 6 | 7 | "github.com/nooncall/owls/go/global" 8 | ) 9 | 10 | var DB *gorm.DB 11 | 12 | func GetDB() *gorm.DB { 13 | // todo, refactor to config~~ 14 | return global.GVA_DB.Debug() 15 | } 16 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/dao/rule.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/service/tidb_or_mysql/checker" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type RuleDaoImpl struct { 9 | } 10 | 11 | var Rule RuleDaoImpl 12 | 13 | func (RuleDaoImpl) ListAllStatus() ([]checker.OwlRuleStatus, error) { 14 | var ruleStatus []checker.OwlRuleStatus 15 | return ruleStatus, GetDB().Find(&ruleStatus).Error 16 | } 17 | 18 | func (RuleDaoImpl) UpdateRuleStatus(ruleStatus *checker.OwlRuleStatus) error { 19 | err := GetDB().Where("name = ?", ruleStatus.Name).First(&checker.OwlRuleStatus{}).Error 20 | if err == gorm.ErrRecordNotFound { 21 | return GetDB().Create(ruleStatus).Error 22 | } 23 | if err != nil { 24 | return err 25 | } 26 | 27 | return GetDB().Model(ruleStatus).Where("name = ?", ruleStatus.Name).Updates(ruleStatus).Error 28 | } 29 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/dao/sub_task.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import "github.com/nooncall/owls/go/service/tidb_or_mysql/task" 4 | 5 | type SubTaskDaoImpl struct { 6 | } 7 | 8 | var SubTask SubTaskDaoImpl 9 | 10 | func (SubTaskDaoImpl) UpdateItem(item *task.OwlExecItem) error { 11 | return GetDB().Model(item).Where("id = ?", item.ID).Updates(item).Error 12 | } 13 | 14 | func (SubTaskDaoImpl) DelItem(item *task.OwlExecItem) error { 15 | return GetDB().Model(item).Where("id = ?", item.ID).Delete(item).Error 16 | } 17 | 18 | func (SubTaskDaoImpl) UpdateItemByBackupId(item *task.OwlExecItem) error { 19 | return GetDB().Model(item).Where("backup_id = ?", item.BackupID).Updates(item).Error 20 | } 21 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/sql_util/amend_sql.go: -------------------------------------------------------------------------------- 1 | package sql_util 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/nooncall/owls/go/utils/logger" 8 | "github.com/pingcap/parser/ast" 9 | ) 10 | 11 | type readSql struct { 12 | sql string 13 | } 14 | 15 | func NewReadSql(sql string) *readSql { 16 | return &readSql{sql} 17 | } 18 | 19 | func (readSql *readSql) String() string { 20 | return readSql.sql 21 | } 22 | 23 | func (readSql *readSql) appendLimit() { 24 | readSql.sql = fmt.Sprintf("%s limit 10", readSql.sql) 25 | } 26 | 27 | func (readSql *readSql) Trim() { 28 | readSql.sql = strings.TrimSpace(readSql.sql) 29 | readSql.sql = strings.TrimRight(readSql.sql, ";") 30 | } 31 | 32 | func (readSql *readSql) SetLimitResult() string { 33 | f := "AddLimitToSelect-->: " 34 | 35 | readSql.Trim() 36 | stmtNodes, _, err := getParser().Parse(readSql.sql, "", "") 37 | if err != nil { 38 | logger.Errorf("%sparse sql err: %v", f, err) 39 | return readSql.sql 40 | } 41 | 42 | for _, tiStmt := range stmtNodes { 43 | switch node := tiStmt.(type) { 44 | case *ast.SelectStmt: 45 | if node.Limit == nil { 46 | readSql.appendLimit() 47 | return readSql.String() 48 | } 49 | default: 50 | return readSql.String() 51 | } 52 | } 53 | 54 | return readSql.String() 55 | } 56 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/sql_util/test/amend_sql_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/service/tidb_or_mysql/sql_util" 5 | "testing" 6 | ) 7 | 8 | func TestAddLimit(t *testing.T) { 9 | testData := []struct { 10 | source, expect string 11 | }{ 12 | { 13 | "select * from cluster; ", 14 | "select * from cluster limit 10", 15 | }, 16 | { 17 | "select * from cluster limit 2", 18 | "select * from cluster limit 2", 19 | }, 20 | { 21 | "select * from cluster", 22 | "select * from cluster limit 10", 23 | }, 24 | 25 | { 26 | "select * from owl_clusters where id in (select id from owl_clusters limit 2)", 27 | "select * from owl_clusters where id in (select id from owl_clusters limit 2) limit 10", 28 | }, 29 | { 30 | "select * from owl_clusters where id in (select id from owl_clusters)", 31 | "select * from owl_clusters where id in (select id from owl_clusters) limit 10", 32 | }, 33 | { 34 | "select * from owl_clusters where id in (select id from owl_clusters) limit 5", 35 | "select * from owl_clusters where id in (select id from owl_clusters) limit 5", 36 | }, 37 | } 38 | 39 | for i, v := range testData { 40 | if v.expect != sql_util.NewReadSql(v.source).SetLimitResult() { 41 | t.Log("failed at :", testData[i]) 42 | t.FailNow() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/task/checker.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | type sqlChecker interface { 4 | SqlCheck(sql, charset, collation string, info *DBInfo) (pass bool, suggestion string, affectRow int) 5 | ListRules() interface{} 6 | } 7 | 8 | var checker sqlChecker 9 | 10 | func SetChecker(impl sqlChecker) { 11 | checker = impl 12 | } 13 | -------------------------------------------------------------------------------- /go/service/tidb_or_mysql/task/db.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "database/sql" 5 | 6 | "github.com/nooncall/owls/go/utils/logger" 7 | ) 8 | 9 | type DBInfo struct { 10 | DB *sql.DB 11 | DefaultDB *sql.DB 12 | DBName string 13 | } 14 | 15 | func (db *DBInfo) CloseConn() { 16 | if err := db.DB.Close(); err != nil { 17 | logger.Errorf("close db conn err: %s", err.Error()) 18 | } 19 | if err := db.DefaultDB.Close(); err != nil { 20 | logger.Errorf("close default db conn err: %s", err.Error()) 21 | } 22 | } 23 | 24 | type dbTools interface { 25 | GetDBConn(dbName, cluster string) (*DBInfo, error) 26 | } 27 | 28 | var dbTool dbTools 29 | 30 | func SetDbTools(impl dbTools) { 31 | dbTool = impl 32 | } 33 | 34 | type OwlAccount struct { 35 | ID uint64 `json:"id" gorm:"column:id"` 36 | Username string `json:"username" gorm:"column:username"` 37 | Passwd string `json:"passwd" gorm:"column:passwd"` 38 | Dbtype int64 `json:"dbtype" gorm:"column:dbtype"` 39 | } 40 | -------------------------------------------------------------------------------- /go/source/example/file_mysql.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/example" 6 | "github.com/nooncall/owls/go/model/system" 7 | "github.com/pkg/errors" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | var FileMysql = new(fileMysql) 12 | 13 | type fileMysql struct{} 14 | 15 | func (f *fileMysql) TableName() string { 16 | return "exa_file_upload_and_downloads" 17 | } 18 | 19 | func (f *fileMysql) Initialize(initData *system.InitDBData) error { 20 | entities := []example.ExaFileUploadAndDownload{ 21 | {Name: "10.png", Url: "https://qmplusimg.henrongyi.top/gvalogo.png", Tag: "png", Key: "158787308910.png"}, 22 | {Name: "logo.png", Url: "https://qmplusimg.henrongyi.top/1576554439myAvatar.png", Tag: "png", Key: "1587973709logo.png"}, 23 | } 24 | if err := global.GVA_DB.Create(&entities).Error; err != nil { 25 | return errors.Wrap(err, f.TableName()+"表数据初始化失败!") 26 | } 27 | return nil 28 | } 29 | 30 | func (f *fileMysql) CheckDataExist() bool { 31 | if errors.Is(global.GVA_DB.Where("`name` = ? AND `key` = ?", "logo.png", "1587973709logo.png").First(&example.ExaFileUploadAndDownload{}).Error, gorm.ErrRecordNotFound) { 32 | return false 33 | } 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /go/source/example/file_pgsql.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/example" 6 | "github.com/nooncall/owls/go/model/system" 7 | "github.com/pkg/errors" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | var FilePgsql = new(filePgsql) 12 | 13 | type filePgsql struct{} 14 | 15 | func (f *filePgsql) TableName() string { 16 | return "exa_file_upload_and_downloads" 17 | } 18 | 19 | func (f *filePgsql) Initialize(initData *system.InitDBData) error { 20 | entities := []example.ExaFileUploadAndDownload{ 21 | {Name: "10.png", Url: "https://qmplusimg.henrongyi.top/gvalogo.png", Tag: "png", Key: "158787308910.png"}, 22 | {Name: "logo.png", Url: "https://qmplusimg.henrongyi.top/1576554439myAvatar.png", Tag: "png", Key: "1587973709logo.png"}, 23 | } 24 | if err := global.GVA_DB.Create(&entities).Error; err != nil { 25 | return errors.Wrap(err, f.TableName()+"表数据初始化失败!") 26 | } 27 | return nil 28 | } 29 | 30 | func (f *filePgsql) CheckDataExist() bool { 31 | if errors.Is(global.GVA_DB.Where("name = ? AND key = ?", "logo.png", "1587973709logo.png").First(&example.ExaFileUploadAndDownload{}).Error, gorm.ErrRecordNotFound) { 32 | return false 33 | } 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /go/source/system/authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/system" 6 | "github.com/pkg/errors" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | var Authority = new(authority) 11 | 12 | type authority struct{} 13 | 14 | func (a *authority) TableName() string { 15 | return "sys_authorities" 16 | } 17 | 18 | func (a *authority) Initialize(initData *system.InitDBData) error { 19 | entities := []system.SysAuthority{ 20 | {AuthorityId: "888", AuthorityName: "dev", ParentId: "0", DefaultRouter: "dashboard"}, 21 | {AuthorityId: "887", AuthorityName: "admin", ParentId: "0", DefaultRouter: "dashboard"}, 22 | {AuthorityId: "886", AuthorityName: "user", ParentId: "0", DefaultRouter: "dashboard"}, 23 | } 24 | if err := global.GVA_DB.Create(&entities).Error; err != nil { 25 | return errors.Wrapf(err, "%s表数据初始化失败!", a.TableName()) 26 | } 27 | return nil 28 | } 29 | 30 | func (a *authority) CheckDataExist() bool { 31 | if errors.Is(global.GVA_DB.Where("authority_id = ?", "888").First(&system.SysAuthority{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 32 | return false 33 | } 34 | return true 35 | } 36 | -------------------------------------------------------------------------------- /go/source/system/user_authority.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "github.com/nooncall/owls/go/global" 5 | "github.com/nooncall/owls/go/model/system" 6 | "github.com/pkg/errors" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | var UserAuthority = new(userAuthority) 11 | 12 | type userAuthority struct{} 13 | 14 | func (a *userAuthority) TableName() string { 15 | var entity system.SysUseAuthority 16 | return entity.TableName() 17 | } 18 | 19 | func (a *userAuthority) Initialize(initData *system.InitDBData) error { 20 | entities := []system.SysUseAuthority{ 21 | {SysUserId: 1, SysAuthorityAuthorityId: "888"}, 22 | {SysUserId: 2, SysAuthorityAuthorityId: "887"}, 23 | {SysUserId: 3, SysAuthorityAuthorityId: "886"}, 24 | } 25 | if err := global.GVA_DB.Create(&entities).Error; err != nil { 26 | return errors.Wrap(err, a.TableName()+"表数据初始化失败!") 27 | } 28 | return nil 29 | } 30 | 31 | func (a *userAuthority) CheckDataExist() bool { 32 | if errors.Is(global.GVA_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", 1, "888").First(&system.SysUseAuthority{}).Error, gorm.ErrRecordNotFound) { // 判断是否存在数据 33 | return false 34 | } 35 | return true 36 | } 37 | -------------------------------------------------------------------------------- /go/utils/constant.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // todo, refactor to consts package 4 | const ( 5 | ConfigEnv = "GVA_CONFIG" 6 | ConfigFile = "config.yaml" 7 | 8 | ClusterTypeRedis = "redis" 9 | ClusterTypeMysql = "mysql" // or tidb 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /go/utils/crypto.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "fmt" 7 | "github.com/nooncall/owls/go/global" 8 | ) 9 | 10 | //加密 11 | func AesCrypto(source []byte) ([]byte, error) { 12 | var block cipher.Block 13 | var err error 14 | if block, err = aes.NewCipher([]byte(global.GVA_CONFIG.DBFilter.AesKey)); err != nil { 15 | return nil, fmt.Errorf("crypto err: %s", err.Error()) 16 | } 17 | stream := cipher.NewCTR(block, []byte(global.GVA_CONFIG.DBFilter.AesIv)) 18 | stream.XORKeyStream(source, source) 19 | return source, nil 20 | } 21 | 22 | //解密 23 | func AesDeCrypto(cryptoData []byte) ([]byte, error) { 24 | return AesCrypto(cryptoData) 25 | } 26 | -------------------------------------------------------------------------------- /go/utils/crypto_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nooncall/owls/go/config" 7 | "github.com/nooncall/owls/go/utils/logger" 8 | ) 9 | 10 | func TestCryPto(t *testing.T) { 11 | logger.InitLog(".", "test.log", "debug") 12 | config.InitConfig("../config/config.yaml") 13 | 14 | f := "TestCryPto" 15 | pwd := "aaaaaa" 16 | 17 | cryptoPwd, err := AesCrypto([]byte(pwd)) 18 | if err != nil { 19 | t.Errorf("%s crypto err: %s", f, err.Error()) 20 | t.FailNow() 21 | } 22 | deCryptoPwd, err := AesDeCrypto(cryptoPwd) 23 | if err != nil { 24 | t.Errorf("%s decrypto err: %s", f, err.Error()) 25 | t.FailNow() 26 | } 27 | 28 | if pwd != string(deCryptoPwd) { 29 | t.Errorf("%s failed, not equal", f) 30 | t.FailNow() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /go/utils/db_automation.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | "gorm.io/gorm" 9 | ) 10 | 11 | //@author: [songzhibin97](https://github.com/songzhibin97) 12 | //@function: ClearTable 13 | //@description: 清理数据库表数据 14 | //@param: db(数据库对象) *gorm.DB, tableName(表名) string, compareField(比较字段) string, interval(间隔) string 15 | //@return: error 16 | 17 | func ClearTable(db *gorm.DB, tableName string, compareField string, interval string) error { 18 | if db == nil { 19 | return errors.New("db Cannot be empty") 20 | } 21 | duration, err := time.ParseDuration(interval) 22 | if err != nil { 23 | return err 24 | } 25 | if duration < 0 { 26 | return errors.New("parse duration < 0") 27 | } 28 | return db.Debug().Exec(fmt.Sprintf("DELETE FROM %s WHERE %s < ?", tableName, compareField), time.Now().Add(-duration)).Error 29 | } 30 | 31 | func GenerateOrderField(key string, desc bool) string { 32 | if desc { 33 | return fmt.Sprintf("%s desc", key) 34 | } 35 | 36 | return fmt.Sprintf("%s asc", key) 37 | } 38 | -------------------------------------------------------------------------------- /go/utils/directory.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "github.com/nooncall/owls/go/global" 8 | "go.uber.org/zap" 9 | ) 10 | 11 | //@author: [piexlmax](https://github.com/piexlmax) 12 | //@function: PathExists 13 | //@description: 文件目录是否存在 14 | //@param: path string 15 | //@return: bool, error 16 | 17 | func PathExists(path string) (bool, error) { 18 | fi, err := os.Stat(path) 19 | if err == nil { 20 | if fi.IsDir() { 21 | return true, nil 22 | } 23 | return false, errors.New("存在同名文件") 24 | } 25 | if os.IsNotExist(err) { 26 | return false, nil 27 | } 28 | return false, err 29 | } 30 | 31 | //@author: [piexlmax](https://github.com/piexlmax) 32 | //@function: CreateDir 33 | //@description: 批量创建文件夹 34 | //@param: dirs ...string 35 | //@return: err error 36 | 37 | func CreateDir(dirs ...string) (err error) { 38 | for _, v := range dirs { 39 | exist, err := PathExists(v) 40 | if err != nil { 41 | return err 42 | } 43 | if !exist { 44 | global.GVA_LOG.Debug("create directory" + v) 45 | if err := os.MkdirAll(v, os.ModePerm); err != nil { 46 | global.GVA_LOG.Error("create directory"+v, zap.Any(" error:", err)) 47 | return err 48 | } 49 | } 50 | } 51 | return err 52 | } 53 | -------------------------------------------------------------------------------- /go/utils/fmt_plus.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | //@author: [piexlmax](https://github.com/piexlmax) 10 | //@function: StructToMap 11 | //@description: 利用反射将结构体转化为map 12 | //@param: obj interface{} 13 | //@return: map[string]interface{} 14 | 15 | func StructToMap(obj interface{}) map[string]interface{} { 16 | obj1 := reflect.TypeOf(obj) 17 | obj2 := reflect.ValueOf(obj) 18 | 19 | data := make(map[string]interface{}) 20 | for i := 0; i < obj1.NumField(); i++ { 21 | if obj1.Field(i).Tag.Get("mapstructure") != "" { 22 | data[obj1.Field(i).Tag.Get("mapstructure")] = obj2.Field(i).Interface() 23 | } else { 24 | data[obj1.Field(i).Name] = obj2.Field(i).Interface() 25 | } 26 | } 27 | return data 28 | } 29 | 30 | //@author: [piexlmax](https://github.com/piexlmax) 31 | //@function: ArrayToString 32 | //@description: 将数组格式化为字符串 33 | //@param: array []interface{} 34 | //@return: string 35 | 36 | func ArrayToString(array []interface{}) string { 37 | return strings.Replace(strings.Trim(fmt.Sprint(array), "[]"), " ", ",", -1) 38 | } 39 | -------------------------------------------------------------------------------- /go/utils/http.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | PostMethod = "POST" 12 | GetMethod = "GET" 13 | ) 14 | 15 | func DoHttpReq(method, url, jsonParam string, header http.Header) ([]byte, error) { 16 | cli := &http.Client{} 17 | req, err := http.NewRequest(method, url, strings.NewReader(jsonParam)) 18 | if err != nil { 19 | return nil, fmt.Errorf("new request err:%s", err.Error()) 20 | } 21 | 22 | req.Header = header 23 | resp, err := cli.Do(req) 24 | if err != nil { 25 | return nil, fmt.Errorf("do request err: %s", err.Error()) 26 | } 27 | defer resp.Body.Close() 28 | 29 | return ioutil.ReadAll(resp.Body) 30 | } 31 | -------------------------------------------------------------------------------- /go/utils/md5.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | //@author: [piexlmax](https://github.com/piexlmax) 9 | //@function: MD5V 10 | //@description: md5加密 11 | //@param: str []byte 12 | //@return: string 13 | 14 | func MD5V(str []byte, b ...byte) string { 15 | h := md5.New() 16 | h.Write(str) 17 | return hex.EncodeToString(h.Sum(b)) 18 | } 19 | -------------------------------------------------------------------------------- /go/utils/numbers.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "strconv" 4 | 5 | func SubTwoStrNum(numA, numB string) (int64, error) { 6 | a, err := strconv.ParseInt(numA, 10, 64) 7 | if err != nil { 8 | return 0, err 9 | } 10 | 11 | b, err := strconv.ParseInt(numB, 10, 64) 12 | if err != nil { 13 | return 0, err 14 | } 15 | 16 | return a - b, nil 17 | } 18 | 19 | func Int64ArrayDeduplication(data []int64) (result []int64) { 20 | mapData := make(map[int64]struct{}) 21 | for _, v := range data { 22 | mapData[v] = struct{}{} 23 | } 24 | 25 | for k, _ := range mapData { 26 | result = append(result, k) 27 | } 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /go/utils/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | const ( 8 | OnlyFuncName = "Plugin" 9 | ) 10 | 11 | // Plugin 插件模式接口化 12 | type Plugin interface { 13 | // Register 注册路由 14 | Register(group *gin.RouterGroup) 15 | 16 | // RouterPath 用户返回注册路由 17 | RouterPath() string 18 | } 19 | -------------------------------------------------------------------------------- /go/utils/reload.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "os/exec" 7 | "runtime" 8 | "strconv" 9 | ) 10 | 11 | func Reload() error { 12 | if runtime.GOOS == "windows" { 13 | return errors.New("系统不支持") 14 | } 15 | pid := os.Getpid() 16 | cmd := exec.Command("kill", "-1", strconv.Itoa(pid)) 17 | return cmd.Run() 18 | } 19 | -------------------------------------------------------------------------------- /go/utils/rotatelogs.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/natefinch/lumberjack" 7 | "github.com/nooncall/owls/go/global" 8 | "go.uber.org/zap/zapcore" 9 | ) 10 | 11 | //@author: [SliverHorn](https://github.com/SliverHorn) 12 | //@function: GetWriteSyncer 13 | //@description: zap logger中加入file-rotatelogs 14 | //@return: zapcore.WriteSyncer, error 15 | 16 | func GetWriteSyncer(file string) zapcore.WriteSyncer { 17 | lumberJackLogger := &lumberjack.Logger{ 18 | Filename: file, // 日志文件的位置 19 | MaxSize: 10, // 在进行切割之前,日志文件的最大大小(以MB为单位) 20 | MaxBackups: 200, // 保留旧文件的最大个数 21 | MaxAge: 30, // 保留旧文件的最大天数 22 | Compress: true, // 是否压缩/归档旧文件 23 | } 24 | 25 | if global.GVA_CONFIG.Zap.LogInConsole { 26 | return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger)) 27 | } 28 | return zapcore.AddSync(lumberJackLogger) 29 | } 30 | -------------------------------------------------------------------------------- /go/utils/set.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func MergeStringMaps(m1, m2 map[string]string) map[string]string { 4 | for k, v := range m1 { 5 | m2[k] = v 6 | } 7 | 8 | return m2 9 | } 10 | -------------------------------------------------------------------------------- /go/utils/time_convert.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "time" 4 | 5 | const timeFmt = "2006-01-02 15:04:05" 6 | 7 | func TimestampToStr(timestamp int64) string { 8 | return time.Unix(timestamp, 0).Format(timeFmt) 9 | } 10 | -------------------------------------------------------------------------------- /go/utils/timer/timed_task_test.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | var job = mockJob{} 12 | 13 | type mockJob struct{} 14 | 15 | func (job mockJob) Run() { 16 | mockFunc() 17 | } 18 | 19 | func mockFunc() { 20 | time.Sleep(time.Second) 21 | fmt.Println("1s...") 22 | } 23 | 24 | func TestNewTimerTask(t *testing.T) { 25 | tm := NewTimerTask() 26 | _tm := tm.(*timer) 27 | 28 | { 29 | _, err := tm.AddTaskByFunc("func", "@every 1s", mockFunc) 30 | assert.Nil(t, err) 31 | _, ok := _tm.taskList["func"] 32 | if !ok { 33 | t.Error("no find func") 34 | } 35 | } 36 | 37 | { 38 | _, err := tm.AddTaskByJob("job", "@every 1s", job) 39 | assert.Nil(t, err) 40 | _, ok := _tm.taskList["job"] 41 | if !ok { 42 | t.Error("no find job") 43 | } 44 | } 45 | 46 | { 47 | _, ok := tm.FindCron("func") 48 | if !ok { 49 | t.Error("no find func") 50 | } 51 | _, ok = tm.FindCron("job") 52 | if !ok { 53 | t.Error("no find job") 54 | } 55 | _, ok = tm.FindCron("none") 56 | if ok { 57 | t.Error("find none") 58 | } 59 | } 60 | { 61 | tm.Clear("func") 62 | _, ok := tm.FindCron("func") 63 | if ok { 64 | t.Error("find func") 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /go/utils/util_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestDelUselessSpace(t *testing.T) { 9 | type Data struct { 10 | Str string 11 | Target string 12 | } 13 | data := []Data{ 14 | { 15 | Str: " hgetall hello", 16 | Target: "hgetall hello", 17 | }, 18 | { 19 | Str: " hgetall hello", 20 | Target: "hgetall hello", 21 | }, 22 | { 23 | Str: " he llo ", 24 | Target: "he llo", 25 | }, 26 | } 27 | 28 | for _, v := range data { 29 | resp := DelUselessSpace(v.Str) 30 | if v.Target != resp { 31 | t.Log(fmt.Sprintf("test DelUselessSpace failed, expect: %s, got:%s", v.Target, resp)) 32 | t.FailNow() 33 | } 34 | } 35 | } 36 | 37 | func TestSubTwoStrNum(t *testing.T) { 38 | type Data struct { 39 | a string 40 | b string 41 | target int64 42 | } 43 | data := []Data{ 44 | { 45 | a: "10", 46 | b: "8", 47 | target: 2, 48 | }, 49 | { 50 | a: "7", 51 | b: "8", 52 | target: -1, 53 | }, 54 | } 55 | 56 | for _, v := range data { 57 | resp, err := SubTwoStrNum(v.a, v.b) 58 | if err != nil { 59 | t.Log("test sub str num err: ", err.Error()) 60 | t.FailNow() 61 | } 62 | 63 | if v.target != resp { 64 | t.Log(fmt.Sprintf("test DelUselessSpace failed, expect: %d, got:%d", v.target, resp)) 65 | t.FailNow() 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /issue.list: -------------------------------------------------------------------------------- 1 | 1, 使用admin做开发,但是给admin加权限还是看不到新页面,需要给普通用户加权限才能看到。 2 | 3 | 2, 页面报404,有可能是没有权限。 4 | -------------------------------------------------------------------------------- /web/.docker-compose/nginx/conf.d/my.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080; 3 | server_name localhost; 4 | 5 | #charset koi8-r; 6 | #access_log logs/host.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; 11 | try_files $uri $uri/ /index.html; 12 | } 13 | 14 | location /api { 15 | proxy_set_header Host $http_host; 16 | proxy_set_header X-Real-IP $remote_addr; 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | proxy_set_header X-Forwarded-Proto $scheme; 19 | rewrite ^/api/(.*)$ /$1 break; #重写 20 | proxy_pass http://177.7.0.12:80; # 设置代理服务器的协议和地址 21 | } 22 | 23 | location /api/swagger/index.html { 24 | proxy_pass http://127.0.0.1:80/swagger/index.html; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /web/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /web/.env.development: -------------------------------------------------------------------------------- 1 | ENV = 'development' 2 | VITE_CLI_PORT = 8080 3 | VITE_SERVER_PORT = 8778 4 | VITE_BASE_API = /api 5 | VITE_BASE_PATH = http://127.0.0.1 6 | // 如果使用docker-compose开发模式,设置为下面的地址 7 | //VITE_BASE_PATH = http://177.7.0.12 8 | -------------------------------------------------------------------------------- /web/.env.production: -------------------------------------------------------------------------------- 1 | ENV = 'production' 2 | 3 | VITE_CLI_PORT = 8080 4 | VITE_SERVER_PORT = 8778 5 | VITE_BASE_API = / 6 | #下方修改为你的线上ip 7 | VITE_BASE_PATH = https://127.0.0.1:80 8 | -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | src/assets 3 | public 4 | dist 5 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | package-lock.json 3 | yarn.lock -------------------------------------------------------------------------------- /web/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:16 2 | 3 | WORKDIR /gva_web/ 4 | COPY . . 5 | 6 | RUN yarn && yarn build 7 | 8 | FROM nginx:alpine 9 | LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com" 10 | 11 | COPY .docker-compose/nginx/conf.d/my.conf /etc/nginx/conf.d/my.conf 12 | COPY --from=0 /gva_web/dist /usr/share/nginx/html 13 | RUN cat /etc/nginx/nginx.conf 14 | RUN cat /etc/nginx/conf.d/my.conf 15 | RUN ls -al /usr/share/nginx/html 16 | -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ], 5 | 'plugins': [ 6 | 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/favicon.ico -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/limit.js: -------------------------------------------------------------------------------- 1 | // 运行项目前通过node执行此脚本 (此脚本与 node_modules 目录同级) 2 | const fs = require('fs') 3 | const path = require('path') 4 | const wfPath = path.resolve(__dirname, './node_modules/.bin') 5 | 6 | fs.readdir(wfPath, (err, files) => { 7 | if (err) { 8 | console.log(err) 9 | } else { 10 | if (files.length != 0) { 11 | files.forEach((item) => { 12 | if (item.split('.')[1] === 'cmd') { 13 | replaceStr(`${wfPath}/${item}`, /"%_prog%"/, '%_prog%') 14 | } 15 | }) 16 | } 17 | } 18 | }) 19 | 20 | // 参数:[文件路径、 需要修改的字符串、修改后的字符串] (替换对应文件内字符串的公共函数) 21 | function replaceStr(filePath, sourceRegx, targetSrt) { 22 | fs.readFile(filePath, (err, data) => { 23 | if (err) { 24 | console.log(err) 25 | } else { 26 | let str = data.toString() 27 | str = str.replace(sourceRegx, targetSrt) 28 | fs.writeFile(filePath, str, (err) => { 29 | if(err){ 30 | console.log(err) 31 | }else{ 32 | console.log("\x1B[42m%s\x1B[0m","文件修改成功") 33 | } 34 | }) 35 | } 36 | }) 37 | } -------------------------------------------------------------------------------- /web/openDocument.js: -------------------------------------------------------------------------------- 1 | /* 2 | 商用代码公司自用产品无需授权 3 | 若作为代码出售的产品(任何涉及代码交付第三方作为后续开发)必须保留此脚本 4 | 或标注原作者信息 5 | 否则将依法维权 6 | */ 7 | 8 | var child_process = require('child_process') 9 | 10 | var url = 'http://owls.nooncall.cn:8778/docs' 11 | var cmd = '' 12 | console.log(process.platform) 13 | switch (process.platform) { 14 | case 'win32': 15 | cmd = 'start' 16 | child_process.exec(cmd + ' ' + url) 17 | break 18 | 19 | case 'darwin': 20 | cmd = 'open' 21 | child_process.exec(cmd + ' ' + url) 22 | break 23 | } 24 | -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 28 | -------------------------------------------------------------------------------- /web/src/api/auth/auth.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const listAuth = (data, subType) => { 4 | return service({ 5 | url: '/auth/list', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export const delAuth = (data) => { 12 | return service({ 13 | url: '/auth?id=' + data, 14 | method: 'delete', 15 | }) 16 | } 17 | 18 | export const createAuth = (data) => { 19 | return service({ 20 | url: '/auth', 21 | method: 'post', 22 | data 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /web/src/api/authorityBtn.js: -------------------------------------------------------------------------------- 1 | 2 | import service from '@/utils/request' 3 | 4 | export const getAuthorityBtnApi = (data) => { 5 | return service({ 6 | url: '/authorityBtn/getAuthorityBtn', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | export const setAuthorityBtnApi = (data) => { 13 | return service({ 14 | url: '/authorityBtn/setAuthorityBtn', 15 | method: 'post', 16 | data 17 | }) 18 | } 19 | 20 | export const canRemoveAuthorityBtnApi = (params) => { 21 | return service({ 22 | url: '/authorityBtn/canRemoveAuthorityBtn', 23 | method: 'post', 24 | params 25 | }) 26 | } 27 | 28 | -------------------------------------------------------------------------------- /web/src/api/breakpoint.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Summary 设置角色资源权限 3 | // @Security ApiKeyAuth 4 | // @accept application/json 5 | // @Produce application/json 6 | // @Param data body sysModel.SysAuthority true "设置角色资源权限" 7 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}" 8 | // @Router /authority/setDataAuthority [post] 9 | 10 | export const findFile = (params) => { 11 | return service({ 12 | url: '/fileUploadAndDownload/findFile', 13 | method: 'get', 14 | params 15 | }) 16 | } 17 | 18 | export const breakpointContinue = (data) => { 19 | return service({ 20 | url: '/fileUploadAndDownload/breakpointContinue', 21 | method: 'post', 22 | headers: { 'Content-Type': 'multipart/form-data' }, 23 | data 24 | }) 25 | } 26 | 27 | export const breakpointContinueFinish = (params) => { 28 | return service({ 29 | url: '/fileUploadAndDownload/breakpointContinueFinish', 30 | method: 'post', 31 | params 32 | }) 33 | } 34 | 35 | export const removeChunk = (data, params) => { 36 | return service({ 37 | url: '/fileUploadAndDownload/removeChunk', 38 | method: 'post', 39 | data, 40 | params 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /web/src/api/casbin.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags authority 3 | // @Summary 更改角色api权限 4 | // @Security ApiKeyAuth 5 | // @accept application/json 6 | // @Produce application/json 7 | // @Param data body api.CreateAuthorityPatams true "更改角色api权限" 8 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 9 | // @Router /casbin/UpdateCasbin [post] 10 | export const UpdateCasbin = (data) => { 11 | return service({ 12 | url: '/casbin/updateCasbin', 13 | method: 'post', 14 | data 15 | }) 16 | } 17 | 18 | // @Tags casbin 19 | // @Summary 获取权限列表 20 | // @Security ApiKeyAuth 21 | // @accept application/json 22 | // @Produce application/json 23 | // @Param data body api.CreateAuthorityPatams true "获取权限列表" 24 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 25 | // @Router /casbin/getPolicyPathByAuthorityId [post] 26 | export const getPolicyPathByAuthorityId = (data) => { 27 | return service({ 28 | url: '/casbin/getPolicyPathByAuthorityId', 29 | method: 'post', 30 | data 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /web/src/api/db/cluster.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const listCluster = (data) => { 4 | return service({ 5 | url: '/db/cluster/list', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export const listClusterName = (filter, ctype) => { 12 | return service({ 13 | url: '/db/cluster/name/list?filter=' + filter + '&c_type=' + ctype, 14 | method: 'get', 15 | }) 16 | } 17 | 18 | export const listDatabase = (cluster, filter) => { 19 | return service({ 20 | url: '/db/cluster/db/list?filter=' + filter + '&cluster=' + cluster, 21 | method: 'get', 22 | }) 23 | } 24 | 25 | export const listTable = (cluster, db) => { 26 | return service({ 27 | url: '/db/cluster/table/list?cluster=' + cluster + '&db=' + db, 28 | method: 'get' 29 | }) 30 | } 31 | 32 | export const deleteCluster = (data) => { 33 | return service({ 34 | url: '/db/cluster?id=' + data.id, 35 | method: 'delete', 36 | data 37 | }) 38 | } 39 | 40 | export const updateCluster = (data) => { 41 | return service({ 42 | url: '/db/cluster', 43 | method: 'put', 44 | data 45 | }) 46 | } 47 | 48 | export const createCluster = (data) => { 49 | return service({ 50 | url: '/db/cluster', 51 | method: 'post', 52 | data 53 | }) 54 | } 55 | -------------------------------------------------------------------------------- /web/src/api/db/read.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const readRedisData = (data) => { 4 | return service({ 5 | url: '/redis/read', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | 12 | export const readData = (data) => { 13 | return service({ 14 | url: '/db/read', 15 | method: 'post', 16 | data 17 | }) 18 | } 19 | 20 | export const getTableInfo = (data) => { 21 | return service({ 22 | url: '/db/table', 23 | method: 'post', 24 | data 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /web/src/api/db/rule.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const listRedisRule = (data) => { 4 | return service({ 5 | url: '/redis/rule/list', 6 | method: 'get', 7 | data 8 | }) 9 | } 10 | 11 | export const listRule = (data) => { 12 | return service({ 13 | url: '/db/rule/list', 14 | method: 'post', 15 | data 16 | }) 17 | } 18 | 19 | export const updateRuleStatus = (data) => { 20 | return service({ 21 | url: '/db/rule/status', 22 | method: 'put', 23 | data 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /web/src/api/db/task.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const listTask = (data) => { 4 | return service({ 5 | url: '/db/task/list', 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export const listReviewTask = (data) => { 12 | return service({ 13 | url: '/db/task/review', 14 | method: 'post', 15 | data 16 | }) 17 | } 18 | 19 | export const listHistoryTask = (data) => { 20 | return service({ 21 | url: '/db/task/history', 22 | method: 'post', 23 | data 24 | }) 25 | } 26 | export const getTask = (data) => { 27 | return service({ 28 | url: '/db/task?id=' + data, 29 | method: 'get', 30 | }) 31 | } 32 | 33 | export const updateTask = (data) => { 34 | return service({ 35 | url: '/db/task', 36 | method: 'put', 37 | data 38 | }) 39 | } 40 | 41 | export const createTask = (data) => { 42 | return service({ 43 | url: '/db/task', 44 | method: 'post', 45 | data 46 | }) 47 | } 48 | 49 | export const listBackup = (data) => { 50 | return service({ 51 | url: '/db/backup/list', 52 | method: 'post', 53 | data 54 | }) 55 | } 56 | 57 | export const rollback = (data) => { 58 | return service({ 59 | url: '/db/backup/rollback', 60 | method: 'post', 61 | data 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /web/src/api/email.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags email 3 | // @Summary 发送测试邮件 4 | // @Security ApiKeyAuth 5 | // @Produce application/json 6 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 7 | // @Router /email/emailTest [post] 8 | export const emailTest = (data) => { 9 | return service({ 10 | url: '/email/emailTest', 11 | method: 'post', 12 | data 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /web/src/api/fileUploadAndDownload.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags FileUploadAndDownload 3 | // @Summary 分页文件列表 4 | // @Security ApiKeyAuth 5 | // @accept application/json 6 | // @Produce application/json 7 | // @Param data body modelInterface.PageInfo true "分页获取文件户列表" 8 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}" 9 | // @Router /fileUploadAndDownload/getFileList [post] 10 | export const getFileList = (data) => { 11 | return service({ 12 | url: '/fileUploadAndDownload/getFileList', 13 | method: 'post', 14 | data 15 | }) 16 | } 17 | 18 | // @Tags FileUploadAndDownload 19 | // @Summary 删除文件 20 | // @Security ApiKeyAuth 21 | // @Produce application/json 22 | // @Param data body dbModel.FileUploadAndDownload true "传入文件里面id即可" 23 | // @Success 200 {string} json "{"success":true,"data":{},"msg":"返回成功"}" 24 | // @Router /fileUploadAndDownload/deleteFile [post] 25 | export const deleteFile = (data) => { 26 | return service({ 27 | url: '/fileUploadAndDownload/deleteFile', 28 | method: 'post', 29 | data 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /web/src/api/github.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const service = axios.create() 4 | 5 | export function Commits(page) { 6 | return service({ 7 | url: 'https://api.github.com/repos/flipped-aurora/gin-vue-admin/commits?page=' + page, 8 | method: 'get' 9 | }) 10 | } 11 | 12 | export function Members() { 13 | return service({ 14 | url: 'https://api.github.com/orgs/FLIPPED-AURORA/members', 15 | method: 'get' 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /web/src/api/initdb.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags InitDB 3 | // @Summary 初始化用户数据库 4 | // @Produce application/json 5 | // @Param data body request.InitDB true "初始化数据库参数" 6 | // @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}" 7 | // @Router /init/initdb [post] 8 | export const initDB = (data) => { 9 | return service({ 10 | url: '/init/initdb', 11 | method: 'post', 12 | data 13 | }) 14 | } 15 | 16 | // @Tags CheckDB 17 | // @Summary 初始化用户数据库 18 | // @Produce application/json 19 | // @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}" 20 | // @Router /init/checkdb [post] 21 | export const checkDB = () => { 22 | return service({ 23 | url: '/init/checkdb', 24 | method: 'post' 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /web/src/api/jwt.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags jwt 3 | // @Summary jwt加入黑名单 4 | // @Security ApiKeyAuth 5 | // @accept application/json 6 | // @Produce application/json 7 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}" 8 | // @Router /jwt/jsonInBlacklist [post] 9 | export const jsonInBlacklist = () => { 10 | return service({ 11 | url: '/jwt/jsonInBlacklist', 12 | method: 'post' 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /web/src/api/system.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | // @Tags systrm 3 | // @Summary 获取配置文件内容 4 | // @Security ApiKeyAuth 5 | // @Produce application/json 6 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 7 | // @Router /system/getSystemConfig [post] 8 | export const getSystemConfig = () => { 9 | return service({ 10 | url: '/system/getSystemConfig', 11 | method: 'post' 12 | }) 13 | } 14 | 15 | // @Tags system 16 | // @Summary 设置配置文件内容 17 | // @Security ApiKeyAuth 18 | // @Produce application/json 19 | // @Param data body sysModel.System true 20 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 21 | // @Router /system/setSystemConfig [post] 22 | export const setSystemConfig = (data) => { 23 | return service({ 24 | url: '/system/setSystemConfig', 25 | method: 'post', 26 | data 27 | }) 28 | } 29 | 30 | // @Tags system 31 | // @Summary 获取服务器运行状态 32 | // @Security ApiKeyAuth 33 | // @Produce application/json 34 | // @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}" 35 | // @Router /system/getServerInfo [post] 36 | export const getSystemState = () => { 37 | return service({ 38 | url: '/system/getServerInfo', 39 | method: 'post', 40 | donNotShowLoading: true 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /web/src/api/task/task.js: -------------------------------------------------------------------------------- 1 | import service from '@/utils/request' 2 | 3 | export const listTask = (data, subType) => { 4 | return service({ 5 | url: '/task/list?type=' + subType, 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export const listReviewTask = (data, subType) => { 12 | return service({ 13 | url: '/task/review?type=' + subType, 14 | method: 'post', 15 | data 16 | }) 17 | } 18 | 19 | export const listHistoryTask = (data, subType) => { 20 | return service({ 21 | url: '/task/history?type=' + subType, 22 | method: 'post', 23 | data 24 | }) 25 | } 26 | export const getTask = (data, subType) => { 27 | return service({ 28 | url: '/task?id=' + data + '&type=' + subType, 29 | method: 'get', 30 | }) 31 | } 32 | 33 | export const updateTask = (data) => { 34 | return service({ 35 | url: '/task', 36 | method: 'put', 37 | data 38 | }) 39 | } 40 | 41 | export const createTask = (data) => { 42 | return service({ 43 | url: '/task', 44 | method: 'post', 45 | data 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /web/src/assets/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/dashboard.png -------------------------------------------------------------------------------- /web/src/assets/docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/docs.png -------------------------------------------------------------------------------- /web/src/assets/flipped-aurora.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/flipped-aurora.png -------------------------------------------------------------------------------- /web/src/assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/github.png -------------------------------------------------------------------------------- /web/src/assets/kefu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/kefu.png -------------------------------------------------------------------------------- /web/src/assets/login_background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/login_background.jpg -------------------------------------------------------------------------------- /web/src/assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/logo.jpg -------------------------------------------------------------------------------- /web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/logo.png -------------------------------------------------------------------------------- /web/src/assets/logo_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/logo_login.png -------------------------------------------------------------------------------- /web/src/assets/nav_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/nav_logo.png -------------------------------------------------------------------------------- /web/src/assets/noBody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/noBody.png -------------------------------------------------------------------------------- /web/src/assets/notFound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/notFound.png -------------------------------------------------------------------------------- /web/src/assets/qm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/qm.png -------------------------------------------------------------------------------- /web/src/assets/video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/assets/video.png -------------------------------------------------------------------------------- /web/src/components/warningBar/warningBar.vue: -------------------------------------------------------------------------------- 1 | 13 | 22 | 42 | -------------------------------------------------------------------------------- /web/src/core/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 网站配置文件 3 | */ 4 | 5 | const config = { 6 | appName: 'Owls', 7 | // todo, set a better image addr 8 | appLogo: 'https://img0.baidu.com/it/u=2822765666,2555722031&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501', 9 | showViteLogo: true 10 | } 11 | 12 | export const viteLogo = (env) => { 13 | if (config.showViteLogo) { 14 | const chalk = require('chalk') 15 | console.log( 16 | chalk.green( 17 | `> 欢迎使用Owls,开源地址:https://github.com/qingfeng777/owls` 18 | ) 19 | ) 20 | console.log( 21 | chalk.green( 22 | `> 当前版本:V0.0.1` 23 | ) 24 | ) 25 | console.log( 26 | chalk.green( 27 | `> 加群方式:微信:xxxx QQ群:xxxx` 28 | ) 29 | ) 30 | console.log( 31 | chalk.green( 32 | `> 默认自动化文档地址:http://127.0.0.1:${env.VITE_SERVER_PORT}/swagger/index.html` 33 | ) 34 | ) 35 | console.log( 36 | chalk.green( 37 | `> 默认前端文件运行地址:http://127.0.0.1:${env.VITE_CLI_PORT}` 38 | ) 39 | ) 40 | console.log( 41 | chalk.green( 42 | `> 如果项目让您获得了收益,那就帮忙宣传一下吧!` 43 | ) 44 | ) 45 | console.log('\n') 46 | } 47 | } 48 | 49 | export default config 50 | -------------------------------------------------------------------------------- /web/src/core/gin-vue-admin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * owls 3 | * 4 | * */ 5 | // 加载网站配置文件夹 6 | import { register } from './global' 7 | 8 | export default { 9 | install: (app) => { 10 | register(app) 11 | console.log(` 12 | 欢迎使用 owls 13 | 当前版本:V0.3.0 14 | 加群方式:微信:grsixk 15 | 默认自动化文档地址:http://127.0.0.1:${import.meta.env.VITE_SERVER_PORT}/swagger/index.html 16 | 默认前端文件运行地址:http://127.0.0.1:${import.meta.env.VITE_CLI_PORT} 17 | 如果项目让您获得了收益,希望您能帮忙宣传下 18 | `) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web/src/core/global.js: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | import { emitter } from '@/utils/bus.js' 3 | 4 | // 统一导入el-icon图标 5 | import * as ElIconModules from '@element-plus/icons-vue' 6 | // 导入转换图标名称的函数 7 | 8 | export const closeThisPage = () => { 9 | emitter.emit('closeThisPage') 10 | } 11 | 12 | export const register = (app) => { 13 | // 统一注册el-icon图标 14 | for (const iconName in ElIconModules) { 15 | app.component(iconName, ElIconModules[iconName]) 16 | } 17 | app.config.globalProperties.$GIN_VUE_ADMIN = config 18 | } 19 | -------------------------------------------------------------------------------- /web/src/directive/auth.js: -------------------------------------------------------------------------------- 1 | // 权限按钮展示指令 2 | import { useUserStore } from '@/pinia/modules/user' 3 | export default { 4 | install: (app) => { 5 | const userStore = useUserStore() 6 | app.directive('auth', { 7 | // 当被绑定的元素插入到 DOM 中时…… 8 | mounted: function(el, binding) { 9 | const userInfo = userStore.userInfo 10 | let type = '' 11 | switch (Object.prototype.toString.call(binding.value)) { 12 | case '[object Array]': 13 | type = 'Array' 14 | break 15 | case '[object String]': 16 | type = 'String' 17 | break 18 | case '[object Number]': 19 | type = 'Number' 20 | break 21 | default: 22 | type = '' 23 | break 24 | } 25 | if (type === '') { 26 | el.parentNode.removeChild(el) 27 | return 28 | } 29 | const waitUse = binding.value.toString().split(',') 30 | let flag = waitUse.some(item => item === userInfo.authorityId) 31 | if (binding.modifiers.not) { 32 | flag = !flag 33 | } 34 | if (!flag) { 35 | el.parentNode.removeChild(el) 36 | } 37 | } 38 | }) 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /web/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import 'element-plus/dist/index.css' 3 | import './style/element_visiable.scss' 4 | import ElementPlus from 'element-plus' 5 | import zhCn from 'element-plus/es/locale/lang/zh-cn' 6 | // 引入gin-vue-admin前端初始化相关内容 7 | import './core/gin-vue-admin' 8 | // 引入封装的router 9 | import router from '@/router/index' 10 | import '@/permission' 11 | import run from '@/core/gin-vue-admin.js' 12 | import auth from '@/directive/auth' 13 | import { store } from '@/pinia' 14 | import App from './App.vue' 15 | const app = createApp(App) 16 | app.config.productionTip = false 17 | 18 | app 19 | .use(run) 20 | .use(store) 21 | .use(auth) 22 | .use(router) 23 | .use(ElementPlus, { locale: zhCn }) 24 | .mount('#app') 25 | 26 | export default app 27 | -------------------------------------------------------------------------------- /web/src/pinia/index.js: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | 3 | const store = createPinia() 4 | 5 | export { 6 | store 7 | } 8 | -------------------------------------------------------------------------------- /web/src/pinia/modules/dictionary.js: -------------------------------------------------------------------------------- 1 | import { findSysDictionary } from '@/api/sysDictionary' 2 | 3 | import { defineStore } from 'pinia' 4 | import { ref } from 'vue' 5 | 6 | export const useDictionaryStore = defineStore('dictionary', () => { 7 | const dictionaryMap = ref({}) 8 | 9 | const setDictionaryMap = (dictionaryRes) => { 10 | dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes } 11 | } 12 | 13 | const getDictionary = async(type) => { 14 | if (dictionaryMap.value[type] && dictionaryMap.value[type].length) { 15 | return dictionaryMap.value[type] 16 | } else { 17 | const res = await findSysDictionary({ type }) 18 | if (res.code === 0) { 19 | const dictionaryRes = {} 20 | const dict = [] 21 | res.data.resysDictionary.sysDictionaryDetails && res.data.resysDictionary.sysDictionaryDetails.forEach(item => { 22 | dict.push({ 23 | label: item.label, 24 | value: item.value 25 | }) 26 | }) 27 | dictionaryRes[res.data.resysDictionary.type] = dict 28 | setDictionaryMap(dictionaryRes) 29 | return dictionaryMap.value[type] 30 | } 31 | } 32 | } 33 | 34 | return { 35 | dictionaryMap, 36 | setDictionaryMap, 37 | getDictionary 38 | } 39 | }) 40 | -------------------------------------------------------------------------------- /web/src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from 'vue-router' 2 | 3 | const routes = [{ 4 | path: '/', 5 | name: 'root', 6 | redirect: '/login' 7 | }, 8 | { 9 | path: '/init', 10 | name: 'Init', 11 | component: () => import('@/view/init/index.vue') 12 | }, 13 | { 14 | path: '/login', 15 | name: 'Login', 16 | component: () => import('@/view/login/index.vue') 17 | } 18 | ] 19 | 20 | const router = createRouter({ 21 | history: createWebHashHistory(), 22 | routes 23 | }) 24 | 25 | export default router 26 | -------------------------------------------------------------------------------- /web/src/style/base.scss: -------------------------------------------------------------------------------- 1 | .clearflex { 2 | *zoom: 1; 3 | } 4 | 5 | .clearflex:after { 6 | content: ''; 7 | display: block; 8 | height: 0; 9 | visibility: hidden; 10 | clear: both; 11 | } 12 | 13 | .fl-left { 14 | float: left; 15 | } 16 | 17 | .fl-right { 18 | float: right; 19 | } 20 | 21 | .mg { 22 | margin: 10px !important; 23 | } 24 | 25 | .left-mg-xs { 26 | margin-left: 6px !important; 27 | } 28 | 29 | .left-mg-sm { 30 | margin-left: 10px !important; 31 | } 32 | 33 | .left-mg-md { 34 | margin-left: 14px !important; 35 | } 36 | 37 | .top-mg-lg { 38 | margin-top: 20px !important; 39 | } 40 | 41 | .tb-mg-lg { 42 | margin: 20px 0 !important; 43 | } 44 | 45 | .bottom-mg-lg { 46 | margin-bottom: 20px !important; 47 | } 48 | 49 | .left-mg-lg { 50 | margin-left: 18px !important; 51 | } 52 | 53 | .title-1 { 54 | text-align: center; 55 | font-size: 32px; 56 | } 57 | 58 | .title-3 { 59 | text-align: center; 60 | } 61 | -------------------------------------------------------------------------------- /web/src/style/basics.scss: -------------------------------------------------------------------------------- 1 | // basice 2 | $font-size: 14px; 3 | $icon-size:18px; 4 | $active-color:#1890ff; 5 | $bg-main:#f0f2f5; 6 | $border-color: #f4f4f4; 7 | $white-bg:#fff; 8 | $el-icon-small:30px; 9 | $el-icon-mini:24px; 10 | // aside 11 | $width-aside:220px; 12 | $width-hideside-aside:54px; 13 | $width-mobile-aside:210px; 14 | $color-aside:rgba(255, 255, 255, .9); 15 | $icon-arrow-size-aside:12px; 16 | $width-submenu-aside:55px; 17 | $height-aside-tilte:60px; 18 | $height-aside-img:30px; 19 | $width-aside-img:30px; 20 | // header 21 | $height-header: 60px; 22 | // nav-scroll 23 | $height-nav-scroll:40px; 24 | $active-bg-tabs-item-nav-scroll:#409eff; 25 | $bg-tabs-item-nav-scroll:#ddd; 26 | // table 27 | $bg-color-table-thead:#fafafa; 28 | $border-color-table:#ededed; 29 | $height-table-cell:45px; 30 | $color-table-tbody:#595959; 31 | $color-table-thead:#262626; 32 | // dashboard 33 | $height-car:68px; 34 | // mobile 35 | $padding-xs: 5px; 36 | $margin-xs: 5px; -------------------------------------------------------------------------------- /web/src/style/init.sass: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nooncall/owls/73f8a9ca53babede1d74790766883082c02fe0dc/web/src/style/init.sass -------------------------------------------------------------------------------- /web/src/utils/asyncRouter.js: -------------------------------------------------------------------------------- 1 | const modules = import.meta.glob('../view/**/*.vue') 2 | 3 | export const asyncRouterHandle = (asyncRouter) => { 4 | asyncRouter.forEach(item => { 5 | if (item.component) { 6 | item.component = dynamicImport(modules, item.component) 7 | } else { 8 | delete item['component'] 9 | } 10 | if (item.children) { 11 | asyncRouterHandle(item.children) 12 | } 13 | }) 14 | } 15 | 16 | function dynamicImport( 17 | dynamicViewsModules, 18 | component 19 | ) { 20 | const keys = Object.keys(dynamicViewsModules) 21 | const matchKeys = keys.filter((key) => { 22 | const k = key.replace('../', '') 23 | return k === component 24 | }) 25 | const matchKey = matchKeys[0] 26 | 27 | return dynamicViewsModules[matchKey] 28 | } 29 | -------------------------------------------------------------------------------- /web/src/utils/btnAuth.js: -------------------------------------------------------------------------------- 1 | import { useRoute } from 'vue-router' 2 | import {reactive }from 'vue' 3 | export const useBtnAuth = () => { 4 | const route = useRoute() 5 | return route.meta.btns || reactive({}) 6 | } 7 | -------------------------------------------------------------------------------- /web/src/utils/bus.js: -------------------------------------------------------------------------------- 1 | 2 | // using ES6 modules 3 | import mitt from 'mitt' 4 | 5 | export const emitter = mitt() 6 | 7 | -------------------------------------------------------------------------------- /web/src/utils/date.js: -------------------------------------------------------------------------------- 1 | // 对Date的扩展,将 Date 转化为指定格式的String 2 | // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 3 | // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 4 | // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 5 | // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 6 | // eslint-disable-next-line no-extend-native 7 | Date.prototype.Format = function(fmt) { 8 | var o = { 9 | 'M+': this.getMonth() + 1, // 月份 10 | 'd+': this.getDate(), // 日 11 | 'h+': this.getHours(), // 小时 12 | 'm+': this.getMinutes(), // 分 13 | 's+': this.getSeconds(), // 秒 14 | 'q+': Math.floor((this.getMonth() + 3) / 3), // 季度 15 | 'S': this.getMilliseconds() // 毫秒 16 | } 17 | if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length)) } 18 | for (var k in o) { 19 | if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) } 20 | } 21 | return fmt 22 | } 23 | 24 | export function formatTimeToStr(times, pattern) { 25 | var d = new Date(times).Format('yyyy-MM-dd hh:mm:ss') 26 | if (pattern) { 27 | d = new Date(times).Format(pattern) 28 | } 29 | return d.toLocaleString() 30 | } 31 | -------------------------------------------------------------------------------- /web/src/utils/dictionary.js: -------------------------------------------------------------------------------- 1 | import { useDictionaryStore } from '@/pinia/modules/dictionary' 2 | // 获取字典方法 使用示例 getDict('sex').then(res) 或者 async函数下 const res = await getDict('sex') 3 | export const getDict = async(type) => { 4 | const dictionaryStore = useDictionaryStore() 5 | await dictionaryStore.getDictionary(type) 6 | return dictionaryStore.dictionaryMap[type] 7 | } 8 | -------------------------------------------------------------------------------- /web/src/utils/downloadImg.js: -------------------------------------------------------------------------------- 1 | export const downloadImage = (imgsrc, name) => { // 下载图片地址和图片名 2 | var image = new Image() 3 | image.setAttribute('crossOrigin', 'anonymous') 4 | image.onload = function() { 5 | var canvas = document.createElement('canvas') 6 | canvas.width = image.width 7 | canvas.height = image.height 8 | var context = canvas.getContext('2d') 9 | context.drawImage(image, 0, 0, image.width, image.height) 10 | var url = canvas.toDataURL('image/png') // 得到图片的base64编码数据 11 | 12 | var a = document.createElement('a') // 生成一个a元素 13 | var event = new MouseEvent('click') // 创建一个单击事件 14 | a.download = name || 'photo' // 设置图片名称 15 | a.href = url // 将生成的URL设置为a.href属性 16 | a.dispatchEvent(event) // 触发a的单击事件 17 | } 18 | image.src = imgsrc 19 | } 20 | -------------------------------------------------------------------------------- /web/src/utils/format.js: -------------------------------------------------------------------------------- 1 | import { formatTimeToStr } from '@/utils/date' 2 | import { getDict } from '@/utils/dictionary' 3 | 4 | export const formatBoolean = (bool) => { 5 | if (bool !== null) { 6 | return bool ? '是' : '否' 7 | } else { 8 | return '' 9 | } 10 | } 11 | export const formatDate = (time) => { 12 | if (time !== null && time !== '') { 13 | var date = new Date(time) 14 | return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss') 15 | } else { 16 | return '' 17 | } 18 | } 19 | 20 | export const filterDict = (value, options) => { 21 | const rowLabel = options && options.filter(item => item.value === value) 22 | return rowLabel && rowLabel[0] && rowLabel[0].label 23 | } 24 | 25 | export const getDictFunc = async(type) => { 26 | const dicts = await getDict(type) 27 | return dicts 28 | } 29 | -------------------------------------------------------------------------------- /web/src/utils/page.js: -------------------------------------------------------------------------------- 1 | import config from '@/core/config' 2 | export default function getPageTitle(pageTitle) { 3 | if (pageTitle) { 4 | return `${pageTitle} - ${config.appName}` 5 | } 6 | return `${config.appName}` 7 | } 8 | -------------------------------------------------------------------------------- /web/src/utils/stringFun.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export const toUpperCase = (str) => { 3 | if (str[0]) { 4 | return str.replace(str[0], str[0].toUpperCase()) 5 | } else { 6 | return '' 7 | } 8 | } 9 | 10 | export const toLowerCase = (str) => { 11 | if (str[0]) { 12 | return str.replace(str[0], str[0].toLowerCase()) 13 | } else { 14 | return '' 15 | } 16 | } 17 | 18 | // 驼峰转换下划线 19 | export const toSQLLine = (str) => { 20 | if (str === 'ID') return 'ID' 21 | return str.replace(/([A-Z])/g, "_$1").toLowerCase(); 22 | } 23 | 24 | // 下划线转换驼峰 25 | export const toHump = (name) => { 26 | return name.replace(/\_(\w)/g, function(all, letter) { 27 | return letter.toUpperCase(); 28 | }); 29 | } -------------------------------------------------------------------------------- /web/src/view/auth/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 22 | // 申请页,列表、创建、撤销 23 | // 审批页,简单通过或拒绝,无历史页 24 | 25 | // 初始化名改为管理员角色,管理员拥有所有权限 26 | 27 | // auth: 列表、添加、删除。 28 | 29 | // 然后就可以写查询啦! 30 | -------------------------------------------------------------------------------- /web/src/view/error/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 21 | 22 | 46 | -------------------------------------------------------------------------------- /web/src/view/error/reload.vue: -------------------------------------------------------------------------------- 1 | 4 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /web/src/view/example/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 22 | -------------------------------------------------------------------------------- /web/src/view/layout/aside/asideComponent/asyncSubmenu.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /web/src/view/layout/aside/asideComponent/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 35 | 36 | -------------------------------------------------------------------------------- /web/src/view/layout/aside/asideComponent/menuItem.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 28 | 29 | 34 | -------------------------------------------------------------------------------- /web/src/view/layout/bottomInfo/bottomInfo.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 26 | 27 | 44 | -------------------------------------------------------------------------------- /web/src/view/redis/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 22 | -------------------------------------------------------------------------------- /web/src/view/routerHolder.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 19 | 20 | 24 | -------------------------------------------------------------------------------- /web/src/view/superAdmin/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 22 | -------------------------------------------------------------------------------- /web/src/view/systemTools/formCreate/index.vue: -------------------------------------------------------------------------------- 1 |