Browse Source

init

pull/1/head
wailovet 6 years ago
commit
cb49025e3c
  1. 16
      .gitignore
  2. 257
      README.md
  3. 43
      example/app/wstest.go
  4. 8
      example/config.json
  5. 13
      example/main.go
  6. 11
      example/private.json
  7. 157
      osmanthuswine.go
  8. 122
      src/core/config.go
  9. 25
      src/core/controller.go
  10. 121
      src/core/db.go
  11. 36
      src/core/redis.go
  12. 117
      src/core/request.go
  13. 115
      src/core/response.go
  14. 118
      src/core/router.go
  15. 51
      src/core/websocket.go
  16. 71
      src/core/xorm.go
  17. 30
      src/helper/db2struct.go
  18. 19
      src/helper/log.go
  19. 10
      src/helper/md5.go
  20. 8
      src/helper/uuid.go
  21. 11
      src/helper/uuid_test.go
  22. 21
      src/interfaces/websocket.go
  23. 50
      src/session/securecookie.go

16
.gitignore

@ -0,0 +1,16 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.idea
dev.bat
test.go

257
README.md

@ -0,0 +1,257 @@
## 内部使用基于go-chi的web框架
# 框架引入
> go get -u github.com/wailovet/osmanthuswine
### 目录结构
```
app
|--index
|--index.go
html
|--静态文件....
main.go
config.json
```
# 开始
#### 创建以上目录结构
+ /config.json 配置文件
```json
{
"port": "8808",
"host": "0.0.0.0",
"cross_domain": "*",
"post_max_memory": 1024000,
"update_path": "new_exe",
"db": {
"host": "",
"port": "",
"user": "",
"password": "",
"name": "",
"max_open_conn": 500
}
}
```
+ /main.go文件
```
package main
import (
"./app/index"
"github.com/wailovet/osmanthuswine"
"github.com/wailovet/osmanthuswine/src/core"
)
func main() {
//注册index控制器
core.GetInstanceRouterManage().Registered(&index.Index{})
//主程序执行
osmanthuswine.Run()
}
```
+ /app/index/index.go文件
```
package index
import (
"github.com/wailovet/osmanthuswine/src/core"
)
type Index struct {
core.Controller
}
func (that *Index) Index() {
that.DisplayByData(that.Request.REQUEST)
}
```
## core.Controller.Request 使用说明
#### 输入参数
+ that.Request.GET["参数"] //类型:map[string]string
+ that.Request.POST["参数"] //类型:map[string]string
+ that.Request.REQUEST["参数"] //类型:map[string]string , 为GET以及POST的合并值,当出现值冲突时GET参数会被覆盖
#### session与cookie获取
+ that.Request.SESSION["参数"] //类型:map[string]string
+ that.Request.COOKIE["参数"] //类型:map[string]string
#### header信息获取
+ that.Request.HEADER["参数"] //类型:map[string]string
#### 该取值一般以POST-RAW形式传入原始数据,有可能
+ that.Request.BODY //类型:string
#### 快速获取上传的文件
+ that.Request.FILE //类型:*multipart.FileHeader
#### 获取上传的所有文件
+ that.Request.FILES //类型:map[string][]*multipart.FileHeader
## core.Controller 使用说明
#### 输出显示
> 即使输出时不处于函数结尾,也无需return
+ that.DisplayByData(data interface{})
```
{
"code":0,
"data":data,
"msg":""
}
```
- - -
+ that.DisplayBySuccess(msg string)
```
{
"code":0,
"data":null,
"msg":msg
}
```
- - -
+ that.DisplayByError(msg string, code int)
```
{
"code":code,
"data":null,
"msg":msg
}
```
- - -
+ that.Display(data interface{}, msg string, code int)
```
{
"code":code,
"data":data,
"msg":msg
}
```
- - -
+ that.DisplayByString(data string)
```
data //直接输出data以string形式
```
- - -
+ that.DisplayByRaw(data []byte)
```
data //直接输出data以[]byte形式,可用于直接输出二进制文件
```
- - -
##### 20190416新增
+ that.CheckErrDisplayByError(err error,msg...)
```
err //错误信息,自动判断是否等于nil,如果等于nil该语句会被忽略
msg //错误文案提示,不填直接输出err.Error()
```
#### session操作
> 目前session实现基于securecookie,以加密形式储存在cookie中,注意不要存放大量数据,以免超过cookie的最大储存值
+ that.SetSession(name string, value string) //设置session
+ that.DeleteSession(name string) //删除session
+ that.ClearSession() //清空session
#### cookie操作
> 尽量以session的形式操作
+ that.SetCookie(name string, value string)
## 数据库操作
> 目前框架中集成gorm与xorm框架
+ core.GetXormAuto() //获取xorm实例
+ core.GetGormAuto() //获取gorm实例
#### 数据库配置
```
实例的数据库配置来自于相同目录下的config.json或者private.json文件
{
...其他配置
"db": {
"host": "",
"port": "",
"user": "",
"password": "",
"name": "",
"prefix": "",
"max_open_conn": 500
}
}
prefix为表前缀
max_open_conn为可支持最大连接数(未测试是否可用
```
## 支持WebSocket
> 当传入core.GetInstanceRouterManage().Registered的对象继承自core.WebSocket时,协议升级为websocket,路由地址忽略最后方法名
#### 集成melody库,使用详情https://github.com/olahol/melody
```
package index
import (
"github.com/wailovet/osmanthuswine/src/core"
"gopkg.in/olahol/melody.v1"
)
type Index struct {
core.WebSocket
}
func (that *Wstest) HandleConnect(session *melody.Session) {
//implement
}
func (that *Wstest) HandlePong(session *melody.Session) {
//implement
}
func (that *Wstest) HandleMessage(session *melody.Session, data []byte) {
that.GetMelody().Broadcast(data)
//implement
}
func (that *Wstest) HandleMessageBinary(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleSentMessage(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleSentMessageBinary(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleDisconnect(session *melody.Session) {
//implement
}
func (that *Wstest) HandleError(session *melody.Session, err error) {
//implement
}
```
```
//javascript
var ws = new WebSocket("ws://127.0.0.1/Api/Index/Index")
```
> PS:不同url对应不同的melody实例
## 杂项
> 热更新,仅支持linux
```
默认情况下,检测同路径下的<文件名_update>,如果该文件与当前文件不一致,则进行热更,已连接的连接无需断连
可在config.json中配置检测的文件名
{
...其他配置
"update_path": "需要检测的文件路径"
}
备注:需要检测的文件路径最好不要与当前运行的文件路径相同
```

