feat(router): 为exports路由添加免认证下载接口
添加exportsDownloadHandler处理函数用于免认证下载文件,并修改路由配置使/download路径跳过认证中间件。当文件URI获取失败时,自动尝试从本地storage目录查找匹配文件。
This commit is contained in:
parent
d0f65da375
commit
20deec5879
|
|
@ -1961,3 +1961,60 @@ func parseIntVal(s string) int {
|
||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exportsDownloadHandler 独立的下载处理函数(不需要认证)
|
||||||
|
func exportsDownloadHandler(metaDB, marketingDB, ymtDB *sql.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// 提取 ID,路径格式:/api/exports/{id}/download
|
||||||
|
path := r.URL.Path
|
||||||
|
if !strings.HasPrefix(path, "/api/exports/") || !strings.HasSuffix(path, "/download") {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("invalid path"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除 /api/exports/ 和 /download
|
||||||
|
path = strings.TrimPrefix(path, "/api/exports/")
|
||||||
|
path = strings.TrimSuffix(path, "/download")
|
||||||
|
id := strings.TrimSpace(path)
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("missing id"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rrepo := repo.NewExportRepo()
|
||||||
|
uri, err := rrepo.GetLatestFileURI(metaDB, id)
|
||||||
|
if err != nil {
|
||||||
|
// fallback: try to serve local storage file by job id
|
||||||
|
// search for files named export_job_<id>_*.zip/xlsx/csv
|
||||||
|
dir := "storage"
|
||||||
|
entries, e := os.ReadDir(dir)
|
||||||
|
if e == nil {
|
||||||
|
best := ""
|
||||||
|
var bestInfo os.FileInfo
|
||||||
|
for _, ent := range entries {
|
||||||
|
name := ent.Name()
|
||||||
|
if strings.HasPrefix(name, "export_job_"+id+"_") && (strings.HasSuffix(name, ".zip") || strings.HasSuffix(name, ".xlsx") || strings.HasSuffix(name, ".csv")) {
|
||||||
|
info, _ := os.Stat(filepath.Join(dir, name))
|
||||||
|
if info != nil {
|
||||||
|
if best == "" || info.ModTime().After(bestInfo.ModTime()) {
|
||||||
|
best = name
|
||||||
|
bestInfo = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if best != "" {
|
||||||
|
http.ServeFile(w, r, filepath.Join(dir, best))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte("not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.ServeFile(w, r, uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRouter(metaDB *sql.DB, marketingDB *sql.DB, marketingAuthDB *sql.DB, resellerDB *sql.DB, ymtDB *sql.DB, grpcAddr string, marketingAPIDomain string) http.Handler {
|
func NewRouter(metaDB *sql.DB, marketingDB *sql.DB, marketingAuthDB *sql.DB, resellerDB *sql.DB, ymtDB *sql.DB, grpcAddr string, marketingAPIDomain string) http.Handler {
|
||||||
|
|
@ -16,7 +17,19 @@ func NewRouter(metaDB *sql.DB, marketingDB *sql.DB, marketingAuthDB *sql.DB, res
|
||||||
mux.Handle("/api/templates", withAccess(withTrace(authMiddleware(TemplatesHandler(metaDB, marketingDB)))))
|
mux.Handle("/api/templates", withAccess(withTrace(authMiddleware(TemplatesHandler(metaDB, marketingDB)))))
|
||||||
mux.Handle("/api/templates/", withAccess(withTrace(authMiddleware(TemplatesHandler(metaDB, marketingDB)))))
|
mux.Handle("/api/templates/", withAccess(withTrace(authMiddleware(TemplatesHandler(metaDB, marketingDB)))))
|
||||||
mux.Handle("/api/exports", withAccess(withTrace(authMiddleware(ExportsHandler(metaDB, marketingDB, ymtDB)))))
|
mux.Handle("/api/exports", withAccess(withTrace(authMiddleware(ExportsHandler(metaDB, marketingDB, ymtDB)))))
|
||||||
mux.Handle("/api/exports/", withAccess(withTrace(authMiddleware(ExportsHandler(metaDB, marketingDB, ymtDB)))))
|
|
||||||
|
// exports 路由处理(特殊处理下载接口)
|
||||||
|
mux.HandleFunc("/api/exports/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
path := r.URL.Path
|
||||||
|
// 如果是下载接口,不需要认证
|
||||||
|
if strings.HasSuffix(path, "/download") {
|
||||||
|
exportsDownloadHandler(metaDB, marketingDB, ymtDB).ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
// 其他 exports 路由需要认证
|
||||||
|
withAccess(withTrace(authMiddleware(ExportsHandler(metaDB, marketingDB, ymtDB)))).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
mux.Handle("/api/metadata/fields", withAccess(withTrace(authMiddleware(MetadataHandler(metaDB, marketingDB, ymtDB)))))
|
mux.Handle("/api/metadata/fields", withAccess(withTrace(authMiddleware(MetadataHandler(metaDB, marketingDB, ymtDB)))))
|
||||||
mux.Handle("/api/fields", withAccess(withTrace(authMiddleware(FieldsHandler(marketingDB, ymtDB)))))
|
mux.Handle("/api/fields", withAccess(withTrace(authMiddleware(FieldsHandler(marketingDB, ymtDB)))))
|
||||||
mux.Handle("/api/fields/", withAccess(withTrace(authMiddleware(FieldsHandler(marketingDB, ymtDB)))))
|
mux.Handle("/api/fields/", withAccess(withTrace(authMiddleware(FieldsHandler(marketingDB, ymtDB)))))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue