mirror of
https://github.com/OHTU-German-learning-website/OHTU-German-learning-website.git
synced 2026-10-12 19:13:51 +03:00
Update backend for editing communications pages
This commit is contained in:
+1
-2
@@ -51,7 +51,6 @@ services:
|
||||
command: >
|
||||
sh -c "apt update && apt install -y wget postgresql-client &&
|
||||
wget https://github.com/peterldowns/pgmigrate/releases/download/v0.0.6%2Bcommit.4f90829/pgmigrate-linux-amd64 -O /usr/local/bin/pgmigrate &&
|
||||
chmod +x /usr/local/bin/pgmigrate &&
|
||||
/usr/local/bin/docker-entrypoint.sh sleep inf"
|
||||
chmod +x /usr/local/bin/pgmigrate && sleep inf"
|
||||
volumes:
|
||||
pg-data:
|
||||
|
||||
+10
-13
@@ -18,26 +18,26 @@ import { useTestDatabase } from "@/backend/test/testdb";
|
||||
import { useTestRequest } from "@/backend/test/mock-request";
|
||||
import { getHTMLContent } from "@/backend/html-services";
|
||||
|
||||
describe("GET /api/html-content/[id]", () => {
|
||||
describe("GET /api/html-content/[type]/[id]", () => {
|
||||
useTestDatabase();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const getRequest = async (id, type = "resources") => {
|
||||
const getRequest = async (type, id) => {
|
||||
const { mockGet } = useTestRequest();
|
||||
const req = mockGet(`/api/html-content/${id}?type=${type}`);
|
||||
return GET(req);
|
||||
const req = mockGet(`/api/html-content/${type}/${id}`);
|
||||
return GET(req, { params: { type, id } });
|
||||
};
|
||||
|
||||
it("should return HTML content as JSON", async () => {
|
||||
getHTMLContent.mockResolvedValueOnce("<p>Mocked HTML</p>");
|
||||
|
||||
const response = await getRequest(1, "resources");
|
||||
const response = await getRequest("resources", 1);
|
||||
const body = await response.json();
|
||||
|
||||
expect(getHTMLContent).toHaveBeenCalledWith("1", "learning_pages_html");
|
||||
expect(getHTMLContent).toHaveBeenCalledWith("learning_pages_html", 1);
|
||||
expect(response.status).toBe(200);
|
||||
expect(body).toEqual({ content: "<p>Mocked HTML</p>" });
|
||||
});
|
||||
@@ -45,21 +45,18 @@ describe("GET /api/html-content/[id]", () => {
|
||||
it("should call correct table for communications type", async () => {
|
||||
getHTMLContent.mockResolvedValueOnce("<p>Mocked HTML</p>");
|
||||
|
||||
const response = await getRequest(5, "communications");
|
||||
const response = await getRequest("communications", 5);
|
||||
await response.json();
|
||||
|
||||
expect(getHTMLContent).toHaveBeenCalledWith(
|
||||
"5",
|
||||
"communications_pages_html"
|
||||
);
|
||||
expect(getHTMLContent).toHaveBeenCalledWith("communications_pages_html", 5);
|
||||
});
|
||||
|
||||
it("should return empty string table if type is invalid", async () => {
|
||||
getHTMLContent.mockResolvedValueOnce("<p>Mocked HTML</p>");
|
||||
|
||||
const response = await getRequest(3, "unknown");
|
||||
const response = await getRequest("unknown", 3);
|
||||
await response.json();
|
||||
|
||||
expect(getHTMLContent).toHaveBeenCalledWith("3", "");
|
||||
expect(getHTMLContent).toHaveBeenCalledWith("", 3);
|
||||
});
|
||||
});
|
||||
+15
-16
@@ -6,19 +6,8 @@ import DOMPurify from "dompurify";
|
||||
/*
|
||||
This route fetches html content from db according to id and page type
|
||||
*/
|
||||
export async function GET(req) {
|
||||
// create URL-object from request
|
||||
const url = new URL(req.url);
|
||||
|
||||
// split path to segments
|
||||
const pathSegments = url.pathname.split("/");
|
||||
|
||||
// get page type from url parameters
|
||||
const type = url.searchParams.get("type");
|
||||
|
||||
// get last segment, which is the correct id of the page
|
||||
const id = pathSegments[pathSegments.length - 1];
|
||||
|
||||
export async function GET(req, { params }) {
|
||||
const { type, id } = await params;
|
||||
// returns correct db table based on url. Prevents malicious code
|
||||
const getTableType = (type) => {
|
||||
if (type == "resources") {
|
||||
@@ -30,7 +19,7 @@ export async function GET(req) {
|
||||
}
|
||||
};
|
||||
|
||||
const content = await getHTMLContent(id, getTableType(type));
|
||||
const content = await getHTMLContent(getTableType(type), id);
|
||||
|
||||
// returns the fetched html as json
|
||||
return new Response(JSON.stringify({ content }), {
|
||||
@@ -41,13 +30,23 @@ export async function GET(req) {
|
||||
export const PUT = withAuth(
|
||||
async (req, { params }) => {
|
||||
const data = await req.json();
|
||||
const { id } = await params;
|
||||
const { type, id } = await params;
|
||||
|
||||
const getTableType = (type) => {
|
||||
if (type == "resources") {
|
||||
return "learning_pages_html";
|
||||
} else if (type == "communications") {
|
||||
return "communications_pages_html";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const window = new JSDOM("").window;
|
||||
const purify = DOMPurify(window);
|
||||
const cleaned = purify.sanitize(data.content, { ADD_ATTR: ["target"] });
|
||||
|
||||
if (!(await updateHTMLContent(id, cleaned))) {
|
||||
if (!(await updateHTMLContent(getTableType(type), id, cleaned))) {
|
||||
return new Response("Error updating HTML content", {
|
||||
status: 400,
|
||||
});
|
||||
@@ -22,7 +22,7 @@ export default function RenderHTML({ contentId, type }) {
|
||||
// function to fetch HTML content using html-services, and set it to state html
|
||||
async function fetchHTML() {
|
||||
// fetch response through next.js route using getHTMLContent function
|
||||
const res = await fetch(`/api/html-content/${contentId}?type=${type}`);
|
||||
const res = await fetch(`/api/html-content/${type}/${contentId}`);
|
||||
|
||||
// chnages the response to proper json
|
||||
const data = await res.json();
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function Chapters() {
|
||||
useEffect(() => {
|
||||
async function fetchHTML() {
|
||||
const res = await fetch(
|
||||
`/api/html-content/${parseInt(chapter)}?type=resources`
|
||||
`/api/html-content/resources/${parseInt(chapter)}`
|
||||
);
|
||||
const data = await res.json();
|
||||
setEditorContent(data.content);
|
||||
@@ -35,14 +35,17 @@ export default function Chapters() {
|
||||
const jsonData = JSON.stringify({
|
||||
content: editorContent.replace(/ /g, " "),
|
||||
});
|
||||
const res = await fetch(`/api/html-content/${parseInt(chapter)}`, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "PUT",
|
||||
body: jsonData,
|
||||
});
|
||||
const res = await fetch(
|
||||
`/api/html-content/resources/${parseInt(chapter)}`,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "PUT",
|
||||
body: jsonData,
|
||||
}
|
||||
);
|
||||
if (res.status == 200) {
|
||||
setEditorMessage({ error: false, msg: "Updated successfully" });
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { DB } from "@/backend/db";
|
||||
|
||||
const allowedTables = ["learning_pages_html", "communications_pages_html"];
|
||||
|
||||
/*
|
||||
This function fetches html content from database
|
||||
|
||||
@@ -15,9 +17,12 @@ NOTE: This function could be used to fetch html content to all pages, not just
|
||||
other identifier as parameter for this function.
|
||||
|
||||
*/
|
||||
export async function getHTMLContent(id, type) {
|
||||
export async function getHTMLContent(type, id) {
|
||||
try {
|
||||
console.log("Table type: ", type);
|
||||
if (!allowedTables.includes(type)) {
|
||||
return "<p>Error loading page</p>";
|
||||
}
|
||||
// get content from db using parameterized query
|
||||
const result = await DB.pool(
|
||||
"SELECT content FROM " + type + " WHERE id = $1",
|
||||
@@ -49,10 +54,13 @@ NOTE: This function also might need to be updated if the functions in these fi
|
||||
should be used to fetch and update any html content, and not just the chapter pages.
|
||||
In this case, same kind of modifications apply as seen above in function getHTMLContent(id)
|
||||
*/
|
||||
export async function updateHTMLContent(id, content) {
|
||||
export async function updateHTMLContent(type, id, content) {
|
||||
if (!allowedTables.includes(type)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const result = await DB.pool(
|
||||
"UPDATE learning_pages_html SET content = $1 WHERE id = $2",
|
||||
"UPDATE " + type + " SET content = $1 WHERE id = $2",
|
||||
[content, id]
|
||||
);
|
||||
if (result.rowCount == 1) {
|
||||
|
||||
Reference in New Issue
Block a user