├── .gitignore ├── README.md ├── common ├── db │ └── db.go ├── middlewares │ ├── authz.go │ └── session.go ├── pkg │ └── e │ │ ├── code.go │ │ └── message.go ├── utils │ └── utils.go └── validator │ ├── custom_validate.go │ └── v8_to_v9.go ├── conf ├── app.ini ├── authz │ ├── model.conf │ └── policy.csv └── conf.go ├── docs ├── docs.go └── swagger │ ├── swagger.json │ └── swagger.yaml ├── glide.yaml ├── qrcode.jpg ├── routers ├── api │ └── v1 │ │ ├── mining-machine │ │ ├── controlers.go │ │ ├── models.go │ │ ├── routers.go │ │ └── validators.go │ │ ├── user │ │ ├── controlers.go │ │ ├── middlewares.go │ │ ├── models.go │ │ ├── routers.go │ │ └── validators.go │ │ └── v1.go └── routers.go ├── server.go └── web ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue └── main.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | main.exe 3 | main -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-gin-restfulAPI-example-app 2 | 3 | 4 | 5 | > 一个用go语言基于Gin写的restful风格api服务程序的例子. 6 | 7 | ## 项目特性 8 | - 基于[gin](https://github.com/gin-gonic/gin) 9 | - 使用[MongoDB](https://github.com/mongodb/mongo)数据库 10 | - [gin-jwt](https://github.com/appleboy/gin-jwt)权限验证 11 | - [gin-sessions](https://github.com/gin-contrib/sessions) 12 | - [gin-authz](https://github.com/gin-contrib/authz)从session里取用户的角色进行权限管理 13 | - 使用[gin-swagger](https://github.com/swaggo/gin-swagger)自动生成api文档 14 | - 将gin默认的validator.v8升级到[validator.v9](https://github.com/go-playground/validator) 15 | - 使用[casbin](https://github.com/casbin/casbin)权限管理 16 | - 使用[go-ini](https://github.com/go-ini/ini)读写配置文件 17 | 18 | ## 项目目录 19 | ``` 20 | . 21 | ├── server.go // 入口文件 22 | ├── docs // swagger生成的api文档 23 | ├── web // vue写的前端单页页面 24 | ├── common 25 | │ ├── db // mongoDB相关 26 | │ ├── utils // 公用工具函数 27 | │ ├── pkg // 公用包 28 | | | └── e 29 | | | ├── code.go // http状态码常量 30 | │ | └── message.go // 状态码对应的message常量 31 | │ ├── validator 32 | | | ├── custom_validate.go // 自定义验证器 33 | │ | └── v8_to_v9.go // 将gin的默认验证器从v8升级到v9 34 | │ └── middlewares 35 | | ├── authz.go // 角色认证 36 | │ └── session.go // 使用session 37 | ├── conf // 应用配置相关文件 38 | | ├── authz 39 | | | ├── model.conf // 权限管理方案配置 40 | │ | └── policy.csv // 权限分配表 41 | | ├── app.ini // 应用配置文件 42 | │ └── conf.go // 初始化配置文件 43 | └── routers 44 | ├── routers.go // 路由初始化 45 | └── api // api文件 46 | └── v1 // api版本v1 47 | ├── v1.go // v1版本api入口 48 | ├── mining-machine // 矿机模块 49 | | ├── models.go // 模型和数据库操作 50 | | ├── controlers.go // 当前模块的控制器 51 | | ├── routers.go // 当前模块的路由 52 | | ├── middlewares.go // 当前模块的中间件 53 | | └── validators.go // 当前模块的验证器 54 | └── user // 用户模块 55 | ├── models.go // 模型和数据库操作 56 | ├── controlers.go // 当前模块的控制器 57 | ├── routers.go // 当前模块的路由 58 | ├── middlewares.go // 当前模块的中间件 59 | └── validators.go // 当前模块的验证器 60 | 61 | ``` 62 | 63 | ## 联系作者 64 | -------------------------------------------------------------------------------- /common/db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "fmt" 5 | "golang-gin-restfulAPI-example-app/conf" 6 | 7 | "gopkg.in/mgo.v2" 8 | ) 9 | 10 | // Connection 数据库 11 | type Connection struct { 12 | session *mgo.Session 13 | } 14 | 15 | // NewConnection 新建数据库连接 16 | func NewConnection() (conn *Connection) { 17 | sec, err := conf.Cfg.GetSection("database") 18 | if err != nil { 19 | fmt.Println("loade config fail") 20 | } 21 | host := sec.Key("HOST").String() 22 | session, err := mgo.Dial(host) 23 | if err != nil { 24 | fmt.Println("connect mongoDB fail!") 25 | panic(err) 26 | } 27 | session.SetMode(mgo.Monotonic, true) 28 | conn = &Connection{session} 29 | 30 | return 31 | } 32 | 33 | // Use 切换数据库 34 | func (conn *Connection) Use(dbName, tableName string) (collection *mgo.Collection) { 35 | return conn.session.DB(dbName).C(tableName) 36 | } 37 | 38 | // Close 关闭数据库连接 39 | func (conn *Connection) Close() { 40 | conn.session.Close() 41 | } 42 | -------------------------------------------------------------------------------- /common/middlewares/authz.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/casbin/casbin" 7 | "github.com/gin-contrib/sessions" 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // NewAuthorizer returns the authorizer, uses a Casbin enforcer as input 12 | func NewAuthorizer(e *casbin.Enforcer) gin.HandlerFunc { 13 | a := &BasicAuthorizer{enforcer: e} 14 | 15 | return func(c *gin.Context) { 16 | if !a.CheckPermission(c) { 17 | a.RequirePermission(c) 18 | } 19 | } 20 | } 21 | 22 | // BasicAuthorizer stores the casbin handler 23 | type BasicAuthorizer struct { 24 | enforcer *casbin.Enforcer 25 | } 26 | 27 | // GetRoleName gets the user name from the request. 28 | // Currently, only HTTP basic authentication is supported 29 | func (a *BasicAuthorizer) GetRoleName(c *gin.Context) interface{} { 30 | session := sessions.Default(c) 31 | role := session.Get("role") 32 | if role != nil && role != "" { 33 | fmt.Println("session role --->", role) 34 | return role 35 | } 36 | fmt.Println("defaulte role ---> common") 37 | return "common" 38 | } 39 | 40 | // CheckPermission checks the user/method/path combination from the request. 41 | // Returns true (permission granted) or false (permission forbidden) 42 | func (a *BasicAuthorizer) CheckPermission(c *gin.Context) bool { 43 | role := a.GetRoleName(c) 44 | method := c.Request.Method 45 | path := c.Request.URL.Path 46 | return a.enforcer.Enforce(role, path, method) 47 | } 48 | 49 | // RequirePermission returns the 403 Forbidden to the client 50 | func (a *BasicAuthorizer) RequirePermission(c *gin.Context) { 51 | c.AbortWithStatus(403) 52 | } 53 | -------------------------------------------------------------------------------- /common/middlewares/session.go: -------------------------------------------------------------------------------- 1 | package middlewares 2 | 3 | import ( 4 | "github.com/gin-contrib/sessions" 5 | "github.com/gin-contrib/sessions/mongo" 6 | "github.com/gin-gonic/gin" 7 | "github.com/globalsign/mgo" 8 | ) 9 | 10 | // SessionsMiddleware . 11 | func SessionsMiddleware(c *gin.Context) gin.HandlerFunc { 12 | session, err := mgo.Dial("localhost:27017/test") 13 | if err != nil { 14 | // handle err 15 | } 16 | 17 | collection := session.DB("test").C("sessions") 18 | store := mongo.NewStore(collection, 3600, true, []byte("secret")) 19 | return sessions.Sessions("mysession", store) 20 | } 21 | -------------------------------------------------------------------------------- /common/pkg/e/code.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | const ( 4 | SUCCESS = 200 5 | ERROR = 500 6 | INVALID_PARAMS = 400 7 | 8 | ERROR_EXIST_TAG = 10001 9 | ERROR_NOT_EXIST_TAG = 10002 10 | ERROR_NOT_EXIST_ARTICLE = 10003 11 | 12 | ERROR_AUTH_CHECK_TOKEN_FAIL = 20001 13 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002 14 | ERROR_AUTH_TOKEN = 20003 15 | ERROR_AUTH = 20004 16 | ) 17 | -------------------------------------------------------------------------------- /common/pkg/e/message.go: -------------------------------------------------------------------------------- 1 | package e 2 | 3 | var msgFlags = map[int]string{ 4 | SUCCESS: "ok", 5 | ERROR: "fail", 6 | INVALID_PARAMS: "请求参数错误", 7 | ERROR_EXIST_TAG: "已存在该标签名称", 8 | ERROR_NOT_EXIST_TAG: "该标签不存在", 9 | ERROR_NOT_EXIST_ARTICLE: "该文章不存在", 10 | ERROR_AUTH_CHECK_TOKEN_FAIL: "Token鉴权失败", 11 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token已超时", 12 | ERROR_AUTH_TOKEN: "Token生成失败", 13 | ERROR_AUTH: "Token错误", 14 | } 15 | 16 | // GetMessage 通过code获取message 17 | func GetMessage(code int) string { 18 | msg, ok := msgFlags[code] 19 | if ok { 20 | return msg 21 | } 22 | 23 | return msgFlags[ERROR] 24 | } 25 | -------------------------------------------------------------------------------- /common/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "time" 5 | "golang-gin-restfulAPI-example-app/common/pkg/e" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // RES 返回信息自动根据code插入message 11 | func RES(c *gin.Context, code int, obj gin.H) { 12 | if obj["message"] == "" { 13 | obj["message"] = e.GetMessage(code) 14 | } 15 | obj["timestamp"] = time.Now().Format("2006-01-02 15:04:05") 16 | c.JSON(code, obj) 17 | } 18 | -------------------------------------------------------------------------------- /common/validator/custom_validate.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | 8 | ut "github.com/go-playground/universal-translator" 9 | "gopkg.in/go-playground/validator.v9" 10 | ) 11 | 12 | // ValidateMultiEmails 校验以“,”分隔的多个邮箱 eg. test1@163.com,test2@163.com 13 | func ValidateMultiEmails(fl validator.FieldLevel) bool { 14 | return isValidMultiEmails(fl.Field().String()) 15 | } 16 | 17 | // ValidateMultiEmailsRegisterTranslationsFunc . 18 | func ValidateMultiEmailsRegisterTranslationsFunc(ut ut.Translator) (err error) { 19 | if err = ut.Add("isValidMultiEmails", "{0}邮箱不合法,多个邮箱请以逗号(半角)分隔", false); err != nil { 20 | return 21 | } 22 | return 23 | 24 | } 25 | 26 | func translateFunc(ut ut.Translator, fe validator.FieldError) string { 27 | t, err := ut.T(fe.Tag(), fe.Field()) 28 | if err != nil { 29 | fmt.Println("警告: 翻译字段错误: ", fe) 30 | return fe.(error).Error() 31 | } 32 | return t 33 | } 34 | 35 | // 校验以“,”分隔的多个邮箱 eg. test1@163.com,test2@163.com 36 | func isValidMultiEmails(mailsStr string) bool { 37 | if len(mailsStr) == 0 || mailsStr == "" { 38 | return false 39 | } 40 | mails := strings.Split(mailsStr, ",") 41 | for _, mail := range mails { 42 | if !isValidEmail(mail) { 43 | return false 44 | } 45 | } 46 | return true 47 | } 48 | 49 | var isEmailRe = regexp.MustCompile(`^([a-z_A-Z.0-9-])+@([a-zA-Z0-9_-])+\.([a-zA-Z0-9_-])+`) 50 | 51 | // 判断是否正确的电子邮件 52 | func isValidEmail(email string) bool { 53 | return isEmailRe.MatchString(email) 54 | } 55 | -------------------------------------------------------------------------------- /common/validator/v8_to_v9.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | "strings" 8 | "sync" 9 | 10 | "github.com/gin-gonic/gin/binding" 11 | local_zh "github.com/go-playground/locales/zh" 12 | ut "github.com/go-playground/universal-translator" 13 | "gopkg.in/go-playground/validator.v9" 14 | translations_zh "gopkg.in/go-playground/validator.v9/translations/zh" 15 | ) 16 | 17 | var ( 18 | uni *ut.UniversalTranslator 19 | validate *validator.Validate 20 | ) 21 | 22 | type defaultValidator struct { 23 | once sync.Once 24 | validate *validator.Validate 25 | trans *ut.Translator 26 | } 27 | 28 | // 初始化gin验证器 29 | func init() { 30 | binding.Validator = new(defaultValidator) 31 | } 32 | 33 | var _ binding.StructValidator = &defaultValidator{} 34 | 35 | func (v *defaultValidator) ValidateStruct(obj interface{}) error { 36 | if kindOfData(obj) == reflect.Struct { 37 | v.lazyinit() 38 | if err := v.validate.Struct(obj); err != nil { 39 | errs := err.(validator.ValidationErrors) 40 | messages := make([]string, len(errs), len(errs)) 41 | for i, e := range errs { 42 | messages[i] = e.Translate(*v.trans) 43 | } 44 | return errors.New(strings.Join(messages, ", ")) 45 | } 46 | } 47 | return nil 48 | } 49 | 50 | func (v *defaultValidator) Engine() interface{} { 51 | v.lazyinit() 52 | return v.validate 53 | } 54 | 55 | func (v *defaultValidator) lazyinit() { 56 | v.once.Do(func() { 57 | fmt.Println("lazyinit validator") 58 | // 国际化 59 | localZH := local_zh.New() 60 | uni = ut.New(localZH, localZH) 61 | trans, _ := uni.GetTranslator("zh") 62 | 63 | v.validate = validator.New() 64 | v.trans = &trans 65 | // validate.v9 tag 默认validate,兼容老代码 66 | v.validate.SetTagName("validate") 67 | // 汉化验证提示 68 | translations_zh.RegisterDefaultTranslations(v.validate, trans) 69 | // 自定义验证器 https://godoc.org/gopkg.in/go-playground/validator.v9 70 | v.validate.RegisterValidation("isValidMultiEmails", ValidateMultiEmails) 71 | v.validate.RegisterTranslation("isValidMultiEmails", trans, ValidateMultiEmailsRegisterTranslationsFunc, translateFunc) 72 | }) 73 | } 74 | 75 | func kindOfData(data interface{}) reflect.Kind { 76 | value := reflect.ValueOf(data) 77 | valueType := value.Kind() 78 | if valueType == reflect.Ptr { 79 | valueType = value.Elem().Kind() 80 | } 81 | return valueType 82 | } 83 | -------------------------------------------------------------------------------- /conf/app.ini: -------------------------------------------------------------------------------- 1 | #debug or release 2 | RUN_MODE = debug 3 | 4 | [app] 5 | PAGE_SIZE = 10 6 | JWT_SECRET = 23347$040412 7 | 8 | [server] 9 | HTTP_PORT = 8000 10 | READ_TIMEOUT = 60 11 | WRITE_TIMEOUT = 60 12 | 13 | [database] 14 | TYPE = mogodb 15 | USER = 数据库账号 16 | PASSWORD = 数据库密码 17 | #127.0.0.1:3306 18 | HOST = 127.0.0.1 19 | NAME = blog -------------------------------------------------------------------------------- /conf/authz/model.conf: -------------------------------------------------------------------------------- 1 | #定义请求参数 2 | [request_definition] 3 | r = sub, obj, act 4 | 5 | [policy_definition] 6 | p = sub, obj, act 7 | 8 | [policy_effect] 9 | e = some(where (p.eft == allow)) 10 | 11 | [matchers] 12 | m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act) -------------------------------------------------------------------------------- /conf/authz/policy.csv: -------------------------------------------------------------------------------- 1 | p, admin, /*, (GET|POST|PUT|DELETE) 2 | p, user, /*, (GET|POST|PUT|DELETE) 3 | p, common, /login, POST 4 | p, common, /register, POST 5 | p, common, /*, (GET|POST|PUT|DELETE) -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-ini/ini" 7 | ) 8 | 9 | var ( 10 | // Cfg . 11 | Cfg *ini.File 12 | ) 13 | 14 | // 加载配置文件 15 | func init() { 16 | var err error 17 | Cfg, err = ini.Load("conf/app.ini") 18 | if err != nil { 19 | fmt.Println("loade config err") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/docs.go: -------------------------------------------------------------------------------- 1 | // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT 2 | // This file was generated by swaggo/swag at 3 | // 2019-03-15 12:09:24.2722086 +0800 CST m=+1.407080501 4 | 5 | package docs 6 | 7 | import ( 8 | "bytes" 9 | 10 | "github.com/alecthomas/template" 11 | "github.com/swaggo/swag" 12 | ) 13 | 14 | var doc = `{ 15 | "swagger": "2.0", 16 | "info": { 17 | "description": "yottachain 的API接口.", 18 | "title": "yottachain API", 19 | "termsOfService": "http://swagger.io/terms/", 20 | "contact": { 21 | "name": "API Support", 22 | "url": "http://www.swagger.io/support", 23 | "email": "support@swagger.io" 24 | }, 25 | "license": { 26 | "name": "Apache 2.0", 27 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 28 | }, 29 | "version": "1.0" 30 | }, 31 | "host": "petstore.swagger.io", 32 | "basePath": "/v2", 33 | "paths": { 34 | "/api/v1/tags": { 35 | "post": { 36 | "produces": [ 37 | "application/json" 38 | ], 39 | "summary": "用户注册", 40 | "parameters": [ 41 | { 42 | "description": "Name", 43 | "name": "name", 44 | "in": "body", 45 | "required": true, 46 | "schema": { 47 | "type": "object" 48 | } 49 | }, 50 | { 51 | "description": "State", 52 | "name": "state", 53 | "in": "body", 54 | "schema": { 55 | "type": "object" 56 | } 57 | }, 58 | { 59 | "description": "CreatedBy", 60 | "name": "created_by", 61 | "in": "body", 62 | "schema": { 63 | "type": "object" 64 | } 65 | } 66 | ], 67 | "responses": { 68 | "200": {}, 69 | "500": {} 70 | } 71 | } 72 | } 73 | } 74 | }` 75 | 76 | type swaggerInfo struct { 77 | Version string 78 | Host string 79 | BasePath string 80 | Title string 81 | Description string 82 | } 83 | 84 | // SwaggerInfo holds exported Swagger Info so clients can modify it 85 | var SwaggerInfo swaggerInfo 86 | 87 | type s struct{} 88 | 89 | func (s *s) ReadDoc() string { 90 | t, err := template.New("swagger_info").Parse(doc) 91 | if err != nil { 92 | return doc 93 | } 94 | 95 | var tpl bytes.Buffer 96 | if err := t.Execute(&tpl, SwaggerInfo); err != nil { 97 | return doc 98 | } 99 | 100 | return tpl.String() 101 | } 102 | 103 | func init() { 104 | swag.Register(swag.Name, &s{}) 105 | } 106 | -------------------------------------------------------------------------------- /docs/swagger/swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "description": "yottachain 的API接口.", 5 | "title": "yottachain API", 6 | "termsOfService": "http://swagger.io/terms/", 7 | "contact": { 8 | "name": "API Support", 9 | "url": "http://www.swagger.io/support", 10 | "email": "support@swagger.io" 11 | }, 12 | "license": { 13 | "name": "Apache 2.0", 14 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 15 | }, 16 | "version": "1.0" 17 | }, 18 | "host": "petstore.swagger.io", 19 | "basePath": "/v2", 20 | "paths": { 21 | "/api/v1/tags": { 22 | "post": { 23 | "produces": [ 24 | "application/json" 25 | ], 26 | "summary": "用户注册", 27 | "parameters": [ 28 | { 29 | "description": "Name", 30 | "name": "name", 31 | "in": "body", 32 | "required": true, 33 | "schema": { 34 | "type": "object" 35 | } 36 | }, 37 | { 38 | "description": "State", 39 | "name": "state", 40 | "in": "body", 41 | "schema": { 42 | "type": "object" 43 | } 44 | }, 45 | { 46 | "description": "CreatedBy", 47 | "name": "created_by", 48 | "in": "body", 49 | "schema": { 50 | "type": "object" 51 | } 52 | } 53 | ], 54 | "responses": { 55 | "200": {}, 56 | "500": {} 57 | } 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /docs/swagger/swagger.yaml: -------------------------------------------------------------------------------- 1 | basePath: /v2 2 | host: petstore.swagger.io 3 | info: 4 | contact: 5 | email: support@swagger.io 6 | name: API Support 7 | url: http://www.swagger.io/support 8 | description: yottachain 的API接口. 9 | license: 10 | name: Apache 2.0 11 | url: http://www.apache.org/licenses/LICENSE-2.0.html 12 | termsOfService: http://swagger.io/terms/ 13 | title: yottachain API 14 | version: "1.0" 15 | paths: 16 | /api/v1/tags: 17 | post: 18 | parameters: 19 | - description: Name 20 | in: body 21 | name: name 22 | required: true 23 | schema: 24 | type: object 25 | - description: State 26 | in: body 27 | name: state 28 | schema: 29 | type: object 30 | - description: CreatedBy 31 | in: body 32 | name: created_by 33 | schema: 34 | type: object 35 | produces: 36 | - application/json 37 | responses: 38 | "200": {} 39 | "500": {} 40 | summary: 用户注册 41 | swagger: "2.0" 42 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: mining-machine-manage 2 | import: 3 | - package: github.com/gin-gonic/gin 4 | version: ^1.3.0 5 | - package: gopkg.in/mgo.v2 6 | subpackages: 7 | - bson -------------------------------------------------------------------------------- /qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-eric/golang-gin-restfulAPI-example-app/4b9c5838d82bc182b962edc0574638a0ca8fb428/qrcode.jpg -------------------------------------------------------------------------------- /routers/api/v1/mining-machine/controlers.go: -------------------------------------------------------------------------------- 1 | package miningmachine 2 | 3 | import ( 4 | "fmt" 5 | "golang-gin-restfulAPI-example-app/common/pkg/e" 6 | . "golang-gin-restfulAPI-example-app/common/utils" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | // 添加矿机 12 | func addItem(c *gin.Context) { 13 | m := &miningMachine{} 14 | m1 := &miningMachine{} 15 | if err := c.BindJSON(m); err != nil { 16 | RES(c, e.INVALID_PARAMS, gin.H{ 17 | "message": err.Error(), 18 | }) 19 | return 20 | } 21 | err := m1.getOneByNodeID(m.NodeID) 22 | if err == nil { 23 | m.ID = m1.ID 24 | err = m.update() 25 | if err != nil { 26 | fmt.Println(err) 27 | RES(c, e.ERROR, gin.H{}) 28 | } else { 29 | RES(c, e.SUCCESS, gin.H{}) 30 | } 31 | } else { 32 | err = m.add() 33 | if err != nil { 34 | fmt.Println(err) 35 | RES(c, e.ERROR, gin.H{}) 36 | } else { 37 | RES(c, e.SUCCESS, gin.H{}) 38 | } 39 | } 40 | } 41 | 42 | // 获取矿机列表 43 | func getList(c *gin.Context) { 44 | mList := &miningMachineList{} 45 | err := mList.getAll() 46 | if err != nil { 47 | RES(c, e.ERROR, gin.H{}) 48 | } else { 49 | RES(c, e.SUCCESS, gin.H{ 50 | "data": mList, 51 | "count": mList.count(), 52 | }) 53 | } 54 | } 55 | 56 | // 通过id获取矿机信息 57 | func getItemByID(c *gin.Context) { 58 | m := &miningMachine{} 59 | id := c.Param("id") 60 | err := m.getOneByID(id) 61 | if err != nil { 62 | RES(c, e.ERROR, gin.H{}) 63 | } else { 64 | RES(c, e.SUCCESS, gin.H{ 65 | "data": m, 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /routers/api/v1/mining-machine/models.go: -------------------------------------------------------------------------------- 1 | package miningmachine 2 | 3 | import ( 4 | "golang-gin-restfulAPI-example-app/common/db" 5 | 6 | "gopkg.in/mgo.v2/bson" 7 | ) 8 | 9 | var dbConnect *db.Connection 10 | 11 | func init() { 12 | dbConnect = db.NewConnection() 13 | } 14 | 15 | // 矿机 16 | type miningMachine struct { 17 | ID bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` 18 | NodeID string `json:"nodeId" form:"nodeId" validate:"required" bson:"nodeId"` 19 | Addrs []string `json:"addrs" form:"addrs" bson:"addrs"` 20 | PubAddr string `json:"pubAddr" form:"pubAddr" bson:"pubAddr"` 21 | ExpirationTime string `json:"expirationTime" bson:"expirationTime"` 22 | } 23 | 24 | // 添加矿机 25 | func (m *miningMachine) add() error { 26 | c := dbConnect.Use("MiningMachine", "list") 27 | return c.Insert(m) 28 | } 29 | 30 | // 根据Id查询 31 | func (m *miningMachine) getOneByID(id string) error { 32 | c := dbConnect.Use("MiningMachine", "list") 33 | query := bson.M{"_id": bson.ObjectIdHex(id)} 34 | return c.Find(query).One(m) 35 | } 36 | 37 | // 根据nodeID查询 38 | func (m *miningMachine) getOneByNodeID(nodeID string) error { 39 | c := dbConnect.Use("MiningMachine", "list") 40 | query := bson.M{"nodeId": nodeID} 41 | return c.Find(query).One(m) 42 | } 43 | 44 | // 更新矿机信息 45 | func (m *miningMachine) update() error { 46 | c := dbConnect.Use("MiningMachine", "list") 47 | query := bson.M{"_id": m.ID} 48 | return c.Update(query, m) 49 | } 50 | 51 | // 矿机列表 52 | type miningMachineList []miningMachine 53 | 54 | // 获取矿机列表 55 | func (ml *miningMachineList) getAll() error { 56 | c := dbConnect.Use("MiningMachine", "list") 57 | return c.Find(nil).All(ml) 58 | } 59 | 60 | // 获取矿机总数 61 | func (ml *miningMachineList) count() int { 62 | c := dbConnect.Use("MiningMachine", "list") 63 | count, _ := c.Find(nil).Count() 64 | return count 65 | } 66 | -------------------------------------------------------------------------------- /routers/api/v1/mining-machine/routers.go: -------------------------------------------------------------------------------- 1 | package miningmachine 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | // RegisterRouter 注册路由 8 | func RegisterRouter(r *gin.RouterGroup) { 9 | // 添加矿机 10 | r.POST("/item", addItem) 11 | // 矿机列表 12 | r.GET("/list", getList) 13 | // 根具矿机id获取矿机详情 14 | r.GET("/item/:id", getItemByID) 15 | } 16 | -------------------------------------------------------------------------------- /routers/api/v1/mining-machine/validators.go: -------------------------------------------------------------------------------- 1 | package miningmachine 2 | -------------------------------------------------------------------------------- /routers/api/v1/user/controlers.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "time" 5 | 6 | "golang-gin-restfulAPI-example-app/common/pkg/e" 7 | . "golang-gin-restfulAPI-example-app/common/utils" 8 | 9 | "github.com/gin-gonic/gin" 10 | "gopkg.in/mgo.v2/bson" 11 | ) 12 | 13 | // @Summary 用户注册 14 | // @Produce json 15 | // @Param name body string true "Name" 16 | // @Param state body int false "State" 17 | // @Param created_by body int false "CreatedBy" 18 | // @Success 200 19 | // @Failure 500 20 | // @Router /api/v1/tags [post] 21 | func register(c *gin.Context) { 22 | u := &user{} 23 | 24 | if err := c.ShouldBind(u); err != nil { 25 | RES(c, e.INVALID_PARAMS, gin.H{ 26 | "message": err.Error(), 27 | }) 28 | return 29 | } 30 | 31 | if err = u.getOneByUsername(u.Username); err != nil { 32 | err = u.add() 33 | if err != nil { 34 | RES(c, e.ERROR, gin.H{ 35 | "message": err.Error(), 36 | }) 37 | } else { 38 | RES(c, e.SUCCESS, gin.H{}) 39 | } 40 | } else { 41 | RES(c, e.ERROR, gin.H{ 42 | "message": "用户名已存在!", 43 | }) 44 | } 45 | } 46 | 47 | // 获取用户列表 48 | func getUserList(c *gin.Context) { 49 | users := &users{} 50 | err := users.getAll() 51 | if err != nil { 52 | RES(c, e.ERROR, gin.H{ 53 | "message": err.Error(), 54 | }) 55 | } else { 56 | RES(c, e.SUCCESS, gin.H{ 57 | "data": users, 58 | }) 59 | } 60 | } 61 | 62 | // 更新用户信息 63 | func updateUserByID(c *gin.Context) { 64 | id := c.Param("id") 65 | sex := c.PostForm("sex") 66 | user := &user{} 67 | user.getOneByID(id) 68 | user.Sex = sex 69 | user.UpdateTime = time.Now().UnixNano() / int64(time.Millisecond) 70 | err := user.update() 71 | if err != nil { 72 | RES(c, e.ERROR, gin.H{ 73 | "message": err.Error(), 74 | }) 75 | } else { 76 | RES(c, e.SUCCESS, gin.H{}) 77 | } 78 | } 79 | 80 | // 删除用户信息 81 | func deleteUserByID(c *gin.Context) { 82 | user := &user{ 83 | ID: bson.ObjectIdHex(c.Param("id")), 84 | } 85 | err := user.delete() 86 | if err != nil { 87 | RES(c, e.ERROR, gin.H{ 88 | "message": err.Error(), 89 | }) 90 | } else { 91 | RES(c, e.SUCCESS, gin.H{}) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /routers/api/v1/user/middlewares.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | jwt "github.com/appleboy/gin-jwt" 10 | "github.com/gin-contrib/sessions" 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | var ( 15 | identityKey = "id" 16 | // Auth 认证中间件 17 | Auth *jwt.GinJWTMiddleware 18 | err error 19 | ) 20 | 21 | func init() { 22 | // the jwt middleware 23 | fmt.Println("init jwt") 24 | Auth, err = jwt.New(&jwt.GinJWTMiddleware{ 25 | Realm: "test zone", 26 | Key: []byte("secret key"), 27 | Timeout: time.Hour, 28 | MaxRefresh: time.Hour, 29 | IdentityKey: identityKey, 30 | // 登录时调用,可将载荷添加到token中 31 | PayloadFunc: func(data interface{}) jwt.MapClaims { 32 | fmt.Println("调用:PayloadFunc") 33 | if v, ok := data.(*user); ok { 34 | return jwt.MapClaims{ 35 | identityKey: v.Username, 36 | } 37 | } 38 | return jwt.MapClaims{ 39 | identityKey: data, 40 | } 41 | }, 42 | // 验证登录状态 43 | IdentityHandler: func(c *gin.Context) interface{} { 44 | fmt.Println("调用:IdentityHandler") 45 | claims := jwt.ExtractClaims(c) 46 | // return &User{ 47 | // Username: claims["id"].(string), 48 | // } 49 | fmt.Println(claims[identityKey]) 50 | return claims[identityKey] 51 | }, 52 | // 验证登录 53 | Authenticator: func(c *gin.Context) (interface{}, error) { 54 | login := &login{} 55 | if err := c.ShouldBind(login); err != nil { 56 | return "", err 57 | } 58 | user, msg, result := login.validator() 59 | 60 | if result { 61 | session := sessions.Default(c) 62 | session.Set("role", user.Role) 63 | session.Save() 64 | return &user, nil 65 | } 66 | 67 | return nil, errors.New(msg) 68 | }, 69 | // 鉴权成功后执行 70 | Authorizator: func(data interface{}, c *gin.Context) bool { 71 | session := sessions.Default(c) 72 | session.Set("userInfo", data) 73 | session.Save() 74 | return true 75 | }, 76 | // 登录成功的回调函数 77 | LoginResponse: func(c *gin.Context, code int, token string, expire time.Time) { 78 | c.JSON(http.StatusOK, gin.H{ 79 | "code": http.StatusOK, 80 | "token": token, 81 | "expire": expire.Format(time.RFC3339), 82 | "message": "login success!", 83 | }) 84 | }, 85 | // 登录失效时的回调函数 86 | Unauthorized: func(c *gin.Context, code int, message string) { 87 | c.JSON(code, gin.H{ 88 | "code": code, 89 | "message": message, 90 | }) 91 | }, 92 | // TokenLookup is a string in the form of ":" that is used 93 | // to extract token from the request. 94 | // Optional. Default value "header:Authorization". 95 | // Possible values: 96 | // - "header:" 97 | // - "query:" 98 | // - "cookie:" 99 | // - "param:" 100 | TokenLookup: "header: Authorization, query: token, cookie: jwt", 101 | 102 | // TokenHeadName is a string in the header. Default value is "Bearer" 103 | TokenHeadName: "Bearer", 104 | 105 | // TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens. 106 | TimeFunc: time.Now, 107 | 108 | // Optionally return the token as a cookie 109 | SendCookie: true, 110 | }) 111 | } 112 | -------------------------------------------------------------------------------- /routers/api/v1/user/models.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "errors" 5 | "golang-gin-restfulAPI-example-app/common/db" 6 | 7 | "gopkg.in/mgo.v2" 8 | "gopkg.in/mgo.v2/bson" 9 | ) 10 | 11 | var userCollection *mgo.Collection 12 | 13 | func init() { 14 | dbConnect := db.NewConnection() 15 | userCollection = dbConnect.Use("test", "user") 16 | } 17 | 18 | // User 是用户类型 19 | type user struct { 20 | ID bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` 21 | Username string `json:"username" form:"username" validate:"required,email" bson:"username"` 22 | Password string `json:"password" form:"password" validate:"required" bson:"password"` 23 | Sex string `json:"sex" form:"sex" bson:"sex"` 24 | UpdateTime int64 `json:"updateTime" bson:"updateTime"` 25 | Role string `json:"role" form:"role" bson:"role"` 26 | } 27 | 28 | // Users 是用户列表 29 | type users []user 30 | 31 | // Login is login struct 32 | type login struct { 33 | Username string `form:"username" validate:"required" bson:"username"` 34 | Password string `form:"password" validate:"required" bson:"password"` 35 | } 36 | 37 | func checkMongoDBNotNull() error { 38 | if userCollection == nil { 39 | return errors.New("mongoDB client is null please set it before use") 40 | } 41 | return nil 42 | } 43 | 44 | // Add 添加用户 45 | func (u *user) add() error { 46 | err := checkMongoDBNotNull() 47 | err = userCollection.Insert(u) 48 | return err 49 | } 50 | 51 | // Validator . 52 | func (login *login) validator() (*user, string, bool) { 53 | user := &user{} 54 | err := userCollection.Find(bson.M{"username": login.Username}).One(user) 55 | var msg string 56 | if err != nil { 57 | msg = "没有该账户!" 58 | return nil, msg, false 59 | } 60 | 61 | if user.Password != login.Password { 62 | msg = "密码错误!" 63 | return nil, msg, false 64 | } 65 | 66 | msg = "登录成功!" 67 | return user, msg, true 68 | } 69 | 70 | // GetOneByUsername 根据username查询 71 | func (u *user) getOneByUsername(username string) error { 72 | query := bson.M{"username": username} 73 | err := userCollection.Find(query).One(u) 74 | return err 75 | } 76 | 77 | // GetOneByID 根据Id查询 78 | func (u *user) getOneByID(id string) error { 79 | query := bson.M{"_id": bson.ObjectIdHex(id)} 80 | err := userCollection.Find(query).One(u) 81 | return err 82 | } 83 | 84 | // Update 更新用户信息 85 | func (u *user) update() error { 86 | query := bson.M{"_id": u.ID} 87 | err := userCollection.Update(query, u) 88 | return err 89 | } 90 | 91 | // GetAll 获取用户列表 92 | func (us *users) getAll() error { 93 | err := checkMongoDBNotNull() 94 | err = userCollection.Find(nil).All(us) 95 | return err 96 | } 97 | 98 | // Delete 通过Id删除用户 99 | func (u *user) delete() error { 100 | err := userCollection.RemoveId(u.ID) 101 | return err 102 | } 103 | -------------------------------------------------------------------------------- /routers/api/v1/user/routers.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | // RegisterRouter 注册路由 8 | func RegisterRouter(r *gin.RouterGroup) { 9 | 10 | // 注册 11 | r.POST("/register", register) 12 | // 登录 13 | r.POST("/login", Auth.LoginHandler) 14 | 15 | auth := r.Group("") 16 | auth.Use(Auth.MiddlewareFunc()) 17 | { 18 | // 用户列表 19 | auth.GET("", getUserList) 20 | // 删除用户 21 | auth.DELETE("/:id", deleteUserByID) 22 | // 更新用户信息 23 | auth.PUT("/:id", updateUserByID) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /routers/api/v1/user/validators.go: -------------------------------------------------------------------------------- 1 | package user 2 | -------------------------------------------------------------------------------- /routers/api/v1/v1.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | miningmachine "golang-gin-restfulAPI-example-app/routers/api/v1/mining-machine" 5 | "golang-gin-restfulAPI-example-app/routers/api/v1/user" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // RegisterRouter 注册路由 11 | func RegisterRouter(router *gin.RouterGroup) { 12 | v1 := router.Group("/v1") 13 | { 14 | // 用户路由 15 | user.RegisterRouter(v1.Group("/user")) 16 | // 矿机管理路由 17 | miningmachine.RegisterRouter(v1.Group("/miningMachine")) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /routers/routers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "fmt" 5 | "golang-gin-restfulAPI-example-app/common/middlewares" 6 | "golang-gin-restfulAPI-example-app/conf" 7 | v1 "golang-gin-restfulAPI-example-app/routers/api/v1" 8 | 9 | _ "golang-gin-restfulAPI-example-app/docs" 10 | 11 | ginSwagger "github.com/swaggo/gin-swagger" 12 | "github.com/swaggo/gin-swagger/swaggerFiles" 13 | 14 | "github.com/casbin/casbin" 15 | "github.com/gin-contrib/sessions" 16 | "github.com/gin-contrib/sessions/mongo" 17 | "github.com/gin-gonic/gin" 18 | "github.com/globalsign/mgo" 19 | ) 20 | 21 | // InitRouter 初始化路由 22 | func InitRouter() *gin.Engine { 23 | 24 | router := gin.Default() 25 | 26 | setUpConfig(router) 27 | setUpRouter(router) 28 | 29 | return router 30 | } 31 | 32 | // 初始化应用设置 33 | func setUpConfig(router *gin.Engine) { 34 | // 设置静态文件处理 35 | router.Static("/assets", "./web/dist") 36 | router.StaticFile("/app.html", "./web/dist/index.html") 37 | 38 | // 使用swagger自动生成接口文档 39 | router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 40 | 41 | // 使用session中间件 42 | sec, err := conf.Cfg.GetSection("database") 43 | if err != nil { 44 | fmt.Println("loade config fail") 45 | } 46 | host := sec.Key("HOST").String() 47 | connection, err := mgo.Dial(host) 48 | if err != nil { 49 | // handle err 50 | } 51 | collection := connection.DB("test").C("sessions") 52 | store := mongo.NewStore(collection, 3600, true, []byte("secret")) 53 | router.Use(sessions.Sessions("mysession", store)) 54 | 55 | // 使用权限管理中间件 56 | e := casbin.NewEnforcer("conf/authz/model.conf", "conf/authz/policy.csv") 57 | router.Use(middlewares.NewAuthorizer(e)) 58 | } 59 | 60 | // 设置路由 61 | func setUpRouter(router *gin.Engine) { 62 | api := router.Group("/api") 63 | { 64 | v1.RegisterRouter(api) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | _ "golang-gin-restfulAPI-example-app/common/validator" 6 | "golang-gin-restfulAPI-example-app/conf" 7 | "golang-gin-restfulAPI-example-app/routers" 8 | ) 9 | 10 | // @title yottachain API 11 | // @version 1.0 12 | // @description yottachain 的API接口. 13 | // @termsOfService http://swagger.io/terms/ 14 | 15 | // @contact.name API Support 16 | // @contact.url http://www.swagger.io/support 17 | // @contact.email support@swagger.io 18 | 19 | // @license.name Apache 2.0 20 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 21 | 22 | // @host petstore.swagger.io 23 | // @BasePath /v2 24 | 25 | func main() { 26 | port, err := conf.Cfg.Section("server").GetKey("HTTP_PORT") 27 | if err != nil { 28 | fmt.Println("loade config fail") 29 | } 30 | 31 | r := routers.InitRouter() 32 | r.Run(":" + port.String()) 33 | } 34 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # hello-world 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /web/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "version": "0.1.0", 4 | "homepage": ".", 5 | "private": true, 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "vue": "^2.6.6" 13 | }, 14 | "devDependencies": { 15 | "@vue/cli-plugin-babel": "^3.4.0", 16 | "@vue/cli-plugin-eslint": "^3.4.0", 17 | "@vue/cli-service": "^3.4.0", 18 | "babel-eslint": "^10.0.1", 19 | "eslint": "^5.8.0", 20 | "eslint-plugin-vue": "^5.0.0", 21 | "vue-template-compiler": "^2.5.21" 22 | }, 23 | "eslintConfig": { 24 | "root": true, 25 | "env": { 26 | "node": true 27 | }, 28 | "extends": [ 29 | "plugin:vue/essential", 30 | "eslint:recommended" 31 | ], 32 | "rules": {}, 33 | "parserOptions": { 34 | "parser": "babel-eslint" 35 | } 36 | }, 37 | "postcss": { 38 | "plugins": { 39 | "autoprefixer": {} 40 | } 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions", 45 | "not ie <= 8" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-eric/golang-gin-restfulAPI-example-app/4b9c5838d82bc182b962edc0574638a0ca8fb428/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | hello-world 9 | 10 | 11 | 12 | We're sorry but hello-world doesn't work properly without JavaScript enabled. Please enable it to continue. 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /web/src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 矿机列表 4 | 共计:{{count}}个 5 | 6 | 7 | {{item._id}} 8 | 9 | 10 | 11 | 12 | 13 | 37 | 38 | 48 | -------------------------------------------------------------------------------- /web/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-eric/golang-gin-restfulAPI-example-app/4b9c5838d82bc182b962edc0574638a0ca8fb428/web/src/assets/logo.png -------------------------------------------------------------------------------- /web/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ msg }} 4 | 5 | For a guide and recipes on how to configure / customize this project, 6 | check out the 7 | vue-cli documentation. 8 | 9 | Installed CLI Plugins 10 | 11 | babel 12 | eslint 13 | 14 | Essential Links 15 | 16 | Core Docs 17 | Forum 18 | Community Chat 19 | Twitter 20 | News 21 | 22 | Ecosystem 23 | 24 | vue-router 25 | vuex 26 | vue-devtools 27 | vue-loader 28 | awesome-vue 29 | 30 | 31 | 32 | 33 | 41 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /web/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /web/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devServer: { 3 | port: 8080, // 端口号 4 | host: "localhost", 5 | https: false, // https:{type:Boolean} 6 | open: true, //配置自动启动浏览器 7 | proxy: 'http://localhost:8000' // 配置跨域处理,只有一个代理 8 | // proxy: { 9 | // "/api": { 10 | // target: "", 11 | // ws: true, 12 | // changeOrigin: true 13 | // }, 14 | // "/foo": { 15 | // target: "" 16 | // } 17 | // } // 配置多个代理 18 | }, 19 | publicPath: process.env.NODE_ENV === 'production' 20 | ? './assets' 21 | : '/' 22 | } 23 | --------------------------------------------------------------------------------
5 | For a guide and recipes on how to configure / customize this project, 6 | check out the 7 | vue-cli documentation. 8 |