43
example/app/wstest.go

@ -0,0 +1,43 @@
package app
import (
"github.com/wailovet/osmanthuswine/src/core"
"gopkg.in/olahol/melody.v1"
)
type Wstest struct {
core.WebSocket
}
func (that *Wstest) HandleConnect(session *melody.Session) {
//implement
}
func (that *Wstest) HandlePong(session *melody.Session) {
//implement
}
func (that *Wstest) HandleMessage(session *melody.Session, data []byte) {
that.GetMelody().Broadcast(data)
//implement
}
func (that *Wstest) HandleMessageBinary(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleSentMessage(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleSentMessageBinary(session *melody.Session, data []byte) {
//implement
}
func (that *Wstest) HandleDisconnect(session *melody.Session) {
//implement
}
func (that *Wstest) HandleError(session *melody.Session, err error) {
//implement
}

8
example/config.json

@ -0,0 +1,8 @@
{
"port": "20180",
"host": "0.0.0.0",
"cross_domain": "*",
"post_max_memory": 1024000,
"api_router": "/Api/*",
"update_path": "example_update"
}

13
example/main.go

@ -0,0 +1,13 @@
package main
import (
"github.com/wailovet/osmanthuswine"
"github.com/wailovet/osmanthuswine/example/app"
"github.com/wailovet/osmanthuswine/src/core"
)
func main() {
core.GetInstanceRouterManage().Registered(&app.Wstest{})
core.GetInstanceRouterManage().Registered(&app.Test{})
osmanthuswine.Run()
}

11
example/private.json

@ -0,0 +1,11 @@
{
"db": {
"host": "",
"port": "",
"user": "",
"password": "",
"name": "",
"prefix": "",
"max_open_conn": 500
}
}

157
osmanthuswine.go

@ -0,0 +1,157 @@
package osmanthuswine
import (
"errors"
"fmt"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/wailovet/osmanthuswine/src/core"
"github.com/wailovet/osmanthuswine/src/helper"
"github.com/wailovet/osmanthuswine/src/session"
"github.com/wailovet/overseer"
"github.com/wailovet/overseer/fetcher"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"runtime/debug"
"strings"
"time"
)
var chiRouter *chi.Mux
func GetChiRouter() *chi.Mux {
if chiRouter == nil {
chiRouter = chi.NewRouter()
chiRouter.Use(middleware.RequestID)
chiRouter.Use(middleware.RealIP)
chiRouter.Use(middleware.Logger)
chiRouter.Use(middleware.Recoverer)
chiRouter.Use(middleware.Timeout(60 * time.Second))
}
return chiRouter
}
func Run() {
path, _ := GetCurrentPath()
os.Chdir(path)
log.Println("工作目录:", path)
cc := core.GetInstanceConfig()
if runtime.GOOS == "windows" || cc.UpdatePath == "" {
listener, err := net.Listen("tcp", cc.Host+":"+cc.Port)
if err != nil {
log.Fatal(err.Error())
}
RunProg(overseer.State{
Listener: listener,
})
} else {
overseer.Run(overseer.Config{
Program: RunProg,
Address: cc.Host + ":" + cc.Port,
Fetcher: &fetcher.File{
Path: cc.UpdateDir + cc.UpdatePath,
Interval: time.Second * 10,
},
})
}
}
func RunProg(state overseer.State) {
cc := core.GetInstanceConfig()
helper.GetInstanceLog().Out("开始监听:", cc.Host+":"+cc.Port)
r := GetChiRouter()
apiRouter := cc.ApiRouter
r.HandleFunc(apiRouter, func(writer http.ResponseWriter, request *http.Request) {
requestData := core.Request{}
sessionMan := session.New(request, writer)
requestData.REQUEST = make(map[string]string)
//GET
requestData.SyncGetData(request)
//POST
requestData.SyncPostData(request, cc.PostMaxMemory)
//HEADER
requestData.SyncHeaderData(request)
//COOKIE
requestData.SyncCookieData(request)
//SESSION
requestData.SyncSessionData(sessionMan)
responseHandle := core.Response{OriginResponseWriter: writer, Session: sessionMan}
defer func() {
errs := recover()
if errs == nil {
return
}
errtxt := fmt.Sprintf("%v", errs)
if errtxt != "" {
responseHandle.DisplayByError(errtxt, 500, strings.Split(string(debug.Stack()), "\n\t")...)
}
}()
core.GetInstanceRouterManage().RouterSend(request.URL.Path, requestData, responseHandle, cc.CrossDomain)
})
r.HandleFunc("/*", func(writer http.ResponseWriter, request *http.Request) {
path := request.URL.Path
if path == "/" {
path = "/index.html"
}
helper.GetInstanceLog().Out("静态文件:", "./html"+path)
f, err := os.Stat("./html" + path)
if err == nil {
if f.IsDir() {
path += "/index.html"
}
data, err := ioutil.ReadFile("./html" + path)
if err == nil {
writer.Write(data)
return
}
}
writer.WriteHeader(404)
writer.Write([]byte(err.Error()))
})
http.Serve(state.Listener, r)
//http.ListenAndServe(cc.Host+":"+cc.Port, r)
}
func GetCurrentPath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
path, err := filepath.Abs(file)
if err != nil {
return "", err
}
i := strings.LastIndex(path, "/")
if i < 0 {
i = strings.LastIndex(path, "\\")
}
if i < 0 {
return "", errors.New(`error: Can't find "/" or "\".`)
}
return string(path[0 : i+1]), nil
}

122
src/core/config.go

@ -0,0 +1,122 @@
package core
import (
"encoding/json"
"io/ioutil"
"log"
"os"
)
type Config struct {
Port string `json:"port"`
Host string `json:"host"`
CrossDomain string `json:"cross_domain"`
ApiRouter string `json:"api_router"`
PostMaxMemory int64 `json:"post_max_memory"`
Db struct {
Host string `json:"host"`
Port string `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Name string `json:"name"`
Prefix string `json:"prefix"`
MaxOpenConn int `json:"max_open_conn"`
Params map[string]string `json:"params"`
Debug bool `json:"debug"`
} `json:"db"`
Redis struct {
Addr string `json:"addr"`
Password string `json:"password"`
Db int `json:"db"`
} `json:"redis"`
UpdateDir string `json:"update_dir"`
UpdatePath string `json:"update_path"`
}
var instanceConfig *Config
var configFile = "./config.json"
var privateConfigFile = "./private.json"
func SetConfigFile(c string) {
configFile = c
}
func SetConfig(c *Config) {
instanceConfig = c
}
func GetInstanceConfig() *Config {
if instanceConfig == nil {
instanceConfig = &Config{
Host: "localhost",
Port: "8808",
ApiRouter: "/Api/*",
CrossDomain: "*",
PostMaxMemory: 1024 * 1024 * 10,
Db: struct {
Host string `json:"host"`
Port string `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Name string `json:"name"`
Prefix string `json:"prefix"`
MaxOpenConn int `json:"max_open_conn"`
Params map[string]string `json:"params"`
Debug bool `json:"debug"`
}{
Host: "localhost",
Port: "3306",
User: "root",
Password: "root",
Name: "test",
Prefix: "",
MaxOpenConn: 500,
Params: map[string]string{
"charset": "utf8mb4",
"parseTime": "true",
},
Debug: true,
},
Redis: struct {
Addr string `json:"addr"`
Password string `json:"password"`
Db int `json:"db"`
}{
Addr: "localhost:6379",
Password: "",
Db: 0,
},
UpdateDir: "",
UpdatePath: os.Args[0] + "_update",
}
instanceConfig.ReadConfig(configFile)
instanceConfig.ReadPrivateConfig(privateConfigFile)
}
return instanceConfig
}
func (c *Config) ReadConfig(file string) {
configText, err := ioutil.ReadFile(file)
if err != nil {
log.Println("配置文件错误,启动失败:", err.Error())
os.Exit(0)
}
err = json.Unmarshal(configText, c)
if err != nil {
log.Println("配置文件错误,启动失败:", err.Error())
os.Exit(0)
}
}
func (c *Config) ReadPrivateConfig(file string) {
configText, err := ioutil.ReadFile(file)
if err != nil {
log.Println("未加载", privateConfigFile, ":", err.Error())
return
}
err = json.Unmarshal(configText, c)
if err != nil {
log.Println("未加载", privateConfigFile, ":", err.Error())
}
}

