package handlers import ( "net/http" "github.com/breakpilot/school-service/internal/middleware" "github.com/breakpilot/school-service/internal/models" "github.com/gin-gonic/gin" ) // ---------- Teacher-side (uses JWT/dev auth from existing middleware) ---------- func (h *Handler) InviteParent(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } var req models.InviteParentRequest if err := c.ShouldBindJSON(&req); err != nil { respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error()) return } out, err := h.parentService.InviteParent(c.Request.Context(), uid, &req) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to invite parent: "+err.Error()) return } respondCreated(c, out) } func (h *Handler) ListParentInvites(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } items, err := h.parentService.ListInvites(c.Request.Context(), uid) if err != nil { respondError(c, http.StatusInternalServerError, "Failed to list invites: "+err.Error()) return } if items == nil { items = []models.ParentInviteListItem{} } respondSuccess(c, items) } func (h *Handler) DeleteParentInvite(c *gin.Context) { uid := getUserID(c) if uid == "" { respondError(c, http.StatusUnauthorized, "User not authenticated") return } if err := h.parentService.DeleteInvite(c.Request.Context(), c.Param("id"), uid); err != nil { respondError(c, http.StatusInternalServerError, "Failed to delete invite: "+err.Error()) return } c.JSON(http.StatusOK, gin.H{"message": "Invite removed"}) } // ---------- Parent-side (uses ParentSessionMiddleware) ---------- func (h *Handler) RedeemMagicLink(c *gin.Context) { var req models.RedeemMagicLinkRequest if err := c.ShouldBindJSON(&req); err != nil { respondError(c, http.StatusBadRequest, "Invalid request: "+err.Error()) return } session, parent, err := h.parentService.RedeemMagicLink(c.Request.Context(), req.Token) if err != nil { respondError(c, http.StatusUnauthorized, err.Error()) return } // HttpOnly + Lax → cookie survives a fresh redirect from /eltern/login but // isn't sent on cross-site CSRF requests. c.SetSameSite(http.SameSiteLaxMode) c.SetCookie(middleware.ParentSessionCookieName, session, 60*60*24*30, "/", "", false, true) respondSuccess(c, parent) } func (h *Handler) ParentMe(c *gin.Context) { parentID := c.GetString("parent_id") children, err := h.parentService.ListChildren(c.Request.Context(), parentID) if err != nil { respondError(c, http.StatusInternalServerError, err.Error()) return } if children == nil { children = []models.ParentChild{} } respondSuccess(c, gin.H{ "parent": gin.H{ "id": parentID, "email": c.GetString("parent_email"), "preferred_language": c.GetString("parent_language"), }, "children": children, }) } // ParentTimetable returns the latest completed timetable lessons for the // given child's class. Authorization: parent must own a child in that class. func (h *Handler) ParentTimetable(c *gin.Context) { parentID := c.GetString("parent_id") classID := c.Query("class_id") if classID == "" { respondError(c, http.StatusBadRequest, "class_id required") return } ok, err := h.parentService.ChildBelongsToParent(c.Request.Context(), parentID, classID) if err != nil { respondError(c, http.StatusInternalServerError, err.Error()) return } if !ok { respondError(c, http.StatusForbidden, "Not allowed") return } // Need the teacher's user_id to find the right solution. We re-derive it // from parent_account.created_by_user_id via a small extra query. teacherID, err := h.parentService.TeacherOfParent(c.Request.Context(), parentID) if err != nil { respondError(c, http.StatusInternalServerError, err.Error()) return } lessons, err := h.parentService.LatestCompletedSolutionLessonsForClass(c.Request.Context(), classID, teacherID) if err != nil { respondError(c, http.StatusInternalServerError, err.Error()) return } respondSuccess(c, lessons) } func (h *Handler) ParentLogout(c *gin.Context) { c.SetSameSite(http.SameSiteLaxMode) c.SetCookie(middleware.ParentSessionCookieName, "", -1, "/", "", false, true) c.JSON(http.StatusOK, gin.H{"message": "Logged out"}) }