Beyhan Oğur e6f3268c28 first commit
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00
2026-04-26 21:48:15 +03:00

Go Image Manipulation API

Fiber v3, GORM (MySQL) ve libvips/bimg tabanlı, JWT kimlik doğrulamalı resim yükleme ve işleme REST API.


İçindekiler


Özellikler

Özellik Detay
🔐 JWT Auth Access token (15 dk) + Refresh token (7 gün), role claim içerir
🖼️ Resim Yükleme UUID tabanlı isim, otomatik format dönüşümü
⚙️ Resim İşleme Yeniden boyutlandırma, kırpma, cover modu, kalite, format değişimi
🗂️ Resim Listeleme Sayfalı listeleme, tek resim detayı
🌐 Statik Erişim Yüklenen resimlere doğrudan URL ile erişim
🛡️ Admin Panel Kullanıcıya API token atama, tüm resimleri listeleme
🗄️ MySQL + GORM Otomatik migrasyon, soft delete
Redis Bağlantı havuzu hazır
📄 Swagger UI /swagger adresinde interaktif API belgesi

Teknoloji Yığını

Katman Kütüphane/Araç
HTTP Framework Fiber v3
ORM GORM + MySQL driver
Resim İşleme bimg (libvips bağlayıcısı)
JWT golang-jwt/jwt v5
Redis go-redis/redis v9
Parola Hashing bcrypt
UUID google/uuid
Swagger swaggo/swag
Env Yönetimi godotenv

Proje Yapısı

goimgApi/
├── main.go                   # Uygulama başlangıç noktası
├── go.mod / go.sum
├── .env                      # Ortam değişkenleri (Git'e eklenmez)
│
├── accounts/                 # Kimlik doğrulama & kullanıcı yönetimi
│   ├── models.go             # User struct (GORM modeli)
│   ├── handlers.go           # Register, Login, Refresh, Middleware, Admin handlers
│   ├── jwt.go                # Token üretimi & parse, rol normalizasyonu
│   ├── accounts_test.go      # JWT & rol testleri
│   ├── jwt_test.go           # Token claim testleri
│   └── models/               # İkincil model paketi (sosyal hesap, profil, refresh token)
│       ├── account.go
│       ├── token.go
│       └── models_test.go
│
├── images/                   # Resim yükleme & işleme
│   ├── models.go             # Image struct (GORM modeli)
│   ├── handlers.go           # Upload, ListImages, GetImage, AdminListImages, Process
│   ├── processor.go          # bimg işleme motoru
│   └── handlers_test.go      # Path yardımcı & sayfalama testleri
│
├── configs/                  # Bağlantı yapılandırmaları
│   ├── db.go                 # MySQL bağlantısı, CORS/rate-limit seed yardımcıları
│   ├── redis.go              # Redis bağlantısı
│   └── configs_test.go       # Yardımcı fonksiyon testleri
│
├── router/
│   ├── routers.go            # Tüm route tanımları, statik dosya sunumu
│   └── routers_test.go       # Swagger & statik route testleri
│
├── docs/                     # Swaggo tarafından üretilen Swagger dosyaları
│   ├── docs.go
│   ├── swagger.json
│   ├── swagger.yaml
│   └── docs_test.go
│
├── uploads/                  # Yüklenen resimlerin depolandığı dizin
└── tmp/                      # Geliştirme sırasında kullanılan geçici scriptler

Gereksinimler

  • Go 1.26+
  • MySQL 8.0+
  • Redis 6+
  • libvips 8.8+ (bimg için)

libvips Kurulumu

# Ubuntu / Debian
sudo apt-get install -y libvips-dev

# macOS
brew install vips

# Arch Linux
sudo pacman -S libvips

Kurulum ve Çalıştırma

1. Depoyu klonlayın

git clone https://github.com/kullanici/goimgApi.git
cd goimgApi

2. Bağımlılıkları yükleyin

go mod download

3. Ortam değişkenlerini ayarlayın

cp .env.example .env
# .env dosyasını düzenleyin

4. Swagger spec'ini üretin

