diff --git a/desktop/main.go b/desktop/main.go index d13d358..c841ca7 100644 --- a/desktop/main.go +++ b/desktop/main.go @@ -81,6 +81,9 @@ func (c *CommandHandler) Init() { Data: marshaledPayload, }) } + if ctx.Err() != nil { + return + } c.App.EmitEvent("hicli_event", &hicli.JSONCommand{ Command: "init_complete", RequestID: 0, diff --git a/pkg/gomuks/gomuks.go b/pkg/gomuks/gomuks.go index 8abba28..264f964 100644 --- a/pkg/gomuks/gomuks.go +++ b/pkg/gomuks/gomuks.go @@ -55,7 +55,9 @@ type Gomuks struct { TempDir string LogDir string - FrontendFS embed.FS + FrontendFS embed.FS + indexWithETag []byte + frontendETag string Config Config DisableAuth bool diff --git a/pkg/gomuks/server.go b/pkg/gomuks/server.go index a317020..106124d 100644 --- a/pkg/gomuks/server.go +++ b/pkg/gomuks/server.go @@ -17,15 +17,19 @@ package gomuks import ( + "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "errors" "fmt" + "html" + "io" "io/fs" "net/http" _ "net/http/pprof" + "strconv" "strings" "time" @@ -73,6 +77,25 @@ func (gmx *Gomuks) StartServer() { gmx.Log.Warn().Msg("Frontend not found") } else { router.Handle("/", gmx.FrontendCacheMiddleware(http.FileServerFS(frontend))) + if gmx.Commit != "unknown" && !gmx.BuildTime.IsZero() { + gmx.frontendETag = fmt.Sprintf(`"%s-%s"`, gmx.Commit, gmx.BuildTime.Format(time.RFC3339)) + + indexFile, err := frontend.Open("index.html") + if err != nil { + gmx.Log.Err(err).Msg("Failed to open index.html") + } else { + data, err := io.ReadAll(indexFile) + _ = indexFile.Close() + if err == nil { + gmx.indexWithETag = bytes.Replace( + data, + []byte(""), + []byte(fmt.Sprintf(``, html.EscapeString(gmx.frontendETag))), + 1, + ) + } + } + } } gmx.Server = &http.Server{ Addr: gmx.Config.Web.ListenAddress, @@ -88,20 +111,23 @@ func (gmx *Gomuks) StartServer() { } func (gmx *Gomuks) FrontendCacheMiddleware(next http.Handler) http.Handler { - var frontendCacheETag string - if gmx.Commit != "unknown" && !gmx.BuildTime.IsZero() { - frontendCacheETag = fmt.Sprintf(`"%s-%s"`, gmx.Commit, gmx.BuildTime.Format(time.RFC3339)) - } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("If-None-Match") == frontendCacheETag { + if gmx.frontendETag != "" && r.Header.Get("If-None-Match") == gmx.frontendETag { w.WriteHeader(http.StatusNotModified) return } if strings.HasPrefix(r.URL.Path, "/assets/") { w.Header().Set("Cache-Control", "max-age=604800, immutable") } - if frontendCacheETag != "" { - w.Header().Set("ETag", frontendCacheETag) + if gmx.frontendETag != "" { + w.Header().Set("ETag", gmx.frontendETag) + if r.URL.Path == "/" && gmx.indexWithETag != nil { + w.Header().Set("Content-Type", "text/html") + w.Header().Set("Content-Length", strconv.Itoa(len(gmx.indexWithETag))) + w.WriteHeader(http.StatusOK) + _, _ = w.Write(gmx.indexWithETag) + return + } } next.ServeHTTP(w, r) }) diff --git a/pkg/gomuks/websocket.go b/pkg/gomuks/websocket.go index 0a78446..bec91dc 100644 --- a/pkg/gomuks/websocket.go +++ b/pkg/gomuks/websocket.go @@ -59,6 +59,11 @@ type PingRequestData struct { var runID = time.Now().UnixNano() +type RunData struct { + RunID string `json:"run_id"` + ETag string `json:"etag"` +} + func (gmx *Gomuks) HandleWebsocket(w http.ResponseWriter, r *http.Request) { var conn *websocket.Conn log := zerolog.Ctx(r.Context()) @@ -233,9 +238,12 @@ func (gmx *Gomuks) HandleWebsocket(w http.ResponseWriter, r *http.Request) { log.Trace().Int64("req_id", cmd.RequestID).Msg("Sent response to command") } } - initErr := writeCmd(ctx, conn, &hicli.JSONCommandCustom[string]{ + initErr := writeCmd(ctx, conn, &hicli.JSONCommandCustom[*RunData]{ Command: "run_id", - Data: strconv.FormatInt(runID, 10), + Data: &RunData{ + RunID: strconv.FormatInt(runID, 10), + ETag: gmx.frontendETag, + }, }) if initErr != nil { log.Err(initErr).Msg("Failed to write init client state message") @@ -315,6 +323,9 @@ func (gmx *Gomuks) sendInitialData(ctx context.Context, conn *websocket.Conn) { return } } + if ctx.Err() != nil { + return + } err := writeCmd(ctx, conn, &hicli.JSONCommand{ Command: "init_complete", RequestID: 0, diff --git a/pkg/hicli/init.go b/pkg/hicli/init.go index 6b6f7a0..7b44f07 100644 --- a/pkg/hicli/init.go +++ b/pkg/hicli/init.go @@ -23,6 +23,9 @@ func (h *HiClient) getInitialSyncRoom(ctx context.Context, room *database.Room) ad, err := h.DB.AccountData.GetAllRoom(ctx, h.Account.UserID, room.ID) if err != nil { zerolog.Ctx(ctx).Err(err).Stringer("room_id", room.ID).Msg("Failed to get room account data") + if ctx.Err() != nil { + return nil + } syncRoom.AccountData = make(map[event.Type]*database.AccountData) } else { syncRoom.AccountData = make(map[event.Type]*database.AccountData, len(ad)) @@ -34,6 +37,9 @@ func (h *HiClient) getInitialSyncRoom(ctx context.Context, room *database.Room) previewEvent, err := h.DB.Event.GetByRowID(ctx, room.PreviewEventRowID) if err != nil { zerolog.Ctx(ctx).Err(err).Stringer("room_id", room.ID).Msg("Failed to get preview event for room") + if ctx.Err() != nil { + return nil + } } else if previewEvent != nil { h.ReprocessExistingEvent(ctx, previewEvent) previewMember, err := h.DB.CurrentState.Get(ctx, room.ID, event.StateMember, previewEvent.Sender.String()) @@ -85,6 +91,9 @@ func (h *HiClient) GetInitialSync(ctx context.Context, batchSize int) iter.Seq[* } maxTS = room.SortingTimestamp.Time payload.Rooms[room.ID] = h.getInitialSyncRoom(ctx, room) + if ctx.Err() != nil { + return + } } if !yield(&payload) || len(rooms) < batchSize { break diff --git a/web/index.html b/web/index.html index 07cd4b7..739246e 100644 --- a/web/index.html +++ b/web/index.html @@ -5,6 +5,7 @@