fix ( user access )
deskripsi: - membatasi akses user yang hanya bisa di berikan oleh admin
This commit is contained in:
@@ -24,7 +24,7 @@ export async function POST(req: Request) {
|
||||
data: {
|
||||
username: data.username,
|
||||
nomor: data.nomor,
|
||||
active: true,
|
||||
active: false,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get";
|
||||
import backendLogger from "@/util/backendLogger";
|
||||
import { NextResponse } from "next/server";
|
||||
import sharp from "sharp";
|
||||
export async function POST(request: Request) {
|
||||
const formData = await request.formData();
|
||||
|
||||
@@ -11,9 +12,30 @@ export async function POST(request: Request) {
|
||||
|
||||
if (request.method === "POST") {
|
||||
try {
|
||||
const file: any = formData.get("file");
|
||||
console.log("ini file baru>", file);
|
||||
|
||||
// Resize ukuran
|
||||
const imageBuffer = await file.arrayBuffer();
|
||||
const resize = await sharp(imageBuffer).resize(2000).toBuffer();
|
||||
|
||||
// Convert buffer ke Blob
|
||||
const blob = new Blob([resize], { type: file.type });
|
||||
|
||||
// Convert Blob ke File
|
||||
const resizedFile = new File([blob], file.name, {
|
||||
type: file.type,
|
||||
lastModified: new Date().getTime(),
|
||||
});
|
||||
|
||||
// Buat FormData baru
|
||||
const newFormData = new FormData();
|
||||
newFormData.append("file", resizedFile);
|
||||
newFormData.append("dirId", formData.get("dirId") as string);
|
||||
|
||||
const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
body: newFormData,
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function GET(request: Request) {
|
||||
where: {
|
||||
id: userLoginId,
|
||||
},
|
||||
select: {
|
||||
include: {
|
||||
Profile: {
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@@ -19,27 +19,30 @@ export type ITypeStatusNotifikasi =
|
||||
| "Menunggu"
|
||||
| "Gagal";
|
||||
|
||||
|
||||
/**
|
||||
* @param kategoriApp | "JOB", "VOTING", "EVENT", "DONASI", "INVESTASI", "COLLABORATION", "FORUM"
|
||||
* @type string
|
||||
*/
|
||||
export type IRealtimeData = {
|
||||
status?: ITypeStatusNotifikasi;
|
||||
appId: string;
|
||||
appId?: string;
|
||||
userId: string;
|
||||
pesan: string;
|
||||
title: string;
|
||||
kategoriApp:
|
||||
pesan?: string;
|
||||
title?: string;
|
||||
kategoriApp?:
|
||||
| "JOB"
|
||||
| "VOTING"
|
||||
| "EVENT"
|
||||
| "DONASI"
|
||||
| "INVESTASI"
|
||||
| "COLLABORATION"
|
||||
| "FORUM";
|
||||
| "FORUM"
|
||||
| "ACCESS"; // Untuk trigger akses user
|
||||
};
|
||||
|
||||
// Access User
|
||||
export const gs_access_user = atom<boolean>(false);
|
||||
|
||||
export const gs_realtimeData = atom<IRealtimeData | null>(null);
|
||||
export const gs_admin_ntf = atom<number>(0);
|
||||
export const gs_user_ntf = atom<number>(0);
|
||||
@@ -63,4 +66,4 @@ export const gs_donasiTriggerBeranda = atom<boolean>(false);
|
||||
|
||||
// investasi
|
||||
export const gs_adminInvestasi_triggerReview = atom<boolean>(false);
|
||||
export const gs_investasiTriggerBeranda = atom<boolean>(false);
|
||||
export const gs_investasiTriggerBeranda = atom<boolean>(false);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useShallowEffect } from "@mantine/hooks";
|
||||
import { useAtom } from "jotai";
|
||||
import { WibuRealtime } from "wibu-pkg";
|
||||
import {
|
||||
gs_access_user,
|
||||
gs_admin_ntf,
|
||||
gs_adminDonasi_triggerReview,
|
||||
gs_adminEvent_triggerReview,
|
||||
@@ -31,6 +32,9 @@ export type TypeNotification = {
|
||||
userId?: string;
|
||||
};
|
||||
|
||||
// Tambahkan flag global untuk mencegah inisialisasi ulang
|
||||
let isWibuRealtimeInitialized = false;
|
||||
|
||||
export default function RealtimeProvider({
|
||||
userId,
|
||||
WIBU_REALTIME_TOKEN,
|
||||
@@ -42,6 +46,9 @@ export default function RealtimeProvider({
|
||||
const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf);
|
||||
const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf);
|
||||
|
||||
// ACCESS USER
|
||||
const [isAccessUser, setIsAccessUser] = useAtom(gs_access_user);
|
||||
|
||||
// JOB
|
||||
const [isTriggerJobBeranda, setIsTriggerJobBeranda] =
|
||||
useAtom(gs_jobTiggerBeranda);
|
||||
@@ -83,152 +90,168 @@ export default function RealtimeProvider({
|
||||
|
||||
useShallowEffect(() => {
|
||||
try {
|
||||
WibuRealtime.init({
|
||||
project: "hipmi",
|
||||
WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN,
|
||||
onData(data: TypeNotification) {
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "ADMIN"
|
||||
) {
|
||||
setNewAdminNtf((e) => e + 1);
|
||||
}
|
||||
if (!isWibuRealtimeInitialized) {
|
||||
WibuRealtime.init({
|
||||
project: "hipmi",
|
||||
WIBU_REALTIME_TOKEN: WIBU_REALTIME_TOKEN,
|
||||
onData(data: TypeNotification) {
|
||||
// Notifikasi ke admin
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "ADMIN"
|
||||
) {
|
||||
setNewAdminNtf((e) => e + 1);
|
||||
}
|
||||
|
||||
// Notifikasi ke semua user , yang datanya di acc admin
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.userId == userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
setDataRealtime(data.dataMessage as any);
|
||||
}
|
||||
// trigger access
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "ACCESS" &&
|
||||
data.dataMessage?.userId == userId
|
||||
) {
|
||||
setIsAccessUser(data.dataMessage.status as any);
|
||||
}
|
||||
|
||||
// ---------------------- JOB ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "JOB"
|
||||
) {
|
||||
setIsAdminJob_TriggerReview(true);
|
||||
}
|
||||
// Notifikasi ke semua user , yang datanya di acc admin
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.userId == userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
setDataRealtime(data.dataMessage as any);
|
||||
}
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "JOB" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerJobBeranda(true);
|
||||
}
|
||||
// ---------------------- JOB ------------------------- //
|
||||
// ---------------------- JOB ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "JOB"
|
||||
) {
|
||||
setIsAdminJob_TriggerReview(true);
|
||||
}
|
||||
|
||||
// ---------------------- EVENT ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "EVENT"
|
||||
) {
|
||||
setIsAdminEvent_TriggerReview(true);
|
||||
}
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "JOB" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerJobBeranda(true);
|
||||
}
|
||||
// ---------------------- JOB ------------------------- //
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "EVENT" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerEventBeranda(true);
|
||||
}
|
||||
// ---------------------- EVENT ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "EVENT"
|
||||
) {
|
||||
setIsAdminEvent_TriggerReview(true);
|
||||
}
|
||||
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.status == "Peserta Event" &&
|
||||
userId !== data.dataMessage?.userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
}
|
||||
// ---------------------- EVENT ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "EVENT" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerEventBeranda(true);
|
||||
}
|
||||
|
||||
// ---------------------- VOTING ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "VOTING"
|
||||
) {
|
||||
setIsAdminVoting_TriggerReview(true);
|
||||
}
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.status == "Peserta Event" &&
|
||||
userId !== data.dataMessage?.userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
}
|
||||
// ---------------------- EVENT ------------------------- //
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "VOTING" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerVotingBeranda(true);
|
||||
}
|
||||
// ---------------------- VOTING ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "VOTING"
|
||||
) {
|
||||
setIsAdminVoting_TriggerReview(true);
|
||||
}
|
||||
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.status == "Voting Masuk" &&
|
||||
userId !== data.dataMessage?.userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
}
|
||||
// ---------------------- VOTING ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "VOTING" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerVotingBeranda(true);
|
||||
}
|
||||
|
||||
// ---------------------- DONASI ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "DONASI"
|
||||
) {
|
||||
setIsAdminDonasi_TriggerReview(true);
|
||||
}
|
||||
if (
|
||||
data.type == "notification" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.status == "Voting Masuk" &&
|
||||
userId !== data.dataMessage?.userId
|
||||
) {
|
||||
setNewUserNtf((e) => e + 1);
|
||||
}
|
||||
// ---------------------- VOTING ------------------------- //
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "DONASI" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerDonasiBeranda(true);
|
||||
}
|
||||
// ---------------------- DONASI ------------------------- //
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "DONASI"
|
||||
) {
|
||||
setIsAdminDonasi_TriggerReview(true);
|
||||
}
|
||||
|
||||
// if (
|
||||
// data.type == "notification" &&
|
||||
// data.pushNotificationTo == "ADMIN" &&
|
||||
// data.dataMessage?.status == "Menunggu" &&
|
||||
// userId !== data.dataMessage?.userId
|
||||
// ) {
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "DONASI" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerDonasiBeranda(true);
|
||||
}
|
||||
|
||||
// }
|
||||
// ---------------------- DONASI ------------------------- //
|
||||
// if (
|
||||
// data.type == "notification" &&
|
||||
// data.pushNotificationTo == "ADMIN" &&
|
||||
// data.dataMessage?.status == "Menunggu" &&
|
||||
// userId !== data.dataMessage?.userId
|
||||
// ) {
|
||||
|
||||
// ---------------------- INVESTASI ------------------------- //
|
||||
// }
|
||||
// ---------------------- DONASI ------------------------- //
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "INVESTASI"
|
||||
) {
|
||||
setIsAdminInvestasi_TriggerReview(true);
|
||||
}
|
||||
// ---------------------- INVESTASI ------------------------- //
|
||||
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "INVESTASI" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerInvestasiBeranda(true);
|
||||
}
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "ADMIN" &&
|
||||
data.dataMessage?.kategoriApp == "INVESTASI"
|
||||
) {
|
||||
setIsAdminInvestasi_TriggerReview(true);
|
||||
}
|
||||
|
||||
// ---------------------- INVESTASI ------------------------- //
|
||||
},
|
||||
});
|
||||
if (
|
||||
data.type == "trigger" &&
|
||||
data.pushNotificationTo == "USER" &&
|
||||
data.dataMessage?.kategoriApp == "INVESTASI" &&
|
||||
data.dataMessage.status == "Publish"
|
||||
) {
|
||||
setIsTriggerInvestasiBeranda(true);
|
||||
}
|
||||
|
||||
// ---------------------- INVESTASI ------------------------- //
|
||||
},
|
||||
});
|
||||
|
||||
// Tandai bahwa WibuRealtime telah diinisialisasi
|
||||
isWibuRealtimeInitialized = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error Realtime:", error);
|
||||
}
|
||||
|
||||
52
src/app/zCoba/upload/fun_upload.ts
Normal file
52
src/app/zCoba/upload/fun_upload.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
"use server";
|
||||
|
||||
import _ from "lodash";
|
||||
// import { v4 } from "uuid";
|
||||
import fs from "fs";
|
||||
import sharp from "sharp";
|
||||
|
||||
export default async function fun_upload({
|
||||
file,
|
||||
dirId,
|
||||
}: {
|
||||
file: File;
|
||||
dirId: string;
|
||||
}) {
|
||||
// const file: any = formData.get("file");
|
||||
// const fName = file.name;
|
||||
// const fileSize = file.size;
|
||||
|
||||
// // Convert ke KB
|
||||
// const fileSizeInKB = fileSize / 1024;
|
||||
|
||||
// // Convert ke MB
|
||||
// const fileSizeInMB = fileSize / (1024 * 1024);
|
||||
|
||||
// console.log(`Ukuran file dalam bytes: ${fileSize}`);
|
||||
// console.log(`Ukuran file dalam KB: ${fileSizeInKB.toFixed(2)} KB`);
|
||||
// console.log(`Ukuran file dalam MB: ${fileSizeInMB.toFixed(2)} MB`);
|
||||
|
||||
const imageBuffer = await file.arrayBuffer();
|
||||
const resize = await sharp(imageBuffer).resize(2000).toBuffer();
|
||||
|
||||
const newFile = Buffer.from(resize);
|
||||
console.log("file new",newFile);
|
||||
// fs.writeFileSync(`./public/upload/${fName}`, upFolder as any);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("dirId", dirId);
|
||||
|
||||
// const upload = await fetch("/api/image/upload", {
|
||||
// method: "POST",
|
||||
// body: formData,
|
||||
// });
|
||||
|
||||
// const res = await upload.json();
|
||||
|
||||
// if (upload.ok) {
|
||||
// return { success: true, data: res.data, message: res.message };
|
||||
// } else {
|
||||
// return { success: false, data: {}, message: res.message };
|
||||
// }
|
||||
}
|
||||
137
src/app/zCoba/upload/page.tsx
Normal file
137
src/app/zCoba/upload/page.tsx
Normal file
@@ -0,0 +1,137 @@
|
||||
"use client";
|
||||
|
||||
import { MainColor } from "@/app_modules/_global/color";
|
||||
import { ComponentGlobal_BoxUploadImage } from "@/app_modules/_global/component";
|
||||
import { MAX_SIZE } from "@/app_modules/_global/lib";
|
||||
import { PemberitahuanMaksimalFile } from "@/app_modules/_global/lib/max_size";
|
||||
import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global";
|
||||
import {
|
||||
UIGlobal_LayoutHeaderTamplate,
|
||||
UIGlobal_LayoutTamplate,
|
||||
} from "@/app_modules/_global/ui";
|
||||
import { clientLogger } from "@/util/clientLogger";
|
||||
import {
|
||||
AspectRatio,
|
||||
Button,
|
||||
Center,
|
||||
FileButton,
|
||||
Image,
|
||||
Stack,
|
||||
} from "@mantine/core";
|
||||
import { IconImageInPicture, IconUpload } from "@tabler/icons-react";
|
||||
import { useState } from "react";
|
||||
import fun_upload from "./fun_upload";
|
||||
import { funGlobal_UploadToStorage } from "@/app_modules/_global/fun";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<UIGlobal_LayoutTamplate
|
||||
header={<UIGlobal_LayoutHeaderTamplate title="Upload" />}
|
||||
>
|
||||
<Upload />
|
||||
</UIGlobal_LayoutTamplate>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Upload() {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [image, setImage] = useState<any | null>(null);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
async function onUpload() {
|
||||
if (!file) return alert("File Kosong");
|
||||
try {
|
||||
setLoading(true);
|
||||
const formData = new FormData();
|
||||
formData.append("file", file as File);
|
||||
|
||||
const uploadPhoto = await funGlobal_UploadToStorage({
|
||||
file: file,
|
||||
dirId: "cm5ohsepe002bq4nlxeejhg7q",
|
||||
});
|
||||
|
||||
if (uploadPhoto.success) {
|
||||
setLoading(false);
|
||||
alert("berhasil upload");
|
||||
console.log("uploadPhoto", uploadPhoto);
|
||||
} else {
|
||||
setLoading(false);
|
||||
console.log("gagal upload", uploadPhoto);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error upload img:", error);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<ComponentGlobal_BoxUploadImage>
|
||||
{image ? (
|
||||
<AspectRatio ratio={1 / 1} mt={5} maw={300} mx={"auto"}>
|
||||
<Image style={{ maxHeight: 250 }} alt="Avatar" src={image} />
|
||||
</AspectRatio>
|
||||
) : (
|
||||
<Center h={"100%"}>
|
||||
<IconImageInPicture size={50} />
|
||||
</Center>
|
||||
)}
|
||||
</ComponentGlobal_BoxUploadImage>
|
||||
|
||||
<Center>
|
||||
<FileButton
|
||||
onChange={async (files: any | null) => {
|
||||
try {
|
||||
const buffer = URL.createObjectURL(
|
||||
new Blob([new Uint8Array(await files.arrayBuffer())])
|
||||
);
|
||||
|
||||
// if (files.size > MAX_SIZE) {
|
||||
// ComponentGlobal_NotifikasiPeringatan(
|
||||
// PemberitahuanMaksimalFile
|
||||
// );
|
||||
// return;
|
||||
// } else {
|
||||
|
||||
// }
|
||||
|
||||
console.log("ini buffer", buffer);
|
||||
|
||||
setFile(files);
|
||||
setImage(buffer);
|
||||
} catch (error) {
|
||||
clientLogger.error("Upload error:", error);
|
||||
}
|
||||
}}
|
||||
accept="image/png,image/jpeg"
|
||||
>
|
||||
{(props) => (
|
||||
<Button
|
||||
{...props}
|
||||
radius={"sm"}
|
||||
leftIcon={<IconUpload />}
|
||||
bg={MainColor.yellow}
|
||||
color="yellow"
|
||||
c={"black"}
|
||||
>
|
||||
Upload
|
||||
</Button>
|
||||
)}
|
||||
</FileButton>
|
||||
</Center>
|
||||
|
||||
<Button
|
||||
loaderPosition="center"
|
||||
loading={isLoading}
|
||||
onClick={() => {
|
||||
onUpload();
|
||||
}}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user