# swag CLI kurulumu (tek seferlik)
go install github.com/swaggo/swag/cmd/swag@latest

# docs/ klasörünü yeniden üret
swag init --parseDependency --parseInternal

⚠️ Önemli: Yeni bir endpoint ekledikten veya mevcut bir endpoint'in godoc yorumunu değiştirdikten sonra bu komutu mutlaka tekrar çalıştırın; aksi hâlde Swagger UI güncellenmiş endpoint'leri göstermez.

5. Uygulamayı başlatın

go run .

Sunucu http://localhost:8080 adresinde başlar.

Geliştirme modunda (hot-reload)

# air kurulumu (opsiyonel)
go install github.com/air-verse/air@latest
air

Ortam Değişkenleri

Proje kök dizininde bir .env dosyası oluşturun:

# ─── Veritabanı (MySQL) ──────────────────────────────────────────────────────
DB_HOST=localhost
DB_PORT=3306
DB_USER=go_imgapi
DB_PASSWORD=your_password
DB_NAME=go_imgapi

# ─── Redis ───────────────────────────────────────────────────────────────────
REDIS_URL=redis://default:your_password@localhost:6379/0
# veya ayrı ayrı:
# REDIS_HOST=localhost
# REDIS_PORT=6379
# REDIS_PASSWORD=

# ─── JWT ─────────────────────────────────────────────────────────────────────
JWT_SECRET=en-az-32-karakter-uzun-gizli-anahtar
JWT_REFRESH_SECRET=farkli-bir-gizli-anahtar-refresh-icin

# ─── Sunucu ──────────────────────────────────────────────────────────────────
PORT=8080

# ─── CORS Bootstrap (opsiyonel) ──────────────────────────────────────────────
CORS_BOOTSTRAP_WHITELIST_ORIGINS=http://localhost:3000,https://yourfrontend.com

# ─── Rate Limit Bootstrap (opsiyonel) ────────────────────────────────────────
RL_BOOTSTRAP_LOGIN_MAX_REQUESTS=10
RL_BOOTSTRAP_LOGIN_WINDOW_SECONDS=60
RL_BOOTSTRAP_API_MAX_REQUESTS=120
RL_BOOTSTRAP_API_WINDOW_SECONDS=60

Not: JWT_SECRET ve JWT_REFRESH_SECRET birbirinden farklı ve en az 32 karakter olmalıdır.


API Referansı

Base URL: http://localhost:8080

Auth

POST /auth/register — Kayıt

Yeni kullanıcı oluşturur.

İstek (multipart/form-data)

Alan Tür Zorunlu ıklama
email string Geçerli e-posta adresi
password string En az 6 karakter

Başarılı Yanıt 201 Created

{
  "message": "User registered",
  "user_id": 1
}

Hata Yanıtları

Kod ıklama
400 Email boş / şifre 6 karakterden kısa / email zaten kullanımda

POST /auth/login — Giriş

Kullanıcıyı doğrular ve JWT token çifti döner.

İstek (multipart/form-data)

Alan Tür Zorunlu
email string
password string

Başarılı Yanıt 200 OK

{
  "access_token": "eyJhbGci...",
  "refresh_token": "eyJhbGci...",
  "user": {
    "id": 1,
    "email": "kullanici@ornek.com",
    "role": "admin"
  }
}

role değeri "admin" veya "user" olur.

Hata Yanıtları

Kod ıklama
401 Geçersiz e-posta veya şifre

POST /auth/refresh — Token Yenile

Geçerli bir refresh token ile yeni token çifti üretir. Refresh sırasında kullanıcının güncel rolü DB'den yeniden okunur.

İstek (multipart/form-data)

Alan Tür Zorunlu
refresh_token string

Başarılı Yanıt 200 OK

{
  "access_token": "eyJhbGci...",
  "refresh_token": "eyJhbGci..."
}

Hata Yanıtları

Kod ıklama
401 Refresh token eksik, geçersiz veya süresi dolmuş
401 Token'a ait kullanıcı bulunamadı (silinmiş olabilir)

Images

