mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-19 05:47:02 +00:00
Add driver profile stats API, nickname update editing, and security checks
This commit is contained in:
@@ -24,9 +24,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/x0gp/server/internal/auth"
|
||||
"github.com/x0gp/server/internal/clans"
|
||||
"github.com/x0gp/server/internal/config"
|
||||
"github.com/x0gp/server/internal/drivers"
|
||||
"github.com/x0gp/server/internal/lobby"
|
||||
"github.com/x0gp/server/internal/races"
|
||||
"github.com/x0gp/server/internal/transport"
|
||||
)
|
||||
|
||||
@@ -284,13 +287,102 @@ func driversListHandler(svc *drivers.Service) http.HandlerFunc {
|
||||
// @Router /api/drivers/{id} [put]
|
||||
// @Router /api/drivers/{id} [delete]
|
||||
// @Router /api/drivers/{id}/car [put]
|
||||
func driversByIDHandler(svc *drivers.Service, lobbySvc *lobby.Service) http.HandlerFunc {
|
||||
func driversByIDHandler(svc *drivers.Service, lobbySvc *lobby.Service, racesSvc *races.Service, cfg *config.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := strings.TrimPrefix(r.URL.Path, "/api/drivers/")
|
||||
if id == "" {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", "missing driver id")
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasSuffix(id, "/profile") {
|
||||
driverID := strings.TrimSuffix(id, "/profile")
|
||||
if driverID == "" {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", "missing driver id")
|
||||
return
|
||||
}
|
||||
if driverID == "me" {
|
||||
authedID, ok := auth.DriverIDFromContext(r.Context())
|
||||
if !ok {
|
||||
if cfg.DevMode {
|
||||
driverID = "driver-alice"
|
||||
} else {
|
||||
writeError(w, http.StatusUnauthorized, "unauthorized", "missing or invalid auth token")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
driverID = authedID
|
||||
}
|
||||
}
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
w.Header().Set("Allow", "GET")
|
||||
writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", r.Method)
|
||||
return
|
||||
}
|
||||
|
||||
drv, err := svc.Get(r.Context(), driverID)
|
||||
if err != nil {
|
||||
if errors.Is(err, drivers.ErrNotFound) {
|
||||
writeError(w, http.StatusNotFound, "not_found", "driver not found")
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, "internal", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
dbStats, dbLast, dbBest, err := racesSvc.GetDriverProfileStats(r.Context(), driverID)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "internal", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var lastSummary *transport.DriverRaceSummary
|
||||
if dbLast != nil {
|
||||
lastSummary = &transport.DriverRaceSummary{
|
||||
RaceID: dbLast.RaceID,
|
||||
RaceName: dbLast.RaceName,
|
||||
FinishedMs: dbLast.FinishedMs,
|
||||
Position: dbLast.Position,
|
||||
TotalTimeMs: dbLast.TotalTimeMs,
|
||||
BestLapMs: dbLast.BestLapMs,
|
||||
TrackID: dbLast.TrackID,
|
||||
TrackName: dbLast.TrackName,
|
||||
}
|
||||
}
|
||||
|
||||
var bestSummary *transport.DriverRaceSummary
|
||||
if dbBest != nil {
|
||||
bestSummary = &transport.DriverRaceSummary{
|
||||
RaceID: dbBest.RaceID,
|
||||
RaceName: dbBest.RaceName,
|
||||
FinishedMs: dbBest.FinishedMs,
|
||||
Position: dbBest.Position,
|
||||
TotalTimeMs: dbBest.TotalTimeMs,
|
||||
BestLapMs: dbBest.BestLapMs,
|
||||
TrackID: dbBest.TrackID,
|
||||
TrackName: dbBest.TrackName,
|
||||
}
|
||||
}
|
||||
|
||||
profile := transport.DriverProfileResponse{
|
||||
Driver: driverToWire(drv),
|
||||
Stats: transport.DriverStats{
|
||||
TotalRacesStarted: dbStats.TotalRacesStarted,
|
||||
TotalRacesFinished: dbStats.TotalRacesFinished,
|
||||
Wins: dbStats.Wins,
|
||||
Podiums: dbStats.Podiums,
|
||||
BestLapMs: dbStats.BestLapMs,
|
||||
TotalPlaytimeMs: dbStats.TotalPlaytimeMs,
|
||||
},
|
||||
LastRace: lastSummary,
|
||||
BestRace: bestSummary,
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, profile)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.HasSuffix(id, "/car") {
|
||||
driverID := strings.TrimSuffix(id, "/car")
|
||||
if driverID == "" {
|
||||
@@ -389,12 +481,21 @@ func driversByIDHandler(svc *drivers.Service, lobbySvc *lobby.Service) http.Hand
|
||||
}
|
||||
writeJSON(w, http.StatusOK, driverToWire(d))
|
||||
case http.MethodPut:
|
||||
if !cfg.DevMode {
|
||||
authedID, ok := auth.DriverIDFromContext(r.Context())
|
||||
if !ok || authedID != id {
|
||||
writeError(w, http.StatusForbidden, "forbidden", "cannot update another driver's profile")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var req transport.DriverUpdateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
|
||||
return
|
||||
}
|
||||
d, err := svc.Update(r.Context(), id, drivers.UpdateInput{
|
||||
Nickname: req.Nickname,
|
||||
Name: req.Name,
|
||||
AvatarURL: req.AvatarURL,
|
||||
ClanID: req.ClanID,
|
||||
@@ -404,11 +505,27 @@ func driversByIDHandler(svc *drivers.Service, lobbySvc *lobby.Service) http.Hand
|
||||
writeError(w, http.StatusNotFound, "not_found", "driver not found")
|
||||
return
|
||||
}
|
||||
if errors.Is(err, drivers.ErrAlreadyExists) {
|
||||
writeError(w, http.StatusConflict, "conflict", fmt.Sprintf("nickname %s is already taken", req.Nickname))
|
||||
return
|
||||
}
|
||||
if errors.Is(err, drivers.ErrInvalidInput) {
|
||||
writeError(w, http.StatusBadRequest, "bad_request", err.Error())
|
||||
return
|
||||
}
|
||||
writeError(w, http.StatusInternalServerError, "internal", err.Error())
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, driverToWire(d))
|
||||
case http.MethodDelete:
|
||||
if !cfg.DevMode {
|
||||
authedID, ok := auth.DriverIDFromContext(r.Context())
|
||||
if !ok || authedID != id {
|
||||
writeError(w, http.StatusForbidden, "forbidden", "cannot delete another driver's profile")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := svc.Delete(r.Context(), id); err != nil {
|
||||
if errors.Is(err, drivers.ErrNotFound) {
|
||||
writeError(w, http.StatusNotFound, "not_found", "driver not found")
|
||||
|
||||
Reference in New Issue
Block a user