25
src/core/controller.go

@ -0,0 +1,25 @@
package core
import "encoding/json"
type Controller struct {
Response
Request Request
}
func (c *Controller) ControllerInit(req Request, res Response) {
c.Request = req
c.Session = res.Session
c.OriginResponseWriter = res.OriginResponseWriter
}
func (c *Controller) RequestToStruct(v interface{}) error {
if c.Request.BODY != "" {
err := json.Unmarshal([]byte(c.Request.BODY), v)
if err == nil {
return err
}
}
data, _ := json.Marshal(c.Request.REQUEST)
return json.Unmarshal(data, v)
}

121
src/core/db.go

@ -0,0 +1,121 @@
package core
import (
"fmt"
"github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"strings"
"time"
)
var instanceDb *gorm.DB
func GetDb() (*gorm.DB, error) {
if instanceDb == nil {
config := GetInstanceConfig()
mysqlConfig := mysql.NewConfig()
mysqlConfig.User = config.Db.User
mysqlConfig.DBName = config.Db.Name
mysqlConfig.Passwd = config.Db.Password
mysqlConfig.Params = config.Db.Params
mysqlConfig.Net = "tcp"
mysqlConfig.Addr = config.Db.Host + ":" + config.Db.Port
db, err := gorm.Open("mysql", mysqlConfig.FormatDSN())
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
if len(defaultTableName) > len(config.Db.Prefix) && defaultTableName[:len(config.Db.Prefix)] == config.Db.Prefix {
return defaultTableName
}
return config.Db.Prefix + defaultTableName
}
db.DB().SetMaxOpenConns(config.Db.MaxOpenConn)
db.SingularTable(true)
instanceDb = db
return instanceDb, err
}
return instanceDb, nil
}
func GetGormAuto() *gorm.DB {
return GetDbAuto()
}
func GetDbAuto() *gorm.DB {
db, err := GetDb()
if err != nil {
panic("数据库访问错误")
}
return db
}
func DbQuery(query string, args ...interface{}) []map[string]interface{} {
db := GetDbAuto()
rows, _ := db.DB().Query(query, args...) // Note: Ignoring errors for brevity
defer rows.Close()
cols, _ := rows.Columns()
var data []map[string]interface{}
for rows.Next() {
columns := make([]interface{}, len(cols))
columnPointers := make([]interface{}, len(cols))
for i, _ := range columns {
columnPointers[i] = &columns[i]
}
// Scan the result into the column pointers...
if err := rows.Scan(columnPointers...); err != nil {
panic(err)
}
// Create our map, and retrieve the value for each column from the pointers slice,
// storing it in the map with the name of the column as the key.
m := make(map[string]interface{})
for i, colName := range cols {
val := columnPointers[i].(*interface{})
m[colName] = *val
}
data = append(data, m)
}
return data
}
var isUpdateComment = make(map[string]bool)
func GetDbAutoMigrate(values ...interface{}) *gorm.DB {
db := GetDbAuto()
db.AutoMigrate(values...)
for _, value := range values {
scope := db.NewScope(value)
tableName := scope.TableName()
_, isOk := isUpdateComment[tableName]
if !isOk {
field := scope.Fields()
for e := range field {
comment := field[e].Tag.Get("comment")
if len(strings.Trim(comment, " ")) > 0 {
fieldType := db.Dialect().DataTypeOf(field[e].StructField)
scope.Raw(fmt.Sprintf("ALTER TABLE `%v` MODIFY COLUMN `%v` %v COMMENT '%v';", tableName, field[e].DBName, fieldType, comment)).Exec()
}
}
}
isUpdateComment[tableName] = true
}
return db
}
func init() {
go func() {
for {
if instanceDb != nil {
err := instanceDb.DB().Ping()
if err != nil {
println(err.Error())
instanceDb.Close()
instanceDb = nil
}
}
time.Sleep(time.Second)
}
}()
}

