156 lines
3.5 KiB
Go
156 lines
3.5 KiB
Go
package routes
|
||
|
||
import (
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/gorilla/mux"
|
||
)
|
||
|
||
type ProductImageItem struct {
|
||
ID int64 `json:"id"`
|
||
FileName string `json:"file_name"`
|
||
FileSize int64 `json:"file_size"`
|
||
Storage string `json:"storage_path"`
|
||
ContentURL string `json:"content_url"`
|
||
}
|
||
|
||
// GET /api/product-images?code=...&color=...
|
||
func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
code := strings.TrimSpace(r.URL.Query().Get("code"))
|
||
color := strings.TrimSpace(r.URL.Query().Get("color"))
|
||
|
||
if code == "" {
|
||
http.Error(w, "Eksik parametre: code gerekli", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
query := `
|
||
SELECT
|
||
b.id,
|
||
b.file_name,
|
||
COALESCE(b.file_size, 0) AS file_size,
|
||
COALESCE(b.storage_path, '') AS storage_path
|
||
FROM dfblob b
|
||
JOIN mmitem i
|
||
ON i.id = b.src_id
|
||
WHERE b.typ = 'img'
|
||
AND b.src_table = 'mmitem'
|
||
AND i.code = $1
|
||
AND ($2 = '' OR b.file_name ILIKE '%' || '-' || $2 || '-%')
|
||
ORDER BY COALESCE(b.sort_order, 999999), b.zlins_dttm DESC, b.id DESC
|
||
`
|
||
|
||
rows, err := pg.Query(query, code, color)
|
||
if err != nil {
|
||
http.Error(w, "Görsel sorgu hatası: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
defer rows.Close()
|
||
|
||
items := make([]ProductImageItem, 0, 16)
|
||
for rows.Next() {
|
||
var it ProductImageItem
|
||
if err := rows.Scan(&it.ID, &it.FileName, &it.FileSize, &it.Storage); err != nil {
|
||
continue
|
||
}
|
||
it.ContentURL = fmt.Sprintf("/api/product-images/%d/content", it.ID)
|
||
items = append(items, it)
|
||
}
|
||
|
||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||
_ = json.NewEncoder(w).Encode(items)
|
||
}
|
||
}
|
||
|
||
// GET /api/product-images/{id}/content
|
||
func GetProductImageContentHandler(pg *sql.DB) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
idStr := mux.Vars(r)["id"]
|
||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||
if err != nil || id <= 0 {
|
||
http.Error(w, "Geçersiz görsel id", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
var (
|
||
fileName string
|
||
storagePath string
|
||
storedInDB bool
|
||
binData []byte
|
||
)
|
||
|
||
err = pg.QueryRow(`
|
||
SELECT
|
||
COALESCE(file_name, ''),
|
||
COALESCE(storage_path, ''),
|
||
COALESCE(stored_in_db, false),
|
||
bin
|
||
FROM dfblob
|
||
WHERE id = $1
|
||
AND typ = 'img'
|
||
`, id).Scan(&fileName, &storagePath, &storedInDB, &binData)
|
||
if err != nil {
|
||
if err == sql.ErrNoRows {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
http.Error(w, "Görsel okunamadı: "+err.Error(), http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
if storedInDB && len(binData) > 0 {
|
||
w.Header().Set("Content-Type", http.DetectContentType(binData))
|
||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||
_, _ = w.Write(binData)
|
||
return
|
||
}
|
||
|
||
resolved := resolveStoragePath(storagePath)
|
||
if resolved == "" {
|
||
http.NotFound(w, r)
|
||
return
|
||
}
|
||
|
||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||
http.ServeFile(w, r, resolved)
|
||
}
|
||
}
|
||
|
||
func resolveStoragePath(storagePath string) string {
|
||
raw := strings.TrimSpace(storagePath)
|
||
if raw == "" {
|
||
return ""
|
||
}
|
||
|
||
raw = strings.TrimPrefix(raw, "./")
|
||
candidates := []string{
|
||
filepath.Clean(storagePath),
|
||
filepath.Clean(raw),
|
||
filepath.Join(".", raw),
|
||
filepath.Join("..", raw),
|
||
}
|
||
|
||
if root := strings.TrimSpace(os.Getenv("BLOB_ROOT")); root != "" {
|
||
candidates = append(candidates, filepath.Join(root, raw))
|
||
}
|
||
|
||
for _, p := range candidates {
|
||
if p == "" {
|
||
continue
|
||
}
|
||
if st, err := os.Stat(p); err == nil && !st.IsDir() {
|
||
return p
|
||
}
|
||
}
|
||
|
||
return ""
|
||
}
|