Improved icon fetching with DriveCache disabled
Some checks failed
Run Integration Tests / test (push) Failing after 37s

This commit is contained in:
partisan 2025-07-04 15:24:16 +02:00
parent 43d7068c7a
commit b17b9bc05f
6 changed files with 239 additions and 131 deletions

View file

@ -150,17 +150,6 @@ func cacheImage(imageURL, imageID string, imageType string) (string, bool, error
return "", false, err
}
if err != nil {
recordInvalidImageID(imageID)
return "", false, fmt.Errorf("failed to decode image: %v", err)
}
// This is not working
// // Ensure the cache directory exists
// if _, err := os.Stat(config.DriveCache.Path); os.IsNotExist(err) {
// os.Mkdir(config.DriveCache.Path, os.ModePerm)
// }
// Open the temp file for writing
outFile, err := os.Create(tempImagePath)
if err != nil {
@ -194,7 +183,7 @@ func cacheImage(imageURL, imageID string, imageType string) (string, bool, error
}
func handleImageServe(w http.ResponseWriter, r *http.Request) {
// Extract the image ID and type from the URL
// Extract image ID and type from URL
imageName := filepath.Base(r.URL.Path)
idType := imageName
@ -202,7 +191,6 @@ func handleImageServe(w http.ResponseWriter, r *http.Request) {
hasExtension := false
if strings.HasSuffix(idType, ".webp") {
// Cached image, remove extension
idType = strings.TrimSuffix(idType, ".webp")
hasExtension = true
}
@ -216,79 +204,111 @@ func handleImageServe(w http.ResponseWriter, r *http.Request) {
imageType = parts[1]
filename := fmt.Sprintf("%s_%s.webp", imageID, imageType)
// Adjust to read from config.DriveCache.Path / images
cachedImagePath := filepath.Join(config.DriveCache.Path, "images", filename)
if hasExtension && (imageType == "thumb" || imageType == "icon") {
// --- 1. PENDING: Is image currently being cached? Return 202 Accepted ---
imageKey := fmt.Sprintf("%s_%s", imageID, imageType)
imageURLMapMu.RLock()
imageURL, exists := imageURLMap[imageKey]
imageURLMapMu.RUnlock()
if exists {
cachingImagesMu.Lock()
_, isCaching := cachingImages[imageURL]
cachingImagesMu.Unlock()
if isCaching {
// Image is still being processed
w.WriteHeader(http.StatusAccepted) // 202
return
}
}
// --- 2. INVALID: Is image known as invalid? Return fallback with 410 ---
invalidImageIDsMu.Lock()
_, isInvalid := invalidImageIDs[imageID]
invalidImageIDsMu.Unlock()
if isInvalid {
if imageType == "icon" {
serveGlobeImage(w)
} else {
serveMissingImage(w)
}
return
}
// --- 3. READY: Serve cached file if available ---
if hasExtension && (imageType == "thumb" || imageType == "icon" || imageType == "full") {
if _, err := os.Stat(cachedImagePath); err == nil {
// Update the modification time
_ = os.Chtimes(cachedImagePath, time.Now(), time.Now())
w.Header().Set("Content-Type", "image/webp")
w.Header().Set("Cache-Control", "public, max-age=31536000")
http.ServeFile(w, r, cachedImagePath)
return
} else {
if config.DriveCacheEnabled {
if imageType == "icon" {
serveGlobeImage(w, r)
} else {
serveMissingImage(w, r)
}
return
} else if config.DriveCacheEnabled {
// With cache enabled, if cache file is missing, return fallback (410)
if imageType == "icon" {
serveGlobeImage(w)
} else {
serveMissingImage(w)
}
return
}
}
// For full images, proceed to proxy the image
// Image not cached or caching not enabled
imageKey := fmt.Sprintf("%s_%s", imageID, imageType)
imageURLMapMu.RLock()
imageURL, exists := imageURLMap[imageKey]
imageURLMapMu.RUnlock()
// --- 4. MISSING MAPPING: No source image URL known, fallback with 410 ---
if !exists {
// Cannot find original URL, serve missing image
serveMissingImage(w, r)
serveMissingImage(w)
return
}
// For thumbnails, if HardCacheEnabled is true, and image not cached, serve missing image
// --- 5. PROXY ICON: If not cached, and icon requested, proxy original ---
if imageType == "icon" && !hasExtension {
resp, err := http.Get(imageURL)
if err != nil {
recordInvalidImageID(imageID)
serveGlobeImage(w)
return
}
defer resp.Body.Close()
contentType := resp.Header.Get("Content-Type")
if contentType != "" && strings.HasPrefix(contentType, "image/") {
w.Header().Set("Content-Type", contentType)
} else {
serveGlobeImage(w)
return
}
_, _ = io.Copy(w, resp.Body)
return
}
// --- 6. PROXY THUMB (if cache disabled): With cache ON, must be on disk ---
if imageType == "thumb" && config.DriveCacheEnabled {
// Thumbnail should be cached, but not found
serveMissingImage(w, r)
serveMissingImage(w)
return
}
// For full images, proceed to proxy the image
// Fetch the image from the original URL
// --- 7. PROXY FULL: For full images, proxy directly ---
resp, err := http.Get(imageURL)
if err != nil {
printWarn("Error fetching image: %v", err)
recordInvalidImageID(imageID)
serveMissingImage(w, r)
serveMissingImage(w)
return
}
defer resp.Body.Close()
// Check if the request was successful
if resp.StatusCode != http.StatusOK {
serveMissingImage(w, r)
serveMissingImage(w)
return
}
// Set the Content-Type header to the type of the fetched image
contentType := resp.Header.Get("Content-Type")
if contentType != "" && strings.HasPrefix(contentType, "image/") {
w.Header().Set("Content-Type", contentType)
} else {
serveMissingImage(w, r)
serveMissingImage(w)
return
}
// Write the image content to the response
if _, err := io.Copy(w, resp.Body); err != nil {
printWarn("Error writing image to response: %v", err)
}
@ -536,20 +556,31 @@ func safeDecodeImage(contentType string, data []byte) (img image.Image, err erro
}
// Serve missing.svg
func serveMissingImage(w http.ResponseWriter, r *http.Request) {
func serveMissingImage(w http.ResponseWriter) {
missingImagePath := filepath.Join("static", "images", "missing.svg")
// Set error code FIRST
w.WriteHeader(http.StatusGone)
// Now read the file and write it manually, to avoid conflict with http.ServeFile
data, err := os.ReadFile(missingImagePath)
if err != nil {
http.Error(w, "globe.svg not found", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-store, must-revalidate")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Expires", "0")
http.ServeFile(w, r, missingImagePath)
_, _ = w.Write(data)
}
func serveGlobeImage(w http.ResponseWriter, r *http.Request) {
func serveGlobeImage(w http.ResponseWriter) {
globePath := filepath.Join("static", "images", "globe.svg")
// Set error code FIRST
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(http.StatusGone)
// Now read the file and write it manually, to avoid conflict with http.ServeFile
data, err := os.ReadFile(globePath)