Skip to content

Commit

Permalink
dev mode fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
bgentry committed Aug 26, 2024
1 parent 4f60fbc commit 0e8a045
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 80 deletions.
5 changes: 0 additions & 5 deletions .envrc.example

This file was deleted.

10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
.PHONY: dev
dev: fake_assets
pnpm dev

.PHONY: fake_assets
fake_assets:
@echo 'Skipping asset build'
@mkdir -p dist
@echo "assets build was skipped" > dist/index.html

.PHONY: generate
generate:
generate: generate/sqlc
Expand Down
6 changes: 5 additions & 1 deletion cmd/riverui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ func main() {
func initAndServe(ctx context.Context) int {
var (
devMode bool
liveFS bool
pathPrefix string
)
flag.BoolVar(&devMode, "dev", false, "enable development mode")
_, liveFS = os.LookupEnv("LIVE_FS")
_, devMode = os.LookupEnv("DEV")

flag.StringVar(&pathPrefix, "prefix", "/", "path prefix to use for the API and UI HTTP requests")
flag.Parse()

Expand Down Expand Up @@ -81,6 +84,7 @@ func initAndServe(ctx context.Context) int {
Client: client,
DBPool: dbPool,
DevMode: devMode,
LiveFS: liveFS,
Logger: logger,
Prefix: pathPrefix,
}
Expand Down
49 changes: 30 additions & 19 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"log/slog"
"net/http"
"net/url"
"os"
"strings"

"github.com/jackc/pgx/v5"
Expand Down Expand Up @@ -38,6 +39,8 @@ type HandlerOpts struct {
DBPool DBTXWithBegin
// DevMode is whether the server is running in development mode.
DevMode bool
// LiveFS is whether to use the live filesystem for the frontend.
LiveFS bool
// Logger is the logger to use logging errors within the handler.
Logger *slog.Logger
// Prefix is the path prefix to use for the API and UI HTTP requests.
Expand Down Expand Up @@ -90,6 +93,17 @@ func NewServer(opts *HandlerOpts) (*Server, error) {
if err != nil {
return nil, fmt.Errorf("error getting frontend index: %w", err)
}

if opts.LiveFS {
if opts.DevMode {
fmt.Println("Using live filesystem at ./public")
frontendIndex = os.DirFS("./public")
} else {
fmt.Println("Using live filesystem at ./dist")
frontendIndex = os.DirFS("./dist")
}
}

if !opts.DevMode {
if _, err := frontendIndex.Open(".vite/manifest.json"); err != nil {
return nil, errors.New("manifest.json not found")
Expand All @@ -105,10 +119,7 @@ func NewServer(opts *HandlerOpts) (*Server, error) {

httpFS := http.FS(frontendIndex)
fileServer := http.FileServer(httpFS)
serveIndex, err := serveIndexHTML(opts.DevMode, manifest, prefix, httpFS)
if err != nil {
return nil, err
}
serveIndex := serveIndexHTML(opts.DevMode, manifest, prefix, httpFS)

apiBundle := apiBundle{
// TODO: Switch to baseservice.NewArchetype when available.
Expand Down Expand Up @@ -211,22 +222,22 @@ func (s *Server) Start(ctx context.Context) error {
func readManifest(frontendIndex fs.FS, devMode bool) (map[string]interface{}, error) {
if devMode {
return map[string]interface{}{}, nil
} else {
file, err := frontendIndex.Open(".vite/manifest.json")
if err != nil {
return nil, err
}
bytes, err := io.ReadAll(file)
if err != nil {
return nil, errors.New("could not read .vite/manifest.json")
}
var manifest map[string]interface{}
err = json.Unmarshal(bytes, &manifest)
if err != nil {
return nil, errors.New("could not unmarshal .vite/manifest.json")
}
return manifest, nil
}

file, err := frontendIndex.Open(".vite/manifest.json")
if err != nil {
return nil, err
}
bytes, err := io.ReadAll(file)
if err != nil {
return nil, errors.New("could not read .vite/manifest.json")
}
var manifest map[string]interface{}
err = json.Unmarshal(bytes, &manifest)
if err != nil {
return nil, errors.New("could not unmarshal .vite/manifest.json")
}
return manifest, nil
}

//go:embed public
Expand Down
6 changes: 3 additions & 3 deletions handler_api_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ func (a *workflowListEndpoint) Execute(ctx context.Context, req *workflowListReq
case "active":
workflows, err := dbsqlc.New().WorkflowListActive(ctx, a.dbPool, &dbsqlc.WorkflowListActiveParams{
After: ptrutil.ValOrDefault(req.After, ""),
PaginationLimit: int32(ptrutil.ValOrDefault(req.Limit, 100)),
PaginationLimit: int32(min(ptrutil.ValOrDefault(req.Limit, 100), 1000)), //nolint:gosec
})
if err != nil {
return nil, fmt.Errorf("error listing workflows: %w", err)
Expand All @@ -739,7 +739,7 @@ func (a *workflowListEndpoint) Execute(ctx context.Context, req *workflowListReq
case "inactive":
workflows, err := dbsqlc.New().WorkflowListInactive(ctx, a.dbPool, &dbsqlc.WorkflowListInactiveParams{
After: ptrutil.ValOrDefault(req.After, ""),
PaginationLimit: int32(ptrutil.ValOrDefault(req.Limit, 100)),
PaginationLimit: int32(min(ptrutil.ValOrDefault(req.Limit, 100), 1000)), //nolint:gosec
})
if err != nil {
return nil, fmt.Errorf("error listing workflows: %w", err)
Expand All @@ -748,7 +748,7 @@ func (a *workflowListEndpoint) Execute(ctx context.Context, req *workflowListReq
default:
workflows, err := dbsqlc.New().WorkflowListAll(ctx, a.dbPool, &dbsqlc.WorkflowListAllParams{
After: ptrutil.ValOrDefault(req.After, ""),
PaginationLimit: int32(ptrutil.ValOrDefault(req.Limit, 100)),
PaginationLimit: int32(min(ptrutil.ValOrDefault(req.Limit, 100), 1000)), //nolint:gosec
})
if err != nil {
return nil, fmt.Errorf("error listing workflows: %w", err)
Expand Down
8 changes: 5 additions & 3 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ func TestNewHandlerIntegration(t *testing.T) {
t.Cleanup(func() { tx.Rollback(ctx) })

server, err := NewServer(&HandlerOpts{
Client: client,
DBPool: tx,
Logger: logger,
Client: client,
DBPool: tx,
DevMode: true,
LiveFS: true,
Logger: logger,
})
require.NoError(t, err)

Expand Down
11 changes: 9 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@
</script>

{{- if .Dev}}
<script type="module" src="/src/main.tsx"></script>
<script type="module" src="/@vite/client"></script>
<script type="module">
import RefreshRuntime from "http://localhost:5173/@react-refresh";
RefreshRuntime.injectIntoGlobalHook(window);
window.$RefreshReg$ = () => {};
window.$RefreshSig$ = () => (type) => type;
window.__vite_plugin_react_preamble_installed__ = true;
</script>
<script type="module" src="http://localhost:5173/src/main.tsx"></script>
<script type="module" src="http://localhost:5173/@vite/client"></script>
{{- else }} {{ $js := index .Manifest "src/main.tsx" "file" }} {{ $css := index .Manifest "src/main.tsx" "css" }}
<script>
window.__riverUiBasePath = () => "{{ .Base }}";
Expand Down
99 changes: 52 additions & 47 deletions spa_response_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,66 +27,71 @@ func intercept404(handler, on404 http.Handler) http.Handler {
})
}

func serveIndexHTML(devMode bool, manifest map[string]interface{}, pathPrefix string, files http.FileSystem) (http.HandlerFunc, error) {
rawIndex, err := files.Open("index.html")
if err != nil {
return nil, fmt.Errorf("index.html not found in embedded files: %w", err)
}

config := struct {
APIURL string `json:"apiUrl"`
Base string `json:"base"`
}{
APIURL: pathPrefix + "/api",
Base: pathPrefix,
}
func serveIndexHTML(devMode bool, manifest map[string]interface{}, pathPrefix string, files http.FileSystem) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
// Restrict only to instances where the browser is looking for an HTML file
if !strings.Contains(req.Header.Get("Accept"), "text/html") {
rw.WriteHeader(http.StatusNotFound)
fmt.Fprint(rw, "404 not found")

templateData := map[string]interface{}{
"Config": config,
"Dev": devMode,
"Manifest": manifest,
"Base": pathPrefix,
}
return
}

fileInfo, err := rawIndex.Stat()
if err != nil {
return nil, fmt.Errorf("could not stat index.html: %w", err)
}
rawIndex, err := files.Open("index.html")
if err != nil {
http.Error(rw, "could not open index.html", http.StatusInternalServerError)
return
}

indexBuf, err := io.ReadAll(rawIndex)
if err != nil {
return nil, fmt.Errorf("could not read index.html: %w", err)
}
config := struct {
APIURL string `json:"apiUrl"` //nolint:tagliatelle
Base string `json:"base"`
}{
APIURL: pathPrefix + "/api",
Base: pathPrefix,
}

tmpl, err := template.New("index.html").Funcs(template.FuncMap{
"marshal": func(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a)
},
}).Parse(string(indexBuf))
if err != nil {
return nil, fmt.Errorf("could not parse index.html: %w", err)
}
templateData := map[string]interface{}{
"Config": config,
"Dev": devMode,
"Manifest": manifest,
"Base": pathPrefix,
}

var output bytes.Buffer
if err = tmpl.Execute(&output, templateData); err != nil {
return nil, fmt.Errorf("could not execute index.html: %w", err)
}
fileInfo, err := rawIndex.Stat()
if err != nil {
http.Error(rw, "could not stat index.html", http.StatusInternalServerError)
return
}

index := bytes.NewReader(output.Bytes())
indexBuf, err := io.ReadAll(rawIndex)
if err != nil {
http.Error(rw, "could not read index.html", http.StatusInternalServerError)
return
}

return func(rw http.ResponseWriter, req *http.Request) {
// Restrict only to instances where the browser is looking for an HTML file
if !strings.Contains(req.Header.Get("Accept"), "text/html") {
rw.WriteHeader(http.StatusNotFound)
fmt.Fprint(rw, "404 not found")
tmpl, err := template.New("index.html").Funcs(template.FuncMap{
"marshal": func(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a) //nolint:gosec
},
}).Parse(string(indexBuf))
if err != nil {
http.Error(rw, "could not parse index.html", http.StatusInternalServerError)
return
}

var output bytes.Buffer
if err = tmpl.Execute(&output, templateData); err != nil {
http.Error(rw, "could not execute index.html", http.StatusInternalServerError)
return
}

index := bytes.NewReader(output.Bytes())

rw.Header().Set("Content-Type", "text/html; charset=utf-8")
http.ServeContent(rw, req, fileInfo.Name(), fileInfo.ModTime(), index)
}, nil
}
}

type spaResponseWriter struct {
Expand Down

0 comments on commit 0e8a045

Please sign in to comment.