36
src/core/redis.go

@ -0,0 +1,36 @@
package core
import (
"github.com/go-redis/redis"
"time"
)
var instanceRedis *redis.Client
func GetRedis() *redis.Client {
if instanceRedis == nil {
config := GetInstanceConfig()
instanceRedis = redis.NewClient(&redis.Options{
Addr: config.Redis.Addr,
Password: config.Redis.Password, // no password set
DB: config.Redis.Db, // use default DB
})
}
return instanceRedis
}
func init() {
go func() {
for {
if instanceRedis != nil {
err := instanceRedis.Ping().Err()
if err != nil {
println(err.Error())
instanceRedis.Close()
instanceRedis = nil
}
}
time.Sleep(time.Second)
}
}()
}

117
src/core/request.go

@ -0,0 +1,117 @@
package core
import (
"github.com/wailovet/osmanthuswine/src/session"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"net/url"
)
type Request struct {
GET map[string]string
POST map[string]string
REQUEST map[string]string
COOKIE map[string]string
SESSION map[string]string
HEADER map[string]string
BODY string
FILES map[string][]*multipart.FileHeader
FILE *multipart.FileHeader
OriginRequest *http.Request
}
func (r *Request) SyncGetData(request *http.Request) {
if r.OriginRequest == nil {
r.OriginRequest = request
}
get := request.URL.Query()
r.GET = make(map[string]string)
for k := range get {
str := request.URL.Query().Get(k)
tmp, err := url.QueryUnescape(str)
if err != nil {
log.Println(err.Error())
r.GET[k] = str
r.REQUEST[k] = str
} else {
r.GET[k] = tmp
r.REQUEST[k] = tmp
}
}
}
func (r *Request) SyncPostData(request *http.Request, mem int64) {
if r.OriginRequest == nil {
r.OriginRequest = request
}
request.ParseForm()
request.ParseMultipartForm(mem)
r.POST = make(map[string]string)
post := request.PostForm
for k := range post {
str := request.PostFormValue(k)
tmp, err := url.QueryUnescape(str)
if err != nil {
log.Println(err.Error())
r.POST[k] = str
r.REQUEST[k] = str
} else {
r.POST[k] = tmp
r.REQUEST[k] = tmp
}
}
if request.MultipartForm != nil {
r.FILES = request.MultipartForm.File
if len(r.FILES) > 0 {
for fe := range r.FILES {
for fk := range r.FILES[fe] {
r.FILE = r.FILES[fe][fk]
}
}
}
mf := request.MultipartForm.Value
for k := range mf {
if len(mf[k]) > 0 {
r.POST[k] = mf[k][0]
r.REQUEST[k] = mf[k][0]
}
}
}
body, _ := ioutil.ReadAll(request.Body)
r.BODY = string(body)
}
func (r *Request) SyncHeaderData(request *http.Request) {
if r.OriginRequest == nil {
r.OriginRequest = request
}
r.HEADER = make(map[string]string)
header := request.Header
for k := range header {
if len(header[k]) > 0 {
r.HEADER[k] = header[k][0]
}
}
}
func (r *Request) SyncCookieData(request *http.Request) {
if r.OriginRequest == nil {
r.OriginRequest = request
}
cookie := request.Cookies()
r.COOKIE = make(map[string]string)
for k := range cookie {
r.COOKIE[cookie[k].Name] = cookie[k].Value
}
}
func (r *Request) SyncSessionData(session *session.Session) {
r.SESSION = session.GetSession()
}

