Expose learning, registration controls, mentor chat, realtime WebSocket, and staff administration.
92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"pargar/backend/internal/models"
|
|
"pargar/backend/internal/service"
|
|
)
|
|
|
|
type ctxKey int
|
|
|
|
const (
|
|
ctxUser ctxKey = iota
|
|
ctxMeta
|
|
)
|
|
|
|
// requestMeta is shared across middleware and auth so access logs can include user_id.
|
|
type requestMeta struct {
|
|
UserID int64
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
_ = json.NewEncoder(w).Encode(v)
|
|
}
|
|
|
|
// requireAuth resolves the caller from the Bearer token and stores the user in context.
|
|
func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
u, err := s.Authenticate(r)
|
|
if err != nil {
|
|
writeErrCode(w, http.StatusUnauthorized, CodeUnauthorized, err.Error())
|
|
return
|
|
}
|
|
if meta, ok := r.Context().Value(ctxMeta).(*requestMeta); ok && meta != nil {
|
|
meta.UserID = u.ID
|
|
}
|
|
ctx := context.WithValue(r.Context(), ctxUser, u)
|
|
next(w, r.WithContext(ctx))
|
|
}
|
|
}
|
|
|
|
// requireRole wraps requireAuth and enforces a minimum role.
|
|
func (s *Server) requireRole(roles ...models.Role) func(http.HandlerFunc) http.HandlerFunc {
|
|
return func(next http.HandlerFunc) http.HandlerFunc {
|
|
return s.requireAuth(func(w http.ResponseWriter, r *http.Request) {
|
|
u := currentUser(r)
|
|
if u == nil {
|
|
writeErr(w, http.StatusUnauthorized, "نیاز به ورود دارید")
|
|
return
|
|
}
|
|
allowed := false
|
|
for _, role := range roles {
|
|
if u.Role == role {
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
if !allowed {
|
|
writeErr(w, http.StatusForbidden, "دسترسی کافی ندارید")
|
|
return
|
|
}
|
|
next(w, r)
|
|
})
|
|
}
|
|
}
|
|
|
|
func currentUser(r *http.Request) *models.User {
|
|
u, _ := r.Context().Value(ctxUser).(*models.User)
|
|
return u
|
|
}
|
|
|
|
func (s *Server) lockedEnforce(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
u := currentUser(r)
|
|
if u != nil && u.IsLocked && !u.Role.IsStaff() {
|
|
writeErrCode(w, http.StatusLocked, CodeLocked, service.ErrLocked.Error())
|
|
return
|
|
}
|
|
next(w, r)
|
|
}
|
|
}
|
|
|
|
// bodyJSON decodes a JSON request body into v.
|
|
func bodyJSON(r *http.Request, v any) error {
|
|
dec := json.NewDecoder(r.Body)
|
|
return dec.Decode(v)
|
|
}
|