Skip to content

Commit e3746bf

Browse files
committed
Add secure place update API endpoint with ownership validation
1 parent 888d3fe commit e3746bf

1 file changed

Lines changed: 129 additions & 0 deletions

File tree

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import type { RequestHandler } from "@builder.io/qwik-city";
2+
import { UpdatePlace } from "~/api/Query";
3+
4+
export const onPost: RequestHandler = async ({ request, json, event }) => {
5+
try {
6+
const body = await request.parseBody();
7+
8+
// Validate required fields
9+
if (!body.placeId) {
10+
return json(400, {
11+
success: false,
12+
message: "Missing required field: placeId",
13+
});
14+
}
15+
16+
const placeId = parseInt(body.placeId as string);
17+
if (isNaN(placeId)) {
18+
return json(400, {
19+
success: false,
20+
message: "Invalid placeId format",
21+
});
22+
}
23+
24+
// Parse and validate placeData
25+
let placeData;
26+
try {
27+
placeData =
28+
typeof body.placeData === "string"
29+
? JSON.parse(body.placeData)
30+
: body.placeData;
31+
} catch (error) {
32+
return json(400, {
33+
success: false,
34+
message: "Invalid placeData format. Must be valid JSON.",
35+
});
36+
}
37+
38+
// Validate specific fields
39+
const allowedFields = [
40+
"name",
41+
"address",
42+
"image",
43+
"description",
44+
"tags",
45+
"rating",
46+
"wifiSpeed",
47+
"lat",
48+
"lng",
49+
"hasQuietEnvironment"
50+
];
51+
const invalidFields = Object.keys(placeData).filter(
52+
(field) => !allowedFields.includes(field)
53+
);
54+
55+
if (invalidFields.length > 0) {
56+
return json(400, {
57+
success: false,
58+
message: `Invalid fields: ${invalidFields.join(
59+
", "
60+
)}. Allowed fields: ${allowedFields.join(", ")}`,
61+
});
62+
}
63+
64+
// Field-specific validations
65+
if (placeData.rating !== undefined) {
66+
if (typeof placeData.rating !== "number" || placeData.rating < 1 || placeData.rating > 5) {
67+
return json(400, {
68+
success: false,
69+
message: "Rating must be a number between 1 and 5",
70+
});
71+
}
72+
}
73+
74+
if (placeData.wifiSpeed !== undefined) {
75+
if (typeof placeData.wifiSpeed !== "number" || placeData.wifiSpeed < 0) {
76+
return json(400, {
77+
success: false,
78+
message: "WifiSpeed must be a non-negative number",
79+
});
80+
}
81+
}
82+
83+
if (placeData.lat !== undefined && (typeof placeData.lat !== "number" || placeData.lat < -90 || placeData.lat > 90)) {
84+
return json(400, {
85+
success: false,
86+
message: "Latitude must be a number between -90 and 90",
87+
});
88+
}
89+
90+
if (placeData.lng !== undefined && (typeof placeData.lng !== "number" || placeData.lng < -180 || placeData.lng > 180)) {
91+
return json(400, {
92+
success: false,
93+
message: "Longitude must be a number between -180 and 180",
94+
});
95+
}
96+
97+
// Call the secure update function with built-in ownership validation
98+
const result = await UpdatePlace({
99+
event,
100+
placeId,
101+
placeData,
102+
});
103+
104+
if (result.success) {
105+
return json(200, result);
106+
} else {
107+
const statusCode = result.message.includes("Unauthorized")
108+
? 403
109+
: result.message.includes("not found")
110+
? 404
111+
: 400;
112+
return json(statusCode, result);
113+
}
114+
} catch (error) {
115+
console.error("API Error in place update:", error);
116+
return json(500, {
117+
success: false,
118+
message: "Internal server error",
119+
});
120+
}
121+
};
122+
123+
// Keep GET endpoint for backward compatibility or debugging
124+
export const onGet: RequestHandler = async ({ json }) => {
125+
return json(405, {
126+
success: false,
127+
message: "Method not allowed. Use POST to update place.",
128+
});
129+
};

0 commit comments

Comments
 (0)