package qldt import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strconv" "strings" "time" ) type Client struct { baseURL string origin string http *http.Client } func NewClient(baseURL, origin string) *Client { baseURL = strings.TrimRight(baseURL, "/") return &Client{ baseURL: baseURL, origin: origin, http: &http.Client{ Timeout: 45 * time.Second, }, } } type StudentsResponse struct { Message string `json:"message"` StatusCode int `json:"statusCode"` Data []StudentDTO `json:"data"` Total int `json:"total"` Page string `json:"page"` PageSize string `json:"pageSize"` } type StudentDTO struct { ID int64 `json:"id"` StudentCode string `json:"studentCode"` FullName string `json:"fullName"` Phone *string `json:"phone"` Email string `json:"email"` DateOfBirth *string `json:"dateOfBirth"` Gender *int `json:"gender"` Status *string `json:"status"` Location *string `json:"location"` Avatar *string `json:"avatar"` System *struct { ID int64 `json:"id"` Name string `json:"name"` } `json:"system"` } type ClassesResponse struct { Data []ClassDTO `json:"data"` StatusCode int `json:"statusCode"` } type ClassDTO struct { ID int64 `json:"id"` Name string `json:"name"` ClassCode string `json:"classCode"` Type string `json:"type"` StudentCount int `json:"studentCount"` CreatedAt string `json:"createdAt"` UpdatedAt *string `json:"updatedAt"` Specializes *struct { ID int64 `json:"id"` Name string `json:"name"` Systems *struct { ID int64 `json:"id"` SystemCode string `json:"systemCode"` Name string `json:"name"` } `json:"systems"` } `json:"specializes"` Courses []classCourseNestDTO `json:"courses"` } type classCourseNestDTO struct { ID int64 `json:"id"` Name string `json:"name"` } func (c *Client) GetStudents(ctx context.Context, token string, page, pageSize int) (*StudentsResponse, error) { u, err := url.Parse(c.baseURL + "/students") if err != nil { return nil, err } q := u.Query() q.Set("page", strconv.Itoa(page)) q.Set("pageSize", strconv.Itoa(pageSize)) u.RawQuery = q.Encode() req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) if c.origin != "" { req.Header.Set("Origin", c.origin) req.Header.Set("Referer", c.origin+"/") } req.Header.Set("Accept", "application/json") resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("qldt students http %d: %s", resp.StatusCode, string(body)) } var out StudentsResponse if err := json.Unmarshal(body, &out); err != nil { return nil, err } if out.StatusCode != 0 && out.StatusCode != 200 { return nil, fmt.Errorf("qldt statusCode %d: %s", out.StatusCode, out.Message) } return &out, nil } func (c *Client) GetClassesBySystem(ctx context.Context, token string, systemID int64) (*ClassesResponse, error) { u := fmt.Sprintf("%s/classes/system/%d", c.baseURL, systemID) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) if c.origin != "" { req.Header.Set("Origin", c.origin) req.Header.Set("Referer", c.origin+"/") } req.Header.Set("Accept", "application/json") resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("qldt classes/system/%d http %d: %s", systemID, resp.StatusCode, string(body)) } var outClasses ClassesResponse if err := json.Unmarshal(body, &outClasses); err != nil { return nil, err } if outClasses.StatusCode != 0 && outClasses.StatusCode != 200 { return nil, fmt.Errorf("qldt classes statusCode %d", outClasses.StatusCode) } return &outClasses, nil } func (c *Client) GetClassDashboard(ctx context.Context, token string, classID, courseID int64) ([]byte, error) { u := fmt.Sprintf("%s/classes/dashboard/%d/%d", c.baseURL, classID, courseID) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) if c.origin != "" { req.Header.Set("Origin", c.origin) req.Header.Set("Referer", c.origin+"/") } req.Header.Set("Accept", "application/json") resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("qldt classes/dashboard/%d/%d http %d: %s", classID, courseID, resp.StatusCode, string(body)) } var probe struct { StatusCode int `json:"statusCode"` Message string `json:"message"` } if err := json.Unmarshal(body, &probe); err == nil { if probe.StatusCode != 0 && probe.StatusCode != 200 { return nil, fmt.Errorf("qldt classes/dashboard statusCode %d: %s", probe.StatusCode, probe.Message) } } return body, nil } func (c *Client) GetLeaveRequests(ctx context.Context, token string, classID, courseID int64, date string) ([]byte, error) { u := fmt.Sprintf("%s/request-leave?classId=%d&courseId=%d&date=%s", c.baseURL, classID, courseID, date) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil) if err != nil { return nil, err } c.setAuthHeaders(req, token) resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { return nil, fmt.Errorf("qldt request-leave http %d: %s", resp.StatusCode, string(body)) } return body, nil } func (c *Client) UpdateLeaveStatus(ctx context.Context, token string, leaveID int64, status string) ([]byte, error) { payload := map[string]string{"status": status} jsonData, _ := json.Marshal(payload) u := fmt.Sprintf("%s/request-leave/%d/status", c.baseURL, leaveID) req, err := http.NewRequestWithContext(ctx, http.MethodPatch, u, bytes.NewBuffer(jsonData)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") c.setAuthHeaders(req, token) resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode < 200 || resp.StatusCode >= 300 { // Fallback to POST method if PATCH is Method Not Allowed or Not Found if resp.StatusCode == http.StatusMethodNotAllowed || resp.StatusCode == http.StatusNotFound { req2, err2 := http.NewRequestWithContext(ctx, http.MethodPost, u, bytes.NewBuffer(jsonData)) if err2 == nil { req2.Header.Set("Content-Type", "application/json") c.setAuthHeaders(req2, token) resp2, errOr := c.http.Do(req2) if errOr == nil { defer resp2.Body.Close() body2, _ := io.ReadAll(resp2.Body) if resp2.StatusCode >= 200 && resp2.StatusCode < 300 { return body2, nil } } } } return nil, fmt.Errorf("qldt request-leave/%d/status http %d: %s", leaveID, resp.StatusCode, string(body)) } return body, nil }