115
src/core/response.go

@ -0,0 +1,115 @@
package core
import (
"encoding/json"
"github.com/wailovet/osmanthuswine/src/session"
"net/http"
"strings"
)
type ResponseData struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Message string `json:"message"`
}
type Response struct {
Session *session.Session
IsWebSocket bool
OriginResponseWriter http.ResponseWriter
}
func (r *Response) DisplayByRaw(data []byte) {
if r.IsWebSocket {
panic(nil)
return
}
r.OriginResponseWriter.Header().Add("Content-Type", "application/json; charset=utf-8")
r.OriginResponseWriter.Write(data)
panic(nil)
}
func (r *Response) DisplayByString(data string) {
r.DisplayByRaw([]byte(data))
}
func (r *Response) Display(data interface{}, msg string, code int) {
result := ResponseData{code, data, msg}
text, err := json.Marshal(result)
if err != nil {
r.OriginResponseWriter.WriteHeader(500)
r.DisplayByString("服务器异常:" + err.Error())
}
r.DisplayByRaw(text)
}
func (r *Response) DisplayByError(msg string, code int, data ...string) {
result := ResponseData{code, data, msg}
text, err := json.Marshal(result)
if err != nil {
r.Display(nil, "JSON返回格式解析异常:"+err.Error(), 500)
}
r.DisplayByRaw(text)
}
func (r *Response) CheckErrDisplayByError(err error, msg ...string) {
if err == nil {
return
}
if len(msg) > 0 {
r.DisplayByError(strings.Join(msg, ","), 504)
} else {
r.DisplayByError(err.Error(), 504)
}
}
func (r *Response) DisplayBySuccess(msg string) {
result := ResponseData{0, nil, msg}
text, err := json.Marshal(result)
if err != nil {
r.Display(nil, "JSON返回格式解析异常:"+err.Error(), 500)
}
r.DisplayByRaw(text)
}
func (r *Response) DisplayByData(data interface{}) {
result := ResponseData{0, data, ""}
text, err := json.Marshal(result)
if err != nil {
r.Display(nil, "JSON返回格式解析异常:"+err.Error(), 500)
}
r.DisplayByRaw(text)
}
func (r *Response) SetSession(name string, value string) {
data := r.Session.GetSession()
data[name] = value
r.Session.SetSession(data)
}
func (r *Response) DeleteSession(name string) {
data := r.Session.GetSession()
delete(data, name)
r.Session.SetSession(data)
}
func (r *Response) ClearSession() {
data := make(map[string]string)
r.Session.SetSession(data)
}
func (r *Response) SetCookie(name string, value string) {
cookie := &http.Cookie{
Name: name,
Value: value,
Path: "/",
Secure: false,
HttpOnly: false,
}
http.SetCookie(r.OriginResponseWriter, cookie)
}
func (r *Response) SetHeader(name string, value string) {
r.OriginResponseWriter.Header().Set(name, value)
}

