better protection against bot

This commit is contained in:
MuslemRahimi 2025-02-12 19:08:58 +01:00
parent efccac0ba4
commit b071412f29
4 changed files with 203 additions and 109 deletions

View File

@ -15,44 +15,70 @@
}
async function handleCreateAlert() {
// Validate input locally.
if (targetPrice < 0) {
toast.error(`Target Price must be above zero`, {
toast.error("Target Price must be above zero", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
} else {
const closePopup = document.getElementById("priceAlertModal");
closePopup?.dispatchEvent(new MouseEvent("click"));
return;
}
const postData = {
userId: data?.user?.id,
symbol: ticker,
name: data?.getStockQuote?.name,
assetType: assetType,
priceWhenCreated: currentPrice,
condition: condition,
targetPrice: targetPrice,
};
// Optionally close the modal popup.
const closePopup = document.getElementById("priceAlertModal");
closePopup?.dispatchEvent(new MouseEvent("click"));
// Make the POST request to the endpoint
// Prepare data for the POST request.
const postData = {
userId: data?.user?.id,
symbol: ticker,
name: data?.getStockQuote?.name,
assetType: assetType,
priceWhenCreated: currentPrice,
condition: condition,
targetPrice: targetPrice,
};
toast.success(`Successfully created price alert`, {
// Create a promise for the POST request.
const promise = fetch("/api/create-price-alert", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
}).then(async (response) => {
const result = await response.json();
// If the response is not ok, throw an error to be caught by toast.promise.
if (!response.ok) {
throw new Error(result.error || "Failed to create price alert");
}
return result;
});
// Use toast.promise to handle pending, success, and error states.
toast.promise(
promise,
{
loading: "Creating price alert...",
success: "Successfully created price alert",
error: (err) => err.message || "Failed to create price alert",
},
{
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
},
);
const response = await fetch("/api/create-price-alert", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
$newPriceAlertData = await response?.json();
//const output = await response.json();
// Await the promise and handle the result.
try {
const newPriceAlertData = await promise;
// Update reactive store or state as needed.
$newPriceAlertData = newPriceAlertData;
// Optionally reset targetPrice or perform further actions.
targetPrice = currentPrice;
} catch (error) {
// The error is already handled by toast.promise, but you can log it here.
console.error("Error creating price alert:", error);
}
}

View File

@ -1,30 +1,60 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const { pb } = locals;
const { user, pb } = locals;
const data = await request.json();
let output;
if (user?.id) {
let newAlert = {
'user': data['userId'],
'symbol': data['symbol']?.toUpperCase(),
'name': data['name'],
'assetType': data['assetType']?.toLowerCase(),
'targetPrice': Number(data['targetPrice']),
'condition': data['condition']?.toLowerCase(),
'priceWhenCreated': Number(data['priceWhenCreated']),
'triggered': false,
// If the user is not Pro, check the current number of active price alerts.
if (user?.tier !== 'Pro') {
const totalAlerts = await pb.collection("priceAlert").getFullList({
// Ensure the filter checks for a boolean false.
filter: `user="${user?.id}" && triggered=false`
});
// If the user already has 3 or more active alerts, return an error.
if (totalAlerts?.length >= 3) {
return new Response(
JSON.stringify({ error: "Price alert limit reached for non-Pro users" }),
{ status: 403 }
);
}
}
try {
// Prepare the new alert data.
const newAlert = {
user: user?.id,
symbol: data['symbol']?.toUpperCase(),
name: data['name'],
assetType: data['assetType']?.toLowerCase(),
targetPrice: Number(data['targetPrice']),
condition: data['condition']?.toLowerCase(),
priceWhenCreated: Number(data['priceWhenCreated']),
triggered: false,
};
output = await pb.collection("priceAlert")?.create(newAlert)
} catch (err) {
output = {}
}
let output;
try {
output = await pb.collection("priceAlert").create(newAlert);
} catch (err) {
// Return an error response if the alert could not be created.
return new Response(
JSON.stringify({ error: "Error creating price alert" }),
{ status: 500 }
);
}
return new Response(JSON.stringify(output));
}
else {
return new Response(
JSON.stringify({ error: "Error creating price alert" }),
{ status: 500 }
);
}
};

View File

@ -1,17 +1,33 @@
import type { RequestHandler } from "./$types";
export const POST: RequestHandler = async ({ request, locals }) => {
const { pb } = locals;
const { user, pb } = locals;
const data = await request.json();
let output;
try {
output = await pb.collection("watchlist").create(data)
// For non-Pro users, ensure they can only have 1 watchlist.
if (user?.tier !== 'Pro') {
// Get the current watchlists for the user.
const existingWatchlists = await pb.collection("watchlist").getFullList({
filter: `user="${user.id}"`
});
// If the user already has a watchlist, return an error response.
if (existingWatchlists.length >= 1) {
return new Response(
JSON.stringify({ error: "Non-Pro users can only have 1 watchlist" }),
{ status: 403 }
);
}
catch(e) {
//console.log(e)
output = {};
}
console.log(output)
}
// If the user is Pro or doesn't have a watchlist yet, attempt to create one.
try {
output = await pb.collection("watchlist").create(data);
} catch (err) {
// Optionally, log the error or adjust the error message as needed.
output = { error: "Failed to create watchlist" };
}
return new Response(JSON.stringify(output));
};

View File

@ -287,13 +287,13 @@
}
async function createWatchList(event) {
event.preventDefault(); // prevent the default form submission behavior
const formData = new FormData(event.target); // create a FormData object from the form
event.preventDefault(); // Prevent the default form submission behavior
const formData = new FormData(event.target); // Create a FormData object from the form
const title = formData.get("title");
if (!title || title?.length === 0) {
// Validate the title input
if (!title || title.toString().trim().length === 0) {
toast.error("Title cannot be empty!", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
@ -301,7 +301,7 @@
return;
}
if (title?.length > 100) {
if (title.toString().length > 100) {
toast.error("Title is too long. Keep it simple and concise bruv!", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
@ -309,54 +309,66 @@
return;
}
const postData = {};
// Iterate through the FormData entries and populate the object
for (const [key, value] of formData?.entries()) {
// Build the POST data object
const postData: Record<string, any> = {};
for (const [key, value] of formData.entries()) {
postData[key] = value;
}
try {
const response = await fetch("/api/create-watchlist", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
}); // make a POST request to the server with the FormData object
if (response?.ok) {
const output = await response.json();
try {
// Save the version along with the rules
localStorage?.setItem(
"last-watchlist-id",
JSON?.stringify(output?.id),
);
} catch (e) {
console.log("Failed saving indicator rules: ", e);
}
toast.success("Watchlist created successfully!", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
const clicked = document.getElementById("addWatchlist");
clicked?.dispatchEvent(new MouseEvent("click"));
const anchor = document.createElement("a");
anchor.href = "/watchlist/stocks";
anchor.dispatchEvent(new MouseEvent("click"));
} else {
toast.error("Something went wrong. Please try again!", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
// Create a promise for the POST request
const promise = fetch("/api/create-watchlist", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
}).then(async (response) => {
const output = await response.json();
if (!response.ok) {
// If the server returned an error (e.g. nonPro user already has a watchlist),
// throw an error to be caught by toast.promise.
throw new Error(
output.error || "Something went wrong. Please try again!",
);
}
} catch (error) {
console.error("Error:", error);
toast.error("An error occurred. Please try again later.", {
return output;
});
// Use toast.promise to display a loading toast, then a success or error message
toast.promise(
promise,
{
loading: "Creating watchlist...",
success: "Watchlist created successfully!",
error: (err) =>
err.message || "Something went wrong. Please try again!",
},
{
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
},
);
try {
const output = await promise;
// Save the watchlist ID to localStorage (optional)
try {
localStorage.setItem("last-watchlist-id", JSON.stringify(output?.id));
} catch (e) {
console.log("Failed saving watchlist id: ", e);
}
// Dispatch events to close the modal and navigate to the watchlist page
const clicked = document.getElementById("addWatchlist");
clicked?.dispatchEvent(new MouseEvent("click"));
const anchor = document.createElement("a");
anchor.href = "/watchlist/stocks";
anchor.dispatchEvent(new MouseEvent("click"));
} catch (error) {
console.error("Error creating watchlist:", error);
// No additional toast.error is needed here since toast.promise already handles errors.
}
}
@ -633,6 +645,7 @@
try {
const savedRules = localStorage?.getItem("watchlist-ruleOfList");
const savedLastWatchlistId = localStorage?.getItem("last-watchlist-id");
if (savedRules) {
const parsedRules = JSON.parse(savedRules);
@ -666,21 +679,30 @@
}
// Update checked items and sort the indicators
checkedItems = new Set(ruleOfList.map((item) => item.name));
checkedItems = new Set(ruleOfList?.map((item) => item.name));
allRows = sortIndicatorCheckMarks(allRows);
if (
typeof savedLastWatchlistId !== "undefined" &&
savedLastWatchlistId?.length > 0
) {
// Parse savedLastWatchlistId safely
if (savedLastWatchlistId && savedLastWatchlistId.length > 0) {
let parsedLastWatchlistId;
try {
parsedLastWatchlistId = JSON.parse(savedLastWatchlistId);
} catch (error) {
console.error(
"Error parsing last watchlist id from localStorage. Using raw value instead.",
error,
);
parsedLastWatchlistId = savedLastWatchlistId;
}
displayWatchList = allList?.find(
(item) => item?.id === JSON?.parse(savedLastWatchlistId),
(item) => item?.id === parsedLastWatchlistId,
);
}
// If no valid watchlist found, default to the first element of allList
// If no valid watchlist is found, default to the first element of allList
if (!displayWatchList && allList?.length > 0) {
displayWatchList = allList?.at(0);
displayWatchList = allList.at(0);
} else if (!displayWatchList) {
displayWatchList = {};
}
@ -696,7 +718,7 @@
isLoaded = true;
} catch (e) {
console.log(e);
console.error("onMount error:", e);
}
if ($isOpen) {