Initial commit
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"pet-house.com/core/server/database"
|
||||
"pet-house.com/core/server/viper_server"
|
||||
)
|
||||
|
||||
// Initialize initialize
|
||||
func Initialize() error {
|
||||
var cover string
|
||||
if IsExist() {
|
||||
fmt.Println("Your web config is initialized , reinitialized web will cover your web config.")
|
||||
fmt.Println("Did you want to do it ? [Y/N]")
|
||||
fmt.Scanln(&cover)
|
||||
switch strings.ToUpper(cover) {
|
||||
case "Y":
|
||||
case "N":
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
err := Remove()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = initConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("web iris-admin initialized finished!")
|
||||
return nil
|
||||
}
|
||||
|
||||
func initConfig() error {
|
||||
var dbType string
|
||||
fmt.Println("Please choose your database type: ")
|
||||
fmt.Println("1. mysql (only support mysql now)")
|
||||
fmt.Scanln(&dbType)
|
||||
switch dbType {
|
||||
case "1":
|
||||
CONFIG.System.DbType = "mysql"
|
||||
if err := database.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
CONFIG.System.DbType = "mysql"
|
||||
if err := database.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var systemTimeFormat, systemAddr string
|
||||
fmt.Println("Please input your system timeformat: ")
|
||||
fmt.Printf("System timeformat is '%s'\n", CONFIG.System.TimeFormat)
|
||||
fmt.Scanln(&systemTimeFormat)
|
||||
if systemTimeFormat != "" {
|
||||
CONFIG.System.TimeFormat = systemTimeFormat
|
||||
}
|
||||
|
||||
fmt.Println("Please input your system addr: ")
|
||||
fmt.Printf("System addr is '%s'\n", CONFIG.System.Addr)
|
||||
fmt.Scanln(&systemAddr)
|
||||
if systemAddr != "" {
|
||||
CONFIG.System.Addr = systemAddr
|
||||
}
|
||||
err := viper_server.Init(getViperConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/bwmarrin/snowflake"
|
||||
"go.uber.org/zap"
|
||||
"pet-house.com/core/g"
|
||||
"pet-house.com/core/helper/str"
|
||||
"pet-house.com/core/migration"
|
||||
"pet-house.com/core/server/casbin"
|
||||
"pet-house.com/core/server/database"
|
||||
"pet-house.com/core/server/operation"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/web/web_gin"
|
||||
"pet-house.com/core/server/web/web_iris"
|
||||
"pet-house.com/core/server/zap_server"
|
||||
)
|
||||
|
||||
// BeforeTestMainGin
|
||||
func BeforeTestMainGin(party func(wi *web_gin.WebServer), seed func(wi *web_gin.WebServer, mc *migration.MigrationCmd)) (string, *web_gin.WebServer) {
|
||||
fmt.Println("+++++ TEST BEGAIN +++++")
|
||||
zap_server.CONFIG.LogInConsole = true
|
||||
zap_server.Recover()
|
||||
|
||||
dbType := g.TestDbType
|
||||
if dbType != "" {
|
||||
web.CONFIG.System.DbType = dbType
|
||||
}
|
||||
web.Recover()
|
||||
|
||||
node, _ := snowflake.NewNode(1)
|
||||
uuid := str.Join("gin", "_", node.Generate().String())
|
||||
|
||||
database.CONFIG.DbName = uuid
|
||||
mysqlPwd := g.TestMysqlPwd
|
||||
if mysqlPwd != "" {
|
||||
database.CONFIG.Password = mysqlPwd
|
||||
}
|
||||
mysqlAddr := g.TestMysqlAddr
|
||||
if mysqlAddr != "" {
|
||||
database.CONFIG.Path = mysqlAddr
|
||||
}
|
||||
database.CONFIG.LogMode = true
|
||||
|
||||
database.Recover()
|
||||
|
||||
if database.Instance() == nil {
|
||||
fmt.Println("database instance is nil")
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
wi := web_gin.Init()
|
||||
party(wi)
|
||||
web.StartTest(wi)
|
||||
|
||||
mc := migration.New()
|
||||
fmt.Println("++++++ add data to database ++++++")
|
||||
seed(wi, mc)
|
||||
err := mc.Migrate()
|
||||
if err != nil {
|
||||
fmt.Printf("migrate fail: [%s]", err.Error())
|
||||
return uuid, nil
|
||||
}
|
||||
err = mc.Seed()
|
||||
if err != nil {
|
||||
fmt.Printf("seed fail: [%s]", err.Error())
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
return uuid, wi
|
||||
}
|
||||
|
||||
// BeforeTestMainIris
|
||||
func BeforeTestMainIris(party func(wi *web_iris.WebServer), seed func(wi *web_iris.WebServer, mc *migration.MigrationCmd)) (string, *web_iris.WebServer) {
|
||||
fmt.Println("+++++ TEST BEGAIN +++++")
|
||||
zap_server.CONFIG.LogInConsole = true
|
||||
zap_server.Recover()
|
||||
|
||||
dbType := g.TestDbType
|
||||
if dbType != "" {
|
||||
web.CONFIG.System.DbType = dbType
|
||||
}
|
||||
web.Recover()
|
||||
|
||||
node, _ := snowflake.NewNode(1)
|
||||
uuid := str.Join("iris", "_", node.Generate().String())
|
||||
|
||||
database.CONFIG.DbName = uuid
|
||||
mysqlPwd := g.TestMysqlPwd
|
||||
if mysqlPwd != "" {
|
||||
database.CONFIG.Password = mysqlPwd
|
||||
}
|
||||
mysqlAddr := g.TestMysqlAddr
|
||||
if mysqlAddr != "" {
|
||||
database.CONFIG.Path = mysqlAddr
|
||||
}
|
||||
database.CONFIG.LogMode = true
|
||||
|
||||
database.Recover()
|
||||
|
||||
if database.Instance() == nil {
|
||||
fmt.Println("database instance is nil")
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
wi := web_iris.Init()
|
||||
party(wi)
|
||||
web.StartTest(wi)
|
||||
|
||||
mc := migration.New()
|
||||
|
||||
fmt.Println("++++++ add datas to database ++++++")
|
||||
|
||||
seed(wi, mc)
|
||||
err := mc.Migrate()
|
||||
if err != nil {
|
||||
fmt.Printf("migrate fail: [%s]", err.Error())
|
||||
return uuid, nil
|
||||
}
|
||||
err = mc.Seed()
|
||||
if err != nil {
|
||||
fmt.Printf("seed fail: [%s]", err.Error())
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
return uuid, wi
|
||||
}
|
||||
|
||||
func AfterTestMain(uuid string, isDelDb bool) {
|
||||
defer fmt.Println("++++++++ AFTER END ++++++++")
|
||||
if isDelDb {
|
||||
err := database.DorpDB(database.CONFIG.BaseDsn(), "mysql", uuid)
|
||||
if err != nil {
|
||||
zap_server.ZAPLOG.Error("delete database fail", zap.String("uuid", uuid), zap.String("err", err.Error()))
|
||||
}
|
||||
}
|
||||
fmt.Println("++++++++ DELETE DATABASE ++++++++")
|
||||
|
||||
db, err := database.Instance().DB()
|
||||
if err != nil {
|
||||
zap_server.ZAPLOG.Error(str.Join("get database instance fail:", err.Error()))
|
||||
}
|
||||
if db != nil {
|
||||
db.Close()
|
||||
}
|
||||
|
||||
defer zap_server.Remove()
|
||||
defer operation.Remove()
|
||||
defer casbin.Remove()
|
||||
defer web.Remove()
|
||||
defer database.Remove()
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"pet-house.com/core/g"
|
||||
"pet-house.com/core/helper/str"
|
||||
"pet-house.com/core/server/viper_server"
|
||||
)
|
||||
|
||||
var CONFIG = Web{
|
||||
FileMaxSize: 1024, // upload file size limit 1024M
|
||||
SessionTimeout: 60, // session timeout after 60 Minute
|
||||
Cors: Cors{
|
||||
AccessOrigin: "*",
|
||||
AccessHeaders: "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-U-Id",
|
||||
AccessMethods: "POST,GET",
|
||||
AccessExposeHeaders: "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type",
|
||||
AccessCredentials: "true",
|
||||
},
|
||||
Except: Route{
|
||||
Uri: "",
|
||||
Method: "",
|
||||
},
|
||||
Menu: Route{
|
||||
Uri: "",
|
||||
Method: "",
|
||||
},
|
||||
System: System{
|
||||
Tls: false,
|
||||
Level: "debug",
|
||||
Addr: "127.0.0.1:8085",
|
||||
DbType: "mysql",
|
||||
TimeFormat: "2006-01-02 15:04:05",
|
||||
},
|
||||
Limit: Limit{
|
||||
Disable: true,
|
||||
Limit: 0,
|
||||
Burst: 5,
|
||||
},
|
||||
Captcha: Captcha{
|
||||
KeyLong: 4,
|
||||
ImgWidth: 240,
|
||||
ImgHeight: 80,
|
||||
},
|
||||
}
|
||||
|
||||
type Web struct {
|
||||
FileMaxSize int64 `mapstructure:"file-max-size" json:"file-max-size" yaml:"file-max-siz"`
|
||||
SessionTimeout int64 `mapstructure:"session-timeout" json:"session-timeout" yaml:"session-timeout"`
|
||||
Except Route `mapstructure:"except" json:"except" yaml:"except"`
|
||||
Menu Route `mapstructure:"menu" json:"menu" yaml:"menu"`
|
||||
System System `mapstructure:"system" json:"system" yaml:"system"`
|
||||
Limit Limit `mapstructure:"limit" json:"limit" yaml:"limit"`
|
||||
Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
|
||||
Cors Cors `mapstructure:"cors" json:"cors" yaml:"cors"`
|
||||
}
|
||||
|
||||
type Cors struct {
|
||||
AccessOrigin string `mapstructure:"access-origin" json:"burst" access-origin:"access-origin"`
|
||||
AccessHeaders string `mapstructure:"access-headers" json:"access-headers" yaml:"access-headers"`
|
||||
AccessMethods string `mapstructure:"access-methods" json:"access-methods" yaml:"access-methods"`
|
||||
AccessExposeHeaders string `mapstructure:"access-expose-headers" json:"access-expose-headers" yaml:"access-expose-headers"`
|
||||
AccessCredentials string `mapstructure:"access-credentials" json:"access-credentials" yaml:"access-credentials"`
|
||||
}
|
||||
type Route struct {
|
||||
Uri string `mapstructure:"uri" json:"uri" yaml:"uri"`
|
||||
Method string `mapstructure:"method" json:"method" yaml:"method"`
|
||||
}
|
||||
|
||||
type Captcha struct {
|
||||
KeyLong int `mapstructure:"key-long" json:"key-long" yaml:"key-long"`
|
||||
ImgWidth int `mapstructure:"img-width" json:"img-width" yaml:"img-width"`
|
||||
ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"`
|
||||
}
|
||||
|
||||
type Limit struct {
|
||||
Disable bool `mapstructure:"disable" json:"disable" yaml:"disable"`
|
||||
Limit float64 `mapstructure:"limit" json:"limit" yaml:"limit"`
|
||||
Burst int `mapstructure:"burst" json:"burst" yaml:"burst"`
|
||||
}
|
||||
|
||||
type System struct {
|
||||
Tls bool `mapstructure:"tls" json:"tls" yaml:"tls"` // debug,release,test
|
||||
Level string `mapstructure:"level" json:"level" yaml:"level"` // debug,release,test
|
||||
Addr string `mapstructure:"addr" json:"addr" yaml:"addr"`
|
||||
StaticPrefix string `mapstructure:"static-prefix" json:"static-prefix" yaml:"static-prefix"`
|
||||
WebPrefix string `mapstructure:"web-prefix" json:"web-prefix" yaml:"web-prefix"`
|
||||
DbType string `mapstructure:"db-type" json:"db-type" yaml:"db-type"`
|
||||
TimeFormat string `mapstructure:"time-format" json:"time-format" yaml:"time-format"`
|
||||
}
|
||||
|
||||
// SetDefaultAddrAndTimeFormat
|
||||
func SetDefaultAddrAndTimeFormat() {
|
||||
if CONFIG.System.Addr == "" {
|
||||
CONFIG.System.Addr = "127.0.0.1:8085"
|
||||
}
|
||||
|
||||
if CONFIG.System.TimeFormat == "" {
|
||||
CONFIG.System.TimeFormat = "2006-01-02 15:04:05"
|
||||
}
|
||||
}
|
||||
|
||||
// ToStaticUrl
|
||||
func ToStaticUrl(uri string) string {
|
||||
path := filepath.Join(CONFIG.System.Addr, CONFIG.System.StaticPrefix, uri)
|
||||
if CONFIG.System.Tls {
|
||||
return filepath.ToSlash(str.Join("https://", path))
|
||||
}
|
||||
return filepath.ToSlash(str.Join("http://", path))
|
||||
}
|
||||
|
||||
// IsExist config file is exist
|
||||
func IsExist() bool {
|
||||
return getViperConfig().IsFileExist()
|
||||
}
|
||||
|
||||
// Remove remove config file
|
||||
func Remove() error {
|
||||
return getViperConfig().Remove()
|
||||
}
|
||||
|
||||
// Recover
|
||||
func Recover() error {
|
||||
b, err := json.Marshal(CONFIG)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return getViperConfig().Recover(b)
|
||||
}
|
||||
|
||||
// getViperConfig get viper config
|
||||
func getViperConfig() viper_server.ViperConfig {
|
||||
maxSize := strconv.FormatInt(CONFIG.FileMaxSize, 10)
|
||||
sessionTimeout := strconv.FormatInt(CONFIG.SessionTimeout, 10)
|
||||
keyLong := strconv.FormatInt(int64(CONFIG.Captcha.KeyLong), 10)
|
||||
imgWidth := strconv.FormatInt(int64(CONFIG.Captcha.ImgWidth), 10)
|
||||
imgHeight := strconv.FormatInt(int64(CONFIG.Captcha.ImgHeight), 10)
|
||||
limit := strconv.FormatInt(int64(CONFIG.Limit.Limit), 10)
|
||||
burst := strconv.FormatInt(int64(CONFIG.Limit.Burst), 10)
|
||||
disable := strconv.FormatBool(CONFIG.Limit.Disable)
|
||||
tls := strconv.FormatBool(CONFIG.System.Tls)
|
||||
configName := "web"
|
||||
return viper_server.ViperConfig{
|
||||
Debug: true,
|
||||
Directory: g.ConfigDir,
|
||||
Name: configName,
|
||||
Type: g.ConfigType,
|
||||
Watch: func(vi *viper.Viper) error {
|
||||
if err := vi.Unmarshal(&CONFIG); err != nil {
|
||||
return fmt.Errorf("get Unarshal error: %v", err)
|
||||
}
|
||||
// watch config file change
|
||||
vi.SetConfigName(configName)
|
||||
return nil
|
||||
},
|
||||
//
|
||||
Default: []byte(`
|
||||
{
|
||||
"file-max-size": ` + maxSize + `,
|
||||
"session-timeout": ` + sessionTimeout + `,
|
||||
"except":
|
||||
{
|
||||
"uri": "` + CONFIG.Except.Uri + `",
|
||||
"method": "` + CONFIG.Except.Method + `"
|
||||
},
|
||||
"menu":
|
||||
{
|
||||
"uri": "` + CONFIG.Menu.Uri + `",
|
||||
"method": "` + CONFIG.Menu.Method + `"
|
||||
},
|
||||
"cors":
|
||||
{
|
||||
"access-origin": "` + CONFIG.Cors.AccessOrigin + `",
|
||||
"access-headers": "` + CONFIG.Cors.AccessHeaders + `",
|
||||
"access-methods": "` + CONFIG.Cors.AccessMethods + `",
|
||||
"access-expose-headers": "` + CONFIG.Cors.AccessExposeHeaders + `",
|
||||
"access-credentials": "` + CONFIG.Cors.AccessCredentials + `"
|
||||
},
|
||||
"captcha":
|
||||
{
|
||||
"key-long": ` + keyLong + `,
|
||||
"img-width": ` + imgWidth + `,
|
||||
"img-height": ` + imgHeight + `
|
||||
},
|
||||
"limit":
|
||||
{
|
||||
"limit": ` + limit + `,
|
||||
"disable": ` + disable + `,
|
||||
"burst": ` + burst + `
|
||||
},
|
||||
"system":
|
||||
{
|
||||
"tls": ` + tls + `,
|
||||
"level": "` + CONFIG.System.Level + `",
|
||||
"addr": "` + CONFIG.System.Addr + `",
|
||||
"db-type": "` + CONFIG.System.DbType + `",
|
||||
"time-format": "` + CONFIG.System.TimeFormat + `"
|
||||
}
|
||||
}`),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": "charset=utf8mb4\u0026parseTime=True\u0026loc=Local",
|
||||
"db-name": "iris-admin",
|
||||
"log-mode": false,
|
||||
"log-zap": "error",
|
||||
"max-idle-conns": 0,
|
||||
"max-open-conns": 0,
|
||||
"password": "",
|
||||
"path": "127.0.0.1:3306",
|
||||
"username": "root"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"captcha": {
|
||||
"img-height": 80,
|
||||
"img-width": 240,
|
||||
"key-long": 4
|
||||
},
|
||||
"cors": {
|
||||
"access-credentials": "true",
|
||||
"access-expose-headers": "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type",
|
||||
"access-headers": "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-U-Id",
|
||||
"access-methods": "POST,GET",
|
||||
"access-origin": "*"
|
||||
},
|
||||
"except": {
|
||||
"method": "",
|
||||
"uri": ""
|
||||
},
|
||||
"file-max-size": 1024,
|
||||
"limit": {
|
||||
"burst": 5,
|
||||
"disable": true,
|
||||
"limit": 0
|
||||
},
|
||||
"menu": {
|
||||
"method": "",
|
||||
"uri": ""
|
||||
},
|
||||
"session-timeout": 60,
|
||||
"system": {
|
||||
"addr": "127.0.0.1:8085",
|
||||
"db-type": "mysql",
|
||||
"level": "debug",
|
||||
"time-format": "2006-01-02 15:04:05",
|
||||
"tls": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"director": "logs",
|
||||
"encode-level": "LowercaseColorLevelEncoder",
|
||||
"format": "console",
|
||||
"level": "debug",
|
||||
"link-name": "latest_log",
|
||||
"log-in-console": false,
|
||||
"prefix": "[IRIS-ADMIN]",
|
||||
"show-line": true,
|
||||
"stacktrace-key": "stacktrace"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsExist(t *testing.T) {
|
||||
t.Run("test web config IsExist function", func(t *testing.T) {
|
||||
if !IsExist() {
|
||||
t.Errorf("config's files is not exist.")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Test Remove function", func(t *testing.T) {
|
||||
if err := Remove(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if IsExist() {
|
||||
t.Errorf("config's files remove is fail.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetDefaultAddrAndTimeFormat(t *testing.T) {
|
||||
CONFIG.System.Addr = ""
|
||||
CONFIG.System.TimeFormat = ""
|
||||
t.Run("test set defualt addr and time format", func(t *testing.T) {
|
||||
SetDefaultAddrAndTimeFormat()
|
||||
if CONFIG.System.Addr != "127.0.0.1:8085" {
|
||||
t.Errorf("applyURI want %s but get %s", "127.0.0.1:8085", CONFIG.System.Addr)
|
||||
}
|
||||
if CONFIG.System.TimeFormat != "2006-01-02 15:04:05" {
|
||||
t.Errorf("applyURI want %s but get %s", "2006-01-02 15:04:05", CONFIG.System.TimeFormat)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestToStaticUrl(t *testing.T) {
|
||||
SetDefaultAddrAndTimeFormat()
|
||||
CONFIG.System.StaticPrefix = "/admin"
|
||||
t.Run("test to static url with tls", func(t *testing.T) {
|
||||
CONFIG.System.Tls = true
|
||||
staticPath := ToStaticUrl("/uploads/123.png")
|
||||
if staticPath != "https://127.0.0.1:8085/admin/uploads/123.png" {
|
||||
t.Errorf("applyURI want %s but get %s", "https://127.0.0.1:8085/admin/uploads/123.png", staticPath)
|
||||
}
|
||||
})
|
||||
t.Run("test to static url with tls", func(t *testing.T) {
|
||||
CONFIG.System.Tls = false
|
||||
staticPath := ToStaticUrl("/uploads/123.png")
|
||||
if staticPath != "http://127.0.0.1:8085/admin/uploads/123.png" {
|
||||
t.Errorf("applyURI want %s but get %s", "https://127.0.0.1:8085/admin/uploads/123.png", staticPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"pet-house.com/core/server/viper_server"
|
||||
"pet-house.com/core/server/zap_server"
|
||||
)
|
||||
|
||||
// init
|
||||
func init() {
|
||||
viper_server.Init(getViperConfig())
|
||||
}
|
||||
|
||||
type WebBaseFunc interface {
|
||||
AddWebStatic(staticAbsPath, webPrefix string, paths ...string)
|
||||
AddUploadStatic(staticAbsPath, webPrefix string)
|
||||
InitRouter() error
|
||||
Run()
|
||||
}
|
||||
|
||||
// WebFunc
|
||||
// - GetTestClient
|
||||
// - GetTestLogin
|
||||
// - AddWebStatic
|
||||
// - AddUploadStatic
|
||||
// - Run
|
||||
type WebFunc interface {
|
||||
WebBaseFunc
|
||||
}
|
||||
|
||||
// Start
|
||||
func Start(wf WebFunc) {
|
||||
err := wf.InitRouter()
|
||||
if err != nil {
|
||||
zap_server.ZAPLOG.Error(err.Error())
|
||||
return
|
||||
}
|
||||
wf.Run()
|
||||
}
|
||||
|
||||
// StartTest
|
||||
func StartTest(wf WebFunc) {
|
||||
err := wf.InitRouter()
|
||||
if err != nil {
|
||||
zap_server.ZAPLOG.Error(err.Error())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mattn/go-colorable"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"pet-house.com/core/helper/arr"
|
||||
"pet-house.com/core/helper/dir"
|
||||
"pet-house.com/core/helper/str"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/web/web_gin/middleware"
|
||||
)
|
||||
|
||||
var ErrAuthDriverEmpty = errors.New("auth driver initialize fail")
|
||||
|
||||
// WebServer
|
||||
// - app gin.Engine
|
||||
// - idleConnsClosed
|
||||
// - addr
|
||||
// - timeFormat
|
||||
// - staticPrefix
|
||||
type WebServer struct {
|
||||
app *gin.Engine
|
||||
server
|
||||
addr string
|
||||
timeFormat string
|
||||
webStatics []WebStatic
|
||||
}
|
||||
|
||||
type WebStatic struct {
|
||||
Prefix string
|
||||
IndexFile []byte
|
||||
}
|
||||
|
||||
// Init
|
||||
func Init() *WebServer {
|
||||
gin.SetMode(web.CONFIG.System.Level)
|
||||
app := gin.Default()
|
||||
if web.CONFIG.System.Tls {
|
||||
app.Use(middleware.LoadTls())
|
||||
}
|
||||
app.Use(middleware.Cors())
|
||||
registerValidation()
|
||||
|
||||
gin.DefaultWriter = colorable.NewColorableStdout()
|
||||
|
||||
web.SetDefaultAddrAndTimeFormat()
|
||||
|
||||
return &WebServer{
|
||||
app: app,
|
||||
addr: web.CONFIG.System.Addr,
|
||||
timeFormat: web.CONFIG.System.TimeFormat,
|
||||
}
|
||||
}
|
||||
|
||||
// NoRoute for 404 http status
|
||||
func (ws *WebServer) NoRoute() {
|
||||
if len(ws.webStatics) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ws.app.NoRoute(func(ctx *gin.Context) {
|
||||
// excepte for /v0 /v1 and so on
|
||||
reg := `^/v[0-9]+$|^(/v[0-9]+)/`
|
||||
ok, _ := regexp.MatchString(reg, ctx.Request.RequestURI)
|
||||
if ok {
|
||||
ctx.Writer.WriteHeader(http.StatusNotFound)
|
||||
ctx.Writer.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
var indexFile []byte
|
||||
for _, wp := range ws.webStatics {
|
||||
// match /admin or /admin/***
|
||||
reg := str.Join("^", wp.Prefix, "$|^(", wp.Prefix, ")/")
|
||||
ok, err := regexp.MatchString(reg, ctx.Request.RequestURI)
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
indexFile = wp.IndexFile
|
||||
}
|
||||
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
ctx.Writer.Write(indexFile)
|
||||
|
||||
ctx.Writer.Header().Add("Accept", "text/html")
|
||||
ctx.Writer.Flush()
|
||||
})
|
||||
}
|
||||
|
||||
// GetEngine return *gin.Engine
|
||||
func (ws *WebServer) GetEngine() *gin.Engine {
|
||||
return ws.app
|
||||
}
|
||||
|
||||
// AddWebStatic
|
||||
func (ws *WebServer) AddWebStatic(staticAbsPath, webPrefix string, paths ...string) {
|
||||
webPrefixs := strings.Split(web.CONFIG.System.WebPrefix, ",")
|
||||
wp := arr.NewCheckArrayType(2)
|
||||
for _, webPrefix := range webPrefixs {
|
||||
wp.Add(webPrefix)
|
||||
}
|
||||
if wp.Check(webPrefix) {
|
||||
return
|
||||
}
|
||||
|
||||
favicon := filepath.Join(staticAbsPath, "favicon.ico")
|
||||
index := filepath.Join(staticAbsPath, "index.html")
|
||||
|
||||
ws.app.Static(str.Join(webPrefix, "/favicon.ico"), favicon)
|
||||
ws.app.StaticFile(webPrefix, index)
|
||||
|
||||
if len(paths) > 0 {
|
||||
for _, path := range paths {
|
||||
static := filepath.Join(staticAbsPath, path)
|
||||
ws.app.Static(path, static)
|
||||
}
|
||||
}
|
||||
|
||||
web.CONFIG.System.WebPrefix = str.Join(web.CONFIG.System.WebPrefix, ",", webPrefix)
|
||||
file, _ := dir.ReadBytes(index)
|
||||
webStatic := WebStatic{
|
||||
Prefix: webPrefix,
|
||||
IndexFile: file,
|
||||
}
|
||||
ws.webStatics = append(ws.webStatics, webStatic)
|
||||
|
||||
}
|
||||
|
||||
// AddUploadStatic
|
||||
func (ws *WebServer) AddUploadStatic(webPrefix, staticAbsPath string) {
|
||||
ws.app.StaticFS(webPrefix, http.Dir(staticAbsPath))
|
||||
web.CONFIG.System.StaticPrefix = webPrefix
|
||||
}
|
||||
|
||||
// Run
|
||||
func (ws *WebServer) Run() {
|
||||
ws.NoRoute()
|
||||
s := initServer(web.CONFIG.System.Addr, ws.app)
|
||||
time.Sleep(10 * time.Microsecond)
|
||||
fmt.Printf("默认监听地址: http://%s\n", web.CONFIG.System.Addr)
|
||||
s.ListenAndServe()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"pet-house.com/core/server/database"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/zap_server"
|
||||
)
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
defer zap_server.Remove()
|
||||
defer web.Remove()
|
||||
defer database.Remove()
|
||||
web.CONFIG.System.Addr = "127.0.0.1:18088"
|
||||
go func() {
|
||||
web.Start(Init())
|
||||
}()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
t.Run("test web start", func(t *testing.T) {
|
||||
resp, err := http.Get("http://127.0.0.1:18088")
|
||||
if err != nil {
|
||||
t.Errorf("test web start get %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("test web start get %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("test web start want [%d] but get [%d]", http.StatusNotFound, resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"pet-house.com/core/server/web"
|
||||
)
|
||||
|
||||
// Cors
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
method := c.Request.Method
|
||||
c.Header("Access-Control-Allow-Origin", web.CONFIG.Cors.AccessOrigin)
|
||||
c.Header("Access-Control-Allow-Headers", web.CONFIG.Cors.AccessHeaders)
|
||||
c.Header("Access-Control-Allow-Methods", web.CONFIG.Cors.AccessMethods)
|
||||
c.Header("Access-Control-Expose-Headers", web.CONFIG.Cors.AccessExposeHeaders)
|
||||
c.Header("Access-Control-Allow-Credentials", web.CONFIG.Cors.AccessCredentials)
|
||||
|
||||
if method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/unrolled/secure"
|
||||
)
|
||||
|
||||
// LoadTls
|
||||
func LoadTls() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
middleware := secure.New(secure.Options{
|
||||
SSLRedirect: true,
|
||||
SSLHost: "127.0.0.1:443",
|
||||
})
|
||||
err := middleware.Process(c.Writer, c.Request)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package request
|
||||
|
||||
// Paging common input parameter structure
|
||||
type PageInfo struct {
|
||||
Page int `json:"page" form:"page" binding:"required"`
|
||||
PageSize int `json:"pageSize" form:"pageSize" binding:"required"`
|
||||
OrderBy string `json:"orderBy" form:"orderBy"`
|
||||
SortBy string `json:"sortBy" form:"sortBy"`
|
||||
}
|
||||
|
||||
// Find by id structure
|
||||
type IdBinding struct {
|
||||
Id uint `json:"id" uri:"id" form:"id" binding:"required"`
|
||||
}
|
||||
|
||||
type IdsBinding struct {
|
||||
Ids []uint `json:"ids" form:"ids" binding:"required,dive,required"`
|
||||
}
|
||||
|
||||
type Empty struct{}
|
||||
@@ -0,0 +1,76 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
ResponseOkMessage = "OK"
|
||||
ResponseErrorMessage = "FAIL"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"status"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Msg string `json:"message"`
|
||||
}
|
||||
|
||||
func Result(code int, data interface{}, msg string, ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, Response{code, data, msg})
|
||||
}
|
||||
|
||||
func Ok(ctx *gin.Context) {
|
||||
Result(http.StatusOK, map[string]interface{}{}, ResponseOkMessage, ctx)
|
||||
}
|
||||
|
||||
func OkWithMessage(message string, ctx *gin.Context) {
|
||||
Result(http.StatusOK, map[string]interface{}{}, message, ctx)
|
||||
}
|
||||
|
||||
func OkWithData(data interface{}, ctx *gin.Context) {
|
||||
Result(http.StatusOK, data, ResponseOkMessage, ctx)
|
||||
}
|
||||
|
||||
func OkWithDetailed(data interface{}, message string, ctx *gin.Context) {
|
||||
Result(http.StatusOK, data, message, ctx)
|
||||
}
|
||||
|
||||
func Fail(ctx *gin.Context) {
|
||||
Result(http.StatusBadRequest, map[string]interface{}{}, ResponseErrorMessage, ctx)
|
||||
}
|
||||
|
||||
func UnauthorizedFailWithMessage(message string, ctx *gin.Context) {
|
||||
Result(http.StatusUnauthorized, map[string]interface{}{}, message, ctx)
|
||||
}
|
||||
|
||||
func UnauthorizedFailWithDetailed(data interface{}, message string, ctx *gin.Context) {
|
||||
Result(http.StatusUnauthorized, data, message, ctx)
|
||||
}
|
||||
|
||||
func ForbiddenFailWithMessage(message string, ctx *gin.Context) {
|
||||
Result(http.StatusForbidden, map[string]interface{}{}, message, ctx)
|
||||
}
|
||||
|
||||
func FailWithMessage(message string, ctx *gin.Context) {
|
||||
Result(http.StatusBadRequest, map[string]interface{}{}, message, ctx)
|
||||
}
|
||||
|
||||
func FailWithDetailed(data interface{}, message string, ctx *gin.Context) {
|
||||
Result(http.StatusBadRequest, data, message, ctx)
|
||||
}
|
||||
|
||||
type PageResult struct {
|
||||
List interface{} `json:"list,omitempty"`
|
||||
Total int64 `json:"total"`
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
type BaseResponse struct {
|
||||
Id uint `json:"id"`
|
||||
CreatedAt *time.Time `json:"createdAt"`
|
||||
UpdatedAt *time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
limit "github.com/aviddiviner/gin-limit"
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
"pet-house.com/core/helper/arr"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/web/web_gin/middleware"
|
||||
)
|
||||
|
||||
func (ws *WebServer) GetRouterGroup(relativePath string) *gin.RouterGroup {
|
||||
return ws.app.Group(relativePath)
|
||||
}
|
||||
|
||||
// InitRouter
|
||||
func (ws *WebServer) InitRouter() error {
|
||||
ws.app.Use(limit.MaxAllowed(50))
|
||||
if web.CONFIG.System.Level == "debug" {
|
||||
pprof.Register(ws.app)
|
||||
}
|
||||
router := ws.app.Group("/")
|
||||
{
|
||||
router.Use(middleware.Cors())
|
||||
|
||||
router.GET("/v0/version", func(ctx *gin.Context) {
|
||||
ctx.String(http.StatusOK, "IRIS-ADMIN is running!!!")
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSources
|
||||
// - PermRoutes
|
||||
// - NoPermRoutes
|
||||
func (ws *WebServer) GetSources() ([]map[string]string, []map[string]string) {
|
||||
|
||||
methodExcepts := strings.Split(web.CONFIG.Except.Method, ";")
|
||||
uriExcepts := strings.Split(web.CONFIG.Except.Uri, ";")
|
||||
methodMenus := strings.Split(web.CONFIG.Menu.Method, ";")
|
||||
uriMenus := strings.Split(web.CONFIG.Menu.Uri, ";")
|
||||
|
||||
routeLen := len(ws.app.Routes())
|
||||
permRoutes := make([]map[string]string, 0, routeLen)
|
||||
noPermRoutes := make([]map[string]string, 0, routeLen)
|
||||
|
||||
for _, r := range ws.app.Routes() {
|
||||
bases := strings.Split(filepath.Base(r.Handler), ".")
|
||||
if len(bases) != 2 {
|
||||
continue
|
||||
}
|
||||
path := filepath.ToSlash(filepath.Clean(r.Path))
|
||||
route := map[string]string{
|
||||
"path": path,
|
||||
"desc": bases[1],
|
||||
"group": bases[0],
|
||||
"method": r.Method,
|
||||
"is_menu": "0",
|
||||
}
|
||||
if len(methodMenus) > 0 && len(uriMenus) > 0 && len(methodMenus) == len(uriMenus) {
|
||||
for i := 0; i < len(methodMenus); i++ {
|
||||
if strings.EqualFold(r.Method, strings.ToLower(methodMenus[i])) && strings.EqualFold(path, strings.ToLower(uriMenus[i])) {
|
||||
route["is_menu"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpStatusType := arr.NewCheckArrayType(4)
|
||||
httpStatusType.AddMutil(http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete)
|
||||
if !httpStatusType.Check(r.Method) {
|
||||
noPermRoutes = append(noPermRoutes, route)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(methodExcepts) > 0 && len(uriExcepts) > 0 && len(methodExcepts) == len(uriExcepts) {
|
||||
for i := 0; i < len(methodExcepts); i++ {
|
||||
if strings.EqualFold(r.Method, strings.ToLower(methodExcepts[i])) && strings.EqualFold(path, strings.ToLower(uriExcepts[i])) {
|
||||
noPermRoutes = append(noPermRoutes, route)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
permRoutes = append(permRoutes, route)
|
||||
}
|
||||
return permRoutes, noPermRoutes
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package web_gin
|
||||
|
||||
type server interface {
|
||||
ListenAndServe() error
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/fvbock/endless"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
s := endless.NewServer(address, router)
|
||||
s.BeforeBegin = func(add string) {
|
||||
fmt.Printf("Actual pid is %d\n", syscall.Getpid())
|
||||
// save it somehow
|
||||
}
|
||||
s.ReadHeaderTimeout = 10 * time.Millisecond
|
||||
s.WriteTimeout = 10 * time.Second
|
||||
s.MaxHeaderBytes = 1 << 20
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fvbock/endless"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
s := endless.NewServer(address, router)
|
||||
s.ReadHeaderTimeout = 10 * time.Millisecond
|
||||
s.WriteTimeout = 10 * time.Second
|
||||
s.MaxHeaderBytes = 1 << 20
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func initServer(address string, router *gin.Engine) server {
|
||||
return &http.Server{
|
||||
Addr: address,
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package web_gin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"pet-house.com/core/server/web"
|
||||
)
|
||||
|
||||
func registerValidation() {
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
v.RegisterValidation("dev-required", validateDevRequired)
|
||||
}
|
||||
}
|
||||
|
||||
var validateDevRequired validator.Func = func(fl validator.FieldLevel) bool {
|
||||
if web.CONFIG.System.Level == "release" {
|
||||
return fl.Field().String() != ""
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": "charset=utf8mb4\u0026parseTime=True\u0026loc=Local",
|
||||
"db-name": "iris-admin",
|
||||
"log-mode": false,
|
||||
"log-zap": "error",
|
||||
"max-idle-conns": 0,
|
||||
"max-open-conns": 0,
|
||||
"password": "",
|
||||
"path": "127.0.0.1:3306",
|
||||
"username": "root"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"captcha": {
|
||||
"img-height": 80,
|
||||
"img-width": 240,
|
||||
"key-long": 4
|
||||
},
|
||||
"cors": {
|
||||
"access-credentials": "true",
|
||||
"access-expose-headers": "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type",
|
||||
"access-headers": "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-U-Id",
|
||||
"access-methods": "POST,GET",
|
||||
"access-origin": "*"
|
||||
},
|
||||
"except": {
|
||||
"method": "",
|
||||
"uri": ""
|
||||
},
|
||||
"file-max-size": 1024,
|
||||
"limit": {
|
||||
"burst": 5,
|
||||
"disable": true,
|
||||
"limit": 0
|
||||
},
|
||||
"menu": {
|
||||
"method": "",
|
||||
"uri": ""
|
||||
},
|
||||
"session-timeout": 60,
|
||||
"system": {
|
||||
"addr": "127.0.0.1:8085",
|
||||
"db-type": "mysql",
|
||||
"level": "debug",
|
||||
"time-format": "2006-01-02 15:04:05",
|
||||
"tls": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"director": "logs",
|
||||
"encode-level": "LowercaseColorLevelEncoder",
|
||||
"format": "console",
|
||||
"level": "debug",
|
||||
"link-name": "latest_log",
|
||||
"log-in-console": false,
|
||||
"prefix": "[IRIS-ADMIN]",
|
||||
"show-line": true,
|
||||
"stacktrace-key": "stacktrace"
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package web_iris
|
||||
|
||||
import (
|
||||
stdContext "context"
|
||||
"errors"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/middleware/recover"
|
||||
"pet-house.com/core/helper/arr"
|
||||
"pet-house.com/core/helper/str"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/web/web_iris/middleware"
|
||||
)
|
||||
|
||||
var ErrAuthDriverEmpty = errors.New("auth driver initialize fail")
|
||||
|
||||
// WebServer
|
||||
// - app iris application
|
||||
// - idleConnsClosed
|
||||
// - addr
|
||||
// - timeFormat
|
||||
// - staticPrefix
|
||||
|
||||
type WebServer struct {
|
||||
app *iris.Application
|
||||
idleConnsClosed chan struct{}
|
||||
parties []Party
|
||||
addr string
|
||||
timeFormat string
|
||||
}
|
||||
|
||||
// Party
|
||||
// - perfix
|
||||
// - partyFunc
|
||||
type Party struct {
|
||||
Perfix string
|
||||
PartyFunc func(index iris.Party)
|
||||
}
|
||||
|
||||
func Init() *WebServer {
|
||||
app := iris.New()
|
||||
if web.CONFIG.System.Tls {
|
||||
app.Use(middleware.LoadTls())
|
||||
}
|
||||
app.Use(recover.New())
|
||||
app.Validator = validator.New()
|
||||
app.Logger().SetLevel(web.CONFIG.System.Level)
|
||||
idleConnsClosed := make(chan struct{})
|
||||
iris.RegisterOnInterrupt(func() {
|
||||
timeout := 10 * time.Second
|
||||
ctx, cancel := stdContext.WithTimeout(stdContext.Background(), timeout)
|
||||
defer cancel()
|
||||
app.Shutdown(ctx) // close all hosts
|
||||
close(idleConnsClosed)
|
||||
})
|
||||
// 自定义应用程序配置
|
||||
_ = iris.Configuration{
|
||||
DisablePathCorrection: true,
|
||||
DisableBodyConsumptionOnUnmarshal: true,
|
||||
Charset: "UTF-8",
|
||||
}
|
||||
app.Configure(iris.WithCharset("UTF-8"), iris.WithOptimizations)
|
||||
web.SetDefaultAddrAndTimeFormat()
|
||||
|
||||
return &WebServer{
|
||||
app: app,
|
||||
addr: web.CONFIG.System.Addr,
|
||||
timeFormat: web.CONFIG.System.TimeFormat,
|
||||
idleConnsClosed: idleConnsClosed,
|
||||
}
|
||||
}
|
||||
|
||||
func (ws *WebServer) GetEngine() *iris.Application {
|
||||
return ws.app
|
||||
}
|
||||
|
||||
func (ws *WebServer) AddModule(parties ...Party) {
|
||||
ws.parties = append(ws.parties, parties...)
|
||||
}
|
||||
|
||||
func (ws *WebServer) AddFrontFunc(f func(ctx *context.Context)) {
|
||||
ws.app.Use(f)
|
||||
}
|
||||
|
||||
func (ws *WebServer) AddWebStatic(staticAbsPath, webPrefix string, paths ...string) {
|
||||
webPrefixs := strings.Split(web.CONFIG.System.WebPrefix, ",")
|
||||
wp := arr.NewCheckArrayType(2)
|
||||
for _, webPrefix := range webPrefixs {
|
||||
wp.Add(webPrefix)
|
||||
}
|
||||
if wp.Check(webPrefix) {
|
||||
return
|
||||
}
|
||||
|
||||
fsOrDir := iris.Dir(staticAbsPath)
|
||||
opt := iris.DirOptions{
|
||||
IndexName: "index.html",
|
||||
SPA: true,
|
||||
}
|
||||
ws.app.HandleDir(webPrefix, fsOrDir, opt)
|
||||
web.CONFIG.System.WebPrefix = str.Join(web.CONFIG.System.WebPrefix, ",", webPrefix)
|
||||
}
|
||||
|
||||
func (ws *WebServer) AddUploadStatic(webPrefix, staticAbsPath string) {
|
||||
fsOrDir := iris.Dir(staticAbsPath)
|
||||
ws.app.HandleDir(webPrefix, fsOrDir)
|
||||
web.CONFIG.System.StaticPrefix = webPrefix
|
||||
}
|
||||
|
||||
func (ws *WebServer) Run() {
|
||||
ws.app.Listen(
|
||||
ws.addr,
|
||||
iris.WithoutInterruptHandler,
|
||||
iris.WithoutServerError(iris.ErrServerClosed),
|
||||
iris.WithOptimizations,
|
||||
iris.WithTimeFormat(ws.timeFormat),
|
||||
)
|
||||
<-ws.idleConnsClosed
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package web_iris
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"pet-house.com/core/server/database"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/zap_server"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
defer zap_server.Remove()
|
||||
defer web.Remove()
|
||||
defer database.Remove()
|
||||
web.CONFIG.System.Addr = "127.0.0.1:18085"
|
||||
go func() {
|
||||
web.Start(Init())
|
||||
}()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
t.Run("test web run", func(t *testing.T) {
|
||||
resp, err := http.Get("http://127.0.0.1:18085/v0/version")
|
||||
if err != nil {
|
||||
t.Errorf("test web start get %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
s, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("test web start get %v", err)
|
||||
}
|
||||
if string(s) != "IRIS-ADMIN is running!!!" {
|
||||
t.Errorf("test web start want %s but get %s", "Not Found", string(s))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/iris-contrib/middleware/cors"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
// CrsAuth
|
||||
func CrsAuth() context.Handler {
|
||||
return cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts.
|
||||
AllowedMethods: []string{"POST", "GET"},
|
||||
AllowedHeaders: []string{"*"},
|
||||
ExposedHeaders: []string{"Accept", "Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization"},
|
||||
AllowCredentials: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/iris-contrib/middleware/secure"
|
||||
"github.com/kataras/iris/v12"
|
||||
)
|
||||
|
||||
// LoadTls
|
||||
func LoadTls() iris.Handler {
|
||||
middleware := secure.New(secure.Options{
|
||||
SSLRedirect: true,
|
||||
SSLHost: "127.0.0.1:443",
|
||||
})
|
||||
return middleware.Handler
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package web_iris
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/middleware/pprof"
|
||||
"github.com/kataras/iris/v12/middleware/rate"
|
||||
"github.com/kataras/iris/v12/middleware/recover"
|
||||
"pet-house.com/core/helper/arr"
|
||||
"pet-house.com/core/server/web"
|
||||
"pet-house.com/core/server/web/web_iris/middleware"
|
||||
)
|
||||
|
||||
// InitRouter
|
||||
func (ws *WebServer) InitRouter() error {
|
||||
app := ws.app.Party("/").AllowMethods(iris.MethodOptions)
|
||||
{
|
||||
app.Get("/v0/version", func(ctx iris.Context) {
|
||||
ctx.WriteString("IRIS-ADMIN is running!!!")
|
||||
})
|
||||
|
||||
app.UseRouter(middleware.CrsAuth())
|
||||
app.UseRouter(recover.New())
|
||||
if !web.CONFIG.Limit.Disable {
|
||||
limitV1 := rate.Limit(web.CONFIG.Limit.Limit, web.CONFIG.Limit.Burst, rate.PurgeEvery(time.Minute, 5*time.Minute))
|
||||
app.Use(limitV1)
|
||||
}
|
||||
if web.CONFIG.System.Level == "debug" {
|
||||
debug := func(index iris.Party) {
|
||||
index.Get("/", func(ctx iris.Context) {
|
||||
ctx.HTML("<h1>请点击<a href='/debug/pprof'>这里</a>打开调试页面")
|
||||
})
|
||||
index.Any("/pprof", pprof.New())
|
||||
index.Any("/pprof/{action:path}", pprof.New())
|
||||
}
|
||||
app.PartyFunc("/debug", debug)
|
||||
}
|
||||
|
||||
for _, party := range ws.parties {
|
||||
app.PartyFunc(party.Perfix, party.PartyFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// http test must build
|
||||
if err := ws.app.Build(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSources
|
||||
// - PermRoutes
|
||||
// - NoPermRoutes
|
||||
func (ws *WebServer) GetSources() ([]map[string]string, []map[string]string) {
|
||||
methodExcepts := strings.Split(web.CONFIG.Except.Method, ";")
|
||||
uris := strings.Split(web.CONFIG.Except.Uri, ";")
|
||||
|
||||
methodMenus := strings.Split(web.CONFIG.Menu.Method, ";")
|
||||
uriMenus := strings.Split(web.CONFIG.Menu.Uri, ";")
|
||||
|
||||
routeLen := len(ws.app.GetRoutes())
|
||||
permRoutes := make([]map[string]string, 0, routeLen)
|
||||
noPermRoutes := make([]map[string]string, 0, routeLen)
|
||||
|
||||
for _, r := range ws.app.GetRoutes() {
|
||||
route := map[string]string{
|
||||
"path": r.Path,
|
||||
"name": r.Name,
|
||||
"act": r.Method,
|
||||
}
|
||||
if len(methodMenus) > 0 && len(uriMenus) > 0 && len(methodMenus) == len(uriMenus) {
|
||||
for i := 0; i < len(methodMenus); i++ {
|
||||
if strings.EqualFold(r.Method, strings.ToLower(methodMenus[i])) && strings.EqualFold(r.Path, strings.ToLower(uriMenus[i])) {
|
||||
route["is_menu"] = "1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
httpStatusType := arr.NewCheckArrayType(4)
|
||||
httpStatusType.AddMutil(http.MethodGet, http.MethodPost)
|
||||
if !httpStatusType.Check(r.Method) {
|
||||
noPermRoutes = append(noPermRoutes, route)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(methodExcepts) > 0 && len(uris) > 0 && len(methodExcepts) == len(uris) {
|
||||
for i := 0; i < len(methodExcepts); i++ {
|
||||
if strings.EqualFold(r.Method, strings.ToLower(methodExcepts[i])) && strings.EqualFold(r.Path, strings.ToLower(uris[i])) {
|
||||
noPermRoutes = append(noPermRoutes, route)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permRoutes = append(permRoutes, route)
|
||||
}
|
||||
return permRoutes, noPermRoutes
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// ValidRequest
|
||||
func ValidRequest(err interface{}) []string {
|
||||
var errs []string
|
||||
if err == nil {
|
||||
return errs
|
||||
}
|
||||
if validateErrs, ok := err.(validator.ValidationErrors); ok {
|
||||
for _, e := range validateErrs {
|
||||
e := e
|
||||
sErr := fmt.Errorf("%s param error: %v", e.Namespace(), e.Value())
|
||||
errs = append(errs, sErr.Error())
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
Reference in New Issue
Block a user