118
src/core/router.go

@ -0,0 +1,118 @@
package core
import (
"github.com/wailovet/osmanthuswine/src/interfaces"
"log"
"reflect"
"strings"
"unicode"
)
type RouterManage struct {
RegisteredData map[string]reflect.Type
}
var instanceRouterManage *RouterManage
func GetInstanceRouterManage() *RouterManage {
if instanceRouterManage == nil {
instanceRouterManage = &RouterManage{} // not thread safe
instanceRouterManage.RegisteredData = make(map[string]reflect.Type)
}
return instanceRouterManage
}
func (rm *RouterManage) Registered(i interface{}) {
t := reflect.ValueOf(i)
GetInstanceRouterManage().RegisteredData[t.Type().String()] = reflect.Indirect(t).Type()
}
func (rm *RouterManage) GetModuleName(name string) string {
if name == "" {
return "index"
}
return strings.ToLower(name)
}
func (rm *RouterManage) GetControllerName(name string) string {
if name == "" {
return "Index"
}
for i, v := range name {
return string(unicode.ToUpper(v)) + name[i+1:]
}
return "Index"
}
func (rm *RouterManage) GetFunName(name string) string {
if name == "" {
return "Index"
}
for i, v := range name {
return string(unicode.ToUpper(v)) + name[i+1:]
}
return "Index"
}
func (rm *RouterManage) RouterSend(urlPath string, request Request, response Response, crossDomain string) {
tmp := strings.Split(urlPath, ".")
if len(tmp) > 1 {
urlPath = strings.Join(tmp[0:len(tmp)-1], ".")
}
sar := strings.Split(urlPath, "/")
for len(sar) < 5 {
sar = append(sar, "")
}
//过滤非 /Api开头的
module := rm.GetModuleName(sar[2])
controller := rm.GetControllerName(sar[3])
fun := rm.GetFunName(sar[4])
ctr := "*" + module + "." + controller
_, ok := rm.RegisteredData[ctr]
if !ok {
panic("未注册该组件:" + ctr)
}
vc := reflect.New(rm.RegisteredData[ctr])
wsinit := vc.MethodByName("WebSocketInit")
if wsinit.IsValid() {
response.IsWebSocket = true
hand := vc.Interface().(interfaces.WebSocketInterface)
ws := GetWebSocket(ctr+"-"+fun, hand)
hand.SetFunName(fun)
hand.WebSocketInit(ws)
defer func() {
errs := recover()
if errs == nil {
return
}
log.Printf("websocket error:%v", errs)
}()
_ = ws.HandleRequest(response.OriginResponseWriter, request.OriginRequest)
return
}
f := vc.MethodByName(fun)
if !f.IsValid() {
panic("组件找不到相应function:" + fun)
}
init := vc.MethodByName("ControllerInit")
if init.IsValid() {
init.Call([]reflect.Value{reflect.ValueOf(request), reflect.ValueOf(response)})
f.Call(nil)
} else {
//兼容模式
log.Println("兼容模式")
f.Call([]reflect.Value{reflect.ValueOf(request), reflect.ValueOf(response)})
}
response.OriginResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
if crossDomain != "" {
response.OriginResponseWriter.Header().Set("Access-Control-Allow-Origin", crossDomain)
}
}

