gdgbursa.com için geliştirilmiş, production-ready RESTful backend servisi.
- Genel Bakış
- Mimari
- Teknoloji Yığını
- Kurulum
- Konfigürasyon
- API Referansı
- Middleware Zinciri
- Veritabanı Şeması
- Lisans
Bu sistem, GDG Bursa'nın resmi web sitesi için geliştirilmiş backend servisidir. Etkinliklerin, takım üyelerinin, sponsorların, istatistiklerin ve duyuruların yönetildiği; aynı zamanda trafik loglama, Redis caching, rate limiting ve audit trail özellikleriyle donatılmış, ölçeklenebilir bir mimari sunar.
- JWT tabanlı kimlik doğrulama — HttpOnly Cookie ile güvenli oturum yönetimi
- Redis caching — Tüm public GET istekleri 24 saat TTL ile önbelleklenir
- Rate limiting — IP bazlı Token Bucket algoritması ile DDoS koruması
- Traffic & Audit logging — Asenkron trafik izleme ve admin işlem denetimi
- Graceful shutdown — Açık isteklerin güvenli tamamlanması (30s timeout)
- Distroless Docker — Minimum saldırı yüzeyi ile production imajı
gdgbursacom/
├── main.go # Giriş noktası, router tanımları, graceful shutdown
├── conf.yaml # Uygulama konfigürasyonu (port, DB pool, Redis, JWT, rate limit)
├── .env # Gizli veriler (DSN, JWT_SECRET) — .gitignore'da
│
├── config/
│ ├── config.go # YAML config struct'ları ve LoadConfig()
│ └── logger.go # Zap logger (dev: console+file, prod: JSON file)
│
├── db/
│ ├── connect.go # PostgreSQL bağlantısı + connection pooling
│ ├── redis.go # Redis bağlantısı + pool ayarları
│ ├── migrate.go # GORM AutoMigrate (tüm modeller)
│ └── seed.go # Varsayılan admin kullanıcısı oluşturma
│
├── models/ # GORM modelleri (veritabanı şeması)
│ ├── events.go # EventCategory → Event → EventPhoto (cascade)
│ ├── announcement.go # Duyuru modeli
│ ├── faq.go # SSS modeli
│ ├── sponsors.go # Sponsor modeli
│ ├── stats.go # İstatistik modeli
│ ├── teams.go # Takım üyesi modeli
│ ├── user.go # Admin kullanıcı modeli
│ ├── traffic.go # Trafik logu modeli
│ └── audit.go # Audit logu modeli
│
├── controllers/ # HTTP handler'lar (request → response)
│ ├── authcontroller.go
│ ├── eventcontroller.go
│ ├── announcementcontroller.go
│ ├── faqcontroller.go
│ ├── sponsorcontroller.go
│ ├── statscontroller.go
│ ├── teamcontroller.go
│ ├── usercontroller.go
│ └── trafficcontroller.go
│
├── services/ # Business logic katmanı
│ ├── authservice.go
│ ├── eventservice.go
│ ├── announcementservice.go
│ ├── faqservices.go
│ ├── sponsorservice.go
│ ├── stats.go
│ ├── teamservice.go
│ ├── userservice.go
│ └── trafficservice.go
│
├── middlewares/ # Gin middleware zinciri
│ ├── requestid.go # Her isteğe UUID ataması
│ ├── logger.go # İstek loglama (Zap)
│ ├── traffic.go # Asenkron trafik logu (DB'ye yazar)
│ ├── ratelimit.go # IP bazlı Token Bucket rate limiter
│ ├── cache.go # Redis GET cache (24h TTL, auto-invalidation)
│ ├── auth.go # JWT token doğrulama
│ └── audit.go # Admin işlem denetim logu
│
├── Dockerfile # Multi-stage build → Distroless production imajı
├── compose.yaml # PostgreSQL + Redis + API orchestration
├── .air.toml # Hot-reload (geliştirme ortamı)
└── go.mod # Go modül tanımları
| Teknoloji | Versiyon | Rol |
|---|---|---|
| Go | 1.25 | Ana programlama dili |
| Gin | 1.12 | HTTP framework |
| GORM | 1.31 | ORM (AutoMigrate, cascade relations) |
| PostgreSQL | 15+ Alpine | İlişkisel veritabanı |
| Redis | 7 Alpine | Cache, rate limiting state |
| Zap | 1.28 | Yapılandırılmış loglama (JSON / Console) |
| Lumberjack | 2.2 | Log dosyası rotasyonu |
| golang-jwt | 5.3 | JWT token üretimi ve doğrulaması |
| bcrypt | — | Şifre hash'leme |
| Docker | Distroless | Minimum saldırı yüzeyi production imajı |
- Go 1.25+
- PostgreSQL 15+
- Redis 7+
- (Opsiyonel) Docker & Docker Compose
# Repoyu klonla
git clone https://github.com/poizdev/gdgbursa.com.git
cd gdgbursa.com
# Bağımlılıkları indir
go mod download
# Konfigürasyon dosyalarını oluştur (aşağıdaki bölüme bakınız)
# conf.yaml ve .env dosyalarını hazırla
# Uygulamayı başlat
go run main.go# Tüm sistemi (API + PostgreSQL + Redis) başlat
docker compose up -d
# Logları takip et
docker compose logs -f app
# Sistemi durdur
docker compose down
# Veritabanı verilerini de sil (temiz başlangıç)
docker compose down -vDocker Compose servisleri:
| Servis | İmaj | Port |
|---|---|---|
app |
Multi-stage build → distroless/static-debian12:nonroot |
2025 |
db |
postgres:15-alpine |
5432 |
redis |
redis:7-alpine |
6379 |
Projede Air yapılandırması (.air.toml) mevcuttur:
airSistem, yapısal ayarları conf.yaml dosyasından, hassas verileri ise .env dosyasından okur.
.envdosyası asla Git'e commit edilmemelidir..gitignoreiçinde zaten tanımlıdır.
server:
port: ":2025"
shutdown_timeout: 30s
active_level: "dev" # "dev" veya "prod"
env_path: ".env"
database:
max_idle_conns: 25
max_open_conns: 45
conn_max_lifetime: 1m
conn_max_idle_time: 45s
redis:
host: "localhost"
port: 6379
db: 0
pool_size: 10
min_idle_conns: 3
jwt:
expiration: 24h
rate_limit:
requests_per_minute: 60
burst_size: 10
window_duration: 1mdsn="host=localhost user=postgres password=sifreniz dbname=gdgbursa port=5432 sslmode=disable TimeZone=Europe/Istanbul"
JWT_SECRET="cok-gizli-jwt-anahtari"
# Opsiyonel: Docker ortamında Redis adresi ve şifresi
REDIS_ADDR="redis:6379"
REDIS_PASSWORD=""
# Opsiyonel: Varsayılan admin bilgileri (seed sırasında kullanılır)
DEFAULT_ADMIN_USER="admin"
DEFAULT_ADMIN_PASS="admin123"Base URL: http://localhost:2025
Sistem JWT token kullanır. Token, Auth isimli HttpOnly cookie'ye yazılır. Postman üzerinden test ederken Authorization header'ı da kullanılabilir.
POST /api/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "sifre123"
}Başarılı yanıt: 200 OK
{ "message": "Login successful" }Set-Cookie: Auth=<jwt-token>; Path=/; HttpOnly; SameSite=None
POST /api/auth/logout| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/event-categories |
Public | Tüm kategorileri listele |
GET |
/api/event-categories/:id |
Public | Kategori detayı |
POST |
/api/event-categories |
Admin | Yeni kategori oluştur |
PATCH |
/api/event-categories/:id |
Admin | Kategoriyi güncelle |
DELETE |
/api/event-categories/:id |
Admin | Kategoriyi sil |
Örnek İstek/Yanıtlar
GET /api/event-categories
[
{
"category_id": 1,
"category_name": "DevFest",
"cover_image": "/public/categories/devfest.png",
"events": null
}
]POST /api/event-categories
{
"category_name": "WTM",
"cover_image": "/public/categories/wtm.png"
}PATCH /api/event-categories/:id
{
"category_name": "Women Techmakers"
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/events/category/:categoryId |
Public | Kategoriye göre etkinlikleri listele |
GET |
/api/events/:id |
Public | Etkinlik detayı |
POST |
/api/events |
Admin | Yeni etkinlik oluştur |
PATCH |
/api/events/:id |
Admin | Etkinliği güncelle |
DELETE |
/api/events/:id |
Admin | Etkinliği sil |
Örnek İstek/Yanıtlar
GET /api/events/category/1
[
{
"event_id": 1,
"category_id": 1,
"event_year": 2024,
"event_name": "DevFest Bursa 2024",
"event_desc": "Bursa'nın en büyük yazılım etkinliği",
"event_date": "2024-12-08T00:00:00Z",
"photos": null
}
]POST /api/events
{
"category_id": 1,
"event_year": 2024,
"event_name": "DevFest Bursa 2024",
"event_desc": "Bursa'nın en büyük etkinliği",
"event_date": "2024-12-08T00:00:00Z"
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/photos/event/:eventId |
Public | Etkinliğe ait fotoğrafları getir |
POST |
/api/photos |
Admin | Fotoğraf ekle |
PATCH |
/api/photos/:id |
Admin | Fotoğrafı güncelle |
DELETE |
/api/photos/:id |
Admin | Fotoğrafı sil |
Örnek İstek/Yanıtlar
GET /api/photos/event/1
[
{
"photo_id": 1,
"event_id": 1,
"photo_url": "/public/events/1/photo1.jpg",
"is_cover": true
}
]POST /api/photos
{
"event_id": 1,
"photo_url": "/public/events/1/photo2.jpg",
"is_cover": false
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/faqs |
Public | Tüm SSS listesi |
GET |
/api/faqs/:id |
Public | SSS detayı |
POST |
/api/faqs |
Admin | Yeni soru ekle |
PATCH |
/api/faqs/:id |
Admin | Soruyu güncelle |
DELETE |
/api/faqs/:id |
Admin | Soruyu sil |
Örnek İstek/Yanıtlar
GET /api/faqs
[
{
"faq_id": 1,
"question": "Etkinlik ücretli mi?",
"answer": "Hayır, tüm etkinliklerimiz tamamen ücretsizdir."
}
]POST /api/faqs
{
"question": "Sertifika verilecek mi?",
"answer": "Evet, katılım sağlayan herkese dijital sertifika iletilecektir."
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/sponsors |
Public | Tüm sponsorları listele |
GET |
/api/sponsors/active |
Public | Sadece aktif sponsorlar |
GET |
/api/sponsors/:id |
Public | Sponsor detayı |
POST |
/api/sponsors |
Admin | Sponsor ekle |
PATCH |
/api/sponsors/:id |
Admin | Sponsoru güncelle |
DELETE |
/api/sponsors/:id |
Admin | Sponsoru sil |
Örnek İstek/Yanıtlar
GET /api/sponsors
[
{
"sponsor_id": 1,
"name": "Google",
"website_url": "https://google.com",
"logo_url": "/public/sponsors/google.png",
"order": 1,
"is_active": true
}
]POST /api/sponsors
{
"name": "JetBrains",
"website_url": "https://jetbrains.com",
"logo_url": "/public/sponsors/jetbrains.png",
"order": 2,
"is_active": true
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/stats |
Public | Tüm istatistikleri listele |
GET |
/api/stats/:id |
Public | İstatistik detayı |
POST |
/api/stats |
Admin | İstatistik ekle |
PATCH |
/api/stats/:id |
Admin | İstatistiği güncelle |
DELETE |
/api/stats/:id |
Admin | İstatistiği sil |
Örnek İstek/Yanıtlar
GET /api/stats
[
{
"stats_id": 1,
"target_number": 5000,
"label": "Katılımcı",
"prefix": "+"
}
]POST /api/stats
{
"target_number": 20,
"label": "Konuşmacı",
"prefix": "+"
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/team |
Public | Tüm üyeleri listele |
GET |
/api/team/:id |
Public | Üye detayı |
POST |
/api/team |
Admin | Üye ekle |
PATCH |
/api/team/:id |
Admin | Üyeyi güncelle |
DELETE |
/api/team/:id |
Admin | Üyeyi sil |
Örnek İstek/Yanıtlar
GET /api/team
[
{
"id": 1,
"member_name": "Ahmet Yılmaz",
"member_photo": "/public/team/ahmet.jpg",
"ig_account": "ahmety",
"linkedin_account": "ahmetyilmaz"
}
]POST /api/team
{
"member_name": "Ayşe Kaya",
"member_photo": "/public/team/ayse.jpg",
"ig_account": "aysek",
"linkedin_account": "aysekaya"
}| Metod | Endpoint | Yetki | Açıklama |
|---|---|---|---|
GET |
/api/announcements |
Public | Tüm duyuruları listele |
GET |
/api/announcements/active |
Public | Sadece aktif duyurular |
GET |
/api/announcements/:id |
Public | Duyuru detayı |
POST |
/api/announcements |
Admin | Duyuru oluştur |
PATCH |
/api/announcements/:id |
Admin | Duyuruyu güncelle |
DELETE |
/api/announcements/:id |
Admin | Duyuruyu sil |
Örnek İstek/Yanıtlar
GET /api/announcements
[
{
"announcement_id": 1,
"title": "Konuşmacı Çağrısı",
"subtitle": "DevFest 2026",
"short_description": "Başvurular Başladı",
"long_description": "Konuşmacı başvurularınızı bekliyoruz.",
"link": "https://gdgbursa.com/cfs",
"button_text": "Hemen Başvur",
"icon": "mic",
"is_active": true
}
]POST /api/announcements
{
"title": "Konuşmacı Çağrısı",
"subtitle": "DevFest 2026",
"short_description": "Başvurular Başladı",
"long_description": "Konuşmacı başvurularınızı bekliyoruz.",
"link": "https://gdgbursa.com/cfs",
"button_text": "Hemen Başvur",
"icon": "mic",
"is_active": true
}Tüm kullanıcı endpoint'leri JWT token gerektirir.
| Metod | Endpoint | Açıklama |
|---|---|---|
GET |
/api/users |
Tüm kullanıcıları listele |
GET |
/api/users/:id |
Kullanıcı detayı |
POST |
/api/users |
Kullanıcı oluştur |
PATCH |
/api/users/:id |
Kullanıcıyı güncelle |
DELETE |
/api/users/:id |
Kullanıcıyı sil |
Örnek İstek/Yanıtlar
GET /api/users
[
{
"user_id": 1,
"username": "admin",
"role": "admin",
"created_at": "2026-05-17T20:00:00+03:00",
"updated_at": "2026-05-17T20:00:00+03:00"
}
]POST /api/users
{
"username": "yeniadmin",
"password": "sifre123",
"role": "admin"
}Trafik endpoint'leri JWT token gerektirir.
| Metod | Endpoint | Açıklama |
|---|---|---|
GET |
/api/traffic/summary |
Genel trafik özeti |
GET |
/api/traffic/top-endpoints |
En çok ziyaret edilen endpoint'ler |
Örnek Yanıtlar
GET /api/traffic/summary
{
"total_requests": 15420,
"unique_ips": 350,
"avg_latency_ms": 45
}GET /api/traffic/top-endpoints
[
{ "path": "/api/events", "count": 5200 },
{ "path": "/api/sponsors/active", "count": 3150 }
]Her HTTP isteği aşağıdaki middleware zincirinden sırayla geçer:
Client (Tarayıcı / Postman / Mobil)
│
▼
┌─────────────────────────────────────────────┐
│ 1. CORS Middleware │
│ ✓ Origin kontrolü & preflight │
├─────────────────────────────────────────────┤
│ 2. RequestID Middleware │
│ ✓ Her isteğe UUID ataması │
├─────────────────────────────────────────────┤
│ 3. Request Logger Middleware │
│ ✓ Zap ile istek log kaydı │
├─────────────────────────────────────────────┤
│ 4. Traffic Middleware │
│ ✓ IP, User-Agent, path → DB (async) │
├─────────────────────────────────────────────┤
│ 5. Rate Limiter │
│ ✓ IP bazlı 60 req/dk + 10 burst │
│ ✗ Limit aşıldıysa → 429 Too Many Req │
├─────────────────────────────────────────────┤
│ 6. Cache Middleware (yalnızca GET) │
│ ✓ Redis HIT → anında dön (~1-2ms) │
│ ✗ MISS → handler'a devam et │
├─────────────────────────────────────────────┤
│ 7. Auth Middleware (yalnızca /api admin) │
│ ✓ JWT token doğrulama │
│ ✗ Geçersiz token → 401 Unauthorized │
├─────────────────────────────────────────────┤
│ 8. Audit Middleware (yalnızca admin) │
│ ✓ POST/PATCH/DELETE işlemlerini logla │
├─────────────────────────────────────────────┤
│ 9. Handler (Controller → Service → DB) │
│ ✓ Business logic çalışır │
│ ✓ GORM ile veritabanı sorgusu │
├─────────────────────────────────────────────┤
│ 10. Response │
│ ✓ JSON yanıt + Zap latency log │
│ ✓ Cache MISS ise → Redis'e yaz │
└─────────────────────────────────────────────┘
│
▼
Client
| Özellik | Değer |
|---|---|
| TTL | 24 saat (veriler nadiren değişir) |
| Key Prefix | gdg:cache: |
| Key Format | gdg:cache:/api/events?categoryId=1 |
| Cache Header | X-Cache: HIT veya X-Cache: MISS |
| Invalidation | Admin mutasyon (POST/PATCH/DELETE) sonrası ilgili prefix'ler temizlenir |
| Parametre | Değer |
|---|---|
| Algoritma | Token Bucket |
| Limit | 60 istek / dakika (IP başına) |
| Burst | 10 anlık fazla istek hakkı |
| Pencere | 1 dakika |
| Aşım Yanıtı | 429 Too Many Requests |
| Ortam | Çıktı | Format | Level | Dosya |
|---|---|---|---|---|
dev |
Console + dosya | Console (renkli) | Debug | logs/debug.log |
prod |
Yalnızca dosya | JSON (structured) | Info | logs/prod.log |
Log dosyaları Lumberjack ile yönetilir: maks. 10MB, 5 yedek, 28 gün tutma, sıkıştırma açık.
GORM AutoMigrate ile otomatik oluşturulan tablolar:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ EventCategory │ 1───N │ Event │ 1───N │ EventPhoto │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ category_id (PK) │ │ event_id (PK) │ │ photo_id (PK) │
│ category_name │ │ category_id (FK) │ │ event_id (FK) │
│ cover_image │ │ event_year │ │ photo_url │
└──────────────────┘ │ event_name │ │ is_cover │
│ event_desc │ └──────────────────┘
│ event_date │
└──────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Sponsors │ │ Stats │ │ FAQ │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ sponsor_id (PK) │ │ stats_id (PK) │ │ faq_id (PK) │
│ name │ │ target_number │ │ question │
│ website_url │ │ label │ │ answer │
│ logo_url │ │ prefix │ └──────────────────┘
│ order │ └──────────────────┘
│ is_active │
└──────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ TeamMember │ │ User │ │ Announcement │
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
│ id (PK) │ │ user_id (PK) │ │ announcement_id │
│ member_name │ │ username │ │ title │
│ member_photo │ │ password (hash) │ │ subtitle │
│ ig_account │ │ role │ │ short_description│
│ linkedin_account │ │ created_at │ │ long_description │
└──────────────────┘ │ updated_at │ │ link │
└──────────────────┘ │ button_text │
│ icon │
│ is_active │
└──────────────────┘
┌──────────────────────┐ ┌──────────────────────┐
│ TrafficLog │ │ AuditLog │
├──────────────────────┤ ├──────────────────────┤
│ id (PK) │ │ id (PK) │
│ path │ │ user_id │
│ method │ │ username │
│ status_code │ │ method │
│ ip_address │ │ path │
│ user_agent │ │ request_body │
│ latency │ │ status_code │
│ request_id │ │ ip_address │
│ created_at │ │ request_id │
└──────────────────────┘ │ created_at │
└──────────────────────┘
İlişkiler:
EventCategory→Event: One-to-Many (CASCADE delete)Event→EventPhoto: One-to-Many (CASCADE delete)
Sunucu kapatılırken (SIGTERM/SIGINT) açık isteklerin güvenle tamamlanması sağlanır:
t=0s SIGTERM / SIGINT alındı
→ Yeni istekler reddedilir
t=0-30s Mevcut istekler işlenmeye devam eder
→ DB sorguları tamamlanır
→ Response'lar gönderilir
t=30s Shutdown timeout doldu
→ Sistem güvenle kapatılır
Timeout süresi conf.yaml → server.shutdown_timeout ile ayarlanır.
| Kod | İsim | Açıklama |
|---|---|---|
200 |
OK | İstek başarılı (GET, PATCH) |
201 |
Created | Yeni kayıt oluşturuldu (POST) |
400 |
Bad Request | JSON formatı hatalı veya eksik veri |
401 |
Unauthorized | Token eksik, geçersiz veya süresi dolmuş |
403 |
Forbidden | Token geçerli ama yetki yetersiz |
404 |
Not Found | Kayıt veya endpoint bulunamadı |
429 |
Too Many Requests | Rate limit aşıldı |
500 |
Internal Server Error | Sunucu hatası |
Sorularınız veya bulduğunuz hatalar için:
- GitHub Issues: github.com/poizdev/gdgbursa.com
- E-posta: musa@gdgbursa.com
Bu proje MIT Lisansı ile lisanslanmıştır.
MIT License — Copyright (c) 2025 poizdev
