├── LICENSE ├── README.md ├── build ├── build.sh ├── build_windows.go ├── git_push.bat ├── go.mod └── go.sum ├── console ├── .gitignore ├── README.md ├── biz │ ├── client │ │ ├── model │ │ │ └── db.go │ │ ├── service │ │ │ ├── login.go │ │ │ ├── ras_hash.go │ │ │ ├── ras_tool.go │ │ │ ├── req.go │ │ │ └── rsa.go │ │ └── view │ │ │ ├── errors.go │ │ │ ├── login.go │ │ │ └── router.go │ ├── dash │ │ ├── service │ │ │ ├── chart.go │ │ │ ├── dash.go │ │ │ ├── model.go │ │ │ └── top5.go │ │ └── view │ │ │ ├── dash.go │ │ │ ├── errors.go │ │ │ └── router.go │ ├── gm │ │ ├── model │ │ │ ├── db.go │ │ │ ├── gold.go │ │ │ ├── roles.go │ │ │ ├── table.go │ │ │ └── task.go │ │ ├── service │ │ │ ├── accounts.go │ │ │ ├── auction.go │ │ │ ├── email.go │ │ │ ├── gold_init.go │ │ │ ├── model_account.go │ │ │ ├── model_task.go │ │ │ ├── roles.go │ │ │ ├── task.go │ │ │ └── util.go │ │ └── view │ │ │ ├── account.go │ │ │ ├── auction.go │ │ │ ├── email.go │ │ │ ├── errors.go │ │ │ ├── roles.go │ │ │ ├── router.go │ │ │ └── task.go │ ├── log │ │ ├── model │ │ │ ├── table.go │ │ │ └── util.go │ │ ├── service │ │ │ ├── model.go │ │ │ ├── operate_log.go │ │ │ └── recharge_log.go │ │ └── view │ │ │ ├── errors.go │ │ │ ├── recharge_log.go │ │ │ └── router.go │ ├── middleware │ │ ├── casbin_rbac.go │ │ ├── error.go │ │ └── operate.go │ ├── static │ │ └── view.go │ ├── user │ │ ├── auth │ │ │ ├── model │ │ │ │ └── config.go │ │ │ ├── service │ │ │ │ ├── auth.go │ │ │ │ └── op_info.go │ │ │ └── view │ │ │ │ └── auth.go │ │ ├── role │ │ │ ├── model │ │ │ │ └── role_map.go │ │ │ ├── service │ │ │ │ └── role.go │ │ │ └── view │ │ │ │ ├── errors.go │ │ │ │ ├── role.go │ │ │ │ └── router.go │ │ ├── router.go │ │ └── users │ │ │ ├── model │ │ │ ├── table.go │ │ │ └── user_map.go │ │ │ ├── service │ │ │ ├── user.go │ │ │ └── user_init.go │ │ │ └── view │ │ │ ├── errors.go │ │ │ ├── op_info.go │ │ │ ├── router.go │ │ │ └── user.go │ └── view │ │ ├── config.go │ │ ├── cors.go │ │ └── views.go ├── cmds │ ├── args.go │ ├── config.go │ ├── server.go │ ├── service.go │ ├── service_daemon.go │ └── service_main.go ├── config │ ├── rbac_model.conf │ └── server.json ├── go.mod ├── go.sum ├── main.go ├── mods │ ├── casbinx │ │ ├── casbin.go │ │ ├── config.go │ │ └── table.go │ ├── game_db │ │ ├── config.go │ │ ├── db.go │ │ ├── db_mysql.go │ │ ├── db_sqlite.go │ │ ├── migrate.go │ │ └── model.go │ ├── ginx │ │ ├── router.go │ │ └── table.go │ ├── pathx │ │ └── path.go │ └── readme.md └── source │ ├── gold.txt │ ├── private.pem │ └── publickey.pem ├── dist ├── .gitignore ├── config │ ├── rbac_model.conf │ └── server.json ├── main.exe ├── source │ ├── gold.txt │ ├── private.pem │ └── publickey.pem └── web │ └── static │ ├── favicon.ico │ ├── index.html │ └── lib │ ├── css │ ├── 141.95778f3a.css │ ├── 190.8bdff399.css │ ├── 200.fb6b2964.css │ ├── 220.95778f3a.css │ ├── 228.bd54ec38.css │ ├── 23.c722803a.css │ ├── 387.58c3a4c7.css │ ├── 504.95778f3a.css │ ├── 582.8bdff399.css │ ├── 693.8bdff399.css │ ├── 902.2ad5574d.css │ ├── 929.8bdff399.css │ ├── 953.8bdff399.css │ ├── app.76c5944a.css │ └── chunk-vendors.b6fbac7d.css │ ├── img │ ├── 401.1800ba9e.gif │ ├── 404.458c248a.png │ └── logo2.7bca92d9.png │ └── js │ ├── 134.d50f8eb4.js │ ├── 141.0831c36a.js │ ├── 190.3fe911a3.js │ ├── 200.8e316701.js │ ├── 220.76a17955.js │ ├── 228.5c599d35.js │ ├── 23.5cedb868.js │ ├── 387.08b35757.js │ ├── 390.a3a879bf.js │ ├── 504.6e8b787b.js │ ├── 567.a133d293.js │ ├── 582.59ea8022.js │ ├── 693.89e0d7a3.js │ ├── 902.255c5b24.js │ ├── 929.5b9f21b5.js │ ├── 953.5a64bcf3.js │ ├── app.a00b3824.js │ └── chunk-vendors.86afa7e2.js ├── web ├── .browserslistrc ├── .env.development ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── api │ │ ├── client │ │ │ └── login.ts │ │ ├── dash │ │ │ └── index.ts │ │ ├── gm │ │ │ ├── accounts.ts │ │ │ ├── auction.ts │ │ │ ├── email.ts │ │ │ ├── role.ts │ │ │ └── task.ts │ │ ├── log │ │ │ └── index.ts │ │ └── user │ │ │ ├── auth.ts │ │ │ └── users.ts │ ├── assets │ │ ├── 401_images │ │ │ └── 401.gif │ │ ├── 404_images │ │ │ ├── 404.png │ │ │ └── 404_cloud.png │ │ ├── cms-icons.png │ │ ├── logo.png │ │ ├── logo2.png │ │ └── toinstlist.png │ ├── components │ │ ├── BreadCrumb │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ └── BreadCrumb.vue │ │ ├── HamBurger │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ └── HamBurger.vue │ │ ├── HeaderSearch │ │ │ └── index.vue │ │ ├── Layout │ │ │ ├── components │ │ │ │ ├── AppMain.vue │ │ │ │ ├── Navbar.vue │ │ │ │ ├── TagsView │ │ │ │ │ ├── ScrollPane.vue │ │ │ │ │ └── TagsView.vue │ │ │ │ ├── index.ts │ │ │ │ └── sidebar │ │ │ │ │ ├── Link.vue │ │ │ │ │ ├── SidebarItem.vue │ │ │ │ │ └── index.vue │ │ │ └── index.vue │ │ ├── PanelStep │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ └── PanelStep.vue │ │ ├── PanelTitle │ │ │ ├── index.ts │ │ │ └── src │ │ │ │ └── PanelTitle.vue │ │ ├── Screenfull │ │ │ └── index.vue │ │ └── SvgIcon │ │ │ └── index.vue │ ├── icons │ │ ├── index.ts │ │ └── svg │ │ │ ├── bell.svg │ │ │ ├── bug.svg │ │ │ ├── close-eye.svg │ │ │ ├── cloud-download.svg │ │ │ ├── cog.svg │ │ │ ├── dashboard.svg │ │ │ ├── dollar.svg │ │ │ ├── envelope-closed.svg │ │ │ ├── envelope-open.svg │ │ │ ├── exit-fullscreen.svg │ │ │ ├── expand-down.svg │ │ │ ├── eye.svg │ │ │ ├── file.svg │ │ │ ├── fork.svg │ │ │ ├── fullscreen.svg │ │ │ ├── grid-four-up.svg │ │ │ ├── headphones.svg │ │ │ ├── home.svg │ │ │ ├── international.svg │ │ │ ├── key.svg │ │ │ ├── language.svg │ │ │ ├── layers.svg │ │ │ ├── link.svg │ │ │ ├── list-rich.svg │ │ │ ├── list.svg │ │ │ ├── lock.svg │ │ │ ├── menu.svg │ │ │ ├── nested.svg │ │ │ ├── open-eye.svg │ │ │ ├── password.svg │ │ │ ├── people.svg │ │ │ ├── person.svg │ │ │ ├── print.svg │ │ │ ├── pulse.svg │ │ │ ├── search.svg │ │ │ ├── shield.svg │ │ │ ├── timer.svg │ │ │ ├── transfer.svg │ │ │ └── yen.svg │ ├── main.ts │ ├── models │ │ ├── page.ts │ │ └── user │ │ │ ├── auth.ts │ │ │ └── user.ts │ ├── permission.ts │ ├── router │ │ ├── constant.ts │ │ ├── index.ts │ │ ├── modules │ │ │ ├── client.ts │ │ │ ├── gm.ts │ │ │ └── system.ts │ │ └── types.ts │ ├── settings.ts │ ├── store │ │ ├── index.ts │ │ └── modules │ │ │ ├── app.ts │ │ │ ├── permission.ts │ │ │ ├── tagsView.ts │ │ │ └── user.ts │ ├── styles │ │ ├── color.scss │ │ ├── elem.scss │ │ ├── index.scss │ │ ├── mixin.scss │ │ ├── sidebar.scss │ │ └── transition.scss │ ├── types │ │ ├── global.d.ts │ │ └── shims-vue.d.ts │ ├── utils │ │ ├── download │ │ │ └── index.ts │ │ ├── echarts │ │ │ ├── echarts.ts │ │ │ └── useEcharts.ts │ │ ├── element │ │ │ ├── form.ts │ │ │ ├── message.ts │ │ │ └── messageBox.ts │ │ ├── event │ │ │ ├── breakpointEnum.ts │ │ │ ├── useBreakpoint.ts │ │ │ └── useEventListener.ts │ │ ├── filters.ts │ │ ├── fn.ts │ │ ├── get-page-title.ts │ │ ├── http │ │ │ ├── config.ts │ │ │ ├── const.ts │ │ │ ├── core.ts │ │ │ ├── download.ts │ │ │ ├── index.ts │ │ │ ├── upload.ts │ │ │ └── utils.ts │ │ ├── index.ts │ │ ├── progress │ │ │ └── index.ts │ │ ├── propTypes.ts │ │ ├── storage │ │ │ └── index.ts │ │ ├── utils.ts │ │ └── vue │ │ │ ├── index.ts │ │ │ └── install.ts │ └── views │ │ ├── client │ │ └── login │ │ │ ├── index.vue │ │ │ └── models │ │ │ └── login.ts │ │ ├── dashboard │ │ ├── components │ │ │ ├── BarChart.vue │ │ │ ├── DashBody.vue │ │ │ ├── DashFoot.vue │ │ │ └── DashHeader.vue │ │ ├── dash.scss │ │ ├── index.vue │ │ └── model │ │ │ └── dash.ts │ │ ├── errorPage │ │ ├── 401.vue │ │ └── 404.vue │ │ ├── gm │ │ ├── account │ │ │ ├── components │ │ │ │ ├── ChangePassword.vue │ │ │ │ ├── CreateAccountDialog.vue │ │ │ │ ├── EditAccountDialog.vue │ │ │ │ └── RechargeDialog.vue │ │ │ ├── filter.vue │ │ │ ├── index.vue │ │ │ ├── model │ │ │ │ ├── info.ts │ │ │ │ └── model.ts │ │ │ └── table.vue │ │ ├── auction │ │ │ ├── index.vue │ │ │ └── model.ts │ │ ├── email │ │ │ ├── EmailForm.vue │ │ │ ├── index.vue │ │ │ └── model.ts │ │ ├── roles │ │ │ ├── RolesTable.vue │ │ │ ├── components │ │ │ │ ├── RolePvpDialog.vue │ │ │ │ └── RoleQPDialog.vue │ │ │ ├── index.vue │ │ │ ├── model.ts │ │ │ └── model │ │ │ │ ├── pvp.ts │ │ │ │ └── qp.ts │ │ └── tasks │ │ │ ├── RoleTable.vue │ │ │ ├── SelectAccount.vue │ │ │ ├── TaskDetail.vue │ │ │ ├── index.vue │ │ │ └── model.ts │ │ ├── login │ │ ├── index.vue │ │ └── login.scss │ │ ├── redirect │ │ └── index.vue │ │ ├── test.vue │ │ └── user │ │ ├── info │ │ └── index.vue │ │ ├── list │ │ ├── EditCreateUserdialog.vue │ │ └── index.vue │ │ └── test │ │ └── index.vue ├── tsconfig.json └── vue.config.js └── wxp.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 jason 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ../web 4 | 5 | npm install 6 | npm run build 7 | 8 | rm -rf ../dist/web/static/* 9 | cp -r dist/* ../dist/web/static 10 | 11 | cd ../console 12 | 13 | go build main.go 14 | yes | cp main ../dist 15 | 16 | cd ../build -------------------------------------------------------------------------------- /build/build_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "errors" 7 | log "github.com/sirupsen/logrus" 8 | "golang.org/x/text/encoding/simplifiedchinese" 9 | "golang.org/x/text/transform" 10 | "io" 11 | "os/exec" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | cmds := []string{ 17 | "cd ..\\web && npm i", 18 | "cd ..\\web && npm run build", 19 | "cd ..\\dist\\web && rmdir /s /q static", 20 | "cd ..\\dist\\web && mkdir static", 21 | "cd ..\\web && xcopy /s /e /i dist ..\\dist\\web\\static /Y", 22 | 23 | "cd ..\\console && go build main.go", 24 | "cd ..\\console && copy main.exe ..\\dist\\main.exe /Y && del main.exe", 25 | } 26 | 27 | for _, cmd := range cmds { 28 | out, err := WindowsExecByShell(cmd) 29 | if err != nil { 30 | log.Error(out) 31 | } 32 | log.Info("OUT:", out) 33 | } 34 | } 35 | 36 | func WindowsExecByShell(cmd string) (string, error) { 37 | ctx, cancel := context.WithTimeout(context.Background(), 500*time.Second) 38 | defer cancel() 39 | 40 | command := exec.CommandContext(ctx, "cmd", "/C", cmd) 41 | stdOut := bytes.Buffer{} 42 | stderr := bytes.Buffer{} 43 | command.Stdout = &stdOut 44 | command.Stderr = &stderr 45 | err := command.Run() 46 | if err != nil { 47 | s, _ := GbkToUtf8(stderr.Bytes()) 48 | return string(s), errors.New(stderr.String()) 49 | } 50 | return stdOut.String(), nil 51 | } 52 | 53 | func GbkToUtf8(s []byte) ([]byte, error) { 54 | reader := transform.NewReader(bytes.NewReader(s), simplifiedchinese.GBK.NewDecoder()) 55 | d, e := io.ReadAll(reader) 56 | if e != nil { 57 | return nil, e 58 | } 59 | return d, nil 60 | } 61 | -------------------------------------------------------------------------------- /build/git_push.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem git push 3 | 4 | git add . 5 | git commit -m "push" 6 | git push 7 | 8 | echo done 9 | pause -------------------------------------------------------------------------------- /build/go.mod: -------------------------------------------------------------------------------- 1 | module build 2 | 3 | go 1.21.5 4 | 5 | require ( 6 | github.com/sirupsen/logrus v1.9.3 7 | golang.org/x/text v0.14.0 8 | ) 9 | 10 | require golang.org/x/sys v0.5.0 // indirect 11 | -------------------------------------------------------------------------------- /build/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 7 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 10 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 11 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 12 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 13 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 14 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 15 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 16 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 17 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 18 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 19 | -------------------------------------------------------------------------------- /console/.gitignore: -------------------------------------------------------------------------------- 1 | /org 2 | /.vscode 3 | 4 | 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | !.vscode/*.code-snippets 11 | 12 | # Local History for Visual Studio Code 13 | .history/ 14 | 15 | # Built Visual Studio Code Extensions 16 | *.vsix 17 | .idea 18 | 19 | 20 | data 21 | /log/ 22 | -------------------------------------------------------------------------------- /console/README.md: -------------------------------------------------------------------------------- 1 | # webconsole 2 | -------------------------------------------------------------------------------- /console/biz/client/model/db.go: -------------------------------------------------------------------------------- 1 | package model 2 | -------------------------------------------------------------------------------- /console/biz/client/service/login.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | gmModel "console/biz/gm/model" 5 | "console/mods/game_db" 6 | "crypto/md5" 7 | "encoding/hex" 8 | "errors" 9 | "fmt" 10 | "gorm.io/gorm" 11 | "os" 12 | ) 13 | 14 | func ToMd5(str string) string { 15 | h := md5.New() 16 | h.Write([]byte(str)) 17 | return hex.EncodeToString(h.Sum(nil)) 18 | } 19 | 20 | func Hex2bin(raw string) string { 21 | result, _ := hex.DecodeString(raw) 22 | return string(result) 23 | } 24 | 25 | // 10转16进制,不够补0 26 | func uidTo16(uid int) string { 27 | return fmt.Sprintf("%08x", uid) 28 | } 29 | 30 | func Login(data *LoginReq) (string, error) { 31 | dbx := game_db.DBPools.Get(gmModel.DTaiwan) 32 | 33 | var account gmModel.Accounts 34 | 35 | var err error 36 | if data.LoginType == "uid" { 37 | err = dbx.Where("UID = ?", data.Uid).First(&account).Error 38 | } else { 39 | err = dbx.Where("accountname = ? AND password = ?", data.Username, ToMd5(data.Password)).First(&account).Error 40 | } 41 | 42 | if errors.Is(err, gorm.ErrRecordNotFound) { 43 | return "", errors.New("账号未找到!") 44 | } 45 | 46 | uid16 := uidTo16(account.Uid) 47 | fmt.Println("data", data.LoginType, account.Uid, uid16) 48 | 49 | rsa := NewRsa() 50 | key := fmt.Sprintf("%s010101010101010101010101010101010101010101010101010101010101010155914510010403030101", uid16) 51 | d := Hex2bin(key) 52 | 53 | privateKey, _ := os.ReadFile(rsa.PrivateFile) 54 | token, err := applyPriEPubD(d, string(privateKey)) 55 | fmt.Println("err", err) 56 | if err != nil { 57 | return "", err 58 | } 59 | fmt.Println("key:", d) 60 | //token := base64.StdEncoding.EncodeToString(hashToken) 61 | fmt.Println("token:", token) 62 | return token, nil 63 | } 64 | -------------------------------------------------------------------------------- /console/biz/client/service/ras_hash.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "encoding/pem" 8 | "os" 9 | ) 10 | 11 | // RasEncrypt RSA加密 12 | // plainText 要加密的数据 13 | // path 公钥匙文件地址 14 | func RasEncrypt(plainText []byte, path string) ([]byte, error) { 15 | //打开文件 16 | file, err := os.Open(path) 17 | if err != nil { 18 | panic(err) 19 | } 20 | defer file.Close() 21 | //读取文件的内容 22 | info, _ := file.Stat() 23 | buf := make([]byte, info.Size()) 24 | file.Read(buf) 25 | //pem解码 26 | block, _ := pem.Decode(buf) 27 | //x509解码 28 | 29 | publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes) 30 | if err != nil { 31 | return nil, err 32 | } 33 | //类型断言 34 | publicKey := publicKeyInterface.(*rsa.PublicKey) 35 | //对明文进行加密 36 | cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText) 37 | if err != nil { 38 | return nil, err 39 | } 40 | //返回密文 41 | return cipherText, nil 42 | } 43 | 44 | // RsaDecrypt RSA解密 45 | // cipherText 需要解密的byte数据 46 | // path 私钥文件路径 47 | func RsaDecrypt(cipherText []byte, path string) []byte { 48 | //打开文件 49 | file, err := os.Open(path) 50 | if err != nil { 51 | panic(err) 52 | } 53 | defer file.Close() 54 | //获取文件内容 55 | info, _ := file.Stat() 56 | buf := make([]byte, info.Size()) 57 | file.Read(buf) 58 | //pem解码 59 | block, _ := pem.Decode(buf) 60 | //X509解码 61 | privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 62 | if err != nil { 63 | panic(err) 64 | } 65 | //对密文进行解密 66 | plainText, _ := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText) 67 | //返回明文 68 | return plainText 69 | } 70 | -------------------------------------------------------------------------------- /console/biz/client/service/ras_tool.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | gorsa "github.com/Lyafei/go-rsa" 5 | ) 6 | 7 | func applyPriEPubD(data, pirvatekey string) (string, error) { 8 | prienctypt, err := gorsa.PriKeyEncrypt(data, pirvatekey) 9 | if err != nil { 10 | return "", err 11 | } 12 | 13 | return prienctypt, nil 14 | } 15 | -------------------------------------------------------------------------------- /console/biz/client/service/req.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type LoginReq struct { 4 | LoginType string `json:"login_type"` 5 | Uid int `json:"uid"` 6 | Username string `json:"username"` 7 | Password string `json:"password"` 8 | } 9 | -------------------------------------------------------------------------------- /console/biz/client/service/rsa.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/mods/pathx" 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "os" 10 | "path/filepath" 11 | ) 12 | 13 | type Rsa struct { 14 | PrivateFile string 15 | PublicFile string 16 | } 17 | 18 | func NewRsa() *Rsa { 19 | exeDir, _ := pathx.GetExeDir() 20 | publickeyFile := filepath.Join(exeDir, "source", "publickey.pem") 21 | privateFile := filepath.Join(exeDir, "source", "private.pem") 22 | return &Rsa{ 23 | PublicFile: publickeyFile, 24 | PrivateFile: privateFile, 25 | } 26 | } 27 | 28 | // GenRsaKey RSA公钥私钥产生 29 | func (r *Rsa) GenRsaKey(bits int) error { 30 | // 生成私钥文件 31 | privateKey, err := rsa.GenerateKey(rand.Reader, bits) 32 | if err != nil { 33 | return err 34 | } 35 | derStream := x509.MarshalPKCS1PrivateKey(privateKey) 36 | block := &pem.Block{ 37 | Type: "RSA PRIVATE KEY", 38 | Bytes: derStream, 39 | } 40 | file, err := os.Create(r.PrivateFile) 41 | if err != nil { 42 | return err 43 | } 44 | err = pem.Encode(file, block) 45 | if err != nil { 46 | return err 47 | } 48 | // 生成公钥文件 49 | publicKey := &privateKey.PublicKey 50 | derPkix, err := x509.MarshalPKIXPublicKey(publicKey) 51 | if err != nil { 52 | return err 53 | } 54 | block = &pem.Block{ 55 | Type: "PUBLIC KEY", 56 | Bytes: derPkix, 57 | } 58 | file, err = os.Create(r.PublicFile) 59 | if err != nil { 60 | return err 61 | } 62 | err = pem.Encode(file, block) 63 | if err != nil { 64 | return err 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /console/biz/client/view/errors.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | const ( 4 | E_CLIENT_LOGIN = "E_CLIENT_LOGIN" 5 | ) 6 | -------------------------------------------------------------------------------- /console/biz/client/view/login.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/client/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func login(c *gin.Context) { 10 | data := &service.LoginReq{} 11 | uv.PB(c, data) 12 | 13 | token, err := service.Login(data) 14 | uv.PEIf(E_CLIENT_LOGIN, err) 15 | c.JSON(200, map[string]string{"token": token}) 16 | } 17 | -------------------------------------------------------------------------------- /console/biz/client/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "console/mods/ginx" 4 | 5 | func InitClientRouter(r *ginx.RouterGroup) { 6 | client := r.Group("客户端登录", "") 7 | { 8 | client.POST("登录", "login", login) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /console/biz/dash/service/chart.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | logModel "console/biz/log/model" 5 | "fmt" 6 | "github.com/localhostjason/webserver/db" 7 | "time" 8 | ) 9 | 10 | func GetDashChart() ChartResult { 11 | return ChartResult{ 12 | Cera: getChartByType("cera"), 13 | CeraPoint: getChartByType("cera_point"), 14 | } 15 | } 16 | 17 | func getChartByType(typ string) ChartTranInfo { 18 | var data []ChartInfo 19 | 20 | now := time.Now() 21 | nowYear := now.Format("2006") 22 | 23 | tx := db.DB.Debug().Model(&logModel.RechargeLog{}). 24 | Select("sum(number) as total,strftime('%Y',time) as year, strftime('%m',time) AS month"). 25 | Group("year, month"). 26 | Having("year = ?", nowYear) 27 | 28 | if typ == "cera" { 29 | tx = tx.Where("action = ?", logModel.RechargeCera) 30 | } else { 31 | tx = tx.Where("action = ?", logModel.RechargeCeraPoint) 32 | } 33 | tx.Find(&data) 34 | 35 | date := make([]string, 0) 36 | for i := 1; i <= 12; i++ { 37 | date = append(date, fmt.Sprintf("%d月", i)) 38 | } 39 | 40 | number := make([]int, 0) 41 | for i := 1; i <= 12; i++ { 42 | isAppend := false 43 | for _, info := range data { 44 | if info.Month == i { 45 | number = append(number, info.Total) 46 | isAppend = true 47 | break 48 | } 49 | } 50 | 51 | if !isAppend { 52 | number = append(number, 0) 53 | } 54 | 55 | } 56 | 57 | return ChartTranInfo{ 58 | Date: date, 59 | Total: number, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /console/biz/dash/service/dash.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/gm/model" 5 | logModel "console/biz/log/model" 6 | "console/mods/game_db" 7 | "github.com/localhostjason/webserver/db" 8 | ) 9 | 10 | func GetDashStatTotal() StatCountResult { 11 | return StatCountResult{ 12 | CeraTotal: GetRechargeTotal("cera"), 13 | CeraPointTotal: GetRechargeTotal("cera_point"), 14 | UserTotal: getAccountTotal(), 15 | CharacTotal: getCharacTotal(), 16 | } 17 | } 18 | 19 | func GetRechargeTotal(typ string) int { 20 | type _R struct { 21 | Total int `json:"total"` 22 | } 23 | var data _R 24 | tx := db.DB.Model(&logModel.RechargeLog{}).Select("sum(number) as total").Group("action") 25 | if typ == "cera" { 26 | tx.Having("action = 1").First(&data) 27 | } else { 28 | tx.Having("action = 2").First(&data) 29 | } 30 | 31 | return data.Total 32 | } 33 | 34 | func getAccountTotal() int { 35 | dbx := game_db.DBPools.Get(model.DTaiwan) 36 | 37 | var total int64 38 | dbx.Model(&model.Accounts{}).Count(&total) 39 | return int(total) 40 | } 41 | 42 | func getCharacTotal() int { 43 | dbx := game_db.DBPools.Get(model.DTaiwan) 44 | var accounts []model.Accounts 45 | dbx.Find(&accounts) 46 | 47 | uids := make([]int, 0) 48 | for _, info := range accounts { 49 | uids = append(uids, info.Uid) 50 | } 51 | 52 | db2 := game_db.DBPools.Get(model.TaiwanCain) 53 | 54 | var total int64 55 | db2.Table("charac_info").Where("m_id IN ? AND delete_flag = ?", uids, 0).Count(&total) 56 | return int(total) 57 | } 58 | -------------------------------------------------------------------------------- /console/biz/dash/service/model.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type StatCountResult struct { 4 | UserTotal int `json:"user_total"` 5 | CharacTotal int `json:"charac_total"` 6 | CeraTotal int `json:"cera_total"` 7 | CeraPointTotal int `json:"cera_point_total"` 8 | } 9 | 10 | type TopInfo struct { 11 | Uid int `json:"uid"` 12 | AccountName string `json:"account_name"` 13 | Total int `json:"total"` 14 | } 15 | 16 | type StatTop5Result struct { 17 | Cera []TopInfo `json:"cera"` 18 | CeraPoint []TopInfo `json:"cera_point"` 19 | CeraTotal int `json:"cera_total"` 20 | CeraPointTotal int `json:"cera_point_total"` 21 | } 22 | 23 | type ChartInfo struct { 24 | Year int `json:"year"` 25 | Month int `json:"month"` 26 | Total int `json:"total"` 27 | } 28 | 29 | type ChartTranInfo struct { 30 | Date []string `json:"date"` 31 | Total []int `json:"total"` 32 | } 33 | 34 | type ChartResult struct { 35 | Cera ChartTranInfo `json:"cera"` 36 | CeraPoint ChartTranInfo `json:"cera_point"` 37 | } 38 | -------------------------------------------------------------------------------- /console/biz/dash/service/top5.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/gm/model" 5 | logModel "console/biz/log/model" 6 | "console/mods/game_db" 7 | "github.com/localhostjason/webserver/db" 8 | ) 9 | 10 | func GetRechargeTop5() StatTop5Result { 11 | return StatTop5Result{ 12 | Cera: getRechargeTop5ByType("cera"), 13 | CeraPoint: getRechargeTop5ByType("cera_point"), 14 | CeraTotal: GetRechargeTotal("cera"), 15 | CeraPointTotal: GetRechargeTotal("cera_point"), 16 | } 17 | } 18 | 19 | func getRechargeTop5ByType(typ string) []TopInfo { 20 | var data []TopInfo 21 | tx := db.DB.Model(&logModel.RechargeLog{}).Select("sum(number) as total, uid").Group("action, uid").Order("total desc").Limit(5) 22 | 23 | if typ == "cera" { 24 | tx.Having("action = 1") 25 | } else { 26 | tx.Having("action = 2") 27 | } 28 | tx.Find(&data) 29 | 30 | result := make([]TopInfo, 0) 31 | for _, info := range data { 32 | info.AccountName = getAccountNameByUid(info.Uid) 33 | result = append(result, info) 34 | } 35 | return result 36 | } 37 | 38 | func getAccountNameByUid(uid int) string { 39 | dbx := game_db.DBPools.Get(model.DTaiwan) 40 | var data model.Accounts 41 | dbx.Where("UID = ?", uid).First(&data) 42 | return data.AccountName 43 | } 44 | -------------------------------------------------------------------------------- /console/biz/dash/view/dash.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/dash/service" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func getDashStatCount(c *gin.Context) { 9 | data := service.GetDashStatTotal() 10 | c.JSON(200, data) 11 | } 12 | 13 | func getDashTop5(c *gin.Context) { 14 | data := service.GetRechargeTop5() 15 | c.JSON(200, data) 16 | } 17 | 18 | func getDashChart(c *gin.Context) { 19 | data := service.GetDashChart() 20 | c.JSON(200, data) 21 | } 22 | -------------------------------------------------------------------------------- /console/biz/dash/view/errors.go: -------------------------------------------------------------------------------- 1 | package view 2 | -------------------------------------------------------------------------------- /console/biz/dash/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "console/mods/ginx" 4 | 5 | func InitDashRouter(r *ginx.RouterGroup) { 6 | r.GET("获取首页统计", "stat/count", getDashStatCount) 7 | r.GET("获取首页Top5", "stat/top5", getDashTop5) 8 | r.GET("获取首页图表", "stat/chart", getDashChart) 9 | //r.GET("操作日志", "operate", getOperateLog) 10 | } 11 | -------------------------------------------------------------------------------- /console/biz/gm/model/db.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const DTaiwan = "d_taiwan" 4 | const TaiwanCain = "taiwan_cain" 5 | 6 | const TaiwanCain2nd = "taiwan_cain_2nd" 7 | 8 | const TaiwanBilling = "taiwan_billing" 9 | 10 | const TaiwanLogin = "taiwan_login" 11 | 12 | const TaiwanCainAuctionGold = "taiwan_cain_auction_gold" 13 | const TaiwanCainAuctionCera = "taiwan_cain_auction_cera" 14 | -------------------------------------------------------------------------------- /console/biz/gm/model/gold.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "console/mods/game_db" 5 | ) 6 | 7 | type Gold struct { 8 | Id int `json:"id" gorm:"primaryKey"` 9 | Code int `json:"code"` 10 | Name string `json:"name" gorm:"type:string;size:128"` 11 | } 12 | 13 | func init() { 14 | game_db.RegTables(TaiwanCain2nd, &Gold{}) 15 | } 16 | -------------------------------------------------------------------------------- /console/biz/gm/model/roles.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "time" 4 | 5 | type CharacInfo struct { 6 | MId int `json:"m_id"` 7 | CharacNo int `json:"charac_no"` 8 | CharacName string `json:"charac_name"` 9 | Lev int `json:"lev"` 10 | CreateTime time.Time `json:"create_time"` 11 | } 12 | 13 | type CharacInfoV2 struct { 14 | MId int `json:"m_id"` 15 | CharacNo int `json:"charac_no"` 16 | ConvertedCharacName string `json:"converted_charac_name"` 17 | Lev int `json:"lev"` 18 | CreateTime time.Time `json:"create_time"` 19 | } 20 | -------------------------------------------------------------------------------- /console/biz/gm/model/table.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "console/mods/game_db" 4 | 5 | type Accounts struct { 6 | Uid int `json:"uid" gorm:"column:UID;primaryKey"` 7 | AccountName string `json:"account_name" gorm:"column:accountname;type:string;size:128;unique;not null"` 8 | Password string `json:"-" gorm:"type:string;size:128;not null"` 9 | QQ string `json:"qq" gorm:"type:string;size:128;"` 10 | } 11 | 12 | func init() { 13 | game_db.RegTables(DTaiwan, &Accounts{}) 14 | } 15 | -------------------------------------------------------------------------------- /console/biz/gm/model/task.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Task struct { 4 | CharacNo int `json:"charac_no"` 5 | Play1 int `json:"play_1" gorm:"column:play_1;"` 6 | Play1Trigger int `json:"play_1_trigger" gorm:"column:play_1_trigger;"` 7 | } 8 | -------------------------------------------------------------------------------- /console/biz/gm/service/email.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/gm/model" 5 | "console/mods/game_db" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | "time" 8 | ) 9 | 10 | // SendEmail 插入 2条记录 letter postal 11 | func SendEmail(characNo int, email *Email) error { 12 | dbx := game_db.DBPools.Get(model.TaiwanCain2nd) 13 | 14 | err := dbx.Table("letter").Create(map[string]interface{}{ 15 | "charac_no": characNo, 16 | "send_charac_no": 0, 17 | "send_charac_name": "GM", 18 | "letter_text": "Thanks!", 19 | "stat": 1, 20 | }).Error 21 | 22 | if err != nil { 23 | return err 24 | } 25 | 26 | type _R struct { 27 | LetterId int `json:"letter_id"` 28 | } 29 | var data _R 30 | dbx.Table("letter").Where("charac_no = ?", characNo).Order("letter_id desc").Take(&data) 31 | 32 | //fmt.Println(123, data.LetterId) 33 | amplifyOption := 0 34 | amplifyValue := 0 35 | if email.IsAmplify { 36 | amplifyOption = email.AmplifyOption 37 | amplifyValue = email.AmplifyValue 38 | } 39 | 40 | now := time.Now() 41 | return dbx.Debug().Table("postal").Create(map[string]interface{}{ 42 | "occ_time": now.Format("2006-01-02 15:04:05"), 43 | "send_charac_no": 0, 44 | "send_charac_name": "GAME MASTER", 45 | "receive_charac_no": characNo, 46 | "item_id": email.Code, 47 | "add_info": email.Number, 48 | "letter_id": data.LetterId, 49 | "seperate_upgrade": email.SeperateUpgrade, 50 | "upgrade": email.Upgrade, 51 | "amplify_option": amplifyOption, 52 | "amplify_value": amplifyValue, 53 | "gold": email.Gold, 54 | "seal_flag": email.SealFlag, 55 | }).Error 56 | 57 | } 58 | 59 | func GetGoldList(q *GoldQ, pi *uv.PagingIn, order *uv.Order) ([]model.Gold, *uv.PagingOut, error) { 60 | dbx := game_db.DBPools.Get(model.TaiwanCain2nd) 61 | tx := q.FilterQuery(dbx) 62 | 63 | var lst = make([]model.Gold, 0) 64 | po, err := uv.PagingFind(tx, &lst, pi, order) 65 | 66 | return lst, po, err 67 | } 68 | -------------------------------------------------------------------------------- /console/biz/gm/service/gold_init.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "bufio" 5 | "console/biz/gm/model" 6 | "console/mods/game_db" 7 | "console/mods/pathx" 8 | "fmt" 9 | "github.com/localhostjason/webserver/util" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strconv" 15 | "strings" 16 | ) 17 | 18 | type GoldFile struct { 19 | File string 20 | } 21 | 22 | func NewGoldFile() *GoldFile { 23 | return &GoldFile{ 24 | File: getGoldFile(), 25 | } 26 | } 27 | 28 | func getGoldFile() string { 29 | exeDir, _ := pathx.GetExeDir() 30 | goldFile := filepath.Join(exeDir, "source", "gold.txt") 31 | if !util.PathExists(goldFile) { 32 | return "" 33 | } 34 | return goldFile 35 | } 36 | 37 | func (gf *GoldFile) Read() []model.Gold { 38 | fs, err := os.Open(gf.File) 39 | if err != nil { 40 | return nil 41 | } 42 | 43 | goldList := make([]model.Gold, 0) 44 | line := bufio.NewReader(fs) 45 | for { 46 | content, _, err := line.ReadLine() 47 | if err == io.EOF { 48 | fs.Close() //关闭 49 | break 50 | } 51 | 52 | data := string(content) 53 | reg, _ := regexp.Compile("\\[(.*?)]") 54 | ver := reg.FindAllStringSubmatch(data, 1) 55 | if len(ver) == 0 { 56 | continue 57 | } 58 | code, err := strconv.Atoi(strings.TrimSpace(ver[0][1])) 59 | if err != nil { 60 | continue 61 | } 62 | 63 | reg2, _ := regexp.Compile(".*name:(.*)") 64 | d := reg2.FindAllStringSubmatch(data, 1) 65 | name := strings.TrimSpace(d[0][1]) 66 | 67 | goldList = append(goldList, model.Gold{ 68 | Code: code, 69 | Name: name, 70 | }) 71 | } 72 | 73 | return goldList 74 | } 75 | 76 | func (gf *GoldFile) WriteDB() error { 77 | data := gf.Read() 78 | dbx := game_db.DBPools.Get(model.TaiwanCain2nd) 79 | 80 | var gs []model.Gold 81 | dbx.Find(&gs) 82 | if len(gs) > 0 { 83 | return nil 84 | } 85 | 86 | fmt.Println("start") 87 | dbx.Where("1=1").Delete(&model.Gold{}) 88 | dbx.CreateInBatches(&data, 10000) 89 | fmt.Println("stop") 90 | return nil 91 | } 92 | 93 | func init() { 94 | gf := NewGoldFile() 95 | // 数据太多,直接执行sql 导入? 96 | game_db.AddInitHook(gf.WriteDB) 97 | } 98 | -------------------------------------------------------------------------------- /console/biz/gm/service/model_task.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/gm/model" 5 | "github.com/localhostjason/webserver/db" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type TaskId struct { 10 | Ids []int `json:"ids"` 11 | } 12 | 13 | type Email struct { 14 | Code int `json:"code" binding:"gte=0"` 15 | Number int `json:"number" binding:"required"` 16 | SeperateUpgrade int `json:"seperate_upgrade"` // 武将锻造等级 17 | Upgrade int `json:"upgrade"` // 装备等级 18 | IsAmplify bool `json:"is_amplify"` // 具有异界属性 19 | AmplifyOption int `json:"amplify_option"` 20 | AmplifyValue int `json:"amplify_value"` 21 | Gold int `json:"gold"` // 金币 22 | SealFlag bool `json:"seal_flag"` // 是否封装 23 | } 24 | 25 | type GoldQ struct { 26 | Name string `form:"name"` 27 | } 28 | 29 | func (q GoldQ) FilterQuery(dbx *gorm.DB) (tx *gorm.DB) { 30 | tx = dbx.Model(&model.Gold{}) 31 | 32 | if q.Name != "" { 33 | tx = tx.Where("name like ?", db.Like(q.Name)) 34 | } 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /console/biz/gm/service/task.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/gm/model" 5 | "console/mods/game_db" 6 | "errors" 7 | "fmt" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | func GetTaskByRole(characNo int) ([]model.Task, error) { 12 | dbx := game_db.DBPools.Get(model.TaiwanCain) 13 | 14 | var characInfo model.CharacInfo 15 | err := dbx.Table("charac_info").Where("charac_no = ?", characNo).First(&characInfo).Error 16 | if errors.Is(err, gorm.ErrRecordNotFound) { 17 | return nil, errors.New("角色不存在!") 18 | } 19 | 20 | var data = make([]model.Task, 0) 21 | dbx.Table("new_charac_quest").Where("charac_no = ?", characNo).Find(&data) 22 | return data, nil 23 | } 24 | 25 | func UpdateTaskByRole(characNo int, ids []int) error { 26 | dbx := game_db.DBPools.Get(model.TaiwanCain) 27 | 28 | var characInfo model.CharacInfo 29 | err := dbx.Table("charac_info").Where("charac_no = ?", characNo).First(&characInfo).Error 30 | if errors.Is(err, gorm.ErrRecordNotFound) { 31 | return errors.New("角色不存在!") 32 | } 33 | 34 | var updateMap = make(map[string]interface{}, 0) 35 | for i := 1; i <= 20; i++ { 36 | key := fmt.Sprintf("play_%d_trigger", i) 37 | updateMap[key] = 0 38 | } 39 | 40 | fmt.Println(111, characNo, ids, updateMap) 41 | 42 | if len(ids) == 0 { 43 | return errors.New("请选择任务!") 44 | } 45 | 46 | for _, id := range ids { 47 | err = dbx.Debug().Table("new_charac_quest").Where("charac_no = ? AND play_1 = ?", characNo, id).Updates(updateMap).Error 48 | if err != nil { 49 | return err 50 | } 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /console/biz/gm/service/util.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | func ToMd5(str string) string { 9 | h := md5.New() 10 | h.Write([]byte(str)) 11 | return hex.EncodeToString(h.Sum(nil)) 12 | } 13 | -------------------------------------------------------------------------------- /console/biz/gm/view/account.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/gm/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func getAccounts(c *gin.Context) { 10 | var pi, order, q = &uv.PagingIn{}, &uv.Order{}, &service.AccountFilter{} 11 | uv.PQ(c, pi, order, q) 12 | lst, po, err := service.GetAccounts(q, pi, order) 13 | uv.PEIf(E_ACCOUNT_GET, err) 14 | 15 | c.JSON(200, uv.PagedOut(lst, po)) 16 | } 17 | 18 | func rechargeAccount(c *gin.Context) { 19 | uid := uv.PPID(c, "id") 20 | 21 | data := &service.RechargeReq{} 22 | uv.PB(c, data) 23 | 24 | err := service.RechargeAccount(uid, data, c) 25 | uv.PEIf(E_RECHARGE_POST, err) 26 | c.Status(201) 27 | } 28 | 29 | func resetCreateCharac(c *gin.Context) { 30 | uid := uv.PPID(c, "id") 31 | err := service.ResetCreateCharac(uid) 32 | uv.PEIf(E_RESET_CREATE_CHARAC, err) 33 | c.Status(201) 34 | } 35 | 36 | func deleteAccount(c *gin.Context) { 37 | uid := uv.PPID(c, "id") 38 | err := service.DeleteAccount(uid) 39 | uv.PEIf(E_ACCOUNT_DELETE, err) 40 | c.Status(201) 41 | } 42 | 43 | func changePassword(c *gin.Context) { 44 | args := &service.PasswordReq{} 45 | uv.PB(c, args) 46 | 47 | uid := uv.PPID(c, "id") 48 | err := service.ChangeAccountPassword(uid, args) 49 | uv.PEIf(E_ACCOUNT_CHANGE_PASSWORD, err) 50 | c.Status(201) 51 | } 52 | 53 | func createAccount(c *gin.Context) { 54 | args := &service.CreateAccountReq{} 55 | uv.PB(c, args) 56 | 57 | err := service.CreateAccount(args) 58 | uv.PEIf(E_ACCOUNT_CREARE, err) 59 | c.Status(201) 60 | } 61 | 62 | func updateAccount(c *gin.Context) { 63 | args := &service.UpdateAccountReq{} 64 | uv.PB(c, args) 65 | 66 | uid := uv.PPID(c, "id") 67 | err := service.UpdateAccountInfo(uid, args) 68 | uv.PEIf(E_ACCOUNT_UPDATE, err) 69 | c.Status(201) 70 | } 71 | -------------------------------------------------------------------------------- /console/biz/gm/view/auction.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/gm/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func getAuctionSate(c *gin.Context) { 10 | data := service.GetAuctionState() 11 | c.JSON(200, data) 12 | } 13 | 14 | func openAuctionSate(c *gin.Context) { 15 | name := c.Param("name") 16 | err := service.OpenAuction(name) 17 | uv.PEIf(E_OPEN_AUCTION, err) 18 | c.Status(201) 19 | } 20 | 21 | func closeAuction(c *gin.Context) { 22 | c.JSON(200, "未实现") 23 | } 24 | -------------------------------------------------------------------------------- /console/biz/gm/view/email.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/gm/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func sendEmail(c *gin.Context) { 10 | characNo := uv.PPID(c, "id") 11 | 12 | email := &service.Email{} 13 | uv.PB(c, email) 14 | err := service.SendEmail(characNo, email) 15 | uv.PEIf(E_EMAIL_POST, err) 16 | c.Status(201) 17 | } 18 | 19 | func getGolds(c *gin.Context) { 20 | var pi, order, q = &uv.PagingIn{}, &uv.Order{}, &service.GoldQ{} 21 | uv.PQ(c, pi, order, q) 22 | 23 | lst, po, err := service.GetGoldList(q, pi, order) 24 | uv.PEIf(E_GOLD_GET, err) 25 | c.JSON(200, uv.PagedOut(lst, po)) 26 | } 27 | -------------------------------------------------------------------------------- /console/biz/gm/view/roles.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/gm/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func getRoles(c *gin.Context) { 10 | mid := uv.PPID(c, "id") 11 | roles, err := service.GetRoles(mid) 12 | uv.PEIf(E_ROLES_GET, err) 13 | c.JSON(200, roles) 14 | } 15 | 16 | func changeQp(c *gin.Context) { 17 | args := &service.UpdateQpReq{} 18 | uv.PB(c, args) 19 | 20 | characNo := uv.PPID(c, "id") 21 | err := service.UpdateQp(characNo, args) 22 | uv.PEIf(E_ROLES_UPDATE_QP, err) 23 | c.Status(201) 24 | } 25 | 26 | func changePvp(c *gin.Context) { 27 | args := &service.UpdatePvpReq{} 28 | uv.PB(c, args) 29 | 30 | characNo := uv.PPID(c, "id") 31 | err := service.UpdatePvp(characNo, args) 32 | uv.PEIf(E_ROLES_UPDATE_PVP, err) 33 | c.Status(201) 34 | } 35 | -------------------------------------------------------------------------------- /console/biz/gm/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "console/mods/ginx" 4 | 5 | func InitGmRouter(r *ginx.RouterGroup) { 6 | 7 | accounts := r.Group("账号相关", "account") 8 | { 9 | accounts.GET("获取账号列表", "list", getAccounts) 10 | accounts.GET("获取角色信息", ":id/roles", getRoles) 11 | 12 | accounts.DELETE("删除账号", ":id", deleteAccount) 13 | accounts.POST("修改账号密码", ":id/change_password", changePassword) 14 | accounts.POST("创建账号", "", createAccount) 15 | accounts.PUT("修改账号信息", ":id", updateAccount) 16 | 17 | accounts.POST("充值", ":id/recharge", rechargeAccount) 18 | accounts.POST("重置创建角色", ":id/reset_create_charac", resetCreateCharac) 19 | } 20 | 21 | roles := r.Group("角色管理", "roles") 22 | { 23 | roles.PUT("修改QP", ":id/qp", changeQp) 24 | roles.PUT("修改pk段位", ":id/pvp", changePvp) 25 | 26 | roles.GET("获取任务列表", ":id/tasks", getRoleTasks) 27 | roles.PUT("更新任务列表", ":id/tasks", updateRoleTasks) 28 | 29 | roles.POST("发送邮件", ":id/email", sendEmail) // id === charac no 30 | } 31 | 32 | gold := r.Group("物品", "gold") 33 | { 34 | gold.GET("获取物品代码", "list", getGolds) 35 | } 36 | 37 | auction := r.Group("拍卖行", "auction") 38 | { 39 | auction.GET("状态", "state", getAuctionSate) 40 | auction.POST("开启", ":name/open", openAuctionSate) 41 | auction.POST("关闭", ":name/close", closeAuction) 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /console/biz/gm/view/task.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/gm/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func getRoleTasks(c *gin.Context) { 10 | characNo := uv.PPID(c, "id") 11 | data, err := service.GetTaskByRole(characNo) 12 | uv.PEIf(E_TASKS_GET, err) 13 | c.JSON(200, data) 14 | } 15 | 16 | func updateRoleTasks(c *gin.Context) { 17 | characNo := uv.PPID(c, "id") 18 | 19 | args := &service.TaskId{} 20 | uv.PB(c, args) 21 | 22 | err := service.UpdateTaskByRole(characNo, args.Ids) 23 | uv.PEIf(E_TASKS_UPDATE, err) 24 | c.Status(201) 25 | } 26 | -------------------------------------------------------------------------------- /console/biz/log/model/table.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/localhostjason/webserver/db" 5 | "time" 6 | ) 7 | 8 | const ( 9 | RechargeCera = 1 // action 充值D币 10 | RechargeCeraPoint = 2 // action 充值D点 11 | ) 12 | 13 | // RechargeLog 充值記錄 14 | type RechargeLog struct { 15 | Id int `json:"id" gorm:"primaryKey"` 16 | Uid int `json:"uid" gorm:"uid" gorm:"not null"` 17 | Ip string `json:"ip" gorm:"type=string;size=64"` 18 | Time time.Time `json:"time" gorm:"not null"` 19 | Action int `json:"action"` 20 | Number int `json:"number"` 21 | } 22 | 23 | // 操作日志 24 | 25 | type OperateLog struct { 26 | Id int `json:"id" gorm:"primaryKey"` 27 | Username string `json:"username"` 28 | Time time.Time `json:"time" gorm:"not null"` 29 | Ip string `json:"ip" gorm:"type=string;size=64"` 30 | Action string `json:"action"` 31 | Result string `json:"result"` 32 | } 33 | 34 | func init() { 35 | db.RegTables(&RechargeLog{}, &OperateLog{}) 36 | } 37 | -------------------------------------------------------------------------------- /console/biz/log/model/util.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "console/biz/user/auth/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/db" 7 | "time" 8 | ) 9 | 10 | func CreateRechargeLog(uid int, action int, number int, c *gin.Context) { 11 | log := &RechargeLog{ 12 | Uid: uid, 13 | Time: time.Now(), 14 | Action: action, 15 | Number: number, 16 | Ip: c.RemoteIP(), 17 | } 18 | 19 | db.DB.Create(log) 20 | return 21 | } 22 | 23 | func CreateOperateLog(action string, msg string, c *gin.Context) { 24 | currentUser := service.CurrentUser(c) 25 | log := &OperateLog{ 26 | Username: currentUser.Username, 27 | Time: time.Now(), 28 | Action: action, 29 | Result: msg, 30 | Ip: c.RemoteIP(), 31 | } 32 | db.DB.Create(&log) 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /console/biz/log/service/model.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/log/model" 5 | "github.com/localhostjason/webserver/db" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type RechargeQ struct { 10 | Uid string `form:"uid"` 11 | } 12 | 13 | func (q RechargeQ) FilterQuery(dbx *gorm.DB) (tx *gorm.DB) { 14 | tx = dbx.Model(&model.RechargeLog{}) 15 | 16 | if q.Uid != "" { 17 | tx = tx.Where("uid like ?", db.Like(q.Uid)) 18 | } 19 | return 20 | } 21 | 22 | type OperateQ struct { 23 | Action string `form:"action"` 24 | } 25 | 26 | func (q OperateQ) FilterQuery(dbx *gorm.DB) (tx *gorm.DB) { 27 | tx = dbx.Model(&model.OperateLog{}) 28 | 29 | if q.Action != "" { 30 | tx = tx.Where("action like ?", db.Like(q.Action)) 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /console/biz/log/service/operate_log.go: -------------------------------------------------------------------------------- 1 | package service 2 | -------------------------------------------------------------------------------- /console/biz/log/service/recharge_log.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/log/model" 5 | "github.com/localhostjason/webserver/db" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func GetReChargeLogList(q *RechargeQ, pi *uv.PagingIn, order *uv.Order) ([]model.RechargeLog, *uv.PagingOut, error) { 10 | tx := q.FilterQuery(db.DB) 11 | var lst = make([]model.RechargeLog, 0) 12 | 13 | po, err := uv.PagingFind(tx, &lst, pi, order) 14 | return lst, po, err 15 | } 16 | 17 | func GetOperateLogList(q *OperateQ, pi *uv.PagingIn, order *uv.Order) ([]model.OperateLog, *uv.PagingOut, error) { 18 | tx := q.FilterQuery(db.DB) 19 | var lst = make([]model.OperateLog, 0) 20 | 21 | po, err := uv.PagingFind(tx, &lst, pi, order) 22 | return lst, po, err 23 | } 24 | -------------------------------------------------------------------------------- /console/biz/log/view/errors.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "github.com/localhostjason/webserver/server/util/ue" 4 | 5 | const ( 6 | E_LOG_RECHARGE = "E_LOG_RECHARGE" 7 | E_LOG_OPERATE = "E_LOG_OPERATE" 8 | ) 9 | 10 | var eMap = map[string]ue.Error{ 11 | E_LOG_RECHARGE: {Code: E_LOG_RECHARGE, Desc: "获取充值记录错误", Msg: "%v"}, 12 | E_LOG_OPERATE: {Code: E_LOG_OPERATE, Desc: "获取充值记录错误", Msg: "%v"}, 13 | } 14 | 15 | func init() { 16 | ue.RegErrors(eMap) 17 | } 18 | -------------------------------------------------------------------------------- /console/biz/log/view/recharge_log.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/log/service" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/uv" 7 | ) 8 | 9 | func getRechargeLog(c *gin.Context) { 10 | var pi, order, q = &uv.PagingIn{}, &uv.Order{}, &service.RechargeQ{} 11 | uv.PQ(c, pi, order, q) 12 | lst, po, err := service.GetReChargeLogList(q, pi, order) 13 | uv.PEIf(E_LOG_RECHARGE, err) 14 | 15 | c.JSON(200, uv.PagedOut(lst, po)) 16 | } 17 | 18 | func getOperateLog(c *gin.Context) { 19 | var pi, order, q = &uv.PagingIn{}, &uv.Order{}, &service.OperateQ{} 20 | uv.PQ(c, pi, order, q) 21 | lst, po, err := service.GetOperateLogList(q, pi, order) 22 | uv.PEIf(E_LOG_RECHARGE, err) 23 | 24 | c.JSON(200, uv.PagedOut(lst, po)) 25 | } 26 | -------------------------------------------------------------------------------- /console/biz/log/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "console/mods/ginx" 4 | 5 | func InitLogRouter(r *ginx.RouterGroup) { 6 | r.GET("充值日志", "recharge", getRechargeLog) 7 | r.GET("操作日志", "operate", getOperateLog) 8 | } 9 | -------------------------------------------------------------------------------- /console/biz/middleware/casbin_rbac.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | roleService "console/biz/user/role/service" 5 | "console/mods/casbinx" 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func CasbinHandler(c *gin.Context) { 10 | // 获取请求的URI 11 | obj := c.Request.URL.RequestURI() 12 | // 获取请求方法 13 | act := c.Request.Method 14 | // 获取用户的角色 15 | user := roleService.GetCurrentUser(c) 16 | sub := user.Role 17 | 18 | // 判断策略中是否存在 19 | success, _ := casbinx.E.Enforce(sub, obj, act) 20 | 21 | if success { 22 | c.AbortWithStatus(403) 23 | } else { 24 | c.Next() 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /console/biz/middleware/error.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/localhostjason/webserver/server/util/ue" 6 | log "github.com/sirupsen/logrus" 7 | "net/http" 8 | "runtime/debug" 9 | "sync" 10 | ) 11 | 12 | var lock sync.Mutex 13 | 14 | // ErrorHandler remove 15 | func ErrorHandler(c *gin.Context) { 16 | defer func() { 17 | if r := recover(); r != nil { 18 | if err, ok := r.(*ue.Error); ok { 19 | c.AbortWithStatusJSON(http.StatusUnprocessableEntity, err) 20 | } else { 21 | log.Error(string(debug.Stack())) 22 | c.AbortWithStatus(http.StatusInternalServerError) 23 | } 24 | } 25 | 26 | }() 27 | lock.Lock() 28 | defer lock.Unlock() 29 | c.Next() 30 | } 31 | -------------------------------------------------------------------------------- /console/biz/middleware/operate.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "console/biz/log/model" 5 | "github.com/gin-gonic/gin" 6 | "github.com/localhostjason/webserver/server/util/ue" 7 | ) 8 | 9 | const OperateKey = "_OpLog" 10 | 11 | func OperateHandler(c *gin.Context) { 12 | c.Next() 13 | 14 | // 全局操作 入庫 15 | data, ok := c.Get(OperateKey) 16 | if ok { 17 | info, ok2 := data.(*ue.Info) 18 | if ok2 { 19 | model.CreateOperateLog(info.Action, info.Msg, c) 20 | } 21 | //fmt.Println(11111, data) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /console/biz/static/view.go: -------------------------------------------------------------------------------- 1 | package static 2 | 3 | import ( 4 | "console/mods/pathx" 5 | "path/filepath" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func AddStaticToRouter(r *gin.Engine) { 11 | exeDir, _ := pathx.GetExeDir() 12 | 13 | r.GET("/", redirectRoot) 14 | r.Static("/static", filepath.Join(exeDir, "web", "static")) 15 | } 16 | 17 | func redirectRoot(c *gin.Context) { 18 | c.Redirect(302, "/static/index.html") 19 | } 20 | -------------------------------------------------------------------------------- /console/biz/user/auth/model/config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/localhostjason/webserver/server/config" 4 | 5 | const _key = "auth" 6 | 7 | func regConfig() { 8 | c := ConfigAuth{ 9 | Realm: "test zone", 10 | Secret: "f450a7bdbde3416d22474b9fdc2a3636", 11 | IDKey: "username", 12 | Timeout: 12 * 3600, 13 | MaxRefresh: 3600, 14 | } 15 | _ = config.RegConfig(_key, c) 16 | } 17 | 18 | type ConfigAuth struct { 19 | Realm string `json:"realm"` 20 | Secret string `json:"secret"` 21 | IDKey string `json:"id_key"` 22 | Timeout int `json:"timeout"` 23 | MaxRefresh int `json:"max_refresh"` 24 | } 25 | 26 | func GetConfig() (ConfigAuth, error) { 27 | var c ConfigAuth 28 | err := config.GetConfig(_key, &c) 29 | return c, err 30 | } 31 | 32 | func init() { 33 | regConfig() 34 | } 35 | -------------------------------------------------------------------------------- /console/biz/user/auth/service/op_info.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/localhostjason/webserver/server/util/ue" 5 | ) 6 | 7 | const ( 8 | I_OP_LOGIN = "I_OP_LOGIN" 9 | I_OP_LOGOUT = "I_OP_LOGOUT" 10 | ) 11 | 12 | var iMap = map[string]ue.Info{ 13 | I_OP_LOGIN: {Code: I_OP_LOGIN, Action: "登录", Msg: "用户名: %v,登录系统"}, 14 | I_OP_LOGOUT: {Code: I_OP_LOGOUT, Action: "退出登录", Msg: "用户名: %v"}, 15 | } 16 | 17 | func init() { 18 | ue.RegInfos(iMap) 19 | } 20 | 21 | const operateKey = "_OpLog" 22 | -------------------------------------------------------------------------------- /console/biz/user/auth/view/auth.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/user/auth/model" 5 | "console/biz/user/auth/service" 6 | jwt "github.com/appleboy/gin-jwt/v2" 7 | "github.com/gin-gonic/gin" 8 | "time" 9 | ) 10 | 11 | func AddJwtAuth(authApi, authedApi *gin.RouterGroup) error { 12 | m, err := newAuthMiddleWare() 13 | if err != nil { 14 | return err 15 | } 16 | 17 | authApi.POST("login", m.LoginHandler) 18 | authedApi.Use(m.MiddlewareFunc()) 19 | 20 | authedApi.POST("auth/logout", m.LogoutHandler) 21 | return nil 22 | } 23 | 24 | func newAuthMiddleWare() (*jwt.GinJWTMiddleware, error) { 25 | conf, err := model.GetConfig() 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | c := authConfig(conf) 31 | return jwt.New(c) 32 | } 33 | 34 | func authConfig(conf model.ConfigAuth) *jwt.GinJWTMiddleware { 35 | c := jwt.GinJWTMiddleware{ 36 | Realm: conf.Realm, 37 | Key: []byte(conf.Secret), 38 | Timeout: time.Duration(conf.Timeout) * time.Second, 39 | MaxRefresh: time.Duration(conf.MaxRefresh) * time.Second, 40 | IdentityKey: conf.IDKey, 41 | PayloadFunc: service.PayloadFunc, 42 | IdentityHandler: service.IdHandler, 43 | Authenticator: service.Authenticator, 44 | Authorizator: service.Authorizator, 45 | Unauthorized: service.UnAuth, 46 | LoginResponse: service.LoginResponse, 47 | LogoutResponse: service.LogoutResponse, 48 | TokenLookup: "header: Authorization, query: token", 49 | TokenHeadName: "Bearer", 50 | TimeFunc: time.Now, 51 | } 52 | return &c 53 | } 54 | -------------------------------------------------------------------------------- /console/biz/user/role/model/role_map.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type RolePost struct { 4 | Role string `json:"role" binding:"required"` 5 | Path string `json:"path" binding:"required"` 6 | Method string `json:"method" binding:"required,oneof=PUT POST GET DELETE"` 7 | } 8 | -------------------------------------------------------------------------------- /console/biz/user/role/service/role.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | jwtService "console/biz/user/auth/service" 5 | "console/biz/user/users/model" 6 | "console/mods/casbinx" 7 | "errors" 8 | "github.com/gin-gonic/gin" 9 | "github.com/localhostjason/webserver/db" 10 | "gorm.io/gorm" 11 | ) 12 | 13 | func CreateRole(role, path, method string) error { 14 | if success, _ := casbinx.E.AddPolicy(role, path, method); !success { 15 | return errors.New("存在相同的策略,添加失败") 16 | } 17 | return nil 18 | } 19 | 20 | func UpdateRoleDesc(id int, desc string) error { 21 | policy, err := checkPolicyInDb(id) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | policy.Desc = desc 27 | return db.DB.Save(policy).Error 28 | } 29 | 30 | func UpdateRole(id int, role, path, method string) error { 31 | policy, err := checkPolicyInDb(id) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | updated, err := casbinx.E.UpdatePolicy([]string{policy.Role, policy.Path, policy.Method}, []string{role, path, method}) 37 | if err != nil { 38 | return err 39 | } 40 | if !updated { 41 | return errors.New("更新策略失败") 42 | } 43 | return nil 44 | } 45 | 46 | func DeleteRole(id int) error { 47 | policy, err := checkPolicyInDb(id) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | ok, err := casbinx.E.RemoveFilteredNamedPolicy(policy.PType, 0, policy.Role, policy.Path, policy.Method) 53 | if err != nil { 54 | return err 55 | } 56 | if !ok { 57 | return errors.New("已删除策略") 58 | } 59 | return nil 60 | } 61 | 62 | func checkPolicyInDb(id int) (*casbinx.CasbinRule, error) { 63 | var rule casbinx.CasbinRule 64 | err := db.DB.First(&rule, &casbinx.CasbinRule{ID: uint(id)}).Error 65 | if errors.Is(err, gorm.ErrRecordNotFound) { 66 | return &rule, errors.New("未找到策略") 67 | } 68 | return &rule, nil 69 | } 70 | 71 | func GetRolePolicy() [][]string { 72 | return casbinx.E.GetPolicy() 73 | } 74 | 75 | func GetCurrentUser(ctx *gin.Context) *model.User { 76 | currentUser := jwtService.CurrentUser(ctx) 77 | return currentUser 78 | } 79 | -------------------------------------------------------------------------------- /console/biz/user/role/view/errors.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "github.com/localhostjason/webserver/server/util/ue" 4 | 5 | const ( 6 | E_USER_ROLE_GET = "E_USER_ROLE_GET" 7 | E_USER_ROLE_UPDATE = "E_USER_ROLE_UPDATE" 8 | E_USER_ROLE_DELETE = "E_USER_ROLE_DELETE" 9 | E_USER_ROLE_CREATE = "E_USER_ROLE_CREATE" 10 | ) 11 | 12 | var eMap = map[string]ue.Error{ 13 | E_USER_ROLE_GET: {Code: E_USER_ROLE_GET, Desc: "获取权限错误", Msg: "%v"}, 14 | E_USER_ROLE_UPDATE: {Code: E_USER_ROLE_UPDATE, Desc: "更新权限错误", Msg: "%v"}, 15 | E_USER_ROLE_DELETE: {Code: E_USER_ROLE_DELETE, Desc: "删除权限错误", Msg: "%v"}, 16 | E_USER_ROLE_CREATE: {Code: E_USER_ROLE_CREATE, Desc: "创建权限错误", Msg: "%v"}, 17 | } 18 | 19 | func init() { 20 | ue.RegErrors(eMap) 21 | } 22 | -------------------------------------------------------------------------------- /console/biz/user/role/view/role.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/user/role/model" 5 | "console/biz/user/role/service" 6 | "github.com/gin-gonic/gin" 7 | "github.com/localhostjason/webserver/server/util/uv" 8 | "strconv" 9 | ) 10 | 11 | func createRole(c *gin.Context) { 12 | rq := &model.RolePost{} 13 | uv.PB(c, rq) 14 | 15 | err := service.CreateRole(rq.Role, rq.Path, rq.Method) 16 | uv.PEIf(E_USER_ROLE_CREATE, err) 17 | c.Status(201) 18 | } 19 | 20 | func deleteRole(c *gin.Context) { 21 | id := c.Param("id") 22 | idInt, _ := strconv.Atoi(id) 23 | 24 | err := service.DeleteRole(idInt) 25 | uv.PEIf(E_USER_ROLE_DELETE, err) 26 | c.JSON(200, service.GetRolePolicy()) 27 | } 28 | 29 | func updateRole(c *gin.Context) { 30 | err := service.UpdateRole(1, "admin", "/api/user/password", "GET") 31 | uv.PEIf(E_USER_ROLE_UPDATE, err) 32 | c.JSON(200, service.GetRolePolicy()) 33 | } 34 | 35 | func getRole(c *gin.Context) { 36 | data := service.GetRolePolicy() 37 | c.JSON(200, data) 38 | } 39 | -------------------------------------------------------------------------------- /console/biz/user/role/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "console/mods/ginx" 4 | 5 | func InitRoleRouter(r *ginx.RouterGroup) { 6 | r.GET("获取权限", "", getRole) 7 | r.DELETE("删除权限", ":id", deleteRole) 8 | r.PUT("更新权限", ":id", updateRole) 9 | r.POST("创建权限", "", createRole) 10 | } 11 | -------------------------------------------------------------------------------- /console/biz/user/router.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | role "console/biz/user/role/view" 5 | users "console/biz/user/users/view" 6 | "console/mods/ginx" 7 | ) 8 | 9 | func InitUserRouter(g *ginx.RouterGroup) { 10 | { 11 | role.InitRoleRouter(g.Group("用户权限", "role")) 12 | users.InitUsersRouter(g.Group("用户信息", "")) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /console/biz/user/users/model/table.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/localhostjason/webserver/db" 5 | uuid "github.com/satori/go.uuid" 6 | "golang.org/x/crypto/bcrypt" 7 | "time" 8 | ) 9 | 10 | type User struct { 11 | // 基本信息 12 | Id int64 `json:"id" gorm:"primaryKey"` 13 | Username string `json:"username" gorm:"type:string;size:64;unique;not null"` 14 | Password string `json:"-" gorm:"column:_password;type:string;size:128"` 15 | LastLoginTime *time.Time `json:"last_login_time"` 16 | Time time.Time `json:"time"` // 创建时间 17 | JwtKey uuid.UUID `json:"-" gorm:"type:string;size:128;"` // 为每个用户存一个唯一的jwt key (通用唯一识别码) 18 | 19 | Role string `json:"role"` 20 | Email string `json:"email" gorm:"type:string;size:64"` 21 | Desc string `json:"desc" gorm:"type:string;size:256"` 22 | 23 | IsSuperAdmin bool `json:"is_super_admin"` 24 | } 25 | 26 | func (u *User) SetPassword(password string) { 27 | b, err := bcrypt.GenerateFromPassword([]byte(password), 14) 28 | if err != nil { 29 | return 30 | } 31 | u.Password = string(b) 32 | } 33 | 34 | func (u *User) CheckPassword(password string) bool { 35 | err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) 36 | return err == nil 37 | } 38 | 39 | func (u *User) GetInfo() map[string]interface{} { 40 | return map[string]interface{}{ 41 | "username": u.Username, 42 | "role": u.Role, 43 | "email": u.Email, 44 | "desc": u.Desc, 45 | "last_login_time": u.LastLoginTime, 46 | } 47 | } 48 | 49 | func init() { 50 | db.RegTables(&User{}) 51 | } 52 | -------------------------------------------------------------------------------- /console/biz/user/users/model/user_map.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type UserInfoPut struct { 4 | Desc string `json:"desc"` 5 | } 6 | -------------------------------------------------------------------------------- /console/biz/user/users/service/user.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | roleService "console/biz/user/role/service" 5 | "console/biz/user/users/model" 6 | "github.com/gin-gonic/gin" 7 | "github.com/localhostjason/webserver/db" 8 | ) 9 | 10 | func GetUserInfo(c *gin.Context) *model.User { 11 | return roleService.GetCurrentUser(c) 12 | } 13 | 14 | func UpdateUserInfo(c *gin.Context, desc string) error { 15 | currentUser := roleService.GetCurrentUser(c) 16 | currentUser.Desc = desc 17 | 18 | return db.DB.Save(currentUser).Error 19 | } 20 | 21 | func GetUsers() []model.User { 22 | var users = make([]model.User, 0) 23 | db.DB.Find(&users) 24 | return users 25 | } 26 | -------------------------------------------------------------------------------- /console/biz/user/users/service/user_init.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "console/biz/user/users/model" 5 | "github.com/localhostjason/webserver/db" 6 | uuid "github.com/satori/go.uuid" 7 | "time" 8 | ) 9 | 10 | const ( 11 | Normal = 1 12 | Locked = -1 13 | ) 14 | 15 | const ( 16 | ADMIN = "admin" 17 | SECURITY = "security" 18 | AUDITOR = "auditor" 19 | ) 20 | 21 | const _defaultPassword = "123" 22 | 23 | func InitUser() error { 24 | now := time.Now() 25 | users := []model.User{ 26 | { 27 | Username: "admin", 28 | Role: ADMIN, 29 | Email: "", 30 | Desc: "超级管理员", 31 | Time: now, 32 | IsSuperAdmin: true, 33 | }, 34 | } 35 | 36 | for i := range users { 37 | u := &users[i] 38 | 39 | var userList []model.User 40 | if db.DB.Limit(1).Where("username = ? ", u.Username).Find(&userList); len(userList) == 0 { 41 | u.SetPassword(_defaultPassword) 42 | u.JwtKey = uuid.NewV4() 43 | db.DB.Create(u) 44 | } 45 | } 46 | return nil 47 | } 48 | 49 | func init() { 50 | db.AddInitHook(InitUser) 51 | } 52 | -------------------------------------------------------------------------------- /console/biz/user/users/view/errors.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "github.com/localhostjason/webserver/server/util/ue" 4 | 5 | const ( 6 | E_USER_INFO_GET = "E_USER_INFO_GET" 7 | E_USER_INFO_UPDATE = "E_USER_INFO_UPDATE" 8 | E_USERS_GET = "E_USERS_GET" 9 | ) 10 | 11 | var eMap = map[string]ue.Error{ 12 | E_USER_INFO_GET: {Code: E_USER_INFO_GET, Desc: "获取个人用户错误", Msg: "%v"}, 13 | E_USER_INFO_UPDATE: {Code: E_USER_INFO_UPDATE, Desc: "更新个人用户错误", Msg: "%v"}, 14 | E_USERS_GET: {Code: E_USERS_GET, Desc: "获取用户列表错误", Msg: "%v"}, 15 | } 16 | 17 | func init() { 18 | ue.RegErrors(eMap) 19 | } 20 | -------------------------------------------------------------------------------- /console/biz/user/users/view/op_info.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/middleware" 5 | "github.com/localhostjason/webserver/server/util/ue" 6 | ) 7 | 8 | const ( 9 | I_OP = "I_OP" 10 | ) 11 | 12 | var iMap = map[string]ue.Info{ 13 | I_OP: {Code: I_OP, Action: "测试", Msg: "测试ID: %v, 测试名称:%v"}, 14 | } 15 | 16 | func init() { 17 | ue.RegInfos(iMap) 18 | } 19 | 20 | const operateKey = middleware.OperateKey 21 | -------------------------------------------------------------------------------- /console/biz/user/users/view/router.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/mods/ginx" 5 | "console/mods/pathx" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "net/url" 9 | "path/filepath" 10 | ) 11 | 12 | func InitUsersRouter(r *ginx.RouterGroup) { 13 | userInfo := r.Group("用户个人信息", "info") 14 | { 15 | userInfo.GET("获取个人信息", "", getUserInfo) 16 | userInfo.PUT("更新个人信息", "", updateUserInfo) 17 | } 18 | 19 | users := r.Group("用户管理", "list") 20 | { 21 | users.GET("获取用户列表", "", getUsers) 22 | 23 | } 24 | 25 | r.GET("下载", "file", loadFile) 26 | } 27 | 28 | func loadFile(c *gin.Context) { 29 | //uv.PEIf(E_USER_INFO_UPDATE, errors.New("test error")) 30 | pwd, _ := pathx.GetExeDir() 31 | file := filepath.Join(pwd, "config", "rbac_model.conf") 32 | output := url.QueryEscape(filepath.Base(file)) 33 | c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", output)) 34 | c.Writer.Header().Add("Access-Control-Expose-Headers", "Content-Disposition") 35 | c.Writer.Header().Add("Content-Type", "application/octet-stream") 36 | c.File(file) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /console/biz/user/users/view/user.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "console/biz/user/users/model" 5 | "console/biz/user/users/service" 6 | "github.com/gin-gonic/gin" 7 | "github.com/localhostjason/webserver/server/util/uv" 8 | ) 9 | 10 | func getUserInfo(c *gin.Context) { 11 | //c.Set(operateKey, uv.OP(I_OP, "1", "hello")) 12 | 13 | data := service.GetUserInfo(c) 14 | c.JSON(200, data) 15 | } 16 | 17 | func updateUserInfo(c *gin.Context) { 18 | userQ := &model.UserInfoPut{} 19 | uv.PB(c, userQ) 20 | 21 | err := service.UpdateUserInfo(c, userQ.Desc) 22 | uv.PEIf(E_USER_INFO_UPDATE, err) 23 | c.Status(201) 24 | } 25 | 26 | func getUsers(c *gin.Context) { 27 | data := service.GetUsers() 28 | c.JSON(200, data) 29 | } 30 | -------------------------------------------------------------------------------- /console/biz/view/config.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import "github.com/localhostjason/webserver/server/config" 4 | 5 | const _key = "api" 6 | 7 | type ConfigView struct { 8 | CORS bool `json:"cors"` 9 | } 10 | 11 | func init() { 12 | c := ConfigView{CORS: true} 13 | _ = config.RegConfig(_key, c) 14 | } 15 | 16 | func GetConfig() (ConfigView, error) { 17 | var c ConfigView 18 | err := config.GetConfig(_key, &c) 19 | return c, err 20 | } 21 | -------------------------------------------------------------------------------- /console/biz/view/cors.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | "github.com/gin-contrib/cors" 5 | "github.com/gin-gonic/gin" 6 | ) 7 | 8 | func setCORS(r *gin.Engine) { 9 | config := cors.DefaultConfig() 10 | config.AddAllowHeaders("*") 11 | config.AllowOrigins = []string{"*"} 12 | config.AllowMethods = []string{"*"} 13 | r.Use(cors.New(config)) 14 | } 15 | -------------------------------------------------------------------------------- /console/biz/view/views.go: -------------------------------------------------------------------------------- 1 | package view 2 | 3 | import ( 4 | client "console/biz/client/view" 5 | dash "console/biz/dash/view" 6 | gm "console/biz/gm/view" 7 | log "console/biz/log/view" 8 | "console/biz/middleware" 9 | "console/biz/static" 10 | "console/biz/user" 11 | auth "console/biz/user/auth/view" 12 | "console/mods/ginx" 13 | "github.com/gin-gonic/gin" 14 | 15 | _ "console/biz/user/users/service" 16 | ) 17 | 18 | func SetView(r *gin.Engine) error { 19 | c, err := GetConfig() 20 | if err != nil { 21 | return err 22 | } 23 | if c.CORS { 24 | setCORS(r) 25 | } 26 | 27 | static.AddStaticToRouter(r) 28 | r.Use(middleware.OperateHandler) 29 | 30 | apiAuth := r.Group("api/auth") 31 | api := r.Group("api") 32 | 33 | err = auth.AddJwtAuth(apiAuth, api) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | // load casbin 39 | api.Use(middleware.CasbinHandler, middleware.ErrorHandler) 40 | routeGroup := ginx.NewRouterGroup(api) 41 | { 42 | gm.InitGmRouter(routeGroup.Group("GM管理", "gm")) 43 | client.InitClientRouter(routeGroup.Group("客户端", "client")) 44 | 45 | user.InitUserRouter(routeGroup.Group("用户管理", "user")) 46 | log.InitLogRouter(routeGroup.Group("日志", "log")) 47 | dash.InitDashRouter(routeGroup.Group("统计", "dash")) 48 | } 49 | 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /console/cmds/args.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | import ( 4 | "console/mods/game_db" 5 | "flag" 6 | "fmt" 7 | "github.com/gin-gonic/gin" 8 | "github.com/localhostjason/webserver/server" 9 | "github.com/localhostjason/webserver/server/config" 10 | ) 11 | 12 | type MainWorkFunc func(r *gin.Engine) error 13 | 14 | type MainServer struct { 15 | DefaultConfigPath string 16 | } 17 | 18 | func NewMainServer() *MainServer { 19 | return &MainServer{} 20 | } 21 | 22 | func (m *MainServer) SetServerConfigFile(file string) { 23 | m.DefaultConfigPath = file 24 | } 25 | 26 | // Run 可根据自己业务 替换扩展 27 | func (m *MainServer) Run() { 28 | setDefaultPathNoPath := server.InitDefaultServerConfigFile(m.DefaultConfigPath) 29 | 30 | configPath := flag.String("p", setDefaultPathNoPath, "path to config") 31 | initDB := flag.Bool("i", false, "int db") 32 | dumpConfig := flag.Bool("d", false, "dump default config") 33 | 34 | createPem := flag.Bool("pem", false, "create pem") 35 | 36 | // for service 37 | singleMode := flag.Bool("x", false, "start, no daemon/service mode") 38 | svcCMD := flag.String("k", "", "cmds:start|stop|status, windows: install|uninstall") 39 | 40 | flag.Parse() 41 | 42 | if err := config.SetConfigFile(*configPath); err != nil { 43 | fmt.Println("failed to set config path", *configPath, err) 44 | return 45 | } 46 | 47 | // commands 48 | 49 | if *dumpConfig { 50 | DumpDefaultConfig() 51 | return 52 | } 53 | 54 | if *createPem { 55 | if err := CreatePem(); err != nil { 56 | fmt.Println("error create pem", err) 57 | return 58 | } 59 | fmt.Println("success create publicKey.pem / private.pem") 60 | return 61 | } 62 | 63 | // DB 初始表结构和默认值 64 | if *initDB { 65 | if err := SyncDB(); err != nil { 66 | fmt.Println("error when sync db schema", err) 67 | return 68 | } 69 | if err := game_db.SyncDB(); err != nil { 70 | fmt.Println("error when sync game db schema", err) 71 | return 72 | } 73 | 74 | fmt.Println("success: sync db schema") 75 | return 76 | } 77 | 78 | RunService(*singleMode, *svcCMD) 79 | } 80 | -------------------------------------------------------------------------------- /console/cmds/config.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | import ( 4 | "console/biz/client/service" 5 | "errors" 6 | "fmt" 7 | "github.com/localhostjason/webserver/db" 8 | "github.com/localhostjason/webserver/server/config" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func DumpDefaultConfig() { 13 | content, err := config.GeneDefaultConfig() 14 | if err != nil { 15 | fmt.Println("failed to generate default config") 16 | } else { 17 | fmt.Println(string(content)) 18 | } 19 | } 20 | 21 | func SyncDB() (err error) { 22 | if !db.DBEnable() { 23 | logrus.Info("no enable sql") 24 | return 25 | } 26 | err = db.Connect() 27 | if err != nil { 28 | return errors.New(fmt.Sprintf("failed to migrate:%v", err)) 29 | } 30 | err = db.Migrate() 31 | if err != nil { 32 | return 33 | } 34 | 35 | err = db.InitData() 36 | return 37 | } 38 | 39 | func AutoMigrate() (err error) { 40 | if !db.DBEnable() { 41 | return 42 | } 43 | err = db.Connect() 44 | if err != nil { 45 | return errors.New(fmt.Sprintf("failed to migrate:%v", err)) 46 | } 47 | return db.Migrate() 48 | } 49 | 50 | func CreatePem() error { 51 | rsa := service.NewRsa() 52 | return rsa.GenRsaKey(2048) 53 | } 54 | -------------------------------------------------------------------------------- /console/cmds/server.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | func Run() { 4 | s := NewMainServer() 5 | s.Run() 6 | } 7 | -------------------------------------------------------------------------------- /console/cmds/service.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/localhostjason/webserver/svc" 7 | 8 | log "github.com/sirupsen/logrus" 9 | ) 10 | 11 | func createService(singleMode bool) (*svc.Svc, error) { 12 | prc := NewProc(singleMode) 13 | svcx, err := NewService(prc) 14 | if err != nil { 15 | return nil, errors.New(fmt.Sprintf("failed to create program:%v", err)) 16 | } 17 | return svcx, nil 18 | } 19 | 20 | func RunService(singleMode bool, cmd string) { 21 | defer func() { 22 | if r := recover(); r != nil { 23 | fmt.Println(r) 24 | } 25 | }() 26 | 27 | s, err := createService(true) 28 | if err != nil { 29 | log.Fatalln("failed to start", err) 30 | } 31 | 32 | s.RunMain(singleMode, cmd) 33 | } 34 | -------------------------------------------------------------------------------- /console/cmds/service_daemon.go: -------------------------------------------------------------------------------- 1 | package cmds 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/localhostjason/webserver/server" 7 | "github.com/localhostjason/webserver/server/config" 8 | "github.com/localhostjason/webserver/svc" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | ) 13 | 14 | const _groupService = "service" 15 | 16 | type ServiceConfig struct { 17 | PidFile string `json:"pid_file"` 18 | DaemonLog string `json:"daemon_log"` 19 | } 20 | 21 | func getConf() (ServiceConfig, error) { 22 | var c ServiceConfig 23 | err := config.GetConfig(_groupService, &c) 24 | if err != nil { 25 | return c, err 26 | 27 | } 28 | confWebServer, err := server.GetConfig() 29 | if err != nil { 30 | return c, err 31 | } 32 | logPath := confWebServer.LogPath 33 | c.PidFile = filepath.Join(logPath, c.PidFile) 34 | c.DaemonLog = filepath.Join(logPath, c.DaemonLog) 35 | 36 | if err := os.MkdirAll(logPath, 0755); err != nil { 37 | return c, fmt.Errorf("failed to create log dir %s", logPath) 38 | } 39 | 40 | return c, nil 41 | } 42 | 43 | func init() { 44 | c := ServiceConfig{ 45 | PidFile: "console.pid", 46 | DaemonLog: "daemon.log", 47 | } 48 | _ = config.RegConfig(_groupService, c) 49 | } 50 | 51 | func NewService(prc *MainProc) (*svc.Svc, error) { 52 | if runtime.GOOS == "windows" { 53 | svcName := "center" 54 | svcDescription := "center 服务" 55 | return svc.NewSvc(svcName, svcDescription, prc), nil 56 | } else { 57 | c, err := getConf() 58 | if err != nil { 59 | return nil, errors.New(fmt.Sprintf("failed to get daemon config:%v", err)) 60 | } 61 | return svc.NewSvc(c.PidFile, c.DaemonLog, prc), nil 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /console/config/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub,obj,act 3 | 4 | [policy_definition] 5 | p = sub,obj,act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act 15 | -------------------------------------------------------------------------------- /console/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "console/cmds" 5 | ) 6 | 7 | func main() { 8 | cmds.Run() 9 | } 10 | -------------------------------------------------------------------------------- /console/mods/casbinx/casbin.go: -------------------------------------------------------------------------------- 1 | package casbinx 2 | 3 | import ( 4 | "github.com/casbin/casbin/v2" 5 | gormadapter "github.com/casbin/gorm-adapter/v3" 6 | "github.com/localhostjason/webserver/db" 7 | "github.com/localhostjason/webserver/util" 8 | ) 9 | 10 | var E *casbin.Enforcer // 作为全局访问 11 | 12 | type CasBin struct { 13 | File string 14 | } 15 | 16 | func NewCasBin() *CasBin { 17 | cfg, _ := GetCasbinConfig() 18 | return &CasBin{File: cfg.File} 19 | } 20 | 21 | func (c *CasBin) Run() error { 22 | if !util.PathExists(c.File) { 23 | createCasbinConfigFile(c.File) 24 | } 25 | 26 | dbx := db.DB 27 | adapter, err := gormadapter.NewAdapterByDBWithCustomTable(dbx, &CasbinRule{}) 28 | 29 | var enforcer *casbin.Enforcer 30 | enforcer, err = casbin.NewEnforcer(c.File, adapter) 31 | 32 | err = enforcer.LoadPolicy() 33 | if err != nil { 34 | return err 35 | } 36 | E = enforcer 37 | return nil 38 | } 39 | 40 | func (c *CasBin) Clear(v int, p ...string) bool { 41 | ok, _ := E.RemoveFilteredPolicy(v, p...) 42 | return ok 43 | } 44 | 45 | func (c *CasBin) Get() [][]string { 46 | policy := E.GetPolicy() 47 | return policy 48 | } 49 | -------------------------------------------------------------------------------- /console/mods/casbinx/config.go: -------------------------------------------------------------------------------- 1 | package casbinx 2 | 3 | import ( 4 | "console/mods/pathx" 5 | "github.com/localhostjason/webserver/server/config" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | const _casbinKey = "casbin" 11 | const casbinText = `[request_definition] 12 | r = sub,obj,act 13 | 14 | [policy_definition] 15 | p = sub,obj,act 16 | 17 | [role_definition] 18 | g = _, _ 19 | 20 | [policy_effect] 21 | e = some(where (p.eft == allow)) 22 | 23 | [matchers] 24 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act 25 | ` 26 | 27 | func regCasbinConfig() { 28 | extPath, _ := pathx.GetExeDir() 29 | file := filepath.Join(extPath, "config", "rbac_model.conf") 30 | 31 | c := CasbinConfig{ 32 | File: file, 33 | } 34 | _ = config.RegConfig(_casbinKey, c) 35 | } 36 | 37 | type CasbinConfig struct { 38 | File string `json:"file"` 39 | } 40 | 41 | func createCasbinConfigFile(file string) { 42 | _ = os.WriteFile(file, []byte(casbinText), os.ModePerm) 43 | } 44 | 45 | func GetCasbinConfig() (CasbinConfig, error) { 46 | var c CasbinConfig 47 | err := config.GetConfig(_casbinKey, &c) 48 | return c, err 49 | } 50 | 51 | func init() { 52 | regCasbinConfig() 53 | } 54 | -------------------------------------------------------------------------------- /console/mods/casbinx/table.go: -------------------------------------------------------------------------------- 1 | package casbinx 2 | 3 | type CasbinRule struct { 4 | ID uint `json:"id" gorm:"primaryKey;autoIncrement"` 5 | PType string `json:"p_type" gorm:"column:ptype" description:"策略类型"` 6 | Role string `json:"role" gorm:"column:v0" description:"角色"` 7 | Path string `json:"path" gorm:"column:v1" description:"api路径 obj"` 8 | Method string `json:"method" gorm:"column:v2" description:"访问方法 act"` 9 | V3 string `json:"-" gorm:"column:v3"` 10 | V4 string `json:"-" gorm:"column:v4"` 11 | V5 string `json:"-" gorm:"column:v5" ` 12 | V6 string `json:"-" gorm:"column:v6" ` 13 | V7 string `json:"-" gorm:"column:v7" ` 14 | ApiName string `json:"api_name" gorm:"-"` 15 | GroupName string `json:"group_name" gorm:"-"` 16 | Desc string `json:"desc" description:"策略描述"` 17 | } 18 | -------------------------------------------------------------------------------- /console/mods/game_db/config.go: -------------------------------------------------------------------------------- 1 | package game_db 2 | 3 | import "github.com/localhostjason/webserver/server/config" 4 | 5 | const _key = "game_db" 6 | 7 | type MysqlDBConfig struct { 8 | Key string `json:"key"` 9 | User string `json:"user"` 10 | Password string `json:"password"` 11 | Host string `json:"host"` 12 | Port int `json:"port"` 13 | DB string `json:"db"` 14 | Charset string `json:"charset"` 15 | Timeout int `json:"timeout"` 16 | MultiStatements bool `json:"multi_statements"` 17 | Debug bool `json:"debug"` 18 | } 19 | 20 | type SqliteDBConfig struct { 21 | Key string `json:"key"` 22 | DbFile string `json:"db_file"` 23 | Debug bool `json:"debug"` 24 | } 25 | 26 | type DbConfig struct { 27 | Enable bool `json:"enable"` 28 | Mysql []MysqlDBConfig `json:"mysql"` 29 | Sqlite []SqliteDBConfig `json:"sqlite"` 30 | } 31 | 32 | func init() { 33 | mc := MysqlDBConfig{ 34 | Key: "TestMysql1", 35 | User: "root", 36 | Password: "123456", 37 | Host: "127.0.0.1", 38 | Port: 3306, 39 | DB: "test", 40 | Charset: "utf8mb4", 41 | MultiStatements: false, 42 | Timeout: 5, 43 | Debug: false, 44 | } 45 | 46 | sc := SqliteDBConfig{ 47 | Key: "TestSqlite1", 48 | DbFile: "data/data.db", 49 | Debug: false, 50 | } 51 | 52 | c := DbConfig{ 53 | Enable: true, 54 | Mysql: []MysqlDBConfig{mc}, 55 | Sqlite: []SqliteDBConfig{sc}, 56 | } 57 | _ = config.RegConfig(_key, c) 58 | } 59 | 60 | func getDbConfig() DbConfig { 61 | var c DbConfig 62 | _ = config.GetConfig(_key, &c) 63 | return c 64 | } 65 | -------------------------------------------------------------------------------- /console/mods/game_db/db.go: -------------------------------------------------------------------------------- 1 | package game_db 2 | 3 | import ( 4 | log "github.com/sirupsen/logrus" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | var DBPools = new(DBPool) 9 | 10 | func Connect() error { 11 | c := getDbConfig() 12 | if !c.Enable { 13 | return nil 14 | } 15 | 16 | if err := ConnectWithMysqlConfig(c.Mysql); err != nil { 17 | return err 18 | } 19 | 20 | if err := ConnectWithSqliteConfig(c.Sqlite); err != nil { 21 | return err 22 | } 23 | return nil 24 | } 25 | 26 | func DBEnable() bool { 27 | c := getDbConfig() 28 | return c.Enable 29 | } 30 | 31 | var tableModels = make(map[any][]interface{}) 32 | 33 | // Migrate 初始化或升级表结构 34 | func Migrate() error { 35 | DBPools.Range(func(k, db any) bool { 36 | if err := db.(*gorm.DB).AutoMigrate(tableModels[k]...); err != nil { 37 | log.Fatalln("failed to migrate database:", k, ",err:", err) 38 | return false 39 | } 40 | return true 41 | }) 42 | 43 | return nil 44 | } 45 | 46 | // RegTables 其他模块注册需要访问的表, 会被自动创建 47 | func RegTables(k any, tables ...interface{}) { 48 | tableModels[k] = append(tableModels[k], tables...) 49 | } 50 | 51 | type InitGameDataHandler func() error 52 | 53 | var _initGameHooks []InitGameDataHandler 54 | 55 | // AddInitHook db连接后执行的函数, 可用于初始化数据等 56 | func AddInitHook(h InitGameDataHandler) { 57 | _initGameHooks = append(_initGameHooks, h) 58 | } 59 | 60 | func InitData() error { 61 | for _, h := range _initGameHooks { 62 | if err := h(); err != nil { 63 | return err 64 | } 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /console/mods/game_db/db_mysql.go: -------------------------------------------------------------------------------- 1 | // Package game_db Package db 处理数据库连接信息,实现Model对象的存储读取 2 | package game_db 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | log "github.com/sirupsen/logrus" 8 | "sync" 9 | 10 | "gorm.io/driver/mysql" 11 | "gorm.io/gorm" 12 | "gorm.io/gorm/schema" 13 | ) 14 | 15 | // ConnectWithMysqlConfig 连接,检验配置是否正确 16 | func ConnectWithMysqlConfig(cfgs []MysqlDBConfig) error { 17 | if len(cfgs) == 0 { 18 | return nil 19 | } 20 | 21 | var wg = &sync.WaitGroup{} 22 | for _, c := range cfgs { 23 | wg.Add(1) 24 | 25 | go func(c MysqlDBConfig) { 26 | defer wg.Done() 27 | 28 | err := connectMysqlOne(c) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | }(c) 33 | 34 | } 35 | wg.Wait() 36 | return nil 37 | } 38 | 39 | func connectMysqlOne(c MysqlDBConfig) error { 40 | multiStatements := "false" 41 | if c.MultiStatements { 42 | multiStatements = "true" 43 | } 44 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?multiStatements=%s&charset=%s&parseTime=True&loc=Local&timeout=%ds&allowAllFiles=true", 45 | c.User, c.Password, c.Host, c.Port, c.DB, multiStatements, c.Charset, c.Timeout) 46 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ 47 | FullSaveAssociations: true, 48 | SkipDefaultTransaction: true, 49 | NamingStrategy: schema.NamingStrategy{SingularTable: true}, 50 | }) 51 | 52 | if c.Debug { 53 | db = db.Debug() 54 | } 55 | 56 | if err != nil { 57 | return errors.New(fmt.Sprintf("failed to connect databse %s:%v", dsn, err)) 58 | } 59 | 60 | DBPools.Add(c.Key, db) 61 | return nil 62 | } 63 | 64 | // Close todo 关闭db连接 65 | func Close() error { 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /console/mods/game_db/db_sqlite.go: -------------------------------------------------------------------------------- 1 | package game_db 2 | 3 | import ( 4 | "console/mods/pathx" 5 | "errors" 6 | "fmt" 7 | "github.com/localhostjason/webserver/util" 8 | log "github.com/sirupsen/logrus" 9 | "gorm.io/driver/sqlite" 10 | "gorm.io/gorm" 11 | "gorm.io/gorm/schema" 12 | "os" 13 | "path/filepath" 14 | "sync" 15 | ) 16 | 17 | // ConnectWithSqliteConfig 连接,检验配置是否正确 18 | func ConnectWithSqliteConfig(cfgs []SqliteDBConfig) error { 19 | if len(cfgs) == 0 { 20 | return nil 21 | } 22 | 23 | var wg = &sync.WaitGroup{} 24 | 25 | for _, c := range cfgs { 26 | wg.Add(1) 27 | 28 | go func(c SqliteDBConfig) { 29 | defer wg.Done() 30 | err := connectSqliteOne(c) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | }(c) 35 | 36 | } 37 | 38 | wg.Wait() 39 | return nil 40 | } 41 | 42 | func connectSqliteOne(c SqliteDBConfig) error { 43 | var dbFile string 44 | if filepath.IsAbs(c.DbFile) { 45 | dbFile = c.DbFile 46 | } else { 47 | exePath, _ := pathx.GetExeDir() 48 | dbFile = filepath.Join(exePath, c.DbFile) 49 | } 50 | 51 | path, _ := filepath.Split(dbFile) 52 | if !util.PathExists(path) { 53 | _ = os.MkdirAll(path, os.ModePerm) 54 | } 55 | 56 | db, err := gorm.Open(sqlite.Open(dbFile), &gorm.Config{ 57 | FullSaveAssociations: true, 58 | SkipDefaultTransaction: true, 59 | NamingStrategy: schema.NamingStrategy{SingularTable: true}, 60 | }) 61 | if err != nil { 62 | return errors.New(fmt.Sprintf("db file:%s, err:%s", dbFile, err)) 63 | } 64 | 65 | if c.Debug { 66 | db = db.Debug() 67 | } 68 | 69 | if err != nil { 70 | return errors.New(fmt.Sprintf("failed to connect databse %s:%v", c.DbFile, err)) 71 | } 72 | DBPools.Add(c.Key, db) 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /console/mods/game_db/migrate.go: -------------------------------------------------------------------------------- 1 | package game_db 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/sirupsen/logrus" 7 | ) 8 | 9 | func SyncDB() (err error) { 10 | if !DBEnable() { 11 | logrus.Info("no enable sql") 12 | return 13 | } 14 | 15 | err = Connect() 16 | if err != nil { 17 | return errors.New(fmt.Sprintf("failed to migrate:%v", err)) 18 | } 19 | err = Migrate() 20 | if err != nil { 21 | return 22 | } 23 | 24 | err = InitData() 25 | return 26 | } 27 | 28 | func AutoMigrate() (err error) { 29 | if !DBEnable() { 30 | return 31 | } 32 | err = Connect() 33 | if err != nil { 34 | return errors.New(fmt.Sprintf("failed to migrate:%v", err)) 35 | } 36 | return Migrate() 37 | } 38 | -------------------------------------------------------------------------------- /console/mods/game_db/model.go: -------------------------------------------------------------------------------- 1 | package game_db 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "sync" 6 | ) 7 | 8 | // DBPool 定义一个类,继承字典(异步,带锁的),一会存入db对象 { key : db } 9 | type DBPool struct { 10 | sync.Map 11 | } 12 | 13 | // Get 为这个 类<对象池> 添加方法,分别为 Get,Add,Del 14 | func (p *DBPool) Get(name string) *gorm.DB { 15 | if s, ok := p.Load(name); ok { 16 | return s.(*gorm.DB) 17 | } else { 18 | return nil 19 | } 20 | } 21 | 22 | func (p *DBPool) Add(name string, tx *gorm.DB) { 23 | p.Store(name, tx) 24 | } 25 | 26 | func (p *DBPool) Del(name string) { 27 | p.Delete(name) 28 | } 29 | -------------------------------------------------------------------------------- /console/mods/ginx/table.go: -------------------------------------------------------------------------------- 1 | package ginx 2 | 3 | import "github.com/localhostjason/webserver/db" 4 | 5 | type Authz struct { 6 | ID int `json:"id"` 7 | GroupName string `json:"group_name"` 8 | ApiName string `json:"api_name"` 9 | Url string `json:"url" description:"对象 obj"` 10 | Method string `json:"method" description:"act"` 11 | } 12 | 13 | func init() { 14 | db.RegTables(&Authz{}) 15 | } 16 | -------------------------------------------------------------------------------- /console/mods/pathx/path.go: -------------------------------------------------------------------------------- 1 | package pathx 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "path/filepath" 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | func GetExeDir() (string, error) { 12 | exePath, err := os.Executable() 13 | if err != nil { 14 | return "", err 15 | } 16 | res := filepath.Dir(exePath) 17 | 18 | if strings.Contains(exePath, getTmpDir()) { 19 | // run 模式下,确在当前程序入口目录 20 | return os.Getwd() 21 | } 22 | return res, nil 23 | } 24 | 25 | func getTmpDir() string { 26 | dir := os.Getenv("TEMP") 27 | if dir == "" { 28 | dir = os.Getenv("TMP") 29 | } 30 | return dir 31 | } 32 | 33 | // 获取当前执行文件绝对路径(go run) 34 | func getCurrentAbPathByCaller() string { 35 | var abPath string 36 | _, filename, _, ok := runtime.Caller(0) 37 | if ok { 38 | abPath = path.Dir(filename) 39 | } 40 | return abPath 41 | } 42 | -------------------------------------------------------------------------------- /console/mods/readme.md: -------------------------------------------------------------------------------- 1 | # 插件/库 2 | 3 | 1. 底层函数 4 | 2. 引用外部组件或者库 5 | 6 | 7 | -------------------------------------------------------------------------------- /console/source/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAxCN29bJSzKc1CMX0LUFBtvH12BohqucOLKzwx0olNAVdbNNR 3 | H8p2RAfvrV4QVh2KvxdrUmROVX1L+2sOl0H6zjR2UlnLeEEwls/rK1Al4saorzbU 4 | 5yCsg8zlEK6f+krgTGbEbwbsWv/i7UQTD3nAbZdK4eORpOuiMejupjnH4OE8rsMc 5 | msp1H93j56xOf/63ow7I4CcbGBFVAXOgDaao/8YSWZbyfMNqzuxS9EzHQfuIduEl 6 | e6oX8jOPvNP/1D5lgE4ucrv4pmJiVGuxfuOsI5EfYw0YPryehH+PasGzXvI61ysm 7 | E6f0TSCrKsmL9TjAccFjBsd0fH9oXzP80yIpjwIDAQABAoIBAB31NMROWms71tPi 8 | OGt5HiptRpmdVCsgY3/bYmNuJcSOVTi8BhYO/IVjmO4oAeLFXF3Xm+LXw0c3fhWG 9 | wpHD2CUKyk/Fu1hAzMUcONVCxqaepUEt5NLwoKj48LvdkD3QlfXVdIdozU0Q7H5E 10 | +YxRklyq0RszgRlpjDqHU3w8MxVbzUYhtPd58WdY6j0S+Auhkju7key+MHTPqzRK 11 | ZCxrEkL6nMl9R6GXmVWGZFw5O00usnrTsDVETd1pw6kezeqPLC1Dgz38zBBAuusv 12 | hbnrq0haS/jQmlbYMBtikJDtsY0TcLt1bBdq9q/HHbkiY3lyJbePw613+/lvaFvT 13 | oHtbLIECgYEA2PLaTQjSGXGTdhYUQnaM4I43WsUQlrUYHU5fwVsVDJEacfOBJrLp 14 | nySvI9Z3GasJALQHXftYtvWdGJItwjRNX4lLkatVu/MGeggck36xEdeAMOFjMIsX 15 | 9J/HA8mX9Qf5DFTkdomJxwGXHz1ichdz4Ha8L0wTOJl/7inwt9FIBO8CgYEA53Gq 16 | 12q3OR9SUQWvCQ4O0cecniBnjrFuwdqGEswpm6nt+Pxo73fT48dMbeldzB1q8X97 17 | vcIvO88GC3nDxztAscNBaQdvzPb1HpG07mU7a7MxZyNhtFzHa6f9cbXE98+13+vX 18 | 6Ka79+2H3xbHgEVAukqR7ph+faC5j+otN216ZWECgYEAirqhBenCECsklLqBsg6E 19 | /4NxukWR2g1rojHf4ZEQ4LxZIM0JpWl7Ix0eMdiOyIcqdpyoqVx02motTu0K/cjc 20 | QV9WR4w8grdhSN+vBlJZgINBogA+oWgQpYkWhkF/Pl0e0NfoUDSbOfq2XG+waCy/ 21 | GKZUqBoJoIPn3rBLEgBuAVkCgYA0XXd1/eMuvXN2eX684b1goXVCIbrMgkC5A6RK 22 | JH9VhQEe0J09cTMz2ifkxmQ+hnln4pwwtvrQ4WpQtmAhd7qPfcoS5WfaTBXxsK3r 23 | MmRbXdPTriGxGTzVktC5JZNHLmvMGiRjszXcRpXzV8gTsZePjset4DreWbsiYK2S 24 | WObUgQKBgGPHucZ8cNNRCadyfnHPTJqvychAbSQKzy3BUSNrXvGWpgKs38Zs0SRJ 25 | tnYjxByuzwFLhx9kmmAdBoR6aS6JLQOUsTXvrWNvrNX5CFHLO2F/iCFFd0SWIXvz 26 | JWlkbaakjNZX16xEile5xXjc2lJsmajwkykG6fm+yvjVHQugyYOs 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /console/source/publickey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCN29bJSzKc1CMX0LUFB 3 | tvH12BohqucOLKzwx0olNAVdbNNRH8p2RAfvrV4QVh2KvxdrUmROVX1L+2sOl0H6 4 | zjR2UlnLeEEwls/rK1Al4saorzbU5yCsg8zlEK6f+krgTGbEbwbsWv/i7UQTD3nA 5 | bZdK4eORpOuiMejupjnH4OE8rsMcmsp1H93j56xOf/63ow7I4CcbGBFVAXOgDaao 6 | /8YSWZbyfMNqzuxS9EzHQfuIduEle6oX8jOPvNP/1D5lgE4ucrv4pmJiVGuxfuOs 7 | I5EfYw0YPryehH+PasGzXvI61ysmE6f0TSCrKsmL9TjAccFjBsd0fH9oXzP80yIp 8 | jwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /dist/.gitignore: -------------------------------------------------------------------------------- 1 | /org 2 | /.vscode 3 | 4 | 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | !.vscode/*.code-snippets 11 | 12 | # Local History for Visual Studio Code 13 | .history/ 14 | 15 | # Built Visual Studio Code Extensions 16 | *.vsix 17 | .idea 18 | 19 | data 20 | log 21 | -------------------------------------------------------------------------------- /dist/config/rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub,obj,act 3 | 4 | [policy_definition] 5 | p = sub,obj,act 6 | 7 | [role_definition] 8 | g = _, _ 9 | 10 | [policy_effect] 11 | e = some(where (p.eft == allow)) 12 | 13 | [matchers] 14 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act 15 | -------------------------------------------------------------------------------- /dist/main.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/dist/main.exe -------------------------------------------------------------------------------- /dist/source/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAxCN29bJSzKc1CMX0LUFBtvH12BohqucOLKzwx0olNAVdbNNR 3 | H8p2RAfvrV4QVh2KvxdrUmROVX1L+2sOl0H6zjR2UlnLeEEwls/rK1Al4saorzbU 4 | 5yCsg8zlEK6f+krgTGbEbwbsWv/i7UQTD3nAbZdK4eORpOuiMejupjnH4OE8rsMc 5 | msp1H93j56xOf/63ow7I4CcbGBFVAXOgDaao/8YSWZbyfMNqzuxS9EzHQfuIduEl 6 | e6oX8jOPvNP/1D5lgE4ucrv4pmJiVGuxfuOsI5EfYw0YPryehH+PasGzXvI61ysm 7 | E6f0TSCrKsmL9TjAccFjBsd0fH9oXzP80yIpjwIDAQABAoIBAB31NMROWms71tPi 8 | OGt5HiptRpmdVCsgY3/bYmNuJcSOVTi8BhYO/IVjmO4oAeLFXF3Xm+LXw0c3fhWG 9 | wpHD2CUKyk/Fu1hAzMUcONVCxqaepUEt5NLwoKj48LvdkD3QlfXVdIdozU0Q7H5E 10 | +YxRklyq0RszgRlpjDqHU3w8MxVbzUYhtPd58WdY6j0S+Auhkju7key+MHTPqzRK 11 | ZCxrEkL6nMl9R6GXmVWGZFw5O00usnrTsDVETd1pw6kezeqPLC1Dgz38zBBAuusv 12 | hbnrq0haS/jQmlbYMBtikJDtsY0TcLt1bBdq9q/HHbkiY3lyJbePw613+/lvaFvT 13 | oHtbLIECgYEA2PLaTQjSGXGTdhYUQnaM4I43WsUQlrUYHU5fwVsVDJEacfOBJrLp 14 | nySvI9Z3GasJALQHXftYtvWdGJItwjRNX4lLkatVu/MGeggck36xEdeAMOFjMIsX 15 | 9J/HA8mX9Qf5DFTkdomJxwGXHz1ichdz4Ha8L0wTOJl/7inwt9FIBO8CgYEA53Gq 16 | 12q3OR9SUQWvCQ4O0cecniBnjrFuwdqGEswpm6nt+Pxo73fT48dMbeldzB1q8X97 17 | vcIvO88GC3nDxztAscNBaQdvzPb1HpG07mU7a7MxZyNhtFzHa6f9cbXE98+13+vX 18 | 6Ka79+2H3xbHgEVAukqR7ph+faC5j+otN216ZWECgYEAirqhBenCECsklLqBsg6E 19 | /4NxukWR2g1rojHf4ZEQ4LxZIM0JpWl7Ix0eMdiOyIcqdpyoqVx02motTu0K/cjc 20 | QV9WR4w8grdhSN+vBlJZgINBogA+oWgQpYkWhkF/Pl0e0NfoUDSbOfq2XG+waCy/ 21 | GKZUqBoJoIPn3rBLEgBuAVkCgYA0XXd1/eMuvXN2eX684b1goXVCIbrMgkC5A6RK 22 | JH9VhQEe0J09cTMz2ifkxmQ+hnln4pwwtvrQ4WpQtmAhd7qPfcoS5WfaTBXxsK3r 23 | MmRbXdPTriGxGTzVktC5JZNHLmvMGiRjszXcRpXzV8gTsZePjset4DreWbsiYK2S 24 | WObUgQKBgGPHucZ8cNNRCadyfnHPTJqvychAbSQKzy3BUSNrXvGWpgKs38Zs0SRJ 25 | tnYjxByuzwFLhx9kmmAdBoR6aS6JLQOUsTXvrWNvrNX5CFHLO2F/iCFFd0SWIXvz 26 | JWlkbaakjNZX16xEile5xXjc2lJsmajwkykG6fm+yvjVHQugyYOs 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /dist/source/publickey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCN29bJSzKc1CMX0LUFB 3 | tvH12BohqucOLKzwx0olNAVdbNNRH8p2RAfvrV4QVh2KvxdrUmROVX1L+2sOl0H6 4 | zjR2UlnLeEEwls/rK1Al4saorzbU5yCsg8zlEK6f+krgTGbEbwbsWv/i7UQTD3nA 5 | bZdK4eORpOuiMejupjnH4OE8rsMcmsp1H93j56xOf/63ow7I4CcbGBFVAXOgDaao 6 | /8YSWZbyfMNqzuxS9EzHQfuIduEle6oX8jOPvNP/1D5lgE4ucrv4pmJiVGuxfuOs 7 | I5EfYw0YPryehH+PasGzXvI61ysmE6f0TSCrKsmL9TjAccFjBsd0fH9oXzP80yIp 8 | jwIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /dist/web/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/dist/web/static/favicon.ico -------------------------------------------------------------------------------- /dist/web/static/index.html: -------------------------------------------------------------------------------- 1 | vue3-template
-------------------------------------------------------------------------------- /dist/web/static/lib/css/141.95778f3a.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%}.category-index[data-v-10870d64]{position:absolute;padding-top:2px;text-align:center;color:#fff;font-size:14px;left:0;top:6px;width:20px;height:20px;background:#4385f5;border-radius:50px}.category-name[data-v-10870d64]{position:absolute;left:26px;font-size:14px;top:1px;background:#fff;height:28px;padding:8px 8px;color:#303133;font-weight:600}.tc-step-border{border-left:1px dashed #d8dce5;margin-left:10px}.tc-step-border .l{position:relative;left:-13px}.tc-step-border .last-step~div{background:#fff;margin-top:-25px}.tc-step-border .last-div{padding:20px 0 0 0}.tc-step-border .bline-dotted{border-bottom:0!important} -------------------------------------------------------------------------------- /dist/web/static/lib/css/190.8bdff399.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%} -------------------------------------------------------------------------------- /dist/web/static/lib/css/220.95778f3a.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%}.category-index[data-v-10870d64]{position:absolute;padding-top:2px;text-align:center;color:#fff;font-size:14px;left:0;top:6px;width:20px;height:20px;background:#4385f5;border-radius:50px}.category-name[data-v-10870d64]{position:absolute;left:26px;font-size:14px;top:1px;background:#fff;height:28px;padding:8px 8px;color:#303133;font-weight:600}.tc-step-border{border-left:1px dashed #d8dce5;margin-left:10px}.tc-step-border .l{position:relative;left:-13px}.tc-step-border .last-step~div{background:#fff;margin-top:-25px}.tc-step-border .last-div{padding:20px 0 0 0}.tc-step-border .bline-dotted{border-bottom:0!important} -------------------------------------------------------------------------------- /dist/web/static/lib/css/228.bd54ec38.css: -------------------------------------------------------------------------------- 1 | .errPage-container[data-v-0eb2854e]{width:800px;max-width:100%;margin:100px auto}.errPage-container .pan-back-btn[data-v-0eb2854e]{background:#008489;color:#fff;border:none!important}.errPage-container .pan-gif[data-v-0eb2854e]{margin:0 auto;display:block}.errPage-container .pan-img[data-v-0eb2854e]{display:block;margin:0 auto;width:100%}.errPage-container .text-jumbo[data-v-0eb2854e]{font-size:60px;font-weight:700;color:#484848}.errPage-container .list-unstyled[data-v-0eb2854e]{font-size:14px}.errPage-container .list-unstyled li[data-v-0eb2854e]{padding-bottom:5px}.errPage-container .list-unstyled a[data-v-0eb2854e]{color:#008489;text-decoration:none}.errPage-container .list-unstyled a[data-v-0eb2854e]:hover{text-decoration:underline} -------------------------------------------------------------------------------- /dist/web/static/lib/css/23.c722803a.css: -------------------------------------------------------------------------------- 1 | @supports(-webkit-mask:none) and (not (cater-color:#909399)){.login-container .el-input input[data-v-45d28066]{color:#909399}}.login-container .el-input input[data-v-45d28066]{border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 6px;color:#000;height:40px;caret-color:#909399;font-size:14px}.login-container .el-input input[data-v-45d28066]::-webkit-input-placeholder{color:#bbbcbf}.login-container .el-form-item[data-v-45d28066]{margin-bottom:28px}.login-container .el-form-item__error[data-v-45d28066]{padding-top:7px;left:7px}.login-container .login-form[data-v-45d28066]{margin:80px auto;padding-right:60px}.login-container .title-container[data-v-45d28066]{position:relative}.login-container .title-container .title[data-v-45d28066]{font-size:28px;color:#2d3036;margin:0 auto 65px auto;text-align:center;font-weight:400}.login-container .title-container .set-language[data-v-45d28066]{color:#fff;position:absolute;top:5px;right:0}.login-container .show-pwd[data-v-45d28066]{position:absolute;right:10px;top:0;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.login-logo[data-v-45d28066]{height:80px;padding:20px}.login-content[data-v-45d28066]{position:relative;top:25%;height:420px;width:900px;border-radius:8px;background-color:#fff;margin:0 auto}.login-content-long-title[data-v-45d28066]{position:relative;top:20%;height:474px;width:1170px;border-radius:8px;background-color:#fff;margin:0 auto}.login-btn[data-v-45d28066]{width:100%;margin-top:32px;font-size:18px;border-radius:2px;height:48px;background-color:#0078fc}.container[data-v-45d28066]{background-color:#f2f3f5;height:100%;width:100%}.svg-container[data-v-45d28066]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.svg-container .svg-icon[data-v-45d28066]{font-size:20px} -------------------------------------------------------------------------------- /dist/web/static/lib/css/504.95778f3a.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%}.category-index[data-v-10870d64]{position:absolute;padding-top:2px;text-align:center;color:#fff;font-size:14px;left:0;top:6px;width:20px;height:20px;background:#4385f5;border-radius:50px}.category-name[data-v-10870d64]{position:absolute;left:26px;font-size:14px;top:1px;background:#fff;height:28px;padding:8px 8px;color:#303133;font-weight:600}.tc-step-border{border-left:1px dashed #d8dce5;margin-left:10px}.tc-step-border .l{position:relative;left:-13px}.tc-step-border .last-step~div{background:#fff;margin-top:-25px}.tc-step-border .last-div{padding:20px 0 0 0}.tc-step-border .bline-dotted{border-bottom:0!important} -------------------------------------------------------------------------------- /dist/web/static/lib/css/582.8bdff399.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%} -------------------------------------------------------------------------------- /dist/web/static/lib/css/693.8bdff399.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%} -------------------------------------------------------------------------------- /dist/web/static/lib/css/902.2ad5574d.css: -------------------------------------------------------------------------------- 1 | .dash-header .el-card[data-v-69685f71]{position:relative}.dash-header p[data-v-69685f71]{font-size:28px}.dash-header .right-icon[data-v-69685f71]{position:absolute;top:53%;right:25px;width:60px;height:60px;line-height:60px;color:var(--el-color-primary);text-align:center;background:var(--el-color-primary-light-9);border-radius:50%;transform:translateY(-50%)}.dash-header .right-icon .svg-icon[data-v-69685f71]{font-size:28px}.dash-header .right-icon .svg-icon.rela[data-v-69685f71]{position:relative;left:3px;top:4px}.dash-body[data-v-1a4f5929]{margin-top:18px}.dash-body .card-header[data-v-1a4f5929]{font-size:14px}.dash-body[data-v-1a4f5929] .el-card__body{padding:0!important}.dash-body .dash-table[data-v-1a4f5929]{padding:20px}.dash-foot[data-v-2118e9f0]{margin-top:18px}.dashboard-container[data-v-379039c0]{padding:0;background:#f6f8f9;height:100%;margin:0 -15px}.dashboard-container .dash-panel[data-v-379039c0]{padding:20px} -------------------------------------------------------------------------------- /dist/web/static/lib/css/929.8bdff399.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%} -------------------------------------------------------------------------------- /dist/web/static/lib/css/953.8bdff399.css: -------------------------------------------------------------------------------- 1 | .console-title[data-v-221fb25f]:not(.bline){margin-bottom:-10px}.console-title[data-v-221fb25f]{padding:16px 0;min-height:48px}.pull-left[data-v-221fb25f]{float:left}.console-title h1[data-v-221fb25f],.console-title h2[data-v-221fb25f],.console-title h3[data-v-221fb25f],.console-title h4[data-v-221fb25f],.console-title h5[data-v-221fb25f],.console-title h6[data-v-221fb25f]{display:inline-block;text-indent:8px;border-left:2px solid #39f;margin-top:0;margin-bottom:0;margin-right:8px;vertical-align:top;font-weight:700;color:#555;font-size:18px}.icon-toinstlist[data-v-221fb25f]{width:12px;height:12px;display:inline-block;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAZElEQVQYGWNmQAIpKSkNBgYG9ufPnz8IE2aGMZKSkuqZmZkbmJiYHPT19RlgisAKQJIsLCwNMMXIihiTk5PrgDobYZLI9N+/f+sZgQIgTBxIS0v7D8LIqpmQOdjYKMaj6wZpAACPBx5UHxJMzwAAAABJRU5ErkJggg==) center 1px no-repeat}.btn-small[data-v-221fb25f]{height:21px}.bline[data-v-221fb25f]{border-bottom:1px solid #eee;width:100%} -------------------------------------------------------------------------------- /dist/web/static/lib/img/401.1800ba9e.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/dist/web/static/lib/img/401.1800ba9e.gif -------------------------------------------------------------------------------- /dist/web/static/lib/img/404.458c248a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/dist/web/static/lib/img/404.458c248a.png -------------------------------------------------------------------------------- /dist/web/static/lib/img/logo2.7bca92d9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/dist/web/static/lib/img/logo2.7bca92d9.png -------------------------------------------------------------------------------- /dist/web/static/lib/js/228.5c599d35.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkvue3_template"]=self["webpackChunkvue3_template"]||[]).push([[228],{6523:function(e,t,a){a.r(t),a.d(t,{default:function(){return v}});var l=a(3396),r=a(9242);const n=e=>((0,l.dD)("data-v-0eb2854e"),e=e(),(0,l.Cn)(),e),i={class:"errPage-container"},s=n((()=>(0,l._)("h1",{class:"text-jumbo text-ginormous"},"Oops!",-1))),u=n((()=>(0,l._)("a",{href:"https://zh.airbnb.com/",target:"_blank"},"airbnb",-1))),o=n((()=>(0,l._)("h2",null,"你没有权限去该页面",-1))),c=n((()=>(0,l._)("h6",null,"如有不满请联系你领导",-1))),d={class:"list-unstyled"},h=n((()=>(0,l._)("li",null,"或者你可以去:",-1))),p={class:"link-type"},b=n((()=>(0,l._)("li",{class:"link-type"},[(0,l._)("a",{href:"https://www.taobao.com/"},"随便看看")],-1))),_=["src"];function f(e,t,a,n,f,k){const m=(0,l.up)("el-button"),w=(0,l.up)("router-link"),g=(0,l.up)("el-col"),v=(0,l.up)("el-row");return(0,l.wg)(),(0,l.iD)("div",i,[(0,l.Wm)(m,{icon:"arrow-left",class:"pan-back-btn",onClick:k.back},{default:(0,l.w5)((()=>[(0,l.Uk)("返回")])),_:1},8,["onClick"]),(0,l.Wm)(v,null,{default:(0,l.w5)((()=>[(0,l.Wm)(g,{span:12},{default:(0,l.w5)((()=>[s,(0,l.Uk)(" gif来源"),u,(0,l.Uk)(" 页面 "),o,c,(0,l._)("ul",d,[h,(0,l._)("li",p,[(0,l.Wm)(w,{to:"/dashboard"},{default:(0,l.w5)((()=>[(0,l.Uk)("回首页")])),_:1})]),b,(0,l._)("li",null,[(0,l._)("a",{href:"#",onClick:t[0]||(t[0]=(0,r.iM)((e=>f.dialogVisible=!0),["prevent"]))},"点我看图")])])])),_:1}),(0,l.Wm)(g,{span:12},{default:(0,l.w5)((()=>[(0,l._)("img",{src:f.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."},null,8,_)])),_:1})])),_:1})])}a(7658);var k=a.p+"lib/img/401.1800ba9e.gif",m={name:"Page401",data(){return{errGif:k+"?"+ +new Date,dialogVisible:!1}},methods:{back(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},w=a(89);const g=(0,w.Z)(m,[["render",f],["__scopeId","data-v-0eb2854e"]]);var v=g}}]); -------------------------------------------------------------------------------- /dist/web/static/lib/js/390.a3a879bf.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkvue3_template"]=self["webpackChunkvue3_template"]||[]).push([[390],{8390:function(e,t,r){r.r(t),r.d(t,{default:function(){return s}});var a=r(3396),n=r(4870),p=r(2483),u=(0,a.aZ)({__name:"index",setup(e){const{currentRoute:t,replace:r}=(0,p.tv)(),{params:u,query:c}=(0,n.SU)(t),{path:s,_redirect_type:i="path"}=u;Reflect.deleteProperty(u,"_redirect_type"),Reflect.deleteProperty(u,"path");const l=Array.isArray(s)?s.join("/"):s;return r("name"===i?{name:l,query:c,params:u}:{path:l.startsWith("/")?l:"/"+l,query:c}),(e,t)=>((0,a.wg)(),(0,a.iD)("div"))}});const c=u;var s=c}}]); -------------------------------------------------------------------------------- /dist/web/static/lib/js/929.5b9f21b5.js: -------------------------------------------------------------------------------- 1 | "use strict";(self["webpackChunkvue3_template"]=self["webpackChunkvue3_template"]||[]).push([[929],{2507:function(t,e,n){n.d(e,{Z:function(){return v}});var l=n(7477),s=(n(7658),n(3396)),i=n(7139),o=n(4274),u=n(2483);const a=t=>((0,s.dD)("data-v-221fb25f"),t=t(),(0,s.Cn)(),t),r={class:"pull-left"},c=a((()=>(0,s._)("span",{class:"icon-toinstlist"},null,-1)));var f=(0,s.aZ)({__name:"PanelTitle",props:{isLine:o.i.bool.def(!1),title:o.i.string.def("标题"),router:Object,backTitle:o.i.string.def("")},setup(t){const e=t,n=(0,u.tv)(),l=()=>{e.router?n.push({...e.router}):n.go(-1)};return(e,n)=>{const o=(0,s.up)("el-button"),u=(0,s.up)("el-row");return(0,s.wg)(),(0,s.j4)(u,null,{default:(0,s.w5)((()=>[(0,s._)("div",{class:(0,i.C_)(["console-title clearfix",t.isLine?"bline":""]),style:{"min-height":"50px"}},[(0,s._)("div",r,[(0,s._)("h4",null,(0,i.zw)(t.title),1),Boolean(t.backTitle)?((0,s.wg)(),(0,s.j4)(o,{key:0,size:"small",class:"btn-small",onClick:l},{default:(0,s.w5)((()=>[c,(0,s._)("span",null,(0,i.zw)(t.backTitle),1)])),_:1})):(0,s.kq)("",!0),(0,s.WI)(e.$slots,"button")])],2)])),_:3})}}}),p=n(89);const d=(0,p.Z)(f,[["__scopeId","data-v-221fb25f"]]);var _=d;const b=(0,l.nz)(_);var v=b},7477:function(t,e,n){n.d(e,{nz:function(){return l}});const l=(t,e)=>{if(t.install=n=>{for(const l of[t,...Object.values(e??{})])n.component(l.name,l)},e)for(const[n,l]of Object.entries(e))t[n]=l;return t}},9929:function(t,e,n){n.r(e),n.d(e,{default:function(){return a}});var l=n(3396),s=n(4870),i=n(2507),o=(0,l.aZ)({__name:"test",setup(t){return(t,e)=>((0,l.wg)(),(0,l.iD)("div",null,[(0,l.Wm)((0,s.SU)(i.Z),{title:"测试"})]))}});const u=o;var a=u}}]); -------------------------------------------------------------------------------- /web/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /web/.env.development: -------------------------------------------------------------------------------- 1 | ENV = 'development' 2 | 3 | VUE_APP_BASE_API = 'http://127.0.0.1:8088/api' 4 | 5 | # 详细查看 webpack -------------------------------------------------------------------------------- /web/.env.production: -------------------------------------------------------------------------------- 1 | ENV = 'production' 2 | 3 | VUE_APP_BASE_API = '/api' 4 | 5 | # 详细查看 webpack -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | public 2 | dist 3 | src 4 | -------------------------------------------------------------------------------- /web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /web/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "printWidth": 120, 4 | "singleQuote": true, 5 | "tabWidth": 2, 6 | "trailingComma": "none", 7 | "arrowParens": "avoid" 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # WEB 2 | 3 | https://bbs.aladedalu.com/thread-3143-1-1.html -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@element-plus/icons-vue": "^2.0.9", 12 | "axios": "^0.27.2", 13 | "core-js": "^3.8.3", 14 | "echarts": "^5.4.1", 15 | "element-plus": "^2.2.28", 16 | "file-saver": "^2.0.5", 17 | "fuse.js": "^6.6.2", 18 | "lodash": "^4.17.21", 19 | "moment": "^2.29.4", 20 | "normalize.css": "^8.0.1", 21 | "nprogress": "^0.2.0", 22 | "path-browserify": "^1.0.1", 23 | "pinia": "^2.0.29", 24 | "screenfull": "^6.0.2", 25 | "svg-sprite-loader": "^6.0.11", 26 | "vue": "^3.2.13", 27 | "vue-class-component": "^8.0.0-0", 28 | "vue-router": "^4.1.5", 29 | "vue-types": "^4.2.1" 30 | }, 31 | "devDependencies": { 32 | "@types/file-saver": "^2.0.5", 33 | "@typescript-eslint/eslint-plugin": "^5.4.0", 34 | "@typescript-eslint/parser": "^5.4.0", 35 | "@vue/cli-plugin-babel": "~5.0.0", 36 | "@vue/cli-plugin-eslint": "~5.0.0", 37 | "@vue/cli-plugin-typescript": "~5.0.0", 38 | "@vue/cli-service": "~5.0.0", 39 | "@vue/eslint-config-typescript": "^9.1.0", 40 | "eslint": "^7.32.0", 41 | "eslint-plugin-vue": "^8.0.3", 42 | "sass": "^1.32.7", 43 | "sass-loader": "^12.0.0", 44 | "typescript": "~4.5.5" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /web/src/api/client/login.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { ClientLoginForm } from '@/views/client/login/models/login' 3 | 4 | export const loginClient = (data: ClientLoginForm): Promise => { 5 | return http.request({ 6 | url: `/client/login`, 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /web/src/api/dash/index.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | 3 | export const getDashTotal = (): Promise => { 4 | return http.request({ 5 | url: `/dash/stat/count`, 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export const getDashTop5 = (): Promise => { 11 | return http.request({ 12 | url: `/dash/stat/top5`, 13 | method: 'get' 14 | }) 15 | } 16 | 17 | export const getDashChart = (): Promise => { 18 | return http.request({ 19 | url: `/dash/stat/chart`, 20 | method: 'get' 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /web/src/api/gm/accounts.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { Account, AccountDetail, RechargeForm } from '@/views/gm/account/model/model' 3 | import { ChangePasswordForm, CreateAccountForm, EditAccountForm } from '@/views/gm/account/model/info' 4 | 5 | export const getAccounts = (params: any): Promise => { 6 | return http.request({ 7 | url: `/gm/account/list`, 8 | method: 'get', 9 | params 10 | }) 11 | } 12 | 13 | export const rechargeByUid = (uid: number, data: RechargeForm): Promise => { 14 | return http.request({ 15 | url: `/gm/account/${uid}/recharge`, 16 | method: 'post', 17 | data 18 | }) 19 | } 20 | 21 | export const resetCreateCharac = (uid: number): Promise => { 22 | return http.request({ 23 | url: `/gm/account/${uid}/reset_create_charac`, 24 | method: 'post' 25 | }) 26 | } 27 | 28 | export const deleteAccount = (uid: number): Promise => { 29 | return http.request({ 30 | url: `/gm/account/${uid}`, 31 | method: 'delete' 32 | }) 33 | } 34 | 35 | export const updateAccountPasswordByUid = (uid: number, data: ChangePasswordForm): Promise => { 36 | return http.request({ 37 | url: `/gm/account/${uid}/change_password`, 38 | method: 'post', 39 | data 40 | }) 41 | } 42 | 43 | export const updateAccountInfo = (uid: number, data: EditAccountForm): Promise => { 44 | return http.request({ 45 | url: `/gm/account/${uid}`, 46 | method: 'put', 47 | data 48 | }) 49 | } 50 | 51 | export const createAccountInfo = (data: CreateAccountForm): Promise => { 52 | return http.request({ 53 | url: `/gm/account`, 54 | method: 'post', 55 | data 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /web/src/api/gm/auction.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { AuctionState } from '@/views/gm/auction/model' 3 | 4 | export const getAuctionState = (): Promise => { 5 | return http.request({ 6 | url: `/gm/auction/state`, 7 | method: 'get' 8 | }) 9 | } 10 | 11 | export const openAuction = (name: string): Promise => { 12 | return http.request({ 13 | url: `/gm/auction/${name}/open`, 14 | method: 'post' 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /web/src/api/gm/email.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | 3 | export const sendEmailByRole = (characNo: number, data: any): Promise => { 4 | return http.request({ 5 | url: `/gm/roles/${characNo}/email`, 6 | method: 'post', 7 | data 8 | }) 9 | } 10 | 11 | export const getGolds = (params: object): Promise => { 12 | return http.request({ 13 | url: `/gm/gold/list`, 14 | method: 'get', 15 | params 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /web/src/api/gm/role.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { QpForm } from '@/views/gm/roles/model/qp' 3 | import { PvpForm } from '@/views/gm/roles/model/pvp' 4 | 5 | export const getRoles = (id: number): Promise => { 6 | return http.request({ 7 | url: `/gm/account/${id}/roles`, 8 | method: 'get' 9 | }) 10 | } 11 | 12 | export const updateQPbyRole = (characNo: number, data: QpForm): Promise => { 13 | return http.request({ 14 | url: `/gm/roles/${characNo}/qp`, 15 | method: 'put', 16 | data 17 | }) 18 | } 19 | 20 | export const updatePvpByRole = (characNo: number, data: PvpForm): Promise => { 21 | return http.request({ 22 | url: `/gm/roles/${characNo}/pvp`, 23 | method: 'put', 24 | data 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /web/src/api/gm/task.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { Task } from '@/views/gm/tasks/model' 3 | 4 | export const getTasks = (characNo: number): Promise => { 5 | return http.request({ 6 | url: `/gm/roles/${characNo}/tasks`, 7 | method: 'get' 8 | }) 9 | } 10 | 11 | export const updateTasks = (characNo: number, data: any): Promise => { 12 | return http.request({ 13 | url: `/gm/roles/${characNo}/tasks`, 14 | method: 'put', 15 | data 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /web/src/api/log/index.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | 3 | export const getRechargeLog = (params): Promise => { 4 | return http.request({ 5 | url: `/log/recharge`, 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export const getOperateLog = (params): Promise => { 12 | return http.request({ 13 | url: `/log/operate`, 14 | method: 'get', 15 | params 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /web/src/api/user/auth.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { EditUserInfo, Login, LoginResult, UserInfo } from '@/models/user/auth' 3 | 4 | export const login = (data: Login): Promise => { 5 | return http.request({ 6 | url: '/auth/login', 7 | method: 'post', 8 | data 9 | }) 10 | } 11 | 12 | export const logout = (): Promise => { 13 | return http.request({ 14 | url: '/auth/logout', 15 | method: 'post' 16 | }) 17 | } 18 | 19 | export const getUserInfo = (): Promise => { 20 | return http.request({ 21 | url: `/user/info`, 22 | method: 'get' 23 | }) 24 | } 25 | export const updateUserInfo = (data: EditUserInfo): Promise => { 26 | return http.request({ 27 | url: `/user/info`, 28 | method: 'put', 29 | data 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /web/src/api/user/users.ts: -------------------------------------------------------------------------------- 1 | import { http } from '@/utils/http' 2 | import { downloadHttp } from '@/utils/http/download' 3 | import { User } from '@/models/user/user' 4 | 5 | export const getUsers = (): Promise => { 6 | return http.request({ 7 | url: `/user/list`, 8 | method: 'get' 9 | }) 10 | } 11 | 12 | export const downloadFile = (): Promise => { 13 | return downloadHttp.request({ 14 | url: `/user/file`, 15 | method: 'get' 16 | }) 17 | } 18 | 19 | export const getUsersList = async (): Promise => { 20 | return [ 21 | { 22 | id: 1, 23 | username: 'admin', 24 | last_login_time: '2023-02-10T10:07:56.8351023+08:00', 25 | time: '2023-02-09T14:41:19.3067481+08:00', 26 | role: 'admin', 27 | email: '', 28 | desc: '系统管理员', 29 | is_super_admin: true 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /web/src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /web/src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/404_images/404.png -------------------------------------------------------------------------------- /web/src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /web/src/assets/cms-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/cms-icons.png -------------------------------------------------------------------------------- /web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/logo.png -------------------------------------------------------------------------------- /web/src/assets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/logo2.png -------------------------------------------------------------------------------- /web/src/assets/toinstlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/assets/toinstlist.png -------------------------------------------------------------------------------- /web/src/components/BreadCrumb/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from "vue" 2 | import breadCrumb from "./src/BreadCrumb.vue" 3 | 4 | export const BreadCrumb = Object.assign(breadCrumb, { 5 | install(app: App) { 6 | app.component(breadCrumb.name, breadCrumb) 7 | } 8 | }); 9 | 10 | export default BreadCrumb 11 | -------------------------------------------------------------------------------- /web/src/components/HamBurger/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from "vue" 2 | import hamBurger from "./src/HamBurger.vue" 3 | 4 | export const HamBurger = Object.assign(hamBurger, { 5 | install(app: App) { 6 | app.component(hamBurger.name, hamBurger) 7 | } 8 | }); 9 | 10 | export default HamBurger 11 | -------------------------------------------------------------------------------- /web/src/components/HamBurger/src/HamBurger.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | 39 | 51 | -------------------------------------------------------------------------------- /web/src/components/Layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 27 | 28 | 39 | -------------------------------------------------------------------------------- /web/src/components/Layout/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './Navbar.vue' 2 | export { default as Sidebar } from './sidebar/index.vue' 3 | export { default as TagsView } from './TagsView/TagsView.vue' 4 | export { default as AppMain } from './AppMain.vue' 5 | -------------------------------------------------------------------------------- /web/src/components/Layout/components/sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 31 | -------------------------------------------------------------------------------- /web/src/components/PanelStep/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '@/utils/vue' 2 | 3 | import panelStep from './src/PanelStep.vue' 4 | 5 | export const PanelStep = withInstall(panelStep) 6 | export default PanelStep 7 | -------------------------------------------------------------------------------- /web/src/components/PanelStep/src/PanelStep.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 42 | 43 | 70 | 71 | 97 | -------------------------------------------------------------------------------- /web/src/components/PanelTitle/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '@/utils/vue' 2 | 3 | import panelTitle from './src/PanelTitle.vue' 4 | 5 | export const PanelTitle = withInstall(panelTitle) 6 | export default PanelTitle 7 | -------------------------------------------------------------------------------- /web/src/components/Screenfull/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 56 | 57 | 67 | -------------------------------------------------------------------------------- /web/src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | 37 | 46 | -------------------------------------------------------------------------------- /web/src/icons/index.ts: -------------------------------------------------------------------------------- 1 | const requireAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().map(requireContext) 2 | const req = require.context('./svg', false, /\.svg$/) 3 | requireAll(req) 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/bug.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/icons/svg/close-eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/cloud-download.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/cog.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/dollar.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/envelope-closed.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/envelope-open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/icons/svg/expand-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/file.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/fork.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/fullscreen.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /web/src/icons/svg/grid-four-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/headphones.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/language.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web/src/icons/svg/layers.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/list-rich.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/open-eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/people.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/person.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/print.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/pulse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/src/icons/svg/shield.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/timer.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /web/src/icons/svg/transfer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/icons/svg/yen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /web/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | import router from './router' 5 | import store from './store' 6 | 7 | import ElementPlus from 'element-plus' 8 | import 'element-plus/dist/index.css' 9 | import locale from 'element-plus/lib/locale/lang/zh-cn' 10 | 11 | import * as ElIcon from '@element-plus/icons-vue' 12 | 13 | import SvgIcon from './components/SvgIcon/index.vue' 14 | import './icons' 15 | import { setupPermissionRouter } from './permission' 16 | 17 | // 导入公共样式 18 | import 'normalize.css/normalize.css' 19 | import './styles/index.scss' 20 | 21 | // init 22 | ;(async () => { 23 | const app = createApp(App) 24 | 25 | // load vue router 26 | app.use(router) 27 | 28 | // load pinia store 29 | app.use(store) 30 | 31 | // load element plus ui 32 | app.use(ElementPlus, { 33 | size: 'default', 34 | locale 35 | }) 36 | for (const iconName in ElIcon) { 37 | app.component(iconName, ElIcon[iconName]) 38 | } 39 | 40 | // load 路由守卫 41 | setupPermissionRouter(router) 42 | 43 | // load svg组件 44 | app.component('svg-icon', SvgIcon) 45 | 46 | app.mount('#app', true) 47 | })() 48 | -------------------------------------------------------------------------------- /web/src/models/page.ts: -------------------------------------------------------------------------------- 1 | export type PageQuery = { 2 | page: number 3 | per_page: number 4 | } 5 | -------------------------------------------------------------------------------- /web/src/models/user/auth.ts: -------------------------------------------------------------------------------- 1 | export type UserInfo = { 2 | id: number 3 | username: string 4 | time: string 5 | last_login_time: string 6 | email: string 7 | role: string 8 | desc: string 9 | } 10 | 11 | export type EditUserInfo = { 12 | desc: string 13 | } 14 | 15 | export type Login = { 16 | username: string 17 | password: string 18 | } 19 | 20 | export type LoginResult = { 21 | token: string 22 | username: string 23 | time: string 24 | last_login_time: string 25 | email: string 26 | role: string 27 | desc: string 28 | } 29 | -------------------------------------------------------------------------------- /web/src/models/user/user.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | id: number 3 | username: string 4 | time: string 5 | last_login_time: string | null 6 | email: string 7 | role: string 8 | desc: string 9 | is_super_admin: boolean 10 | } 11 | 12 | export type UserState = { 13 | data: User[] 14 | loading: boolean 15 | } 16 | 17 | export type ModifyUserForm = { 18 | username: string 19 | email: string 20 | desc: string 21 | password?: string 22 | checkPassword?: string 23 | } 24 | -------------------------------------------------------------------------------- /web/src/permission.ts: -------------------------------------------------------------------------------- 1 | import NProgress from '@/utils/progress' 2 | import router, { resetRouter } from './router' 3 | import { getUserInfo } from '@/api/user/auth' 4 | import { useUserStoreWithOut } from '@/store/modules/user' 5 | import { usePermissionStoreWithOut } from '@/store/modules/permission' 6 | import type { Router } from 'vue-router' 7 | import { getPageTitle } from '@/utils/get-page-title' 8 | 9 | const whiteList = ['/login'] 10 | const permissionStore = usePermissionStoreWithOut() 11 | const userStore = useUserStoreWithOut() 12 | 13 | export const setupPermissionRouter = (router: Router) => { 14 | router.beforeEach(async (to, _from, next) => { 15 | NProgress.start() 16 | 17 | // set page title 18 | document.title = getPageTitle(to.meta.title) 19 | 20 | const token = userStore.getToken 21 | const username = userStore.getUsername 22 | if (!token) { 23 | whiteList.includes(to.path) ? next() : next(`/login`) 24 | NProgress.done() 25 | return 26 | } 27 | 28 | if (to.path === '/login') { 29 | next({ path: '/' }) 30 | NProgress.done() 31 | return 32 | } 33 | 34 | if (username) { 35 | next() 36 | return 37 | } 38 | 39 | try { 40 | const { username, role } = await getUserInfo() 41 | userStore.setUserInfo(username, role) 42 | // generate accessible routes map based on roles 43 | const menus = [] // menus = ["Apis"] // 此menus 可通過接口獲得 44 | // generate accessible routes map based on roles 45 | const accessRoutes = permissionStore.generateRoutes(menus) 46 | // dynamically add accessible routes 47 | resetRouter() 48 | accessRoutes.forEach(val => { 49 | router.addRoute(val) 50 | }) 51 | 52 | next({ ...to, replace: true }) 53 | } catch (error) { 54 | console.log('err:', error) 55 | // remove token and go to login page to re-login 56 | userStore.removeUserStore() 57 | next(`/login`) 58 | NProgress.done() 59 | } 60 | }) 61 | 62 | router.afterEach(() => { 63 | NProgress.done() 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /web/src/router/constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: default layout 3 | */ 4 | export const LAYOUT = () => import('@/components/Layout/index.vue') 5 | -------------------------------------------------------------------------------- /web/src/router/modules/client.ts: -------------------------------------------------------------------------------- 1 | import { AppRouteRecordRaw } from '@/router/types' 2 | import { LAYOUT } from '@/router/constant' 3 | 4 | const ClientRoutes: Array = [ 5 | { 6 | path: '/client', 7 | name: 'Client', 8 | component: LAYOUT, 9 | meta: { 10 | title: '客户端', 11 | icon: 'bell' 12 | }, 13 | redirect: 'accounts', 14 | children: [ 15 | { 16 | path: 'login', 17 | name: 'ClientLogin', 18 | component: () => import('@/views/client/login/index.vue'), 19 | meta: { 20 | title: '客户端登录', 21 | icon: 'key' 22 | } 23 | }, 24 | { 25 | path: 'test', 26 | name: 'ClientTest', 27 | component: () => import('@/views/test.vue'), 28 | meta: { 29 | title: '测试', 30 | icon: 'list' 31 | } 32 | } 33 | ] 34 | } 35 | ] 36 | 37 | export default ClientRoutes 38 | -------------------------------------------------------------------------------- /web/src/router/modules/gm.ts: -------------------------------------------------------------------------------- 1 | import { AppRouteRecordRaw } from '@/router/types' 2 | import { LAYOUT } from '@/router/constant' 3 | 4 | const accountsRoutes: Array = [ 5 | { 6 | path: '/gm', 7 | name: 'GM', 8 | component: LAYOUT, 9 | meta: { 10 | title: 'GM管理', 11 | icon: 'cog' 12 | }, 13 | redirect: 'accounts', 14 | children: [ 15 | { 16 | path: 'accounts', 17 | name: 'GmAccounts', 18 | component: () => import('@/views/gm/account/index.vue'), 19 | meta: { 20 | title: '账号管理', 21 | icon: 'list' 22 | } 23 | }, 24 | { 25 | path: 'roles', 26 | name: 'GmRoles', 27 | component: () => import('@/views/gm/roles/index.vue'), 28 | meta: { 29 | title: '角色管理', 30 | icon: 'fork' 31 | } 32 | }, 33 | { 34 | path: 'tasks', 35 | name: 'GmTasks', 36 | component: () => import('@/views/gm/tasks/index.vue'), 37 | meta: { 38 | title: '任务清理', 39 | icon: 'timer' 40 | } 41 | }, 42 | { 43 | path: 'email', 44 | name: 'GmEmail', 45 | component: () => import('@/views/gm/email/index.vue'), 46 | meta: { 47 | title: '邮件工具', 48 | icon: 'transfer' 49 | } 50 | }, 51 | { 52 | path: 'auction', 53 | name: 'GmAuction', 54 | component: () => import('@/views/gm/auction/index.vue'), 55 | meta: { 56 | title: '拍卖行', 57 | icon: 'list' 58 | } 59 | }, 60 | { 61 | path: 'test', 62 | name: 'UserTest', 63 | component: () => import('@/views/test.vue'), 64 | meta: { 65 | title: '测试', 66 | icon: 'list' 67 | } 68 | } 69 | ] 70 | } 71 | ] 72 | 73 | export default accountsRoutes 74 | -------------------------------------------------------------------------------- /web/src/router/modules/system.ts: -------------------------------------------------------------------------------- 1 | import { AppRouteRecordRaw } from '@/router/types' 2 | import { LAYOUT } from '@/router/constant' 3 | 4 | const systemRoutes: Array = [ 5 | { 6 | path: '/system', 7 | name: 'System', 8 | component: LAYOUT, 9 | meta: { 10 | title: '系统管理', 11 | icon: 'lock' 12 | }, 13 | redirect: 'user_list', 14 | children: [ 15 | { 16 | path: 'user_list', 17 | name: 'UserList', 18 | component: () => import('@/views/user/list/index.vue'), 19 | meta: { 20 | title: '用户管理', 21 | icon: 'people' 22 | } 23 | }, 24 | { 25 | path: 'user_info', 26 | name: 'UserInfo', 27 | component: () => import('@/views/user/info/index.vue'), 28 | // hidden: true, 29 | meta: { 30 | title: '个人信息', 31 | icon: 'person' 32 | // hideTag: true 33 | } 34 | } 35 | ] 36 | } 37 | ] 38 | 39 | export default systemRoutes 40 | -------------------------------------------------------------------------------- /web/src/router/types.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from 'vue-router' 2 | import { defineComponent } from 'vue' 3 | 4 | export type Component = 5 | ReturnType 6 | | (() => Promise) 7 | | (() => Promise) 8 | 9 | export interface RouteMeta { 10 | // title 11 | title: string 12 | // icon on tab 13 | icon?: string 14 | affix?: boolean 15 | 16 | // no show tag 17 | hideTag?:boolean 18 | } 19 | 20 | // @ts-ignore 21 | export interface AppRouteRecordRaw extends Omit { 22 | name?: string 23 | meta?: RouteMeta 24 | component?: Component | string 25 | components?: Component 26 | children?: AppRouteRecordRaw[] 27 | props?: Recordable 28 | hidden?: boolean 29 | } 30 | 31 | // export type AppRouteModule = RouteModule | AppRouteRecordRaw; 32 | export type AppRouteModule = AppRouteRecordRaw 33 | -------------------------------------------------------------------------------- /web/src/settings.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'MY ADMIN' 3 | } 4 | -------------------------------------------------------------------------------- /web/src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | 3 | const store = createPinia() 4 | 5 | export default store 6 | -------------------------------------------------------------------------------- /web/src/store/modules/app.ts: -------------------------------------------------------------------------------- 1 | import { storageLocal } from '@/utils/storage' 2 | import { defineStore } from 'pinia' 3 | 4 | interface AppState { 5 | sidebar: { 6 | opened: boolean 7 | withoutAnimation: boolean 8 | } 9 | device: string 10 | } 11 | 12 | function getSideBarOpened(): boolean { 13 | const data = storageLocal.getItem('sidebarStatus') 14 | if (data === null) { 15 | return true 16 | } 17 | return Boolean(Number(data)) 18 | } 19 | 20 | export const useAppStore = defineStore({ 21 | id: 'app', 22 | state: (): AppState => ({ 23 | sidebar: { 24 | opened: getSideBarOpened(), 25 | withoutAnimation: false 26 | }, 27 | device: 'desktop' 28 | }), 29 | getters: { 30 | getSidebarOpened() { 31 | return this.sidebar.opened 32 | } 33 | }, 34 | actions: { 35 | toggleSideBar(): void { 36 | this.sidebar.opened = !this.sidebar.opened 37 | this.sidebar.withoutAnimation = false 38 | if (this.sidebar.opened) { 39 | storageLocal.setItem('sidebarStatus', 1) 40 | } else { 41 | storageLocal.setItem('sidebarStatus', 0) 42 | } 43 | }, 44 | closeSideBar(withoutAnimation: boolean): void { 45 | storageLocal.setItem('sidebarStatus', 0) 46 | this.sidebar.opened = false 47 | this.sidebar.withoutAnimation = withoutAnimation 48 | }, 49 | toggleDevice(device: string): void { 50 | this.device = device 51 | } 52 | } 53 | }) 54 | -------------------------------------------------------------------------------- /web/src/store/modules/permission.ts: -------------------------------------------------------------------------------- 1 | import { asyncRoutes, basicRoutes } from '@/router' 2 | import { defineStore } from 'pinia' 3 | import store from '@/store' 4 | 5 | /** 6 | * 匹配 router:name,生成新的router 7 | * @param routes_name 8 | * @param route 9 | */ 10 | function hasPermission(routes_name, route) { 11 | if (route.name) { 12 | // console.log(routes_map, route.path, routes_map.some(val => val.includes(route.path))); 13 | // return routes_map.includes(route.path) 14 | return Boolean(!routes_name.includes(route.name) || route.hidden) 15 | } else { 16 | return true 17 | } 18 | } 19 | 20 | /** 21 | * 从all 的路由数据 ,刷选出满足用户 hasPermission 的路由 22 | * @param routes 23 | * @param routes_map 24 | */ 25 | export function filterAsyncRoutes(routes, routes_map) { 26 | const res = [] 27 | 28 | routes.forEach(route => { 29 | const tmp = { ...route } 30 | if (hasPermission(routes_map, tmp)) { 31 | if (tmp.children) { 32 | tmp.children = filterAsyncRoutes(tmp.children, routes_map) 33 | } 34 | res.push(tmp) 35 | } 36 | }) 37 | 38 | return res 39 | } 40 | 41 | /*** 42 | * constantRouterMap : 公共路由,比如login ,404,500 43 | * asyncRouterMap : 动态需添加路由,自定义加权限 44 | */ 45 | const state = { 46 | routers: [], 47 | addRouters: [] 48 | } 49 | 50 | interface PermissionState { 51 | routers: any[] 52 | addRouters: any[] 53 | } 54 | 55 | export const usePermissionStore = defineStore({ 56 | id: 'app-permission', 57 | state: (): PermissionState => ({ 58 | routers: [], 59 | addRouters: [] 60 | }), 61 | getters: { 62 | getPermissionAddRouters(): any[] { 63 | return this.addRouters 64 | } 65 | }, 66 | actions: { 67 | generateRoutes(data: string[]) { 68 | let accessedRoutes = filterAsyncRoutes(asyncRoutes, data) 69 | this.addRouters = accessedRoutes 70 | this.routers = basicRoutes.concat(accessedRoutes) 71 | return accessedRoutes 72 | } 73 | } 74 | }) 75 | 76 | // Need to be used outside the setup 77 | export function usePermissionStoreWithOut() { 78 | return usePermissionStore(store) 79 | } 80 | -------------------------------------------------------------------------------- /web/src/store/modules/user.ts: -------------------------------------------------------------------------------- 1 | import { storageLocal } from '@/utils/storage' 2 | import { defineStore } from 'pinia' 3 | import store from '@/store' 4 | 5 | interface UserState { 6 | token?: string 7 | username?: string 8 | role?: string 9 | } 10 | 11 | export const useUserStore = defineStore({ 12 | id: 'app-user', 13 | state: (): UserState => ({ 14 | token: undefined, 15 | username: null, 16 | role: null 17 | }), 18 | getters: { 19 | getToken(): string { 20 | return this.token || storageLocal.getItem('token') 21 | }, 22 | getUsername(): string { 23 | return this.username 24 | }, 25 | getUserRole(): string { 26 | return this.role 27 | } 28 | }, 29 | actions: { 30 | setToken(token: string) { 31 | this.token = token 32 | storageLocal.setItem('token', token) 33 | }, 34 | setUserInfo(username: string, role: string) { 35 | this.username = username 36 | this.role = role 37 | }, 38 | removeUserStore() { 39 | this.token = '' 40 | this.username = '' 41 | this.role = '' 42 | storageLocal.clear() 43 | } 44 | } 45 | }) 46 | 47 | // Need to be used outside the setup 48 | export function useUserStoreWithOut() { 49 | return useUserStore(store) 50 | } 51 | -------------------------------------------------------------------------------- /web/src/styles/color.scss: -------------------------------------------------------------------------------- 1 | $danger: #ff1d46; 2 | $success: #00af3e; 3 | $default: #c2d0d6; 4 | $warning: #ff9032; -------------------------------------------------------------------------------- /web/src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin clearfix { 2 | &:after { 3 | content: ""; 4 | display: table; 5 | clear: both; 6 | } 7 | } 8 | 9 | 10 | @mixin relative { 11 | position: relative; 12 | width: 100%; 13 | height: 100%; 14 | } 15 | 16 | 17 | @mixin backGround($color) { 18 | background: $color; 19 | &:hover { 20 | color: $color; 21 | 22 | &:before, 23 | &:after { 24 | background: $color; 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /web/src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | //global transition css 2 | 3 | /*fade*/ 4 | .fade-enter-active, 5 | .fade-leave-active { 6 | transition: opacity 0.28s; 7 | } 8 | 9 | .fade-enter, 10 | .fade-leave-active { 11 | opacity: 0; 12 | } 13 | 14 | /*fade-transform*/ 15 | .fade-transform-leave-active, 16 | .fade-transform-enter-active { 17 | transition: all .5s; 18 | } 19 | 20 | .fade-transform-enter { 21 | opacity: 0; 22 | transform: translateX(-30px); 23 | } 24 | 25 | .fade-transform-leave-to { 26 | opacity: 0; 27 | transform: translateX(30px); 28 | } 29 | 30 | /*breadcrumb transition*/ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all .5s; 34 | } 35 | 36 | .breadcrumb-enter, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(20px); 40 | } 41 | 42 | .breadcrumb-move { 43 | transition: all .5s; 44 | } 45 | 46 | .breadcrumb-leave-active { 47 | position: absolute; 48 | } 49 | -------------------------------------------------------------------------------- /web/src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | import { PropType as VuePropType } from 'vue' 2 | 3 | declare global { 4 | declare type Nullable = T | null 5 | 6 | declare interface Fn { 7 | (...arg: T[]): R 8 | } 9 | 10 | declare type TimeoutHandle = ReturnType 11 | 12 | // vue 13 | declare type PropType = VuePropType 14 | 15 | declare type Recordable = Record 16 | } 17 | -------------------------------------------------------------------------------- /web/src/types/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | 8 | declare module '*.scss' { 9 | const scss: Record 10 | export default scss 11 | } 12 | -------------------------------------------------------------------------------- /web/src/utils/download/index.ts: -------------------------------------------------------------------------------- 1 | import { saveAs } from 'file-saver' 2 | 3 | export const downloadFileByBlob = async (data: any, filename: string): Promise => { 4 | let blob = new Blob([data], { 5 | type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8' 6 | }) 7 | saveAs(blob, filename) 8 | } 9 | 10 | // 不需要token ,可指定文件名 11 | export function downloadFileByUrl(content, fileName) { 12 | const blob = new Blob([content]) //创建一个类文件对象:Blob对象表示一个不可变的、原始数据的类文件对象 13 | const url = window.URL.createObjectURL(blob) //URL.createObjectURL(object)表示生成一个File对象或Blob对象 14 | let dom = document.createElement('a') //设置一个隐藏的a标签,href为输出流,设置download 15 | dom.style.display = 'none' 16 | dom.href = url 17 | dom.setAttribute('download', fileName) //指示浏览器下载url,而不是导航到它;因此将提示用户将其保存为本地文件 18 | document.body.appendChild(dom) 19 | dom.click() 20 | dom.remove() 21 | } 22 | 23 | // 不需要token ,不需要指定文件名 24 | export function downloadFileByUrlIframe(url) { 25 | try { 26 | let elemIF = document.createElement('iframe') 27 | elemIF.src = url 28 | elemIF.style.display = 'none' 29 | document.body.appendChild(elemIF) 30 | } catch (e) { 31 | console.log(e) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /web/src/utils/echarts/echarts.ts: -------------------------------------------------------------------------------- 1 | import * as echarts from 'echarts/core'; 2 | 3 | import { 4 | BarChart, 5 | LineChart, 6 | PieChart, 7 | MapChart, 8 | PictorialBarChart, 9 | RadarChart, 10 | ScatterChart, 11 | } from 'echarts/charts'; 12 | 13 | import { 14 | TitleComponent, 15 | TooltipComponent, 16 | GridComponent, 17 | PolarComponent, 18 | AriaComponent, 19 | ParallelComponent, 20 | LegendComponent, 21 | RadarComponent, 22 | ToolboxComponent, 23 | DataZoomComponent, 24 | VisualMapComponent, 25 | TimelineComponent, 26 | CalendarComponent, 27 | GraphicComponent, 28 | } from 'echarts/components'; 29 | 30 | import { SVGRenderer } from 'echarts/renderers'; 31 | 32 | echarts.use([ 33 | LegendComponent, 34 | TitleComponent, 35 | TooltipComponent, 36 | GridComponent, 37 | PolarComponent, 38 | AriaComponent, 39 | ParallelComponent, 40 | BarChart, 41 | LineChart, 42 | PieChart, 43 | MapChart, 44 | RadarChart, 45 | SVGRenderer, 46 | PictorialBarChart, 47 | RadarComponent, 48 | ToolboxComponent, 49 | DataZoomComponent, 50 | VisualMapComponent, 51 | TimelineComponent, 52 | CalendarComponent, 53 | GraphicComponent, 54 | ScatterChart, 55 | ]); 56 | 57 | export default echarts; 58 | -------------------------------------------------------------------------------- /web/src/utils/element/form.ts: -------------------------------------------------------------------------------- 1 | import {Ref, unref} from 'vue' 2 | 3 | /** 4 | * 表单校验 5 | * @param ref 节点 6 | * @param isGetError 是否获取错误项 7 | */ 8 | export async function validate(ref: Ref | any, isGetError = false): Promise { 9 | const validateFn = unref(ref).validate; 10 | return new Promise(resolve => validateFn((valid: boolean, object: any) => isGetError ? resolve({ 11 | valid, 12 | object 13 | }) : resolve(valid))) 14 | } 15 | 16 | /** 17 | * 对部分表单字段进行校验的方法 18 | * @param ref 节点 19 | * @param props 字段属性 20 | */ 21 | export async function validateField(ref: Ref | any, props: Array | string): Promise { 22 | const validateFieldFn = unref(ref).validateField; 23 | return new Promise(resolve => validateFieldFn(props, (errorMessage: string) => resolve(errorMessage))) 24 | } 25 | 26 | /** 27 | * 重置表单 28 | * @param ref 节点 29 | */ 30 | export function resetFields(ref: Ref | any): void { 31 | const resetFieldsFn = unref(ref).resetFields; 32 | resetFieldsFn() 33 | } 34 | 35 | /** 36 | * 移除表单项的校验结果 37 | * @param ref 节点 38 | * @param props 字段属性 39 | */ 40 | export function clearValidate(ref: Ref | any, props?: Array | string): void { 41 | const clearValidateFn = unref(ref).clearValidate; 42 | props ? clearValidateFn(props) : clearValidateFn() 43 | } 44 | -------------------------------------------------------------------------------- /web/src/utils/element/message.ts: -------------------------------------------------------------------------------- 1 | import { ElMessage } from 'element-plus' 2 | 3 | // 消息 4 | const Message = (message: string, duration: number = 3000): any => { 5 | return ElMessage({ 6 | showClose: true, 7 | message, 8 | duration 9 | }) 10 | } 11 | 12 | // 成功 13 | const successMessage = (message: string,duration: number = 3000): any => { 14 | return ElMessage({ 15 | showClose: true, 16 | message, 17 | type: 'success', 18 | duration 19 | }) 20 | } 21 | 22 | // 警告 23 | const warnMessage = (message: string,duration: number = 3000): any => { 24 | return ElMessage({ 25 | showClose: true, 26 | message, 27 | type: 'warning', 28 | duration 29 | }) 30 | } 31 | 32 | // 失败 33 | const errorMessage = (message: string,duration: number = 3000): any => { 34 | return ElMessage({ 35 | showClose: true, 36 | message, 37 | type: 'error', 38 | duration 39 | }) 40 | } 41 | 42 | export { Message, successMessage, warnMessage, errorMessage } 43 | -------------------------------------------------------------------------------- /web/src/utils/element/messageBox.ts: -------------------------------------------------------------------------------- 1 | import { ElMessageBox } from 'element-plus' 2 | 3 | export const confirmWarning = (message: string): any => { 4 | return ElMessageBox.confirm(message, '提示', { 5 | confirmButtonText: '确定', 6 | cancelButtonText: '取消', 7 | type: 'warning' 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /web/src/utils/event/breakpointEnum.ts: -------------------------------------------------------------------------------- 1 | export enum sizeEnum { 2 | XS = 'XS', 3 | SM = 'SM', 4 | MD = 'MD', 5 | LG = 'LG', 6 | XL = 'XL', 7 | XXL = 'XXL', 8 | } 9 | 10 | export enum screenEnum { 11 | XS = 480, 12 | SM = 576, 13 | MD = 768, 14 | LG = 992, 15 | XL = 1200, 16 | XXL = 1600, 17 | } 18 | 19 | const screenMap = new Map(); 20 | 21 | screenMap.set(sizeEnum.XS, screenEnum.XS); 22 | screenMap.set(sizeEnum.SM, screenEnum.SM); 23 | screenMap.set(sizeEnum.MD, screenEnum.MD); 24 | screenMap.set(sizeEnum.LG, screenEnum.LG); 25 | screenMap.set(sizeEnum.XL, screenEnum.XL); 26 | screenMap.set(sizeEnum.XXL, screenEnum.XXL); 27 | 28 | export { screenMap }; -------------------------------------------------------------------------------- /web/src/utils/filters.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | // vue3 not support filter 3 | 4 | export const dateFormat = (dataStr: string, pattern: string = 'YYYY-MM-DD HH:mm:ss'): string => { 5 | return moment(dataStr).format(pattern) 6 | } 7 | 8 | export function subtractDate(dataStr: string, days: number = 30, pattern: string = 'YYYY-MM-DD HH:mm:ss'): string { 9 | return moment(dataStr).subtract(days, 'd').format(pattern) 10 | } 11 | 12 | export function addDate(dataStr: string, days: number = 30, pattern: string = 'YYYY-MM-DD HH:mm:ss'): string { 13 | return moment(dataStr).add(days, 'd').format(pattern) 14 | } 15 | 16 | export function compareDate(date_now: string, compare_date: string, pattern: string = 'YYYY-MM-DD HH:mm:ss'): boolean { 17 | const now = moment(date_now).format(pattern) 18 | const compare_time = moment(compare_date).format(pattern) 19 | 20 | return moment(compare_time).isBefore(now) 21 | } 22 | -------------------------------------------------------------------------------- /web/src/utils/fn.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | import { tryOnUnmounted } from '@vueuse/core' 3 | 4 | export function isFunction(val: unknown): val is Function { 5 | return typeof val === 'function' 6 | } 7 | 8 | export function useTimeoutFn(handle: Fn, wait: number, native = false) { 9 | if (!isFunction(handle)) { 10 | throw new Error('handle is not Function!') 11 | } 12 | 13 | const { readyRef, stop, start } = useTimeoutRef(wait) 14 | if (native) { 15 | handle() 16 | } else { 17 | watch( 18 | readyRef, 19 | maturity => { 20 | maturity && handle() 21 | }, 22 | { immediate: false } 23 | ) 24 | } 25 | return { readyRef, stop, start } 26 | } 27 | 28 | export function useTimeoutRef(wait: number) { 29 | const readyRef = ref(false) 30 | 31 | let timer: TimeoutHandle 32 | 33 | function stop(): void { 34 | readyRef.value = false 35 | timer && window.clearTimeout(timer) 36 | } 37 | 38 | function start(): void { 39 | stop() 40 | timer = setTimeout(() => { 41 | readyRef.value = true 42 | }, wait) 43 | } 44 | 45 | start() 46 | 47 | tryOnUnmounted(stop) 48 | 49 | return { readyRef, stop, start } 50 | } 51 | -------------------------------------------------------------------------------- /web/src/utils/get-page-title.ts: -------------------------------------------------------------------------------- 1 | import defaultSettings from '@/settings' 2 | 3 | const title = defaultSettings.title 4 | 5 | export const getPageTitle = (key: any): string => { 6 | if (key) { 7 | return `${key} - ${title}` 8 | } 9 | return title 10 | } 11 | -------------------------------------------------------------------------------- /web/src/utils/http/config.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios' 2 | import { HttpTimeOut, HttpUrl } from '@/utils/http/const' 3 | 4 | const defaultConfig: AxiosRequestConfig = { 5 | baseURL: HttpUrl, // api的base_url 6 | timeout: HttpTimeOut, // request timeout 7 | headers: { 8 | Accept: 'application/json, text/plain, */*; charset=utf-8', 9 | 'Content-Type': 'application/json; charset=utf-8', 10 | Pragma: 'no-cache', 11 | 'Cache-Control': 'no-cache' 12 | } 13 | } 14 | 15 | const getRequestConfig = (config?: AxiosRequestConfig): AxiosRequestConfig => { 16 | if (!config) { 17 | return defaultConfig 18 | } 19 | const { headers } = config 20 | if (headers && typeof headers === 'object') { 21 | defaultConfig.headers = { 22 | ...defaultConfig.headers, 23 | ...headers 24 | } 25 | } 26 | 27 | return { ...excludeProps(config!, 'headers'), ...defaultConfig } 28 | } 29 | 30 | export { getRequestConfig } 31 | 32 | function excludeProps(origin: T, prop: string): { [key: string]: T } { 33 | return Object.keys(origin) 34 | .filter(key => !prop.includes(key)) 35 | .reduce((res, key) => { 36 | res[key] = origin[key] 37 | return res 38 | }, {} as { [key: string]: T }) 39 | } 40 | -------------------------------------------------------------------------------- /web/src/utils/http/const.ts: -------------------------------------------------------------------------------- 1 | export const HttpTimeOut = 50 * 1000 2 | export const HttpUrl = process.env.VUE_APP_BASE_API 3 | -------------------------------------------------------------------------------- /web/src/utils/http/download.ts: -------------------------------------------------------------------------------- 1 | import AxiosHttp from '@/utils/http/core' 2 | import { HttpTimeOut, HttpUrl } from '@/utils/http/const' 3 | import { AxiosRequestConfig } from 'axios' 4 | 5 | const downloadConfig: AxiosRequestConfig = { 6 | baseURL: HttpUrl, // api的base_url 7 | timeout: HttpTimeOut, // request timeout 8 | responseType: 'blob' 9 | } 10 | 11 | // 下载 返回字节 blob 12 | export const downloadHttp = new AxiosHttp(downloadConfig, 'download') 13 | -------------------------------------------------------------------------------- /web/src/utils/http/index.ts: -------------------------------------------------------------------------------- 1 | import AxiosHttp from './core' 2 | 3 | export const http = new AxiosHttp() 4 | -------------------------------------------------------------------------------- /web/src/utils/http/upload.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from 'axios' 2 | import { HttpTimeOut, HttpUrl } from '@/utils/http/const' 3 | import AxiosHttp from '@/utils/http/core' 4 | 5 | const uploadConfig: AxiosRequestConfig = { 6 | baseURL: HttpUrl, // api的base_url 7 | timeout: HttpTimeOut, // request timeout 8 | headers: { 9 | 'Content-Type': 'multipart/form-data' 10 | } 11 | } 12 | 13 | export const uploadHttp = new AxiosHttp(uploadConfig) 14 | -------------------------------------------------------------------------------- /web/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils' 2 | export * from './filters' 3 | export * from './fn' 4 | export * from './get-page-title' 5 | -------------------------------------------------------------------------------- /web/src/utils/progress/index.ts: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress' 2 | import 'nprogress/nprogress.css' 3 | 4 | NProgress.configure({ 5 | easing: 'ease', // 动画方式 6 | speed: 500, // 递增进度条的速度 7 | showSpinner: true, // 是否显示加载ico 8 | trickleSpeed: 200, // 自动递增间隔 9 | minimum: 0.3 // 初始化时的最小百分比 10 | }) 11 | 12 | export default NProgress 13 | -------------------------------------------------------------------------------- /web/src/utils/propTypes.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties, VNodeChild } from 'vue' 2 | import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types' 3 | 4 | export type VueNode = VNodeChild | JSX.Element 5 | 6 | type PropTypes = VueTypesInterface & { 7 | readonly style: VueTypeValidableDef 8 | readonly VNodeChild: VueTypeValidableDef 9 | } 10 | 11 | const propTypes = createTypes({ 12 | func: undefined, 13 | bool: undefined, 14 | string: undefined, 15 | number: undefined, 16 | object: undefined, 17 | array: undefined, 18 | integer: undefined 19 | }) as PropTypes 20 | 21 | propTypes.extend([ 22 | { 23 | name: 'style', 24 | getter: true, 25 | type: [String, Object], 26 | default: undefined 27 | }, 28 | { 29 | name: 'VNodeChild', 30 | getter: true, 31 | type: undefined 32 | } 33 | ]) 34 | export { propTypes } 35 | -------------------------------------------------------------------------------- /web/src/utils/storage/index.ts: -------------------------------------------------------------------------------- 1 | interface ProxyStorage { 2 | getItem(key: string): any 3 | setItem(Key: string, value: string): void 4 | removeItem(key: string): void 5 | clear(): void 6 | } 7 | 8 | //sessionStorage operate 9 | class sessionStorageProxy implements ProxyStorage { 10 | protected storage: ProxyStorage 11 | 12 | constructor(storageModel: ProxyStorage) { 13 | this.storage = storageModel 14 | } 15 | 16 | // 存 17 | public setItem(key: string, value: any): void { 18 | this.storage.setItem(key, value) 19 | } 20 | 21 | // 取 22 | public getItem(key: string): any { 23 | return this.storage.getItem(key) || null 24 | } 25 | 26 | // 删 27 | public removeItem(key: string): void { 28 | this.storage.removeItem(key) 29 | } 30 | 31 | // 清空 32 | public clear(): void { 33 | this.storage.clear() 34 | } 35 | } 36 | 37 | //localStorage operate 38 | class localStorageProxy extends sessionStorageProxy implements ProxyStorage { 39 | constructor(localStorage: ProxyStorage) { 40 | super(localStorage) 41 | } 42 | } 43 | 44 | export const storageSession = new sessionStorageProxy(sessionStorage) 45 | 46 | export const storageLocal = new localStorageProxy(localStorage) 47 | -------------------------------------------------------------------------------- /web/src/utils/vue/index.ts: -------------------------------------------------------------------------------- 1 | export * from './install' 2 | -------------------------------------------------------------------------------- /web/src/utils/vue/install.ts: -------------------------------------------------------------------------------- 1 | import { NOOP } from '@vue/shared' 2 | 3 | import type { App, Directive } from 'vue' 4 | 5 | import type { AppContext, Plugin } from 'vue' 6 | 7 | export type SFCWithInstall = T & Plugin 8 | 9 | export type SFCInstallWithContext = SFCWithInstall & { 10 | _context: AppContext | null 11 | } 12 | 13 | export const withInstall = >(main: T, extra?: E) => { 14 | ;(main as SFCWithInstall).install = (app): void => { 15 | for (const comp of [main, ...Object.values(extra ?? {})]) { 16 | app.component(comp.name, comp) 17 | } 18 | } 19 | 20 | if (extra) { 21 | for (const [key, comp] of Object.entries(extra)) { 22 | ;(main as any)[key] = comp 23 | } 24 | } 25 | return main as SFCWithInstall & E 26 | } 27 | 28 | export const withInstallFunction = (fn: T, name: string) => { 29 | ;(fn as SFCWithInstall).install = (app: App) => { 30 | ;(fn as SFCInstallWithContext)._context = app._context 31 | app.config.globalProperties[name] = fn 32 | } 33 | 34 | return fn as SFCInstallWithContext 35 | } 36 | 37 | export const withInstallDirective = (directive: T, name: string) => { 38 | ;(directive as SFCWithInstall).install = (app: App): void => { 39 | app.directive(name, directive) 40 | } 41 | 42 | return directive as SFCWithInstall 43 | } 44 | 45 | export const withNoopInstall = (component: T) => { 46 | ;(component as SFCWithInstall).install = NOOP 47 | 48 | return component as SFCWithInstall 49 | } 50 | -------------------------------------------------------------------------------- /web/src/views/client/login/models/login.ts: -------------------------------------------------------------------------------- 1 | type LoginType = 'uid' | 'password' 2 | 3 | export type ClientLoginForm = { 4 | login_type: LoginType 5 | uid: number 6 | username: string 7 | password: string 8 | public_pem: string 9 | game_dir: string 10 | } 11 | -------------------------------------------------------------------------------- /web/src/views/dashboard/dash.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/web/src/views/dashboard/dash.scss -------------------------------------------------------------------------------- /web/src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 31 | -------------------------------------------------------------------------------- /web/src/views/dashboard/model/dash.ts: -------------------------------------------------------------------------------- 1 | type Chart = { 2 | cera: ChartInfo 3 | cera_point: ChartInfo 4 | } 5 | 6 | type ChartInfo = { 7 | date: string[] 8 | total: number[] 9 | } 10 | -------------------------------------------------------------------------------- /web/src/views/gm/account/filter.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /web/src/views/gm/account/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /web/src/views/gm/account/model/info.ts: -------------------------------------------------------------------------------- 1 | export type ChangePasswordForm = { 2 | password: string 3 | check_password: string 4 | } 5 | 6 | export type EditAccountForm = { 7 | qq: string 8 | } 9 | 10 | export interface CreateAccountForm extends ChangePasswordForm, EditAccountForm { 11 | account_name: string 12 | } 13 | -------------------------------------------------------------------------------- /web/src/views/gm/account/model/model.ts: -------------------------------------------------------------------------------- 1 | export type AccountDetail = { 2 | uid: number 3 | account_name: string 4 | qqq: string 5 | roles: number 6 | money: any 7 | capacity: number 8 | 9 | cera_point: number // d点 10 | cera: number // d币 11 | } 12 | 13 | export type Account = { 14 | items: AccountDetail[] 15 | total: number 16 | page: number 17 | per_page: number 18 | pages: number 19 | } 20 | 21 | export type AccountState = { 22 | data: AccountDetail[] 23 | loading: boolean 24 | total: number 25 | } 26 | export type FilterAccountForm = { 27 | uid: string 28 | } 29 | 30 | export type RechargeForm = { 31 | cera_point: number // d点 32 | cera: number // d币 33 | 34 | cera_option: string 35 | number?: number 36 | } 37 | -------------------------------------------------------------------------------- /web/src/views/gm/auction/model.ts: -------------------------------------------------------------------------------- 1 | export type AuctionState = { 2 | name: string 3 | desc: string 4 | visible: boolean 5 | } 6 | 7 | -------------------------------------------------------------------------------- /web/src/views/gm/email/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /web/src/views/gm/email/model.ts: -------------------------------------------------------------------------------- 1 | export type Email = { 2 | code: number // 物品代码 3 | number: number // 物品数量 4 | seperate_upgrade: number // 武器锻造等级 5 | upgrade: number // 装备强化等级 6 | is_amplify: boolean // 具备异界属性 7 | amplify_option: number // 异界类型 8 | amplify_value: number // 追加属性 9 | gold: number // 金币 10 | seal_flag: boolean // 是否封装 11 | } 12 | 13 | export type FilterGold = { 14 | name: string 15 | } 16 | -------------------------------------------------------------------------------- /web/src/views/gm/roles/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /web/src/views/gm/roles/model.ts: -------------------------------------------------------------------------------- 1 | import { AccountDetail } from '@/views/gm/account/model/model' 2 | 3 | export type AccountOptions = { 4 | data: AccountDetail[] 5 | } 6 | 7 | export type SelectForm = { 8 | uid: number 9 | } 10 | 11 | export type Role = { 12 | mid: number 13 | charac_no: number 14 | charac_name: string 15 | lev: string 16 | create_time: string 17 | money: number 18 | QP: number 19 | pvp: RolePvp 20 | } 21 | 22 | export type RolePvp = { 23 | win: number 24 | pvp_grade: number 25 | pvp_point: number 26 | } 27 | 28 | export type RoleState = { 29 | loading: boolean 30 | data: Role[] 31 | } 32 | -------------------------------------------------------------------------------- /web/src/views/gm/roles/model/pvp.ts: -------------------------------------------------------------------------------- 1 | export type PvpForm = { 2 | win: number 3 | pvp_grade: number 4 | pvp_point: number 5 | } -------------------------------------------------------------------------------- /web/src/views/gm/roles/model/qp.ts: -------------------------------------------------------------------------------- 1 | export type QpForm = { 2 | qp: number 3 | } -------------------------------------------------------------------------------- /web/src/views/gm/tasks/RoleTable.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /web/src/views/gm/tasks/model.ts: -------------------------------------------------------------------------------- 1 | export type Task = { 2 | charac_no: number 3 | play_1: number 4 | play_1_trigger: boolean 5 | } 6 | 7 | export type TaskState = { 8 | loading: boolean 9 | data: Task[] 10 | } 11 | -------------------------------------------------------------------------------- /web/src/views/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /web/src/views/test.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /web/src/views/user/info/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /web/src/views/user/test/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": false, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "useDefineForClassFields": true, 14 | "sourceMap": false, 15 | "baseUrl": ".", 16 | "types": [ 17 | "webpack-env", 18 | "node" 19 | ], 20 | "paths": { 21 | "@/*": [ 22 | "src/*" 23 | ] 24 | }, 25 | "lib": [ 26 | "esnext", 27 | "dom", 28 | "dom.iterable", 29 | "scripthost" 30 | ] 31 | }, 32 | "include": [ 33 | "src/**/*.ts", 34 | "src/**/*.vue" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /web/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | 3 | const path = require('path') 4 | 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | 9 | const port = 8080 // dev port 10 | 11 | module.exports = defineConfig({ 12 | transpileDependencies: true, 13 | 14 | publicPath: '/static', 15 | outputDir: 'dist', 16 | assetsDir: 'lib', 17 | lintOnSave: process.env.NODE_ENV === 'development', 18 | productionSourceMap: false, 19 | devServer: { 20 | port, 21 | open: false, 22 | hot: true, 23 | client: { 24 | overlay: { 25 | warnings: false, 26 | errors: true 27 | } 28 | }, 29 | proxy: process.env.VUE_APP_BASE_API 30 | }, 31 | configureWebpack: { 32 | // provide the app's title in webpack name field, so that 33 | // it can be accessed in index.html to inject the correct title. 34 | name: 'APP', 35 | resolve: { 36 | alias: { 37 | '@': resolve('src') 38 | }, 39 | fallback: { 40 | path: require.resolve('path-browserify') 41 | } 42 | }, 43 | performance: { 44 | hints: false 45 | } 46 | }, 47 | chainWebpack: config => { 48 | config.resolve.alias.set('@', resolve('src')) 49 | 50 | // set svg-sprite-loader 51 | config.module.rule('svg').exclude.add(resolve('src/icons')).end() 52 | config.module 53 | .rule('icons') 54 | .test(/\.svg$/) 55 | .include.add(resolve('src/icons')) 56 | .end() 57 | .use('svg-sprite-loader') 58 | .loader('svg-sprite-loader') 59 | .options({ 60 | symbolId: 'icon-[name]' 61 | }) 62 | .end() 63 | }, 64 | pluginOptions: { 65 | // 第三方插件配置 66 | // ... 67 | } 68 | }) 69 | -------------------------------------------------------------------------------- /wxp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/localhostjason/dnf-console/cd7cae945e9d25219aef516550e1d63e655f4952/wxp.png --------------------------------------------------------------------------------