Documentation
¶
Overview ¶
<h1 align="center">IrisAdmin</h1>
[](https://app.travis-ci.com/snowlyg/iris-admin) [](https://github.com/snowlyg/iris-admin/blob/master/LICENSE) [](https://godoc.org/github.com/snowlyg/iris-admin) [](https://goreportcard.com/badge/github.com/snowlyg/iris-admin) [](https://codecov.io/gh/snowlyg/iris-admin)
[简体中文](./README.md) | English
#### Project url
[GITHUB](https://github.com/snowlyg/iris-admin) | [GITEE](https://gitee.com/snowlyg/iris-admin) **** > This project just for learning golang, welcome to give your suggestions!
#### Documentation
- [IRIS-ADMIN-DOC](https://doc.snowlyg.com) - [IRIS V12 document for chinese](https://github.com/snowlyg/iris/wiki) - [godoc](https://pkg.go.dev/github.com/snowlyg/iris-admin?utm_source=godoc)
[](https://gitter.im/iris-go-tenancy/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://gitter.im/iris-go-tenancy/iris-admin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) #### BLOG
- [REST API with iris-go web framework](https://blog.snowlyg.com/iris-go-api-1/)
- [How to user iris-go with casbin](https://blog.snowlyg.com/iris-go-api-2/)
---
#### Getting started
- Get master package , Notice must use `master` version.
```sh
go get github.com/snowlyg/iris-admin@master
```
#### Program introduction
##### The project consists of multiple plugins, each with different functions
- [viper_server]
- The plugin configuration is initialized and generate a local configuration file.
- Use github.com/spf13/viper(https://github.com/spf13/viper) third party package.
- Need implement `func getViperConfig() viper_server.ViperConfig` function.
```go package cache
import (
"fmt" "github.com/fsnotify/fsnotify" "github.com/snowlyg/iris-admin/g" "github.com/snowlyg/iris-admin/server/viper_server" "github.com/spf13/viper"
)
var CONFIG Redis
type Redis struct { DB int `mapstructure:"db" json:"db" yaml:"db"` Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` Password string `mapstructure:"password" json:"password" yaml:"password"` PoolSize int `mapstructure:"pool-size" json:"poolSize" yaml:"pool-size"` }
// getViperConfig get initialize config
func getViperConfig() viper_server.ViperConfig { configName := "redis" db := fmt.Sprintf("%d", CONFIG.DB) poolSize := fmt.Sprintf("%d", CONFIG.PoolSize) return viper_server.ViperConfig{ Directory: g.ConfigDir, Name: configName, Type: g.ConfigType, Watch: func(vi *viper.Viper) error { if err := vi.Unmarshal(&CONFIG); err != nil { return fmt.Errorf("deserialization data error: %v", err) } // config file change vi.SetConfigName(configName) return nil }, // Note: When setting the default configuration value, there can be no other symbols such as spaces in front. It must be close to the left Default: []byte(`
db: ` + db + ` addr: "` + CONFIG.Addr + `" password: "` + CONFIG.Password + `" pool-size: ` + poolSize),
} }
```
- [zap_server]
- Plugin logging.
- Use go.uber.org/zap(https://pkg.go.dev/go.uber.org/zap) third party package.
- Through global variables `zap_server.ZAPLOG` record the log of the corresponding level.
```go
zap_server.ZAPLOG.Info("Registration data table error", zap.Any("err", err)) zap_server.ZAPLOG.Debug("Registration data table error", zap.Any("err", err)) zap_server.ZAPLOG.Error("Registration data table error", zap.Any("err", err)) ...
```
- [database]
- database plugin [only support mysql now].
- Use gorm.io/gorm(https://github.com/go-gorm/gorm) third party package.
- Through single instance `database.Instance()` operating data.
```go
database.Instance().Model(&User{}).Where("name = ?","name").Find(&user) ...
```
- [casbin]
- Access control management plugin.
- Use [casbin](github.com/casbin/casbin/v2 ) third party package.
- Through use `casbin.Instance()` middleware on route,implement interface authority authentication
```go
_, err := casbin.Instance().AddRoleForUser("1", "999") uids, err := casbin.Instance().GetRolesForUser("1") _, err := casbin.Instance().RemoveFilteredPolicy(v, p...) ...
```
- [cache]
- Cache-driven plugin
- Use github.com/go-redis/redis(https://github.com/go-redis/redis) third party package.
- Through single instance `cache.Instance()` operating data.
```go
err := cache.Instance().Set(context.Background(), "key", "value", expiration).Err() cache.Instance().Del(context.Background(), "key").Result() cache.Instance().Get(context.Background(), "key") ...
```
- [operation]
- System operation log plugin.
- Through use `index.Use(operation.OperationRecord())` middleware on route , realize the interface to automatically generate operation logs.
- [cron_server]
- Job server
- Use robfig/cron(https://github.com/robfig/cron) third party package.
- Through single instance `cron_server.Instance()` to add job or func.
```go
cron_server.CronInstance().AddJob("@every 1m",YourJob) // or cron_server.CronInstance().AddFunc("@every 1m",YourFunc) ...
```
- [web]
- web_iris [Go-Iris](https://github.com/kataras/iris) web framework plugin.
- web_gin [Go-gin web](https://github.com/gin-gonic/gin) web framework plugin.
- web framework plugin need implement `type WebFunc interface {}` interface.
-
```go
type WebBaseFunc interface { AddWebStatic(staticAbsPath, webPrefix string, paths ...string) AddUploadStatic(staticAbsPath, webPrefix string) InitRouter() error Run() }
// WebFunc web framework // - GetTestClient test client // - GetTestLogin test for login // - AddWebStatic add web static path // - AddUploadStatic add upload static path // - Run start
type WebFunc interface { WebBaseFunc }
```
- [mongodb]
- mongodb
- Use [mongodb](https://www.mongodb.com/) third party package.
#### Initialize database
##### Simple
- Use gorm's `AutoMigrate()` function to auto migrate database.
```go package main
import (
"github.com/snowlyg/iris-admin/server/web" "github.com/snowlyg/iris-admin/server/web/web_iris" "github.com/snowlyg/iris-admin-rbac/iris/perm" "github.com/snowlyg/iris-admin-rbac/iris/role" "github.com/snowlyg/iris-admin/server/database" "github.com/snowlyg/iris-admin/server/operation"
)
func main() { database.Instance().AutoMigrate(&perm.Permission{},&role.Role{},&user.User{},&operation.Oplog{}) }
```
##### Custom migrate tools
- Use `gormigrate` third party package. Tt's helpful for database migrate and program development. - Detail is see [iris-admin-cmd](https://github.com/snowlyg/iris-admin-example/blob/main/iris/cmd/main.go).
---
- Add main.go file.
```go package main
import (
"github.com/snowlyg/iris-admin/server/web" "github.com/snowlyg/iris-admin/server/web/web_iris"
)
func main() { wi := web_iris.Init() web.Start(wi) }
```
#### Run project
- When you first run this cmd `go run main.go` , you can see some config files in the `config` directory, - and `rbac_model.conf` will be created in your project root directory.
```sh go run main.go ```
#### Module
- You can use [iris-admin-rbac](https://github.com/snowlyg/iris-admin-rbac) package to add rbac function for your project quickly. - Your can use AddModule() to add other modules .
```go package main
import (
rbac "github.com/snowlyg/iris-admin-rbac/iris" "github.com/snowlyg/iris-admin/server/web" "github.com/snowlyg/iris-admin/server/web/web_iris"
)
func main() { wi := web_iris.Init() rbacParty := web_iris.Party{ Perfix: "/api/v1", PartyFunc: rbac.Party(), } wi.AddModule(rbacParty) web.Start(web_iris.Init()) }
```
#### Default static file path
- A static file access path has been built in by default - Static files will upload to `/static/upload` directory. - You can set this config key `static-path` to change the default directory.
```yaml system:
addr: "127.0.0.1:8085" db-type: "" level: debug static-prefix: /upload time-format: "2006-01-02 15:04:05" web-path: ./dist
```
#### Use with front-end framework , e.g. vue
- Default,you must build vue to the `dist` directory. - Naturally you can set this config key `web-path` to change the default directory.
```go package main
import (
"github.com/kataras/iris/v12" "github.com/snowlyg/iris-admin/server/web"
)
func main() { webServer := web_iris.Init() wi.AddUploadStatic("/upload", "/var/static") wi.AddWebStatic("/", "/var/static") webServer.Run() }
```
- Front-end page reference/borrowing: *notice: The front-end only realizes preview effect simply*
- [gin-vue-admin](https://github.com/flipped-aurora/gin-vue-admin/tree/master/web)
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
#### Example
- [iris](https://github.com/snowlyg/iris-admin-example/tree/main/iris) - [gin](https://github.com/snowlyg/iris-admin-example/tree/main/gin)
#### RBAC
- [iris-admin-rbac](https://github.com/snowlyg/iris-admin-rbac)
#### Unit test and documentation
- Before start unit tests, you need to set two system environment variables `mysqlPwd` and `mysqlAddr`,that will be used when running the test instance。 - helper/tests(https://github.com/snowlyg/helper/tree/main/tests) package the unit test used, it's simple package base on httpexpect/v2(https://github.com/gavv/httpexpect). - [example for unit test](https://github.com/snowlyg/iris-admin-rbac/tree/main/iris/perm/tests) - [example for unit test](https://github.com/snowlyg/iris-admin-rbac/tree/main/gin/authority/test)
Before create a http api unit test , you need create a base test file named `main_test.go` , this file have some unit test step : ***Suggest use docker mysql, otherwise if the test fails, there will be a lot of test data left behind*** - 1.create database before test start and delete database when test finish. - 2.create tables and seed test data at once time. - 3.`PartyFunc` and `SeedFunc` use to custom someting for your test model. 内容如下所示: ***main_test.go***
```go package test
import (
"os" "testing" "github.com/snowlyg/httptest" rbac "github.com/snowlyg/iris-admin-rbac/gin" "github.com/snowlyg/iris-admin/server/web/common" "github.com/snowlyg/iris-admin/server/web/web_gin"
)
var TestServer *web_gin.WebServer var TestClient *httptest.Client
func TestMain(m *testing.M) { var uuid string uuid, TestServer = common.BeforeTestMainGin(rbac.PartyFunc, rbac.SeedFunc) code := m.Run() common.AfterTestMain(uuid, true) os.Exit(code) }
```
***index_test.go***
```go package test
import (
"fmt" "net/http" "path/filepath" "testing" "github.com/snowlyg/helper/str" "github.com/snowlyg/httptest" rbac "github.com/snowlyg/iris-admin-rbac/gin" "github.com/snowlyg/iris-admin/g" "github.com/snowlyg/iris-admin/server/web" "github.com/snowlyg/iris-admin/server/web/web_gin/response"
)
var (
url = "/api/v1/admin"
)
func TestList(t *testing.T) { TestClient = httptest.Instance(t, str.Join("http://", web.CONFIG.System.Addr), TestServer.GetEngine()) TestClient.Login(rbac.LoginUrl, nil) if TestClient == nil { return } pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, {Key: "data", Value: httptest.Responses{ {Key: "pageSize", Value: 10}, {Key: "page", Value: 1}, {Key: "list", Value: []httptest.Responses{ { {Key: "id", Value: 1, Type: "ge"}, {Key: "nickName", Value: "superadmin"}, {Key: "username", Value: "admin"}, {Key: "headerImg", Value: "http://xxxx/head.png"}, {Key: "status", Value: g.StatusTrue}, {Key: "isShow", Value: g.StatusFalse}, {Key: "phone", Value: "13800138000"}, {Key: "email", Value: "admin@admin.com"}, {Key: "authorities", Value: []string{"superadmin"}}, {Key: "updatedAt", Value: "", Type: "notempty"}, {Key: "createdAt", Value: "", Type: "notempty"}, }, }}, {Key: "total", Value: 0, Type: "ge"}, }}, } TestClient.GET(fmt.Sprintf("%s/getAll", url), pageKeys, httptest.RequestParams) } func TestCreate(t *testing.T) { TestClient = httptest.Instance(t, str.Join("http://", web.CONFIG.System.Addr), TestServer.GetEngine()) TestClient.Login(rbac.LoginUrl, nil) if TestClient == nil { return } data := map[string]interface{}{ "nickName": "test name", "username": "create_test_username", "authorityIds": []uint{web.AdminAuthorityId}, "email": "get@admin.com", "phone": "13800138001", "password": "123456", } id := Create(TestClient, data) if id == 0 { t.Fatalf("add user failed by id=%d", id) } defer Delete(TestClient, id) } func TestUpdate(t *testing.T) { TestClient = httptest.Instance(t, str.Join("http://", web.CONFIG.System.Addr), TestServer.GetEngine()) TestClient.Login(rbac.LoginUrl, nil) if TestClient == nil { return } data := map[string]interface{}{ "nickName": "test name", "username": "create_test_username_for_update", "authorityIds": []uint{web.AdminAuthorityId}, "email": "get@admin.com", "phone": "13800138001", "password": "123456", } id := Create(TestClient, data) if id == 0 { t.Fatalf("add user failed by id=%d", id) } defer Delete(TestClient, id) update := map[string]interface{}{ "nickName": "test name", "email": "get@admin.com", "phone": "13800138003", "password": "123456", } pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, } TestClient.PUT(fmt.Sprintf("%s/updateAdmin/%d", url, id), pageKeys, update) } func TestGetById(t *testing.T) { TestClient = httptest.Instance(t, str.Join("http://", web.CONFIG.System.Addr), TestServer.GetEngine()) TestClient.Login(rbac.LoginUrl, nil) if TestClient == nil { return } data := map[string]interface{}{ "nickName": "test name", "username": "create_test_username_for_get", "email": "get@admin.com", "phone": "13800138001", "authorityIds": []uint{web.AdminAuthorityId}, "password": "123456", } id := Create(TestClient, data) if id == 0 { t.Fatalf("add user failed by id=%d", id) } defer Delete(TestClient, id) pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, {Key: "data", Value: httptest.Responses{ {Key: "id", Value: 1, Type: "ge"}, {Key: "nickName", Value: data["nickName"].(string)}, {Key: "username", Value: data["username"].(string)}, {Key: "status", Value: g.StatusTrue}, {Key: "email", Value: data["email"].(string)}, {Key: "phone", Value: data["phone"].(string)}, {Key: "isShow", Value: g.StatusTrue}, {Key: "headerImg", Value: "http://xxxx/head.png"}, {Key: "updatedAt", Value: "", Type: "notempty"}, {Key: "createdAt", Value: "", Type: "notempty"}, {Key: "createdAt", Value: "", Type: "notempty"}, {Key: "authorities", Value: []string{"超级管理员"}}, }, }, } TestClient.GET(fmt.Sprintf("%s/getAdmin/%d", url, id), pageKeys) } func TestChangeAvatar(t *testing.T) { TestClient = httptest.Instance(t, str.Join("http://", web.CONFIG.System.Addr), TestServer.GetEngine()) TestClient.Login(rbac.LoginUrl, nil) if TestClient == nil { return } data := map[string]interface{}{ "headerImg": "/avatar.png", } pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, } TestClient.POST(fmt.Sprintf("%s/changeAvatar", url), pageKeys, data) profile := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, {Key: "data", Value: httptest.Responses{ {Key: "id", Value: 1, Type: "ge"}, {Key: "nickName", Value: "superadmin"}, {Key: "username", Value: "admin"}, {Key: "headerImg", Value: filepath.ToSlash(web.ToStaticUrl("/avatar.png"))}, {Key: "status", Value: g.StatusTrue}, {Key: "isShow", Value: g.StatusFalse}, {Key: "phone", Value: "13800138000"}, {Key: "email", Value: "admin@admin.com"}, {Key: "authorities", Value: []string{"superadmin"}}, {Key: "updatedAt", Value: "", Type: "notempty"}, {Key: "createdAt", Value: "", Type: "notempty"}, }, }, } TestClient.GET(fmt.Sprintf("%s/profile", url), profile) } func Create(TestClient *httptest.Client, data map[string]interface{}) uint { pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, {Key: "data", Value: httptest.Responses{ {Key: "id", Value: 1, Type: "ge"}, }, }, } return TestClient.POST(fmt.Sprintf("%s/createAdmin", url), pageKeys, data).GetId() } func Delete(TestClient *httptest.Client, id uint) { pageKeys := httptest.Responses{ {Key: "status", Value: http.StatusOK}, {Key: "message", Value: response.ResponseOkMessage}, } TestClient.DELETE(fmt.Sprintf("%s/deleteAdmin/%d", url, id), pageKeys) }
```
## 🔋 JetBrains OS licenses
<a href="https://www.jetbrains.com/?from=iris-admin" target="_blank"><img src="https://raw.githubusercontent.com/panjf2000/illustrations/master/jetbrains/jetbrains-variant-4.png" width="230" align="middle"/></a>
## ☕️ Buy me a coffee
> Please be sure to leave your name, GitHub account or other social media accounts when you donate by the following means so that I can add it to the list of donors as a token of my appreciation. - [为爱发电](https://afdian.net/@snowlyg/plan) - [donating](https://paypal.me/snowlyg?country.x=C2&locale.x=zh_XC)