简体中文 | English
This project just for learning golang, welcome to give your suggestions!
- Get master package , Notice must use
master
version.
go get github.com/snowlyg/iris-admin@master
- [viper_server]
- The plugin configuration is initialized and generate a local configuration file.
- Use github.com/spf13/viper third party package.
- Need implement
func getViperConfig() viper_server.ViperConfig
function.
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 third party package.
- Through global variables
zap_server.ZAPLOG
record the log of the corresponding level.
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 third party package.
- Through single instance
database.Instance()
operating data.
database.Instance().Model(&User{}).Where("name = ?","name").Find(&user)
...
- [casbin]
- Access control management plugin.
- Use casbin third party package.
- Through use
casbin.Instance()
middleware on route,implement interface authority authentication
_, 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 third party package.
- Through single instance
cache.Instance()
operating data.
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 third party package.
- Through single instance
cron_server.Instance()
to add job or func.
cron_server.CronInstance().AddJob("@every 1m",YourJob)
// or
cron_server.CronInstance().AddFunc("@every 1m",YourFunc)
...
-
[web]
- web_iris Go-Iris web framework plugin.
- web_gin Go-gin web web framework plugin.
- web framework plugin need implement
type WebFunc interface {}
interface.
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 third party package.
- Use gorm's
AutoMigrate()
function to auto migrate database.
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{})
}
- Use
gormigrate
third party package. Tt's helpful for database migrate and program development. - Detail is see iris-admin-cmd.
- Add main.go file.
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)
}
- When you first run this cmd
go run main.go
, you can see some config files in theconfig
directory, - and
rbac_model.conf
will be created in your project root directory.
go run main.go
- You can use iris-admin-rbac package to add rbac function for your project quickly.
- Your can use AddModule() to add other modules .
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())
}
- 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.
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
- Default,you must build vue to the
dist
directory. - Naturally you can set this config key
web-path
to change the default directory.
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
- vue-element-admin
- Before start unit tests, you need to set two system environment variables
mysqlPwd
andmysqlAddr
,that will be used when running the test instance。 - helper/tests package the unit test used, it's simple package base on httpexpect/v2.
- example for unit test
- example for unit 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
andSeedFunc
use to custom someting for your test model. 内容如下所示: main_test.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
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: "[email protected]"},
{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": "[email protected]",
"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": "[email protected]",
"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": "[email protected]",
"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": "[email protected]",
"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: "[email protected]"},
{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)
}
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.