51
src/core/websocket.go

@ -0,0 +1,51 @@
package core
import (
"github.com/wailovet/osmanthuswine/src/interfaces"
"gopkg.in/olahol/melody.v1"
"net/http"
"sync"
)
var instanceMelody sync.Map
type WebSocket struct {
ws *melody.Melody
FunName string
}
func (that *WebSocket) SetFunName(funName string) {
that.FunName = funName
}
func (that *WebSocket) WebSocketInit(wsx *melody.Melody) {
that.ws = wsx
}
func (that *WebSocket) GetMelody() *melody.Melody {
return that.ws
}
func (that *WebSocket) GetWebSocket() *melody.Melody {
return that.ws
}
func GetWebSocket(group string, hand interfaces.WebSocketInterface) *melody.Melody {
var m *melody.Melody
tmp, ok := instanceMelody.Load(group)
if !ok {
m = melody.New()
m.Upgrader.CheckOrigin = func(r *http.Request) bool { return true }
m.HandleConnect(hand.HandleConnect)
m.HandleDisconnect(hand.HandleDisconnect)
m.HandlePong(hand.HandlePong)
m.HandleError(hand.HandleError)
m.HandleMessage(hand.HandleMessage)
m.HandleMessageBinary(hand.HandleMessageBinary)
m.HandleSentMessage(hand.HandleSentMessage)
m.HandleSentMessageBinary(hand.HandleSentMessageBinary)
instanceMelody.Store(group, m)
} else {
m = tmp.(*melody.Melody)
}
return m
}

71
src/core/xorm.go

@ -0,0 +1,71 @@
package core
import (
"github.com/go-sql-driver/mysql"
"github.com/xormplus/core"
"github.com/xormplus/xorm"
"time"
)
type Xorm struct {
db *xorm.Engine
}
var instanceXorm *Xorm
func GetXormAuto() *xorm.Engine {
db, err := GetXorm()
if err != nil {
panic("数据库访问错误")
}
return db.db
}
func GetXorm() (*Xorm, error) {
if instanceXorm == nil {
config := GetInstanceConfig()
mysqlConfig := mysql.NewConfig()
mysqlConfig.User = config.Db.User
mysqlConfig.DBName = config.Db.Name
mysqlConfig.Passwd = config.Db.Password
mysqlConfig.Params = config.Db.Params
mysqlConfig.Net = "tcp"
mysqlConfig.Addr = config.Db.Host + ":" + config.Db.Port
engine, err := xorm.NewEngine("mysql", mysqlConfig.FormatDSN())
if config.Db.Prefix != "" {
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, config.Db.Prefix)
engine.SetTableMapper(tbMapper)
}
instanceXorm = &Xorm{db: engine}
instanceXorm.db.SetMaxOpenConns(config.Db.MaxOpenConn)
if config.Db.Debug {
instanceXorm.db.ShowSQL(true)
}
return instanceXorm, err
}
return instanceXorm, nil
}
func (x *Xorm) Ping() error {
session := x.db.NewSession()
defer session.Close()
return session.DB().Ping()
}
func init() {
go func() {
for {
if instanceXorm != nil {
err := instanceXorm.Ping()
if err != nil {
println(err.Error())
instanceXorm.db.Close()
instanceXorm = nil
}
}
time.Sleep(time.Second)
}
}()
}

