Skip to content

Repository files navigation

GDG Bursa Logo

GDG Bursa — Backend API

gdgbursa.com için geliştirilmiş, production-ready RESTful backend servisi.

Go Gin PostgreSQL Redis Docker License


İçindekiler


Genel Bakış

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.

Temel Özellikler

  • 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ı

Mimari

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 Yığını

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ı

Kurulum

Ön Gereksinimler

  • Go 1.25+
  • PostgreSQL 15+
  • Redis 7+
  • (Opsiyonel) Docker & Docker Compose

1. Standart Kurulum

# 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

2. Docker ile Kurulum (Önerilen)

# 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 -v

Docker Compose servisleri:

Servis İmaj Port
app Multi-stage build → distroless/static-debian12:nonroot 2025
db postgres:15-alpine 5432
redis redis:7-alpine 6379

3. Hot-Reload ile Geliştirme

Projede Air yapılandırması (.air.toml) mevcuttur:

air

Konfigürasyon

Sistem, yapısal ayarları conf.yaml dosyasından, hassas verileri ise .env dosyasından okur.

.env dosyası asla Git'e commit edilmemelidir. .gitignore içinde zaten tanımlıdır.

conf.yaml

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: 1m

.env Örneği

dsn="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"

API Referansı

Base URL: http://localhost:2025

Kimlik Doğrulama

Sistem JWT token kullanır. Token, Auth isimli HttpOnly cookie'ye yazılır. Postman üzerinden test ederken Authorization header'ı da kullanılabilir.

Giriş Yap

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

Çıkış Yap

POST /api/auth/logout

Etkinlik Kategorileri

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"
}

Etkinlikler

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"
}

Etkinlik Fotoğrafları

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
}

Sıkça Sorulan Sorular (FAQ)

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."
}

Sponsorlar

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
}

İstatistikler

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": "+"
}

Takım Üyeleri

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"
}

Duyurular

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
}

Kullanıcı Yönetimi

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 & Gözlemleme

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 }
]

Middleware Zinciri

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

Redis Cache Stratejisi

Ö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

Rate Limiting

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

Logging Stratejisi

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.


Veritabanı Şeması

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:

  • EventCategoryEvent: One-to-Many (CASCADE delete)
  • EventEventPhoto: One-to-Many (CASCADE delete)

Graceful Shutdown

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.yamlserver.shutdown_timeout ile ayarlanır.


HTTP Status Kodları

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ı

İletişim

Sorularınız veya bulduğunuz hatalar için:


Lisans

Bu proje MIT Lisansı ile lisanslanmıştır.

MIT License — Copyright (c) 2025 poizdev

About

GDG Bursa resmi web sitesidir

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages