Fix SSE architecture for reliable connections #13
5
assets/assets.go
Normal file
5
assets/assets.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Package assets provides static file serving with build-tag switching
|
||||||
|
// between live filesystem (dev) and embedded hashfs (prod).
|
||||||
|
package assets
|
||||||
|
|
||||||
|
const DirectoryPath = "assets"
|
||||||
22
assets/static_dev.go
Normal file
22
assets/static_dev.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//go:build dev
|
||||||
|
|
||||||
|
package assets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Handler() http.Handler {
|
||||||
|
log.Debug().Str("path", DirectoryPath).Msg("static assets served from filesystem")
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
|
http.StripPrefix("/assets/", http.FileServerFS(os.DirFS(DirectoryPath))).ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func StaticPath(path string) string {
|
||||||
|
return "/assets/" + path
|
||||||
|
}
|
||||||
26
assets/static_prod.go
Normal file
26
assets/static_prod.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//go:build !dev
|
||||||
|
|
||||||
|
package assets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/benbjohnson/hashfs"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed css js
|
||||||
|
staticFiles embed.FS
|
||||||
|
staticSys = hashfs.NewFS(staticFiles)
|
||||||
|
)
|
||||||
|
|
||||||
|
func Handler() http.Handler {
|
||||||
|
log.Debug().Msg("static assets are embedded with hashfs")
|
||||||
|
return hashfs.FileServer(staticSys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StaticPath(path string) string {
|
||||||
|
return "/" + staticSys.HashName(path)
|
||||||
|
}
|
||||||
@@ -51,8 +51,8 @@ templ NicknamePrompt(returnPath string) {
|
|||||||
// LiveClock shows the current server time, updated with each SSE patch.
|
// LiveClock shows the current server time, updated with each SSE patch.
|
||||||
// If the clock stops updating, users know the connection is stale.
|
// If the clock stops updating, users know the connection is stale.
|
||||||
templ LiveClock() {
|
templ LiveClock() {
|
||||||
<div class="fixed top-2 right-2 flex items-center gap-1 text-xs opacity-50 font-mono">
|
<div class="fixed top-2 right-2 flex items-center gap-1.5 text-xs opacity-60 font-mono">
|
||||||
<div class="status status-xs status-success"></div>
|
<div style="width: 6px; height: 6px; border-radius: 50%; background-color: #22c55e;"></div>
|
||||||
{ time.Now().Format("15:04:05") }
|
{ time.Now().Format("15:04:05") }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package layouts
|
package layouts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/ryanhamamura/games/assets"
|
||||||
"github.com/ryanhamamura/games/config"
|
"github.com/ryanhamamura/games/config"
|
||||||
"github.com/ryanhamamura/games/version"
|
"github.com/ryanhamamura/games/version"
|
||||||
)
|
)
|
||||||
@@ -11,8 +12,8 @@ templ Base(title string) {
|
|||||||
<head>
|
<head>
|
||||||
<title>{ title }</title>
|
<title>{ title }</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
|
||||||
<script defer type="module" src="/assets/js/datastar.js"></script>
|
<script defer type="module" src={ assets.StaticPath("js/datastar.js") }></script>
|
||||||
<link href="/assets/css/output.css" rel="stylesheet" type="text/css"/>
|
<link href={ assets.StaticPath("css/output.css") } rel="stylesheet" type="text/css"/>
|
||||||
</head>
|
</head>
|
||||||
<body class="flex flex-col h-screen">
|
<body class="flex flex-col h-screen">
|
||||||
if config.Global.Environment == config.Dev {
|
if config.Global.Environment == config.Dev {
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -68,6 +68,7 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
|
||||||
github.com/aws/smithy-go v1.24.0 // indirect
|
github.com/aws/smithy-go v1.24.0 // indirect
|
||||||
|
github.com/benbjohnson/hashfs v0.2.2 // indirect
|
||||||
github.com/bep/godartsass/v2 v2.5.0 // indirect
|
github.com/bep/godartsass/v2 v2.5.0 // indirect
|
||||||
github.com/bep/golibsass v1.2.0 // indirect
|
github.com/bep/golibsass v1.2.0 // indirect
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -136,6 +136,8 @@ github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl
|
|||||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/benbjohnson/hashfs v0.2.2 h1:vFZtksphM5LcnMRFctj49jCUkCc7wp3NP6INyfjkse4=
|
||||||
|
github.com/benbjohnson/hashfs v0.2.2/go.mod h1:7OMXaMVo1YkfiIPxKrl7OXkUTUgWjmsAKyR+E6xDIRM=
|
||||||
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps=
|
||||||
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU=
|
||||||
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
|
||||||
|
|||||||
6
main.go
6
main.go
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
@@ -29,9 +28,6 @@ import (
|
|||||||
"github.com/ryanhamamura/games/version"
|
"github.com/ryanhamamura/games/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed assets
|
|
||||||
var assets embed.FS
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -97,7 +93,7 @@ func run(ctx context.Context) error {
|
|||||||
sessionManager.LoadAndSave,
|
sessionManager.LoadAndSave,
|
||||||
)
|
)
|
||||||
|
|
||||||
router.SetupRoutes(r, queries, sessionManager, nc, store, snakeStore, assets)
|
router.SetupRoutes(r, queries, sessionManager, nc, store, snakeStore)
|
||||||
|
|
||||||
// HTTP server
|
// HTTP server
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -12,6 +10,7 @@ import (
|
|||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/starfederation/datastar-go/datastar"
|
"github.com/starfederation/datastar-go/datastar"
|
||||||
|
|
||||||
|
"github.com/ryanhamamura/games/assets"
|
||||||
"github.com/ryanhamamura/games/config"
|
"github.com/ryanhamamura/games/config"
|
||||||
"github.com/ryanhamamura/games/connect4"
|
"github.com/ryanhamamura/games/connect4"
|
||||||
"github.com/ryanhamamura/games/db/repository"
|
"github.com/ryanhamamura/games/db/repository"
|
||||||
@@ -31,11 +30,9 @@ func SetupRoutes(
|
|||||||
nc *nats.Conn,
|
nc *nats.Conn,
|
||||||
store *connect4.Store,
|
store *connect4.Store,
|
||||||
snakeStore *snake.SnakeStore,
|
snakeStore *snake.SnakeStore,
|
||||||
assets embed.FS,
|
|
||||||
) {
|
) {
|
||||||
// Static assets
|
// Static assets
|
||||||
subFS, _ := fs.Sub(assets, "assets")
|
router.Handle("/assets/*", assets.Handler())
|
||||||
router.Handle("/assets/*", http.StripPrefix("/assets/", http.FileServerFS(subFS)))
|
|
||||||
|
|
||||||
// Hot-reload for development
|
// Hot-reload for development
|
||||||
if config.Global.Environment == config.Dev {
|
if config.Global.Environment == config.Dev {
|
||||||
|
|||||||
Reference in New Issue
Block a user