30
src/helper/db2struct.go

@ -0,0 +1,30 @@
package helper
import (
"github.com/go-errors/errors"
"github.com/wailovet/db2struct"
"github.com/wailovet/osmanthuswine/src/core"
"strconv"
)
func GetStructByDb(tableName string, packageName string, structName string) (string, error) {
mariadbUser := core.GetInstanceConfig().Db.User
mariadbPassword := core.GetInstanceConfig().Db.Password
mariadbHost := core.GetInstanceConfig().Db.Host
mariadbPort, _ := strconv.Atoi(core.GetInstanceConfig().Db.Port)
mariadbDatabase := core.GetInstanceConfig().Db.Name
columnDataTypes, err := db2struct.GetColumnsFromMysqlTable(mariadbUser, mariadbPassword, mariadbHost, mariadbPort, mariadbDatabase, tableName)
if err != nil {
return "", errors.New("Error in selecting column data information from mysql information schema")
}
// Generate struct string based on columnDataTypes
struc, err := db2struct.Generate(*columnDataTypes, tableName, structName, packageName, true, true, false)
if err != nil {
return "", errors.New("Error in creating struct from json: " + err.Error())
}
return string(struc), nil
}

19
src/helper/log.go

@ -0,0 +1,19 @@
package helper
import "log"
type Log struct {
}
var instanceLog *Log
func GetInstanceLog() *Log {
if instanceLog == nil {
instanceLog = &Log{} // not thread safe
}
return instanceLog
}
func (l *Log) Out(args ...interface{}) {
log.Println(args)
}

10
src/helper/md5.go

@ -0,0 +1,10 @@
package helper
import (
"crypto/md5"
"fmt"
)
func Md5(data string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(data)))
}

8
src/helper/uuid.go

@ -0,0 +1,8 @@
package helper
import "github.com/satori/go.uuid"
func CreateUUID() string {
u1 := uuid.Must(uuid.NewV4())
return u1.String()
}

11
src/helper/uuid_test.go

@ -0,0 +1,11 @@
package helper
import "testing"
func TestCreateUUID(t *testing.T) {
s := CreateUUID()
println(s)
if len(s) != 36 {
t.Error("UUID长度错误")
}
}

21
src/interfaces/websocket.go

@ -0,0 +1,21 @@
package interfaces
import (
"gopkg.in/olahol/melody.v1"
)
//WebSocketInterface interface
type WebSocketInterface interface {
SetFunName(funName string)
WebSocketInit(ws *melody.Melody)
GetWebSocket() *melody.Melody
GetMelody() *melody.Melody
HandleConnect(*melody.Session)
HandlePong(*melody.Session)
HandleMessage(*melody.Session, []byte)
HandleMessageBinary(*melody.Session, []byte)
HandleSentMessage(*melody.Session, []byte)
HandleSentMessageBinary(*melody.Session, []byte)
HandleDisconnect(*melody.Session)
HandleError(*melody.Session, error)
}

50
src/session/securecookie.go

@ -0,0 +1,50 @@
package session
import (
"github.com/gorilla/securecookie"
"net/http"
)
type Session struct {
secureCookie *securecookie.SecureCookie
r *http.Request
w http.ResponseWriter
data map[string]string
}
func New(r *http.Request, w http.ResponseWriter) *Session {
session := &Session{} // not thread safe
var hashKey = []byte("osmanthuswine-very-secret")
// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long.
// Shorter keys may weaken the encryption used.
var blockKey = []byte("osmanthuswine-lot-secret")
session.secureCookie = securecookie.New(hashKey, blockKey)
session.r = r
session.w = w
session.data = make(map[string]string)
if cookie, err := session.r.Cookie("osmseccidhas"); err == nil {
session.secureCookie.Decode("osmseccidhas", cookie.Value, &session.data)
} else {
//helper.GetInstanceLog().Out(err.Error())
}
return session
}
func (session *Session) GetSession() map[string]string {
return session.data
}
func (session *Session) SetSession(value map[string]string) {
if encoded, err := session.secureCookie.Encode("osmseccidhas", value); err == nil {
cookie := &http.Cookie{
Name: "osmseccidhas",
Value: encoded,
Path: "/",
Secure: false,
HttpOnly: false,
}
http.SetCookie(session.w, cookie)
session.data = value
}
}
Loading…
Cancel
Save