first commit
This commit is contained in:
800
README.md
Normal file
800
README.md
Normal file
@@ -0,0 +1,800 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user