Tüm /images endpoint'leri Authorization: Bearer <access_token> başlığı gerektirir.

POST /images — Resim Yükle

İstek (multipart/form-data)

Alan Tür Zorunlu ıklama
image file Resim dosyası
w int Hedef genişlik (px)
h int Hedef yükseklik (px)
q int Kalite 1-100 (varsayılan: 85)
f string Format: webp, avif, png, jpg
mode string cover — kırparak doldur

Başarılı Yanıt 201 Created

{
  "message": "Image uploaded successfully",
  "image_id": 12,
  "filename": "550e8400-e29b-41d4-a716-446655440000.webp",
  "public_path": "/uploads/550e8400-e29b-41d4-a716-446655440000.webp",
  "image_url": "http://localhost:8080/uploads/550e8400-e29b-41d4-a716-446655440000.webp"
}

public_path vs image_url farkı:

  • public_path — domain bağımsız, DB'ye kaydedilen göreli yol (/uploads/...). Farklı ortamlarda (staging, prod, CDN) taşınabilir.
  • image_url — isteği yapan host'a göre dinamik üretilen tam URL. X-Forwarded-Host / X-Forwarded-Proto başlıkları varsa onları kullanır (proxy/CDN için).

Frontend'de resmi göstermek için image_url değerini doğrudan kullanabilir ya da API_BASE_URL + public_path formülünü tercih edebilirsiniz.


GET /images — Resimleri Listele

Kimliği doğrulanmış kullanıcının resimlerini sayfalı döner.

Query Parametreleri

Parametre Varsayılan Maks ıklama
page 1 Sayfa numarası
limit 20 100 Sayfa başına kayıt

Başarılı Yanıt 200 OK

