mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-02-21 18:42:00 +00:00
Implement Omada device management and enhance payment processing
- Added new omada-actions.ts file to handle fetching and updating device groups in Omada. - Updated authMiddleware to include new payment routes. - Enhanced createPayment function to add devices to a group upon successful payment verification. - Improved payment verification process to include device management. - Refactored PaymentsTable and DevicesToPay components for better UI and state handling. - Removed unused hasSession function from auth-guard.ts for cleaner code.
This commit is contained in:
parent
36f22c0614
commit
e9d81c089a
181
actions/omada-actions.ts
Normal file
181
actions/omada-actions.ts
Normal file
@ -0,0 +1,181 @@
|
||||
interface IpAddress {
|
||||
ip: string;
|
||||
mask: number;
|
||||
}
|
||||
|
||||
interface Ipv6Address {
|
||||
ip: string;
|
||||
prefix: number;
|
||||
}
|
||||
|
||||
interface MacAddress {
|
||||
ruleId?: number;
|
||||
name: string;
|
||||
macAddress: string;
|
||||
}
|
||||
|
||||
interface GroupProfile {
|
||||
groupId: string;
|
||||
site?: string;
|
||||
name: string;
|
||||
buildIn?: boolean;
|
||||
ipList?: IpAddress[];
|
||||
ipv6List?: Ipv6Address[];
|
||||
macAddressList?: MacAddress[];
|
||||
count: number;
|
||||
type: number;
|
||||
resource: number;
|
||||
}
|
||||
|
||||
interface OmadaResponse {
|
||||
errorCode: number;
|
||||
msg: string;
|
||||
result: {
|
||||
data: GroupProfile[];
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchOmadaGroupProfiles(
|
||||
omadacId: string,
|
||||
siteId: string,
|
||||
): Promise<OmadaResponse> {
|
||||
if (!omadacId || !siteId) {
|
||||
throw new Error("omadacId and siteId are required parameters");
|
||||
}
|
||||
|
||||
const timestamp: number = Date.now();
|
||||
const baseUrl: string = "https://omada.sarlink.link";
|
||||
const url: string = `${baseUrl}/${omadacId}/api/v2/sites/${siteId}/setting/profiles/groups?_t=${timestamp}`;
|
||||
|
||||
const headers: HeadersInit = {
|
||||
accept: "application/json, text/plain, */*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
cookie: "TPOMADA_SESSIONID=f30bf82348784089ba90740e59e4aa99",
|
||||
"csrf-token": "fedc6283e9604372b402f82fdaeba0db",
|
||||
priority: "u=1, i",
|
||||
referer: baseUrl,
|
||||
refresh: "manual",
|
||||
"sec-ch-ua": '"Brave";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Linux"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
"sec-gpc": "1",
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
};
|
||||
|
||||
try {
|
||||
const response: Response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data: OmadaResponse = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Error fetching Omada group profiles:", error);
|
||||
throw error instanceof Error ? error : new Error("Unknown error occurred");
|
||||
}
|
||||
}
|
||||
|
||||
export { fetchOmadaGroupProfiles, type MacAddress };
|
||||
|
||||
export async function addDevicesToGroup({
|
||||
omadacId,
|
||||
siteId,
|
||||
groupId,
|
||||
newDevices,
|
||||
}: {
|
||||
omadacId?: string;
|
||||
siteId?: string;
|
||||
groupId?: string;
|
||||
newDevices: MacAddress[];
|
||||
}): Promise<void> {
|
||||
if (!omadacId || !siteId || !groupId) {
|
||||
throw new Error("omadacId, siteId, and groupId are required parameters");
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch the existing group profiles
|
||||
const groupProfiles: OmadaResponse = await fetchOmadaGroupProfiles(
|
||||
omadacId,
|
||||
siteId,
|
||||
);
|
||||
|
||||
// Find the group profile with the specified groupId
|
||||
const groupProfile: GroupProfile | undefined =
|
||||
groupProfiles.result.data.find((profile) => profile.groupId === groupId);
|
||||
|
||||
if (!groupProfile) {
|
||||
throw new Error(`Group with ID ${groupId} not found`);
|
||||
}
|
||||
|
||||
// Create a new array with the existing and new devices
|
||||
const updatedMacAddressList: MacAddress[] = [
|
||||
...(groupProfile.macAddressList || []),
|
||||
...newDevices,
|
||||
];
|
||||
|
||||
// Prepare the request payload
|
||||
const requestBody = {
|
||||
name: groupProfile.name,
|
||||
type: groupProfile.type,
|
||||
resource: groupProfile.resource,
|
||||
ipList: groupProfile.ipList,
|
||||
ipv6List: groupProfile.ipv6List,
|
||||
macAddressList: updatedMacAddressList,
|
||||
portList: null,
|
||||
countryList: null,
|
||||
portType: null,
|
||||
portMaskList: null,
|
||||
domainNamePort: null,
|
||||
};
|
||||
|
||||
const timestamp: number = Date.now();
|
||||
const baseUrl: string = "https://omada.sarlink.link";
|
||||
const url: string = `${baseUrl}/${omadacId}/api/v2/sites/${siteId}/setting/profiles/groups/2/${groupId}?_t=${timestamp}`;
|
||||
|
||||
const headers: HeadersInit = {
|
||||
accept: "application/json, text/plain, */*",
|
||||
"accept-language": "en-US,en;q=0.9",
|
||||
"content-type": "application/json;charset=UTF-8",
|
||||
cookie: "TPOMADA_SESSIONID=f30bf82348784089ba90740e59e4aa99",
|
||||
"csrf-token": "fedc6283e9604372b402f82fdaeba0db",
|
||||
origin: baseUrl,
|
||||
priority: "u=1, i",
|
||||
referer: baseUrl,
|
||||
refresh: "manual",
|
||||
"sec-ch-ua": '"Brave";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Linux"',
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
"sec-gpc": "1",
|
||||
"user-agent":
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
};
|
||||
|
||||
const response: Response = await fetch(url, {
|
||||
method: "PATCH",
|
||||
headers: headers,
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error adding devices to group:", error);
|
||||
throw error instanceof Error ? error : new Error("Unknown error occurred");
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
import prisma from "@/lib/db";
|
||||
import type { PaymentType } from "@/lib/types";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { addDevicesToGroup } from "./omada-actions";
|
||||
|
||||
export async function createPayment(data: PaymentType) {
|
||||
console.log("data", data);
|
||||
@ -40,6 +41,9 @@ export async function verifyPayment(data: VerifyPaymentType) {
|
||||
where: {
|
||||
id: data.paymentId,
|
||||
},
|
||||
include: {
|
||||
devices: true,
|
||||
},
|
||||
});
|
||||
const response = await fetch(
|
||||
"https://verifypaymentsapi.baraveli.dev/verify-payment",
|
||||
@ -53,19 +57,38 @@ export async function verifyPayment(data: VerifyPaymentType) {
|
||||
);
|
||||
const json = await response.json();
|
||||
console.log(json);
|
||||
const newDevices = payment?.devices.map((d) => {
|
||||
return {
|
||||
name: d.name,
|
||||
macAddress: d.mac,
|
||||
};
|
||||
});
|
||||
if (json.success === true) {
|
||||
await prisma.payment.update({
|
||||
where: {
|
||||
id: payment?.id,
|
||||
},
|
||||
data: {
|
||||
paid: true,
|
||||
},
|
||||
});
|
||||
await Promise.all([
|
||||
prisma.payment.update({
|
||||
where: {
|
||||
id: payment?.id,
|
||||
},
|
||||
data: {
|
||||
paid: true,
|
||||
},
|
||||
}),
|
||||
addDevicesToGroup({
|
||||
groupId: process.env.OMADA_GROUP_ID,
|
||||
omadacId: process.env.OMADA_CID,
|
||||
siteId: process.env.OMADA_SITE_ID,
|
||||
newDevices: newDevices || [],
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
revalidatePath("/payment[paymentId]");
|
||||
return json;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
export async function addDevicesToOmada() {
|
||||
console.log("hi");
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import DevicesToPay from "@/components/devices-to-pay";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { hasSession } from "@/lib/auth-guard";
|
||||
import prisma from "@/lib/db";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { headers } from "next/headers";
|
||||
import React from "react";
|
||||
export default async function PaymentPage({
|
||||
@ -26,12 +26,14 @@ export default async function PaymentPage({
|
||||
devices: true,
|
||||
},
|
||||
});
|
||||
await hasSession();
|
||||
const formula = await prisma.billFormula.findFirst();
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-between items-center border-b-2 text-gray-500 text-2xl font-bold title-bg py-4 px-2 mb-4">
|
||||
<h3>Payment</h3>
|
||||
<span className={cn("text-sm border px-4 py-2 rounded-md uppercase font-semibold", payment?.paid ? "text-green-500 bg-green-500/20" : "text-yellow-500 bg-yellow-500/20")}>
|
||||
{payment?.paid ? "Paid" : "Pending"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -42,7 +42,7 @@ export default function DevicesToPay({
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="p-2 flex flex-col gap-2">
|
||||
<h3 className="title-bg my-1 font-semibold text-lg">
|
||||
<h3 className="title-bg my-1 p-2 font-semibold text-lg">
|
||||
{!payment?.paid ? "Devices to pay" : "Devices Paid"}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
@ -71,7 +71,7 @@ export default function DevicesToPay({
|
||||
accountNo="90101400028321000"
|
||||
/>
|
||||
{payment?.paid ? (
|
||||
<Button size={"lg"} variant={"secondary"} disabled className="text-green-400 bg-green-800">Payment Verified</Button>
|
||||
<Button size={"lg"} variant={"secondary"} disabled className="dark:text-green-200 text-green-900 bg-green-500/20 uppercase font-semibold">Payment Verified</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={verifying}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import prisma from "@/lib/db";
|
||||
import Link from "next/link";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Calendar } from "lucide-react";
|
||||
import Pagination from "./pagination";
|
||||
import { Badge } from "./ui/badge";
|
||||
@ -96,7 +97,7 @@ export async function PaymentsTable({
|
||||
{payments.map((payment) => (
|
||||
<TableRow key={payment.id}>
|
||||
<TableCell>
|
||||
<div className="flex flex-col items-start title-bg border rounded p-2">
|
||||
<div className={cn("flex flex-col items-start title-bg border rounded p-2", payment?.paid ? "bg-green-500/10 border-dashed border-green=500" : "bg-yellow-500/10 border-dashed border-yellow-500 dark:border-yellow-500/50")}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Calendar size={16} opacity={0.5} />
|
||||
<span className="text-muted-foreground">
|
||||
@ -114,7 +115,7 @@ export async function PaymentsTable({
|
||||
View Details
|
||||
</Button>
|
||||
</Link>
|
||||
<Badge className="p-2" variant={payment.paid ? "outline" : "secondary"}>
|
||||
<Badge className={cn(payment?.paid ? "text-green-500 bg-green-500/20" : "text-yellow-500 bg-yellow-500/20")} variant={payment.paid ? "outline" : "secondary"}>
|
||||
{payment.paid ? "Paid" : "Unpaid"}
|
||||
</Badge>
|
||||
</div>
|
||||
|
@ -12,13 +12,3 @@ export async function AdminAuthGuard() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function hasSession() {
|
||||
const session = await auth.api.getSession({
|
||||
headers: await headers(),
|
||||
});
|
||||
if (!session) {
|
||||
return redirect("/login");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -21,5 +21,5 @@ export default async function authMiddleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ["/devices", "/"],
|
||||
matcher: ["/devices", "/", "/payments", "/payments/:paymentId"],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user