From 85fd2567d5e5309c95c3b323eab907f488370e2a Mon Sep 17 00:00:00 2001 From: lijun Date: Tue, 14 Jan 2025 15:00:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E5=8F=8A?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E4=BF=A1=E6=81=AF=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=EF=BC=8C=E6=B7=BB=E5=8A=A0wss=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E8=A7=86=E9=A2=91=E5=9B=BE=E7=89=87=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dao/db.go | 49 +++++++++++++++++++++ dao/device.go | 22 ++++++++++ dao/user.go | 34 +++++++++++++++ go.mod | 22 +++++++++- go.sum | 43 +++++++++++++++++-- handler/tool.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 98 +++++++++++++++++++++++++++++++++++++++++- proto/conf.go | 10 +++++ service/tool.go | 5 +++ service/user.go | 41 ++++++++++++++++++ 10 files changed, 430 insertions(+), 6 deletions(-) create mode 100644 dao/db.go create mode 100644 dao/device.go create mode 100644 dao/user.go create mode 100644 service/user.go diff --git a/dao/db.go b/dao/db.go new file mode 100644 index 0000000..e18d84d --- /dev/null +++ b/dao/db.go @@ -0,0 +1,49 @@ +package dao + +import ( + "VideoStream/proto" + "fmt" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/gorm" +) + +var DB *gorm.DB + +func Init() error { + var db *gorm.DB + var err error + var dsn string + if proto.Config.DB == 0 { + dsn = proto.Config.MYSQL_DSN + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + } else if proto.Config.DB == 1 { + dsn = proto.Config.PG_DSN + db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + } + + if err != nil { + panic("failed to connect database") + return err + } + err = db.AutoMigrate(&User{}) + if err != nil { + fmt.Println("user table:", err) + return err + } // 自动迁移,创建表,如果表已经存在,会自动更新表结构,不会删除表,只会创建不存在的表 + err = db.AutoMigrate(&Device{}) + if err != nil { + fmt.Println("device table:", err) + return err + } // 自动迁移,创建表,如果表已经存在,会自动更新表结构,不会删除表,只会创建不存在的表 + DB = db + return err +} + +func Close() { + sqlDB, err := DB.DB() + if err != nil { + panic("failed to connect database") + } + sqlDB.Close() +} diff --git a/dao/device.go b/dao/device.go new file mode 100644 index 0000000..aedb38e --- /dev/null +++ b/dao/device.go @@ -0,0 +1,22 @@ +package dao + +import ( + "gorm.io/gorm" +) + +type Device struct { + gorm.Model + AuthID int `gorm:"column:auth_id"` + DeviceName string `gorm:"column:device_name"` + DeviceType string `gorm:"column:device_type"` + DeviceStatus string `gorm:"column:device_status"` + DeviceLocation string `gorm:"column:device_location"` + DeviceIP string `gorm:"column:device_ip"` + DeviceInfo string `gorm:"column:device_info"` +} + +func FindDeviceByID(id, auth_id int) Device { + var device Device + DB.Where("id = ? and auth_id = ?", id, auth_id).First(&device) + return device +} diff --git a/dao/user.go b/dao/user.go new file mode 100644 index 0000000..f3ebe10 --- /dev/null +++ b/dao/user.go @@ -0,0 +1,34 @@ +package dao + +import "gorm.io/gorm" + +type User struct { + gorm.Model + Name string `gorm:"column:name"` + Age int `gorm:"column:age"` + Email string `gorm:"column:email"` + Password string `gorm:"column:password"` + Gender string `gorm:"column:gender"` + Role string `gorm:"column:role"` + Redis bool `gorm:"column:redis"` + Run bool `gorm:"column:run"` + Upload bool `gorm:"column:upload"` + VideoFunc bool `gorm:"column:video_func"` //视频功能 + DeviceFunc bool `gorm:"column:device_func"` //设备功能 + CIDFunc bool `gorm:"column:cid_func"` //持续集成功能 + Avatar string `gorm:"column:avatar"` + CreateTime string `gorm:"column:create_time"` + UpdateTime string `gorm:"column:update_time"` +} + +func FindUserByID(id int) []User { + var users []User + DB.Where("id = ?", id).First(&users) + return users +} + +func FindUserByID2(id int) User { + var user User + DB.Where("id = ?", id).First(&user) + return user +} diff --git a/go.mod b/go.mod index e2d2d13..348bac6 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,20 @@ module VideoStream -go 1.21 +go 1.22 + +toolchain go1.22.10 require ( github.com/gin-gonic/gin v1.10.0 github.com/go-redis/redis/v8 v8.11.5 + github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 github.com/robfig/cron/v3 v3.0.1 gocv.io/x/gocv v0.39.0 + gorm.io/driver/mysql v1.5.7 + gorm.io/driver/postgres v1.5.11 + gorm.io/gorm v1.25.12 ) require ( @@ -21,20 +29,30 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.15.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index c4f9ad7..d13d05d 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/ github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,17 +32,41 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -63,6 +88,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -88,18 +115,21 @@ golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -107,5 +137,12 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= +gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/handler/tool.go b/handler/tool.go index d78b2f4..09b31d4 100644 --- a/handler/tool.go +++ b/handler/tool.go @@ -3,16 +3,59 @@ package handler import ( "VideoStream/proto" "VideoStream/service" + "VideoStream/worker" "fmt" "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/gorilla/websocket" "gocv.io/x/gocv" "io" + "net/http" + "strconv" "time" ) +var ( + upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + // 允许所有来源的连接 + return true + }, + } +) + func SetUpToolGroup(router *gin.Engine) { toolGroup := router.Group("/tool") toolGroup.GET("/video_stream", GetVideoStream) + toolGroup.GET("/video_real_time", GetRealTimeImage) +} + +// 跨域访问:cross origin resource share +func CrosHandler() gin.HandlerFunc { + return func(context *gin.Context) { + //method := context.Request.Method + context.Writer.Header().Set("Access-Control-Allow-Origin", "*") + context.Header("Access-Control-Allow-Origin", "*") // 设置允许访问所有域 + context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") + context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma,token,openid,opentoken") + context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") + context.Header("Access-Control-Max-Age", "172800") + context.Header("Access-Control-Allow-Credentials", "false") + context.Set("content-type", "application/json") //设置返回格式是json + + // if method == "OPTIONS" { + // context.JSON(http.StatusOK, gin.H{ + // "code":1, + // "message":"error", + // "data":"request error", + // }) + // } + + //处理请求 + context.Next() + } } type videoStreamReq struct { @@ -77,3 +120,72 @@ func GetVideoStream(c *gin.Context) { } } + +// 发送实时视频流 +func GetRealTimeImage(c *gin.Context) { + id, _ := c.Get("id") + id1 := id.(int) + device_id := c.Query("device_id") + device_id_int, _ := strconv.Atoi(device_id) + device := service.GetDevice(device_id_int, id1) + if device.ID == 0 { + c.JSON(http.StatusOK, gin.H{"code": 4, "message": "device not found"}) + return + } + ws, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + fmt.Println("connect wss err:", err) + return + } + worker.SetRedisWithExpire(strconv.Itoa(int(device.ID))+"_is_play", "1", time.Minute*5) + fmt.Println("device_id:", device_id_int, " has set is_play to 1") + go subscribeAndHandleMessagesV3(ws, device_id_int) +} + +func subscribeAndHandleMessagesV3(ws *websocket.Conn, device_id int) { + // 生成唯一连接 uuid + con_id := uuid.New().String() + online_conn_key := "device_" + strconv.Itoa(device_id) + "_online_conn_ids" + // 加入设备在线连接集合 + worker.SetRedisSetAddWithExpire(online_conn_key, con_id, time.Minute*5) + //图片计数器 + count := 0 + //定时器,发送计数器 + t_count := 0 + for { + //从service获取当前帧 + img, c := service.GetDeviceCurrentFrame(device_id) + if c != count { + //将img转[]byte + buf, _ := gocv.IMEncode(".jpg", img) + buf1 := buf.GetBytes() + + err2 := ws.WriteMessage(websocket.BinaryMessage, buf1) + if err2 != nil { + fmt.Println("send message to client err:", err2) + worker.SetRedisSetRemove(online_conn_key, con_id) + goto end + } + c = count + } else { + //每秒发送一次心跳检测 + if t_count%10 == 0 { + err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second)) + if err != nil { + fmt.Println("Connection check failed:", err) + worker.SetRedisSetRemove(online_conn_key, con_id) + goto end + } + } + } + time.Sleep(100 * time.Millisecond) + t_count++ + } + +end: + // 查看是否还有其他连接,没有则设置 is_play 为 0 + if worker.IsContainKey(online_conn_key) == false { + worker.SetRedisWithExpire(strconv.Itoa(device_id)+"_is_play", "1", time.Minute*5) + fmt.Println("device_id:", device_id, " has set is_play to 0") + } +} diff --git a/main.go b/main.go index 156b61a..eb8face 100644 --- a/main.go +++ b/main.go @@ -1,21 +1,37 @@ package main import ( + "VideoStream/dao" "VideoStream/handler" "VideoStream/proto" "VideoStream/service" "VideoStream/worker" "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt" "github.com/robfig/cron/v3" "gocv.io/x/gocv" "log" + "strconv" + "strings" "sync" ) func main() { gin.SetMode(gin.ReleaseMode) r := gin.Default() - handler.SetUpToolGroup(r) // Tool + err0 := dao.Init() + defer dao.Close() + if err0 != nil { + panic("failed to connect database:" + err0.Error()) + } + err0 = worker.InitRedis() + defer worker.CloseRedis() + if err0 != nil { + panic("failed to connect redis:" + err0.Error()) + } + r.Use(handler.CrosHandler()) + r.Use(JWTAuthMiddleware()) // 使用 JWT 认证中间件 + handler.SetUpToolGroup(r) // Tool err := worker.InitRedis() if err != nil { panic("failed to connect redis:" + err.Error()) @@ -69,3 +85,83 @@ func ReadConfigAndSetSystem() { } } } + +func JWTAuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // 从请求头中获取 JWT 令牌 + tokenString := c.Request.Header.Get("token") + + //请求方式为get时,从url中获取token + if tokenString == "" { + tokenString = c.Query("token") + } + + //如果请求为login或register,则不需要验证token + for k, _ := range proto.Url_map { + if strings.Contains(c.Request.URL.Path, k) { + c.Next() + return + } + } + if tokenString == "" { + //c.AbortWithStatus(200) + c.JSON(200, gin.H{ + "message": "Unauthorized", + "error": "token is empty", + "code": 3, + }) + return + } + if proto.Config.TOKEN_USE_REDIS { + redisToken := worker.GetRedis(tokenString) + if redisToken == "" { + c.AbortWithStatus(200) + c.JSON(200, gin.H{ + "message": "NOT_LOGIN", + "error": "server token is empty", + "code": 3, + }) + return + } + } + //查看token是否在超级token中 + if worker.IsContainSet("super_permission_tokens", tokenString) { + s_id := c.Request.Header.Get("super_id") + if s_id == "" { + c.AbortWithStatus(200) + c.JSON(200, gin.H{ + "message": "NOT_LOGIN", + "error": "super_id is empty", + "code": 3, + }) + return + } + id, _ := strconv.Atoi(s_id) + //查看s_id类型 + c.Set("id", id) + c.Next() + return + } + + // 使用加密secret 解析 JWT 令牌 + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + return proto.SigningKey, nil + }) + + // 验证令牌 + if err != nil || !token.Valid { + c.AbortWithStatus(200) + c.JSON(200, gin.H{ + "message": "NOT_LOGIN", + "error": "Invalid token", + "code": 4, + }) + return + } + userId := int(token.Claims.(jwt.MapClaims)["id"].(float64)) + // 将用户信息添加到上下文中 + c.Set("id", userId) + // 继续处理请求 + c.Next() + } +} diff --git a/proto/conf.go b/proto/conf.go index 1ad50fb..e74663d 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -7,8 +7,14 @@ import ( ) var Config ConfigStruct +var Url_map = map[string]bool{} // 不需要token验证的url +var SigningKey = []byte{} type ConfigStruct struct { + DB int `json:"db"` // 0: mysql, 1: pg + MYSQL_DSN string `json:"mysql_dsn"` + PG_DSN string `json:"pg_dsn"` + TOKEN_SECRET string `json:"token_secret"` REDIS_ADDR string `json:"redis_addr"` TOKEN_USE_REDIS bool `json:"token_use_redis"` REDIS_User_PW bool `json:"redis_user_pw"` // 是否使用密码 @@ -68,5 +74,9 @@ func ReadConfig(path string) error { Config.SERVER_PORT = "5002" // 默认端口 } } + SigningKey = []byte(Config.TOKEN_SECRET) + if Config.TOKEN_SECRET == "" { + err = fmt.Errorf("token secret is empty") + } return err } diff --git a/service/tool.go b/service/tool.go index ce44354..ea93d66 100644 --- a/service/tool.go +++ b/service/tool.go @@ -1,6 +1,7 @@ package service import ( + "VideoStream/dao" "VideoStream/proto" "fmt" "gocv.io/x/gocv" @@ -141,3 +142,7 @@ func DoGetVideoStream() { go GetVideoStream(device.ID) } } + +func GetDevice(id, auth_id int) dao.Device { + return dao.FindDeviceByID(id, auth_id) +} diff --git a/service/user.go b/service/user.go new file mode 100644 index 0000000..1cf09ba --- /dev/null +++ b/service/user.go @@ -0,0 +1,41 @@ +package service + +import ( + "VideoStream/dao" + "VideoStream/worker" + "encoding/json" + "fmt" + "strconv" +) + +// 获取用户信息,有redis缓存 +func GetUserByIDWithCache(id int) dao.User { + if id <= 0 { + return dao.User{} + } + var user dao.User + //先从redis获取 + key := "user_info_" + strconv.Itoa(id) + user_str := worker.GetRedis(key) + if user_str != "" { + err := json.Unmarshal([]byte(user_str), &user) + if err != nil { + fmt.Println("get user info , json unmarshal error:", err, "\tuser_str:", user_str) + return dao.User{} + } + } else { + user = dao.FindUserByID2(id) + if user.ID != 0 { + userJson, err := json.Marshal(user) + if err != nil { + fmt.Println("get user info , json marshal error:", err) + return dao.User{} + } + success := worker.SetRedis(key, string(userJson)) + if !success { + fmt.Println("set redis error,user json:", string(userJson)) + } + } + } + return user +}