Start to wire up sudo email, add email websocket

This commit is contained in:
Eli Ribble 2026-02-18 17:01:02 +00:00
parent 9cbb81f347
commit ea1af2da53
No known key found for this signature in database
7 changed files with 81 additions and 4 deletions

View file

@ -5,6 +5,8 @@ import (
"fmt"
"sync"
commsemail "github.com/Gleipnir-Technology/nidus-sync/comms/email"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
@ -23,6 +25,12 @@ func Start(ctx context.Context) {
channelJobEmail = make(chan email.Job, 100) // Buffered channel to prevent blocking
channelJobText = make(chan text.Job, 100) // Buffered channel to prevent blocking
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
commsemail.StartWebsocket(ctx, config.ForwardEmailAPIToken)
}()
waitGroup.Add(1)
go func() {
defer waitGroup.Done()

View file

@ -64,7 +64,7 @@ type emailResponse struct {
Message string `json:"message"`
}
var FORWARDEMAIL_API = "https://api.forwardemail.net/v1/emails"
var FORWARDEMAIL_EMAIL_POST_API = "https://api.forwardemail.net/v1/emails"
func Send(ctx context.Context, email Request) (response emailResponse, err error) {
payload, err := json.Marshal(email)
@ -72,7 +72,7 @@ func Send(ctx context.Context, email Request) (response emailResponse, err error
return response, fmt.Errorf("Failed to marshal email request: %w", err)
}
req, _ := http.NewRequest("POST", FORWARDEMAIL_API, bytes.NewReader(payload))
req, _ := http.NewRequest("POST", FORWARDEMAIL_EMAIL_POST_API, bytes.NewReader(payload))
req.SetBasicAuth(config.ForwardEmailAPIToken, "")
req.Header.Add("Content-Type", "application/json")

62
comms/email/websocket.go Normal file
View file

@ -0,0 +1,62 @@
package email
import (
"context"
"errors"
"fmt"
"time"
"github.com/gorilla/websocket"
"github.com/rs/zerolog/log"
)
var FORWARDEMAIL_WS_API = "wss://api.forwardemail.net/v1/ws"
func StartWebsocket(ctx context.Context, api_token string) {
var conn *websocket.Conn
for {
err := ensureConnected(conn, api_token)
if err != nil {
log.Error().Err(err).Msg("Bailing on email websocket")
return
}
select {
case <-ctx.Done():
return
default:
// Read message
message_type, message, err := conn.ReadMessage()
if err != nil {
if !websocket.IsCloseError(err, websocket.CloseNormalClosure) {
conn = nil
}
log.Error().Err(err).Msg("Error reading message")
}
// Process and log the message
log.Info().Int("message_type", message_type).Bytes("message", message).Msg("Got email notification")
}
}
}
func ensureConnected(conn *websocket.Conn, api_token string) error {
if conn != nil {
return nil
}
url := FORWARDEMAIL_WS_API + "?token=" + api_token
for {
new_conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err == nil {
log.Info().Msg("Connected to mail websocket")
*conn = *new_conn
return nil
}
if errors.Is(err, websocket.ErrBadHandshake) {
return fmt.Errorf("Bad handshake connecting to email websocket, bailing.")
}
log.Error().Err(err).Str("url", url).Msg("Error connecting to WebSocket")
time.Sleep(3 * time.Second)
}
}

1
go.mod
View file

@ -48,6 +48,7 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect

2
go.sum
View file

@ -98,6 +98,8 @@ 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/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
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/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=

View file

@ -145,7 +145,7 @@
<i class="bi bi-envelope"></i> Email Testing
</div>
<div class="card-body">
<form>
<form action="/sudo/email" method="POST">
<div class="mb-3">
<label for="emailFrom" class="form-label">From Account</label>
<select class="form-select" id="emailFrom">
@ -163,6 +163,7 @@
type="email"
class="form-control"
id="emailTo"
name="emailTo"
placeholder="user@example.com"
/>
</div>
@ -172,6 +173,7 @@
type="text"
class="form-control"
id="emailSubject"
name="emailSubject"
placeholder="Email Subject"
/>
</div>
@ -180,8 +182,9 @@
<textarea
class="form-control"
id="emailBody"
rows="3"
name="emailBody"
placeholder="Enter your email message here"
rows="3"
></textarea>
</div>
<button type="submit" class="btn btn-warning text-dark">

View file

@ -69,6 +69,7 @@ func Router() chi.Router {
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))
r.Method("GET", "/stadia", auth.NewEnsureAuth(getStadia))
r.Method("GET", "/sudo", authenticatedHandler(getSudo))
r.Method("POST", "/sudo/email", authenticatedHandlerPost(postSudoEmail))
r.Method("POST", "/sudo/sms", authenticatedHandlerPost(postSudoSMS))
r.Method("GET", "/trap/{globalid}", auth.NewEnsureAuth(getTrap))
r.Method("GET", "/text/{destination}", auth.NewEnsureAuth(getTextMessages))