feat: sdk migration in progress

This commit is contained in:
allanice001
2025-11-02 13:19:30 +00:00
commit 0d10d42442
492 changed files with 71067 additions and 0 deletions

16
internal/web/devproxy.go Normal file
View File

@@ -0,0 +1,16 @@
package web
import (
"net/http"
"net/http/httputil"
"net/url"
)
func DevProxy(target string) (http.Handler, error) {
u, err := url.Parse(target)
if err != nil {
return nil, err
}
p := httputil.NewSingleHostReverseProxy(u)
return p, nil
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

15
internal/web/dist/index.html vendored Normal file
View File

@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ui</title>
<script type="module" crossorigin src="/assets/index-CFwByDWI.js"></script>
<link rel="modulepreload" crossorigin href="/assets/react-BZmgNp9X.js">
<link rel="stylesheet" crossorigin href="/assets/index-BvUUUOIq.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

BIN
internal/web/dist/index.html.br vendored Normal file

Binary file not shown.

BIN
internal/web/dist/index.html.gz vendored Normal file

Binary file not shown.

1
internal/web/dist/vite.svg vendored Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

239
internal/web/static.go Normal file
View File

@@ -0,0 +1,239 @@
package web
import (
"embed"
"io"
"io/fs"
"net/http"
"path"
"path/filepath"
"strings"
"time"
)
// NOTE: Vite outputs to web/dist with assets in dist/assets.
// If you add more nested folders in the future, include them here too.
//go:embed dist
var distFS embed.FS
// spaFileSystem serves embedded dist/ files with SPA fallback to index.html
type spaFileSystem struct {
fs fs.FS
}
func (s spaFileSystem) Open(name string) (fs.File, error) {
// Normalize, strip leading slash
if strings.HasPrefix(name, "/") {
name = name[1:]
}
// Try exact file
f, err := s.fs.Open(name)
if err == nil {
return f, nil
}
// If the requested file doesn't exist, fall back to index.html for SPA routes
// BUT only if it's not obviously a static asset extension
ext := strings.ToLower(filepath.Ext(name))
switch ext {
case ".js", ".css", ".map", ".json", ".txt", ".ico", ".png", ".jpg", ".jpeg",
".svg", ".webp", ".gif", ".woff", ".woff2", ".ttf", ".otf", ".eot", ".wasm", ".br", ".gz":
return nil, fs.ErrNotExist
}
return s.fs.Open("index.html")
}
func newDistFS() (fs.FS, error) {
return fs.Sub(distFS, "dist")
}
// SPAHandler returns an http.Handler that serves the embedded UI (with caching)
func SPAHandler() (http.Handler, error) {
sub, err := newDistFS()
if err != nil {
return nil, err
}
spa := spaFileSystem{fs: sub}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/api/") ||
r.URL.Path == "/api" ||
strings.HasPrefix(r.URL.Path, "/swagger") ||
strings.HasPrefix(r.URL.Path, "/debug/pprof") {
http.NotFound(w, r)
return
}
filePath := strings.TrimPrefix(path.Clean(r.URL.Path), "/")
if filePath == "" {
filePath = "index.html"
}
// Try compressed variants for assets and HTML
// NOTE: we only change *Content-Encoding*; Content-Type derives from original ext
// Always vary on Accept-Encoding
w.Header().Add("Vary", "Accept-Encoding")
enc := r.Header.Get("Accept-Encoding")
if tryServeCompressed(w, r, spa, filePath, enc) {
return
}
// Fallback: normal open (or SPA fallback)
f, err := spa.Open(filePath)
if err != nil {
http.NotFound(w, r)
return
}
defer f.Close()
if strings.HasSuffix(filePath, ".html") {
w.Header().Set("Cache-Control", "no-cache")
} else {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
info, _ := f.Stat()
modTime := time.Now()
if info != nil {
modTime = info.ModTime()
}
http.ServeContent(w, r, filePath, modTime, file{f})
}), nil
}
func tryServeCompressed(w http.ResponseWriter, r *http.Request, spa spaFileSystem, filePath, enc string) bool {
wantsBR := strings.Contains(enc, "br")
wantsGZ := strings.Contains(enc, "gzip")
type cand struct {
logical string // MIME/type decision uses this (uncompressed name)
physical string // actual file we open (with .br/.gz)
enc string
}
var cands []cand
// 1) direct compressed variant of requested path (rare for SPA routes, but cheap to try)
if wantsBR {
cands = append(cands, cand{logical: filePath, physical: filePath + ".br", enc: "br"})
}
if wantsGZ {
cands = append(cands, cand{logical: filePath, physical: filePath + ".gz", enc: "gzip"})
}
// 2) SPA route: fall back to compressed index.html
if filepath.Ext(filePath) == "" {
if wantsBR {
cands = append(cands, cand{logical: "index.html", physical: "index.html.br", enc: "br"})
}
if wantsGZ {
cands = append(cands, cand{logical: "index.html", physical: "index.html.gz", enc: "gzip"})
}
}
for _, c := range cands {
f, err := spa.fs.Open(c.physical) // open EXACT path so we don't accidentally get SPA fallback
if err != nil {
continue
}
defer f.Close()
// Cache headers
if strings.HasSuffix(c.logical, ".html") {
w.Header().Set("Cache-Control", "no-cache")
} else {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
if ct := mimeByExt(path.Ext(c.logical)); ct != "" {
w.Header().Set("Content-Type", ct)
}
w.Header().Set("Content-Encoding", c.enc)
w.Header().Add("Vary", "Accept-Encoding")
info, _ := f.Stat()
modTime := time.Now()
if info != nil {
modTime = info.ModTime()
}
// Serve the precompressed bytes
http.ServeContent(w, r, c.physical, modTime, file{f})
return true
}
return false
}
func serveIfExists(w http.ResponseWriter, r *http.Request, spa spaFileSystem, filePath, ext, encoding string) bool {
cf := filePath + ext
f, err := spa.Open(cf)
if err != nil {
return false
}
defer f.Close()
// Set caching headers
if strings.HasSuffix(filePath, ".html") {
w.Header().Set("Cache-Control", "no-cache")
} else {
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
}
// Preserve original content type by extension of *uncompressed* file
if ct := mimeByExt(path.Ext(filePath)); ct != "" {
w.Header().Set("Content-Type", ct)
}
w.Header().Set("Content-Encoding", encoding)
info, _ := f.Stat()
modTime := time.Now()
if info != nil {
modTime = info.ModTime()
}
// Serve the compressed bytes as an io.ReadSeeker if possible
http.ServeContent(w, r, cf, modTime, file{f})
return true
}
func mimeByExt(ext string) string {
switch strings.ToLower(ext) {
case ".html":
return "text/html; charset=utf-8"
case ".js":
return "application/javascript"
case ".css":
return "text/css; charset=utf-8"
case ".json":
return "application/json"
case ".svg":
return "image/svg+xml"
case ".png":
return "image/png"
case ".jpg", ".jpeg":
return "image/jpeg"
case ".webp":
return "image/webp"
case ".ico":
return "image/x-icon"
case ".woff2":
return "font/woff2"
case ".woff":
return "font/woff"
default:
return "" // let Go sniff if empty
}
}
// file wraps fs.File to implement io.ReadSeeker if possible (for ServeContent)
type file struct{ fs.File }
func (f file) Seek(offset int64, whence int) (int64, error) {
if s, ok := f.File.(io.Seeker); ok {
return s.Seek(offset, whence)
}
// Fallback: not seekable
return 0, fs.ErrInvalid
}