From 49853807a16ec6621f57a1a94b94f1f0c4486aba Mon Sep 17 00:00:00 2001 From: Kristian Ollikainen <14197772+DatCaptainHorse@users.noreply.github.com> Date: Sun, 2 Mar 2025 17:47:25 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=E2=AD=90=20feat(relay):=20Port=20muxing=20?= =?UTF-8?q?and=20TLS=20(#197)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR will work on adding port muxing (share single port for HTTP/WS + WebRTC connections), along with API communication. ## Type of Change - [x] Bug fix (non-breaking change) - [x] New feature (non-breaking change) ## Checklist - [ ] I have updated relevant documentation - [x] My code follows the project's coding style - [x] My changes generate no new warnings/errors --------- Co-authored-by: DatCaptainHorse --- containers/relay.Containerfile | 13 +++++- packages/relay/go.mod | 18 ++++---- packages/relay/go.sum | 32 +++++++------- packages/relay/internal/common.go | 29 ++++++++++--- packages/relay/internal/egress.go | 13 +++--- packages/relay/internal/flags.go | 72 +++++++++++++++++++++++++++---- packages/relay/internal/http.go | 32 ++++++++++---- 7 files changed, 152 insertions(+), 57 deletions(-) diff --git a/containers/relay.Containerfile b/containers/relay.Containerfile index ee36a64e..e9823d27 100644 --- a/containers/relay.Containerfile +++ b/containers/relay.Containerfile @@ -1,20 +1,29 @@ -FROM docker.io/golang:1.23-alpine AS go-build +FROM docker.io/golang:1.24-alpine AS go-build WORKDIR /builder COPY packages/relay/ /builder/ RUN go build -FROM docker.io/golang:1.23-alpine +FROM docker.io/golang:1.24-alpine COPY --from=go-build /builder/relay /relay/relay WORKDIR /relay +# TODO: Switch running layer to just alpine (doesn't need golang dev stack) + # ENV flags ENV VERBOSE=false +ENV DEBUG=false ENV ENDPOINT_PORT=8088 ENV WEBRTC_UDP_START=10000 ENV WEBRTC_UDP_END=20000 ENV STUN_SERVER="stun.l.google.com:19302" +ENV WEBRTC_UDP_MUX=8088 +ENV WEBRTC_NAT_IPS="" +ENV AUTO_ADD_LOCAL_IP=true +ENV TLS_CERT="" +ENV TLS_KEY="" EXPOSE $ENDPOINT_PORT EXPOSE $WEBRTC_UDP_START-$WEBRTC_UDP_END/udp +EXPOSE $WEBRTC_UDP_MUX/udp ENTRYPOINT ["/relay/relay"] \ No newline at end of file diff --git a/packages/relay/go.mod b/packages/relay/go.mod index 2657da28..38719c23 100644 --- a/packages/relay/go.mod +++ b/packages/relay/go.mod @@ -1,32 +1,32 @@ module relay -go 1.23 +go 1.24 require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 + github.com/pion/ice/v4 v4.0.7 github.com/pion/interceptor v0.1.37 - github.com/pion/webrtc/v4 v4.0.8 - google.golang.org/protobuf v1.36.4 + github.com/pion/webrtc/v4 v4.0.12 + google.golang.org/protobuf v1.36.5 ) require ( github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v3 v3.0.4 // indirect - github.com/pion/ice/v4 v4.0.5 // indirect github.com/pion/logging v0.2.3 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.11 // indirect - github.com/pion/sctp v1.8.35 // indirect + github.com/pion/rtp v1.8.12 // indirect + github.com/pion/sctp v1.8.36 // indirect github.com/pion/sdp/v3 v3.0.10 // indirect github.com/pion/srtp/v3 v3.0.4 // indirect github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/turn/v4 v4.0.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect ) diff --git a/packages/relay/go.sum b/packages/relay/go.sum index 56976d55..015ec394 100644 --- a/packages/relay/go.sum +++ b/packages/relay/go.sum @@ -10,8 +10,8 @@ github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= -github.com/pion/ice/v4 v4.0.5 h1:6awVfa1jg9YsI9/Lep4TG/o3kwS1Oayr5b8xz50ibJ8= -github.com/pion/ice/v4 v4.0.5/go.mod h1:JJaoEIxUIlGDA9gaRZbwXYqI3j6VG/QchpjX+QmwN6A= +github.com/pion/ice/v4 v4.0.7 h1:mnwuT3n3RE/9va41/9QJqN5+Bhc0H/x/ZyiVlWMw35M= +github.com/pion/ice/v4 v4.0.7/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= @@ -22,10 +22,10 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk= -github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= -github.com/pion/sctp v1.8.35 h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA= -github.com/pion/sctp v1.8.35/go.mod h1:EcXP8zCYVTRy3W9xtOF7wJm1L1aXfKRQzaM33SjQlzg= +github.com/pion/rtp v1.8.12 h1:nsKs8Wi0jQyBFHU3qmn/OvtZrhktVfJY0vRxwACsL5U= +github.com/pion/rtp v1.8.12/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/sctp v1.8.36 h1:owNudmnz1xmhfYje5L/FCav3V9wpPRePHle3Zi+P+M0= +github.com/pion/sctp v1.8.36/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA= github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= @@ -36,23 +36,23 @@ github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1 github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= -github.com/pion/webrtc/v4 v4.0.8 h1:T1ZmnT9qxIJIt4d8XoiMOBrTClGHDDXNg9e/fh018Qc= -github.com/pion/webrtc/v4 v4.0.8/go.mod h1:HHBeUVBAC+j4ZFnYhovEFStF02Arb1EyD4G7e7HBTJw= +github.com/pion/webrtc/v4 v4.0.12 h1:/omInB15DdJDlA3WoAQAAhIQQvFCWNHdJ2t5e2+ozx4= +github.com/pion/webrtc/v4 v4.0.12/go.mod h1:sMOtH6DSNVu6tfndczTMvJkKnyFVVeq+/G3dval418g= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/packages/relay/internal/common.go b/packages/relay/internal/common.go index 95eaa790..b09abd3a 100644 --- a/packages/relay/internal/common.go +++ b/packages/relay/internal/common.go @@ -1,6 +1,7 @@ package relay import ( + "github.com/pion/ice/v4" "github.com/pion/interceptor" "github.com/pion/webrtc/v4" "log" @@ -38,7 +39,7 @@ func InitWebRTCAPI() error { PayloadType: 49, }, } { - if err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil { + if err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo); err != nil { return err } } @@ -58,12 +59,28 @@ func InitWebRTCAPI() error { // New in v4, reduces CPU usage and latency when enabled settingEngine.EnableSCTPZeroChecksum(true) - // Set the UDP port range used by WebRTC - err = settingEngine.SetEphemeralUDPPortRange(uint16(flags.WebRTCUDPStart), uint16(flags.WebRTCUDPEnd)) - if err != nil { - return err + nat11IPs := GetFlags().NAT11IPs + if len(nat11IPs) > 0 { + settingEngine.SetNAT1To1IPs(nat11IPs, webrtc.ICECandidateTypeHost) } + muxPort := GetFlags().UDPMuxPort + if muxPort > 0 { + mux, err := ice.NewMultiUDPMuxFromPort(muxPort) + if err != nil { + return err + } + settingEngine.SetICEUDPMux(mux) + } else { + // Set the UDP port range used by WebRTC + err = settingEngine.SetEphemeralUDPPortRange(uint16(flags.WebRTCUDPStart), uint16(flags.WebRTCUDPEnd)) + if err != nil { + return err + } + } + + settingEngine.SetIncludeLoopbackCandidate(true) // Just in case + // Create a new API object with our customized settings globalWebRTCAPI = webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine), webrtc.WithSettingEngine(settingEngine), webrtc.WithInterceptorRegistry(interceptorRegistry)) @@ -88,7 +105,7 @@ func CreatePeerConnection(onClose func()) (*webrtc.PeerConnection, error) { if connectionState == webrtc.PeerConnectionStateFailed || connectionState == webrtc.PeerConnectionStateDisconnected || connectionState == webrtc.PeerConnectionStateClosed { - err := pc.Close() + err = pc.Close() if err != nil { log.Printf("Error closing PeerConnection: %s\n", err.Error()) } diff --git a/packages/relay/internal/egress.go b/packages/relay/internal/egress.go index 712f0fde..2cf3d661 100644 --- a/packages/relay/internal/egress.go +++ b/packages/relay/internal/egress.go @@ -25,7 +25,7 @@ func participantHandler(participant *Participant, room *Room) { } // Data channel settings - settingOrdered := false + settingOrdered := true settingMaxRetransmits := uint16(0) dc, err := participant.PeerConnection.CreateDataChannel("data", &webrtc.DataChannelInit{ Ordered: &settingOrdered, @@ -75,13 +75,10 @@ func participantHandler(participant *Participant, room *Room) { log.Printf("Failed to marshal input message for participant: '%s' in room: '%s' - reason: %s\n", participant.ID, room.Name, err) return } - if err = room.DataChannel.SendBinary(data); err != nil { - log.Printf("Failed to send input message to room: '%s' - reason: %s\n", room.Name, err) - } - } else { - if err = room.DataChannel.SendBinary(data); err != nil { - log.Printf("Failed to send input message to room: '%s' - reason: %s\n", room.Name, err) - } + } + + if err = room.DataChannel.SendBinary(data); err != nil { + log.Printf("Failed to send input message to room: '%s' - reason: %s\n", room.Name, err) } } }) diff --git a/packages/relay/internal/flags.go b/packages/relay/internal/flags.go index 6a1c0398..765ff804 100644 --- a/packages/relay/internal/flags.go +++ b/packages/relay/internal/flags.go @@ -2,22 +2,28 @@ package relay import ( "flag" + "github.com/pion/webrtc/v4" "log" + "net" "os" "strconv" - - "github.com/pion/webrtc/v4" + "strings" ) var globalFlags *Flags type Flags struct { - Verbose bool - Debug bool - EndpointPort int - WebRTCUDPStart int - WebRTCUDPEnd int - STUNServer string + Verbose bool // Verbose mode - log more information to console + Debug bool // Debug mode - log deeper debug information to console + EndpointPort int // Port for HTTP/S and WS/S endpoint (TCP) + WebRTCUDPStart int // WebRTC UDP port range start - ignored if UDPMuxPort is set + WebRTCUDPEnd int // WebRTC UDP port range end - ignored if UDPMuxPort is set + STUNServer string // WebRTC STUN server + UDPMuxPort int // WebRTC UDP mux port - if set, overrides UDP port range + AutoAddLocalIP bool // Automatically add local IP to NAT 1 to 1 IPs + NAT11IPs []string // WebRTC NAT 1 to 1 IP(s) - allows specifying host IP(s) if behind NAT + TLSCert string // Path to TLS certificate + TLSKey string // Path to TLS key } func (flags *Flags) DebugLog() { @@ -28,6 +34,13 @@ func (flags *Flags) DebugLog() { log.Println("> WebRTC UDP Range Start: ", flags.WebRTCUDPStart) log.Println("> WebRTC UDP Range End: ", flags.WebRTCUDPEnd) log.Println("> WebRTC STUN Server: ", flags.STUNServer) + log.Println("> WebRTC UDP Mux Port: ", flags.UDPMuxPort) + log.Println("> Auto Add Local IP: ", flags.AutoAddLocalIP) + for i, ip := range flags.NAT11IPs { + log.Printf("> WebRTC NAT 1 to 1 IP (%d): %s\n", i, ip) + } + log.Println("> Path to TLS Cert: ", flags.TLSCert) + log.Println("> Path to TLS Key: ", flags.TLSKey) } func getEnvAsInt(name string, defaultVal int) int { @@ -66,6 +79,13 @@ func InitFlags() { flag.IntVar(&globalFlags.WebRTCUDPStart, "webrtcUDPStart", getEnvAsInt("WEBRTC_UDP_START", 10000), "WebRTC UDP port range start") flag.IntVar(&globalFlags.WebRTCUDPEnd, "webrtcUDPEnd", getEnvAsInt("WEBRTC_UDP_END", 20000), "WebRTC UDP port range end") flag.StringVar(&globalFlags.STUNServer, "stunServer", getEnvAsString("STUN_SERVER", "stun.l.google.com:19302"), "WebRTC STUN server") + flag.IntVar(&globalFlags.UDPMuxPort, "webrtcUDPMux", getEnvAsInt("WEBRTC_UDP_MUX", 8088), "WebRTC UDP mux port") + flag.BoolVar(&globalFlags.AutoAddLocalIP, "autoAddLocalIP", getEnvAsBool("AUTO_ADD_LOCAL_IP", true), "Automatically add local IP to NAT 1 to 1 IPs") + // String with comma separated IPs + nat11IPs := "" + flag.StringVar(&nat11IPs, "webrtcNAT11IPs", getEnvAsString("WEBRTC_NAT_IPS", ""), "WebRTC NAT 1 to 1 IP(s)") + flag.StringVar(&globalFlags.TLSCert, "tlsCert", getEnvAsString("TLS_CERT", ""), "Path to TLS certificate") + flag.StringVar(&globalFlags.TLSKey, "tlsKey", getEnvAsString("TLS_KEY", ""), "Path to TLS key") // Parse flags flag.Parse() @@ -75,8 +95,44 @@ func InitFlags() { URLs: []string{"stun:" + globalFlags.STUNServer}, }, } + + // Initialize NAT 1 to 1 IPs + globalFlags.NAT11IPs = []string{} + + // Get local IP + if globalFlags.AutoAddLocalIP { + globalFlags.NAT11IPs = append(globalFlags.NAT11IPs, getLocalIP()) + } + + // Parse NAT 1 to 1 IPs from string + if len(nat11IPs) > 0 { + split := strings.Split(nat11IPs, ",") + if len(split) > 0 { + for _, ip := range split { + globalFlags.NAT11IPs = append(globalFlags.NAT11IPs, ip) + } + } else { + globalFlags.NAT11IPs = append(globalFlags.NAT11IPs, nat11IPs) + } + } } func GetFlags() *Flags { return globalFlags } + +// getLocalIP returns local IP, be it either IPv4 or IPv6, skips loopback addresses +func getLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "" + } + for _, address := range addrs { + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil || ipnet.IP != nil { + return ipnet.IP.String() + } + } + } + return "" +} diff --git a/packages/relay/internal/http.go b/packages/relay/internal/http.go index 2012d000..f01a0aa3 100644 --- a/packages/relay/internal/http.go +++ b/packages/relay/internal/http.go @@ -2,6 +2,7 @@ package relay import ( "encoding/json" + "errors" "github.com/gorilla/websocket" "log" "net/http" @@ -10,7 +11,7 @@ import ( var httpMux *http.ServeMux -func InitHTTPEndpoint() { +func InitHTTPEndpoint() error { // Create HTTP mux which serves our WS endpoint httpMux = http.NewServeMux() @@ -20,15 +21,30 @@ func InitHTTPEndpoint() { // Get our serving port port := GetFlags().EndpointPort + tlsCert := GetFlags().TLSCert + tlsKey := GetFlags().TLSKey // Log and start the endpoint server - log.Println("Starting HTTP endpoint server on :", strconv.Itoa(port)) - go func() { - log.Fatal((&http.Server{ - Handler: httpMux, - Addr: ":" + strconv.Itoa(port), - }).ListenAndServe()) - }() + if len(tlsCert) <= 0 && len(tlsKey) <= 0 { + log.Println("Starting HTTP endpoint server on :", strconv.Itoa(port)) + go func() { + log.Fatal((&http.Server{ + Handler: httpMux, + Addr: ":" + strconv.Itoa(port), + }).ListenAndServe()) + }() + } else if len(tlsCert) > 0 && len(tlsKey) > 0 { + log.Println("Starting HTTPS endpoint server on :", strconv.Itoa(port)) + go func() { + log.Fatal((&http.Server{ + Handler: httpMux, + Addr: ":" + strconv.Itoa(port), + }).ListenAndServeTLS(tlsCert, tlsKey)) + }() + } else { + return errors.New("no TLS certificate or TLS key provided") + } + return nil } // logHTTPError logs (if verbose) and sends an error code to requester From 9ab4b6580c0d4df1e68877ed17c082217fcbab8e Mon Sep 17 00:00:00 2001 From: Wanjohi <71614375+wanjohiryan@users.noreply.github.com> Date: Mon, 3 Mar 2025 13:36:13 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9C=20fix(db):=20Fix=20database=20?= =?UTF-8?q?state=20issues=20(#203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This attempts to fix all database issues for all teammates ## Related Issues 1. Getting funny errors while trying to run `sst dev` 2. The neon pulumi stuff trying to create a db each time on push ## Type of Change - [x] Bug fix (non-breaking change) - [ ] New feature (non-breaking change) - [ ] Breaking change (fix or feature that changes existing functionality) - [ ] Documentation update - [ ] Other (please describe): ## Checklist - [ ] I have updated relevant documentation - [ ] My code follows the project's coding style - [x] My changes generate no new warnings/errors ## Notes for Reviewers ## Screenshots/Demo ## Additional Context --- infra/database.ts | 14 ++++++++------ packages/core/drizzle.config.ts | 10 +++++++++- packages/core/src/drizzle/index.ts | 10 +++++++++- packages/functions/src/auth.ts | 12 ++++++------ 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/infra/database.ts b/infra/database.ts index e758ab42..8a96287a 100644 --- a/infra/database.ts +++ b/infra/database.ts @@ -1,27 +1,29 @@ //Created manually from the dashboard and shared with the whole team/org const dbProject = neon.getProjectOutput({ - id:"red-mud-17843368" + id: "little-band-76544985", }) const dbBranchId = $app.stage !== "production" ? - new neon.Branch("DatabaseBranch", { + new neon.Branch("NeonBranch", { parentId: dbProject.defaultBranchId, projectId: dbProject.id, name: $app.stage, }).id : dbProject.defaultBranchId -const dbEndpoint = new neon.Endpoint("NestriEndpoint", { +const dbEndpoint = new neon.Endpoint("NeonEndpoint", { projectId: dbProject.id, - branchId: dbBranchId + branchId: dbBranchId, + poolerEnabled: true, + type: "read_write", }) -const dbRole = new neon.Role("AdminRole", { +const dbRole = new neon.Role("NeonRole", { name: "admin", branchId: dbBranchId, projectId: dbProject.id, }) -const db = new neon.Database("NestriDatabase", { +const db = new neon.Database("NeonDatabase", { branchId: dbBranchId, projectId: dbProject.id, ownerName: dbRole.name, diff --git a/packages/core/drizzle.config.ts b/packages/core/drizzle.config.ts index 98e3f64a..efff367e 100644 --- a/packages/core/drizzle.config.ts +++ b/packages/core/drizzle.config.ts @@ -1,12 +1,20 @@ import { Resource } from "sst"; import { defineConfig } from "drizzle-kit"; +function addPoolerSuffix(original: string): string { + const firstDotIndex = original.indexOf('.'); + if (firstDotIndex === -1) return original + '-pooler'; + return original.slice(0, firstDotIndex) + '-pooler' + original.slice(firstDotIndex); + } + +const dbHost = addPoolerSuffix(Resource.Database.host) + export default defineConfig({ schema: "./src/**/*.sql.ts", out: "./migrations", dialect: "postgresql", verbose: true, dbCredentials: { - url: `postgresql://${Resource.Database.user}:${Resource.Database.password}@${Resource.Database.host}/${Resource.Database.name}?sslmode=require`, + url: `postgresql://${Resource.Database.user}:${Resource.Database.password}@${dbHost}/${Resource.Database.name}?sslmode=require`, }, }); \ No newline at end of file diff --git a/packages/core/src/drizzle/index.ts b/packages/core/src/drizzle/index.ts index 47d0e4d1..32d53ff2 100644 --- a/packages/core/src/drizzle/index.ts +++ b/packages/core/src/drizzle/index.ts @@ -7,7 +7,15 @@ import { Pool, neonConfig } from "@neondatabase/serverless"; neonConfig.webSocketConstructor = ws; -const client = new Pool({ connectionString: `postgres://${Resource.Database.user}:${Resource.Database.password}@${Resource.Database.host}/${Resource.Database.name}?sslmode=require` }) +function addPoolerSuffix(original: string): string { + const firstDotIndex = original.indexOf('.'); + if (firstDotIndex === -1) return original + '-pooler'; + return original.slice(0, firstDotIndex) + '-pooler' + original.slice(firstDotIndex); + } + +const dbHost = addPoolerSuffix(Resource.Database.host) + +const client = new Pool({ connectionString: `postgres://${Resource.Database.user}:${Resource.Database.password}@${dbHost}/${Resource.Database.name}?sslmode=require` }) export const db = neonDrizzle(client, { logger: diff --git a/packages/functions/src/auth.ts b/packages/functions/src/auth.ts index 41db11cf..513800e0 100644 --- a/packages/functions/src/auth.ts +++ b/packages/functions/src/auth.ts @@ -64,12 +64,12 @@ const app = issuer({ PasswordUI({ sendCode: async (email, code) => { console.log("email & code:", email, code) - await Email.send( - "auth", - email, - `Nestri code: ${code}`, - `Your Nestri login code is ${code}`, - ) + // await Email.send( + // "auth", + // email, + // `Nestri code: ${code}`, + // `Your Nestri login code is ${code}`, + // ) }, }), ), From 1aeafec40b483f89625621bc792fbb7aeb5facbd Mon Sep 17 00:00:00 2001 From: Wanjohi <71614375+wanjohiryan@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:59:48 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9C=20fix(db):=20Migrate=20to=20Ne?= =?UTF-8?q?on=20org=20account=20(#204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Migrates to a neon account shared by the Nestrilabs organisation ## Related Issues ## Type of Change - [ ] Bug fix (non-breaking change) - [ ] New feature (non-breaking change) - [ ] Breaking change (fix or feature that changes existing functionality) - [ ] Documentation update - [ ] Other (please describe): ## Checklist - [ ] I have updated relevant documentation - [ ] My code follows the project's coding style - [ ] My changes generate no new warnings/errors ## Notes for Reviewers ## Screenshots/Demo ## Additional Context --- infra/bus.ts | 2 +- infra/database.ts | 2 +- infra/www.ts | 2 +- packages/www/src/pages/new.tsx | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/bus.ts b/infra/bus.ts index cbc44fde..86b15f01 100644 --- a/infra/bus.ts +++ b/infra/bus.ts @@ -1,6 +1,6 @@ -import { database } from "./database"; import { email } from "./email"; import { allSecrets } from "./secret"; +import { database } from "./database"; export const bus = new sst.aws.Bus("Bus"); diff --git a/infra/database.ts b/infra/database.ts index 8a96287a..3f8f9193 100644 --- a/infra/database.ts +++ b/infra/database.ts @@ -1,6 +1,6 @@ //Created manually from the dashboard and shared with the whole team/org const dbProject = neon.getProjectOutput({ - id: "little-band-76544985", + id: "black-sky-26872933" }) const dbBranchId = $app.stage !== "production" ? diff --git a/infra/www.ts b/infra/www.ts index 24ad1784..6fe83134 100644 --- a/infra/www.ts +++ b/infra/www.ts @@ -1,6 +1,6 @@ // This is the website part where people play and connect -import { auth, api } from "./api"; import { domain } from "./dns"; +import { auth, api } from "./api"; new sst.aws.StaticSite("Web", { path: "./packages/www", diff --git a/packages/www/src/pages/new.tsx b/packages/www/src/pages/new.tsx index fd34c95c..3319de5b 100644 --- a/packages/www/src/pages/new.tsx +++ b/packages/www/src/pages/new.tsx @@ -1,5 +1,5 @@ -import { Container, FullScreen } from "@nestri/www/ui/layout"; import { Text } from "@nestri/www/ui/text"; +import { Container, FullScreen } from "@nestri/www/ui/layout"; export function TeamCreate() { return (