mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-02-22 09:42:01 +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 prisma from "@/lib/db";
|
||||||
import type { PaymentType } from "@/lib/types";
|
import type { PaymentType } from "@/lib/types";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
|
import { addDevicesToGroup } from "./omada-actions";
|
||||||
|
|
||||||
export async function createPayment(data: PaymentType) {
|
export async function createPayment(data: PaymentType) {
|
||||||
console.log("data", data);
|
console.log("data", data);
|
||||||
@ -40,6 +41,9 @@ export async function verifyPayment(data: VerifyPaymentType) {
|
|||||||
where: {
|
where: {
|
||||||
id: data.paymentId,
|
id: data.paymentId,
|
||||||
},
|
},
|
||||||
|
include: {
|
||||||
|
devices: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
"https://verifypaymentsapi.baraveli.dev/verify-payment",
|
"https://verifypaymentsapi.baraveli.dev/verify-payment",
|
||||||
@ -53,19 +57,38 @@ export async function verifyPayment(data: VerifyPaymentType) {
|
|||||||
);
|
);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
console.log(json);
|
console.log(json);
|
||||||
|
const newDevices = payment?.devices.map((d) => {
|
||||||
|
return {
|
||||||
|
name: d.name,
|
||||||
|
macAddress: d.mac,
|
||||||
|
};
|
||||||
|
});
|
||||||
if (json.success === true) {
|
if (json.success === true) {
|
||||||
await prisma.payment.update({
|
await Promise.all([
|
||||||
where: {
|
prisma.payment.update({
|
||||||
id: payment?.id,
|
where: {
|
||||||
},
|
id: payment?.id,
|
||||||
data: {
|
},
|
||||||
paid: true,
|
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]");
|
revalidatePath("/payment[paymentId]");
|
||||||
return json;
|
return json;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addDevicesToOmada() {
|
||||||
|
console.log("hi");
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import DevicesToPay from "@/components/devices-to-pay";
|
import DevicesToPay from "@/components/devices-to-pay";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { hasSession } from "@/lib/auth-guard";
|
|
||||||
import prisma from "@/lib/db";
|
import prisma from "@/lib/db";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
export default async function PaymentPage({
|
export default async function PaymentPage({
|
||||||
@ -26,12 +26,14 @@ export default async function PaymentPage({
|
|||||||
devices: true,
|
devices: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await hasSession();
|
|
||||||
const formula = await prisma.billFormula.findFirst();
|
const formula = await prisma.billFormula.findFirst();
|
||||||
return (
|
return (
|
||||||
<div>
|
<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">
|
<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>
|
<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>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -42,7 +42,7 @@ export default function DevicesToPay({
|
|||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="p-2 flex flex-col gap-2">
|
<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"}
|
{!payment?.paid ? "Devices to pay" : "Devices Paid"}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
@ -71,7 +71,7 @@ export default function DevicesToPay({
|
|||||||
accountNo="90101400028321000"
|
accountNo="90101400028321000"
|
||||||
/>
|
/>
|
||||||
{payment?.paid ? (
|
{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
|
<Button
|
||||||
disabled={verifying}
|
disabled={verifying}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
import prisma from "@/lib/db";
|
import prisma from "@/lib/db";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import { Calendar } from "lucide-react";
|
import { Calendar } from "lucide-react";
|
||||||
import Pagination from "./pagination";
|
import Pagination from "./pagination";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
@ -96,7 +97,7 @@ export async function PaymentsTable({
|
|||||||
{payments.map((payment) => (
|
{payments.map((payment) => (
|
||||||
<TableRow key={payment.id}>
|
<TableRow key={payment.id}>
|
||||||
<TableCell>
|
<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">
|
<div className="flex items-center gap-2">
|
||||||
<Calendar size={16} opacity={0.5} />
|
<Calendar size={16} opacity={0.5} />
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
@ -114,7 +115,7 @@ export async function PaymentsTable({
|
|||||||
View Details
|
View Details
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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"}
|
{payment.paid ? "Paid" : "Unpaid"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,13 +12,3 @@ export async function AdminAuthGuard() {
|
|||||||
}
|
}
|
||||||
return true;
|
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 = {
|
export const config = {
|
||||||
matcher: ["/devices", "/"],
|
matcher: ["/devices", "/", "/payments", "/payments/:paymentId"],
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user