mirror of
https://github.com/gowvp/gb28181.git
synced 2026-04-22 23:17:19 +08:00
go fix
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ func Run(bc *conf.Bootstrap) {
|
||||
go setupZLM(ctx, bc.ConfigDir)
|
||||
|
||||
// 如果需要执行表迁移,递增此版本号和表更新说明
|
||||
versionapi.DBVersion = "0.0.17"
|
||||
versionapi.DBVersion = "0.0.18"
|
||||
versionapi.DBRemark = "onvif device support"
|
||||
|
||||
handler, cleanUp, err := wireApp(bc, log)
|
||||
|
||||
@@ -9,7 +9,7 @@ type Ext struct {
|
||||
}
|
||||
|
||||
// Scan implements orm.Scaner.
|
||||
func (i *Ext) Scan(input interface{}) error {
|
||||
func (i *Ext) Scan(input any) error {
|
||||
return orm.JSONUnmarshal(input, i)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,13 +38,21 @@ type DeviceExt struct {
|
||||
Firmware string `json:"firmware"` // 固件版本
|
||||
Name string `json:"name"` // 设备名
|
||||
GBVersion string `json:"gb_version"` // GB版本
|
||||
Zones []Zone `json:"zones"` // 区域
|
||||
}
|
||||
|
||||
// Scan implements orm.Scaner.
|
||||
func (i *DeviceExt) Scan(input interface{}) error {
|
||||
func (i *DeviceExt) Scan(input any) error {
|
||||
return orm.JSONUnmarshal(input, i)
|
||||
}
|
||||
|
||||
func (i DeviceExt) Value() (driver.Value, error) {
|
||||
return json.Marshal(i)
|
||||
}
|
||||
|
||||
type Zone struct {
|
||||
Name string `json:"name"` // 区域名称
|
||||
Coordinates []float32 `json:"coordinates"` // 坐标
|
||||
Color string `json:"color"` // 颜色,支持 hex 颜色值,如 #FF0000
|
||||
Labels []string `json:"labels"` // 标签
|
||||
}
|
||||
|
||||
@@ -6,15 +6,10 @@ import (
|
||||
|
||||
// Device 设备接口
|
||||
// 注意: 适配器实现时,参数类型为 *ipc.Device,满足此接口
|
||||
type Device interface {
|
||||
// 这里不定义任何方法,让所有类型都能满足
|
||||
// 适配器实现时直接使用 *ipc.Device 类型
|
||||
}
|
||||
type Device any
|
||||
|
||||
// Channel 通道接口
|
||||
type Channel interface {
|
||||
// 同上
|
||||
}
|
||||
type Channel any
|
||||
|
||||
// Protocol 协议抽象接口(端口)
|
||||
//
|
||||
|
||||
@@ -83,32 +83,32 @@ func (d *ZLMDriver) Setup(ctx context.Context, ms *MediaServer, webhookURL strin
|
||||
|
||||
// 构造配置请求
|
||||
req := zlm.SetServerConfigRequest{
|
||||
RtcExternIP: zlm.NewString(ms.IP),
|
||||
GeneralMediaServerID: zlm.NewString(ms.ID),
|
||||
HookEnable: zlm.NewString("1"),
|
||||
HookOnFlowReport: zlm.NewString(""),
|
||||
HookOnPlay: zlm.NewString(fmt.Sprintf("%s/on_play", webhookURL)),
|
||||
RtcExternIP: new(ms.IP),
|
||||
GeneralMediaServerID: new(ms.ID),
|
||||
HookEnable: new("1"),
|
||||
HookOnFlowReport: new(""),
|
||||
HookOnPlay: new(fmt.Sprintf("%s/on_play", webhookURL)),
|
||||
|
||||
ProtocolEnableTs: zlm.NewString("0"),
|
||||
ProtocolEnableFmp4: zlm.NewString("0"),
|
||||
ProtocolEnableHls: zlm.NewString("0"),
|
||||
ProtocolEnableHlsFmp4: zlm.NewString("1"),
|
||||
ProtocolEnableTs: new("0"),
|
||||
ProtocolEnableFmp4: new("0"),
|
||||
ProtocolEnableHls: new("0"),
|
||||
ProtocolEnableHlsFmp4: new("1"),
|
||||
|
||||
HookOnPublish: zlm.NewString(fmt.Sprintf("%s/on_publish", webhookURL)),
|
||||
HookOnStreamNoneReader: zlm.NewString(fmt.Sprintf("%s/on_stream_none_reader", webhookURL)),
|
||||
GeneralStreamNoneReaderDelayMS: zlm.NewString("30000"),
|
||||
HookOnStreamNotFound: zlm.NewString(fmt.Sprintf("%s/on_stream_not_found", webhookURL)),
|
||||
HookOnRecordTs: zlm.NewString(""),
|
||||
HookOnRtspAuth: zlm.NewString(""),
|
||||
HookOnRtspRealm: zlm.NewString(""),
|
||||
HookOnShellLogin: zlm.NewString(""),
|
||||
HookOnStreamChanged: zlm.NewString(fmt.Sprintf("%s/on_stream_changed", webhookURL)),
|
||||
HookOnServerKeepalive: zlm.NewString(fmt.Sprintf("%s/on_server_keepalive", webhookURL)),
|
||||
HookTimeoutSec: zlm.NewString("10"),
|
||||
HookAliveInterval: zlm.NewString(fmt.Sprint(ms.HookAliveInterval)),
|
||||
ProtocolContinuePushMs: zlm.NewString("3000"),
|
||||
HookOnPublish: new(fmt.Sprintf("%s/on_publish", webhookURL)),
|
||||
HookOnStreamNoneReader: new(fmt.Sprintf("%s/on_stream_none_reader", webhookURL)),
|
||||
GeneralStreamNoneReaderDelayMS: new("30000"),
|
||||
HookOnStreamNotFound: new(fmt.Sprintf("%s/on_stream_not_found", webhookURL)),
|
||||
HookOnRecordTs: new(""),
|
||||
HookOnRtspAuth: new(""),
|
||||
HookOnRtspRealm: new(""),
|
||||
HookOnShellLogin: new(""),
|
||||
HookOnStreamChanged: new(fmt.Sprintf("%s/on_stream_changed", webhookURL)),
|
||||
HookOnServerKeepalive: new(fmt.Sprintf("%s/on_server_keepalive", webhookURL)),
|
||||
HookTimeoutSec: new("10"),
|
||||
HookAliveInterval: new(fmt.Sprint(ms.HookAliveInterval)),
|
||||
ProtocolContinuePushMs: new("3000"),
|
||||
RtpProxyPortRange: &ms.RTPPortRange,
|
||||
FfmpegLog: zlm.NewString("./fflogs/ffmpeg.log"),
|
||||
FfmpegLog: new("./fflogs/ffmpeg.log"),
|
||||
}
|
||||
|
||||
resp, err := engine.SetServerConfig(&req)
|
||||
@@ -147,12 +147,12 @@ func (d *ZLMDriver) AddStreamProxy(ctx context.Context, ms *MediaServer, req *Ad
|
||||
RTPType: req.RTPType,
|
||||
RetryCount: 3,
|
||||
TimeoutSec: PullTimeoutMs / 1000,
|
||||
EnableHLSFMP4: zlm.NewBool(true),
|
||||
EnableAudio: zlm.NewBool(true),
|
||||
EnableRTSP: zlm.NewBool(true),
|
||||
EnableRTMP: zlm.NewBool(true),
|
||||
AddMuteAudio: zlm.NewBool(true),
|
||||
AutoClose: zlm.NewBool(true),
|
||||
EnableHLSFMP4: new(true),
|
||||
EnableAudio: new(true),
|
||||
EnableRTSP: new(true),
|
||||
EnableRTMP: new(true),
|
||||
AddMuteAudio: new(true),
|
||||
AutoClose: new(true),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ type MediaServerPorts struct {
|
||||
}
|
||||
|
||||
// Scan implements orm.Scaner.
|
||||
func (i *MediaServerPorts) Scan(input interface{}) error {
|
||||
func (i *MediaServerPorts) Scan(input any) error {
|
||||
return orm.JSONUnmarshal(input, i)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package sms
|
||||
|
||||
type NodeServicer interface{}
|
||||
type NodeServicer any
|
||||
|
||||
@@ -103,6 +103,8 @@ func registerGB28181(g gin.IRouter, api IPCAPI, handler ...gin.HandlerFunc) {
|
||||
group.POST("/:id/play", web.WrapH(api.play)) // 播放(所有协议)
|
||||
group.POST("/:id/snapshot", web.WrapH(api.refreshSnapshot)) // 图像抓拍(所有协议)
|
||||
group.GET("/:id/snapshot", api.getSnapshot) // 获取图像(所有协议)
|
||||
group.POST("/:id/zones", web.WrapH(api.addZone)) // 添加区域(所有协议)
|
||||
group.GET("/:id/zones", web.WrapH(api.getZones)) // 获取区域(所有协议)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,6 +287,7 @@ func (a IPCAPI) play(c *gin.Context, _ *struct{}) (*playOutput, error) {
|
||||
for range 2 {
|
||||
time.Sleep(3 * time.Second)
|
||||
rtsp := fmt.Sprintf("rtsp://%s:%d/%s", "127.0.0.1", svr.Ports.RTSP, stream) + "?" + session
|
||||
|
||||
body, err := a.uc.SMSAPI.smsCore.GetSnapshot(svr, sms.GetSnapRequest{
|
||||
GetSnapRequest: zlm.GetSnapRequest{
|
||||
URL: rtsp,
|
||||
@@ -362,6 +365,20 @@ func (a IPCAPI) refreshSnapshot(c *gin.Context, in *refreshSnapshotInput) (any,
|
||||
return gin.H{"link": fmt.Sprintf("%s/channels/%s/snapshot?token=%s", prefix, channelID, token)}, nil
|
||||
}
|
||||
|
||||
func (a IPCAPI) addZone(c *gin.Context, in *ipc.AddZoneInput) (gin.H, error) {
|
||||
channelID := c.Param("id")
|
||||
if len(in.Labels) == 0 {
|
||||
in.Labels = []string{"person", "car", "cat", "dog"}
|
||||
}
|
||||
zones, err := a.ipc.AddZone(c.Request.Context(), in, channelID)
|
||||
return gin.H{"items": zones}, err
|
||||
}
|
||||
|
||||
func (a IPCAPI) getZones(c *gin.Context, _ *struct{}) (any, error) {
|
||||
channelID := c.Param("id")
|
||||
return a.ipc.GetZones(c.Request.Context(), channelID)
|
||||
}
|
||||
|
||||
func (a IPCAPI) getSnapshot(c *gin.Context) {
|
||||
channelID := c.Param("id")
|
||||
body, err := readCover(a.uc.Conf.ConfigDir, channelID)
|
||||
|
||||
+100
-10
@@ -1,6 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -10,25 +19,85 @@ import (
|
||||
)
|
||||
|
||||
type UserAPI struct {
|
||||
conf *conf.Bootstrap
|
||||
conf *conf.Bootstrap
|
||||
secret *Secret
|
||||
}
|
||||
|
||||
type Secret struct {
|
||||
privateKey *rsa.PrivateKey
|
||||
publicKey *rsa.PublicKey
|
||||
expiredAt time.Time
|
||||
m sync.RWMutex
|
||||
}
|
||||
|
||||
// TODO: 有概率存在过期导致登录解密识别
|
||||
func (s *Secret) GetOrCreatePublicKey() (*rsa.PublicKey, error) {
|
||||
s.m.RLock()
|
||||
if s.publicKey != nil && time.Now().Before(s.expiredAt) {
|
||||
s.m.RUnlock()
|
||||
return s.publicKey, nil
|
||||
}
|
||||
s.m.RUnlock()
|
||||
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.privateKey = privateKey
|
||||
s.publicKey = &privateKey.PublicKey
|
||||
s.expiredAt = time.Now().Add(1 * time.Hour)
|
||||
return s.publicKey, nil
|
||||
}
|
||||
|
||||
func (s *Secret) MarshalPKIXPublicKey(key *rsa.PublicKey) []byte {
|
||||
publicKeyBytes, _ := x509.MarshalPKIXPublicKey(key)
|
||||
return pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: publicKeyBytes,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Secret) Decrypt(ciphertext string) ([]byte, error) {
|
||||
s.m.RLock()
|
||||
pri := s.privateKey
|
||||
s.m.RUnlock()
|
||||
if pri == nil {
|
||||
return nil, fmt.Errorf("请刷新页面后重试")
|
||||
}
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, pri, data, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func NewUserAPI(conf *conf.Bootstrap) UserAPI {
|
||||
return UserAPI{
|
||||
conf: conf,
|
||||
conf: conf,
|
||||
secret: &Secret{},
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterUser(r gin.IRouter, api UserAPI, mid ...gin.HandlerFunc) {
|
||||
group := r.Group("/user")
|
||||
group.POST("/login", web.WrapH(api.login))
|
||||
group.PUT("/user", web.WrapHs(api.updateCredentials, mid...)...)
|
||||
r.POST("/login", web.WrapH(api.login))
|
||||
r.GET("/login/key", web.WrapH(api.getPublicKey))
|
||||
|
||||
group := r.Group("/users", mid...)
|
||||
group.PUT("", web.WrapHs(api.updateCredentials, mid...)...)
|
||||
}
|
||||
|
||||
// 登录请求结构体
|
||||
type loginInput struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
// Username string `json:"username" binding:"required"`
|
||||
// Password string `json:"password" binding:"required"`
|
||||
Data string `json:"data" binding:"required"`
|
||||
}
|
||||
|
||||
// 登录响应结构体
|
||||
@@ -39,16 +108,28 @@ type loginOutput struct {
|
||||
|
||||
// 登录接口
|
||||
func (api UserAPI) login(_ *gin.Context, in *loginInput) (*loginOutput, error) {
|
||||
body, err := api.secret.Decrypt(in.Data)
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(err.Error())
|
||||
}
|
||||
var credentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &credentials); err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(err.Error())
|
||||
}
|
||||
|
||||
// 验证用户名和密码
|
||||
if api.conf.Server.Username == "" && api.conf.Server.Password == "" {
|
||||
api.conf.Server.Username = "admin"
|
||||
api.conf.Server.Password = "admin"
|
||||
}
|
||||
if in.Username != api.conf.Server.Username || in.Password != api.conf.Server.Password {
|
||||
if credentials.Username != api.conf.Server.Username || credentials.Password != api.conf.Server.Password {
|
||||
return nil, reason.ErrNameOrPasswd
|
||||
}
|
||||
|
||||
data := web.NewClaimsData().SetUsername(in.Username)
|
||||
data := web.NewClaimsData().SetUsername(credentials.Username)
|
||||
|
||||
token, err := web.NewToken(data, api.conf.Server.HTTP.JwtSecret, web.WithExpiresAt(time.Now().Add(3*24*time.Hour)))
|
||||
if err != nil {
|
||||
@@ -57,7 +138,7 @@ func (api UserAPI) login(_ *gin.Context, in *loginInput) (*loginOutput, error) {
|
||||
|
||||
return &loginOutput{
|
||||
Token: token,
|
||||
User: in.Username,
|
||||
User: credentials.Username,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -80,3 +161,12 @@ func (api UserAPI) updateCredentials(_ *gin.Context, in *updateCredentialsInput)
|
||||
|
||||
return gin.H{"msg": "凭据更新成功"}, nil
|
||||
}
|
||||
|
||||
func (api UserAPI) getPublicKey(_ *gin.Context, _ *struct{}) (gin.H, error) {
|
||||
publicKey, err := api.secret.GetOrCreatePublicKey()
|
||||
if err != nil {
|
||||
return nil, reason.ErrServer.SetMsg(err.Error())
|
||||
}
|
||||
result := api.secret.MarshalPKIXPublicKey(publicKey)
|
||||
return gin.H{"key": base64.StdEncoding.EncodeToString(result)}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user