Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 6x 8x 8x 3x 5x 5x | import axios from "axios";
import { API_ENDPOINTS } from "@/api/endpoints";
/**
* Upload an image for embedding in a question.
*
* Teacher-Student API only:
* POST /v1/teacher/question/image/upload (multipart: `file`)
*
* The editor used to inline images as base64 `data:` URIs, which put the whole
* image inside the saved question HTML — re-sent in full to every student on
* every attempt, with no CDN caching. The bytes are stored once here and the
* editor embeds a URL instead.
*
* Auth rides on the global axios interceptors (BFF httpOnly cookie + CSRF);
* this deliberately uses the shared instance rather than a bare client.
*
* Note there is no role branch. This client only ever authenticates teachers
* and students, and the staff image API belongs to a different service with a
* different token — calling it from here would 401 and log the user out.
*
* Developer: Allan Ninal
*
* @param {FormData} formData Must contain `file`.
* @returns {Promise<Object>} `{ url, id, path, original_filename, size, content_type, ... }`
* @throws {Error} With the API's `detail` when present, so the dialog can show
* the real reason (415 wrong type, 413 too large) instead of something generic.
*/
export const uploadQuestionImage = async (formData) => {
try {
// Content-Type is deliberately NOT set. FormData needs a multipart
// boundary, and the browser only generates one when it owns the header;
// setting it by hand yields a boundary-less header the server cannot parse.
const response = await axios.post(API_ENDPOINTS.teacher.question.imageUpload, formData);
return response.data;
} catch (error) {
const detail = error.response?.data?.detail;
throw new Error(detail || "Failed to upload image");
}
};
export default uploadQuestionImage;
|