|
|
|
|
@@ -344,9 +344,8 @@ func (h *ImportHandler) Upload(c *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there are no importable hosts, surface clearer feedback. This covers cases
|
|
|
|
|
// where routes were parsed (e.g. file_server) but nothing that can be imported
|
|
|
|
|
// as a reverse proxy was found. Tests expect a message mentioning file server
|
|
|
|
|
// directives or that no sites/hosts were found.
|
|
|
|
|
// where routes were parsed (e.g. file_server) but none are reverse_proxy
|
|
|
|
|
// entries that we can import.
|
|
|
|
|
if importableCount == 0 {
|
|
|
|
|
imports := detectImportDirectives(req.Content)
|
|
|
|
|
if len(imports) > 0 {
|
|
|
|
|
@@ -355,20 +354,29 @@ func (h *ImportHandler) Upload(c *gin.Context) {
|
|
|
|
|
sanitizedImports = append(sanitizedImports, util.SanitizeForLog(filepath.Base(imp)))
|
|
|
|
|
}
|
|
|
|
|
middleware.GetRequestLogger(c).WithField("imports", sanitizedImports).Warn("Import Upload: no importable hosts parsed but imports detected")
|
|
|
|
|
// Keep existing behavior for import directives (400) so callers can react
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no sites found in uploaded Caddyfile; imports detected; please upload the referenced site files using the multi-file import flow", "imports": imports})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If file_server directives were present, return a clearer message that they
|
|
|
|
|
// are not supported for import and that no importable hosts exist.
|
|
|
|
|
// If file_server directives were present, return a preview + explicit
|
|
|
|
|
// warning so the frontend can show a prominent banner while still
|
|
|
|
|
// returning a successful preview shape (tests expect preview + banner).
|
|
|
|
|
if fileServerDetected {
|
|
|
|
|
middleware.GetRequestLogger(c).WithField("content_len", len(req.Content)).Warn("Import Upload: parsed routes were file_server-only and not importable")
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "File server directives are not supported for import or no sites/hosts found in your Caddyfile"})
|
|
|
|
|
// Return 400 but include preview + warning so callers (and E2E) can render
|
|
|
|
|
// the same preview UX while still signaling an error status.
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "File server directives are not supported for import or no sites/hosts found in your Caddyfile",
|
|
|
|
|
"warning": "File server directives are not supported for import or no sites/hosts found in your Caddyfile",
|
|
|
|
|
"session": gin.H{"id": sid, "state": "transient", "source_file": tempPath},
|
|
|
|
|
"preview": result,
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
middleware.GetRequestLogger(c).WithField("content_len", len(req.Content)).Warn("Import Upload: no hosts parsed and no imports detected")
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no sites found in uploaded Caddyfile"})
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no sites found in uploaded Caddyfile", "warning": "No sites or importable hosts were found in the uploaded Caddyfile", "session": gin.H{"id": sid, "state": "transient", "source_file": tempPath}, "preview": result})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -551,7 +559,15 @@ func (h *ImportHandler) UploadMulti(c *gin.Context) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fileServerDetected {
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "File server directives are not supported for import or no sites/hosts found in your Caddyfile"})
|
|
|
|
|
// Return 400 but include preview + warning so the UI can render the
|
|
|
|
|
// preview shape while the HTTP status indicates an error.
|
|
|
|
|
middleware.GetRequestLogger(c).WithField("mainCaddyfile", util.SanitizeForLog(filepath.Base(mainCaddyfile))).Warn("Import UploadMulti: parsed routes were file_server-only and not importable")
|
|
|
|
|
c.JSON(http.StatusBadRequest, gin.H{
|
|
|
|
|
"error": "File server directives are not supported for import or no sites/hosts found in your Caddyfile",
|
|
|
|
|
"warning": "File server directives are not supported for import or no sites/hosts found in your Caddyfile",
|
|
|
|
|
"session": gin.H{"id": sid, "state": "transient", "source_file": mainCaddyfile},
|
|
|
|
|
"preview": result,
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -559,6 +575,44 @@ func (h *ImportHandler) UploadMulti(c *gin.Context) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Additional multi-file behavior: when the main Caddyfile contains import
|
|
|
|
|
// directives, the multi-file flow is expected (by E2E tests) to return only
|
|
|
|
|
// hosts that originated from the imported files. The importer does not
|
|
|
|
|
// currently annotate host origins, so we implement a pragmatic filter:
|
|
|
|
|
// - extract domain names explicitly declared in the main Caddyfile and
|
|
|
|
|
// - if import directives exist, exclude those main-file domains from the
|
|
|
|
|
// preview so the preview reflects imported-file hosts only.
|
|
|
|
|
mainContentBytes, _ := os.ReadFile(mainCaddyfile)
|
|
|
|
|
mainContent := string(mainContentBytes)
|
|
|
|
|
if len(detectImportDirectives(mainContent)) > 0 {
|
|
|
|
|
// crude extraction of domains declared in the main file
|
|
|
|
|
mainDomains := make(map[string]bool)
|
|
|
|
|
for _, line := range strings.Split(mainContent, "\n") {
|
|
|
|
|
ln := strings.TrimSpace(line)
|
|
|
|
|
if ln == "" || strings.HasPrefix(ln, "#") || strings.HasPrefix(ln, "import ") {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if strings.HasSuffix(ln, "{") {
|
|
|
|
|
tokens := strings.Fields(strings.TrimSuffix(ln, "{"))
|
|
|
|
|
if len(tokens) > 0 {
|
|
|
|
|
mainDomains[tokens[0]] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(mainDomains) > 0 {
|
|
|
|
|
filtered := make([]caddy.ParsedHost, 0, len(result.Hosts))
|
|
|
|
|
for _, ph := range result.Hosts {
|
|
|
|
|
if _, found := mainDomains[ph.DomainNames]; found {
|
|
|
|
|
// skip hosts declared in main Caddyfile when imports are present
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
filtered = append(filtered, ph)
|
|
|
|
|
}
|
|
|
|
|
result.Hosts = filtered
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for conflicts
|
|
|
|
|
existingHosts, _ := h.proxyHostSvc.List()
|
|
|
|
|
existingDomains := make(map[string]bool)
|
|
|
|
|
|