first commit
This commit is contained in:
12
belgeler/README.md
Normal file
12
belgeler/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# goaresv3 — Proje Belgeleri
|
||||
|
||||
Bu klasör, projenin teknik belgelerini içerir.
|
||||
|
||||
| Dosya | Konu |
|
||||
|---|---|
|
||||
| [proje-yapisi.md](proje-yapisi.md) | Klasör ve dosya ağacı, her birinin rolü |
|
||||
| [auth.md](auth.md) | Kimlik doğrulama sistemi (Register / Login / JWT) |
|
||||
| [api-referans.md](api-referans.md) | Tüm endpoint'ler, istek/yanıt örnekleri |
|
||||
| [veritabani.md](veritabani.md) | GORM modelleri ve veritabanı şeması |
|
||||
| [ortam-degiskenleri.md](ortam-degiskenleri.md) | `.env` değişkenleri ve açıklamaları |
|
||||
| [testler.md](testler.md) | Otomatik test kapsamı ve çalıştırma rehberi |
|
||||
267
belgeler/api-referans.md
Normal file
267
belgeler/api-referans.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# API Referansı
|
||||
|
||||
Base URL: `http://localhost:8080`
|
||||
|
||||
---
|
||||
|
||||
## Public Endpoint'ler
|
||||
_Token gerektirmez._
|
||||
|
||||
---
|
||||
|
||||
### `POST /api/v1/auth/register`
|
||||
|
||||
Yeni kullanıcı oluşturur ve doğrulama maili gönderir.
|
||||
|
||||
**İstek gövdesi**
|
||||
```json
|
||||
{
|
||||
"username": "ali",
|
||||
"email": "ali@ornek.com",
|
||||
"password": "gizli1234",
|
||||
"confirm_password": "gizli1234"
|
||||
}
|
||||
```
|
||||
|
||||
| Alan | Tip | Kural |
|
||||
|---|---|---|
|
||||
| `username` | string | Zorunlu, 3–50 karakter |
|
||||
| `email` | string | Zorunlu, geçerli e-posta |
|
||||
| `password` | string | Zorunlu, min 8 karakter |
|
||||
| `confirm_password` | string | Zorunlu, `password` ile aynı olmalı |
|
||||
|
||||
**Yanıtlar**
|
||||
|
||||
| Durum | Açıklama | Gövde örneği |
|
||||
|---|---|---|
|
||||
| `201 Created` | Kayıt başarılı, doğrulama maili gönderildi | `{ "message": "user created. please verify your email", "user_id": 1 }` |
|
||||
| `400 Bad Request` | Doğrulama hatası | `{ "error": "Key: 'RegisterRequest.Email' Error:..." }` |
|
||||
| `400 Bad Request` | Şifreler eşleşmiyor | `{ "error": "password and confirm_password do not match" }` |
|
||||
| `409 Conflict` | E-posta zaten kayıtlı | `{ "error": "email already in use" }` |
|
||||
| `500 Internal Server Error` | Doğrulama maili gönderilemedi | `{ "error": "failed to send verification email" }` |
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/v1/auth/verify-email`
|
||||
|
||||
Kullanıcı email doğrulama linkindeki token ile hesabı aktive eder.
|
||||
|
||||
**Query parametreleri**
|
||||
|
||||
| Alan | Tip | Kural |
|
||||
|---|---|---|
|
||||
| `token` | string | Zorunlu |
|
||||
|
||||
**Yanıtlar**
|
||||
|
||||
| Durum | Açıklama | Gövde örneği |
|
||||
|---|---|---|
|
||||
| `200 OK` | Email doğrulandı | `{ "message": "email verified successfully" }` |
|
||||
| `400 Bad Request` | Token eksik/geçersiz | `{ "error": "invalid or expired verification token" }` |
|
||||
| `500 Internal Server Error` | Sunucu hatası | `{ "error": "could not verify email" }` |
|
||||
|
||||
---
|
||||
|
||||
### `POST /api/v1/auth/login`
|
||||
|
||||
E-posta ve şifre ile giriş yapar; token çifti döner.
|
||||
|
||||
**İstek gövdesi**
|
||||
```json
|
||||
{
|
||||
"email": "ali@ornek.com",
|
||||
"password": "gizli1234"
|
||||
}
|
||||
```
|
||||
|
||||
**Yanıtlar**
|
||||
|
||||
| Durum | Açıklama | Gövde örneği |
|
||||
|---|---|---|
|
||||
| `200 OK` | Giriş başarılı | Aşağıya bakın |
|
||||
| `400 Bad Request` | Eksik alan | `{ "error": "..." }` |
|
||||
| `401 Unauthorized` | Yanlış bilgi | `{ "error": "invalid email or password" }` |
|
||||
| `403 Forbidden` | Email doğrulanmamış | `{ "error": "email is not verified" }` |
|
||||
|
||||
```json
|
||||
// 200 OK
|
||||
{
|
||||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `POST /api/v1/auth/refresh`
|
||||
|
||||
Süresi dolmuş access token'ı yeniler.
|
||||
|
||||
**İstek gövdesi**
|
||||
```json
|
||||
{ "refresh_token": "eyJ..." }
|
||||
```
|
||||
|
||||
**Yanıtlar**
|
||||
|
||||
| Durum | Açıklama | Gövde örneği |
|
||||
|---|---|---|
|
||||
| `200 OK` | Yenileme başarılı | `{ "access_token": "eyJ...", "token_type": "Bearer" }` |
|
||||
| `400 Bad Request` | Eksik alan | `{ "error": "..." }` |
|
||||
| `401 Unauthorized` | Token geçersiz/süresi dolmuş | `{ "error": "invalid or expired refresh token" }` |
|
||||
| `403 Forbidden` | Email doğrulanmamış | `{ "error": "email is not verified" }` |
|
||||
|
||||
---
|
||||
|
||||
## Korumalı Endpoint'ler
|
||||
_`Authorization: Bearer <access_token>` başlığı zorunludur._
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/v1/me`
|
||||
|
||||
Oturum açmış kullanıcının kimlik bilgilerini döner.
|
||||
|
||||
**İstek başlığı**
|
||||
```
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
```
|
||||
|
||||
**Yanıtlar**
|
||||
|
||||
| Durum | Açıklama | Gövde örneği |
|
||||
|---|---|---|
|
||||
| `200 OK` | Başarılı | `{ "user_id": 1, "email": "ali@ornek.com", "username": "ali" }` |
|
||||
| `401 Unauthorized` | Başlık eksik veya token geçersiz | `{ "error": "authorization header missing or malformed" }` |
|
||||
|
||||
---
|
||||
|
||||
## Admin Yetkisi Gerektiren Endpoint'ler
|
||||
|
||||
Bu endpointler için kullanıcı hem giriş yapmış olmalı hem de `is_admin=true` olmalıdır.
|
||||
|
||||
### Settings
|
||||
|
||||
- `PUT /api/v1/settings`
|
||||
- `POST /api/v1/settings/heroes`
|
||||
- `PUT /api/v1/settings/heroes/{id}`
|
||||
- `DELETE /api/v1/settings/heroes/{id}`
|
||||
- `POST /api/v1/settings/cors/whitelist`
|
||||
- `PUT /api/v1/settings/cors/whitelist/{id}`
|
||||
- `DELETE /api/v1/settings/cors/whitelist/{id}`
|
||||
- `POST /api/v1/settings/cors/blacklist`
|
||||
- `PUT /api/v1/settings/cors/blacklist/{id}`
|
||||
- `DELETE /api/v1/settings/cors/blacklist/{id}`
|
||||
- `POST /api/v1/settings/rate-limits`
|
||||
- `PUT /api/v1/settings/rate-limits/{id}`
|
||||
- `DELETE /api/v1/settings/rate-limits/{id}`
|
||||
|
||||
### Shop
|
||||
|
||||
- `POST /api/v1/shop/categories`
|
||||
- `PUT /api/v1/shop/categories/{id}`
|
||||
- `DELETE /api/v1/shop/categories/{id}`
|
||||
- `POST /api/v1/shop/tags`
|
||||
- `PUT /api/v1/shop/tags/{id}`
|
||||
- `DELETE /api/v1/shop/tags/{id}`
|
||||
- `POST /api/v1/shop/products`
|
||||
- `PUT /api/v1/shop/products/{id}`
|
||||
- `DELETE /api/v1/shop/products/{id}`
|
||||
|
||||
### Blog
|
||||
|
||||
- `POST /api/v1/blog/categories`
|
||||
- `PUT /api/v1/blog/categories/{id}`
|
||||
- `DELETE /api/v1/blog/categories/{id}`
|
||||
- `POST /api/v1/blog/tags`
|
||||
- `PUT /api/v1/blog/tags/{id}`
|
||||
- `DELETE /api/v1/blog/tags/{id}`
|
||||
- `POST /api/v1/blog/posts`
|
||||
- `PUT /api/v1/blog/posts/{id}`
|
||||
- `DELETE /api/v1/blog/posts/{id}`
|
||||
|
||||
---
|
||||
|
||||
## Auth Gerekli (Okuma / Kullanıcı İşlemleri)
|
||||
|
||||
### Settings (read)
|
||||
- `GET /api/v1/settings`
|
||||
- `GET /api/v1/settings/heroes`
|
||||
- `GET /api/v1/settings/cors/whitelist`
|
||||
- `GET /api/v1/settings/cors/blacklist`
|
||||
- `GET /api/v1/settings/rate-limits`
|
||||
|
||||
### Shop
|
||||
- `GET /api/v1/shop/categories`
|
||||
- `GET /api/v1/shop/tags`
|
||||
- `GET /api/v1/shop/products`
|
||||
- `GET /api/v1/shop/products/{id}`
|
||||
- `GET /api/v1/shop/cart`
|
||||
- `POST /api/v1/shop/cart/items`
|
||||
- `PUT /api/v1/shop/cart/items/{itemId}`
|
||||
- `DELETE /api/v1/shop/cart/items/{itemId}`
|
||||
|
||||
### Blog
|
||||
- `GET /api/v1/blog/categories`
|
||||
- `GET /api/v1/blog/tags`
|
||||
- `GET /api/v1/blog/posts`
|
||||
- `GET /api/v1/blog/posts/{id}`
|
||||
|
||||
---
|
||||
|
||||
## Runtime Güvenlik Davranışı
|
||||
|
||||
- **CORS blacklist**: origin varsa doğrudan blok (`403`).
|
||||
- **CORS whitelist**: origin whitelist'te ise rate-limit muaf.
|
||||
- **Whitelist/blacklist dışı**: CORS geçer, rate-limit uygulanır.
|
||||
- **Rate limit kural sırası**:
|
||||
1. Endpoint adı (`api/v1/auth/login` gibi)
|
||||
2. Fallback `api`
|
||||
|
||||
> En güncel endpoint listesi ve request/response şemaları için Swagger UI kullanın.
|
||||
|
||||
---
|
||||
|
||||
## Hata Formatı
|
||||
|
||||
Tüm hata yanıtları aynı yapıdadır:
|
||||
|
||||
```json
|
||||
{ "error": "açıklayıcı hata mesajı" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## curl Örnekleri
|
||||
|
||||
```bash
|
||||
# Kayıt
|
||||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"ali","email":"ali@ornek.com","password":"gizli1234","confirm_password":"gizli1234"}'
|
||||
|
||||
# Email doğrulama
|
||||
curl "http://localhost:8080/api/v1/auth/verify-email?token=<verification_token>"
|
||||
|
||||
# Giriş
|
||||
curl -X POST http://localhost:8080/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"ali@ornek.com","password":"gizli1234"}'
|
||||
|
||||
# Korumalı endpoint
|
||||
curl http://localhost:8080/api/v1/me \
|
||||
-H "Authorization: Bearer <access_token>"
|
||||
|
||||
# Token yenile
|
||||
curl -X POST http://localhost:8080/api/v1/auth/refresh \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"refresh_token":"<refresh_token>"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Swagger
|
||||
|
||||
- UI: `http://localhost:8080/swagger/index.html`
|
||||
- OpenAPI dosyaları: `docs/swagger.json`, `docs/swagger.yaml`
|
||||
201
belgeler/auth.md
Normal file
201
belgeler/auth.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Kimlik Doğrulama Sistemi
|
||||
|
||||
## Genel Bakış
|
||||
|
||||
Proje email + şifre tabanlı kimlik doğrulama kullanır. Başarılı girişte iki token döner:
|
||||
|
||||
| Token | Ömür | Kullanım |
|
||||
|---|---|---|
|
||||
| `access_token` | 15 dakika | Her korumalı isteğe `Authorization: Bearer` başlığı |
|
||||
| `refresh_token` | 7 gün | Süresi dolan access token'ı yenilemek için |
|
||||
|
||||
Ek kural: Email doğrulaması tamamlanmadan login ve refresh işlemleri engellenir.
|
||||
|
||||
Algoritma: **HS256** (HMAC-SHA256)
|
||||
|
||||
---
|
||||
|
||||
## Akış Diyagramı
|
||||
|
||||
```
|
||||
┌─────────┐ POST /api/v1/auth/register ┌─────────┐
|
||||
│ Client │ ────────────────────────────────────► │ Server │
|
||||
│ │ ◄──── 201 { user_id } + verify mail │ │
|
||||
└─────────┘ └─────────┘
|
||||
|
||||
┌─────────┐ GET /api/v1/auth/verify-email ┌─────────┐
|
||||
│ Client │ ── token linki ile ──────────────────► │ Server │
|
||||
│ │ ◄──── 200 email verified ──────────── │ │
|
||||
└─────────┘ └─────────┘
|
||||
|
||||
┌─────────┐ POST /api/v1/auth/login ┌─────────┐
|
||||
│ Client │ ── { email, password } ──────────────► │ Server │
|
||||
│ │ ◄── 200 { access_token, │ │
|
||||
│ │ refresh_token } ───────────── └─────────┘
|
||||
└─────────┘
|
||||
|
||||
┌─────────┐ GET /api/v1/me ┌─────────┐
|
||||
│ Client │ ── Authorization: Bearer <access> ───► │ Server │
|
||||
│ │ ◄── 200 { user_id, email, username } │ (auth │
|
||||
└─────────┘ │ middleware)
|
||||
└─────────┘
|
||||
|
||||
┌─────────┐ POST /api/v1/auth/refresh ┌─────────┐
|
||||
│ Client │ ── { refresh_token } ────────────────► │ Server │
|
||||
│ │ ◄── 200 { access_token } ───────────── └─────────┘
|
||||
└─────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kayıt (`Register`)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `Register()`
|
||||
|
||||
1. JSON body doğrulanır (`username` min 3, `email` geçerli format, `password` min 8 karakter, `confirm_password` min 8 karakter).
|
||||
2. `password` ve `confirm_password` eşleşmesi kontrol edilir.
|
||||
3. Şifre `bcrypt.DefaultCost` ile hash'lenir.
|
||||
4. `User` kaydı veritabanına yazılır.
|
||||
5. Rastgele bir email doğrulama token'ı üretilir ve kullanıcıya yazılır.
|
||||
6. Kullanıcıya doğrulama maili gönderilir.
|
||||
7. `email` alanı unique index'li; duplicate durumunda `409 Conflict` döner.
|
||||
|
||||
Not: Mail gönderimi başarısız olursa oluşturulan kullanıcı kaydı geri silinir.
|
||||
|
||||
```json
|
||||
// İstek
|
||||
{
|
||||
"username": "ali",
|
||||
"email": "ali@ornek.com",
|
||||
"password": "gizli1234",
|
||||
"confirm_password": "gizli1234"
|
||||
}
|
||||
|
||||
// Başarılı yanıt 201
|
||||
{ "message": "user created. please verify your email", "user_id": 1 }
|
||||
```
|
||||
|
||||
## Email Doğrulama (`VerifyEmail`)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `VerifyEmail()`
|
||||
|
||||
1. Query string içinden `token` alınır.
|
||||
2. Token ile kullanıcı bulunur.
|
||||
3. `email_verified=true`, `email_verified_at=now`, `email_verify_token=""` olarak güncellenir.
|
||||
|
||||
```json
|
||||
// Başarılı yanıt 200
|
||||
{ "message": "email verified successfully" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Giriş (`Login`)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `Login()`
|
||||
|
||||
1. Email ile kullanıcı aranır.
|
||||
2. `email_verified` kontrol edilir; doğrulanmamış hesaplara `403` dönülür.
|
||||
3. `bcrypt.CompareHashAndPassword` ile şifre doğrulanır.
|
||||
4. Her iki adımda da hata mesajı aynı tutulur — kullanıcı numaralandırma saldırısına karşı.
|
||||
5. Başarıda access + refresh token döner.
|
||||
|
||||
```json
|
||||
// İstek
|
||||
{ "email": "ali@ornek.com", "password": "gizli1234" }
|
||||
|
||||
// Başarılı yanıt 200
|
||||
{
|
||||
"access_token": "eyJ...",
|
||||
"refresh_token": "eyJ...",
|
||||
"token_type": "Bearer"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Token Yenileme (`RefreshToken`)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `RefreshToken()`
|
||||
|
||||
1. Body'den `refresh_token` alınır.
|
||||
2. `JWT_REFRESH_SECRET` ile doğrulanır.
|
||||
3. Kullanıcının `email_verified` durumu kontrol edilir; doğrulanmamışsa `403` döner.
|
||||
4. Claims'ten `user_id`, `email`, `username` alanları kullanılarak yeni access token üretilir.
|
||||
5. Refresh token yenilenmez (sliding window değil, sabit pencere).
|
||||
|
||||
```json
|
||||
// İstek
|
||||
{ "refresh_token": "eyJ..." }
|
||||
|
||||
// Başarılı yanıt 200
|
||||
{ "access_token": "eyJ...", "token_type": "Bearer" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Google Login (OAuth 2.0)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `GoogleLogin()` ve `GoogleCallback()`
|
||||
|
||||
Akış:
|
||||
|
||||
1. `GET /api/v1/auth/google/login`
|
||||
2. Sunucu CSRF için `state` üretir, cookie'ye yazar ve `auth_url` döner.
|
||||
3. Client kullanıcıyı `auth_url` adresine yönlendirir.
|
||||
4. Google kullanıcıyı callback'e döndürür: `GET /api/v1/auth/google/callback?state=...&code=...`
|
||||
5. Sunucu `state` doğrular, `code` ile Google'dan token alır.
|
||||
6. Google `userinfo` verisi çekilir.
|
||||
7. Kullanıcı email'e göre bulunur/oluşturulur, `SocialAccount(provider=google)` kaydı bağlanır.
|
||||
8. Google ile gelen kullanıcı email'i doğrulanmış kabul edilir (`email_verified=true`).
|
||||
9. Yerel `access_token` + `refresh_token` üretilip döndürülür.
|
||||
|
||||
Yeni endpointler:
|
||||
|
||||
- `GET /api/v1/auth/google/login`
|
||||
- `GET /api/v1/auth/google/callback`
|
||||
|
||||
---
|
||||
|
||||
## GitHub Login (OAuth 2.0)
|
||||
|
||||
**Dosya:** `app/accounts/controllers/user.go` → `GitHubLogin()` ve `GitHubCallback()`
|
||||
|
||||
Akış:
|
||||
|
||||
1. `GET /api/v1/auth/github/login`
|
||||
2. Sunucu CSRF için `state` üretir, cookie'ye yazar ve `auth_url` döner.
|
||||
3. Client kullanıcıyı `auth_url` adresine yönlendirir.
|
||||
4. GitHub callback'e döndürür: `GET /api/v1/auth/github/callback?state=...&code=...`
|
||||
5. Sunucu `state` doğrular, `code` ile GitHub'dan token alır.
|
||||
6. GitHub profil/email bilgisi çekilir.
|
||||
7. Kullanıcı email'e göre bulunur/oluşturulur, `SocialAccount(provider=github)` kaydı bağlanır.
|
||||
8. GitHub ile gelen kullanıcı email'i doğrulanmış kabul edilir (`email_verified=true`).
|
||||
9. Yerel `access_token` + `refresh_token` üretilip döndürülür.
|
||||
|
||||
Yeni endpointler:
|
||||
|
||||
- `GET /api/v1/auth/github/login`
|
||||
- `GET /api/v1/auth/github/callback`
|
||||
|
||||
---
|
||||
|
||||
## Güvenlik Notları
|
||||
|
||||
- Şifreler veritabanında **asla düz metin** tutulmaz; bcrypt hash'i saklanır.
|
||||
- `Password` alanı modelde `json:"-"` ile işaretlidir — API yanıtlarında görünmez.
|
||||
- Access ve refresh token farklı secret'larla imzalanır (`JWT_SECRET` / `JWT_REFRESH_SECRET`).
|
||||
- Production'da her iki secret'ın en az 32 karakter uzunluğunda, rastgele olması gerekir.
|
||||
- HTTPS zorunludur; token'lar aktarım sırasında şifrelenmez.
|
||||
- Register adımında şifre uyumsuzluğu için `400 Bad Request` dönülür: `password and confirm_password do not match`.
|
||||
|
||||
---
|
||||
|
||||
## Swagger
|
||||
|
||||
- Swagger UI: `/swagger/index.html`
|
||||
- Doküman üretme komutu:
|
||||
|
||||
```bash
|
||||
$(go env GOPATH)/bin/swag init -g main.go
|
||||
```
|
||||
151
belgeler/ortam-degiskenleri.md
Normal file
151
belgeler/ortam-degiskenleri.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# Ortam Değişkenleri
|
||||
|
||||
Proje kök dizinindeki `.env` dosyasından veya sistem ortam değişkenlerinden okunur.
|
||||
`.env` yoksa sistem değişkenleri kullanılır (ör. Docker, CI ortamları).
|
||||
|
||||
---
|
||||
|
||||
## Veritabanı
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `DB_HOST` | `localhost` | MySQL sunucu adresi |
|
||||
| `DB_PORT` | `3306` | MySQL port |
|
||||
| `DB_USER` | `root` | Veritabanı kullanıcı adı |
|
||||
| `DB_PASSWORD` | _(boş)_ | Veritabanı şifresi |
|
||||
| `DB_NAME` | `goaresv3` | Veritabanı adı |
|
||||
|
||||
---
|
||||
|
||||
## JWT
|
||||
|
||||
| Değişken | Açıklama |
|
||||
|---|---|
|
||||
| `JWT_SECRET` | Access token imzalama anahtarı (min 32 karakter önerilir) |
|
||||
| `JWT_REFRESH_SECRET` | Refresh token imzalama anahtarı (min 32 karakter önerilir) |
|
||||
|
||||
> **Uyarı:** Her iki secret production ortamda farklı, uzun ve rastgele olmalıdır.
|
||||
> Örnek üretim komutu:
|
||||
> ```bash
|
||||
> openssl rand -hex 32
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## Uygulama
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `PORT` | `8080` | HTTP sunucu portu |
|
||||
| `APP_BASE_URL` | `http://localhost:8080` | Email doğrulama linki oluşturmak için temel adres |
|
||||
|
||||
---
|
||||
|
||||
## Google OAuth
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `SOCIAL_AUTH_GOOGLE_OAUTH2_KEY` | - | Google OAuth istemci ID |
|
||||
| `SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET` | - | Google OAuth istemci secret |
|
||||
| `SOCIAL_AUTH_GOOGLE_REDIRECT_URL` | - | Google callback URL (ör: `http://localhost:8080/api/v1/auth/google/callback`) |
|
||||
| `SOCIAL_AUTH_GOOGLE_SCOPES` | `openid,email,profile` | İsteğe bağlı scope listesi (virgülle ayrılmış) |
|
||||
|
||||
---
|
||||
|
||||
## GitHub OAuth
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `SOCIAL_AUTH_GITHUB_KEY` | - | GitHub OAuth istemci ID |
|
||||
| `SOCIAL_AUTH_GITHUB_SECRET` | - | GitHub OAuth istemci secret |
|
||||
| `SOCIAL_AUTH_GITHUB_REDIRECT_URL` | - | GitHub callback URL (ör: `http://localhost:8080/api/v1/auth/github/callback`) |
|
||||
| `SOCIAL_AUTH_GITHUB_SCOPES` | `read:user,user:email` | İsteğe bağlı scope listesi (virgülle ayrılmış) |
|
||||
|
||||
---
|
||||
|
||||
## CORS Bootstrap
|
||||
|
||||
> Bu değerler sadece **ilk açılış seed** için kullanılır.
|
||||
> DB'de kayıt varsa üzerine yazılmaz.
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `CORS_BOOTSTRAP_WHITELIST_ORIGINS` | _(boş)_ | Virgülle ayrılmış origin listesi (ör: `http://localhost:3000,https://admin.ornek.com`) |
|
||||
| `CORS_BOOTSTRAP_BLACKLIST_ORIGINS` | _(boş)_ | Virgülle ayrılmış engelli origin listesi |
|
||||
|
||||
---
|
||||
|
||||
## Rate Limit Bootstrap
|
||||
|
||||
> Bu değerler sadece **ilk açılış seed** için kullanılır.
|
||||
> `rate_limit_settings` tablosunda aynı `name` varsa değiştirilmez.
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `RL_BOOTSTRAP_LOGIN_MAX_REQUESTS` | `10` | `api/v1/auth/login` için maksimum istek |
|
||||
| `RL_BOOTSTRAP_LOGIN_WINDOW_SECONDS` | `60` | Login penceresi (sn) |
|
||||
| `RL_BOOTSTRAP_REGISTER_MAX_REQUESTS` | `5` | `api/v1/auth/register` için maksimum istek |
|
||||
| `RL_BOOTSTRAP_REGISTER_WINDOW_SECONDS` | `60` | Register penceresi (sn) |
|
||||
| `RL_BOOTSTRAP_API_MAX_REQUESTS` | `120` | Diğer endpointler için fallback `api` limiti |
|
||||
| `RL_BOOTSTRAP_API_WINDOW_SECONDS` | `60` | Genel fallback pencere (sn) |
|
||||
|
||||
---
|
||||
|
||||
## Email
|
||||
|
||||
| Değişken | Varsayılan | Açıklama |
|
||||
|---|---|---|
|
||||
| `EMAIL_HOST` | - | SMTP sunucu adresi |
|
||||
| `EMAIL_PORT` | - | SMTP port |
|
||||
| `EMAIL_HOST_USER` | _(boş olabilir)_ | SMTP kullanıcı adı |
|
||||
| `EMAIL_HOST_PASSWORD` | _(boş olabilir)_ | SMTP şifresi |
|
||||
| `EMAIL_USE_TLS` | `false` | STARTTLS kullanılsın mı |
|
||||
| `EMAIL_USE_SSL` | `false` | SMTPS (SSL/TLS) doğrudan bağlantı kullanılsın mı |
|
||||
| `EMAIL_FROM` | - | Gönderici email adresi |
|
||||
|
||||
---
|
||||
|
||||
## Örnek `.env`
|
||||
|
||||
```dotenv
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3307
|
||||
DB_USER=root
|
||||
DB_PASSWORD=sifrem
|
||||
DB_NAME=goaresv3
|
||||
|
||||
JWT_SECRET=uretilen-guclu-secret-1
|
||||
JWT_REFRESH_SECRET=uretilen-guclu-secret-2
|
||||
|
||||
PORT=8080
|
||||
APP_BASE_URL=http://localhost:8080
|
||||
|
||||
EMAIL_HOST=10.80.80.70
|
||||
EMAIL_PORT=1025
|
||||
EMAIL_HOST_USER=
|
||||
EMAIL_HOST_PASSWORD=
|
||||
EMAIL_USE_TLS=false
|
||||
EMAIL_USE_SSL=false
|
||||
EMAIL_FROM=noreply@gauth.local
|
||||
|
||||
CORS_BOOTSTRAP_WHITELIST_ORIGINS=http://localhost:3000,http://localhost:5173
|
||||
CORS_BOOTSTRAP_BLACKLIST_ORIGINS=
|
||||
|
||||
RL_BOOTSTRAP_LOGIN_MAX_REQUESTS=10
|
||||
RL_BOOTSTRAP_LOGIN_WINDOW_SECONDS=60
|
||||
RL_BOOTSTRAP_REGISTER_MAX_REQUESTS=5
|
||||
RL_BOOTSTRAP_REGISTER_WINDOW_SECONDS=60
|
||||
RL_BOOTSTRAP_API_MAX_REQUESTS=120
|
||||
RL_BOOTSTRAP_API_WINDOW_SECONDS=60
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Güvenlik
|
||||
|
||||
- `.env` dosyasını **asla** Git'e commit etmeyin.
|
||||
- `.gitignore` dosyanıza ekleyin:
|
||||
```
|
||||
.env
|
||||
```
|
||||
- Staging/production ortamlarda değişkenleri CI/CD secret yöneticisi (GitHub Secrets, Vault) ile iletin.
|
||||
56
belgeler/proje-yapisi.md
Normal file
56
belgeler/proje-yapisi.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Proje Yapısı
|
||||
|
||||
```
|
||||
goaresv3/
|
||||
├── main.go
|
||||
├── .env
|
||||
├── app/
|
||||
│ ├── accounts/
|
||||
│ │ ├── controllers/user.go
|
||||
│ │ └── models/accounts.go
|
||||
│ ├── settings/
|
||||
│ │ ├── controllers/settings.go
|
||||
│ │ └── models/{setting,hero,cors}.go
|
||||
│ ├── shop/
|
||||
│ │ ├── controllers/shop.go
|
||||
│ │ └── models/{product,cart}.go
|
||||
│ └── blog/
|
||||
│ ├── controllers/blog.go
|
||||
│ └── models/blog.go
|
||||
├── config/database.go
|
||||
├── pkg/
|
||||
│ ├── jwt/jwt.go
|
||||
│ ├── mailer/mailer.go
|
||||
│ ├── middleware/{auth,cors_dynamic,rate_limit_dynamic}.go
|
||||
│ └── swaggerui/initializer.go
|
||||
├── router/router.go
|
||||
├── docs/{docs.go,swagger.json,swagger.yaml}
|
||||
└── belgeler/
|
||||
```
|
||||
|
||||
## Uygulama Akışı
|
||||
|
||||
1. `.env` yüklenir (`godotenv`).
|
||||
2. `config.ConnectDB()` ile MySQL bağlantısı açılır.
|
||||
3. `config.RunAutoMigrate()` tüm modüllerin şemalarını uygular.
|
||||
4. `config.SeedSecurityDefaults()` CORS/RateLimit başlangıç kayıtlarını (yoksa) ekler.
|
||||
5. Gin başlatılır, global middlewareler çalışır:
|
||||
- `DynamicCORS()`
|
||||
- `DynamicRateLimit()`
|
||||
6. `router.Setup(r)` ile endpointler yüklenir.
|
||||
|
||||
## Yetki Modeli
|
||||
|
||||
- `AuthRequired()`:
|
||||
- Access token doğrular
|
||||
- Context'e `user_id`, `email`, `username` yazar
|
||||
- `AdminRequired()`:
|
||||
- `users.is_admin` alanını kontrol eder
|
||||
- `POST/PUT/DELETE/PATCH` gibi mutating endpointlerde kullanılır
|
||||
|
||||
## Route Grupları
|
||||
|
||||
- Public: `/api/v1/auth/*`
|
||||
- Auth zorunlu (read + user işlemleri): `/api/v1/*`
|
||||
- Admin zorunlu (mutating yönetim işlemleri): `/api/v1/*` altında admin grubu
|
||||
- Swagger UI: `/swagger/*any`
|
||||
88
belgeler/testler.md
Normal file
88
belgeler/testler.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Test Rehberi
|
||||
|
||||
Bu dokuman projedeki otomatik test kapsamini ve calistirma adimlarini ozetler.
|
||||
|
||||
## Calistirma
|
||||
|
||||
Tum testleri calistirmak icin:
|
||||
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
Kapsam (coverage) ile calistirmak icin:
|
||||
|
||||
```bash
|
||||
go test -cover ./...
|
||||
```
|
||||
|
||||
## Endpoint Test Matrisi
|
||||
|
||||
Asagidaki endpointler otomatik testlerle, hem basarili hem hata senaryolariyla kapsanir:
|
||||
|
||||
| Endpoint | Durum kodlari | Dosyalar |
|
||||
|---|---|---|
|
||||
| `POST /api/v1/auth/register` | `201`, `400`, `409`, `500` | [router/router_test.go](../router/router_test.go), [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go) |
|
||||
| `GET /api/v1/auth/verify-email` | `200`, `400` | [router/router_test.go](../router/router_test.go), [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go) |
|
||||
| `POST /api/v1/auth/login` | `200`, `400`, `401`, `403` | [router/router_test.go](../router/router_test.go), [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go) |
|
||||
| `POST /api/v1/auth/refresh` | `200`, `400`, `401`, `403` | [router/router_test.go](../router/router_test.go), [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go) |
|
||||
| `GET /api/v1/me` | `200`, `401` | [router/router_test.go](../router/router_test.go), [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go), [pkg/middleware/auth_test.go](../pkg/middleware/auth_test.go) |
|
||||
| `GET /swagger/index.html` | `200` | [router/router_test.go](../router/router_test.go) |
|
||||
| `POST /swagger/index.html` | `404` | [router/router_test.go](../router/router_test.go) |
|
||||
| `GET /swagger/swagger-initializer.js` | `200` | [router/router_test.go](../router/router_test.go) |
|
||||
|
||||
## Endpoint Senaryo Detayi
|
||||
|
||||
### `POST /api/v1/auth/register`
|
||||
- Basarili kayit ve mail gonderimi
|
||||
- `password`/`confirm_password` uyusmazligi
|
||||
- Duplicate email
|
||||
- SMTP/config hatasinda register rollback
|
||||
|
||||
### `GET /api/v1/auth/verify-email`
|
||||
- Basarili token ile aktivasyon
|
||||
- Token eksik
|
||||
- Token gecersiz
|
||||
|
||||
### `POST /api/v1/auth/login`
|
||||
- Dogrulanmis hesapla basarili login
|
||||
- Eksik/hatali request body
|
||||
- Yanlis sifre veya bulunamayan email
|
||||
- Dogrulanmamis email
|
||||
|
||||
### `POST /api/v1/auth/refresh`
|
||||
- Gecerli refresh token ile yeni access token
|
||||
- Eksik request body
|
||||
- Bozuk/gecersiz JWT
|
||||
- Token kullanicisi bulunamadi
|
||||
- Dogrulanmamis email
|
||||
|
||||
### `GET /api/v1/me`
|
||||
- Gecerli Bearer access token ile erisim
|
||||
- Authorization header yok
|
||||
- Raw token (Bearer olmadan) reddi
|
||||
- Username context yoksa DB fallback
|
||||
|
||||
## Paket Bazli Testler
|
||||
|
||||
| Paket | Dosya | Kapsam |
|
||||
|---|---|---|
|
||||
| `pkg/jwt` | [pkg/jwt/jwt_test.go](../pkg/jwt/jwt_test.go) | Token uretim/dogrulama, yanlis secret davranisi |
|
||||
| `pkg/middleware` | [pkg/middleware/auth_test.go](../pkg/middleware/auth_test.go), [pkg/middleware/dynamic_policies_test.go](../pkg/middleware/dynamic_policies_test.go) | Auth middleware + dynamic CORS/RateLimit davranisi |
|
||||
| `pkg/mailer` | [pkg/mailer/mailer_test.go](../pkg/mailer/mailer_test.go) | SMTP config validasyonu ve fake SMTP ile gonderim |
|
||||
| `app/accounts/controllers` | [app/accounts/controllers/user_test.go](../app/accounts/controllers/user_test.go) | Verify/Login/Refresh/Me handler seviyesinde davranis testleri |
|
||||
|
||||
## Dynamic Policy Testleri
|
||||
|
||||
`pkg/middleware/dynamic_policies_test.go` su senaryolari kapsar:
|
||||
|
||||
- Blacklist origin'in CORS tarafinda bloklanmasi
|
||||
- Whitelist origin'in rate-limitten muaf olmasi
|
||||
- Whitelist/blacklist disi origin'e rate-limit uygulanmasi
|
||||
- `login` ve `register` endpointlerine ayri limit kurallarinin calismasi
|
||||
|
||||
## Notlar
|
||||
|
||||
- Endpoint entegrasyon testi icin test icinde gecici SQLite veritabani kullanilir.
|
||||
- Register testinde email gonderimi icin test icinde fake SMTP sunucusu ayaga kaldirilir.
|
||||
- Testler ortamdan bagimsiz calisacak sekilde gerekli env degerlerini test icinde set eder.
|
||||
159
belgeler/veritabani.md
Normal file
159
belgeler/veritabani.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Veritabanı
|
||||
|
||||
ORM: **GORM v1.31**
|
||||
Sürücü: **MySQL (gorm.io/driver/mysql)**
|
||||
Karakter seti: `utf8mb4`
|
||||
|
||||
---
|
||||
|
||||
## Tablolar
|
||||
|
||||
### `users`
|
||||
|
||||
`gorm.Model` gömülüdür → `id`, `created_at`, `updated_at`, `deleted_at` otomatik eklenir.
|
||||
|
||||
| Sütun | Tip | Kısıtlama | Açıklama |
|
||||
|---|---|---|---|
|
||||
| `id` | BIGINT UNSIGNED | PK, AUTO_INCREMENT | — |
|
||||
| `username` | VARCHAR(255) | — | Kullanıcı adı |
|
||||
| `email` | VARCHAR(255) | UNIQUE, NOT NULL | Giriş e-postası |
|
||||
| `password` | VARCHAR(255) | — | bcrypt hash (JSON'da gizli) |
|
||||
| `email_verified` | TINYINT(1) | DEFAULT 0 | E-posta doğrulandı mı |
|
||||
| `email_verify_token` | VARCHAR(255) | INDEX | Doğrulama token'ı |
|
||||
| `email_verified_at` | DATETIME | NULL | Doğrulama zamanı |
|
||||
| `is_admin` | TINYINT(1) | DEFAULT 0 | Yönetici mi |
|
||||
| `created_at` | DATETIME | — | — |
|
||||
| `updated_at` | DATETIME | — | — |
|
||||
| `deleted_at` | DATETIME | INDEX, NULL | Soft-delete |
|
||||
|
||||
---
|
||||
|
||||
### `social_accounts`
|
||||
|
||||
| Sütun | Tip | Kısıtlama | Açıklama |
|
||||
|---|---|---|---|
|
||||
| `id` | BIGINT UNSIGNED | PK | — |
|
||||
| `user_id` | BIGINT UNSIGNED | NOT NULL, INDEX | `users.id` yabancı anahtar |
|
||||
| `provider` | VARCHAR(255) | NOT NULL | `google`, `github` vb. |
|
||||
| `provider_id` | VARCHAR(255) | NOT NULL | Sağlayıcıdan gelen ID |
|
||||
| `email` | VARCHAR(255) | — | Sağlayıcı e-postası |
|
||||
| `name` | VARCHAR(255) | — | Tam ad |
|
||||
| `avatar_url` | VARCHAR(255) | — | Profil fotoğrafı URL |
|
||||
| `created_at` | DATETIME | — | — |
|
||||
| `updated_at` | DATETIME | — | — |
|
||||
| `deleted_at` | DATETIME | NULL | Soft-delete |
|
||||
|
||||
---
|
||||
|
||||
### `profiles`
|
||||
|
||||
| Sütun | Tip | Kısıtlama | Açıklama |
|
||||
|---|---|---|---|
|
||||
| `id` | BIGINT UNSIGNED | PK | — |
|
||||
| `user_id` | BIGINT UNSIGNED | NOT NULL, INDEX | `users.id` yabancı anahtar |
|
||||
| `avatar_url` | VARCHAR(255) | — | Profil fotoğrafı |
|
||||
| `first_name` | VARCHAR(255) | — | Ad |
|
||||
| `last_name` | VARCHAR(255) | — | Soyad |
|
||||
| `created_at` | DATETIME | — | — |
|
||||
| `updated_at` | DATETIME | — | — |
|
||||
| `deleted_at` | DATETIME | NULL | Soft-delete |
|
||||
|
||||
---
|
||||
|
||||
### `settings`
|
||||
|
||||
Site genel ayarları.
|
||||
|
||||
Örnek alanlar:
|
||||
- `title`, `meta_title`, `meta_description`
|
||||
- `phone`, `url`, `email`
|
||||
- sosyal alanlar (`facebook`, `x`, `instagram`, ...)
|
||||
- logo/medya alanları (`w_logo`, `b_logo`, `w_width`, ...)
|
||||
- `is_active`
|
||||
|
||||
### `heroes`
|
||||
|
||||
Anasayfa hero/banner kayıtları:
|
||||
- `color`, `title`, `text1/text2/text4/text5`
|
||||
- `image`, `width`, `height`, `quality`, `format`
|
||||
- `is_active`
|
||||
|
||||
### `cors_whitelists` / `cors_blacklists`
|
||||
|
||||
Dynamic CORS politika tabloları:
|
||||
- whitelist: izinli origin
|
||||
- blacklist: engelli origin (öncelikli blok)
|
||||
|
||||
### `rate_limit_settings`
|
||||
|
||||
DB tabanlı rate-limit kuralları:
|
||||
- `name` (unique): ör. `api/v1/auth/login`, `api/v1/auth/register`, `api`
|
||||
- `max_requests`, `window_seconds`, `is_active`
|
||||
|
||||
### Shop tabloları
|
||||
|
||||
- `product_categories`
|
||||
- `product_tags`
|
||||
- `products`
|
||||
- join tabloları: `product_product_categories`, `product_product_tags`
|
||||
- `product_category_views`
|
||||
- `product_comments`
|
||||
- `carts`
|
||||
- `cart_items`
|
||||
|
||||
### Blog tabloları
|
||||
|
||||
- `categories`
|
||||
- `tags`
|
||||
- `posts`
|
||||
- join tabloları: `post_categories`, `post_tags`
|
||||
- `category_views`
|
||||
- `comments`
|
||||
|
||||
---
|
||||
|
||||
## İlişkiler
|
||||
|
||||
```
|
||||
users (1) ──────── (N) social_accounts
|
||||
users (1) ──────── (N) profiles
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AutoMigrate
|
||||
|
||||
`config.ConnectDB()` her başlatmada çalışır ve eksik tabloları / sütunları ekler.
|
||||
Mevcut sütunları silmez veya daraltmaz.
|
||||
|
||||
```go
|
||||
db.AutoMigrate(
|
||||
&accountModels.User{},
|
||||
&accountModels.SocialAccount{},
|
||||
&accountModels.Profile{},
|
||||
&settingsModels.Setting{},
|
||||
&settingsModels.Hero{},
|
||||
&settingsModels.CorsWhitelist{},
|
||||
&settingsModels.CorsBlacklist{},
|
||||
&settingsModels.RateLimitSetting{},
|
||||
&shopModels.ProductCategory{},
|
||||
&shopModels.ProductTag{},
|
||||
&shopModels.Product{},
|
||||
&shopModels.ProductCategoryView{},
|
||||
&shopModels.ProductComment{},
|
||||
&shopModels.Cart{},
|
||||
&shopModels.CartItem{},
|
||||
&blogModels.Category{},
|
||||
&blogModels.Tag{},
|
||||
&blogModels.Post{},
|
||||
&blogModels.CategoryView{},
|
||||
&blogModels.Comment{},
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notlar
|
||||
|
||||
- Tüm modellerde `gorm.Model` kullanıldığı için soft delete (`deleted_at`) aktiftir.
|
||||
- CORS ve rate-limit için başlangıç seed kayıtları uygulama açılışında yalnızca **yoksa** oluşturulur.
|
||||
Reference in New Issue
Block a user