import { hashAPIPath } from "../../../actions/core/hash"; import { deleteBlob } from "../../../actions/azurestorage"; import { respondError, respondSuccess } from "../middleware/apiResponse"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../auth/[...nextauth]"; import nextConnect from "next-connect"; import middleware from "../middleware/middleware"; const ApiProxy = nextConnect(); ApiProxy.use(middleware); const forbidden = (res) => respondError(res, { status: 403, code: "FORBIDDEN", message: "Request not permitted" }); const hasValue = (value) => typeof value === "string" && value.trim().length > 0; const isValidContainer = (value) => typeof value === "string" && value.length >= 3 && value.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(value); ApiProxy.get(async (req, res) => { const containerName = req.query.container; const casefolderID = req.query.casefolderID; const blobName = req.query.blobname; const checkHash = req.query.hash; if ( !hasValue(containerName) || !hasValue(casefolderID) || !hasValue(blobName) || !hasValue(checkHash) ) { return respondError(res, { status: 400, code: "MISSING_REQUIRED_QUERY", message: "container, casefolderID, blobname and hash are required" }); } const casefolderIDTrimmed = casefolderID.trim(); const blobNameTrimmed = blobName.trim(); if (!isValidContainer(containerName)) { return respondError(res, { status: 400, code: "INVALID_REQUEST", message: "Invalid request" }); } const session = await getServerSession(req, res, authOptions(req, res)); if ( typeof session?.user?.id !== "string" || session.user.id.length === 0 || session.user.id !== containerName ) { return forbidden(res); } const hashCandidatePaths = [ "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + casefolderIDTrimmed + "&blobname=" + blobNameTrimmed, "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + encodeURIComponent(casefolderIDTrimmed) + "&blobname=" + blobNameTrimmed, "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + casefolderIDTrimmed + "&blobname=" + encodeURIComponent(blobNameTrimmed), "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + encodeURIComponent(casefolderIDTrimmed) + "&blobname=" + encodeURIComponent(blobNameTrimmed) ]; const isHashValid = hashCandidatePaths.some( (candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash ); if (!isHashValid) { return respondError(res, { status: 400, code: "INVALID_HASH", message: "Invalid hash" }); } try { const normalizedBlobName = blobNameTrimmed.startsWith( casefolderIDTrimmed + "/" ) ? blobNameTrimmed : casefolderIDTrimmed + "/files/" + blobNameTrimmed; const data = await deleteBlob(containerName, normalizedBlobName); return respondSuccess(res, { data: data }); } catch (error) { return respondError(res, { status: 400, code: "DELETE_BLOB_FAILED", message: "Unable to delete blob" }); } }); export const config = { api: { bodyParser: false } }; export default ApiProxy;