From 4140847a1558dce2b3b318c3a32ceb6c6243f951 Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Fri, 3 Jul 2026 18:24:24 +0700 Subject: [PATCH] =?UTF-8?q?fix(web,server):=20=D0=BF=D0=BB=D0=B5=D0=B9?= =?UTF-8?q?=D1=81=D1=85=D0=BE=D0=BB=D0=B4=D0=B5=D1=80=20dist=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B2=D0=BE=D1=81=D0=BF=D1=80=D0=BE=D0=B8=D0=B7?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4=D0=B8=D0=BC=D0=BE=D0=B9=20=D1=81=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=BA=D0=B8=20+=20/api=20=D0=B1=D0=B5=D0=B7=20=D1=81?= =?UTF-8?q?=D0=BB=D1=8D=D1=88=D0=B0=20=E2=86=92=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Коммитим internal/web/dist/index.html как минимальный плейсхолдер, чтобы //go:embed all:dist находил совпадения на чистом клоне без npm/`make web` (CRITICAL: go build ./... падал с "pattern all:dist: no matching files found"). .gitignore теперь игнорирует только реальные build-ассеты (internal/web/dist/* кроме index.html); `make web` перезаписывает плейсхолдер настоящей сборкой. Также чинит MEDIUM: голый /api (без хвостового слэша) уходил в SPA-fallback вместо API-роутера — вынесен isAPIPath() с явной проверкой path == "/api", покрыт TestIsAPIPath. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3 --- .gitignore | 9 ++++++++- cmd/server/main.go | 10 +++++++++- cmd/server/main_test.go | 22 ++++++++++++++++++++++ internal/web/dist/index.html | 1 + 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 cmd/server/main_test.go create mode 100644 internal/web/dist/index.html diff --git a/.gitignore b/.gitignore index 4bed46b..8e0c165 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,11 @@ # web (Vite frontend) web/node_modules/ web/dist/ -internal/web/dist/ + +# internal/web/dist: real build output is generated by `make web` and +# gitignored, EXCEPT index.html — a minimal placeholder is committed so +# `go build ./...` (which //go:embed all:dist needs) works on a clean +# clone without npm/CI web-build step. `make web` overwrites the +# placeholder with the real built index.html. +internal/web/dist/* +!internal/web/dist/index.html diff --git a/cmd/server/main.go b/cmd/server/main.go index 67be8b4..308c423 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -18,6 +18,14 @@ import ( "github.com/vasyakrg/dns-autoresolver/internal/web" ) +// isAPIPath reports whether path must be routed to the API router rather +// than the SPA. "/api" (no trailing slash) counts as an API path too — +// only strings.HasPrefix(path, "/api/") would otherwise miss it and fall +// through to the SPA fallback. +func isAPIPath(path string) bool { + return path == "/api" || strings.HasPrefix(path, "/api/") +} + func main() { ctx := context.Background() cfg, err := config.Load() @@ -52,7 +60,7 @@ func main() { } mux := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.HasPrefix(r.URL.Path, "/api/") { + if isAPIPath(r.URL.Path) { apiRouter.ServeHTTP(w, r) return } diff --git a/cmd/server/main_test.go b/cmd/server/main_test.go new file mode 100644 index 0000000..e056b4e --- /dev/null +++ b/cmd/server/main_test.go @@ -0,0 +1,22 @@ +package main + +import "testing" + +func TestIsAPIPath(t *testing.T) { + cases := []struct { + path string + want bool + }{ + {"/api", true}, + {"/api/", true}, + {"/api/domains", true}, + {"/", false}, + {"/domains/xyz", false}, + {"/apix", false}, + } + for _, c := range cases { + if got := isAPIPath(c.path); got != c.want { + t.Errorf("isAPIPath(%q) = %v, want %v", c.path, got, c.want) + } + } +} diff --git a/internal/web/dist/index.html b/internal/web/dist/index.html new file mode 100644 index 0000000..564ee18 --- /dev/null +++ b/internal/web/dist/index.html @@ -0,0 +1 @@ +DNS AutoresolverUI not built. Run: make web