]> git.feebdaed.xyz Git - gomehub.git/commitdiff
Add oauth
authorseantywork <seantywork@gmail.com>
Wed, 19 Jun 2024 10:06:26 +0000 (19:06 +0900)
committerseantywork <seantywork@gmail.com>
Wed, 19 Jun 2024 10:06:26 +0000 (19:06 +0900)
27 files changed:
.gitignore
config.yaml
ctl/base.go [new file with mode: 0644]
ctl/config.go
ctl/ctl.go
db/Dockerfile [new file with mode: 0644]
db/docker-compose.yaml [new file with mode: 0644]
db/init.sql [new file with mode: 0644]
go.mod
go.sum
main.go
pkg/auth/auth.go [new file with mode: 0644]
pkg/auth/oauth2.go [new file with mode: 0644]
pkg/dbquery/dbquery.go [new file with mode: 0644]
pkg/sorrylinushub/sorrylinushub.go [new file with mode: 0644]
pkg/stream/stream_cctv.go
pkg/stream/stream_cctv_local.go
pkg/stream/stream_peers.go
pkg/stream/stream_peers_signal.go
public/js/cctv.js [new file with mode: 0644]
public/js/cctv_local.js
public/js/peers.js
public/js/peers_p2p.js
view/cctv.html
view/index.html [new file with mode: 0644]
view/peers.html
view/signin.html [new file with mode: 0644]

index fc2f3b86326113396beac2dcddec8ed572184fe2..daa27703d3d9504c6826d39fbd17bd373fed1bfe 100644 (file)
@@ -2,4 +2,5 @@
 vendor/gocv
 vendor/yolo
 *.out
-*.mp4
\ No newline at end of file
+*.mp4
+oauth.json
\ No newline at end of file
index 94076b6223f6a496fb41158c3e262861e2e5ffd8..8a46238552c8b40ca9897361ba31e8532a1eddf1 100644 (file)
@@ -1,6 +1,7 @@
+debug: true
 externalUrl: "localhost"
 serveAddr: "0.0.0.0"
-servePort: "8080"
+servePort: "8000"
 maxFileSize: 838860800
 stream:
   turnServerAddr: "stun:stun.l.google.com:19302"
@@ -9,7 +10,7 @@ stream:
   extAllowList:
     - "mp4"
   udpBufferByteSize: 300000 
-  signalPort: "8082"
-  rtpReceivePort: "8084"
+  signalPort: "8002"
+  rtpReceivePort: "8004"
 utils:
   useCompress: false