{
  "data": [
    {
      "id": 12,
      "user_id": 1,
      "filename": "550e8400....webp",
      "public_path": "/uploads/550e8400....webp",
      "image_url": "http://localhost:8080/uploads/550e8400....webp",
      "mime_type": "image/jpeg",
      "size_kb": 42,
      "width": 800,
      "height": 600,
      "quality": 85,
      "format": "webp",
      "mode": "original",
      "created_at": "2026-04-10T01:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "limit": 20
}

GET /images/:id — Tek Resim Detayı

Kimliği doğrulanmış kullanıcıya ait belirli bir resmin detayını döner.

Başarılı Yanıt 200 OK

Listeleme endpoint'indeki tek kayıt yapısı ile aynı.

Hata Yanıtları

Kod ıklama
404 Resim bulunamadı veya kullanıcıya ait değil

GET /images/:id/process — Resmi İşle ve Döndür

API token ile korunur (JWT gerektirmez). Web sitelerinde <img src="..."> ile doğrudan kullanım için tasarlanmıştır.

Query Parametreleri

Parametre Zorunlu ıklama
token Kullanıcıya ait API token
w Hedef genişlik (px)
h Hedef yükseklik (px)
q Kalite 1-100
f Format: webp, avif, png, jpg
mode cover — kırparak doldur

Başarılı Yanıt 200 OK — İşlenmiş resim binary verisi (image/webp, image/jpeg, vb.)

Örnek Kullanım

<!-- Orijinal resmi göster -->
<img src="http://localhost:8080/images/12/process?token=API_TOKEN">

<!-- 400×300 WebP olarak göster -->
<img src="http://localhost:8080/images/12/process?token=API_TOKEN&w=400&h=300&f=webp">

<!-- Cover kırpma ile 200×200 thumbnail -->
<img src="http://localhost:8080/images/12/process?token=API_TOKEN&w=200&h=200&mode=cover&f=webp">

Hata Yanıtları

Kod ıklama
401 Token eksik, geçersiz veya süresi dolmuş
404 Resim bulunamadı
500 Dosya okunamadı veya işleme hatası

Admin

Tüm /admin endpoint'leri hem Authorization: Bearer <access_token> hem de admin yetkisi gerektirir.

POST /admin/users/:id/api-token — API Token Oluştur

Belirtilen kullanıcıya yeni bir API token atar.

Path Parametreleri

Parametre Tür ıklama
id int Hedef kullanıcı ID

İstek (multipart/form-data veya query)

Alan Tür ıklama
expires_in_days int Kaç gün geçerli (0 = süresiz)

Başarılı Yanıt 200 OK

{
  "message": "API token created successfully",
  "api_token": "550e8400-e29b-41d4-a716-446655440000",
  "expires_at": "2026-05-10T01:00:00Z"
}

GET /admin/images — Tüm Resimleri Listele

Tüm kullanıcılara ait resimleri sayfalı döner.

Query Parametreleri

Parametre ıklama
page Sayfa numarası (varsayılan: 1)
limit Sayfa başına kayıt (varsayılan: 20, maks: 100)
user_id Belirli bir kullanıcıya göre filtrele

Başarılı Yanıt 200 OK

{
  "data": [
    {
      "id": 12,
      "user_id": 3,
      "filename": "550e8400-e29b-41d4-a716-446655440000.webp",
      "public_path": "/uploads/550e8400-e29b-41d4-a716-446655440000.webp",
      "image_url": "http://localhost:8080/uploads/550e8400-e29b-41d4-a716-446655440000.webp",
      "mime_type": "image/jpeg",
      "size_kb": 42,
      "width": 800,
      "height": 600,
      "quality": 85,
      "format": "webp",
      "mode": "original",
      "created_at": "2026-04-10T01:00:00Z"
    }
  ],
  "total": 50,
  "page": 1,
  "limit": 20
}

Static Files

GET /uploads/:filename — Yüklenen Resme Eriş

Yüklenen resimlere doğrudan URL ile erişim sağlar. Path traversal saldırılarına karşı korumalıdır.

GET /uploads/550e8400-e29b-41d4-a716-446655440000.webp

Kimlik Doğrulama

JWT Token Yapısı

{
  "user_id": 1,
  "role": "admin",
  "exp": 1775771479,
  "iat": 1775770579
}
Claim ıklama
user_id Kullanıcı birincil anahtarı
role "admin" veya "user"
exp Token son geçerlilik zamanı (Unix timestamp)
iat Token üretim zamanı

Token Ömürleri

Token Ömür
Access Token 15 dakika
Refresh Token 7 gün
API Token Admin tarafından belirlenir (sınırsız veya belirli gün)

İstek Başlığı

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token Yenileme Akışı

POST /auth/login
  → access_token (15 dk) + refresh_token (7 gün)

access_token süresi dolunca:
  POST /auth/refresh  body: refresh_token=...
  → yeni access_token + yeni refresh_token

Veri Modelleri

User (accounts paketi)

Alan Tür ıklama
id uint Birincil anahtar
email string Benzersiz, zorunlu
password_hash string bcrypt (JSON'da gizli)
is_admin bool Admin yetkisi (varsayılan: false)
api_token string Resim işleme için API token
api_token_expires_at *time Token bitiş zamanı (null = süresiz)
created_at time
updated_at time
deleted_at gorm.DeletedAt Soft delete

Image (images paketi)

Alan Tür ıklama
id uint Birincil anahtar
user_id uint Sahibi (indexed)
filename string UUID tabanlı dosya adı
public_path string /uploads/<uuid>.<ext>
mime_type string Orijinal MIME türü
size int64 KB cinsinden boyut
width int Piksel genişlik
height int Piksel yükseklik
quality int 1100 arası kalite değeri
format string bimg format adı
mode string original veya cover
created_at time

Resim İşleme

Resimler libvips ile işlenir. Desteklenen işlemler:

İşlem Parametre Örnek
Genişlik ayarla w=400 400px genişliğe küçült
Yükseklik ayarla h=300 300px yüksekliğe küçült
Kalite ayarla q=75 %75 kalite
Format dönüştür f=webp WebP formatına çevir
Cover kırpma mode=cover Oranı koruyarak kırp ve doldur

Desteklenen Formatlar

Format Parametre Değeri MIME Türü
JPEG jpg veya jpeg image/jpeg
PNG png image/png
WebP webp image/webp
AVIF avif image/avif

Yükleme Sırasında İşleme

Upload endpoint'ine w, h, f, q parametreleri verilirse resim diske yazılmadan önce işlenir:

curl -X POST http://localhost:8080/images \
  -H "Authorization: Bearer TOKEN" \
  -F "image=@foto.jpg" \
  -F "w=800" \
  -F "f=webp" \
  -F "q=85"

Swagger UI

Tarayıcıda aç:

http://localhost:8080/swagger

Swagger JSON spec:

http://localhost:8080/docs/swagger.json

Spec içindeki host ve schemes alanları çalışma zamanında dinamik olarak kaldırılır; böylece farklı ortamlarda (proxy, HTTPS, CDN) Swagger UI her zaman mevcut host'a istek atar.

Swagger'dan istek atmak için:

  1. /auth/login endpoint'ini çalıştırın
  2. Dönen access_token'ı kopyalayın
  3. Sağ üstteki Authorize butonuna tıklayın
  4. Bearer <token> formatında girin

Testler

# Tüm paket testleri
go test ./...

# Detaylı çıktı
go test -v ./...

# Belirli paket
go test -v ./accounts
go test -v ./images
go test -v ./router
go test -v ./configs
go test -v ./docs
go test -v ./accounts/models

Test Kapsamı

Paket Test Sayısı Kapsam
accounts 15 JWT üretimi, parse, rol normalizasyonu, roleFromUser, User model
accounts/models 6 IsEmailVerified, JSON tag güvenliği, GORM tag, RefreshToken alanları
configs 27 normalizeOrigin (8), parseOriginList (4), envIntOr/envInt64Or (8), bootstrap (7)
docs 10 SwaggerInfo alanları, şablon içeriği, swaggo registry kaydı
images 9 Path yardımcıları, buildImageURL (forwarded header), sayfalama
router 2 Swagger JSON host/scheme kaldırma, statik dosya servisi

Veritabanı veya Redis bağlantısı gerektiren testler mevcut değildir; tüm testler izole ve bağımsız çalışır.


Swagger Spec Güncelleme

Yeni endpoint eklendiğinde veya mevcut godoc yorumu değiştirildiğinde spec'i yeniden üretmek gerekir:

swag init --parseDependency --parseInternal

Bu komut şu dosyaları günceller:

docs/
├── docs.go        ← Go kodu (swaggo runtime için)
├── swagger.json   ← Swagger UI'ın okuduğu spec
└── swagger.yaml   ← YAML formatı

Deployment

Docker ile

FROM golang:1.26-alpine AS builder
RUN apk add --no-cache vips-dev build-base
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o goimgApi .

FROM alpine:3.19
RUN apk add --no-cache vips
WORKDIR /app
COPY --from=builder /app/goimgApi .
COPY --from=builder /app/docs ./docs
RUN mkdir -p uploads
EXPOSE 8080
CMD ["./goimgApi"]
docker build -t goimgapi .
docker run -p 8080:8080 --env-file .env goimgapi

Nginx Reverse Proxy Örneği

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        client_max_body_size 50M;
    }
}

X-Forwarded-Proto ve X-Forwarded-Host başlıkları, upload response'unda dönen image_url değerinin doğru domain ile üretilmesi için gereklidir.

Production için .env Kontrol Listesi

  • JWT_SECRET — en az 64 karakter, rastgele üretilmiş
  • JWT_REFRESH_SECRET — JWT_SECRET'tan farklı, en az 64 karakter
  • DB_PASSWORD — güçlü, production şifresi
  • CORS_BOOTSTRAP_WHITELIST_ORIGINS — yalnızca gerçek frontend domain'leri
  • uploads/ dizini uygulama tarafından yazılabilir (chmod 755)

Katkı

  1. Fork'layın
  2. Feature branch oluşturun: git checkout -b feature/ozellik-adi
  3. Testler ekleyin ve geçtiğinden emin olun: go test ./...
  4. Pull request açın

Lisans

MIT

Description
No description provided
Readme 28 MiB
Languages
Go 100%