Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
55bd20d
Feat: Add contest thumbnail upload API endpoint (#59)
Sunja-An Feb 8, 2026
a7eb7a3
Merge pull request #60 from FOR-GAMERS/feat/#59-contest-thumbnail-upload
Sunja-An Feb 8, 2026
84ff7f3
Fix: Handle Discord OAuth2 denial with redirect instead of 500 error …
Sunja-An Feb 9, 2026
e44dafb
Docs: Regenerate Swagger docs for Discord OAuth2 callback changes
Sunja-An Feb 9, 2026
102148d
Refactor: Use domain methods and proper error logging in contest appl…
Sunja-An Feb 16, 2026
dd9eba8
Test: Add unit and integration tests for Auth and Discord OAuth2 logi…
Sunja-An Feb 16, 2026
1a51111
Fix: Reflects Code Review
Sunja-An Feb 16, 2026
3517ef2
Fix: Add CSRF state validation in controller and fix appErr bug (#61)
Sunja-An Feb 16, 2026
0e41c31
Merge pull request #61 from FOR-GAMERS/fix/#9-discord-oauth2-deny-error
Sunja-An Feb 16, 2026
a1d0743
Feat/Refactor/Test/Chore: Valorant tier-based point calculation in co…
Sunja-An Feb 25, 2026
c7b68b5
Fix: Apply code review feedback on PR #69
Sunja-An Feb 26, 2026
0c027d1
Merge pull request #69 from FOR-GAMERS/feat/#64-valorant-point-contes…
Sunja-An Feb 26, 2026
263a9c7
Fix: Resolve flaky integration test caused by same-second JWT generation
Sunja-An Feb 26, 2026
cf9d149
Merge pull request #70 from FOR-GAMERS/feat/#63
Sunja-An Mar 1, 2026
58b0e37
Fix: Member point saved as 0 when contest application is accepted (#71)
Sunja-An Mar 19, 2026
d91b93c
Merge pull request #72 from GMS-developer/fix/#71
Sunja-An Mar 21, 2026
551eca9
feat: Add Valorant roles/description to contest application and membe…
Sunja-An Mar 21, 2026
a673d2a
Merge branch 'develop' into feat/#73
Sunja-An Mar 23, 2026
d6826e6
Merge pull request #74 from GMS-developer/feat/#73
Sunja-An Mar 23, 2026
dccda6b
feat: Grafana 모니터링 스택 연동 (#75)
Sunja-An Mar 23, 2026
ae9929e
fix: Apply PR #76 review feedback
Sunja-An Mar 23, 2026
91e474b
Merge pull request #76 from GMS-developer/feat/#75
Sunja-An Mar 23, 2026
1bb2289
feat: Cloudflare R2 → AWS S3 스토리지 어댑터 교체 (#78)
Sunja-An Mar 25, 2026
36b2d89
feat: AWS S3 → Google Cloud Storage(GCS) 스토리지 어댑터 교체 (#78)
Sunja-An Mar 25, 2026
14c322c
feat: Add pkg/lcu — Go client library for LCU (League Client Update) …
Sunja-An Mar 29, 2026
49811c3
fix: Address PR #80 code review — pkg/lcu robustness improvements
Sunja-An Mar 29, 2026
980bd24
fix: Address PR #80 second code review batch
Sunja-An Mar 29, 2026
512c5a1
Merge pull request #80 from GMS-developer/feat/#79
Sunja-An Mar 29, 2026
f6c86e6
fix: Resolve build errors in contest application service
Sunja-An Mar 29, 2026
045b15a
Merge pull request #81 from GMS-developer/fix/#79-build-errors
Sunja-An Mar 29, 2026
63ec280
feat: LoL Temporal Contest 팀 밸런싱 API 구현 (#82)
Sunja-An Mar 31, 2026
62d9f97
test: LoL Temporal Contest 단위 테스트 추가 (#82)
Sunja-An Mar 31, 2026
3f007b0
feat: LoL Temporal 포지션 선호도 기반 5:5 팀 매칭 알고리즘 구현 (#84)
Sunja-An Apr 2, 2026
c561a1d
refactor: contest 서비스 안정성 및 LoL 팀 밸런싱 성능 개선
Sunja-An Apr 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,20 @@ func main() {
// Storage module - provides R2 storage integration for images
storageDeps := storage.ProvideStorageDependencies(appRouter)

// Set storage port for contest thumbnail upload
if storageDeps != nil {
contestDeps.ContestService.SetStoragePort(storageDeps.StoragePort)
}

// Banner module - provides main banner management for homepage
bannerDeps := banner.ProvideBannerDependencies(db, appRouter)

// Notification module - provides SSE real-time notifications
notificationDeps := notification.ProvideNotificationDependencies(db, appRouter)

// Wire score table port for point calculation during contest application
contestDeps.ApplicationService.SetScoreTablePort(pointDeps.ScoreTableRepository)

// Wire notification handler to contest and game services
contestDeps.ApplicationService.SetNotificationHandler(notificationDeps.Service)
gameDeps.TeamService.SetNotificationHandler(notificationDeps.Service)
Expand All @@ -176,6 +184,7 @@ func startServer(engine interface{}) {
log.Println("===========================================")
log.Println("Server: http://localhost:8080")
log.Println("Health Check: http://localhost:8080/health")
log.Println("Metrics: http://localhost:8080/metrics")
log.Println("Swagger UI: http://localhost:8080/swagger/index.html")
log.Println("===========================================")

Expand Down Expand Up @@ -207,9 +216,11 @@ func setupRouter(
notificationDeps *notification.Dependencies,
) *router.Router {

appRouter.Engine().Use(middleware.PrometheusMetrics())
appRouter.Engine().Use(middleware.GlobalErrorHandler())

appRouter.RegisterHealthCheck()
appRouter.RegisterMetrics()
appRouter.RegisterSwagger(ginSwagger.WrapHandler(swaggerFiles.Handler))

authDeps.Controller.RegisterRoutes()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE contests_members
DROP COLUMN valorant_roles,
DROP COLUMN description;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE contests_members
ADD COLUMN valorant_roles JSON DEFAULT NULL AFTER point,
ADD COLUMN description VARCHAR(64) DEFAULT '' AFTER valorant_roles;
2 changes: 2 additions & 0 deletions docker/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=your_secure_password
38 changes: 38 additions & 0 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,44 @@ services:
networks:
- gamers-network

prometheus:
image: prom/prometheus:v2.53.1
container_name: gamers-prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.retention.time=15d"
ports:
- "9090:9090"
networks:
- gamers-network
restart: unless-stopped

grafana:
image: grafana/grafana:12.0.1
container_name: gamers-grafana
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/etc/grafana/dashboards:ro
- grafana-data:/var/lib/grafana
environment:
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
GF_USERS_ALLOW_SIGN_UP: "false"
ports:
- "3001:3000"
networks:
- gamers-network
depends_on:
- prometheus
restart: unless-stopped

volumes:
prometheus-data:
grafana-data:

networks:
gamers-network:
external: true
111 changes: 111 additions & 0 deletions docker/grafana/dashboards/gamers-api.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"title": "GAMERS API",
"uid": "gamers-api",
"schemaVersion": 39,
"version": 1,
"refresh": "10s",
"time": { "from": "now-1h", "to": "now" },
"panels": [
{
"id": 1,
"title": "RPS (Requests Per Second)",
"type": "timeseries",
"gridPos": { "x": 0, "y": 0, "w": 12, "h": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(http_requests_total[1m])) by (method, path)",
"legendFormat": "{{method}} {{path}}"
}
]
},
{
"id": 2,
"title": "Error Rate (5xx / total)",
"type": "timeseries",
"gridPos": { "x": 12, "y": 0, "w": 12, "h": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "sum(rate(http_requests_total{status=~\"5..\"}[1m])) / sum(rate(http_requests_total[1m]))",
"legendFormat": "error rate"
}
]
},
{
"id": 3,
"title": "Request Duration p50 / p95 / p99",
"type": "timeseries",
"gridPos": { "x": 0, "y": 8, "w": 12, "h": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[1m])) by (le))",
"legendFormat": "p50"
},
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[1m])) by (le))",
"legendFormat": "p95"
},
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[1m])) by (le))",
"legendFormat": "p99"
}
]
},
{
"id": 4,
"title": "In-Flight Requests",
"type": "timeseries",
"gridPos": { "x": 12, "y": 8, "w": 12, "h": 8 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "http_requests_in_flight",
"legendFormat": "in-flight"
}
]
},
{
"id": 5,
"title": "Goroutines",
"type": "timeseries",
"gridPos": { "x": 0, "y": 16, "w": 8, "h": 7 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "go_goroutines",
"legendFormat": "goroutines"
}
]
},
{
"id": 6,
"title": "Heap Alloc (MB)",
"type": "timeseries",
"gridPos": { "x": 8, "y": 16, "w": 8, "h": 7 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "go_memstats_heap_alloc_bytes / 1024 / 1024",
"legendFormat": "heap alloc MB"
}
]
},
{
"id": 7,
"title": "GC Pause Duration (p99, ms)",
"type": "timeseries",
"gridPos": { "x": 16, "y": 16, "w": 8, "h": 7 },
"targets": [
{
"datasource": { "type": "prometheus" },
"expr": "histogram_quantile(0.99, sum(rate(go_gc_duration_seconds_bucket[1m])) by (le)) * 1000",
"legendFormat": "gc p99 ms"
}
]
}
]
}
10 changes: 10 additions & 0 deletions docker/grafana/provisioning/dashboards/dashboards.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
apiVersion: 1

providers:
- name: GAMERS
folder: GAMERS
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/dashboards
9 changes: 9 additions & 0 deletions docker/grafana/provisioning/datasources/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: 1

datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
9 changes: 9 additions & 0 deletions docker/prometheus/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global:
scrape_interval: 15s
evaluation_interval: 15s

scrape_configs:
- job_name: "gamers-api"
static_configs:
- targets: ["app:8080"]
metrics_path: /metrics
Loading
Loading