diff --git a/ctl/base.go b/ctl/base.go
new file mode 100644 (file)
index 0000000..641c991
--- /dev/null
@@ -0,0 +1,18 @@
+package controller
+
+import "github.com/gin-gonic/gin"
+
+func GetIndex(c *gin.Context) {
+
+       c.HTML(200, "index.html", gin.H{
+               "title": "Index",
+       })
+}
+
+func GetSigninIndex(c *gin.Context) {
+
+       c.HTML(200, "signin.html", gin.H{
+               "title": "Signin",
+       })
+
+}
index 4ac7c7546e9715c8593e0947fc1de0e58948f9b3..e9bb33090dd1233ceb0f8d07f0d0252f71ad895c 100644 (file)
@@ -7,6 +7,7 @@ import (
 )
 
 type SOLIAGAIN_CONFIG struct {
+       Debug       bool   `yaml:"debug"`
        ExternalUrl string `yaml:"externalUrl"`
        ServeAddr   string `yaml:"serveAddr"`
        ServePort   string `yaml:"servePort"`
index b4db9ed0d9dc78ef65402080e6d75e1b8fe1c2e0..16ac6620fb8afffa6b84d0161a705e21209e4c67 100644 (file)
@@ -3,7 +3,9 @@ package controller
 import (
        "time"
 
+       "github.com/gin-gonic/contrib/sessions"
        "github.com/gin-gonic/gin"
+       pkgauth "github.com/seantywork/sorrylinus-again/pkg/auth"
        pkgstream "github.com/seantywork/sorrylinus-again/pkg/stream"
        pkgutils "github.com/seantywork/sorrylinus-again/pkg/utils"
 )
@@ -12,7 +14,23 @@ func CreateServer() *gin.Engine {
 
        genserver := gin.Default()
 
-       genserver.MaxMultipartMemory = CONF.MaxFileSize
+       store := sessions.NewCookieStore([]byte("SOLIAGAIN"))
+
+       genserver.Use(sessions.Sessions("SOLIAGAIN", store))
+
+       ConfigureRuntime(genserver)
+
+       RegisterRoutes(genserver)
+
+       return genserver
+
+}
+
+func ConfigureRuntime(e *gin.Engine) {
+
+       e.MaxMultipartMemory = CONF.MaxFileSize
+
+       pkgauth.DEBUG = CONF.Debug
 
        pkgstream.EXTERNAL_URL = CONF.ExternalUrl
 
@@ -31,48 +49,54 @@ func CreateServer() *gin.Engine {
 
        pkgutils.USE_COMPRESS = CONF.Utils.UseCompress
 
+}
+
+func RegisterRoutes(e *gin.Engine) {
+
        // base
 
-       genserver.LoadHTMLGlob("view/*")
+       e.LoadHTMLGlob("view/*")
 
-       genserver.Static("/public", "./public")
+       e.Static("/public", "./public")
 
-       // stream
+       e.GET("/", GetIndex)
 
-       // cctv
+       e.GET("/signin", GetSigninIndex)
 
-       genserver.GET("/cctv", pkgstream.GetCCTVIndex)
+       e.GET("/api/oauth2/google/signin", pkgauth.OauthGoogleLogin)
 
-       genserver.POST("/cctv/create", pkgstream.PostCCTVCreate)
+       e.GET("/oauth2/google/callback", pkgauth.OauthGoogleCallback)
 
-       // cctv local
+       pkgauth.InitAuth()
 
-       genserver.GET("/cctv/local", pkgstream.GetCCTVLocalIndex)
+       // stream
 
-       genserver.GET("/cctv/local/turn/address", pkgstream.GetCCTVLocalTurnServeAddr)
+       // cctv
 
-       genserver.POST("/cctv/local/offer", pkgstream.PostCCTVLocalOffer)
+       e.GET("/cctv", pkgstream.GetCCTVIndex)
 
-       // video
+       e.POST("/api/cctv/create", pkgstream.PostCCTVCreate)
 
-       genserver.GET("/video", pkgstream.GetVideoIndex)
+       e.POST("/api/cctv/delete", pkgstream.PostCCTVDelete)
 
-       genserver.GET("/video/watch", pkgstream.GetVideoWatchPage)
+       go pkgstream.InitRTMPServer()
 
-       genserver.POST("/video/upload", pkgstream.PostVideoUpload)
+       // video
 
-       genserver.GET("/video/watch/c/:contentId", pkgstream.GetVideoWatchContentByID)
+       e.GET("/video", pkgstream.GetVideoIndex)
 
-       // peers
+       e.GET("/api/video/watch", pkgstream.GetVideoWatchPage)
 
-       genserver.GET("/peers", pkgstream.GetPeersIndex)
+       e.POST("/api/video/upload", pkgstream.PostVideoUpload)
 
-       genserver.GET("/peers/signal/address", pkgstream.GetPeersSignalAddress)
+       e.GET("/api/video/watch/c/:contentId", pkgstream.GetVideoWatchContentByID)
 
-       go pkgstream.InitPeersSignalOn("/peers/signal")
+       // peers
 
-       // utils
+       e.GET("/peers", pkgstream.GetPeersIndex)
 
-       return genserver
+       e.GET("/api/peers/signal/address", pkgstream.GetPeersSignalAddress)
+
+       go pkgstream.InitPeersSignalOn("/ch/peers/signal")
 
 }
diff --git a/db/Dockerfile b/db/Dockerfile
new file mode 100644 (file)
index 0000000..ab53684
--- /dev/null
@@ -0,0 +1,8 @@
+FROM mysql:8
+
+ENV MYSQL_ROOT_PASSWORD letsshareitwiththewholeuniverse
+ENV MYSQL_HOST '%'
+
+EXPOSE 3306
+
+ADD ./init.sql /docker-entrypoint-initdb.d
\ No newline at end of file
diff --git a/db/docker-compose.yaml b/db/docker-compose.yaml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/db/init.sql b/db/init.sql
new file mode 100644 (file)
index 0000000..b32f867
--- /dev/null
@@ -0,0 +1,22 @@
+CREATE USER 'seantywork'@'%' IDENTIFIED BY 'letsshareitwiththewholeuniverse';
+
+GRANT ALL PRIVILEGES ON *.* TO 'seantywork'@'%';
+
+CREATE DATABASE sorrylinusdb;
+
+USE sorrylinusdb;
+
+CREATE TABLE users (
+    id VARCHAR(128), 
+    pw VARCHAR(256), 
+    sess VARCHAR(128), 
+    PRIMARY KEY(id)
+    );
+
+CREATE TABLE admin (
+    id VARCHAR(128), 
+    PRIMARY KEY(id)
+    );
+
+
+COMMIT;
\ No newline at end of file
diff --git a/go.mod b/go.mod
index e0fd5618782bedc6dcf7ed123704ccdcaa06c3d2..0aa1a2a108f68896c1e874c24f11f86318b1dde2 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -13,17 +13,23 @@ require (
 )
 
 require (
+       github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
        github.com/bytedance/sonic v1.11.8 // indirect
        github.com/bytedance/sonic/loader v0.1.1 // indirect
        github.com/cloudwego/base64x v0.1.4 // indirect
        github.com/cloudwego/iasm v0.2.0 // indirect
        github.com/gabriel-vasile/mimetype v1.4.4 // indirect
        github.com/gin-contrib/sse v0.1.0 // indirect
+       github.com/gin-gonic/contrib v0.0.0-20240508051311-c1c6bf0061b0 // indirect
        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.22.0 // indirect
        github.com/goccy/go-json v0.10.3 // indirect
+       github.com/gomodule/redigo v2.0.0+incompatible // indirect
        github.com/google/uuid v1.6.0 // indirect
+       github.com/gorilla/context v1.1.2 // indirect
+       github.com/gorilla/securecookie v1.1.2 // indirect
+       github.com/gorilla/sessions v1.3.0 // indirect
        github.com/hashicorp/errwrap v1.1.0 // indirect
        github.com/hashicorp/go-multierror v1.1.0 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
diff --git a/go.sum b/go.sum
index f723dbdfca821e7b5e28288fb8220f77131c106f..5cab8a82ad9c56f2194b4384d73ebd79eadec439 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04=
+github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
 github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
 github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LRA=
@@ -18,6 +20,8 @@ github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy
 github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/contrib v0.0.0-20240508051311-c1c6bf0061b0 h1:EUFmvQ8ffefnSAmaUZd9HZYZSw9w/bFjp3FiNaJ5WmE=
+github.com/gin-gonic/contrib v0.0.0-20240508051311-c1c6bf0061b0/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg=
 github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
 github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -36,11 +40,22 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
 github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 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/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
+github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
+github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
+github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
+github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
+github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
 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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
diff --git a/main.go b/main.go
index cc4dda3f743f42349b10487a9b205d633dd4026d..e22445bd09ad63a525d13c18d9bc921e743360a6 100644 (file)
--- a/main.go
+++ b/main.go
@@ -19,6 +19,6 @@ func main() {
 
        server := solictl.CreateServer()
 
-       server.Run()
+       server.Run(solictl.CONF.ServeAddr + ":" + solictl.CONF.ServePort)
 
 }
diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go
new file mode 100644 (file)
index 0000000..e5b4712
--- /dev/null
@@ -0,0 +1,84 @@
+package auth
+
+import (
+       "encoding/json"
+       "fmt"
+
+       "github.com/gin-gonic/contrib/sessions"
+       "github.com/gin-gonic/gin"
+)
+
+var DEBUG bool = false
+
+func OauthGoogleLogin(c *gin.Context) {
+
+       oauth_state := GenerateStateAuthCookie(c)
+
+       u := GoogleOauthConfig.AuthCodeURL(oauth_state)
+
+       c.Redirect(302, u)
+
+}
+
+func OauthGoogleCallback(c *gin.Context) {
+
+       session := sessions.Default(c)
+
+       var session_id string
+
+       v := session.Get("SOLIAGAIN")
+
+       if v == nil {
+               fmt.Printf("access auth failed: %s\n", "session id not found")
+               return
+       } else {
+               session_id = v.(string)
+       }
+
+       state := c.Request.FormValue("state")
+
+       if state == "" {
+               fmt.Printf("access auth failed: %s\n", "form state not found")
+               return
+       }
+
+       if state != session_id {
+               fmt.Printf("access auth failed: %s\n", "value not matching")
+               c.Redirect(302, "/signin")
+               return
+       }
+
+       data, err := GetUserDataFromGoogle(c.Request.FormValue("code"))
+       if err != nil {
+               fmt.Printf("access auth failed: %s\n", err.Error())
+               c.Redirect(302, "/signin")
+               return
+       }
+
+       var oauth_struct OAuthStruct
+
+       err = json.Unmarshal(data, &oauth_struct)
+
+       if err != nil {
+               fmt.Printf("access auth failed: %s\n", err.Error())
+               c.Redirect(302, "/signin")
+               return
+       }
+
+       if !oauth_struct.VERIFIED_EMAIL {
+               fmt.Printf("access auth failed: %s\n", err.Error())
+               c.Redirect(302, "/signin")
+               return
+       }
+
+       if err != nil {
+               fmt.Printf("access auth failed: %s\n", err.Error())
+               c.Redirect(302, "/signin")
+               return
+       }
+
+       fmt.Println("oauth sign in success")
+
+       c.Redirect(302, "/")
+
+}
diff --git a/pkg/auth/oauth2.go b/pkg/auth/oauth2.go
new file mode 100644 (file)
index 0000000..27905d6
--- /dev/null
@@ -0,0 +1,127 @@
+package auth
+
+import (
+       "context"
+       "crypto/rand"
+       "encoding/base64"
+       "encoding/json"
+       "fmt"
+       "io"
+       "net/http"
+       "os"
+
+       "github.com/gin-gonic/contrib/sessions"
+       "github.com/gin-gonic/gin"
+       "golang.org/x/oauth2"
+       "golang.org/x/oauth2/google"
+)
+
+type OAuthJSON struct {
+       Web struct {
+               ClientID                string   `json:"client_id"`
+               ProjectID               string   `json:"project_id"`
+               AuthURI                 string   `json:"auth_uri"`
+               TokenURI                string   `json:"token_uri"`
+               AuthProviderX509CertURL string   `json:"auth_provider_x509_cert_url"`
+               ClientSecret            string   `json:"client_secret"`
+               RedirectUris            []string `json:"redirect_uris"`
+       } `json:"web"`
+}
+
+const OauthGoogleUrlAPI = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
+
+type OAuthStruct struct {
+       ID             string `json:"id"`
+       EMAIL          string `json:"email"`
+       VERIFIED_EMAIL bool   `json:"verified_email"`
+       PICTURE        string `json:"picture"`
+}
+
+var OAUTH_JSON OAuthJSON
+
+var GoogleOauthConfig *oauth2.Config
+
+func InitAuth() {
+
+       OAUTH_JSON = GetOAuthJSON()
+
+       GoogleOauthConfig = GenerateGoogleOauthConfig()
+
+}
+
+func GetOAuthJSON() OAuthJSON {
+
+       var cj OAuthJSON
+
+       file_byte, err := os.ReadFile("oauth.json")
+
+       if err != nil {
+               panic(err)
+       }
+
+       err = json.Unmarshal(file_byte, &cj)
+
+       if err != nil {
+               panic(err)
+       }
+
+       return cj
+
+}
+
+func GenerateGoogleOauthConfig() *oauth2.Config {
+
+       google_oauth_config := &oauth2.Config{
+               ClientID:     OAUTH_JSON.Web.ClientID,
+               ClientSecret: OAUTH_JSON.Web.ClientSecret,
+               Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
+               Endpoint:     google.Endpoint,
+       }
+
+       if DEBUG {
+
+               google_oauth_config.RedirectURL = OAUTH_JSON.Web.RedirectUris[0]
+
+       } else {
+
+               google_oauth_config.RedirectURL = OAUTH_JSON.Web.RedirectUris[1]
+       }
+
+       fmt.Println(google_oauth_config.RedirectURL)
+
+       return google_oauth_config
+
+}
+
+func GenerateStateAuthCookie(c *gin.Context) string {
+
+       b := make([]byte, 64)
+       rand.Read(b)
+
+       state := base64.URLEncoding.EncodeToString(b)
+
+       session := sessions.Default(c)
+
+       session.Set("SOLIAGAIN", state)
+       session.Save()
+
+       return state
+}
+
+func GetUserDataFromGoogle(code string) ([]byte, error) {
+
+       token, err := GoogleOauthConfig.Exchange(context.Background(), code)
+       if err != nil {
+               return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
+       }
+       response, err := http.Get(OauthGoogleUrlAPI + token.AccessToken)
+       if err != nil {
+               return nil, fmt.Errorf("failed getting user info: %s", err.Error())
+       }
+       defer response.Body.Close()
+       contents, err := io.ReadAll(response.Body)
+       if err != nil {
+               return nil, fmt.Errorf("failed read response: %s", err.Error())
+       }
+       return contents, nil
+}
diff --git a/pkg/dbquery/dbquery.go b/pkg/dbquery/dbquery.go
new file mode 100644 (file)
index 0000000..7cdbc2f
--- /dev/null
@@ -0,0 +1 @@
+package dbquery
diff --git a/pkg/sorrylinushub/sorrylinushub.go b/pkg/sorrylinushub/sorrylinushub.go
new file mode 100644 (file)
index 0000000..32f6165
--- /dev/null
@@ -0,0 +1 @@
+package sorrylinushub
index da81a2c640612d0a6222ef687bb561dfc01bcb12..e2c0e61357c4f8d4c919d31e2f760e571ede0d33 100644 (file)
@@ -4,6 +4,7 @@ import (
        "bytes"
        "encoding/binary"
        "encoding/json"
+       "fmt"
        "io"
        "log"
        "net"
@@ -18,6 +19,27 @@ import (
        rtmpmsg "github.com/yutopp/go-rtmp/message"
 )
 
+var RTP_RECEIVE_ADDR string
+
+var RTP_RECEIVE_PORT string
+
+var RTP_CONSUMERS = make(map[string]RTMPWebRTCPeer)
+
+const RTP_HEADER_LENGTH_FIELD = 4
+
+var TEST_KEY string = "foobar"
+
+type RTMPHandler struct {
+       rtmp.DefaultHandler
+       PublisherKey string
+}
+
+type RTMPWebRTCPeer struct {
+       peerConnection *webrtc.PeerConnection
+       videoTrack     *webrtc.TrackLocalStaticSample
+       audioTrack     *webrtc.TrackLocalStaticSample
+}
+
 func GetCCTVIndex(c *gin.Context) {
 
        c.HTML(200, "cctv.html", gin.H{
@@ -27,6 +49,7 @@ func GetCCTVIndex(c *gin.Context) {
 }
 
 func PostCCTVCreate(c *gin.Context) {
+
        log.Println("Incoming HTTP Request")
 
        peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{})
@@ -50,8 +73,20 @@ func PostCCTVCreate(c *gin.Context) {
                panic(err)
        }
 
+       var req CLIENT_REQ
+
        var offer webrtc.SessionDescription
-       if err := json.NewDecoder(c.Request.Body).Decode(&offer); err != nil {
+
+       if err := c.BindJSON(&req); err != nil {
+
+               panic(err)
+
+       }
+
+       err = json.Unmarshal([]byte(req.Data), &offer)
+
+       if err != nil {
+
                panic(err)
        }
 
@@ -68,15 +103,43 @@ func PostCCTVCreate(c *gin.Context) {
        }
        <-gatherComplete
 
+       /*
+
+               TODO:
+                       remove test key
+
+       */
+       RTP_CONSUMERS[TEST_KEY] = RTMPWebRTCPeer{
+               peerConnection: peerConnection,
+               videoTrack:     videoTrack,
+               audioTrack:     audioTrack,
+       }
+
        c.JSON(200, peerConnection.LocalDescription())
 
-       go startRTMPServer(peerConnection, videoTrack, audioTrack)
 }
 
-func startRTMPServer(peerConnection *webrtc.PeerConnection, videoTrack, audioTrack *webrtc.TrackLocalStaticSample) {
+func PostCCTVDelete(c *gin.Context) {
+
+       /*
+
+               TODO:
+                       sorrylinus exchange
+
+       */
+
+       var resp SERVER_RE
+
+       resp.Status = "success"
+       resp.Reply = ""
+
+       c.JSON(200, resp)
+}
+
+func InitRTMPServer() {
        log.Println("Starting RTMP Server")
 
-       tcpAddr, err := net.ResolveTCPAddr("tcp", ":8084")
+       tcpAddr, err := net.ResolveTCPAddr("tcp", RTP_RECEIVE_ADDR+":"+RTP_RECEIVE_PORT)
        if err != nil {
                log.Panicf("Failed: %+v", err)
        }
@@ -89,11 +152,7 @@ func startRTMPServer(peerConnection *webrtc.PeerConnection, videoTrack, audioTra
        srv := rtmp.NewServer(&rtmp.ServerConfig{
                OnConnect: func(conn net.Conn) (io.ReadWriteCloser, *rtmp.ConnConfig) {
                        return conn, &rtmp.ConnConfig{
-                               Handler: &Handler{
-                                       peerConnection: peerConnection,
-                                       videoTrack:     videoTrack,
-                                       audioTrack:     audioTrack,
-                               },
+                               Handler: &RTMPHandler{},
 
                                ControlState: rtmp.StreamControlStateConfig{
                                        DefaultBandwidthWindowSize: 6 * 1024 * 1024 / 8,
@@ -106,36 +165,54 @@ func startRTMPServer(peerConnection *webrtc.PeerConnection, videoTrack, audioTra
        }
 }
 
-type Handler struct {
-       rtmp.DefaultHandler
-       peerConnection         *webrtc.PeerConnection
-       videoTrack, audioTrack *webrtc.TrackLocalStaticSample
-}
-
-func (h *Handler) OnServe(conn *rtmp.Conn) {
+func (h *RTMPHandler) OnServe(conn *rtmp.Conn) {
 }
 
-func (h *Handler) OnConnect(timestamp uint32, cmd *rtmpmsg.NetConnectionConnect) error {
+func (h *RTMPHandler) OnConnect(timestamp uint32, cmd *rtmpmsg.NetConnectionConnect) error {
        log.Printf("OnConnect: %#v", cmd)
        return nil
 }
 
-func (h *Handler) OnCreateStream(timestamp uint32, cmd *rtmpmsg.NetConnectionCreateStream) error {
+func (h *RTMPHandler) OnCreateStream(timestamp uint32, cmd *rtmpmsg.NetConnectionCreateStream) error {
        log.Printf("OnCreateStream: %#v", cmd)
        return nil
 }
 
-func (h *Handler) OnPublish(ctx *rtmp.StreamContext, timestamp uint32, cmd *rtmpmsg.NetStreamPublish) error {
+func (h *RTMPHandler) OnPublish(ctx *rtmp.StreamContext, timestamp uint32, cmd *rtmpmsg.NetStreamPublish) error {
        log.Printf("OnPublish: %#v", cmd)
 
        if cmd.PublishingName == "" {
-               return errors.New("PublishingName is empty")
+
+               log.Printf("publishing name is empty")
+
+               return errors.New("publishing name is empty")
        }
+
+       /*
+
+               TODO:
+                       key validation
+
+       */
+
+       h.PublisherKey = cmd.PublishingName
+
        return nil
 }
 
-func (h *Handler) OnAudio(timestamp uint32, payload io.Reader) error {
+func (h *RTMPHandler) OnAudio(timestamp uint32, payload io.Reader) error {
        var audio flvtag.AudioData
+
+       consumer, okay := RTP_CONSUMERS[h.PublisherKey]
+
+       if !okay {
+
+               return fmt.Errorf("invalid publisher")
+
+       }
+
+       consumerAudioTrack := consumer.audioTrack
+
        if err := flvtag.DecodeAudioData(payload, &audio); err != nil {
                return err
        }
@@ -145,16 +222,25 @@ func (h *Handler) OnAudio(timestamp uint32, payload io.Reader) error {
                return err
        }
 
-       return h.audioTrack.WriteSample(media.Sample{
+       return consumerAudioTrack.WriteSample(media.Sample{
                Data:     data.Bytes(),
                Duration: 128 * time.Millisecond,
        })
 }
 
-const headerLengthField = 4
-
-func (h *Handler) OnVideo(timestamp uint32, payload io.Reader) error {
+func (h *RTMPHandler) OnVideo(timestamp uint32, payload io.Reader) error {
        var video flvtag.VideoData
+
+       consumer, okay := RTP_CONSUMERS[h.PublisherKey]
+
+       if !okay {
+
+               return fmt.Errorf("invalid publisher")
+
+       }
+
+       consumerVideoTrack := consumer.videoTrack
+
        if err := flvtag.DecodeVideoData(payload, &video); err != nil {
                return err
        }
@@ -167,24 +253,24 @@ func (h *Handler) OnVideo(timestamp uint32, payload io.Reader) error {
        outBuf := []byte{}
        videoBuffer := data.Bytes()
        for offset := 0; offset < len(videoBuffer); {
-               bufferLength := int(binary.BigEndian.Uint32(videoBuffer[offset : offset+headerLengthField]))
+               bufferLength := int(binary.BigEndian.Uint32(videoBuffer[offset : offset+RTP_HEADER_LENGTH_FIELD]))
                if offset+bufferLength >= len(videoBuffer) {
                        break
                }
 
-               offset += headerLengthField
+               offset += RTP_HEADER_LENGTH_FIELD
                outBuf = append(outBuf, []byte{0x00, 0x00, 0x00, 0x01}...)
                outBuf = append(outBuf, videoBuffer[offset:offset+bufferLength]...)
 
                offset += int(bufferLength)
        }
 
-       return h.videoTrack.WriteSample(media.Sample{
+       return consumerVideoTrack.WriteSample(media.Sample{
                Data:     outBuf,
                Duration: time.Second / 30,
        })
 }
 
-func (h *Handler) OnClose() {
+func (h *RTMPHandler) OnClose() {
        log.Printf("OnClose")
 }
index 64cc3571bca772fbdeb53f44625e2f67ca293902..3ecd71274570a29ed3115e55b279d9c12481fdb0 100644 (file)
@@ -14,10 +14,6 @@ import (
 
 var UDP_BUFFER_BYTE_SIZE int
 
-var RTP_RECEIVE_ADDR string
-
-var RTP_RECEIVE_PORT string
-
 var RECV_STARTED int = 0
 
 func GetCCTVLocalIndex(c *gin.Context) {
index cd924023ce8d8d6866fdb49035fc877f5f7a99cf..cf6460371e364485e65b6ef5e5db7ac456abd544 100644 (file)
@@ -6,6 +6,8 @@ import (
        "github.com/gin-gonic/gin"
 )
 
+var PEERS_SIGNAL_PATH string
+
 func GetPeersIndex(c *gin.Context) {
 
        c.HTML(200, "peers.html", gin.H{
@@ -16,7 +18,7 @@ func GetPeersIndex(c *gin.Context) {
 
 func GetPeersSignalAddress(c *gin.Context) {
 
-       s_addr := EXTERNAL_URL + ":" + SIGNAL_PORT + "/peers/signal"
+       s_addr := EXTERNAL_URL + ":" + SIGNAL_PORT + PEERS_SIGNAL_PATH
 
        c.JSON(http.StatusOK, SERVER_RE{Status: "success", Reply: s_addr})
 
@@ -24,6 +26,8 @@ func GetPeersSignalAddress(c *gin.Context) {
 
 func InitPeersSignalOn(peerSignalPath string) {
 
+       PEERS_SIGNAL_PATH = peerSignalPath
+
        runPeersSignalHandlerForWS(peerSignalPath)
 
 }
index 5b0ed747d8b2029e523cf885876a6d03ced474cf..da13b66b9d4e14e3bda95ca58a4de3fba8552fff 100644 (file)
@@ -23,13 +23,13 @@ var UPGRADER = websocket.Upgrader{}
 
 type SIGNAL_INFO struct {
        Command string `json:"command"`
-       UserID  string `json:"user_id"`
+       Status  string `json:"status"`
        Data    string `json:"data"`
 }
 
 var listLock sync.RWMutex
-var peerConnections []peerConnectionState
-var trackLocals map[string]*webrtc.TrackLocalStaticRTP
+var peerConnections = make([]peerConnectionState, 0)
+var trackLocals = make(map[string]*webrtc.TrackLocalStaticRTP)
 
 type peerConnectionState struct {
        peerConnection *webrtc.PeerConnection
@@ -199,7 +199,7 @@ func peerSignalHandler(w http.ResponseWriter, r *http.Request) {
 
        }
 
-       uid := sinfo.UserID
+       uid := sinfo.Data
 
        old_c, okay := USER_SIGNAL[uid]
 
@@ -217,7 +217,7 @@ func peerSignalHandler(w http.ResponseWriter, r *http.Request) {
 
                new_uinfo := SIGNAL_INFO{
                        Command: "ADDUSER",
-                       UserID:  uid,
+                       Data:    uid,
                }
 
                v.WriteJSON(&new_uinfo)
@@ -225,7 +225,7 @@ func peerSignalHandler(w http.ResponseWriter, r *http.Request) {
                old_uinfo := SIGNAL_INFO{
 
                        Command: "ADDUSER",
-                       UserID:  k,
+                       Data:    k,
                }
 
                c.WriteJSON(&old_uinfo)
diff --git a/public/js/cctv.js b/public/js/cctv.js
new file mode 100644 (file)
index 0000000..e4d486f
--- /dev/null
@@ -0,0 +1,43 @@
+pc = {}
+
+
+
+async function initCCTV(){
+
+    pc = new RTCPeerConnection()
+    pc.ontrack = function (event) {
+        var el = document.createElement(event.track.kind)
+        el.srcObject = event.streams[0]
+        el.autoplay = true
+        el.controls = true
+
+        document.getElementById('rtmpFeed').appendChild(el)
+    }
+
+    pc.addTransceiver('video')
+    pc.addTransceiver('audio')
+    
+    let offer = await pc.createOffer()
+
+    pc.setLocalDescription(offer)
+
+    console.log(offer)
+
+    let req = {
+        data: JSON.stringify(offer)
+    }
+
+    let resp = await axios.post("/api/cctv/create", req)
+
+    pc.setRemoteDescription(resp.data)
+
+    console.log("init success")
+
+}
+
+
+function closeCCTV(){
+
+
+
+}
index 41eee6dd9d462e41b5a1a569fd1cd9f411c404e5..fef3cd47f83a1e1063cd4abc1f4207f56f2a7e74 100644 (file)
@@ -34,7 +34,7 @@ async function startCCTVOffer(){
 
     offerjson.data = offer_val
 
-    let result = await axios.post("/cctv/offer", offerjson)
+    let result = await axios.post("/api/cctv/offer", offerjson)
 
     if (result.data.status != "success") {
 
@@ -65,7 +65,7 @@ function startSession() {
 
 async function getTurnServerAddressAndInit(){
 
-    let result = await axios.get("/cctv/turn/address")
+    let result = await axios.get("/api/cctv/turn/address")
 
     if(result.data.status != "success"){
 
index 0798c29d9faeb4d3723c72b8f46a4925f54765c1..2db577d7005caedfd61a77ce5bfb176084eb00c1 100644 (file)
@@ -74,6 +74,7 @@ function initSignal(){
                     if (!offer) {
                         return console.log('failed to parse answer')
                     }
+
                     pc.setRemoteDescription(offer)
                     pc.createAnswer().then(function(answer) {
                         pc.setLocalDescription(answer)
index 29989b8f24129979d2f14002111a88a79624dc77..5aa7283e5397e1c0e4218afc3a6e33afe7392984 100644 (file)
@@ -63,7 +63,7 @@ function initPeers() {
 
             sdpjson.data  = btoa(JSON.stringify(pcSender.localDescription))
 
-            let resp = await axios.post("/peers/room/sdp/m/" + meetingId + "/c/"+ userId + "/s/" + true, sdpjson)
+            let resp = await axios.post("/api/peers/room/sdp/m/" + meetingId + "/c/"+ userId + "/s/" + true, sdpjson)
 
             pcSender.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(resp.data.reply))))
             
index 6d80c73d4d13533c9f9cb56bab4c7d284d0700a6..3134fc68db5bf3d45011d203bf4bf1e67f24e6d5 100644 (file)
@@ -1,41 +1,18 @@
 <html>
   <head>
     <title> RTMP to WebRTC </title>
+    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
   </head>
 
   <body>
     <h1> RTMP to WebRTC </h1>
     <div id="rtmpFeed"></div>
-  </body>
 
-  <script>
-    let pc = new RTCPeerConnection()
-    pc.ontrack = function (event) {
-      var el = document.createElement(event.track.kind)
-      el.srcObject = event.streams[0]
-      el.autoplay = true
-      el.controls = true
+    <button onclick="initCCTV()"> start cctv </button>
+    
+
+  </body>
 
-      document.getElementById('rtmpFeed').appendChild(el)
-    }
+  <script src="/public/js/cctv.js"></script>
 
-    pc.addTransceiver('video')
-    pc.addTransceiver('audio')
-    pc.createOffer()
-      .then(offer => {
-        pc.setLocalDescription(offer)
-        console.log(offer)
-        return fetch(`/cctv/create`, {
-          method: 'post',
-          headers: {
-            'Accept': 'application/json, text/plain, */*',
-            'Content-Type': 'application/json'
-          },
-          body: JSON.stringify(offer)
-        })
-      })
-      .then(res => res.json())
-      .then(res => pc.setRemoteDescription(res))
-      .catch(alert)
-  </script>
 </html>
\ No newline at end of file
diff --git a/view/index.html b/view/index.html
new file mode 100644 (file)
index 0000000..39afc3b
--- /dev/null
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+  <head>
+    <title>sorrylinus again</title>
+  </head>
+  <body>
+    <p> sorrylinus again </p>
+  </body>
+</html>
\ No newline at end of file
index cf30fffb96e296e604d931843407f9b77381bdab..fed5213f674eaa03398c29bed7ecc9623647774e 100644 (file)
@@ -2,6 +2,9 @@
 <html>
   <head>
     <meta charset="utf-8">
+    <script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
+
   </head>
   <body>
     <h3> Local Video </h3>
diff --git a/view/signin.html b/view/signin.html
new file mode 100644 (file)
index 0000000..38956ac
--- /dev/null
@@ -0,0 +1,69 @@
+
+<html>
+
+<head>
+
+
+</head>
+
+<body>
+<div></div>
+<main>
+    <center>
+        <div></div>
+        <div>
+            <div>
+                <form method="post" action="#">
+                    <div>
+                        <div>
+                        </div>
+                    </div>
+
+                    <div>
+                        <div>
+                            <input type='email' name='email' id='email'/>
+                            <label for='email'>Enter your email</label>
+                        </div>
+                    </div>
+
+                    <div>
+                        <div>
+                            <input type='password' name='password' id='password'/>
+                            <label for='password'>Enter your password</label>
+                        </div>
+                        <label>
+                            <a href='#!'><b>Forgot Password?</b></a>
+                        </label>
+                    </div>
+
+                    <br/>
+                    <div>
+                        <button type='submit' name='btn_login'>
+                            Login
+                        </button>
+                    </div>
+                </form>
+
+                <div>
+                    <div>
+                        <a href="/api/oauth2/google/signin">
+                            <div class="left">
+                                <img width="30px" alt="Google &quot;G&quot; Logo"
+                                     src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png"/>
+                            </div>
+                            Login with Google
+                        </a>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <a href="#!">Create account</a>
+    </center>
+
+    <div class="section"></div>
+    <div class="section"></div>
+</main>
+
+</body>
+
+</html>
\ No newline at end of file