├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .golangci.yml ├── .travis.yml ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── Makefile ├── README-EN.md ├── README.md ├── cmd ├── api │ └── server.go ├── cobra.go └── migrate │ └── migrate.go ├── config ├── casbin │ └── rbac_model_0.conf ├── in-cluster.yaml ├── in-local.yaml └── locale │ ├── locale_en-US.ini │ └── locale_zh-CN.ini ├── covprofile ├── docs ├── docs.go ├── en │ └── CONTRIBUTING.md ├── images │ ├── arch.png │ ├── dber.png │ ├── logo.png │ ├── screenshot1.png │ ├── screenshot2.png │ ├── screenshot3.png │ ├── wechatqun.jpg │ └── wx.jpg ├── swagger.json ├── swagger.yaml └── zh │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── GenrsaKey.md │ ├── Roadmap.md │ └── Swag.md ├── go.mod ├── go.sum ├── keys ├── jwt_private_key.pem └── jwt_public_key.pem ├── main.go ├── pkg ├── api │ ├── cache │ │ ├── init.go │ │ └── redis.go │ ├── controllers │ │ ├── account.go │ │ ├── auth.go │ │ ├── base.go │ │ ├── data_perm.go │ │ ├── dept.go │ │ ├── domain.go │ │ ├── install.go │ │ ├── log.go │ │ ├── menu.go │ │ ├── probe.go │ │ ├── role.go │ │ ├── setting.go │ │ ├── sync.go │ │ └── user.go │ ├── dao │ │ ├── data_perm.go │ │ ├── dept.go │ │ ├── domain.go │ │ ├── init.go │ │ ├── log.go │ │ ├── menu.go │ │ ├── menu_perm_alias.go │ │ ├── role.go │ │ ├── role_data_perm.go │ │ ├── user.go │ │ ├── user_oauth.go │ │ └── user_secret.go │ ├── domain │ │ ├── account │ │ │ ├── account.go │ │ │ ├── account_test.go │ │ │ ├── ldap │ │ │ │ ├── init.go │ │ │ │ └── ldap.go │ │ │ └── login │ │ │ │ ├── 2fa_handler.go │ │ │ │ ├── 2fa_handler_test.go │ │ │ │ ├── dingding.go │ │ │ │ ├── dingding_test.go │ │ │ │ ├── general.go │ │ │ │ ├── general_test.go │ │ │ │ └── weixin.go │ │ ├── dept │ │ │ └── sync │ │ │ │ └── dingtalk.go │ │ ├── perm │ │ │ ├── adapter │ │ │ │ ├── adapter.go │ │ │ │ └── mysql │ │ │ │ │ └── gorm.go │ │ │ ├── dataperm │ │ │ │ ├── data_perm.go │ │ │ │ └── data_perm_test.go │ │ │ ├── perm.go │ │ │ ├── perm_test.csv │ │ │ ├── perm_test.go │ │ │ └── rbac_model_0.conf │ │ ├── role │ │ │ ├── role.go │ │ │ └── role_test.go │ │ ├── search │ │ │ ├── adapter │ │ │ │ ├── adapter.go │ │ │ │ └── statement │ │ │ │ │ └── sql.go │ │ │ ├── lexer │ │ │ │ ├── lexer │ │ │ │ │ ├── lexer.go │ │ │ │ │ ├── stm.go │ │ │ │ │ ├── stm_begin.go │ │ │ │ │ ├── stm_delimiter.go │ │ │ │ │ ├── stm_equal.go │ │ │ │ │ ├── stm_key.go │ │ │ │ │ ├── stm_test.go │ │ │ │ │ └── stm_val.go │ │ │ │ └── token │ │ │ │ │ └── token.go │ │ │ └── parser │ │ │ │ ├── parser.go │ │ │ │ └── parser_test.go │ │ ├── sync │ │ │ └── dingdingtalk │ │ │ │ ├── sync.go │ │ │ │ └── sync_test.go │ │ └── user │ │ │ ├── user.go │ │ │ └── user_test.go │ ├── dto │ │ ├── data_perm.go │ │ ├── dept.go │ │ ├── domain.go │ │ ├── general.go │ │ ├── init.go │ │ ├── install.go │ │ ├── login.go │ │ ├── login_log.go │ │ ├── menu.go │ │ ├── myaccount.go │ │ ├── operation_log.go │ │ ├── perm.go │ │ ├── role.go │ │ ├── role_data_perm.go │ │ ├── setting.go │ │ ├── user.go │ │ ├── user_oauth.go │ │ └── user_secret.go │ ├── log │ │ └── logger.go │ ├── middleware │ │ ├── cors.go │ │ ├── jwt.go │ │ ├── locale.go │ │ ├── log.go │ │ ├── perm.go │ │ └── prepare.go │ ├── model │ │ ├── base.go │ │ ├── casbin_rule.go │ │ ├── data_perm.go │ │ ├── department.go │ │ ├── domain.go │ │ ├── login_log.go │ │ ├── menu.go │ │ ├── menu_perm_alias.go │ │ ├── operation_log.go │ │ ├── role.go │ │ ├── role_data_perm.go │ │ ├── setting.go │ │ ├── user.go │ │ ├── user_oauth.go │ │ ├── user_role.go │ │ └── user_secret.go │ ├── router │ │ └── router.go │ ├── service │ │ ├── data_perm.go │ │ ├── dept.go │ │ ├── dingtalk.go │ │ ├── domain.go │ │ ├── install.go │ │ ├── log.go │ │ ├── menu.go │ │ ├── menu_perm_alias.go │ │ ├── myaccount.go │ │ ├── role.go │ │ ├── setting.go │ │ ├── user.go │ │ └── user_secret.go │ └── utils │ │ ├── mailTemplate │ │ └── verify.go │ │ └── utils.go └── webui │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .postcssrc.js │ ├── .travis.yml │ ├── build │ ├── build.js │ ├── check-versions.js │ ├── utils.js │ ├── vue-loader.conf.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js │ ├── config │ ├── dev.env.js │ ├── index.js │ ├── prev.env.js │ ├── prod.env.js │ ├── test.env.js │ └── work.env.js │ ├── favicon.ico │ ├── index.html │ ├── node_modules │ └── .bin │ │ └── acorn │ ├── package.json │ ├── readme.md │ ├── src │ ├── App.vue │ ├── api │ │ ├── dataPerm.js │ │ ├── dept.js │ │ ├── domain.js │ │ ├── log.js │ │ ├── login.js │ │ ├── menu.js │ │ ├── qiniu.js │ │ ├── remoteSearch.js │ │ ├── role.js │ │ ├── transaction.js │ │ └── user.js │ ├── assets │ │ ├── 401_images │ │ │ └── 401.gif │ │ ├── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ │ ├── custom-theme │ │ │ ├── fonts │ │ │ │ ├── element-icons.ttf │ │ │ │ └── element-icons.woff │ │ │ └── index.css │ │ └── images │ │ │ ├── background.jpg │ │ │ ├── dingtalk.png │ │ │ ├── error-page │ │ │ ├── error-401.svg │ │ │ ├── error-404.svg │ │ │ └── error-500.svg │ │ │ ├── favicon.png │ │ │ ├── github.png │ │ │ ├── google-authenticator.png │ │ │ ├── logo-min.png │ │ │ ├── logo.png │ │ │ └── wechat.png │ ├── components │ │ ├── BackToTop │ │ │ └── index.vue │ │ ├── Breadcrumb │ │ │ └── index.vue │ │ ├── Charts │ │ │ ├── keyboard.vue │ │ │ ├── lineMarker.vue │ │ │ ├── mixChart.vue │ │ │ └── mixins │ │ │ │ └── resize.js │ │ ├── DndList │ │ │ └── index.vue │ │ ├── DragSelect │ │ │ └── index.vue │ │ ├── Dropzone │ │ │ └── index.vue │ │ ├── ErrorLog │ │ │ └── index.vue │ │ ├── GithubCorner │ │ │ └── index.vue │ │ ├── Hamburger │ │ │ └── index.vue │ │ ├── ImageCropper │ │ │ ├── index.vue │ │ │ └── utils │ │ │ │ ├── data2blob.js │ │ │ │ ├── effectRipple.js │ │ │ │ ├── language.js │ │ │ │ └── mimes.js │ │ ├── JsonEditor │ │ │ └── index.vue │ │ ├── Kanban │ │ │ └── index.vue │ │ ├── LangSelect │ │ │ └── index.vue │ │ ├── MDinput │ │ │ └── index.vue │ │ ├── MarkdownEditor │ │ │ ├── defaultOptions.js │ │ │ └── index.vue │ │ ├── Pagination │ │ │ └── index.vue │ │ ├── PanThumb │ │ │ └── index.vue │ │ ├── Screenfull │ │ │ └── index.vue │ │ ├── ScrollPane │ │ │ └── index.vue │ │ ├── Share │ │ │ └── dropdownMenu.vue │ │ ├── SizeSelect │ │ │ └── index.vue │ │ ├── Sticky │ │ │ └── index.vue │ │ ├── SvgIcon │ │ │ └── index.vue │ │ ├── TextHoverEffect │ │ │ └── Mallki.vue │ │ ├── ThemePicker │ │ │ └── index.vue │ │ ├── Tinymce │ │ │ ├── components │ │ │ │ └── editorImage.vue │ │ │ ├── index.vue │ │ │ ├── plugins.js │ │ │ └── toolbar.js │ │ ├── TreeTable │ │ │ ├── eval.js │ │ │ ├── index.vue │ │ │ └── readme.md │ │ ├── Upload │ │ │ ├── singleImage.vue │ │ │ ├── singleImage2.vue │ │ │ └── singleImage3.vue │ │ └── UploadExcel │ │ │ └── index.vue │ ├── config │ │ └── index.js │ ├── directive │ │ ├── clipboard │ │ │ ├── clipboard.js │ │ │ └── index.js │ │ ├── customEval.js │ │ ├── el-dragDialog │ │ │ ├── drag.js │ │ │ └── index.js │ │ ├── index.js │ │ ├── permission │ │ │ ├── index.js │ │ │ └── permission.js │ │ ├── sticky.js │ │ └── waves │ │ │ ├── index.js │ │ │ ├── waves.css │ │ │ └── waves.js │ ├── filters │ │ └── index.js │ ├── icons │ │ ├── index.js │ │ ├── svg │ │ │ ├── 404.svg │ │ │ ├── bug.svg │ │ │ ├── chart.svg │ │ │ ├── clipboard.svg │ │ │ ├── component.svg │ │ │ ├── dashboard.svg │ │ │ ├── documentation.svg │ │ │ ├── drag.svg │ │ │ ├── edit.svg │ │ │ ├── email.svg │ │ │ ├── example.svg │ │ │ ├── excel.svg │ │ │ ├── eye.svg │ │ │ ├── form.svg │ │ │ ├── guide 2.svg │ │ │ ├── guide.svg │ │ │ ├── icon.svg │ │ │ ├── international.svg │ │ │ ├── language.svg │ │ │ ├── link.svg │ │ │ ├── list.svg │ │ │ ├── lock.svg │ │ │ ├── message.svg │ │ │ ├── money.svg │ │ │ ├── nested.svg │ │ │ ├── password.svg │ │ │ ├── people.svg │ │ │ ├── peoples.svg │ │ │ ├── qq.svg │ │ │ ├── shopping.svg │ │ │ ├── size.svg │ │ │ ├── star.svg │ │ │ ├── tab.svg │ │ │ ├── table.svg │ │ │ ├── theme.svg │ │ │ ├── tree.svg │ │ │ ├── user.svg │ │ │ ├── wechat.svg │ │ │ └── zip.svg │ │ └── svgo.yml │ ├── lang │ │ ├── en.js │ │ ├── index.js │ │ └── zh.js │ ├── main.js │ ├── mock │ │ ├── index.js │ │ ├── login.js │ │ ├── remoteSearch.js │ │ └── transaction.js │ ├── plugin │ │ ├── error-store │ │ │ └── index.js │ │ └── index.js │ ├── router │ │ ├── index.js │ │ ├── modules │ │ │ ├── charts.js │ │ │ ├── components.js │ │ │ ├── nested.js │ │ │ └── table.js │ │ └── routers.js │ ├── store │ │ ├── getters.js │ │ ├── index.js │ │ └── modules │ │ │ ├── app.js │ │ │ ├── errorLog.js │ │ │ ├── permission.js │ │ │ ├── tagsView.js │ │ │ └── user.js │ ├── styles │ │ ├── btn.scss │ │ ├── element-ui.scss │ │ ├── index.scss │ │ ├── mixin.scss │ │ ├── sidebar.scss │ │ ├── transition.scss │ │ └── variables.scss │ ├── utils │ │ ├── auth.js │ │ ├── clipboard.js │ │ ├── createUniqueString.js │ │ ├── i18n.js │ │ ├── index.js │ │ ├── openWindow.js │ │ ├── permission.js │ │ ├── request.js │ │ ├── requireIcons.js │ │ ├── scrollTo.js │ │ └── validate.js │ ├── vendor │ │ ├── Export2Excel.js │ │ └── Export2Zip.js │ └── views │ │ ├── dashboard │ │ └── index.vue │ │ ├── errorPage │ │ ├── 401.vue │ │ └── 404.vue │ │ ├── install │ │ ├── index.vue │ │ └── install.block.js │ │ ├── layout │ │ ├── Layout.vue │ │ ├── components │ │ │ ├── AppMain.vue │ │ │ ├── Navbar.vue │ │ │ ├── Sidebar │ │ │ │ ├── FixiOSBug.js │ │ │ │ ├── Item.vue │ │ │ │ ├── Link.vue │ │ │ │ ├── SidebarItem.vue │ │ │ │ └── index.vue │ │ │ ├── TagsView.vue │ │ │ └── index.js │ │ └── mixin │ │ │ ├── PreCheck.js │ │ │ ├── PwdChangeCheck.js │ │ │ └── ResizeHandler.js │ │ ├── login │ │ ├── authredirect.vue │ │ ├── index.vue │ │ └── socialsignin.vue │ │ ├── logs │ │ ├── error.vue │ │ ├── login.vue │ │ └── operation.vue │ │ ├── my │ │ ├── index.vue │ │ ├── personal.block.js │ │ ├── personal.vue │ │ ├── psw.vue │ │ ├── security.vue │ │ └── third.vue │ │ ├── permission │ │ ├── dept.vue │ │ ├── deptrw.vue │ │ ├── member.vue │ │ ├── role.vue │ │ └── user.vue │ │ ├── redirect │ │ └── index.vue │ │ ├── setting │ │ ├── auth.block.js │ │ ├── auth.vue │ │ ├── email.block.js │ │ └── email.vue │ │ └── system │ │ ├── dataPerm.vue │ │ ├── domain.vue │ │ ├── menu.vue │ │ └── params.vue │ ├── static │ ├── images │ │ ├── wave-back.png │ │ ├── wave-front.png │ │ └── wave-middle.png │ └── tinymce4.7.5 │ │ ├── langs │ │ └── zh_CN.js │ │ ├── plugins │ │ ├── codesample │ │ │ └── css │ │ │ │ └── prism.css │ │ ├── emoticons │ │ │ └── img │ │ │ │ ├── smiley-cool.gif │ │ │ │ ├── smiley-cry.gif │ │ │ │ ├── smiley-embarassed.gif │ │ │ │ ├── smiley-foot-in-mouth.gif │ │ │ │ ├── smiley-frown.gif │ │ │ │ ├── smiley-innocent.gif │ │ │ │ ├── smiley-kiss.gif │ │ │ │ ├── smiley-laughing.gif │ │ │ │ ├── smiley-money-mouth.gif │ │ │ │ ├── smiley-sealed.gif │ │ │ │ ├── smiley-smile.gif │ │ │ │ ├── smiley-surprised.gif │ │ │ │ ├── smiley-tongue-out.gif │ │ │ │ ├── smiley-undecided.gif │ │ │ │ ├── smiley-wink.gif │ │ │ │ └── smiley-yell.gif │ │ └── visualblocks │ │ │ └── css │ │ │ └── visualblocks.css │ │ ├── skins │ │ └── lightgray │ │ │ ├── content.inline.min.css │ │ │ ├── content.min.css │ │ │ ├── fonts │ │ │ ├── tinymce-mobile.woff │ │ │ ├── tinymce-small.eot │ │ │ ├── tinymce-small.svg │ │ │ ├── tinymce-small.ttf │ │ │ ├── tinymce-small.woff │ │ │ ├── tinymce.eot │ │ │ ├── tinymce.svg │ │ │ ├── tinymce.ttf │ │ │ └── tinymce.woff │ │ │ ├── img │ │ │ ├── anchor.gif │ │ │ ├── loader.gif │ │ │ ├── object.gif │ │ │ └── trans.gif │ │ │ ├── skin.min.css │ │ │ └── skin.min.css.map │ │ └── tinymce.min.js │ └── yarn.lock └── scripts ├── apppkg.sh ├── build ├── build.sh ├── docker ├── docker.sh ├── getlatest.sh ├── init.sql ├── pack.sh ├── run.sh ├── sdk ├── go │ ├── README.md │ ├── perm.go │ ├── perm_test.go │ └── pub_key.pub ├── java │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── cn │ │ │ └── bullteam │ │ │ └── PermCenter.java │ │ └── test │ │ └── java │ │ └── Test.java ├── php │ ├── .gitignore │ ├── README.md │ ├── composer.json │ └── src │ │ └── Auth │ │ ├── JWToken.php │ │ └── PermCenter.php └── python │ ├── README.md │ ├── app.py │ ├── permission_center.py │ └── requirements.txt ├── tag.sh ├── ui ├── ui.sh ├── upx ├── linux │ └── upx └── windows │ └── upx └── version.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | .idea/ 14 | .vscode/ 15 | cmd/api/zeus 16 | zeus 17 | keys/ 18 | .company_sns_access_token_file 19 | pkg/webui/node_modules/ 20 | pkg/webui/.vscode/settings.json 21 | pkg/webui/package-lock.json 22 | pkg/webui/dist/ 23 | data/ 24 | config/in-local.yaml 25 | build/ 26 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/golangci/golangci/wiki/Configuration 2 | 3 | service: 4 | prepare: 5 | - go get -t ./... 6 | linters: 7 | enable: 8 | - gofmt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - master 4 | os: 5 | - linux 6 | - osx 7 | dist: trusty 8 | sudo: false 9 | install: true 10 | env: 11 | - GO111MODULE=on 12 | script: 13 | - go build -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.inferGopath": false 3 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | #go build --ldflags "-extldflags -static" -o zeus 3 | COPY zeus /zeus 4 | COPY config /config 5 | COPY keys /keys 6 | COPY data /data 7 | COPY pkg/webui/dist /pkg/webui/dist 8 | WORKDIR / 9 | ENTRYPOINT ["/zeus", "server","--config=./config/in-cluster.yaml","--port=80","--cors=false"] -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/README-EN.md -------------------------------------------------------------------------------- /cmd/cobra.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "os" 6 | "zeus/cmd/api" 7 | "zeus/cmd/migrate" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "zeus", 12 | Short: "zeus API server", 13 | SilenceUsage: true, 14 | DisableAutoGenTag: true, 15 | Long: `Start zeus API server`, 16 | PersistentPreRunE: func(*cobra.Command, []string) error { return nil }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(api.StartCmd) 21 | rootCmd.AddCommand(migrate.MigrateCmd) 22 | } 23 | 24 | //Execute : run commands 25 | func Execute() { 26 | if err := rootCmd.Execute(); err != nil { 27 | os.Exit(-1) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /config/casbin/rbac_model_0.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act, domain 3 | 4 | [policy_definition] 5 | p = sub, obj, act, domain 6 | 7 | [role_definition] 8 | g = _, _ 9 | g2 = _, _ 10 | 11 | [policy_effect] 12 | e = some(where (p.eft == allow)) 13 | 14 | [matchers] 15 | m = (g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act && r.domain == p.domain) || (g2(r.obj, p.sub) && r.act == p.act && r.domain == p.domain) -------------------------------------------------------------------------------- /config/locale/locale_zh-CN.ini: -------------------------------------------------------------------------------- 1 | [ok] 2 | ActionSuccess=操作成功 3 | UpdateDone=更新成功 4 | DeletedDone=删除成功 5 | [err] 6 | Err404 =页面没有找到 7 | Err403 =您没有相关权限 8 | ErrInstall =安装失败 9 | ErrInputData=数据输入错误 10 | ErrDatabase=服务器错误 11 | ErrDupUser=用户信息已存在 12 | ErrNoUser=用户信息不存在 13 | ErrPass=用户信息不存在或密码不正确 14 | ErrDifferentPasswords=输入的密码不一致 15 | ErrSamePasswords=请输入与旧密码不一样的密码 16 | ErrNoUserPass=用户信息不存在或密码不正确 17 | ErrNoUserChange=用户信息不存在或数据未改变 18 | ErrInvalidUser=用户信息不正确 19 | ErrOpenFile=服务器错误 20 | ErrWriteFile=写文件出错 21 | ErrSystem=操作系统错误 22 | ErrExpired=登录已过期 23 | ErrPermission=没有权限 24 | ErrGenJwt=获取令牌失败 25 | ErrChkJwt=无效的令牌 26 | ErrIdData=此ID无数据记录 27 | ErrAddFail=创建失败 28 | ErrEditFail=更新失败 29 | ErrDelFail=删除失败 30 | ErrInvalidParams=验证失败 31 | ErrRoleAssignFail=权限分配失败 32 | ErrMenuData=请传递菜单ids 33 | ErrCaptchaEmpty=验证码不能为空 34 | ErrCaptcha=验证码错误 35 | ErrDeptDel=部门无法删除 36 | ErrDeptHasMember=部门不可删除 37 | ErrDupRecord=记录已存在 38 | ErrWrongRefreshToken =无效的refresh令牌 39 | ErrBindDingtalk=绑定钉钉失败 40 | ErrUnBindDingtalk=解除绑定钉钉失败 41 | ErrGoogleBindCode=无效验证码 42 | ErrSendMail=发送邮件失败 43 | ErrValidate=请求参数验证失败 44 | ErrRoleExists=角色已存在 45 | ErrDuplicated=记录已存在 46 | ErrNoRecord=记录不存在 47 | ErrHasSubRecord=子节点不为空 48 | ErrUploadAvatar=上传头像失败 49 | ErrLogin=账号名或密码不正确 50 | ErrSendCode=验证码发送失败 -------------------------------------------------------------------------------- /covprofile: -------------------------------------------------------------------------------- 1 | mode: set 2 | -------------------------------------------------------------------------------- /docs/en/CONTRIBUTING.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/en/CONTRIBUTING.md -------------------------------------------------------------------------------- /docs/images/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/arch.png -------------------------------------------------------------------------------- /docs/images/dber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/dber.png -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/screenshot1.png -------------------------------------------------------------------------------- /docs/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/screenshot2.png -------------------------------------------------------------------------------- /docs/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/screenshot3.png -------------------------------------------------------------------------------- /docs/images/wechatqun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/wechatqun.jpg -------------------------------------------------------------------------------- /docs/images/wx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/docs/images/wx.jpg -------------------------------------------------------------------------------- /docs/zh/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | -------------------------------------------------------------------------------- /docs/zh/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### 从官方仓库 fork 代码 2 | 3 | 1. 浏览器访问 https://zeus 4 | 2. 点击 "Fork" 按钮 (位于页面的右上方) 5 | 6 | ### 从你自己的仓库 Clone 代码 7 | 8 | ```bash 9 | cd $NAFTIS 10 | git clone https://github.com/$YOUR_GITHUB_ACCOUNT/zeus 11 | cd naftis 12 | git remote add upstream 'https://zeus' 13 | git config --global --add http.followRedirects 1 14 | ``` 15 | 16 | ### 创建分支并修改代码 17 | 18 | ```bash 19 | git checkout -b my-feature # 创建一个 my-feature 分支 20 | # 修改代码,加入你自己的变更 21 | ``` 22 | 23 | ### 让你 fork 仓库和官方仓库同步 24 | 25 | ```bash 26 | git fetch upstream 27 | git rebase upstream/master 28 | ``` 29 | 30 | ### 向你 fork 仓库提交 commits 31 | 32 | ```bash 33 | git add . 34 | git commit 35 | git push origin my-feature # 推送 my-featur 到你自己的仓库 36 | ``` 37 | ### 提交 PR 38 | 39 | ```bash 40 | 你可以访问 https://github.com/$YOUR_GITHUB_ACCOUNT/zeus 或者 https://zeus 来浏览你的分支 (比如 "my-feature")。 41 | 42 | 点击 "Compare" 按钮来比较变更, 然后点击你的 "my-feature" 分支旁边的 "Pull request" 按钮来提交 PR。 43 | ``` 44 | 45 | ### Review 代码 46 | 47 | 一个 PR 必须至少有一个人 review,review 无误后由 admin 合并至 master 分支。 48 | 49 | ## 代码结构 50 | 51 | ```bash 52 | 53 | ``` 54 | ## 代码风格 55 | - [Go: CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments) -------------------------------------------------------------------------------- /docs/zh/GenrsaKey.md: -------------------------------------------------------------------------------- 1 | # 生成公钥和密钥 2 | 1. 打开终端`Terminal`或者`iTerm`输入以下命令生成私钥: 3 | 4 | ``` 5 | openssl genrsa -out rsa_private_key.pem 1024 6 | ``` 7 | 2. 把`RSA`私钥转换成`PKCS8`格式 8 | 9 | ``` 10 | openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt 11 | 12 | ``` 13 | 注意把控制台输出的私钥内容替换到私钥文件(`rsa_private_key.pem`)中去。 14 | 15 | 3. 生成公钥 16 | 17 | ``` 18 | openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout 19 | 20 | ``` 21 | 4. 将生成的 `rsa_private_key.pem`,`rsa_public_key.pem` 替换 `cmd/api-server/keys` 22 | -------------------------------------------------------------------------------- /docs/zh/Roadmap.md: -------------------------------------------------------------------------------- 1 | # roadmap 2 | ## 1.0 3 | - 项目基于 VUE + Golang + gin + casbin + gorm 开发 4 | - 功能包括: 5 | - 用户管理 6 | - 角色管理 7 | - 部门管理 8 | - 项目管理 9 | - 菜单管理 10 | - 数据权限 11 | - 个人中心(支持钉钉扫码登陆、Google 两步验证) 12 | 13 | ## 1.1 14 | - 支持 Ldap 15 | - 在线安装功能 16 | - docker 安装 17 | - 安装和使用文档 18 | 19 | ## 1.2 20 | - 配置开关(Ldap后台配置、钉钉、微信等密钥配置) 21 | - 组织架构调整支持钉钉组织架构同步 22 | - 项目管理升级为应用管理(支持OAuth2.0),白名单,自由化设置 23 | ## 1.3 24 | - 登录日志 25 | - 操作日志 26 | - 安全防范(验证码) 27 | ## 1.4 28 | - 支持企业微信登录 29 | ## 1.5 30 | - 支持企业微信组织架构同步 31 | ## 1.6 32 | - 支持普通微信登录 33 | - 支持QQ登陆 34 | ## 1.7 35 | - 支持github登录 36 | - 支持OpenId 37 | - 支持Facebook登录 38 | - 支持Google登录 39 | - 支持twitter登录 40 | ## 1.8 41 | - 支持小程序登录 42 | - 支持钉钉应用登录 43 | 44 | ## 2.0 45 | - 自己 APP 的扫码登录 46 | - geetest 验证码 47 | - vaptcha 验证码 48 | - 多语言 49 | - 多因素认证 50 | - 人脸识别登录(旷视,腾讯AI,阿里云AI) 51 | -------------------------------------------------------------------------------- /docs/zh/Swag.md: -------------------------------------------------------------------------------- 1 | # 如何使用SWaggo 开发 2 | 3 | ## 安装SWAG 4 | 5 | ``` 6 | $ go get -u github.com/swaggo/swag/cmd/swag 7 | ``` 8 | 国内网络如果无法安装,请使用代理。 9 | ``` 10 | export GOPROXY=https://goproxy.io 11 | ``` 12 | 若 `$GOPATH/bin` 没有加入`$PATH`中,你需要执行将其可执行文件移动到`$GOBIN`下 13 | 14 | ``` 15 | mv $GOPATH/bin/swag /usr/local/go/bin 16 | ``` 17 | 18 | ## 验证是否安装成功 19 | 20 | ``` 21 | $ swag -v 22 | swag version v1.1.1 23 | ``` 24 | 25 | ## 生成 26 | 27 | ``` 28 | swag init 29 | ``` 30 | ``` 31 | [root@go-dev zeus-admin]# /root/go/bin/swag init 32 | 2019/05/28 00:21:53 Generate swagger docs.... 33 | 2019/05/28 00:21:53 Generate general API Info 34 | 2019/05/28 00:21:53 create docs.go at docs/docs.go 35 | 2019/05/28 00:21:53 create swagger.json at docs/swagger.json 36 | 2019/05/28 00:21:53 create swagger.yaml at docs/swagger.yaml 37 | ``` 38 | 完毕后会在项目根目录下生成`docs` 39 | ``` 40 | docs/ 41 | ├── docs.go 42 | └── swagger 43 | ├── swagger.json 44 | └── swagger.yaml 45 | ``` 46 | 我们可以检查 `docs.go` 文件中的 `doc` 变量,详细记载中我们文件中所编写的注解和说明 47 | 48 | ## 验证访问 49 | 50 | ``` 51 | http://127.0.0.1/swagger/index.html 52 | http://api.bullteam.cn/swagger/index.html 53 | ``` -------------------------------------------------------------------------------- /keys/jwt_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWwIBAAKBgQC/TYKuXsgYdoICfEZOiy1L12CbyPdudhrCjrjwVcIrhGNn6Udq 3 | /SY5rh0ixm09I2tXPWLYuA1R55kyeo5RPFX+FrD+mQwfJkV/QfhaPsNjU4nCEHFM 4 | trsYCcLYJs9uX0tJdAtE6sg/VSulg1aMqCNWvtVtjrrVXSbu4zbyWzVkxQIDAQAB 5 | AoGAIJrzZQjejdzU99t6mDR8eeqxmpu8IGWc1gBBYSUcvRIJZ1KJS6Dt/PLCIIU1 6 | ZTA+QVZDHLDyBD23DLV6wDnKZgKQnTQSqfPeanT5Zomc96QUmtQBqZ5m/3P4LXTV 7 | HlYZPYeKlOCvL6fUtrb8o9sx2jk1T+d1da4CfmOffI/ZQ8ECQQDuFP+pz3m+DnMk 8 | yozNunRr8XcCk460A7lmHoCOg6l+jPbeXGEgQCBkzdgeEFWj1S8dAjyIETNGE7UI 9 | gUI5o8ydAkEAzbM9k/p08fJr+Sd8+WlYfT9NeDJpcJLXQp0FekJv9bNgRaYr0Wfj 10 | vW7XWV8VyephgAIC8S38CNyUPgqerrR8SQJAJ+9rxx8fK6se01AKeEPLXYPeU5dO 11 | u5FYWvHI3J7nImwgyMG0JQW8qUwB8WEKDHYo9fO3FZfVAu8xUaDk6+g23QJAUJuW 12 | 2/Bf95g6O67/yHVB2gL+hsWqkBTbCh2iUeDLIwuiBGkz7qG5mzheZ4VdcnzIrHMd 13 | WAnfJFHcPdvHh0rvEQJAVuiKX8+EPCpdymbmrK3dH3E2WHjozSksrJT0v6NdFnqY 14 | zeJFUH2EGnbk4M5+33zM8t3f53aGdgdVTA9VOonaow== 15 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /keys/jwt_public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/TYKuXsgYdoICfEZOiy1L12Cb 3 | yPdudhrCjrjwVcIrhGNn6Udq/SY5rh0ixm09I2tXPWLYuA1R55kyeo5RPFX+FrD+ 4 | mQwfJkV/QfhaPsNjU4nCEHFMtrsYCcLYJs9uX0tJdAtE6sg/VSulg1aMqCNWvtVt 5 | jrrVXSbu4zbyWzVkxQIDAQAB 6 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "zeus/cmd" 5 | ) 6 | 7 | // @title Zeus 宙斯权限后台 API 8 | // @version V0.1 9 | // @description Zeus 宙斯权限后台 10 | // @termsOfService http://swagger.io/terms/ 11 | // @contact.name API Support 12 | // @contact.email support@bullteam.cn 13 | // @license.name Apache 2.0 14 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 15 | // @host 127.0.0.1:8012 16 | // @BasePath /v1 17 | // @securityDefinitions.apikey ApiKeyAuth 18 | // @in header 19 | // @name Authorization 20 | func main() { 21 | cmd.Execute() 22 | //dao.Shutdown() 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/cache/init.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "time" 4 | 5 | var adapter Adapter 6 | 7 | type Adapter interface { 8 | Connect() 9 | Get(key string) (string, error) 10 | Set(key string, val string, expire int) error 11 | Del(key string) error 12 | HashGet(hk, key string) (string, error) 13 | HashDel(hk, key string) error 14 | Increase(key string) error 15 | Expire(key string, dur time.Duration) error 16 | } 17 | 18 | func SetUp() { 19 | adapter = &Redis{} 20 | adapter.Connect() 21 | } 22 | 23 | // Set val in cache 24 | func Set(key, val string, expire int) error { 25 | return adapter.Set(key, val, expire) 26 | } 27 | 28 | // Get val in cache 29 | func Get(key string) (string, error) { 30 | return adapter.Get(key) 31 | } 32 | 33 | // Del delete key in cache 34 | func Del(key string) error { 35 | return adapter.Del(key) 36 | } 37 | 38 | // HashGet get val in hashtable cache 39 | func HashGet(hk, key string) (string, error) { 40 | return adapter.HashGet(hk, key) 41 | } 42 | 43 | // HashDel delete one key:value pair in hashtable cache 44 | func HashDel(hk, key string) error { 45 | return adapter.HashDel(hk, key) 46 | } 47 | 48 | // Increase value 49 | func Increase(key string) error { 50 | return adapter.Increase(key) 51 | } 52 | 53 | func Expire(key string, dur time.Duration) error { 54 | return adapter.Expire(key, dur) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/api/controllers/auth.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | jwt "github.com/appleboy/gin-jwt/v2" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | var jwtAuth *jwt.GinJWTMiddleware 9 | var jwtAuths *jwt.GinJWTMiddleware 10 | 11 | type AuthController struct { 12 | BaseController 13 | } 14 | 15 | // @Tags Users 16 | // @Summary 用户登陆 17 | // @Accept json 18 | // @Produce json 19 | // @Param username formData string true "登录名" 20 | // @Param password formData string true "密码" 21 | // @Param captchaid formData string false "验证码ID" 22 | // @Param captchaval formData string false "验证码" 23 | // @Success 200 {array} model.User "{"code":200,"data":{"id":1,"name":"wutong"}}" 24 | // @Failure 400 {string} json "{"code":10004,"msg": "用户信息不存在"}" 25 | // @Router /v1/users/login [post] 26 | func (u *AuthController) JwtAuthLogin(c *gin.Context) { 27 | jwtAuth.LoginHandler(c) 28 | } 29 | 30 | // @Tags Users 31 | // @Summary 用户refresh-token接口 32 | // @Accept json 33 | // @Produce json 34 | // @Accept multipart/form-data 35 | // @Produce json 36 | // @Success 200 {array} model.User "{"code":200,"data":{"id":1,"name":"wutong"}}" 37 | // @Failure 400 {string} json "{"code":10004,"msg": "用户信息不存在"}" 38 | // @Router /v1/users/login/refresh [post] 39 | func (u *AuthController) JwtAuthRefreshLogin(c *gin.Context) { 40 | jwtAuth.RefreshHandler(c) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/api/controllers/install.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "zeus/pkg/api/dto" 6 | "zeus/pkg/api/service" 7 | ) 8 | 9 | type InstallController struct { 10 | BaseController 11 | } 12 | 13 | var installService = service.InstallService{} 14 | 15 | func (i *InstallController) Install(c *gin.Context) { 16 | var InstallDTO dto.InstallDTO 17 | if i.BindAndValidate(c, &InstallDTO) { 18 | ret := installService.Install(InstallDTO) 19 | if !ret { 20 | fail(c, ErrInstall) 21 | return 22 | } 23 | } 24 | resp(c, map[string]interface{}{ 25 | "result": InstallDTO, 26 | }) 27 | } 28 | 29 | func (i *InstallController) IsLock(c *gin.Context) { 30 | isLock := installService.Islock() 31 | resp(c, map[string]interface{}{ 32 | "result": isLock, 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/controllers/probe.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | // @Tags Health 8 | // @Summary 健康检查 9 | // @Produce json 10 | // @Success 200 {string} json "{"code":200,"data":{""}}" 11 | // @Router /healthcheck [get] 12 | func Healthy(c *gin.Context) { 13 | //resp(c, gin.H{ 14 | // "data": true, 15 | //}) 16 | fail(c, Err404) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/api/controllers/sync.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "zeus/pkg/api/service" 6 | ) 7 | 8 | type SyncController struct { 9 | BaseController 10 | } 11 | 12 | var dingTalkService = service.DingTalkService{} 13 | 14 | // @Tags Sync 15 | // @Summary 钉钉组织架构+用户同步 16 | // @Security ApiKeyAuth 17 | // @Produce json 18 | // @Success 200 {string} json "{"code":200,"data":""} 19 | // @Router /v1/sync/dingtalk [post] 20 | func (s *SyncController) SyncDingTalk(c *gin.Context) { 21 | dingTalkService.SyncUsersAndDepartments() 22 | ok(c, "ok.ActionSuccess") 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/dao/data_perm.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "zeus/pkg/api/domain/search/parser" 6 | "zeus/pkg/api/dto" 7 | "zeus/pkg/api/model" 8 | ) 9 | 10 | type DataPerm struct{} 11 | 12 | // List 13 | func (dp DataPerm) List(listDto dto.GeneralListDto) ([]model.DataPerm, int64) { 14 | var dataPerms []model.DataPerm 15 | var total int64 16 | db := GetDb() 17 | ps, err := parser.Parse(listDto.Q) 18 | if err == nil { 19 | for _, sv := range searchAdapter.GenerateConditions(ps, dto.DataPermListSearchMapping) { 20 | k := sv[0].(string) 21 | db = db.Where(k, sv[1:]...) 22 | } 23 | } 24 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&dataPerms) 25 | db.Model(&model.DataPerm{}).Count(&total) 26 | return dataPerms, total 27 | } 28 | 29 | //Get 30 | func (dp DataPerm) Get(id int) model.DataPerm { 31 | var dataPerm model.DataPerm 32 | db := GetDb() 33 | db.Where("id = ?", id).First(&dataPerm) 34 | return dataPerm 35 | } 36 | 37 | // Create 38 | func (dp DataPerm) Create(dataPerm *model.DataPerm) *gorm.DB { 39 | db := GetDb() 40 | return db.Save(dataPerm) 41 | } 42 | 43 | // Update 44 | func (dp DataPerm) Update(dataPerm *model.DataPerm) *gorm.DB { 45 | db := GetDb() 46 | return db.Save(dataPerm) 47 | } 48 | 49 | // Create 50 | func (dp DataPerm) Delete(dataPerm *model.DataPerm) *gorm.DB { 51 | db := GetDb() 52 | return db.Delete(dataPerm) 53 | } 54 | 55 | func (dp DataPerm) GetDataPermsByRoute(route string) []model.DataPerm { 56 | data, _ := dp.List(dto.GeneralListDto{ 57 | Q: "r=" + route, 58 | Limit: 9999999, 59 | }) 60 | return data 61 | } 62 | -------------------------------------------------------------------------------- /pkg/api/dao/menu_perm_alias.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import "zeus/pkg/api/model" 4 | 5 | type MenuPermAlias struct { 6 | } 7 | 8 | // GetByAlias - get single row of alias 9 | func (MenuPermAlias) GetByAlias(alias string, domainId int) model.MenuPermAlias { 10 | var mpa model.MenuPermAlias 11 | db := GetDb() 12 | db.Where("alias = ? and domain_id = ?", alias, domainId).First(&mpa) 13 | return mpa 14 | } 15 | 16 | // GetByPerms - get all aliases rows of specific permission code 17 | func (MenuPermAlias) GetByPerms(perms string) []model.MenuPermAlias { 18 | var mpa []model.MenuPermAlias 19 | db := GetDb() 20 | db.Where("perms = ?", perms).Find(&mpa) 21 | return mpa 22 | } 23 | 24 | // Delete - delete of crud 25 | func (MenuPermAlias) Delete(menuPermAlias model.MenuPermAlias) { 26 | db := GetDb() 27 | db.Where("perms = ? and domain_id = ?", menuPermAlias.Perms, menuPermAlias.DomainId).Delete(menuPermAlias) 28 | } 29 | 30 | // Create - create of crud 31 | func (MenuPermAlias) Create(menuPermAlias *model.MenuPermAlias) { 32 | db := GetDb() 33 | db.Create(menuPermAlias) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/dao/user_oauth.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jinzhu/gorm" 6 | "zeus/pkg/api/dto" 7 | "zeus/pkg/api/model" 8 | ) 9 | 10 | type UserOAuthDao struct { 11 | } 12 | 13 | // List - userOAuth list 14 | func (u UserOAuthDao) List(listDto dto.GeneralListDto) ([]model.UserOAuth, int64) { 15 | var UserOAuth []model.UserOAuth 16 | var total int64 17 | db := GetDb() 18 | for sk, sv := range dto.TransformSearch(listDto.Q, dto.UserListSearchMapping) { 19 | db = db.Where(fmt.Sprintf("%s = ?", sk), sv) 20 | } 21 | db.Offset(listDto.Skip).Limit(listDto.Limit).Find(&UserOAuth) 22 | db.Model(&model.UserOAuth{}).Count(&total) 23 | return UserOAuth, total 24 | } 25 | 26 | func (u UserOAuthDao) Get(id int) model.UserOAuth { 27 | var userOAuth model.UserOAuth 28 | db.Where("id = ?", id).First(&userOAuth) 29 | return userOAuth 30 | } 31 | 32 | func (u UserOAuthDao) Create(UserOAuth *model.UserOAuth) *gorm.DB { 33 | db := GetDb() 34 | return db.Save(UserOAuth) 35 | } 36 | 37 | func (u UserOAuthDao) Delete(UserOAuth *model.UserOAuth) *gorm.DB { 38 | db := GetDb() 39 | return db.Delete(UserOAuth) 40 | } 41 | 42 | func (dao *UserOAuthDao) GetUserByOpenId(openid string, from int) (model.UserOAuth, error) { 43 | var userOAuth model.UserOAuth 44 | db.Where("openid = ? and `from` = ?", openid, from).Find(&userOAuth) 45 | return userOAuth, nil 46 | } 47 | 48 | func (dao *UserOAuthDao) DeleteByUseridAndFrom(from int, user_id int) error { 49 | db := GetDb() 50 | var userOAuth model.UserOAuth 51 | db.Where("`from` = ? and user_id = ?", from, user_id).Delete(&userOAuth) 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/api/dao/user_secret.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "github.com/jinzhu/gorm" 5 | "zeus/pkg/api/model" 6 | ) 7 | 8 | type UserSecretDao struct{} 9 | 10 | func (u UserSecretDao) Get(uid int) model.UserSecret { 11 | var userSecret model.UserSecret 12 | db.Where("user_id = ?", uid).First(&userSecret) 13 | return userSecret 14 | } 15 | 16 | func (u UserSecretDao) Create(UserSecret *model.UserSecret) *gorm.DB { 17 | db := GetDb() 18 | return db.Save(UserSecret) 19 | } 20 | 21 | // Update - update UserSecret 22 | func (u UserSecretDao) Update(UserSecret *model.UserSecret, ups map[string]interface{}) *gorm.DB { 23 | db := GetDb() 24 | return db.Model(UserSecret).Update(ups) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/domain/account/account.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "golang.org/x/crypto/scrypt" 7 | "io" 8 | ) 9 | 10 | const pwHashBytes = 64 11 | 12 | //login type 13 | var ( 14 | LoginStandard = 1 15 | LoginOAuth = 2 16 | LoginLdap = 3 17 | ) 18 | 19 | //login oauth type 20 | const ( 21 | OAuthDingTalk = iota 22 | //todo : ... 23 | OAuthWechat 24 | OAuthQQ 25 | OAuthFacebook 26 | OAuthGoogle 27 | ) 28 | 29 | // HashPassword : password hashing 30 | func HashPassword(password string, salt string) (hash string, err error) { 31 | h, err := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, pwHashBytes) 32 | if err != nil { 33 | return "", err 34 | } 35 | return fmt.Sprintf("%x", h), nil 36 | } 37 | 38 | // MakeSalt : make password more complicated 39 | func MakeSalt() (salt string, err error) { 40 | buf := make([]byte, pwHashBytes) 41 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 42 | return "", err 43 | } 44 | return fmt.Sprintf("%x", buf), nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/api/domain/account/account_test.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | var ( 9 | salt = "" 10 | pwd = "zeus" 11 | err error 12 | ) 13 | 14 | func TestMakeSalt(t *testing.T) { 15 | salt, err = MakeSalt() 16 | assert.Equal(t, nil, err) 17 | } 18 | func TestHashPassword(t *testing.T) { 19 | pwd, err = HashPassword(pwd, salt) 20 | assert.Equal(t, nil, err) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/api/domain/account/ldap/init.go: -------------------------------------------------------------------------------- 1 | package ldap 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | "zeus/pkg/api/log" 6 | ) 7 | 8 | var ldapConn LdapConfig 9 | 10 | func Setup() { 11 | ldapConn = LdapConfig{ 12 | Addr: viper.GetString("ldap.addr"), 13 | BaseDn: viper.GetString("ldap.baseDn"), 14 | UserDn: viper.GetString("ldap.userDn"), 15 | BindDn: viper.GetString("ldap.bindDn"), 16 | BindPass: viper.GetString("ldap.bindPass"), 17 | AuthFilter: viper.GetString("ldap.authFilter"), 18 | Attributes: viper.GetStringSlice("ldap.attributes"), 19 | TLS: viper.GetBool("ldap.tls"), 20 | StartTLS: viper.GetBool("ldap.startTLS"), 21 | } 22 | log.Info("Successfully init ldap config") 23 | } 24 | 25 | func ConnectLdap() { 26 | e := ldapConn.Connect() 27 | if e != nil { 28 | log.Fatal("ldap connect fail!") 29 | } 30 | } 31 | 32 | func GetLdap() LdapConfig { 33 | ConnectLdap() 34 | return ldapConn 35 | } 36 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/2fa_handler_test.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | //var handler = &SmsSendChebao{ 4 | // RequestApi: "http://sms-send-api", 5 | // PrivateKey: "*****", 6 | //} 7 | // 8 | //func TestTwoFaHandlerSms(t *testing.T) { 9 | // if err := handler.Send("135xxxxyyyy"); err != nil { 10 | // t.Error(err) 11 | // } else { 12 | // t.Log("send ok") 13 | // } 14 | //} 15 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/dingding.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | dingtalk "github.com/bullteam/go-dingtalk/src" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | type DingtalkUserInfo struct { 9 | Openid string 10 | Unionid string 11 | Nick string 12 | Dingid string 13 | } 14 | 15 | //GetDingTalkUserInfo - get dingdingtalk's userinfo by code 16 | func GetDingTalkUserInfo(code string) (UserInfo *DingtalkUserInfo, err error) { 17 | c := GetCompanyDingTalkClient() 18 | _ = c.RefreshSNSAccessToken() 19 | perInfo, err := c.SNSGetPersistentCode(code) 20 | if err != nil { 21 | return nil, err 22 | } 23 | snstoken, err := c.SNSGetSNSToken(perInfo.OpenID, perInfo.PersistentCode) 24 | if err != nil { 25 | return nil, err 26 | } 27 | dtUser, err := c.SNSGetUserInfo(snstoken.SnsToken) 28 | if err != nil { 29 | return nil, err 30 | } 31 | return &DingtalkUserInfo{ 32 | dtUser.UserInfo.OpenID, 33 | dtUser.UserInfo.UnionID, 34 | dtUser.UserInfo.Nick, 35 | dtUser.UserInfo.DingID, 36 | }, nil 37 | } 38 | 39 | func GetCompanyDingTalkClient() *dingtalk.DingTalkClient { 40 | return dingtalk.NewDingTalkCompanyClient(&dingtalk.DTConfig{ 41 | SNSAppID: viper.GetString("dingtalk.SNSAppID"), 42 | SNSSecret: viper.GetString("dingtalk.SNSSecret"), 43 | CachePath: viper.GetString("dingtalk.CachePath"), 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/dingding_test.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | dingtalk "github.com/icepy/go-dingtalk/src" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | var dingTalkClient *dingtalk.DingTalkClient 10 | 11 | func init() { 12 | //Overwrite it for tests 13 | dingTalkClient = dingtalk.NewDingTalkCompanyClient(&dingtalk.DTConfig{ 14 | SNSAppID: "dingoatdqa3hb5eta4tlvi", 15 | SNSSecret: "RHNFFTJXh-uxQ2YosdoBt8u0gqBGwWhe8J3dMwqf5gz41vHq9zHvc7D9WNZSDuJ0", 16 | }) 17 | } 18 | 19 | func TestGetCompanyDingTalkClient(t *testing.T) { 20 | assert.NotEqual(t, nil, dingTalkClient) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/general.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "zeus/pkg/api/domain/account" 5 | "zeus/pkg/api/model" 6 | ) 7 | 8 | // VerifyPassword : verify password by salt 9 | func VerifyPassword(password string, userModel model.User) bool { 10 | if pwd, err := account.HashPassword(password, userModel.Salt); err == nil && pwd == userModel.Password { 11 | return true 12 | } 13 | return false 14 | } 15 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/general_test.go: -------------------------------------------------------------------------------- 1 | package login 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | "zeus/pkg/api/domain/account" 7 | "zeus/pkg/api/model" 8 | ) 9 | 10 | type verifyCases struct { 11 | sourcePwd string 12 | targetPwd string 13 | salt string 14 | expected bool 15 | } 16 | 17 | func TestVerifyPassword(t *testing.T) { 18 | salt, _ := account.MakeSalt() 19 | pwd, _ := account.HashPassword("zeus", salt) 20 | for _, cs := range []verifyCases{{ 21 | "zeus", pwd, salt, true, 22 | }, { 23 | "zeus0", pwd, salt, false, 24 | }, { 25 | "zeus", pwd, "wrong", false, 26 | }, { 27 | "zeus", "wrong", salt, false, 28 | }, 29 | } { 30 | assert.Equal(t, cs.expected, VerifyPassword(cs.sourcePwd, model.User{ 31 | Password: cs.targetPwd, 32 | Salt: cs.salt, 33 | }), "Cases of password verification") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/api/domain/account/login/weixin.go: -------------------------------------------------------------------------------- 1 | package login 2 | -------------------------------------------------------------------------------- /pkg/api/domain/dept/sync/dingtalk.go: -------------------------------------------------------------------------------- 1 | package sync 2 | 3 | import ( 4 | dingtalk "github.com/bullteam/go-dingtalk/src" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | func GetDingTalkUserInfo() (deptInfos interface{}, err error) { 9 | c := GetCompanyDingTalkClient() 10 | _ = c.RefreshCompanyAccessToken() 11 | list, err := c.DepartmentList(1, "zh_CN") 12 | if err != nil { 13 | return nil, err 14 | } 15 | return list.Department, nil 16 | } 17 | 18 | func GetCompanyDingTalkClient() *dingtalk.DingTalkClient { 19 | return dingtalk.NewDingTalkCompanyClient(&dingtalk.DTConfig{ 20 | AppKey: viper.GetString("dingtalk.appkey"), 21 | AppSecret: viper.GetString("dingtalk.appsecret"), 22 | CachePath: viper.GetString("dingtalk.CachePath"), 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/domain/perm/adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2/model" 5 | "zeus/pkg/api/domain/perm/adapter/mysql" 6 | ) 7 | 8 | type Adapter interface { 9 | LoadPolicy(model model.Model) error 10 | SavePolicy(model model.Model) error 11 | AddPolicy(sec string, ptype string, rule []string) error 12 | RemovePolicy(sec string, ptype string, rule []string) error 13 | RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error 14 | } 15 | 16 | // NewMysqlAdapter : delegate of adapter 17 | func NewMysqlAdapter() *mysqlAdapter { 18 | ad := &mysqlAdapter{ 19 | a: mysql.NewGormAdapter(), 20 | } 21 | return ad 22 | } 23 | 24 | type mysqlAdapter struct { 25 | a Adapter 26 | } 27 | 28 | func (ca *mysqlAdapter) LoadPolicy(model model.Model) error { return ca.a.LoadPolicy(model) } 29 | func (ca *mysqlAdapter) SavePolicy(model model.Model) error { return ca.a.SavePolicy(model) } 30 | func (ca *mysqlAdapter) AddPolicy(sec string, ptype string, rule []string) error { 31 | return ca.a.AddPolicy(sec, ptype, rule) 32 | } 33 | func (ca *mysqlAdapter) RemovePolicy(sec string, ptype string, rule []string) error { 34 | return ca.a.RemovePolicy(sec, ptype, rule) 35 | } 36 | func (ca *mysqlAdapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { 37 | return ca.a.RemoveFilteredPolicy(sec, ptype, fieldIndex, fieldValues...) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/domain/perm/dataperm/data_perm.go: -------------------------------------------------------------------------------- 1 | package dataperm 2 | 3 | // todo : 4 | // make a data perm rule system! 5 | // sample: 6 | // #1|casbin_rule.v1 == @current.user.id , casbin_rule.v2 == role.name : select(domain_id) 7 | // condition : sql=select [@columns] from [@table] where id in(#1.domain_id) 8 | -------------------------------------------------------------------------------- /pkg/api/domain/perm/dataperm/data_perm_test.go: -------------------------------------------------------------------------------- 1 | package dataperm 2 | 3 | var statement = ` 4 | { 5 | "p": [ 6 | "casbin_rule:casbin_rule.v0=@account.id", 7 | "role:@casbin_rule.v1=role.role_name" 8 | ], 9 | "c": "id=(join[@role.domain_id,','])", 10 | "r": "domain" 11 | } 12 | ` 13 | -------------------------------------------------------------------------------- /pkg/api/domain/perm/perm_test.csv: -------------------------------------------------------------------------------- 1 | p,role-1,zone-1,manage-all-things,department-1 2 | p,role-1,zone-2,manage-some-stuff,department-1 3 | p,role-1,zone-3,can-not-do-anything,department-2 4 | p,role-2,zone-1,see-report,department-1 5 | p,role-2,zone-2,can-not-do-anything,department-3 6 | p,role-3,zone-3,*,department-4 7 | p,role-4,zone-4,*,department-4 8 | p,role-5,zone-5,*,department-5 9 | p,role-6,zone-6,*,department-6 10 | g,100,u4 11 | g,101,u5 12 | g,101,q5 13 | g2,u5,role-5 14 | g2,q5,role-6 15 | g,100,role-6 -------------------------------------------------------------------------------- /pkg/api/domain/perm/rbac_model_0.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act, domain 3 | 4 | [policy_definition] 5 | p = sub, obj, act, domain 6 | 7 | [role_definition] 8 | g = _, _ 9 | g2 = _, _ 10 | 11 | [policy_effect] 12 | e = some(where (p.eft == allow)) 13 | 14 | [matchers] 15 | m = (g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act && r.domain == p.domain) || (g2(r.obj, p.sub) && r.act == p.act && r.domain == p.domain) -------------------------------------------------------------------------------- /pkg/api/domain/role/role.go: -------------------------------------------------------------------------------- 1 | package role 2 | 3 | import ( 4 | "zeus/pkg/api/domain/perm" 5 | ) 6 | 7 | // CheckPerm : check permission by role with domain 8 | func CheckPerm(roleName, zone, action, domain string) (bool, error) { 9 | return perm.Enforce(roleName, zone, action, domain) 10 | } 11 | 12 | // DeletePermWithDomain : clear role permission with domain 13 | func DeletePermWithDomain(roleName, domain string) { 14 | perm.DelRoleByDomain(roleName, domain) 15 | } 16 | 17 | // DeletePerm : delete role in casbin policies 18 | func DeletePerm(roleName string) { 19 | perm.DelRole(roleName) 20 | } 21 | 22 | // DeletePermPolicy : delete role in casbin policies 23 | func DeletePermPolicy(roleName string) { 24 | perm.DeleteRolePolicy(roleName) 25 | } 26 | 27 | // OverwritePerm : overwrite permissions 28 | // remove or create policy 29 | func OverwritePerm(roleName, domainCode string, polices [][]string) { 30 | currentPerms := perm.GetAllPermsByRoleDomain(roleName, domainCode) 31 | for k1, newPerm := range polices { 32 | for k2, currentPerm := range currentPerms { 33 | if newPerm[0] == currentPerm[0] && 34 | newPerm[1] == currentPerm[1] && 35 | newPerm[2] == currentPerm[2] && 36 | newPerm[3] == currentPerm[3] { 37 | polices[k1] = []string{"-skip"} 38 | currentPerms[k2] = []string{"-skip"} 39 | } 40 | } 41 | } 42 | for _, newPerm := range polices { 43 | if newPerm[0] == "-skip" { 44 | continue 45 | } 46 | perm.AddPerm(newPerm) 47 | } 48 | for _, remPerm := range currentPerms { 49 | if remPerm[0] == "-skip" { 50 | continue 51 | } 52 | perm.DelPerm(remPerm) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pkg/api/domain/search/adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import "zeus/pkg/api/domain/search/parser" 4 | 5 | type SearchAdapter interface { 6 | GenerateConditions([]parser.ParsePair, map[string]string) [][]interface{} 7 | } 8 | -------------------------------------------------------------------------------- /pkg/api/domain/search/adapter/statement/sql.go: -------------------------------------------------------------------------------- 1 | package statement 2 | 3 | import ( 4 | "zeus/pkg/api/domain/search/lexer/token" 5 | "zeus/pkg/api/domain/search/parser" 6 | ) 7 | 8 | type SqlSearchAdapter struct{} 9 | 10 | func (msa *SqlSearchAdapter) GenerateConditions(statement []parser.ParsePair, keyMapping map[string]string) [][]interface{} { 11 | var stmt [][]interface{} 12 | for _, p := range statement { 13 | k := p.Key 14 | if _, ok := keyMapping[k]; ok { 15 | k = keyMapping[k] 16 | } 17 | switch p.St { 18 | case token.TOKEN_OP_TYPE_EQ: 19 | stmt = append(stmt, []interface{}{k + "=?", p.Value}) 20 | case token.TOKEN_OP_TYPE_LIKE: 21 | stmt = append(stmt, []interface{}{k + " like ?", "%" + p.Value.(string) + "%"}) 22 | case token.TOKEN_OP_TYPE_IN: 23 | stmt = append(stmt, []interface{}{k + " in (?)", p.Value}) 24 | case token.TOKEN_OP_TYPE_BETWEEN: 25 | val := p.Value.([]string) 26 | stmt = append(stmt, []interface{}{k + " between ? and ?", val[0], val[1]}) 27 | case token.TOKEN_OP_TYPE_GT: 28 | stmt = append(stmt, []interface{}{k + "> ?", p.Value}) 29 | case token.TOKEN_OP_TYPE_LT: 30 | stmt = append(stmt, []interface{}{k + "< ?", p.Value}) 31 | } 32 | } 33 | return stmt 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/lexer/stm.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "zeus/pkg/api/domain/search/lexer/token" 5 | ) 6 | 7 | const ( 8 | lexErrorSyntax = `Unexpected syntax` 9 | ) 10 | 11 | type StmHandler func(*lexer) StmHandler 12 | 13 | // StmError manages syntax error 14 | func StmError(l *lexer) StmHandler { 15 | l.Tokens <- token.Token{ 16 | Type: token.TOKEN_TYPE_ERROR, 17 | Value: "EOF", 18 | OpType: -1, 19 | } 20 | return nil 21 | } 22 | 23 | // StmEnd manages common ends 24 | func StmEnd(l *lexer) StmHandler { 25 | l.Tokens <- token.Token{ 26 | Type: token.TOKEN_TYPE_END, 27 | Value: "DONE", 28 | OpType: -1, 29 | } 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/lexer/stm_begin.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | // StmBegin start to do lexer process 4 | func StmBegin(l *lexer) StmHandler { 5 | return StmKey(l) 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/lexer/stm_delimiter.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "zeus/pkg/api/domain/search/lexer/token" 4 | 5 | // StmDelimiter 6 | func StmDelimiter(l *lexer) StmHandler { 7 | if l.EndOfString() { 8 | return StmEnd 9 | } 10 | l.Forward(len(token.TOEKN_DELIMITER)) 11 | l.Emit(token.TOKEN_TYPE_DELIMITER) 12 | return StmBegin 13 | } 14 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/lexer/stm_equal.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "zeus/pkg/api/domain/search/lexer/token" 4 | 5 | // StmEqual mange equal sign 6 | func StmEqual(l *lexer) StmHandler { 7 | l.Forward(len(token.TOKEN_EQUAL)) 8 | l.Emit(token.TOKEN_TYPE_EQUAL) 9 | return StmValue 10 | } 11 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/lexer/stm_key.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import "zeus/pkg/api/domain/search/lexer/token" 4 | 5 | // StmKey find next key position 6 | func StmKey(l *lexer) StmHandler { 7 | for { 8 | if l.EndOfString() { 9 | return StmError 10 | } 11 | if l.HasPrefix(token.TOKEN_EQUAL) { 12 | if l.Pos == 0 { 13 | l.EmitError(lexErrorSyntax) 14 | return StmError 15 | } 16 | l.Emit(token.TOKEN_TYPE_KEY) 17 | return StmEqual 18 | } 19 | l.Forward(1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pkg/api/domain/search/lexer/token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | const ( 4 | TOKEN_EQUAL = `=` 5 | TOEKN_DELIMITER = `|` 6 | TOKEN_LIKE = `~` 7 | TOKEN_IN_LEFT = `(` 8 | TOKEN_IN_RIGHT = `)` 9 | TOKEN_BETWEEN_LEFT = `[` 10 | TOKEN_BETWEEN_RIGHT = `]` 11 | TOKEN_LT = `<` 12 | TOKEN_GT = `>` 13 | ) 14 | 15 | type TokenType int 16 | 17 | const ( 18 | TOKEN_TYPE_ERROR TokenType = iota 19 | TOKEN_TYPE_KEY 20 | TOKEN_TYPE_EQUAL 21 | TOKEN_TYPE_VAL 22 | TOKEN_TYPE_LIKE 23 | TOKEN_TYPE_VAL_LIKE 24 | TOKEN_TYPE_GT 25 | TOKEN_TYPE_VAL_GT 26 | TOKEN_TYPE_LT 27 | TOKEN_TYPE_VAL_LT 28 | TOKEN_TYPE_VAL_IN 29 | TOKEN_TYPE_VAL_BETWEEN 30 | TOKEN_TYPE_IN_LEFT 31 | TOKEN_TYPE_BETWEEN_LEFT 32 | TOKEN_TYPE_DELIMITER 33 | TOKEN_TYPE_END 34 | ) 35 | 36 | type OpType int 37 | 38 | const ( 39 | TOKEN_OP_TYPE_EQ OpType = iota 40 | TOKEN_OP_TYPE_LIKE 41 | TOKEN_OP_TYPE_BETWEEN 42 | TOKEN_OP_TYPE_IN 43 | TOKEN_OP_TYPE_LT 44 | TOKEN_OP_TYPE_GT 45 | ) 46 | 47 | type Token struct { 48 | Type TokenType 49 | Value interface{} 50 | OpType OpType 51 | } 52 | -------------------------------------------------------------------------------- /pkg/api/domain/search/parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "strings" 7 | "zeus/pkg/api/domain/search/lexer/lexer" 8 | "zeus/pkg/api/domain/search/lexer/token" 9 | ) 10 | 11 | var ( 12 | ErrorUnexpectedToken = errors.New("unexpected syntax") 13 | ) 14 | 15 | type ParsePair struct { 16 | Key string `json:"key"` 17 | Value interface{} `json:"val"` 18 | St token.OpType `json:"st"` 19 | } 20 | 21 | func Parse(source string) ([]ParsePair, error) { 22 | lex := lexer.NewLexer(source) 23 | var tokens []ParsePair 24 | key := "" 25 | for { 26 | tk := lex.Token() 27 | switch tk.Type { 28 | case token.TOKEN_TYPE_ERROR: 29 | return tokens, ErrorUnexpectedToken 30 | case token.TOKEN_TYPE_END: 31 | return tokens, nil 32 | case token.TOKEN_TYPE_KEY: 33 | key = tk.Value.(string) 34 | case token.TOKEN_TYPE_VAL, 35 | token.TOKEN_TYPE_VAL_LIKE: 36 | tokens = append(tokens, ParsePair{ 37 | Key: key, 38 | Value: tk.Value, 39 | St: tk.OpType, 40 | }) 41 | key = "" 42 | case token.TOKEN_TYPE_VAL_GT, 43 | token.TOKEN_TYPE_VAL_LT: 44 | val, err := strconv.Atoi(tk.Value.(string)) 45 | if err != nil { 46 | return tokens, err 47 | } 48 | tokens = append(tokens, ParsePair{ 49 | Key: key, 50 | Value: val, 51 | St: tk.OpType, 52 | }) 53 | key = "" 54 | case token.TOKEN_TYPE_VAL_BETWEEN, 55 | token.TOKEN_TYPE_VAL_IN: 56 | tokens = append(tokens, ParsePair{ 57 | Key: key, 58 | Value: strings.Split(tk.Value.(string), ","), 59 | St: tk.OpType, 60 | }) 61 | key = "" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/api/domain/search/parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func TestParse(t *testing.T) { 11 | tks, err := Parse("u.name=lake|u.id=(1,2,3,4)|u.nick=~sun|u.create_time=[2020-01-01,2020-02-01]|id=>10|age=<20") 12 | assert.NoError(t, err) 13 | prettyTks, _ := json.MarshalIndent(tks, "", " ") 14 | t.Log(fmt.Sprintf("%s", prettyTks)) 15 | 16 | tks, err = Parse("u.name=lake|u.id=1,2,3,4)|u.nick=~sun|u.create_time=[2020-01-01,2020-02-01]") 17 | assert.NoError(t, err) 18 | 19 | tks, err = Parse("u.name=lake|u.id=(1,2,3,4|u.nick=~sun|u.create_time=[2020-01-01,2020-02-01]") 20 | assert.Error(t, err) 21 | 22 | tks, err = Parse("u.name=lake|u.id=(1,2,3,4)|u.nick=~sun|u.create_time=[2020-01-01,2020-02-01]") 23 | assert.NoError(t, err) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/api/domain/sync/dingdingtalk/sync.go: -------------------------------------------------------------------------------- 1 | package dingdingtalk 2 | 3 | import ( 4 | dingtalk "github.com/bullteam/go-dingtalk/src" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | var dingTalkClient *dingtalk.DingTalkClient 9 | 10 | // connect to dingding api 11 | func SetUp() { 12 | dingTalkClient = dingtalk.NewDingTalkCompanyClient(&dingtalk.DTConfig{ 13 | AppKey: viper.GetString("dingtalk.appkey"), 14 | AppSecret: viper.GetString("dingtalk.appsecret"), 15 | CachePath: viper.GetString("dingtalk.CachePath"), 16 | }) 17 | } 18 | 19 | // GetDepartment - get departments of dingding 20 | func GetDepartments() (interface{}, error) { 21 | _ = dingTalkClient.RefreshCompanyAccessToken() 22 | var treeDepartment = map[int][]dingtalk.Department{} 23 | list, err := dingTalkClient.DepartmentList(1, "zh_CN") 24 | if err != nil { 25 | return nil, err 26 | } 27 | for _, d := range list.Department { 28 | treeDepartment[d.ParentId] = append(treeDepartment[d.ParentId], d) 29 | } 30 | return treeDepartment, nil 31 | } 32 | 33 | // GetUsers - get users of dingding 34 | func GetUsers(departmentId int) (interface{}, error) { 35 | list, err := dingTalkClient.UserList(departmentId) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return list.UserList, nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/api/domain/sync/dingdingtalk/sync_test.go: -------------------------------------------------------------------------------- 1 | package dingdingtalk 2 | 3 | import ( 4 | dingtalk "github.com/bullteam/go-dingtalk/src" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | dingTalkClient = dingtalk.NewDingTalkCompanyClient(&dingtalk.DTConfig{ 11 | AppKey: `dingvgvn9hdcp8qtarno`, 12 | AppSecret: `Sn887TtSQgGQNRcSaP-cNQXKYOkuT062vPgHoVvbTm5-HA2qnyew6xbdgZwhxD8N`, 13 | CachePath: `data/`, 14 | }) 15 | } 16 | func TestGetUsers(t *testing.T) { 17 | depts, err := GetDepartments() 18 | assert.NoError(t, err) 19 | if err != nil && depts != nil { 20 | for _, depts := range depts.(map[int][]dingtalk.Department) { 21 | for _, dept := range depts { 22 | users, _ := GetUsers(dept.Id) 23 | if len(users.([]dingtalk.UDetailedList)) > 1 { 24 | t.Log(users) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/api/domain/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "zeus/pkg/api/domain/perm" 5 | ) 6 | 7 | // Permission : where ,do what ,in which domain 8 | type Permission struct { 9 | Zone string 10 | Action string 11 | Domain string 12 | } 13 | 14 | // CheckPermission : check permission in all roles of account 15 | func CheckPermission(userId string, p Permission) (bool, error) { 16 | return perm.Enforce(userId, p.Zone, p.Action, p.Domain) 17 | } 18 | 19 | // OverwriteRoles : assign roles to specific user 20 | func OverwriteRoles(userId string, newRoles [][]string) { 21 | currentRoles := perm.GetGroupsByUser(userId) 22 | for k1, newRole := range newRoles { 23 | for k2, currentRole := range currentRoles { 24 | if newRole[0] == currentRole[0] && newRole[1] == currentRole[1] { 25 | newRoles[k1] = []string{"-skip"} 26 | currentRoles[k2] = []string{"-skip"} 27 | } 28 | } 29 | } 30 | for _, newRole := range newRoles { 31 | if newRole[0] == "-skip" { 32 | continue 33 | } 34 | perm.AddGroup(newRole) 35 | } 36 | for _, rmRole := range currentRoles { 37 | if rmRole[0] == "-skip" { 38 | continue 39 | } 40 | perm.DelGroup(rmRole) 41 | } 42 | } 43 | 44 | // DeleteUser Delete user's group policies 45 | func DeleteUser(uid string) { 46 | groups := perm.GetGroupsByUser(uid) 47 | for _, group := range groups { 48 | perm.DelGroup(group) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/dto/dept.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | //DeptCreateDto - binding domain creation params 6 | type DeptCreateDto struct { 7 | Id int `json:"id"` 8 | Name string `form:"name" json:"name" binding:"required"` 9 | ParentId int `form:"parent_id" json:"parent_id"` 10 | OrderNum int `form:"order_num" json:"order_num"` 11 | CreateTime time.Time `type(datetime)" json:"create_time"` 12 | UpdateTime time.Time `type(datetime)" json:"-"` 13 | } 14 | 15 | //DeptEditDto - binding domain edition params 16 | type DeptEditDto struct { 17 | Id int `uri:"id" json:"id" binding:"required"` 18 | Name string `form:"name" json:"name" binding:"required"` 19 | ParentId int `form:"parent_id" json:"parent_id" binding:"gte=0" ` 20 | OrderNum int `form:"order_num" json:"order_num"` 21 | } 22 | -------------------------------------------------------------------------------- /pkg/api/dto/domain.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | // DomainListSearchMapping - define search query keys in domain list page 6 | var DomainListSearchMapping = map[string]string{ 7 | "id": "id", 8 | } 9 | 10 | //DomainCreateDto - binding domain creation params 11 | type DomainCreateDto struct { 12 | Id int `json:"id"` 13 | Name string `form:"name" json:"name" binding:"required"` 14 | Callbackurl string `form:"callbackurl" json:"callbackurl" binding:"required"` 15 | Remark string `form:"remark" json:"remark"` 16 | Code string `form:"code" json:"code" binding:"required"` 17 | CreateTime time.Time `type(datetime)" json:"create_time"` 18 | LastLoginTime time.Time `type(datetime)" json:"-"` 19 | } 20 | 21 | //DomainEditDto - binding domain edition params 22 | type DomainEditDto struct { 23 | Id int `uri:"id" json:"id" binding:"required"` 24 | Name string `form:"name" json:"name" binding:"required"` 25 | Callbackurl string `form:"callbackurl" json:"callbackurl"` 26 | Remark string `form:"remark" json:"remark"` 27 | Code string `form:"code" json:"code"` 28 | } 29 | -------------------------------------------------------------------------------- /pkg/api/dto/general.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // GeneralListDto - General list request params 8 | type GeneralListDto struct { 9 | Skip int `form:"skip,default=0" json:"skip"` 10 | Limit int `form:"limit,default=20" json:"limit" binding:"max=10000"` 11 | Order string `form:"order" json:"order"` 12 | Q string `form:"q" json:"q"` 13 | } 14 | 15 | type GeneralTreeDto struct { 16 | Q string `form:"q" json:"q"` 17 | } 18 | type GeneralDelDto struct { 19 | Id int `uri:"id" json:"id" binding:"required"` 20 | } 21 | type GeneralGetDto struct { 22 | Id int `uri:"id" json:"id" binding:"required"` 23 | } 24 | 25 | // TransformSearch - transform search query 26 | func TransformSearch(qs string, mapping map[string]string) (ss map[string]string) { 27 | ss = make(map[string]string) 28 | for _, v := range strings.Split(qs, ",") { 29 | vs := strings.Split(v, "=") 30 | if _, ok := mapping[vs[0]]; ok { 31 | ss[mapping[vs[0]]] = vs[1] 32 | } 33 | } 34 | return 35 | } 36 | 37 | // 38 | //// TransformSearch - transform search query 39 | //func ReplaceSearch(qs string, mapping map[string]string) string { 40 | // var ss = make(map[string]string) 41 | // for _, v := range strings.Split(qs, ",") { 42 | // vs := strings.Split(v, "=") 43 | // if _, ok := mapping[vs[0]]; ok { 44 | // ss[mapping[vs[0]]] = vs[1] 45 | // } 46 | // } 47 | // return 48 | //} 49 | -------------------------------------------------------------------------------- /pkg/api/dto/init.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/gin-gonic/gin/binding" 7 | "github.com/go-playground/validator/v10" 8 | "github.com/pkg/errors" 9 | "strings" 10 | "zeus/pkg/api/log" 11 | ) 12 | 13 | func init() { 14 | // Register custom validate methods 15 | if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 16 | _ = v.RegisterValidation("pwdValidate", pwdValidate) 17 | _ = v.RegisterValidation("permsValidate", permsValidate) 18 | } else { 19 | log.Fatal("Gin fail to registered custom validator(v10)") 20 | } 21 | } 22 | 23 | // Bind : bind request dto and auto verify parameters 24 | func Bind(c *gin.Context, obj interface{}) error { 25 | _ = c.ShouldBindUri(obj) 26 | if err := c.ShouldBind(obj); err != nil { 27 | if fieldErr, ok := err.(validator.ValidationErrors); ok { 28 | var tagErrorMsg []string 29 | for _, v := range fieldErr { 30 | if _, has := ValidateErrorMessage[v.Tag()]; has { 31 | tagErrorMsg = append(tagErrorMsg, fmt.Sprintf(ValidateErrorMessage[v.Tag()], v.Field(), v.Value())) 32 | } else { 33 | tagErrorMsg = append(tagErrorMsg, err.Error()) 34 | } 35 | } 36 | return errors.New(strings.Join(tagErrorMsg, ",")) 37 | } 38 | } 39 | return nil 40 | } 41 | 42 | //ValidateErrorMessage : customize error messages 43 | var ValidateErrorMessage = map[string]string{ 44 | "customValidate": "%s can not be %s", 45 | "required": "%s is required,got empty %#v", 46 | "pwdValidate": "%s is not a valid password", 47 | "permsValidate": "perms [%s] is not allow to contains comma", 48 | } 49 | -------------------------------------------------------------------------------- /pkg/api/dto/login.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type LoginDto struct { 4 | Username string `form:"username" json:"username" binding:"required"` 5 | Password string `form:"password" json:"password" binding:"required"` 6 | Code string `form:"code" json:"code"` 7 | } 8 | 9 | type TwoFaDto struct { 10 | Username string `form:"username" json:"username" binding:"required"` 11 | } 12 | 13 | // LoginOAuthDto - oauth login 14 | type LoginOAuthDto struct { 15 | Code string `form:"code" binding:"required"` 16 | Type int `form:"type" binding:"required"` 17 | } 18 | 19 | // BindThirdDto - bind third-part account 20 | type BindThirdDto struct { 21 | From int `form:"from"` 22 | Code string `form:"code" binding:"required"` 23 | } 24 | 25 | // UnBindThirdDto - unbind third-part account 26 | type UnBindThirdDto struct { 27 | OAuthType int `form:"from"` 28 | } 29 | 30 | // LoginDingtalkDto - ding talk login 31 | type LoginDingtalkDto struct { 32 | Code string `form:"code"` 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/dto/login_log.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type LoginLogListDto struct { 4 | Username string `form:"username"` 5 | Ip string `form:"ip"` 6 | StartTime string `form:"start_time"` 7 | EndTime string `form:"end_time"` 8 | Skip int `form:"skip,default=0" json:"skip"` 9 | Limit int `form:"limit,default=20" json:"limit" binding:"max=10000"` 10 | Order string `form:"order" json:"order"` 11 | } 12 | 13 | type LoginLogDto struct { 14 | UserId int `form:"user_id" binding:"required"` 15 | Client string `form:"client"` 16 | Platform string `form:"form"` 17 | Ip string `form:"ip"` 18 | IpLocation string `form:"ip_location"` 19 | LoginResult string `form:"login_result"` 20 | LoginStatus int `form:"login_status"` 21 | OperationContent string `form:"operation_content"` 22 | } 23 | -------------------------------------------------------------------------------- /pkg/api/dto/myaccount.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type BindCodeDto struct { 4 | Google2faToken string `form:"google_2fa_token" valid:"Required"` // 验证码 5 | } 6 | 7 | type VerifyEmailDto struct { 8 | Email string `form:"email" json:"email" valid:"required"` // email 9 | } 10 | 11 | type EmailVerificationDto struct { 12 | Code string `form:"code" binding:"required"` // email 13 | } 14 | -------------------------------------------------------------------------------- /pkg/api/dto/operation_log.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type OperationLogListDto struct { 4 | Username string `form:"username"` 5 | Ip string `form:"ip"` 6 | StartTime string `form:"start_time"` 7 | EndTime string `form:"end_time"` 8 | Skip int `form:"skip,default=0" json:"skip"` 9 | Limit int `form:"limit,default=20" json:"limit" binding:"max=10000"` 10 | Order string `form:"order" json:"order"` 11 | } 12 | 13 | type OperationLogDto struct { 14 | RequestUrl string `form:"request_url"` 15 | OperationMethod string `form:"operation_method"` 16 | Params string `form:"params"` 17 | UserId int `form:"user_id" valid:"Required"` 18 | Ip string `form:"ip"` 19 | IpLocation string `form:"ip_location"` 20 | OperationResult string `form:"operation_result"` 21 | OperationSuccess int `form:"operation_success"` 22 | OperationContent string `form:"operation_content"` 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/dto/perm.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type CheckPermDto struct { 4 | Domain string `form:"domain" json:"domain" binding:"required"` 5 | Perm string `form:"perm" json:"perm" binding:"required"` 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/dto/role.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | // RoleListSearchMapping - define search query keys in role list page 4 | var RoleListSearchMapping = map[string]string{ 5 | "n": "name", 6 | "d": "domain_id", 7 | "r": "role_name", 8 | } 9 | 10 | // RoleCreateDto - dto for role's creation 11 | type RoleCreateDto struct { 12 | Name string `form:"name" json:"name" binding:"required"` 13 | DomainId int `form:"domain_id" json:"domain_id" binding:"required,gte=1"` 14 | RoleName string `form:"role_name" json:"role_name" binding:"required"` 15 | Remark string `form:"remark" json:"remark"` 16 | MenuIds string `form:"menu_ids" json:"menu_ids"` 17 | MenuIdsEle string `form:"menu_ids_ele" json:"menu_ids_ele"` 18 | DataPermIds string `form:"data_perm_ids" json:"data_perm_ids"` 19 | } 20 | 21 | // RoleEditDto - dto for role's modification 22 | type RoleEditDto struct { 23 | Id int `uri:"id" json:"id" binding:"required,gte=1"` 24 | Name string `form:"name" json:"name" binding:"required"` 25 | DomainId int `form:"domain_id" json:"domain_id" binding:"required,gte=1"` 26 | Remark string `form:"remark" json:"remark"` 27 | MenuIds string `form:"menu_ids" json:"menu_ids"` 28 | MenuIdsEle string `form:"menu_ids_ele" json:"menu_ids_ele"` 29 | DataPermIds string `form:"data_perm_ids" json:"data_perm_ids"` 30 | } 31 | -------------------------------------------------------------------------------- /pkg/api/dto/role_data_perm.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type AssignDataPermDto struct { 4 | RoleId int `form:"role_id"` 5 | DataPermId int `form:"data_perm_id"` 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/dto/setting.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | type SettingDTO struct { 4 | LdapUrl string `form:"ldapUrl" json:"ldapUrl"` 5 | LdapSearchDN string `form:"ldapSearchDN" json:"ldapSearchDN"` 6 | LdapSearchPassword string `form:"ldapSearchPassword" json:"ldapSearchPassword"` 7 | LdapBaseDN string `form:"ldapBaseDN" json:"ldapBaseDN"` 8 | LdapFilter string `form:"ldapFilter" json:"ldapFilter"` 9 | LdapUID string `form:"ldapUID" json:"ldapUID"` 10 | LdapGroupBaseDN string `form:"ldapGroupBaseDN" json:"ldapGroupBaseDN"` 11 | LdapGroupFilter string `form:"ldapGroupFilter" json:"ldapGroupFilter"` 12 | LdapGroupGID string `form:"ldapGroupGID" json:"ldapGroupGID"` 13 | LdapGroupAdminDN string `form:"ldapGroupAdminDN" json:"ldapGroupAdminDN"` 14 | } 15 | 16 | type EmailSettingDTO struct { 17 | Port int `form:"port" json:"port"` 18 | SmtpServer string `form:"smtpServer" json:"smtpServer"` 19 | SmtpAddress string `form:"smtpAddress" json:"smtpAddress"` 20 | SmtpUser string `form:"smtpUser" json:"smtpUser"` 21 | SmtpPassword string `form:"smtpPassword" json:"smtpPassword"` 22 | } 23 | -------------------------------------------------------------------------------- /pkg/api/dto/user_oauth.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type UserOAuthCreateDto struct { 6 | Id int `form:"id" json:"id"` 7 | From int `form:"from" json:"from"` 8 | User_id int `form:"user_id" json:"user_id"` 9 | Openid string `form:"openid" json:"openid"` 10 | Unionid string `form:"unionid" json:"unionid"` 11 | Avatar string `form:"avatar" json:"avatar"` 12 | Extra string `form:"extra" json:"extra"` 13 | Name string `form:"name" json:"name"` 14 | CreateTime time.Time `type(datetime)" json:"create_time"` 15 | UpdateTime time.Time `type(datetime)" json:"-"` 16 | } 17 | -------------------------------------------------------------------------------- /pkg/api/dto/user_secret.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "time" 4 | 5 | type UserSecretCreateDto struct { 6 | Id int `json:"id"` 7 | User_id int `form:"user_id" json:"user_id"` 8 | Account_name string `form:"account_name" json:"account_name"` 9 | Secret string `form:"secret" json:"secret"` 10 | CreateTime time.Time `type(datetime)" json:"create_time"` 11 | UpdateTime time.Time `type(datetime)" json:"-"` 12 | } 13 | 14 | type UserSecretRetDto struct { 15 | Is_open int `json:"is_open"` 16 | Code string `json:"code"` 17 | Account string `json:"account"` 18 | Secret string `json:"secret"` 19 | } 20 | -------------------------------------------------------------------------------- /pkg/api/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | "github.com/gin-gonic/gin" 6 | "github.com/spf13/viper" 7 | "time" 8 | ) 9 | 10 | func Cors() gin.HandlerFunc { 11 | return cors.New(cors.Config{ 12 | AllowOrigins: viper.GetStringSlice("cors.allow_origins"), 13 | AllowMethods: viper.GetStringSlice("cors.allow_methods"), 14 | AllowHeaders: viper.GetStringSlice("cors.allow_headers"), 15 | AllowCredentials: viper.GetBool("cors.allow_credentials"), 16 | MaxAge: time.Second * time.Duration(viper.GetInt("cors.max_age")), 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /pkg/api/middleware/log.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/gin-gonic/gin" 7 | "io/ioutil" 8 | "zeus/pkg/api/dto" 9 | "zeus/pkg/api/log" 10 | "zeus/pkg/api/service" 11 | ) 12 | 13 | var logService = service.LogService{} 14 | var ignoreRoutes = map[string]bool{ 15 | "/v1/account/idle": true, 16 | "/v1/account/password": true, 17 | "/v1/account/require-change-pwd": true, 18 | } 19 | 20 | func AccessLog(c *gin.Context) { 21 | //ignore logs 22 | if _, ok := ignoreRoutes[c.Request.URL.Path]; ok { 23 | c.Next() 24 | return 25 | } 26 | b, _ := json.Marshal(c.Request.URL.Query()) 27 | body, _ := ioutil.ReadAll(c.Request.Body) 28 | c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 29 | uid := 0 30 | if c.Value("userId") != nil { 31 | uid = int(c.Value("userId").(float64)) 32 | } 33 | orLogDto := dto.OperationLogDto{ 34 | UserId: uid, 35 | RequestUrl: c.Request.URL.Path, 36 | OperationMethod: c.Request.Method, 37 | Params: "[GET] -> " + string(b) + " | [POST] -> " + string(body), 38 | Ip: c.ClientIP(), 39 | IpLocation: "", //TODO...待接入获取ip位置服务 40 | OperationResult: "success", 41 | OperationSuccess: 1, 42 | OperationContent: "-", 43 | } 44 | err := logService.InsertOperationLog(orLogDto) 45 | if err != nil { 46 | log.Error(err.Error()) 47 | } 48 | c.Next() 49 | } 50 | -------------------------------------------------------------------------------- /pkg/api/middleware/perm.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "github.com/beego/i18n" 6 | "github.com/gin-gonic/gin" 7 | "net/http" 8 | "strings" 9 | "zeus/pkg/api/log" 10 | ) 11 | 12 | // Ignored permissions 13 | var ignoredPerms = map[string]bool{} 14 | 15 | // PermCheck - check permission automatically 16 | func PermCheck(c *gin.Context) { 17 | route := strings.Split(c.Request.URL.RequestURI(), "?")[0] 18 | for _, p := range c.Params { 19 | route = strings.Replace(route, "/"+p.Value, "/:"+p.Key, 1) 20 | } 21 | route = strings.ToLower(c.Request.Method) + "@" + route 22 | uid := fmt.Sprintf("%#v", c.Value("userId")) 23 | if _, ok := ignoredPerms[route]; ok { 24 | c.Next() 25 | return 26 | } 27 | check, _ := accountService.CheckPermission(uid, "root", route) 28 | if !check { 29 | log.Warn(fmt.Sprintf("No permission for %s", route)) 30 | c.JSON(http.StatusOK, gin.H{ 31 | "code": 403, 32 | "msg": i18n.Tr(GetLang(), "err.Err403"), 33 | }) 34 | c.Abort() 35 | return 36 | } else { 37 | log.Info(fmt.Sprintf("Pass permission check for %s", route)) 38 | } 39 | c.Next() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/api/middleware/prepare.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/appleboy/gin-jwt/v2" 5 | "github.com/gin-gonic/gin" 6 | "zeus/pkg/api/model" 7 | ) 8 | 9 | // JwtPrepare : parse jwt and set login session info 10 | func JwtPrepare(c *gin.Context) { 11 | claims := jwt.ExtractClaims(c) 12 | user, _ := c.Get("id") 13 | c.Set("userId", claims["id"]) 14 | c.Set("userName", user.(model.UserClaims).Name) 15 | c.Next() 16 | } 17 | -------------------------------------------------------------------------------- /pkg/api/model/base.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Pagination struct { 4 | Start int 5 | Limit int 6 | } 7 | -------------------------------------------------------------------------------- /pkg/api/model/casbin_rule.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type CasbinRule struct { 4 | Id int 5 | PType string 6 | V0 string 7 | V1 string 8 | V2 string 9 | V3 string 10 | V4 string 11 | V5 string 12 | } 13 | 14 | func (CasbinRule) TableName() string { 15 | return "casbin_rule" 16 | } 17 | -------------------------------------------------------------------------------- /pkg/api/model/data_perm.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DataPerm struct { 4 | Id int `json:"id"` // 自增ID 5 | ParentId int `json:"parent_id"` // 父级id 6 | Name string `json:"name"` // 名称 7 | Perms string `json:"perms"` // 数据权限标识 8 | PermsRule string `json:"perms_rule"` // 数据规则 9 | PermsType int `json:"perms_type"` // 类型 1=分类 2=数据权限 10 | OrderNum int `json:"order_num"` // 排序字段 11 | DomainId int `json:"domain_id"` 12 | Remarks string `json:"remarks"` // 说明 13 | } 14 | 15 | type DataPermQuery struct { 16 | DomainId int 17 | Name string 18 | Pagination *Pagination 19 | } 20 | 21 | func (dp *DataPerm) TableName() string { 22 | return "data_perm" 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/model/department.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Department struct { 4 | Id int `json:"id"` 5 | Name string `json:"name"` 6 | OrderNum int `json:"order_num"` 7 | ParentId int `json:"parent_id"` 8 | ExtendField string `json:"extend_field"` 9 | Lft int `json:"lft"` 10 | Rgt int `json:"rgt"` 11 | Level int `json:"level"` 12 | Path string `json:"path"` 13 | } 14 | 15 | func (Department) TableName() string { 16 | return "department" 17 | } 18 | -------------------------------------------------------------------------------- /pkg/api/model/domain.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Domain struct { 8 | Id int `json:"id"` 9 | Name string `json:"name"` 10 | Callbackurl string `json:"callbackurl"` 11 | Remark string `json:"remark"` 12 | Code string `json:"code"` 13 | CreateTime time.Time `json:"create_time" gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP"` 14 | LastUpdateTime time.Time `json:"updated_time" gorm:"column:last_update_time;not null;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"` 15 | } 16 | 17 | func (d *Domain) TableName() string { 18 | return "domain" 19 | } 20 | -------------------------------------------------------------------------------- /pkg/api/model/login_log.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | //login log mapping 8 | type LoginLog struct { 9 | Id int `json:"id"` 10 | UserId int `form:"user_id" json:"user_id" binding:"required"` 11 | Client string `form:"client" json:"client"` 12 | Platform string `form:"platform" json:"platform"` 13 | LoginResult string `form:"login_result" json:"login_result"` 14 | LoginStatus int `form:"login_status" json:"login_status"` 15 | LoginTime time.Time `gorm:"-" json:"login_time"` 16 | Ip string `form:"ip" json:"ip"` 17 | IpLocation string `json:"ip_location"` 18 | OperationTime string `gorm:"-" json:"operation_time"` 19 | OperationContent string `form:"operation_content" json:"operation_content"` 20 | CreateTime time.Time `gorm:"-" json:"-"` 21 | LastUpdateTime time.Time `gorm:"-" json:"-"` 22 | } 23 | 24 | func (log *LoginLog) TableName() string { 25 | return "login_log" 26 | } 27 | -------------------------------------------------------------------------------- /pkg/api/model/menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Menu struct { 8 | Id int `json:"id"` 9 | ParentId int `json:"parent_id"` 10 | DomainId int `json:"domain_id"` 11 | Domain Domain `json:"domain"` 12 | Name string `json:"name"` 13 | Url string `json:"url"` 14 | Perms string `json:"perms"` 15 | Alias string `json:"alias"` 16 | MenuType int `json:"menu_type"` 17 | Icon string `json:"icon"` 18 | OrderNum int `json:"order_num"` 19 | CreateTime time.Time `gorm:"type:time;column:create_time;not null;default:CURRENT_TIMESTAMP" json:"created_time,omitempty" example:"2019-07-10 0:39"` 20 | LastUpdateTime time.Time `gorm:"type:time;column:last_update_time;not null;default:CURRENT_TIMESTAMP ON UPDATE" json:"last_update_time,omitempty" example:"2019-07-10 0:39"` 21 | } 22 | 23 | func (m *Menu) TableName() string { 24 | return "menu" 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/model/menu_perm_alias.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type MenuPermAlias struct { 4 | Id int `json:"id"` 5 | Perms string `json:"perms"` 6 | Alias string `json:"alias"` 7 | DomainId int `json:"domain_id"` 8 | CreatedTime int64 `json:"created_time"` 9 | UpdatedTime int64 `json:"updated_time"` 10 | } 11 | 12 | func (*MenuPermAlias) TableName() string { 13 | return "menu_perm_alias" 14 | } 15 | -------------------------------------------------------------------------------- /pkg/api/model/operation_log.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | //operation log mapping 6 | type OperationLog struct { 7 | Id int `json:"id"` 8 | LogNo string `form:"log_no" json:"log_no" binding:"required"` 9 | Module string `form:"module" json:"module"` 10 | RequestUrl string `form:"request_url" json:"request_url"` 11 | OperationMethod string `form:"operation_method" json:"operation_method"` 12 | Params string `form:"params" json:"params"` 13 | ExceptionStack string `form:"exception_stack" json:"exception_stack"` 14 | OperationResult string `form:"operation_result" json:"operation_result"` 15 | OperationSuccess int `form:"operation_success" json:"operation_success"` 16 | OperationTime time.Time `gorm:"-" form:"operation_time" json:"operation_time"` 17 | UserId int `form:"user_id" json:"user_id"` 18 | Ip string `form:"ip" json:"ip"` 19 | IpLocation string `form:"ip_location" json:"ip_location"` 20 | OperationContent string `form:"operation_content" json:"operation_content"` 21 | CreateTime time.Time `json:"create_time" gorm:"column:create_time;not null;default:CURRENT_TIMESTAMP"` 22 | LastUpdateTime time.Time `json:"updated_time" gorm:"column:last_update_time;not null;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"` 23 | } 24 | 25 | func (log *OperationLog) TableName() string { 26 | return "operation_log" 27 | } 28 | -------------------------------------------------------------------------------- /pkg/api/model/role.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // Role model 4 | type Role struct { 5 | Id int `json:"id"` 6 | Name string `json:"name"` 7 | DomainId int `json:"domain_id"` 8 | Domain Domain `json:"domain"` 9 | DataPerm []DataPerm `json:"data_perm" gorm:"many2many:role_data_perm";"ForeignKey:RoleId"` 10 | RoleName string `json:"role_name"` 11 | Remark string `json:"remark"` 12 | //Users []*User `json:"users" orm:"reverse(many)"` 13 | MenuIds string `json:"menu_ids"` 14 | MenuIdsEle string `json:"menu_ids_ele"` 15 | } 16 | 17 | func (Role) TableName() string { 18 | return "role" 19 | } 20 | 21 | //for更新创建 22 | type RoleEntity struct { 23 | Id int `json:"id"` 24 | Name string `json:"name"` 25 | DomainId int `json:"domain_id"` 26 | RoleName string `json:"role_name"` 27 | Remark string `json:"remark"` 28 | MenuIds string `json:"menu_ids"` 29 | MenuIdsEle string `json:"menu_ids_ele"` 30 | } 31 | -------------------------------------------------------------------------------- /pkg/api/model/role_data_perm.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type RoleDataPerm struct { 4 | Id int `json:"id"` 5 | RoleId int `json:"role_id"` 6 | DataPermId int `json:"data_perm_id"` 7 | } 8 | 9 | type GetByRoleIdData struct { 10 | Id int `json:"id"` 11 | RoleId int `json:"role_id"` 12 | Name string `json:"name"` 13 | Perms string `json:"perms"` 14 | } 15 | 16 | func (r *RoleDataPerm) TableName() string { 17 | return "role_data_perm" 18 | } 19 | -------------------------------------------------------------------------------- /pkg/api/model/setting.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type LdapSetting struct { 4 | LdapUrl string `json:"ldapUrl"` 5 | LdapSearchDN string `json:"ldapSearchDN"` 6 | LdapSearchPassword string `json:"ldapSearchPassword"` 7 | LdapBaseDN string `json:"ldapBaseDN"` 8 | LdapFilter string `json:"ldapFilter"` 9 | LdapUID string `json:"ldapUID"` 10 | LdapGroupBaseDN string `json:"ldapGroupBaseDN"` 11 | LdapGroupFilter string `json:"ldapGroupFilter"` 12 | LdapGroupGID string `json:"ldapGroupGID"` 13 | LdapGroupAdminDN string `json:"ldapGroupAdminDN"` 14 | } 15 | 16 | type EmailSetting struct { 17 | Port int `json:"port"` 18 | SmtpServer string `json:"smtpServer"` 19 | SmtpAddress string `json:"smtpAddress"` 20 | SmtpUser string `json:"smtpUser"` 21 | SmtpPassword string `json:"smtpPassword"` 22 | } 23 | -------------------------------------------------------------------------------- /pkg/api/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type User struct { 8 | Id int `json:"id" example:"1"` 9 | Username string `json:"username" example:"wutongci"` 10 | Mobile string `json:"mobile" example:"186000000"` 11 | Sex int `json:"sex" example:"1"` 12 | Realname string `json:"realname" example:"黄梧桐"` 13 | Password string `json:"-"` 14 | Salt string `json:"-"` 15 | DepartmentId int `json:"department_id" example:"1"` 16 | Department Department `json:"department" example:"1"` 17 | Faceicon string `json:"faceicon" example:"http://xxx.com"` 18 | Email string `json:"email" example:"xxxx@hotmail.com"` 19 | Title string `json:"title" example:"title"` 20 | Status int `json:"status" example:"1"` 21 | CreateTime time.Time `gorm:"type:time;column:create_time;not null;default:CURRENT_TIMESTAMP" json:"created_time,omitempty" example:"2019-07-10 0:39"` 22 | LastLoginTime time.Time `gorm:"type:time;column:last_login_time;not null;default:CURRENT_TIMESTAMP" json:"logined_time,omitempty" example:"2019-07-10 0:39"` 23 | //Roles []Role `gorm:"many2many:user_role;" json:"roles" example:"roles"` 24 | } 25 | 26 | func (User) TableName() string { 27 | return "user" 28 | } 29 | 30 | type UserClaims struct { 31 | Id int `json:"id"` 32 | Name string `json:"name"` 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/model/user_oauth.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | type UserOAuth struct { 6 | Id int `json:"id"` 7 | From int `json:"from"` 8 | UserId int `json:"user_id"` 9 | Openid string `json:"openid"` 10 | UnionId string `json:"unionid" gorm:"column:unionid"` 11 | Avatar string `json:"avatar"` 12 | Extra string `json:"extra"` 13 | Name string `json:"name"` 14 | CreateTime time.Time `gorm:"type:time;column:create_time;not null;default:CURRENT_TIMESTAMP" json:"create_time,omitempty" example:"2019-07-10 0:39"` 15 | UpdateTime time.Time `gorm:"type:time;column:update_time;not null;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"update_time,omitempty" example:"2019-07-10 0:39"` 16 | } 17 | 18 | func (ur *UserOAuth) TableName() string { 19 | return "user_oauth" 20 | } 21 | -------------------------------------------------------------------------------- /pkg/api/model/user_role.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type UserRole struct { 4 | Id int 5 | UserId int `sql:"index"` 6 | RoleId int `sql:"index"` 7 | } 8 | 9 | func (UserRole) TableName() string { 10 | return "user_role" 11 | } 12 | -------------------------------------------------------------------------------- /pkg/api/model/user_secret.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | type UserSecret struct { 6 | Id int `json:"id"` 7 | User_id int `json:"user_id"` 8 | Account_name string `json:"account_name"` 9 | Secret string `json:"secret"` 10 | Is_open int `json:"is_open"` 11 | CreateTime time.Time `json:"create_time"` 12 | UpdateTime time.Time `json:"update_time"` 13 | } 14 | 15 | type UserSecretQuery struct { 16 | Is_open int 17 | Account_name string 18 | Secret string 19 | } 20 | 21 | func (ur *UserSecret) TableName() string { 22 | return "user_secret" 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/service/dingtalk.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | dingtalk "github.com/bullteam/go-dingtalk/src" 5 | "github.com/spf13/viper" 6 | "strings" 7 | "zeus/pkg/api/domain/sync/dingdingtalk" 8 | "zeus/pkg/api/dto" 9 | ) 10 | 11 | type DingTalkService struct{} 12 | 13 | func (ds DingTalkService) SyncUsersAndDepartments() { 14 | departments, err := dingdingtalk.GetDepartments() 15 | if err != nil { 16 | return 17 | } 18 | ds.SyncRecursive(departments.(map[int][]dingtalk.Department), 0, 0) 19 | } 20 | 21 | func (ds DingTalkService) SyncRecursive(departments map[int][]dingtalk.Department, id int, pid int) { 22 | for _, d := range departments[id] { 23 | created := DeptService.Create(DeptService{}, dto.DeptCreateDto{ 24 | Name: d.Name, 25 | ParentId: pid, 26 | }) 27 | if _, ok := departments[d.Id]; ok { 28 | ds.SyncRecursive(departments, d.Id, created.Id) 29 | } 30 | users, err2 := dingdingtalk.GetUsers(d.Id) 31 | if err2 != nil || len(users.([]dingtalk.UDetailedList)) < 1 { 32 | continue 33 | } 34 | for _, u := range users.([]dingtalk.UDetailedList) { 35 | userName := u.Name 36 | if u.Email != "" { 37 | userName = strings.Split(u.Email, "@")[0] 38 | } 39 | _, _ = UserService.Create(UserService{}, dto.UserCreateDto{ 40 | Username: userName, 41 | Realname: u.Name, 42 | Mobile: u.Mobile, 43 | Email: u.Email, 44 | Title: u.Position, 45 | Status: 1, 46 | Sex: 0, 47 | DepartmentId: created.Id, 48 | Password: viper.GetString("dingtalk.defaultPwd"), 49 | }) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/api/service/install.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | "zeus/pkg/api/dto" 6 | ) 7 | 8 | type InstallService struct { 9 | } 10 | 11 | func (us InstallService) Install(dto dto.InstallDTO) bool { 12 | if viper.GetBool("security.install_lock") { 13 | return false 14 | } 15 | 16 | //base 17 | viper.Set("security.install_lock", true) 18 | viper.Set("base.siteName", dto.SiteName) 19 | viper.Set("base.port", dto.Port) 20 | viper.Set("base.baseUrl", dto.BaseUrl) 21 | viper.Set("base.logPath", dto.LogPath) 22 | viper.Set("base.isEnableCode", dto.IsEnableCode) 23 | viper.Set("base.isEnableAccess", dto.IsEnableAccess) 24 | 25 | //sql 26 | viper.Set("database.driver", dto.SqlType) 27 | viper.Set("database.sqlite.dsn", dto.DataPath) 28 | viper.Set("database.mysql.host", dto.SqlHost) 29 | viper.Set("database.mysql.user", dto.SqlUser) 30 | viper.Set("database.mysql.password", dto.SqlPassword) 31 | viper.Set("database.mysql.name", dto.SqlName) 32 | viper.Set("database.mysql.charset", dto.SqlCharset) 33 | viper.Set("database.mysql.ssl", dto.SqlSSL) 34 | 35 | //Email 36 | viper.Set("email.smtp.port", dto.Port) 37 | viper.Set("email.smtp.address", dto.SmtpAddress) 38 | viper.Set("email.smtp.password", dto.SmtpPassword) 39 | viper.Set("email.smtp.server", dto.SmtpServer) 40 | viper.Set("email.smtp.user", dto.SmtpUser) 41 | 42 | err := viper.WriteConfig() 43 | if err == nil { 44 | return true 45 | } 46 | return false 47 | } 48 | 49 | func (us InstallService) Islock() bool { 50 | return viper.GetBool("security.install_lock") 51 | } 52 | -------------------------------------------------------------------------------- /pkg/api/service/menu_perm_alias.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "zeus/pkg/api/dao" 5 | ) 6 | 7 | var menuPermAlias = dao.MenuPermAlias{} 8 | 9 | //MenuPermAlias 10 | type MenuPermAlias struct { 11 | } 12 | 13 | // GetByAlias - now just for `perm` middleware to automatically check alias record 14 | //func (MenuPermAlias) GetByAlias (alias string) model.MenuPermAlias { 15 | // return menuPermAlias.GetByAlias(alias) 16 | //} 17 | -------------------------------------------------------------------------------- /pkg/api/service/user_secret.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "zeus/pkg/api/dao" 5 | "zeus/pkg/api/dto" 6 | "zeus/pkg/api/log" 7 | "zeus/pkg/api/model" 8 | ) 9 | 10 | var userSecretDao = dao.UserSecretDao{} 11 | 12 | // DomainService 13 | type UserSecretService struct { 14 | } 15 | 16 | // InfoOfId - get role info by id 17 | func (us UserSecretService) InfoOfId(dto dto.GeneralGetDto) model.UserSecret { 18 | return userSecretDao.Get(dto.Id) 19 | } 20 | 21 | // Create - create a new domain 22 | func (us UserSecretService) Create(dto dto.UserSecretCreateDto) model.UserSecret { 23 | userSecret := model.UserSecret{ 24 | User_id: dto.User_id, 25 | Account_name: dto.Account_name, 26 | Secret: dto.Secret, 27 | } 28 | c := userSecretDao.Create(&userSecret) 29 | if c.Error != nil { 30 | log.Error(c.Error.Error()) 31 | } 32 | return userSecret 33 | } 34 | -------------------------------------------------------------------------------- /pkg/api/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/rand" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | const charset = "abcdefghijklmnopqrstuvwxyz" + 10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + 11 | "@#$%^&*(~)" 12 | 13 | var seededRand = rand.New( 14 | rand.NewSource(time.Now().UnixNano())) 15 | 16 | func StringWithCharset(length int, charset string) string { 17 | b := make([]byte, length) 18 | for i := range b { 19 | b[i] = charset[seededRand.Intn(len(charset))] 20 | } 21 | return string(b) 22 | } 23 | 24 | func RandomPwd(length int) string { 25 | return StringWithCharset(length, charset) 26 | } 27 | func StringSliceRemove(s [][]string, i int) [][]string { 28 | s[i] = s[len(s)-1] 29 | return s[:len(s)-1] 30 | } 31 | func IsNilObject(object interface{}) bool { 32 | if object == nil { 33 | return true 34 | } 35 | 36 | value := reflect.ValueOf(object) 37 | kind := value.Kind() 38 | if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { 39 | return true 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /pkg/webui/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": "commonjs", 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"], 12 | "env": { 13 | "development":{ 14 | "plugins": ["dynamic-import-node"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/webui/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | insert_final_newline = false 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /pkg/webui/.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | src/assets 4 | -------------------------------------------------------------------------------- /pkg/webui/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | **/*.log 7 | 8 | test/unit/coverage 9 | test/e2e/reports 10 | selenium-debug.log 11 | 12 | # Editor directories and files 13 | .idea 14 | .vscode 15 | *.suo 16 | *.ntvs* 17 | *.njsproj 18 | *.sln 19 | 20 | package-lock.json 21 | -------------------------------------------------------------------------------- /pkg/webui/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /pkg/webui/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: stable 3 | script: npm run test 4 | notifications: 5 | email: false 6 | -------------------------------------------------------------------------------- /pkg/webui/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | //You can set the vue-loader configuration by yourself. 5 | } 6 | -------------------------------------------------------------------------------- /pkg/webui/config/dev.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"development"', 3 | ENV_CONFIG: '"dev"', 4 | 5 | // 以下是不同 api 接口配置 6 | // 主平台 7 | // 'ZEUS_ADMIN_URL': '"//api.auth.bullteam.local"' 8 | 'ZEUS_ADMIN_URL': '"//127.0.0.1:8082"' 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/config/prev.env.js: -------------------------------------------------------------------------------- 1 | // 公共开发环境 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | ENV_CONFIG: '"prev"', 5 | 6 | // 以下是不同 api 接口配置 7 | // 主域名 8 | 'ZEUS_ADMIN_URL': '"//api.admin.bullteam.cn"' 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/config/prod.env.js: -------------------------------------------------------------------------------- 1 | // 生成环境 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | ENV_CONFIG: '"prod"', 5 | 6 | // 以下是不同 api 接口配置 7 | // 主域名 8 | 'ZEUS_ADMIN_URL': '"//api.auth.bullteam.cn"' 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/config/test.env.js: -------------------------------------------------------------------------------- 1 | // 测试环境 2 | module.exports = { 3 | NODE_ENV: '"production"', 4 | ENV_CONFIG: '"test"', 5 | 6 | // 以下是不同 api 接口配置 7 | // 主域名 8 | 'ZEUS_ADMIN_URL': '"//api.admin.bullteam.test"' 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/config/work.env.js: -------------------------------------------------------------------------------- 1 | // 测试环境 2 | module.exports = { 3 | NODE_ENV: '"development"', 4 | ENV_CONFIG: '"work"', 5 | 6 | // 以下是不同 api 接口配置 7 | // 主域名 8 | // 'ZEUS_ADMIN_URL': '"//127.0.0.1:8082"' 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/favicon.ico -------------------------------------------------------------------------------- /pkg/webui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Zeus 宙斯权限管理后台 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /pkg/webui/node_modules/.bin/acorn: -------------------------------------------------------------------------------- 1 | ../acorn/bin/acorn -------------------------------------------------------------------------------- /pkg/webui/readme.md: -------------------------------------------------------------------------------- 1 | # 集成系统后台 -------------------------------------------------------------------------------- /pkg/webui/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /pkg/webui/src/api/dataPerm.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | // 获取数据权限列表 5 | export function dataPermList(data) { 6 | return request({ 7 | url: 'v1/datas', 8 | method: 'get', 9 | params: data 10 | }) 11 | } 12 | // 删除数据权限 13 | export function dataPermDel(data) { 14 | return request({ 15 | url: 'v1/datas/' + data.id, 16 | method: 'delete', 17 | params: data 18 | }) 19 | } 20 | // 添加数据权限 21 | export function dataPermAdd(data) { 22 | return request({ 23 | url: 'v1/datas', 24 | method: 'post', 25 | data: qs.stringify(data) 26 | }) 27 | } 28 | // 修改数据权限 29 | export function dataPermEdit(data) { 30 | return request({ 31 | url: 'v1/datas/' + data.id, 32 | method: 'put', 33 | data: qs.stringify(data) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/webui/src/api/dept.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | export function fetchDeptList(query) { 5 | return request({ 6 | url: 'v1/depts', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | export function fetchDept(id) { 13 | return request({ 14 | url: 'v1/depts', 15 | method: 'get', 16 | params: { 17 | id 18 | } 19 | }) 20 | } 21 | 22 | export function createDept(data) { 23 | return request({ 24 | url: 'v1/depts', 25 | method: 'post', 26 | data: qs.stringify(data) 27 | }) 28 | } 29 | 30 | export function updateDept(id, data) { 31 | return request({ 32 | url: 'v1/depts/' + id, 33 | method: 'put', 34 | data: qs.stringify(data) 35 | }) 36 | } 37 | 38 | export function rebuildDept(data) { 39 | return request({ 40 | url: 'v1/depts/' + data.id + '/rebuild', 41 | method: 'post', 42 | data: qs.stringify(data) 43 | }) 44 | } 45 | 46 | export function checkMemberDept(data) { 47 | return request({ 48 | url: 'v1/depts/' + data.id + '/check-no-member', 49 | method: 'post', 50 | data: qs.stringify(data) 51 | }) 52 | } 53 | 54 | export function deleteDept(data) { 55 | return request({ 56 | url: 'v1/depts/' + data.id, 57 | method: 'delete', 58 | data: qs.stringify(data) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /pkg/webui/src/api/domain.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | export function fetchDomainList(query) { 5 | return request({ 6 | url: 'v1/domains', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | export function fetchDomain(id) { 13 | return request({ 14 | url: 'v1/domains' + id, 15 | method: 'get' 16 | }) 17 | } 18 | 19 | export function createDomain(data) { 20 | return request({ 21 | url: 'v1/domains', 22 | method: 'post', 23 | data: qs.stringify(data) 24 | }) 25 | } 26 | 27 | export function updateDomain(id, data) { 28 | return request({ 29 | url: 'v1/domains/' + id, 30 | method: 'put', 31 | data: qs.stringify(data) 32 | }) 33 | } 34 | 35 | export function deleteDomain(id, data) { 36 | return request({ 37 | url: 'v1/domains/' + id, 38 | method: 'delete', 39 | data: qs.stringify(data) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/webui/src/api/log.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function fetchLoginLogList(query) { 4 | return request({ 5 | url: 'v1/log/logins', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | 11 | export function fetchOperationLogList(query) { 12 | return request({ 13 | url: 'v1/log/operations', 14 | method: 'get', 15 | params: query 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/webui/src/api/login.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | export function loginByUsername(data) { 5 | return request({ 6 | url: '/v1/users/login', 7 | method: 'post', 8 | data: qs.stringify(data) 9 | }) 10 | } 11 | 12 | // login ldap 13 | export function loginLdapByUsername(data) { 14 | return request({ 15 | url: '/v1/users/login/ldap', 16 | method: 'post', 17 | data: qs.stringify(data) 18 | }) 19 | } 20 | 21 | // 钉钉登录 22 | export function loginDingtalk(data) { 23 | return request({ 24 | url: 'v1/users/login/oauth', 25 | method: 'post', 26 | data: qs.stringify(data) 27 | }) 28 | } 29 | 30 | export function logout() { 31 | return request({ 32 | url: 'login/logout', 33 | method: 'post' 34 | }) 35 | } 36 | 37 | export function getUserPrem(token) { 38 | return request({ 39 | url: '/v1/account/permissions', 40 | method: 'get' 41 | }) 42 | } 43 | 44 | export function getUserDomain(token) { 45 | return request({ 46 | url: '/v1/account/domains', 47 | method: 'get' 48 | }) 49 | } 50 | 51 | export function getUserCaptcha(token) { 52 | return request({ 53 | url: 'captcha/request', 54 | method: 'get', 55 | params: { 56 | token 57 | } 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/webui/src/api/menu.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | export function fetchMenuList(query) { 5 | return request({ 6 | url: 'v1/menus', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | export function fetchMenu(id) { 13 | return request({ 14 | url: 'v1/menus', 15 | method: 'get', 16 | params: { 17 | id 18 | } 19 | }) 20 | } 21 | 22 | export function createMenu(data) { 23 | return request({ 24 | url: 'v1/menus', 25 | method: 'post', 26 | data: qs.stringify(data) 27 | }) 28 | } 29 | 30 | export function updateMenu(data) { 31 | return request({ 32 | url: 'v1/menus/' + data.id, 33 | method: 'put', 34 | data: qs.stringify(data) 35 | }) 36 | } 37 | 38 | export function deleteMenu(data) { 39 | return request({ 40 | url: 'v1/menus/' + data.id, 41 | method: 'delete', 42 | data: qs.stringify(data) 43 | }) 44 | } 45 | 46 | -------------------------------------------------------------------------------- /pkg/webui/src/api/qiniu.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function getToken() { 4 | return request({ 5 | url: '/qiniu/upload/token', // 假地址 自行替换 6 | method: 'get' 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /pkg/webui/src/api/remoteSearch.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function userSearch(name) { 4 | return request({ 5 | url: '/search/user', 6 | method: 'get', 7 | params: { name } 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/src/api/role.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import qs from 'qs' 3 | 4 | export function fetchRoleList(query) { 5 | return request({ 6 | url: 'v1/roles', 7 | method: 'get', 8 | params: query 9 | }) 10 | } 11 | 12 | export function fetchRole(id) { 13 | return request({ 14 | url: 'role/show', 15 | method: 'get', 16 | params: { 17 | id 18 | } 19 | }) 20 | } 21 | 22 | export function createRole(data) { 23 | return request({ 24 | url: 'v1/roles', 25 | method: 'post', 26 | data: qs.stringify(data) 27 | }) 28 | } 29 | 30 | export function copyRole(data) { 31 | return request({ 32 | url: 'v1/roles/' + data.id + '/copy', 33 | method: 'post', 34 | data: qs.stringify(data) 35 | }) 36 | } 37 | 38 | export function updateRole(data) { 39 | return request({ 40 | url: 'v1/roles/' + data.id, 41 | method: 'put', 42 | data: qs.stringify(data) 43 | }) 44 | } 45 | 46 | export function deleteRole(data) { 47 | return request({ 48 | url: 'v1/roles/' + data.id, 49 | method: 'delete', 50 | data: qs.stringify(data) 51 | }) 52 | } 53 | 54 | -------------------------------------------------------------------------------- /pkg/webui/src/api/transaction.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export function fetchList(query) { 4 | return request({ 5 | url: '/transaction/list', 6 | method: 'get', 7 | params: query 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /pkg/webui/src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/404_images/404.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/custom-theme/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/custom-theme/fonts/element-icons.ttf -------------------------------------------------------------------------------- /pkg/webui/src/assets/custom-theme/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/custom-theme/fonts/element-icons.woff -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/background.jpg -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/dingtalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/dingtalk.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/favicon.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/github.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/google-authenticator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/google-authenticator.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/logo-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/logo-min.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/logo.png -------------------------------------------------------------------------------- /pkg/webui/src/assets/images/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/src/assets/images/wechat.png -------------------------------------------------------------------------------- /pkg/webui/src/components/Charts/mixins/resize.js: -------------------------------------------------------------------------------- 1 | import { debounce } from '@/utils' 2 | 3 | export default { 4 | data() { 5 | return { 6 | sidebarElm: null 7 | } 8 | }, 9 | mounted() { 10 | this.__resizeHandler = debounce(() => { 11 | if (this.chart) { 12 | this.chart.resize() 13 | } 14 | }, 100) 15 | window.addEventListener('resize', this.__resizeHandler) 16 | 17 | this.sidebarElm = document.getElementsByClassName('sidebar-container')[0] 18 | this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler) 19 | }, 20 | beforeDestroy() { 21 | window.removeEventListener('resize', this.__resizeHandler) 22 | 23 | this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler) 24 | }, 25 | methods: { 26 | sidebarResizeHandler(e) { 27 | if (e.propertyName === 'width') { 28 | this.__resizeHandler() 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/webui/src/components/DragSelect/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 50 | 51 | 62 | -------------------------------------------------------------------------------- /pkg/webui/src/components/ImageCropper/utils/data2blob.js: -------------------------------------------------------------------------------- 1 | /** 2 | * database64文件格式转换为2进制 3 | * 4 | * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 5 | * @param {[String]} mime [description] 6 | * @return {[blob]} [description] 7 | */ 8 | export default function(data, mime) { 9 | data = data.split(',')[1] 10 | data = window.atob(data) 11 | var ia = new Uint8Array(data.length) 12 | for (var i = 0; i < data.length; i++) { 13 | ia[i] = data.charCodeAt(i) 14 | } 15 | // canvas.toDataURL 返回的默认格式就是 image/png 16 | return new Blob([ia], { 17 | type: mime 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /pkg/webui/src/components/ImageCropper/utils/effectRipple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 点击波纹效果 3 | * 4 | * @param {[event]} e [description] 5 | * @param {[Object]} arg_opts [description] 6 | * @return {[bollean]} [description] 7 | */ 8 | export default function(e, arg_opts) { 9 | var opts = Object.assign({ 10 | ele: e.target, // 波纹作用元素 11 | type: 'hit', // hit点击位置扩散center中心点扩展 12 | bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 13 | }, arg_opts) 14 | var target = opts.ele 15 | if (target) { 16 | var rect = target.getBoundingClientRect() 17 | var ripple = target.querySelector('.e-ripple') 18 | if (!ripple) { 19 | ripple = document.createElement('span') 20 | ripple.className = 'e-ripple' 21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' 22 | target.appendChild(ripple) 23 | } else { 24 | ripple.className = 'e-ripple' 25 | } 26 | switch (opts.type) { 27 | case 'center': 28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' 29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' 30 | break 31 | default: 32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' 33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' 34 | } 35 | ripple.style.backgroundColor = opts.bgc 36 | ripple.className = 'e-ripple z-active' 37 | return false 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/webui/src/components/ImageCropper/utils/mimes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'jpg': 'image/jpeg', 3 | 'png': 'image/png', 4 | 'gif': 'image/gif', 5 | 'svg': 'image/svg+xml', 6 | 'psd': 'image/photoshop' 7 | } 8 | -------------------------------------------------------------------------------- /pkg/webui/src/components/LangSelect/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 32 | 33 | 40 | 41 | -------------------------------------------------------------------------------- /pkg/webui/src/components/MarkdownEditor/defaultOptions.js: -------------------------------------------------------------------------------- 1 | // doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor 2 | export default { 3 | minHeight: '200px', 4 | previewStyle: 'vertical', 5 | useCommandShortcut: true, 6 | useDefaultHTMLSanitizer: true, 7 | usageStatistics: false, 8 | hideModeSwitch: false, 9 | toolbarItems: [ 10 | 'heading', 11 | 'bold', 12 | 'italic', 13 | 'strike', 14 | 'divider', 15 | 'hr', 16 | 'quote', 17 | 'divider', 18 | 'ul', 19 | 'ol', 20 | 'task', 21 | 'indent', 22 | 'outdent', 23 | 'divider', 24 | 'table', 25 | 'image', 26 | 'link', 27 | 'divider', 28 | 'code', 29 | 'codeblock' 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /pkg/webui/src/components/SizeSelect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 47 | 48 | 55 | 56 | -------------------------------------------------------------------------------- /pkg/webui/src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 44 | -------------------------------------------------------------------------------- /pkg/webui/src/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /pkg/webui/src/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /pkg/webui/src/components/TreeTable/eval.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: jianglei 3 | * @Date: 2017-10-12 12:06:49 4 | */ 5 | 'use strict' 6 | import Vue from 'vue' 7 | export default function treeToArray(data, expandAll, parent = null, level = null) { 8 | let tmp = [] 9 | Array.from(data).forEach(function(record) { 10 | if (record._expanded === undefined) { 11 | Vue.set(record, '_expanded', expandAll) 12 | } 13 | let _level = 1 14 | if (level !== undefined && level !== null) { 15 | _level = level + 1 16 | } 17 | Vue.set(record, '_level', _level) 18 | // 如果有父元素 19 | if (parent) { 20 | Vue.set(record, 'parent', parent) 21 | } 22 | tmp.push(record) 23 | if (record.children && record.children.length > 0) { 24 | const children = treeToArray(record.children, expandAll, record, _level) 25 | tmp = tmp.concat(children) 26 | } 27 | }) 28 | return tmp 29 | } 30 | -------------------------------------------------------------------------------- /pkg/webui/src/config/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * @description token在Cookie中存储的天数,默认1天 4 | */ 5 | cookieExpires: 1, 6 | /** 7 | * @description 是否使用国际化,默认为false 8 | * 如果不使用,则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'} 9 | * 用来在菜单中显示文字 10 | */ 11 | useI18n: true, 12 | /** 13 | * @description api请求基础路径 14 | */ 15 | baseUrl: { 16 | dev: 'https://www.easy-mock.com/mock/5add9213ce4d0e69998a6f51/iview-admin/', 17 | pro: 'https://produce.com' 18 | }, 19 | /** 20 | * @description 默认打开的首页的路由name值,默认为home 21 | */ 22 | homeName: '/dashboard', 23 | /** 24 | * @description 需要加载的插件 25 | */ 26 | plugin: { 27 | 'error-store': { 28 | showInHeader: true, // 设为false后不会在顶部显示错误日志徽标 29 | developmentOff: false // 设为true后在开发环境不会收集错误信息,方便开发中排查错误 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/clipboard/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('Clipboard', Clipboard) 5 | } 6 | 7 | if (window.Vue) { 8 | window.clipboard = Clipboard 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | Clipboard.install = install 13 | export default Clipboard 14 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/customEval.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: jianglei 3 | * @Date: 2017-10-12 12:06:49 4 | */ 5 | 'use strict' 6 | import Vue from 'vue' 7 | export default function treeToArray(data, expandAll, parent, level, item) { 8 | const marLTemp = [] 9 | let tmp = [] 10 | Array.from(data).forEach(function(record) { 11 | if (record._expanded === undefined) { 12 | Vue.set(record, '_expanded', expandAll) 13 | } 14 | let _level = 1 15 | if (level !== undefined && level !== null) { 16 | _level = level + 1 17 | } 18 | Vue.set(record, '_level', _level) 19 | // 如果有父元素 20 | if (parent) { 21 | Vue.set(record, 'parent', parent) 22 | // 如果父元素有偏移量,需要计算在this的偏移量中 23 | // 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和 24 | if (!marLTemp[_level]) { 25 | marLTemp[_level] = 0 26 | } 27 | Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft) 28 | Vue.set(record, '_width', record[item] / parent[item] * parent._width) 29 | // 在本次计算过偏移量后加上自己长度,以供下一个元素使用 30 | marLTemp[_level] += record._width 31 | } else { 32 | // 如果为根 33 | // 初始化偏移量存储map 34 | marLTemp[record.id] = [] 35 | // map中是一个数组,存储的是每级的长度和 36 | // 初始情况下为0 37 | marLTemp[record.id][_level] = 0 38 | Vue.set(record, '_marginLeft', 0) 39 | Vue.set(record, '_width', 1) 40 | } 41 | tmp.push(record) 42 | if (record.children && record.children.length > 0) { 43 | const children = treeToArray(record.children, expandAll, record, _level, item) 44 | tmp = tmp.concat(children) 45 | } 46 | }) 47 | return tmp 48 | } 49 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/el-dragDialog/index.js: -------------------------------------------------------------------------------- 1 | import drag from './drag' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('el-drag-dialog', drag) 5 | } 6 | 7 | if (window.Vue) { 8 | window['el-drag-dialog'] = drag 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | drag.install = install 13 | export default drag 14 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/index.js: -------------------------------------------------------------------------------- 1 | import Clipboard from './clipboard/clipboard' 2 | import drag from './el-dragDialog/drag' 3 | import permission from './permission/permission' 4 | import waves from './waves/waves' 5 | 6 | const importDirective = Vue => { 7 | Vue.directive('Clipboard', Clipboard) 8 | Vue.directive('el-drag-dialog', drag) 9 | Vue.directive('permission', permission) 10 | Vue.directive('waves', waves) 11 | } 12 | 13 | export default importDirective 14 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/permission/index.js: -------------------------------------------------------------------------------- 1 | import permission from './permission' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('permission', permission) 5 | } 6 | 7 | if (window.Vue) { 8 | window['permission'] = permission 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | permission.install = install 13 | export default permission 14 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/permission/permission.js: -------------------------------------------------------------------------------- 1 | 2 | import store from '@/store' 3 | 4 | export default{ 5 | inserted(el, binding, vnode) { 6 | const { value } = binding 7 | const auth = store.getters && store.getters.auth 8 | 9 | if (value && value instanceof Array && value.length > 0) { 10 | const permissionRoles = value 11 | 12 | const hasPermission = auth.some(role => { 13 | return permissionRoles.includes(role) 14 | }) 15 | 16 | if (!hasPermission) { 17 | el.parentNode && el.parentNode.removeChild(el) 18 | } 19 | } else { 20 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/waves/index.js: -------------------------------------------------------------------------------- 1 | import waves from './waves' 2 | 3 | const install = function(Vue) { 4 | Vue.directive('waves', waves) 5 | } 6 | 7 | if (window.Vue) { 8 | window.waves = waves 9 | Vue.use(install); // eslint-disable-line 10 | } 11 | 12 | waves.install = install 13 | export default waves 14 | -------------------------------------------------------------------------------- /pkg/webui/src/directive/waves/waves.css: -------------------------------------------------------------------------------- 1 | .waves-ripple { 2 | position: absolute; 3 | border-radius: 100%; 4 | background-color: rgba(0, 0, 0, 0.15); 5 | background-clip: padding-box; 6 | pointer-events: none; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | -ms-user-select: none; 10 | user-select: none; 11 | -webkit-transform: scale(0); 12 | -ms-transform: scale(0); 13 | transform: scale(0); 14 | opacity: 1; 15 | } 16 | 17 | .waves-ripple.z-active { 18 | opacity: 0; 19 | -webkit-transform: scale(2); 20 | -ms-transform: scale(2); 21 | transform: scale(2); 22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; 24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out; 25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; 26 | } -------------------------------------------------------------------------------- /pkg/webui/src/filters/index.js: -------------------------------------------------------------------------------- 1 | // set function parseTime,formatTime to filter 2 | export { parseTime, formatTime } from '@/utils' 3 | 4 | function pluralize(time, label) { 5 | if (time === 1) { 6 | return time + label 7 | } 8 | return time + label + 's' 9 | } 10 | 11 | export function timeAgo(time) { 12 | const between = Date.now() / 1000 - Number(time) 13 | if (between < 3600) { 14 | return pluralize(~~(between / 60), ' minute') 15 | } else if (between < 86400) { 16 | return pluralize(~~(between / 3600), ' hour') 17 | } else { 18 | return pluralize(~~(between / 86400), ' day') 19 | } 20 | } 21 | 22 | /* 数字 格式化*/ 23 | export function numberFormatter(num, digits) { 24 | const si = [ 25 | { value: 1E18, symbol: 'E' }, 26 | { value: 1E15, symbol: 'P' }, 27 | { value: 1E12, symbol: 'T' }, 28 | { value: 1E9, symbol: 'G' }, 29 | { value: 1E6, symbol: 'M' }, 30 | { value: 1E3, symbol: 'k' } 31 | ] 32 | for (let i = 0; i < si.length; i++) { 33 | if (num >= si[i].value) { 34 | return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol 35 | } 36 | } 37 | return num.toString() 38 | } 39 | 40 | export function toThousandFilter(num) { 41 | return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ',')) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg组件 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/guide 2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svg/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pkg/webui/src/icons/svgo.yml: -------------------------------------------------------------------------------- 1 | # replace default config 2 | 3 | # multipass: true 4 | # full: true 5 | 6 | plugins: 7 | 8 | # - name 9 | # 10 | # or: 11 | # - name: false 12 | # - name: true 13 | # 14 | # or: 15 | # - name: 16 | # param1: 1 17 | # param2: 2 18 | 19 | - removeAttrs: 20 | attrs: 21 | - 'fill' 22 | - 'fill-rule' 23 | -------------------------------------------------------------------------------- /pkg/webui/src/lang/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import Cookies from 'js-cookie' 4 | import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang 5 | import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang 6 | import enLocale from './en' 7 | import zhLocale from './zh' 8 | 9 | Vue.use(VueI18n) 10 | 11 | const messages = { 12 | en: { 13 | ...enLocale, 14 | ...elementEnLocale 15 | }, 16 | zh: { 17 | ...zhLocale, 18 | ...elementZhLocale 19 | } 20 | } 21 | 22 | const i18n = new VueI18n({ 23 | // set locale 24 | // options: en | zh | es 25 | locale: Cookies.get('language') || 'zh', 26 | // set locale messages 27 | messages 28 | }) 29 | 30 | export default i18n 31 | -------------------------------------------------------------------------------- /pkg/webui/src/mock/index.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import loginAPI from './login' 3 | 4 | // 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题 5 | // https://github.com/nuysoft/Mock/issues/300 6 | Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send 7 | Mock.XHR.prototype.send = function() { 8 | if (this.custom.xhr) { 9 | this.custom.xhr.withCredentials = this.withCredentials || false 10 | } 11 | this.proxy_send(...arguments) 12 | } 13 | 14 | // Mock.setup({ 15 | // timeout: '350-600' 16 | // }) 17 | 18 | // 登录相关 19 | Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername) 20 | Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) 21 | Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo) 22 | 23 | export default Mock 24 | -------------------------------------------------------------------------------- /pkg/webui/src/mock/login.js: -------------------------------------------------------------------------------- 1 | import { param2Obj } from '@/utils' 2 | 3 | const userMap = { 4 | admin: { 5 | roles: ['admin'], 6 | token: 'admin', 7 | introduction: '我是超级管理员', 8 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 9 | name: 'Super Admin' 10 | }, 11 | editor: { 12 | roles: ['editor'], 13 | token: 'editor', 14 | introduction: '我是编辑', 15 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', 16 | name: 'Normal Editor' 17 | } 18 | } 19 | 20 | export default { 21 | loginByUsername: config => { 22 | const { username } = JSON.parse(config.body) 23 | return userMap[username] 24 | }, 25 | getUserInfo: config => { 26 | const { token } = param2Obj(config.url) 27 | if (userMap[token]) { 28 | return userMap[token] 29 | } else { 30 | return false 31 | } 32 | }, 33 | logout: () => 'success' 34 | } 35 | -------------------------------------------------------------------------------- /pkg/webui/src/mock/remoteSearch.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | import { param2Obj } from '@/utils' 3 | 4 | const NameList = [] 5 | const count = 100 6 | 7 | for (let i = 0; i < count; i++) { 8 | NameList.push(Mock.mock({ 9 | name: '@first' 10 | })) 11 | } 12 | NameList.push({ name: 'mockPan' }) 13 | 14 | export default { 15 | searchUser: config => { 16 | const { name } = param2Obj(config.url) 17 | const mockNameList = NameList.filter(item => { 18 | const lowerCaseName = item.name.toLowerCase() 19 | if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false 20 | return true 21 | }) 22 | return { items: mockNameList } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pkg/webui/src/mock/transaction.js: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | 3 | const List = [] 4 | const count = 20 5 | 6 | for (let i = 0; i < count; i++) { 7 | List.push(Mock.mock({ 8 | order_no: '@guid()', 9 | timestamp: +Mock.Random.date('T'), 10 | username: '@name()', 11 | price: '@float(1000, 15000, 0, 2)', 12 | 'status|1': ['success', 'pending'] 13 | })) 14 | } 15 | 16 | export default { 17 | getList: () => { 18 | return { 19 | total: List.length, 20 | items: List 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/webui/src/plugin/index.js: -------------------------------------------------------------------------------- 1 | import config from '@/config' 2 | const { plugin } = config 3 | 4 | export default (Vue) => { 5 | for (const name in plugin) { 6 | const value = plugin[name] 7 | Vue.use(require(`./${name}`).default, typeof value === 'object' ? value : undefined) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /pkg/webui/src/router/modules/charts.js: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules**/ 2 | 3 | import Layout from '@/views/layout/Layout' 4 | 5 | const chartsRouter = { 6 | path: '/charts', 7 | component: Layout, 8 | redirect: 'noredirect', 9 | name: 'Charts', 10 | meta: { 11 | title: 'charts', 12 | icon: 'chart' 13 | }, 14 | children: [ 15 | { 16 | path: 'keyboard', 17 | component: () => import('@/views/charts/keyboard'), 18 | name: 'KeyboardChart', 19 | meta: { title: 'keyboardChart', noCache: true } 20 | }, 21 | { 22 | path: 'line', 23 | component: () => import('@/views/charts/line'), 24 | name: 'LineChart', 25 | meta: { title: 'lineChart', noCache: true } 26 | }, 27 | { 28 | path: 'mixchart', 29 | component: () => import('@/views/charts/mixChart'), 30 | name: 'MixChart', 31 | meta: { title: 'mixChart', noCache: true } 32 | } 33 | ] 34 | } 35 | 36 | export default chartsRouter 37 | -------------------------------------------------------------------------------- /pkg/webui/src/router/modules/table.js: -------------------------------------------------------------------------------- 1 | /** When your routing table is too long, you can split it into small modules**/ 2 | 3 | import Layout from '@/views/layout/Layout' 4 | 5 | const tableRouter = { 6 | path: '/table', 7 | component: Layout, 8 | redirect: '/table/complex-table', 9 | name: 'Table', 10 | meta: { 11 | title: 'Table', 12 | icon: 'table' 13 | }, 14 | children: [ 15 | { 16 | path: 'dynamic-table', 17 | component: () => import('@/views/table/dynamicTable/index'), 18 | name: 'DynamicTable', 19 | meta: { title: 'dynamicTable' } 20 | }, 21 | { 22 | path: 'drag-table', 23 | component: () => import('@/views/table/dragTable'), 24 | name: 'DragTable', 25 | meta: { title: 'dragTable' } 26 | }, 27 | { 28 | path: 'inline-edit-table', 29 | component: () => import('@/views/table/inlineEditTable'), 30 | name: 'InlineEditTable', 31 | meta: { title: 'inlineEditTable' } 32 | }, 33 | { 34 | path: 'tree-table', 35 | component: () => import('@/views/table/treeTable/treeTable'), 36 | name: 'TreeTableDemo', 37 | meta: { title: 'treeTable' } 38 | }, 39 | { 40 | path: 'custom-tree-table', 41 | component: () => import('@/views/table/treeTable/customTreeTable'), 42 | name: 'CustomTreeTableDemo', 43 | meta: { title: 'customTreeTable' } 44 | }, 45 | { 46 | path: 'complex-table', 47 | component: () => import('@/views/table/complexTable'), 48 | name: 'ComplexTable', 49 | meta: { title: 'complexTable' } 50 | } 51 | ] 52 | } 53 | export default tableRouter 54 | -------------------------------------------------------------------------------- /pkg/webui/src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | sidebar: state => state.app.sidebar, 3 | language: state => state.app.language, 4 | size: state => state.app.size, 5 | device: state => state.app.device, 6 | visitedViews: state => state.tagsView.visitedViews, 7 | cachedViews: state => state.tagsView.cachedViews, 8 | token: state => state.user.token, 9 | avatar: state => state.user.avatar, 10 | name: state => state.user.name, 11 | introduction: state => state.user.introduction, 12 | status: state => state.user.status, 13 | roles: state => state.user.roles, 14 | auth: state => state.user.auth, 15 | setting: state => state.user.setting, 16 | permission_routers: state => state.permission.routers, 17 | addRouters: state => state.permission.addRouters, 18 | errorLogs: state => state.errorLog.logs 19 | } 20 | export default getters 21 | -------------------------------------------------------------------------------- /pkg/webui/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import app from './modules/app' 4 | import errorLog from './modules/errorLog' 5 | import permission from './modules/permission' 6 | import tagsView from './modules/tagsView' 7 | import user from './modules/user' 8 | import getters from './getters' 9 | 10 | Vue.use(Vuex) 11 | 12 | const store = new Vuex.Store({ 13 | modules: { 14 | app, 15 | errorLog, 16 | permission, 17 | tagsView, 18 | user 19 | }, 20 | getters 21 | }) 22 | 23 | export default store 24 | -------------------------------------------------------------------------------- /pkg/webui/src/store/modules/errorLog.js: -------------------------------------------------------------------------------- 1 | const errorLog = { 2 | state: { 3 | logs: [] 4 | }, 5 | mutations: { 6 | ADD_ERROR_LOG: (state, log) => { 7 | state.logs.push(log) 8 | } 9 | }, 10 | actions: { 11 | addErrorLog({ commit }, log) { 12 | commit('ADD_ERROR_LOG', log) 13 | } 14 | } 15 | } 16 | 17 | export default errorLog 18 | -------------------------------------------------------------------------------- /pkg/webui/src/styles/element-ui.scss: -------------------------------------------------------------------------------- 1 | //覆盖一些element-ui样式 2 | 3 | .el-breadcrumb__inner, .el-breadcrumb__inner a{ 4 | font-weight: 400!important; 5 | } 6 | 7 | .el-upload { 8 | input[type="file"] { 9 | display: none !important; 10 | } 11 | } 12 | 13 | .el-upload__input { 14 | display: none; 15 | } 16 | 17 | .cell { 18 | .el-tag { 19 | margin-right: 0px; 20 | } 21 | } 22 | 23 | .small-padding { 24 | .cell { 25 | padding-left: 5px; 26 | padding-right: 5px; 27 | } 28 | } 29 | 30 | .fixed-width{ 31 | .el-button--mini{ 32 | padding: 7px 10px; 33 | width: 60px; 34 | } 35 | } 36 | 37 | .status-col { 38 | .cell { 39 | padding: 0 10px; 40 | text-align: center; 41 | .el-tag { 42 | margin-right: 0px; 43 | } 44 | } 45 | } 46 | 47 | //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461 48 | .el-dialog { 49 | transform: none; 50 | left: 0; 51 | position: relative; 52 | margin: 0 auto; 53 | } 54 | 55 | //文章页textarea修改样式 56 | .article-textarea { 57 | textarea { 58 | padding-right: 40px; 59 | resize: none; 60 | border: none; 61 | border-radius: 0px; 62 | border-bottom: 1px solid #bfcbd9; 63 | } 64 | } 65 | 66 | //element ui upload 67 | .upload-container { 68 | .el-upload { 69 | width: 100%; 70 | .el-upload-dragger { 71 | width: 100%; 72 | height: 200px; 73 | } 74 | } 75 | } 76 | 77 | //dropdown 78 | .el-dropdown-menu{ 79 | a{ 80 | display: block 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /pkg/webui/src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | @mixin scrollBar { 10 | &::-webkit-scrollbar-track-piece { 11 | background: #d3dce6; 12 | } 13 | &::-webkit-scrollbar { 14 | width: 6px; 15 | } 16 | &::-webkit-scrollbar-thumb { 17 | background: #99a9bf; 18 | border-radius: 20px; 19 | } 20 | } 21 | 22 | @mixin relative { 23 | position: relative; 24 | width: 100%; 25 | height: 100%; 26 | } 27 | 28 | @mixin pct($pct) { 29 | width: #{$pct}; 30 | position: relative; 31 | margin: 0 auto; 32 | } 33 | 34 | @mixin triangle($width, $height, $color, $direction) { 35 | $width: $width/2; 36 | $color-border-style: $height solid $color; 37 | $transparent-border-style: $width solid transparent; 38 | height: 0; 39 | width: 0; 40 | @if $direction==up { 41 | border-bottom: $color-border-style; 42 | border-left: $transparent-border-style; 43 | border-right: $transparent-border-style; 44 | } 45 | @else if $direction==right { 46 | border-left: $color-border-style; 47 | border-top: $transparent-border-style; 48 | border-bottom: $transparent-border-style; 49 | } 50 | @else if $direction==down { 51 | border-top: $color-border-style; 52 | border-left: $transparent-border-style; 53 | border-right: $transparent-border-style; 54 | } 55 | @else if $direction==left { 56 | border-right: $color-border-style; 57 | border-top: $transparent-border-style; 58 | border-bottom: $transparent-border-style; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pkg/webui/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | //globl transition css 2 | 3 | /*fade*/ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /*fade-transform*/ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | .fade-transform-enter { 20 | opacity: 0; 21 | transform: translateX(-30px); 22 | } 23 | .fade-transform-leave-to { 24 | opacity: 0; 25 | transform: translateX(30px); 26 | } 27 | 28 | /*breadcrumb transition*/ 29 | .breadcrumb-enter-active, 30 | .breadcrumb-leave-active { 31 | transition: all .5s; 32 | } 33 | 34 | .breadcrumb-enter, 35 | .breadcrumb-leave-active { 36 | opacity: 0; 37 | transform: translateX(20px); 38 | } 39 | 40 | .breadcrumb-move { 41 | transition: all .5s; 42 | } 43 | 44 | .breadcrumb-leave-active { 45 | position: absolute; 46 | } 47 | 48 | -------------------------------------------------------------------------------- /pkg/webui/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $blue:#324157; 2 | $light-blue:#3A71A8; 3 | $red:#C03639; 4 | $pink: #E65D6E; 5 | $green: #30B08F; 6 | $tiffany: #4AB7BD; 7 | $yellow:#FEC171; 8 | $panGreen: #30B08F; 9 | 10 | //sidebar 11 | $menuBg:#304156; 12 | $subMenuBg:#1f2d3d; 13 | $menuHover:#001528; 14 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie' 2 | import { 3 | getDomainHost 4 | } from '@/utils' 5 | 6 | const TokenKey = 'jwt' 7 | export function getToken() { 8 | return Cookies.get(TokenKey) || '' 9 | } 10 | 11 | export function setToken(token) { 12 | if (!token) { 13 | console.warn('设置 cookies token 失败 token:' + token) 14 | return false 15 | } 16 | 17 | // 打包的环境 18 | if (process.env.NODE_ENV === 'production') { 19 | /** 20 | * @description 设置主域 .bullteam.xxx 21 | * 1. 获取当前域名对应的环境 22 | * 2. 设置当前环境的主域名 23 | */ 24 | const domainHost = getDomainHost() 25 | Cookies.set(TokenKey, token, { 26 | domain: domainHost.domain 27 | }) 28 | } else { 29 | // 设置 localhost:8080 | 127.0.0.1:8080 30 | Cookies.set(TokenKey, token) 31 | } 32 | } 33 | 34 | export function removeToken() { 35 | // 打包的环境 36 | if (process.env.NODE_ENV === 'production') { 37 | /** 38 | * @description 设置主域 .bullteam.xxx 39 | * 1. 获取当前域名对应的环境 40 | * 2. 设置当前环境的主域名 41 | */ 42 | const domainHost = getDomainHost() 43 | Cookies.remove(TokenKey, { 44 | domain: domainHost.domain 45 | }) 46 | } else { 47 | // 设置 localhost:8080 | 127.0.0.1:8080 48 | Cookies.remove(TokenKey) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Clipboard from 'clipboard' 3 | 4 | function clipboardSuccess() { 5 | Vue.prototype.$message({ 6 | message: 'Copy successfully', 7 | type: 'success', 8 | duration: 1500 9 | }) 10 | } 11 | 12 | function clipboardError() { 13 | Vue.prototype.$message({ 14 | message: 'Copy failed', 15 | type: 'error' 16 | }) 17 | } 18 | 19 | export default function handleClipboard(text, event) { 20 | const clipboard = new Clipboard(event.target, { 21 | text: () => text 22 | }) 23 | clipboard.on('success', () => { 24 | clipboardSuccess() 25 | clipboard.off('error') 26 | clipboard.off('success') 27 | clipboard.destroy() 28 | }) 29 | clipboard.on('error', () => { 30 | clipboardError() 31 | clipboard.off('error') 32 | clipboard.off('success') 33 | clipboard.destroy() 34 | }) 35 | clipboard.onClick(event) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/createUniqueString.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 17/3/8. 3 | */ 4 | export default function createUniqueString() { 5 | const timestamp = +new Date() + '' 6 | const randomNum = parseInt((1 + Math.random()) * 65536) + '' 7 | return (+(randomNum + timestamp)).toString(32) 8 | } 9 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/i18n.js: -------------------------------------------------------------------------------- 1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview 2 | export function generateTitle(title) { 3 | const hasKey = this.$te('route.' + title) 4 | 5 | if (hasKey) { 6 | // $t :this method from vue-i18n, inject in @/lang/index.js 7 | const translatedTitle = this.$t('route.' + title) 8 | 9 | return translatedTitle 10 | } 11 | return title 12 | } 13 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/openWindow.js: -------------------------------------------------------------------------------- 1 | /** 2 | *Created by jiachenpan on 16/11/29. 3 | * @param {Sting} url 4 | * @param {Sting} title 5 | * @param {Number} w 6 | * @param {Number} h 7 | */ 8 | 9 | export default function openWindow(url, title, w, h) { 10 | // Fixes dual-screen position Most browsers Firefox 11 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left 12 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top 13 | 14 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width 15 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height 16 | 17 | const left = ((width / 2) - (w / 2)) + dualScreenLeft 18 | const top = ((height / 2) - (h / 2)) + dualScreenTop 19 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left) 20 | 21 | // Puts focus on the newWindow 22 | if (window.focus) { 23 | newWindow.focus() 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/permission.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | /** 4 | * @param {Array} value 5 | * @returns {Boolean} 6 | * @example see @/views/permission/directive.vue 7 | */ 8 | export default function checkPermission(value) { 9 | if (value && value instanceof Array && value.length > 0) { 10 | const roles = store.getters && store.getters.roles 11 | const permissionRoles = value 12 | 13 | const hasPermission = roles.some(role => { 14 | return permissionRoles.includes(role) 15 | }) 16 | 17 | if (!hasPermission) { 18 | return false 19 | } 20 | return true 21 | } else { 22 | console.error(`need roles! Like v-permission="['admin','editor']"`) 23 | return false 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/requireIcons.js: -------------------------------------------------------------------------------- 1 | 2 | const req = require.context('@/icons/svg', false, /\.svg$/) 3 | const requireAll = requireContext => requireContext.keys() 4 | 5 | const re = /\.\/(.*)\.svg/ 6 | 7 | const icons = requireAll(req).map(i => { 8 | return i.match(re)[1] 9 | }) 10 | 11 | export default icons 12 | -------------------------------------------------------------------------------- /pkg/webui/src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jiachenpan on 16/11/18. 3 | */ 4 | 5 | export function isvalidUsername(str) { 6 | const valid_map = ['admin', 'editor'] 7 | return valid_map.indexOf(str.trim()) >= 0 8 | } 9 | 10 | /* 合法uri*/ 11 | export function validateURL(textval) { 12 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 13 | return urlregex.test(textval) 14 | } 15 | 16 | /* 小写字母*/ 17 | export function validateLowerCase(str) { 18 | const reg = /^[a-z]+$/ 19 | return reg.test(str) 20 | } 21 | 22 | /* 大写字母*/ 23 | export function validateUpperCase(str) { 24 | const reg = /^[A-Z]+$/ 25 | return reg.test(str) 26 | } 27 | 28 | /* 大小写字母*/ 29 | export function validateAlphabets(str) { 30 | const reg = /^[A-Za-z]+$/ 31 | return reg.test(str) 32 | } 33 | 34 | /** 35 | * validate email 36 | * @param email 37 | * @returns {boolean} 38 | */ 39 | export function validateEmail(email) { 40 | const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 41 | return re.test(email) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/webui/src/vendor/Export2Zip.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('script-loader!file-saver'); 3 | import JSZip from 'jszip' 4 | 5 | export function export_txt_to_zip(th, jsonData, txtName, zipName) { 6 | const zip = new JSZip() 7 | const txt_name = txtName || 'file' 8 | const zip_name = zipName || 'file' 9 | const data = jsonData 10 | let txtData = `${th}\r\n` 11 | data.forEach((row) => { 12 | let tempStr = '' 13 | tempStr = row.toString() 14 | txtData += `${tempStr}\r\n` 15 | }) 16 | zip.file(`${txt_name}.txt`, txtData) 17 | zip.generateAsync({ 18 | type: "blob" 19 | }).then((blob) => { 20 | saveAs(blob, `${zip_name}.zip`) 21 | }, (err) => { 22 | alert('导出失败') 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/webui/src/views/install/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 25 | 26 | 57 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | 34 | 35 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/components/Sidebar/FixiOSBug.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | device() { 4 | return this.$store.state.app.device 5 | } 6 | }, 7 | mounted() { 8 | // In order to fix the click on menu on the ios device will trigger the mouseeleave bug 9 | // https://github.com/PanJiaChen/vue-element-admin/issues/1135 10 | this.fixBugIniOS() 11 | }, 12 | methods: { 13 | fixBugIniOS() { 14 | const $submenu = this.$refs.submenu 15 | if ($submenu) { 16 | const handleMouseleave = $submenu.handleMouseleave 17 | $submenu.handleMouseleave = (e) => { 18 | if (this.device === 'mobile') { 19 | return 20 | } 21 | handleMouseleave(e) 22 | } 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/components/Sidebar/Item.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/components/Sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 40 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar' 2 | export { default as Sidebar } from './Sidebar/index.vue' 3 | export { default as TagsView } from './TagsView' 4 | export { default as AppMain } from './AppMain' 5 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/mixin/PreCheck.js: -------------------------------------------------------------------------------- 1 | import { checkIdle } from '../../../api/user' 2 | export default { 3 | name: 'PreCheck', 4 | mounted() { 5 | // todo : check if password not chant for a long time 6 | // 如果此后台需要过等保3,可以开放下这个定时空闲检查函数 7 | // this.intervalCheckIdle() 8 | }, 9 | methods: { 10 | intervalCheckIdle: function() { 11 | // eslint-disable-next-line no-unused-vars 12 | let timer 13 | checkIdle({}).then(res => { 14 | if (res.data.idle) { 15 | clearTimeout(timer) 16 | const self = this 17 | // 3 seconds later log out directly 18 | setTimeout(function() { 19 | self.$store.dispatch('LogOut').then(() => { 20 | location.reload() // In order to re-instantiate the vue-router object to avoid bugs 21 | }) 22 | }, 1500) 23 | this.$alert('系统检测到您1小时内无操作,请重新登录!', '警告', { 24 | confirmButtonText: '确定', 25 | showConfirmButton: false, 26 | callback: action => { 27 | // eslint-disable-next-line no-const-assign 28 | // eslint-disable-next-line no-undef 29 | // this.$store.dispatch('LogOut').then(() => { 30 | // location.reload() // In order to re-instantiate the vue-router object to avoid bugs 31 | // }) 32 | } 33 | }) 34 | } else { 35 | timer = setTimeout(this.intervalCheckIdle, 1000 * 60 * 5) 36 | } 37 | }) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/mixin/PwdChangeCheck.js: -------------------------------------------------------------------------------- 1 | import { checkChangePwd } from '../../../api/user' 2 | export default { 3 | name: 'PwdChangeCheck', 4 | created() { 5 | // todo : check if password not chant for a long time 6 | checkChangePwd({}).then(res => { 7 | if (res.data.needed) { 8 | if (res.data.days > 0) { 9 | this.$confirm('密码将于' + res.data.days + '天内过期,请及时更新密码', '警告', { 10 | confirmButtonText: '立即更换', 11 | showCancelButton: true, 12 | cancelButtonText: '稍后更换', 13 | type: 'warning' 14 | }).then(() => { 15 | location.href = '/#/my/psw' 16 | }) 17 | } else { 18 | this.$confirm('密码已过期,请联系管理员!', '警告', { 19 | confirmButtonText: '退出登录', 20 | showCancelButton: false, 21 | cancelButtonText: '稍后更换', 22 | type: 'error' 23 | }).then(() => { 24 | this.$store.dispatch('LogOut').then(() => { 25 | location.reload() // In order to re-instantiate the vue-router object to avoid bugs 26 | }) 27 | }) 28 | const _this = this 29 | setTimeout(() => { 30 | _this.$store.dispatch('LogOut').then(() => { 31 | location.reload() // In order to re-instantiate the vue-router object to avoid bugs 32 | }) 33 | }, 3000) 34 | } 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/webui/src/views/layout/mixin/ResizeHandler.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | 3 | const { body } = document 4 | const WIDTH = 1024 5 | const RATIO = 3 6 | 7 | export default { 8 | watch: { 9 | $route(route) { 10 | if (this.device === 'mobile' && this.sidebar.opened) { 11 | store.dispatch('closeSideBar', { withoutAnimation: false }) 12 | } 13 | } 14 | }, 15 | beforeMount() { 16 | window.addEventListener('resize', this.resizeHandler) 17 | }, 18 | mounted() { 19 | const isMobile = this.isMobile() 20 | if (isMobile) { 21 | store.dispatch('toggleDevice', 'mobile') 22 | store.dispatch('closeSideBar', { withoutAnimation: true }) 23 | } 24 | }, 25 | methods: { 26 | isMobile() { 27 | const rect = body.getBoundingClientRect() 28 | return rect.width - RATIO < WIDTH 29 | }, 30 | resizeHandler() { 31 | if (!document.hidden) { 32 | const isMobile = this.isMobile() 33 | store.dispatch('toggleDevice', isMobile ? 'mobile' : 'desktop') 34 | 35 | if (isMobile) { 36 | store.dispatch('closeSideBar', { withoutAnimation: true }) 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/webui/src/views/login/authredirect.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /pkg/webui/src/views/logs/error.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /pkg/webui/src/views/my/personal.block.js: -------------------------------------------------------------------------------- 1 | export default { 2 | type: 'form', 3 | ctx: 'edit', 4 | data: { 5 | }, 6 | // props: { 7 | // labelWidth: '200px' 8 | // }, 9 | resource: { 10 | api: { 11 | prefix: process.env['ZEUS_ADMIN_URL'] || '/', 12 | contentType: 'form', 13 | update: '/v1/user' 14 | }, 15 | fields: { 16 | avatar: { 17 | type: 'image', 18 | label: 'avatar' 19 | }, 20 | name: { 21 | type: 'text', 22 | label: 'name', 23 | style: { 24 | width: '200px' 25 | } 26 | }, 27 | introduction: { 28 | type: 'text', 29 | label: 'introduction' 30 | } 31 | } 32 | }, 33 | operations: { 34 | install: { 35 | type: 'button', 36 | label: '保存', 37 | event: 'update', 38 | // event: 'install', 39 | props: { 40 | type: 'primary' 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/webui/src/views/my/personal.vue: -------------------------------------------------------------------------------- 1 | 6 | 32 | 39 | -------------------------------------------------------------------------------- /pkg/webui/src/views/permission/deptrw.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /pkg/webui/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /pkg/webui/src/views/setting/auth.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /pkg/webui/src/views/setting/email.block.js: -------------------------------------------------------------------------------- 1 | const settingBlock = { 2 | type: 'form', 3 | ctx: 'edit', 4 | data: { 5 | }, 6 | props: { 7 | labelWidth: '200px' 8 | }, 9 | resource: { 10 | api: { 11 | prefix: process.env['ZEUS_ADMIN_URL'] || '/', 12 | contentType: 'form', 13 | update: '/v1/setting/email', 14 | read: '/v1/setting/email' 15 | }, 16 | fields: { 17 | smtpServer: { 18 | type: 'text', 19 | label: 'SMTP主机' 20 | }, 21 | smtpAddress: { 22 | type: 'text', 23 | label: '电子邮件发件人' 24 | }, 25 | smtpUser: { 26 | type: 'text', 27 | label: 'SMTP 用户名' 28 | }, 29 | smtpPassword: { 30 | type: 'text', 31 | label: 'SMTP 密码' 32 | }, 33 | adminEmail: { 34 | type: 'text', 35 | label: '电子邮件地址' 36 | } 37 | } 38 | }, 39 | actions: { 40 | async init() { 41 | const host = process.env['ZEUS_ADMIN_URL'] || '/' 42 | const res = await this.$ams.request({ 43 | url: `${host}/v1/setting/email` 44 | }) 45 | this.data = res.data.data.list 46 | } 47 | }, 48 | style: { 49 | width: '60%' 50 | }, 51 | operations: { 52 | install: { 53 | type: 'button', 54 | label: '保存', 55 | event: 'update', 56 | // event: 'install', 57 | props: { 58 | type: 'primary' 59 | } 60 | } 61 | }, 62 | blocks: { 63 | } 64 | } 65 | 66 | export default settingBlock 67 | 68 | -------------------------------------------------------------------------------- /pkg/webui/src/views/setting/email.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /pkg/webui/src/views/system/params.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /pkg/webui/static/images/wave-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/images/wave-back.png -------------------------------------------------------------------------------- /pkg/webui/static/images/wave-front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/images/wave-front.png -------------------------------------------------------------------------------- /pkg/webui/static/images/wave-middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/images/wave-middle.png -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-cool.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-cry.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-embarassed.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-foot-in-mouth.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-frown.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-innocent.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-kiss.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-laughing.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-money-mouth.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-sealed.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-smile.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-surprised.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-tongue-out.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-undecided.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-wink.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/plugins/emoticons/img/smiley-yell.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-mobile.woff -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.eot -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.ttf -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce-small.woff -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.eot -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.ttf -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/fonts/tinymce.woff -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/img/anchor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/img/anchor.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/img/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/img/loader.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/img/object.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/img/object.gif -------------------------------------------------------------------------------- /pkg/webui/static/tinymce4.7.5/skins/lightgray/img/trans.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/pkg/webui/static/tinymce4.7.5/skins/lightgray/img/trans.gif -------------------------------------------------------------------------------- /scripts/apppkg.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # get import path of current go project. 4 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 5 | echo ${ROOT//$GOPATH\/src\//} 6 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export GO111MODULE=on 3 | export GOPROXY=https://goproxy.cn 4 | go build --ldflags "-extldflags -static" -o zeus 5 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export GO111MODULE=on 3 | export GOPROXY=https://goproxy.cn 4 | go build --ldflags "-extldflags -static" -o zeus 5 | -------------------------------------------------------------------------------- /scripts/docker: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | repo=bullteam/zeus-admin:latest 3 | if [[ ! -z $1 ]]; then 4 | repo=bullteam/zeus-admin:$1 5 | fi 6 | target=${repo} 7 | docker build -t ${repo} . 8 | docker tag ${repo} ${target} 9 | docker push ${target} -------------------------------------------------------------------------------- /scripts/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | repo=bullteam/zeus-admin:latest 3 | if [[ ! -z $1 ]]; then 4 | repo=bullteam/zeus-admin:$1 5 | fi 6 | target=${repo} 7 | docker build -t ${repo} . 8 | docker tag ${repo} ${target} 9 | docker push ${target} -------------------------------------------------------------------------------- /scripts/getlatest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir zeus 4 | curl -L https://api.github.com/repos/bullteam/zeus-admin/tarball | tar -zx -C zeus --strip-components=1 5 | 6 | cd zeus 7 | curl -s https://api.github.com/repos/bullteam/zeus-admin/releases/latest | grep "browser_download_url" | cut -d : -f 2,3 | tr -d \" | wget -qi - && tar zxvf manifest.tar.gz 8 | 9 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export MYSQL_USERNAME=root 4 | export MYSQL_PASSWORD=root 5 | export MYSQL_HOST=127.0.0.1 6 | export MYSQL_DB=zeus 7 | export MYSQL_PORT=3306 8 | export REDIS_HOST=127.0.0.1 9 | export REDIS_PORT=6379 10 | export REDIS_PASSWORD="" 11 | 12 | 13 | function SysTem() { 14 | a="uname $(-a)" 15 | 16 | if [[ $a =~ "Darwin" ]];then 17 | os_name="darwin" 18 | elif [[ $a =~ "MINGW" || $a =~ "MSYS" ]];then 19 | os_name="windows" 20 | else 21 | os_name="linux" 22 | fi 23 | } 24 | 25 | SysTem 26 | server_bin="ls ${os_name}*" 27 | echo "${server_bin}" 28 | 29 | ./"${server_bin}" server -------------------------------------------------------------------------------- /scripts/sdk/go/README.md: -------------------------------------------------------------------------------- 1 | #Zeusclient-go 宙斯后台权限系统golang客户端 2 | 3 | 4 | ``` 5 | import "ZeusSdk" 6 | var ( 7 | ZeusService = ZeusSdkService{} 8 | ) 9 | func main(){ 10 | tokenStr := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODg0Mjc5MTksImlkIjoyLCJuYW1lIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU4ODM0MTUxOSwidWlkIjoyLCJ1bmFtZSI6ImFkbWluIn0.A15i8yx8R3p386_1Li124StTyRkw24lREPHmDsq6uR_fJCReKLaOU3--66xuUyG285ZcAZiz3a8P-Dv0sj6H3j2mgi5pnOFrXWwJX51OWeDvOCoKajyDtvwp-FZxa33XOajzWe2F9Z4IIMY5CK8ljK5slg9bkIU8F0oT_IPEyIo" 11 | //校验token是否过期 12 | VerifyTokenRes := ZeusService.VerifyToken(tokenStr) 13 | if VerifyTokenRes != nil { 14 | // 抛错处理 15 | } 16 | checkPermRes := ZeusService.checkPerm($urlStr) 17 | if checkPermRes != nil { 18 | //没有权限 19 | } 20 | } 21 | ``` -------------------------------------------------------------------------------- /scripts/sdk/go/perm_test.go: -------------------------------------------------------------------------------- 1 | package ZeusSdk 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | const tokenStr = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODg0Mjc5MTksImlkIjoyLCJuYW1lIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTU4ODM0MTUxOSwidWlkIjoyLCJ1bmFtZSI6ImFkbWluIn0.A15i8yx8R3p386_1Li124StTyRkw24lREPHmDsq6uR_fJCReKLaOU3--66xuUyG285ZcAZiz3a8P-Dv0sj6H3j2mgi5pnOFrXWwJX51OWeDvOCoKajyDtvwp-FZxa33XOajzWe2F9Z4IIMY5CK8ljK5slg9bkIU8F0oT_IPEyIo" 10 | 11 | var ( 12 | err error 13 | ZeusService = ZeusSdkService{} 14 | ) 15 | 16 | func TestCheckPerm(t *testing.T) { 17 | str, _ := ZeusService.CheckPerm(tokenStr, "zeus-config", "/configlist/list") 18 | fmt.Print(str) 19 | assert.Equal(t, nil, err) 20 | } 21 | func TestGetUserPerms(t *testing.T) { 22 | str, _ := ZeusService.GetUserPerms(tokenStr, "zeus-config") 23 | fmt.Print(str) 24 | assert.Equal(t, nil, err) 25 | } 26 | func TestVerify(t *testing.T) { 27 | str := ZeusService.VerifyToken(tokenStr) 28 | fmt.Print(str) 29 | assert.Equal(t, nil, err) 30 | } 31 | 32 | func TestGetUserInfo(t *testing.T) { 33 | _, str := ZeusService.GetUserInfo(tokenStr) 34 | fmt.Print(str) 35 | assert.Equal(t, nil, err) 36 | } 37 | -------------------------------------------------------------------------------- /scripts/sdk/go/pub_key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/TYKuXsgYdoICfEZOiy1L12Cb 3 | yPdudhrCjrjwVcIrhGNn6Udq/SY5rh0ixm09I2tXPWLYuA1R55kyeo5RPFX+FrD+ 4 | mQwfJkV/QfhaPsNjU4nCEHFMtrsYCcLYJs9uX0tJdAtE6sg/VSulg1aMqCNWvtVt 5 | jrrVXSbu4zbyWzVkxQIDAQAB 6 | -----END PUBLIC KEY----- -------------------------------------------------------------------------------- /scripts/sdk/java/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target/ 4 | -------------------------------------------------------------------------------- /scripts/sdk/java/README.md: -------------------------------------------------------------------------------- 1 | # zeusclient-java 2 | Zeus client Java客户端 3 | 4 | ``` 5 | //校验是否有权限 6 | PermCenter permCenter = new PermCenter(accessToken, CENTER_SERVICE); 7 | try{ 8 | Map userInfo = permCenter.verify(); 9 | if (userInfo == null) { 10 | throw new IllegalArgumentException("用户不存在"); 11 | } 12 | 13 | System.out.println(userInfo.get("uid").asString()); 14 | System.out.println(userInfo.get("exp").asLong()); 15 | System.out.println(userInfo.get("uname").asString()); 16 | }catch(JWTVerificationException e){ 17 | //校验出错 18 | } 19 | 20 | if (!permCenter.checkPerm(url)) { 21 | //没有权限 22 | } 23 | ``` -------------------------------------------------------------------------------- /scripts/sdk/java/src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import cn.bullteam.PermCenter; 2 | import com.auth0.jwt.exceptions.JWTVerificationException; 3 | import com.auth0.jwt.interfaces.Claim; 4 | 5 | import java.io.IOException; 6 | import java.util.Map; 7 | 8 | import static cn.bullteam.PermCenter.CENTER_SERVICE; 9 | 10 | public class Test { 11 | public static void main(String[] args) throws IOException { 12 | 13 | String accessToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIyIiwidW5hbWUiOiJhZG1pbiIsImV4cCI6MTU1NzIzNTg1N30.rQizrl2W97bG6rhttrM9noDxn0ni1vnC6XDSDgIOanMPZAcyb47WWhB-MbGQ8Jkghmd4Ag42yNdySisXczB0zSJZr59_iXls45_VDbFm5PtkH4oU-nU8ZNlvQ-gdSnnfxYrPcAxJ6fW-B0dF1kN9_GM23NOn9Wm-PhtP3K2DlYY"; 14 | String url =""; 15 | //校验是否有权限 16 | PermCenter permCenter = new PermCenter(accessToken, CENTER_SERVICE); 17 | try{ 18 | Map userInfo = permCenter.verify(); 19 | if (userInfo == null) { 20 | throw new IllegalArgumentException("用户不存在"); 21 | } 22 | 23 | System.out.println(userInfo.get("uid").asString()); 24 | System.out.println(userInfo.get("exp").asLong()); 25 | System.out.println(userInfo.get("uname").asString()); 26 | }catch(JWTVerificationException e){ 27 | //校验出错 28 | } 29 | 30 | if (!permCenter.checkPerm(url)) { 31 | //没有权限 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /scripts/sdk/php/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | Thumbs.db 4 | /phpunit.xml 5 | /.vscode -------------------------------------------------------------------------------- /scripts/sdk/php/README.md: -------------------------------------------------------------------------------- 1 | #Zeusclient-php 宙斯后台权限系统PHP客户端 2 | 3 | ##composer.json配置 4 | 增加如下配置 5 | ```` 6 | { 7 | "require": { 8 | "zeusclient-php": "^0.1" 9 | }, 10 | "zeusclient-php": { 11 | "type": "git", 12 | "url": "git@github.com:bullteam/zeusclient-php.git" 13 | } 14 | } 15 | } 16 | 17 | ```` 18 | 如下代码可以增加在Php MVC框架中的controller 父类中 19 | ```` 20 | use ZeusClient\Auth\PermCenter 21 | 22 | //校验是否有权限 23 | $perm = new PermCenter($accessToken, 'moto-admin'); 24 | try{ 25 | $userInfo = $perm->verify(); 26 | if ($userInfo == null) { 27 | throw new \Exception(""); 28 | } 29 | }catch(\Exception $e){ 30 | if ($e->getCode() == 1001){ 31 | //登陆过期 32 | }else{ 33 | //校验出错 34 | } 35 | //抛出异常 36 | } 37 | echo $userInfo->uid;//用户ID 38 | echo $userInfo->uname;//用户名称 39 | if (!$perm->checkPerm($urlStr)) { 40 | //没有权限 41 | } 42 | ```` -------------------------------------------------------------------------------- /scripts/sdk/php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zeusclient-php", 3 | "description": "Zeus php client", 4 | "keywords": [], 5 | "type": "project", 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "coso", 10 | "email": "fuxiang.huang@xgo.one" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.2.8" 15 | }, 16 | "replace": { 17 | }, 18 | "conflict": { 19 | }, 20 | "require-dev": { 21 | }, 22 | "autoload": { 23 | "files": [ 24 | ], 25 | "psr-4": { 26 | "Utils\\": "src/" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "files": [ 31 | ], 32 | "psr-4": { 33 | "Utils\\Tests\\": "tests/" 34 | } 35 | }, 36 | "extra": { 37 | "branch-alias": { 38 | } 39 | }, 40 | "suggest": { 41 | }, 42 | "config": { 43 | "sort-packages": true 44 | }, 45 | "minimum-stability": "dev", 46 | "prefer-stable": true 47 | } -------------------------------------------------------------------------------- /scripts/sdk/python/README.md: -------------------------------------------------------------------------------- 1 | # zeusclient-python 2 | Zeus client python客户端 3 | 4 | python client demo 只做参考,使用 `flask` 框架,由于本身较为简单,建议参考API文档自行实现。 5 | # API文档 6 | * [POSTMAN](https://documenter.getpostman.com/view/159835/Rzfjk7Jh) -------------------------------------------------------------------------------- /scripts/sdk/python/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask_restful import Resource, Api, abort 3 | from flask_cors import CORS 4 | from permission_center import PermCenter 5 | 6 | 7 | app = Flask(__name__) 8 | api = Api(app) 9 | CORS(app) 10 | 11 | 12 | @app.before_request 13 | def before_request(): 14 | jwtoken = request.headers['Authorization'].split('Bearer ')[1] 15 | perm = PermCenter(jwtoken, 'finance-admin') 16 | 17 | # 读取用户信息 18 | user_info = perm.verify() 19 | print('user_info', user_info) 20 | 21 | # 进行权限验证 22 | permission, message = perm.check_permission(request.path) 23 | if not permission: 24 | abort(403, message=f'Permission Denied : {message}') 25 | 26 | 27 | class Root(Resource): 28 | def get(self): 29 | abort(404, message='api not found') 30 | 31 | 32 | api.add_resource(Root, '/') 33 | -------------------------------------------------------------------------------- /scripts/sdk/python/permission_center.py: -------------------------------------------------------------------------------- 1 | from flask_restful import abort 2 | import requests 3 | import jwt 4 | 5 | class PermCenter: 6 | def __init__(self, token, domain): 7 | self.token = token 8 | self.domain = domain 9 | 10 | self.public_key = '''-----BEGIN PUBLIC KEY----- 11 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC/TYKuXsgYdoICfEZOiy1L12Cb 12 | yPdudhrCjrjwVcIrhGNn6Udq/SY5rh0ixm09I2tXPWLYuA1R55kyeo5RPFX+FrD+ 13 | mQwfJkV/QfhaPsNjU4nCEHFMtrsYCcLYJs9uX0tJdAtE6sg/VSulg1aMqCNWvtVt 14 | jrrVXSbu4zbyWzVkxQIDAQAB 15 | -----END PUBLIC KEY-----''' 16 | self.center_service = 'http://api.admin.bullteam.cn' 17 | 18 | def check_permission(self, path): 19 | url = self.center_service + '/user/perm/check' 20 | headers = { 21 | 'Authorization': 'Bearer ' + self.token, 22 | 'Content-Type': 'application/x-www-form-urlencoded', 23 | } 24 | data = { 25 | 'domain': self.domain, 26 | 'perm': path 27 | } 28 | r = requests.post(url, headers=headers, data=data) 29 | perm = r.json() 30 | # print(perm) 31 | return perm['code'] == 0, perm['msg'] 32 | 33 | def verify(self): 34 | try: 35 | user_info = jwt.decode(self.token, self.public_key, algorithms='RS256') 36 | return user_info 37 | except Exception as e: 38 | msg = f'Invalid Token - {e}' 39 | # print(msg) 40 | abort(403, message=msg) -------------------------------------------------------------------------------- /scripts/sdk/python/requirements.txt: -------------------------------------------------------------------------------- 1 | cryptography==2.8 -------------------------------------------------------------------------------- /scripts/tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TODO modify TAG of released Naftis during CI 4 | # export TAG=$(if [ `git branch | grep \* | cut -d ' ' -f2` != "master" ]; then git checkout master --quiet; fi && git describe --tags --abbrev=0) 5 | #if [[ -z $TAG ]]; then 6 | # TAG=`git describe --tags --abbrev=0` 7 | #fi 8 | # 9 | #echo $TAG -------------------------------------------------------------------------------- /scripts/ui: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd pkg/webui 3 | npm run build:work 4 | cd ../../ 5 | -------------------------------------------------------------------------------- /scripts/ui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd pkg/webui 3 | npm run build:work 4 | cd ../../ 5 | -------------------------------------------------------------------------------- /scripts/upx/linux/upx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/scripts/upx/linux/upx -------------------------------------------------------------------------------- /scripts/upx/windows/upx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bullteam/zeus-admin/203d4b03596412c9a58e553879066fc51f39c595/scripts/upx/windows/upx -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 4 | 5 | BUILD_GIT_REVISION=$(git rev-parse HEAD 2> /dev/null) 6 | if [[ $? == 0 ]]; then 7 | git diff-index --quiet HEAD 8 | if [[ $? != 0 ]]; then 9 | BUILD_GIT_REVISION=${BUILD_GIT_REVISION}"-dirty" 10 | fi 11 | else 12 | BUILD_GIT_REVISION=unknown 13 | fi 14 | 15 | # Check for local changes 16 | git diff-index --quiet HEAD -- 17 | if [[ $? == 0 ]]; 18 | then 19 | tree_status="Clean" 20 | else 21 | tree_status="Modified" 22 | fi 23 | 24 | # XXX This needs to be updated to accomodate tags added after building, rather than prior to builds 25 | RELEASE_TAG=$(git describe --match '[0-9]*\.[0-9]*\.[0-9]*' --exact-match 2> /dev/null || echo "") 26 | 27 | # security wanted VERSION='unknown' 28 | VERSION="${BUILD_GIT_REVISION}" 29 | if [[ -n "${RELEASE_TAG}" ]]; then 30 | VERSION="${RELEASE_TAG}" 31 | elif [[ -n ${ISTIO_VERSION} ]]; then 32 | VERSION="${ISTIO_VERSION}" 33 | fi 34 | 35 | # used by pkg/version 36 | echo buildVersion "${VERSION}" 37 | echo buildGitRevision "${BUILD_GIT_REVISION}" 38 | echo buildUser "$(whoami)" 39 | echo buildHost "$(hostname -f)" 40 | echo buildStatus "${tree_status}" 41 | echo buildTime "$(date +%Y-%m-%d--%T)" 42 | --------------------------------------------------------------------------------