Improved icon fetching with DriveCache disabled
Some checks failed
Run Integration Tests / test (push) Failing after 37s
Some checks failed
Run Integration Tests / test (push) Failing after 37s
This commit is contained in:
parent
43d7068c7a
commit
b17b9bc05f
6 changed files with 239 additions and 131 deletions
137
cache-images.go
137
cache-images.go
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue