clean code

This commit is contained in:
MuslemRahimi 2025-04-16 23:04:32 +02:00
parent faafa73c6e
commit 98ee3a9c37
4 changed files with 133 additions and 46 deletions

View File

@ -116,7 +116,7 @@
class="text-center mb-10 relative w-fit flex justify-center m-auto text-white"
>
<div class="mb-4 flex justify-center -mt-3 lg:mb-3">
<a href="/learning-center/article/stocknear-discord-bot"
<a href="/discord-bot"
><div
class="flex items-center justify-center sm:hover:text-muted dark:sm:hover:text-white text-blue-700 dark:text-blue-400"
>

View File

@ -2,32 +2,7 @@ import { error, fail, redirect } from "@sveltejs/kit";
import { validateData } from "$lib/utils";
import { loginUserSchema, registerUserSchema } from "$lib/schemas";
/*
export const load = async () => {
const apiKey = import.meta.env.VITE_LEMON_SQUEEZY_API_KEY;
const getLTDCount = async () => {
// make the POST request to the endpoint
const response = await fetch('https://api.lemonsqueezy.com/v1/order-items?page[size]=100', {
headers: {
'Accept': 'application/vnd.api+json',
'Content-Type': 'application/vnd.api+json',
'Authorization': `Bearer ${apiKey}`
}
});
const output = await response.json();
const filteredData = output?.data?.filter(item => item?.attributes?.product_name === 'Pro Subscription (Life Time Access)');
const count = filteredData?.length || 0;
return count;
};
return {
getLTDCount: await getLTDCount(),
};
};
*/
export const actions = {
login: async ({ request, locals }) => {

View File

@ -24,22 +24,14 @@
}
});
async function purchasePlan(subscriptionType: string = "") {
async function purchasePlan() {
if (data?.user) {
let subId = "";
if (subscriptionType === "lifeTime") {
subId = import.meta.env.VITE_LEMON_SQUEEZY_LIFE_TIME_ACCESS_ID;
} else if (mode && subscriptionType === "plus") {
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PLUS;
} else if (!mode && subscriptionType === "plus") {
subId = import.meta.env.VITE_LEMON_SQUEEZY_MONTHLY_ID_PLUS;
} else if (mode && subscriptionType === "pro") {
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PRO;
} else if (!mode && subscriptionType === "pro") {
subId = import.meta.env.VITE_LEMON_SQUEEZY_MONTHLY_ID_PRO;
if (mode) {
subId = import.meta.env.VITE_LEMON_SQUEEZY_DISCORD_ANNUAL_ID;
} else {
subId = import.meta.env.VITE_LEMON_SQUEEZY_ANNUAL_ID_PRO;
subId = import.meta.env.VITE_LEMON_SQUEEZY_DISCORD_MONTHLY_ID;
}
try {
} catch (e) {
@ -66,8 +58,8 @@
</script>
<SEO
title="Pricing Plans"
description="Get unlimited access to all of our data and tools, including full financial history, full ETF holdings, and more."
title="Discord Bot Plans"
description="Explore our affordable Discord Bot plans. Unlock unlimited access and powerful features to supercharge your Discord server with Stocknear dataset."
/>
<svelte:head>
@ -259,12 +251,13 @@
<div
class="flex flex-col sm:flex-row items-center justify-between w-full"
>
<button
on:click={() => purchasePlan("lifeTime")}
<label
for={!data?.user ? "userLogin" : ""}
on:click={() => purchasePlan()}
class=" text-lg text-white cursor-pointer w-full lg:w-auto py-3 lg:mt-2 px-4 bg-blue-600 rounded font-semibold sm:hover:bg-blue-700 transition duration-100 flex items-center justify-center lg:justify-end"
>
Unlock Discord Bot
</button>
</label>
<div
class="hidden lg:flex flex-col justify-center lg:justify-end lg:ml-auto items-center mb-1"
>
@ -299,7 +292,7 @@
class="relative overflow-hidden mt-4 card card-side mt-10 mb-10 bg-[#18181B] to-black rounded-lg p-5 flex flex-col lg:flex-row items-center justify-between"
>
<div class="card-body relative z-10 min-h-96 sm:min-h-0">
<h2 class="card-title text-4xl font-bold mb-6 text-start">
<h2 class="card-title text-3xl sm:text-4xl font-bold mb-6 text-start">
See Our Discord Bots In Action!
</h2>
<p
@ -314,13 +307,15 @@
href={discordURL}
rel="noopener noreferrer"
target="_blank"
class="btn btn-ghost bg-blue-600 text-lg font-bold px-5 py-2.5 rounded"
class="btn btn-ghost bg-blue-600 text-lg font-semibold px-5 py-3 rounded w-full sm:w-fit"
>
Join Discord
</a>
</div>
</div>
<figure class="absolute -bottom-48 sm:top-5 sm:-right-20 z-2">
<figure
class="absolute -bottom-30 sm:-bottom-48 sm:top-5 sm:-right-20 z-2"
>
<div class="flex-shrink-0">
<img
class="rounded-xl w-full sm:w-[600px] opacity-60 sm:opacity-100"

View File

@ -0,0 +1,117 @@
import crypto from "node:crypto";
// Your secret key provided by Lemon Squeezy
const SECRET_KEY = import.meta.env.VITE_LEMON_SQUEEZY_SECRET_KEY;
if (!SECRET_KEY) {
throw new Error("Missing Lemon Squeezy secret key.");
}
/**
* Verifies that the provided signature matches the HMAC digest for the given payload.
*
* @param {string} payload - The raw request body.
* @param {string} signatureHeader - The signature from the request header.
* @returns {boolean} - True if the signature is valid; otherwise, false.
*/
function isValidSignature(payload, signatureHeader) {
const hmac = crypto.createHmac("sha256", SECRET_KEY);
const computedDigestHex = hmac.update(payload).digest("hex");
// Convert both values to buffers for timing-safe comparison
const computedBuffer = Buffer.from(computedDigestHex, "utf8");
const signatureBuffer = Buffer.from(signatureHeader, "utf8");
// Ensure the buffers are the same length; if not, they can't be equal.
if (computedBuffer.length !== signatureBuffer.length) {
return false;
}
return crypto.timingSafeEqual(computedBuffer, signatureBuffer);
}
export const POST = async ({ request, locals }) => {
try {
const bodyText = await request.text();
// Retrieve the signature header; return early if missing.
const signatureHeader = request.headers.get("x-Signature");
if (!signatureHeader) {
console.error("Missing x-Signature header.");
return new Response(
JSON.stringify({ error: "Missing signature header" }),
{
status: 403,
headers: { "Content-Type": "application/json" },
}
);
}
if (!isValidSignature(bodyText, signatureHeader)) {
console.error("Signature verification failed.");
return new Response(
JSON.stringify({ error: "Invalid signature" }),
{
status: 403,
headers: { "Content-Type": "application/json" },
}
);
}
// Parse the JSON payload
const payload = JSON.parse(bodyText);
const userId = payload?.meta?.custom_data?.userId;
const { status, refunded } = payload?.data?.attributes || {};
if (!userId || status === undefined) {
console.error("Missing userId or status in payload:", payload);
return new Response(
JSON.stringify({ error: "Invalid payload structure" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
try {
const paymentData = { user: userId, data: payload };
await locals.pb.collection("discordPayments").create(paymentData);
} catch (dbError) {
console.error("Database error:", dbError);
return new Response(
JSON.stringify({ error: "Pocketbase error" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
return new Response(
JSON.stringify({ message: "Payment data received successfully" }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
} catch (error) {
console.error("Error processing request:", error);
return new Response(
JSON.stringify({ error: "Internal server error" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
};