801 lines
21 KiB
Markdown
801 lines
21 KiB
Markdown
# 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](#özellikler)
|
||
- [Teknoloji Yığını](#teknoloji-yığını)
|
||
- [Proje Yapısı](#proje-yapısı)
|
||
- [Gereksinimler](#gereksinimler)
|
||
- [Kurulum ve Çalıştırma](#kurulum-ve-çalıştırma)
|
||
- [Ortam Değişkenleri](#ortam-değişkenleri)
|
||
- [API Referansı](#api-referansı)
|
||
- [Auth](#auth)
|
||
- [Images](#images)
|
||
- [Admin](#admin)
|
||
- [Static Files](#static-files)
|
||
- [Kimlik Doğrulama](#kimlik-doğrulama)
|
||
- [Veri Modelleri](#veri-modelleri)
|
||
- [Resim İşleme](#resim-işleme)
|
||
- [Swagger UI](#swagger-ui)
|
||
- [Testler](#testler)
|
||
- [Deployment](#deployment)
|
||
|
||
---
|
||
|
||
## Ö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](https://github.com/gofiber/fiber) |
|
||
| ORM | [GORM](https://gorm.io) + MySQL driver |
|
||
| Resim İşleme | [bimg](https://github.com/h2non/bimg) (libvips bağlayıcısı) |
|
||
| JWT | [golang-jwt/jwt v5](https://github.com/golang-jwt/jwt) |
|
||
| Redis | [go-redis/redis v9](https://github.com/redis/go-redis) |
|
||
| 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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
git clone https://github.com/kullanici/goimgApi.git
|
||
cd goimgApi
|
||
```
|
||
|
||
### 2. Bağımlılıkları yükleyin
|
||
|
||
```bash
|
||
go mod download
|
||
```
|
||
|
||
### 3. Ortam değişkenlerini ayarlayın
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
# .env dosyasını düzenleyin
|
||
```
|
||
|
||
### 4. Swagger spec'ini üretin
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
go run .
|
||
```
|
||
|
||
Sunucu `http://localhost:8080` adresinde başlar.
|
||
|
||
### Geliştirme modunda (hot-reload)
|
||
|
||
```bash
|
||
# 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:
|
||
|
||
```dotenv
|
||
# ─── 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 | Açıklama |
|
||
|------|-----|---------|----------|
|
||
| `email` | string | ✅ | Geçerli e-posta adresi |
|
||
| `password` | string | ✅ | En az 6 karakter |
|
||
|
||
**Başarılı Yanıt** `201 Created`
|
||
|
||
```json
|
||
{
|
||
"message": "User registered",
|
||
"user_id": 1
|
||
}
|
||
```
|
||
|
||
**Hata Yanıtları**
|
||
|
||
| Kod | Açı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`
|
||
|
||
```json
|
||
{
|
||
"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 | Açı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`
|
||
|
||
```json
|
||
{
|
||
"access_token": "eyJhbGci...",
|
||
"refresh_token": "eyJhbGci..."
|
||
}
|
||
```
|
||
|
||
**Hata Yanıtları**
|
||
|
||
| Kod | Açı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 | Açı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`
|
||
|
||
```json
|
||
{
|
||
"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 | Açıklama |
|
||
|-----------|-----------|------|----------|
|
||
| `page` | `1` | — | Sayfa numarası |
|
||
| `limit` | `20` | `100` | Sayfa başına kayıt |
|
||
|
||
**Başarılı Yanıt** `200 OK`
|
||
|
||
```json
|
||
{
|
||
"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 | Açı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 | Açı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**
|
||
|
||
```html
|
||
<!-- 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 | Açı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 | Açıklama |
|
||
|-----------|-----|----------|
|
||
| `id` | int | Hedef kullanıcı ID |
|
||
|
||
**İstek** (`multipart/form-data` veya query)
|
||
|
||
| Alan | Tür | Açıklama |
|
||
|------|-----|----------|
|
||
| `expires_in_days` | int | Kaç gün geçerli (0 = süresiz) |
|
||
|
||
**Başarılı Yanıt** `200 OK`
|
||
|
||
```json
|
||
{
|
||
"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 | Açı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`
|
||
|
||
```json
|
||
{
|
||
"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ı
|
||
|
||
```json
|
||
{
|
||
"user_id": 1,
|
||
"role": "admin",
|
||
"exp": 1775771479,
|
||
"iat": 1775770579
|
||
}
|
||
```
|
||
|
||
| Claim | Açı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ığı
|
||
|
||
```http
|
||
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 | Açı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 | Açı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 | 1–100 arası kalite değeri |
|
||
| `format` | string | bimg format adı |
|
||
| `mode` | string | `original` veya `cover` |
|
||
| `created_at` | time | |
|
||
|
||
---
|
||
|
||
## Resim İşleme
|
||
|
||
Resimler [libvips](https://libvips.github.io/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:
|
||
|
||
```bash
|
||
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
|
||
|
||
```bash
|
||
# 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:
|
||
|
||
```bash
|
||
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
|
||
|
||
```dockerfile
|
||
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"]
|
||
```
|
||
|
||
```bash
|
||
docker build -t goimgapi .
|
||
docker run -p 8080:8080 --env-file .env goimgapi
|
||
```
|
||
|
||
### Nginx Reverse Proxy Örneği
|
||
|
||
```nginx
|
||
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
|
||
|