Merge pull request #202 from bipproduction/featur/logserver
Featur/logserver
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -34,6 +34,9 @@ yarn-error.log*
|
|||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.2.30](https://github.com/bipproduction/hipmi/compare/v1.2.29...v1.2.30) (2024-12-16)
|
||||||
|
|
||||||
|
## [1.2.29](https://github.com/bipproduction/hipmi/compare/v1.2.28...v1.2.29) (2024-12-13)
|
||||||
|
|
||||||
|
## [1.2.28](https://github.com/bipproduction/hipmi/compare/v1.2.27...v1.2.28) (2024-12-12)
|
||||||
|
|
||||||
## [1.2.27](https://github.com/bipproduction/hipmi/compare/v1.2.26...v1.2.27) (2024-12-12)
|
## [1.2.27](https://github.com/bipproduction/hipmi/compare/v1.2.26...v1.2.27) (2024-12-12)
|
||||||
|
|
||||||
## [1.2.26](https://github.com/bipproduction/hipmi/compare/v1.2.25...v1.2.26) (2024-12-12)
|
## [1.2.26](https://github.com/bipproduction/hipmi/compare/v1.2.25...v1.2.26) (2024-12-12)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hipmi",
|
"name": "hipmi",
|
||||||
"version": "1.2.27",
|
"version": "1.2.30",
|
||||||
"private": true,
|
"private": true,
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"seed": "npx tsx prisma/seed.ts --yes"
|
"seed": "npx tsx prisma/seed.ts --yes"
|
||||||
@@ -50,6 +50,7 @@
|
|||||||
"autoprefixer": "10.4.14",
|
"autoprefixer": "10.4.14",
|
||||||
"bufferutil": "^4.0.8",
|
"bufferutil": "^4.0.8",
|
||||||
"bun": "^1.1.38",
|
"bun": "^1.1.38",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
@@ -95,6 +96,8 @@
|
|||||||
"wibu": "bipproduction/wibu",
|
"wibu": "bipproduction/wibu",
|
||||||
"wibu-cli": "^1.0.91",
|
"wibu-cli": "^1.0.91",
|
||||||
"wibu-pkg": "^1.0.3",
|
"wibu-pkg": "^1.0.3",
|
||||||
|
"winston": "^3.17.0",
|
||||||
|
"winston-daily-rotate-file": "^5.0.0",
|
||||||
"yaml": "^2.3.2"
|
"yaml": "^2.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/app/api/image/delete/route.ts
Normal file
53
src/app/api/image/delete/route.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get";
|
||||||
|
import backendLogger from "@/util/backendLogger";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function DELETE(req: Request) {
|
||||||
|
const data = await req.json();
|
||||||
|
const id = data.fileId;
|
||||||
|
const dirId = data.dirId;
|
||||||
|
|
||||||
|
const keyOfDirectory = await funGetDirectoryNameByValue({
|
||||||
|
value: dirId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (req.method === "DELETE") {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`https://wibu-storage.wibudev.com/api/files/${id}/delete`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
backendLogger.info("Server status code: " + res.status);
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok) {
|
||||||
|
backendLogger.info(
|
||||||
|
`Success delete ${keyOfDirectory}`
|
||||||
|
);
|
||||||
|
return NextResponse.json({ success: true });
|
||||||
|
} else {
|
||||||
|
const errorText = await res.json();
|
||||||
|
backendLogger.error(
|
||||||
|
`Failed delete ${keyOfDirectory}: ` + errorText.message
|
||||||
|
);
|
||||||
|
return NextResponse.json({
|
||||||
|
success: false,
|
||||||
|
message: errorText.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
backendLogger.error(`Delete error ${keyOfDirectory}:`, error);
|
||||||
|
return NextResponse.json({
|
||||||
|
success: false,
|
||||||
|
message: "An unexpected error occurred",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`);
|
||||||
|
return NextResponse.json({ success: false, message: "Method not allowed" });
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/app/api/image/upload/route.ts
Normal file
58
src/app/api/image/upload/route.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get";
|
||||||
|
import backendLogger from "@/util/backendLogger";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const formData = await request.formData();
|
||||||
|
|
||||||
|
const valueOfDir = formData.get("dirId");
|
||||||
|
const keyOfDirectory = await funGetDirectoryNameByValue({
|
||||||
|
value: valueOfDir as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (request.method === "POST") {
|
||||||
|
try {
|
||||||
|
const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
backendLogger.info("Server status code: " + res.status);
|
||||||
|
const dataRes = await res.json();
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
backendLogger.info(
|
||||||
|
`Success upload ${keyOfDirectory}: ${JSON.stringify(dataRes.data)}`
|
||||||
|
);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: true, data: dataRes.data },
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const errorText = await res.text();
|
||||||
|
backendLogger.error(`Failed upload ${keyOfDirectory}: ${errorText}`);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: errorText },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
backendLogger.error(`Error upload ${keyOfDirectory}: ${error}`);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: "An unexpected error occurred",
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: "Method not allowed" },
|
||||||
|
{ status: 405 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import { NextResponse } from "next/server";
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const WS_APIKEY = process.env.WS_APIKEY;
|
|
||||||
console.log(WS_APIKEY);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const formData = await request.formData();
|
|
||||||
|
|
||||||
const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
|
||||||
method: "POST",
|
|
||||||
body: formData,
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// if (res.ok) {
|
|
||||||
// console.log("Berhasil");
|
|
||||||
// const hasil = await res.json();
|
|
||||||
// return { success: true, data: hasil.data };
|
|
||||||
// } else {
|
|
||||||
// const errorText = await res.text();
|
|
||||||
// return { success: false, data: {} };
|
|
||||||
// }
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
|
||||||
// method: "POST",
|
|
||||||
// body: formData,
|
|
||||||
// headers: {
|
|
||||||
// Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (res.ok) {
|
|
||||||
// const hasil = await res.json();
|
|
||||||
// return { success: true, data: hasil.data };
|
|
||||||
// } else {
|
|
||||||
// const errorText = await res.text();
|
|
||||||
// return { success: false, data: {} };
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.error("Upload error:", error);
|
|
||||||
// return { success: false, data: {} };
|
|
||||||
// }
|
|
||||||
|
|
||||||
return NextResponse.json({ success: true });
|
|
||||||
}
|
|
||||||
@@ -33,7 +33,7 @@ export default function Page() {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", filePP as any);
|
formData.append("file", filePP as any);
|
||||||
|
|
||||||
const res = await fetch("/api/upload", {
|
const res = await fetch("/api/image/upload", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,24 +1,50 @@
|
|||||||
export async function funGlobal_DeleteFileById({ fileId }: { fileId: string }) {
|
import { clientLogger } from "@/util/clientLogger";
|
||||||
try {
|
|
||||||
const res = await fetch(
|
|
||||||
`https://wibu-storage.wibudev.com/api/files/${fileId}/delete`,
|
|
||||||
{
|
|
||||||
method: "DELETE",
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.ok) {
|
export async function funGlobal_DeleteFileById({
|
||||||
const hasil = await res.json();
|
fileId,
|
||||||
return { success: true };
|
dirId,
|
||||||
|
}: {
|
||||||
|
fileId: string;
|
||||||
|
dirId?: string;
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/image/delete", {
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify({ fileId, dirId }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
clientLogger.info(`File ${fileId} deleted successfully`);
|
||||||
|
return { success: true, message: "File berhasil dihapus" };
|
||||||
} else {
|
} else {
|
||||||
const errorText = await res.json();
|
return { success: false, message: data.message };
|
||||||
return { success: false };
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false };
|
|
||||||
console.error("Upload error:", error);
|
console.error("Upload error:", error);
|
||||||
|
return { success: false, message: "An unexpected error occurred" };
|
||||||
}
|
}
|
||||||
|
// try {
|
||||||
|
// const res = await fetch(
|
||||||
|
// `https://wibu-storage.wibudev.com/api/files/${fileId}/delete`,
|
||||||
|
// {
|
||||||
|
// method: "DELETE",
|
||||||
|
// headers: {
|
||||||
|
// Authorization: `Bearer ${process.env.WS_APIKEY}`,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (res.ok) {
|
||||||
|
// const hasil = await res.json();
|
||||||
|
// return { success: true, message: "File berhasil dihapus" };
|
||||||
|
// } else {
|
||||||
|
// const errorText = await res.json();
|
||||||
|
// return { success: false, message: errorText.message };
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Upload error:", error);
|
||||||
|
// return { success: false, message: "An unexpected error occurred" };
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/app_modules/_global/fun/get/fun_get_directory_name.ts
Normal file
11
src/app_modules/_global/fun/get/fun_get_directory_name.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { DIRECTORY_ID } from "@/app/lib";
|
||||||
|
|
||||||
|
export async function funGetDirectoryNameByValue({
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
value?: string | null;
|
||||||
|
}) {
|
||||||
|
if (!value) return null;
|
||||||
|
const object: any = DIRECTORY_ID;
|
||||||
|
return Object.keys(object).find((key) => object[key] === value);
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { funGlobal_CheckProfile } from "./fun_check_profile";
|
import { funGlobal_CheckProfile } from "./fun_check_profile";
|
||||||
|
import { funGetDirectoryNameByValue } from "./fun_get_directory_name";
|
||||||
import { funGlobal_getNomorAdmin } from "./fun_get_nomor_admin";
|
import { funGlobal_getNomorAdmin } from "./fun_get_nomor_admin";
|
||||||
import { funGetUserIdByToken } from "./fun_get_user_id_by_token";
|
import { funGetUserIdByToken } from "./fun_get_user_id_by_token";
|
||||||
import { funGlobal_getMasterKategoriApp } from "./fun_master_kategori_app";
|
import { funGlobal_getMasterKategoriApp } from "./fun_master_kategori_app";
|
||||||
@@ -8,3 +8,4 @@ export { funGlobal_getMasterKategoriApp };
|
|||||||
export { funGlobal_getNomorAdmin };
|
export { funGlobal_getNomorAdmin };
|
||||||
export { funGetUserIdByToken };
|
export { funGetUserIdByToken };
|
||||||
export { funGlobal_CheckProfile };
|
export { funGlobal_CheckProfile };
|
||||||
|
export { funGetDirectoryNameByValue };
|
||||||
|
|||||||
@@ -32,40 +32,20 @@ export async function funGlobal_UploadToStorage({
|
|||||||
console.error("File terlalu besar");
|
console.error("File terlalu besar");
|
||||||
return { success: false, message: "File size exceeds limit" };
|
return { success: false, message: "File size exceeds limit" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 30000); // Timeout 30 detik
|
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("file", file);
|
formData.append("file", file);
|
||||||
formData.append("dirId", dirId);
|
formData.append("dirId", dirId);
|
||||||
|
|
||||||
try {
|
const upload = await fetch("/api/image/upload", {
|
||||||
const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
method: "POST",
|
||||||
method: "POST",
|
body: formData,
|
||||||
body: formData,
|
});
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${Env_WS_APIKEY}`,
|
|
||||||
},
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeoutId); // Bersihkan timeout jika selesai tepat waktu
|
const res = await upload.json();
|
||||||
|
|
||||||
if (res.ok) {
|
if (upload.ok) {
|
||||||
const dataRes = await res.json();
|
return { success: true, data: res.data, message: res.message };
|
||||||
// const cekLog = await res.text();
|
} else {
|
||||||
// console.log(cekLog);
|
return { success: false, data: {}, message: res.message };
|
||||||
return { success: true, data: dataRes.data };
|
|
||||||
} else {
|
|
||||||
const errorText = await res.text();
|
|
||||||
console.error("Error:", errorText);
|
|
||||||
return { success: false, message: errorText };
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
clearTimeout(timeoutId); //
|
|
||||||
|
|
||||||
console.error("Error:", error);
|
|
||||||
return { success: false, message: "An unexpected error occurred" };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { DIRECTORY_ID } from "@/app/lib";
|
import { ComponentGlobal_ErrorInput } from "@/app_modules/_global/component";
|
||||||
import { MainColor } from "@/app_modules/_global/color";
|
import { Select, Stack, TextInput } from "@mantine/core";
|
||||||
import {
|
import { IconAt } from "@tabler/icons-react";
|
||||||
ComponentGlobal_BoxInformation,
|
|
||||||
ComponentGlobal_BoxUploadImage,
|
|
||||||
ComponentGlobal_ErrorInput,
|
|
||||||
} from "@/app_modules/_global/component";
|
|
||||||
import {
|
|
||||||
funGlobal_DeleteFileById,
|
|
||||||
funGlobal_UploadToStorage,
|
|
||||||
} from "@/app_modules/_global/fun";
|
|
||||||
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 {
|
|
||||||
AspectRatio,
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Center,
|
|
||||||
FileButton,
|
|
||||||
Image,
|
|
||||||
Paper,
|
|
||||||
Select,
|
|
||||||
Stack,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import { IconAt, IconCamera, IconUpload } from "@tabler/icons-react";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { gmailRegex } from "../../component/regular_expressions";
|
import { gmailRegex } from "../../component/regular_expressions";
|
||||||
import { Profile_ComponentCreateNewProfile } from "../_component";
|
import { Profile_ComponentCreateNewProfile } from "../_component";
|
||||||
|
import Profile_ViewUploadBackground from "./view_upload_background";
|
||||||
|
import Profile_ViewUploadFoto from "./view_upload_foto";
|
||||||
|
|
||||||
export default function CreateProfile() {
|
export default function CreateProfile() {
|
||||||
const [filePP, setFilePP] = useState<File | null>(null);
|
|
||||||
const [imgPP, setImgPP] = useState<any | null>();
|
const [imgPP, setImgPP] = useState<any | null>();
|
||||||
const [fileBG, setFileBG] = useState<File | null>(null);
|
|
||||||
const [imgBG, setImgBG] = useState<any | null>();
|
const [imgBG, setImgBG] = useState<any | null>();
|
||||||
const [fotoProfileId, setFotoProfileId] = useState("");
|
const [fotoProfileId, setFotoProfileId] = useState("");
|
||||||
const [backgroundProfileId, setBackgroundProfileId] = useState("");
|
const [backgroundProfileId, setBackgroundProfileId] = useState("");
|
||||||
@@ -51,223 +25,19 @@ export default function CreateProfile() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack px={"sm"} spacing={40}>
|
<Stack px={"sm"} spacing={40}>
|
||||||
<Box>
|
<Profile_ViewUploadFoto
|
||||||
<Stack spacing={"lg"}>
|
imgPP={imgPP}
|
||||||
<ComponentGlobal_BoxInformation informasi="Upload foto profile anda." />
|
onSetImgPP={setImgPP}
|
||||||
<Center>
|
fotoProfileId={fotoProfileId}
|
||||||
{imgPP ? (
|
onSetFotoProfileId={setFotoProfileId}
|
||||||
<Paper shadow="lg" radius={"100%"}>
|
/>
|
||||||
<Avatar
|
|
||||||
color={"cyan"}
|
|
||||||
sx={{
|
|
||||||
borderStyle: "solid",
|
|
||||||
borderColor: "gray",
|
|
||||||
borderWidth: "0.5px",
|
|
||||||
}}
|
|
||||||
src={imgPP ? imgPP : "/aset/global/avatar.png"}
|
|
||||||
size={150}
|
|
||||||
radius={"100%"}
|
|
||||||
/>
|
|
||||||
</Paper>
|
|
||||||
) : (
|
|
||||||
<Paper shadow="lg" radius={"100%"}>
|
|
||||||
<Avatar
|
|
||||||
variant="light"
|
|
||||||
color="blue"
|
|
||||||
size={150}
|
|
||||||
radius={"100%"}
|
|
||||||
sx={{
|
|
||||||
borderStyle: "solid",
|
|
||||||
borderColor: MainColor.darkblue,
|
|
||||||
borderWidth: "0.5px",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Paper>
|
|
||||||
)}
|
|
||||||
</Center>
|
|
||||||
|
|
||||||
<Center>
|
<Profile_ViewUploadBackground
|
||||||
<FileButton
|
imgBG={imgBG}
|
||||||
onChange={async (files: any | null) => {
|
backgroundProfileId={backgroundProfileId}
|
||||||
try {
|
onSetImgBG={setImgBG}
|
||||||
const buffer = URL.createObjectURL(
|
onSetBackgroundProfileId={setBackgroundProfileId}
|
||||||
new Blob([new Uint8Array(await files.arrayBuffer())])
|
/>
|
||||||
);
|
|
||||||
|
|
||||||
if (files.size > MAX_SIZE) {
|
|
||||||
ComponentGlobal_NotifikasiPeringatan(
|
|
||||||
PemberitahuanMaksimalFile
|
|
||||||
);
|
|
||||||
setImgPP(null);
|
|
||||||
setFilePP(null);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fotoProfileId != "") {
|
|
||||||
const deleteFotoProfile = await funGlobal_DeleteFileById({
|
|
||||||
fileId: fotoProfileId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (deleteFotoProfile.success) {
|
|
||||||
setFotoProfileId("");
|
|
||||||
|
|
||||||
const uploadPhoto = await funGlobal_UploadToStorage({
|
|
||||||
file: files,
|
|
||||||
dirId: DIRECTORY_ID.profile_foto,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uploadPhoto.success) {
|
|
||||||
setFotoProfileId(uploadPhoto.data.id);
|
|
||||||
setImgPP(buffer);
|
|
||||||
setFilePP(files);
|
|
||||||
} else {
|
|
||||||
ComponentGlobal_NotifikasiPeringatan(
|
|
||||||
"Gagal upload foto profile"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const uploadPhoto = await funGlobal_UploadToStorage({
|
|
||||||
file: files,
|
|
||||||
dirId: DIRECTORY_ID.profile_foto,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uploadPhoto.success) {
|
|
||||||
setFotoProfileId(uploadPhoto.data.id);
|
|
||||||
setImgPP(buffer);
|
|
||||||
setFilePP(files);
|
|
||||||
} else {
|
|
||||||
ComponentGlobal_NotifikasiPeringatan(
|
|
||||||
"Gagal upload foto profile"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
accept="image/png,image/jpeg"
|
|
||||||
>
|
|
||||||
{(props) => (
|
|
||||||
<Button
|
|
||||||
{...props}
|
|
||||||
radius={"xl"}
|
|
||||||
leftIcon={<IconCamera />}
|
|
||||||
bg={MainColor.yellow}
|
|
||||||
color="yellow"
|
|
||||||
c={"black"}
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</FileButton>
|
|
||||||
</Center>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box>
|
|
||||||
<Stack spacing={"lg"}>
|
|
||||||
<ComponentGlobal_BoxInformation informasi="Upload foto latar belakang profile anda." />
|
|
||||||
<ComponentGlobal_BoxUploadImage>
|
|
||||||
{imgBG ? (
|
|
||||||
<AspectRatio ratio={1 / 1} mah={265} mx={"auto"}>
|
|
||||||
<Image
|
|
||||||
style={{ maxHeight: 250, margin: "auto", padding: "5px" }}
|
|
||||||
alt="Foto"
|
|
||||||
height={250}
|
|
||||||
src={imgBG ? imgBG : "/aset/no-img.png"}
|
|
||||||
/>
|
|
||||||
</AspectRatio>
|
|
||||||
) : (
|
|
||||||
<Stack justify="center" align="center" h={"100%"}>
|
|
||||||
<IconUpload color="white" />
|
|
||||||
<Text fz={"xs"} c={"white"}>
|
|
||||||
Upload Background
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
)}
|
|
||||||
</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
|
|
||||||
);
|
|
||||||
setImgBG(null);
|
|
||||||
setFileBG(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backgroundProfileId != "") {
|
|
||||||
const deleteFotoBg = await funGlobal_DeleteFileById({
|
|
||||||
fileId: backgroundProfileId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (deleteFotoBg.success) {
|
|
||||||
setBackgroundProfileId("");
|
|
||||||
|
|
||||||
const uploadBackground =
|
|
||||||
await funGlobal_UploadToStorage({
|
|
||||||
file: files,
|
|
||||||
dirId: DIRECTORY_ID.profile_background,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uploadBackground.success) {
|
|
||||||
setBackgroundProfileId(uploadBackground.data.id);
|
|
||||||
setImgBG(buffer);
|
|
||||||
setFileBG(files);
|
|
||||||
} else {
|
|
||||||
ComponentGlobal_NotifikasiPeringatan(
|
|
||||||
"Gagal upload background profile"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const uploadBackground = await funGlobal_UploadToStorage({
|
|
||||||
file: files,
|
|
||||||
dirId: DIRECTORY_ID.profile_background,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uploadBackground.success) {
|
|
||||||
setBackgroundProfileId(uploadBackground.data.id);
|
|
||||||
setImgBG(buffer);
|
|
||||||
setFileBG(files);
|
|
||||||
} else {
|
|
||||||
ComponentGlobal_NotifikasiPeringatan(
|
|
||||||
"Gagal upload background profile"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
accept="image/png,image/jpeg"
|
|
||||||
>
|
|
||||||
{(props) => (
|
|
||||||
<Button
|
|
||||||
{...props}
|
|
||||||
radius={"xl"}
|
|
||||||
leftIcon={<IconCamera />}
|
|
||||||
bg={MainColor.yellow}
|
|
||||||
color="yellow"
|
|
||||||
c={"black"}
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</FileButton>
|
|
||||||
</Center>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Stack mb={"lg"}>
|
<Stack mb={"lg"}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import { DIRECTORY_ID } from "@/app/lib";
|
||||||
|
import { MainColor } from "@/app_modules/_global/color";
|
||||||
|
import {
|
||||||
|
ComponentGlobal_BoxInformation,
|
||||||
|
ComponentGlobal_BoxUploadImage,
|
||||||
|
} from "@/app_modules/_global/component";
|
||||||
|
import {
|
||||||
|
funGlobal_DeleteFileById,
|
||||||
|
funGlobal_UploadToStorage,
|
||||||
|
} from "@/app_modules/_global/fun";
|
||||||
|
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 { clientLogger } from "@/util/clientLogger";
|
||||||
|
import {
|
||||||
|
AspectRatio,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
FileButton,
|
||||||
|
Image,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Loader,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { IconCamera, IconUpload } from "@tabler/icons-react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export default function Profile_ViewUploadBackground({
|
||||||
|
imgBG,
|
||||||
|
onSetImgBG,
|
||||||
|
backgroundProfileId,
|
||||||
|
onSetBackgroundProfileId,
|
||||||
|
}: {
|
||||||
|
imgBG: string;
|
||||||
|
onSetImgBG: (img: string | null) => void;
|
||||||
|
backgroundProfileId: string;
|
||||||
|
onSetBackgroundProfileId: (id: string) => void;
|
||||||
|
}) {
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box>
|
||||||
|
<Stack spacing={"lg"}>
|
||||||
|
<ComponentGlobal_BoxInformation informasi="Upload foto latar belakang profile anda." />
|
||||||
|
<ComponentGlobal_BoxUploadImage>
|
||||||
|
{isLoading ? (
|
||||||
|
<Center h={"100%"}>
|
||||||
|
<Loader variant="oval" size={50} color="cyan" />
|
||||||
|
</Center>
|
||||||
|
) : imgBG ? (
|
||||||
|
<AspectRatio ratio={1 / 1} mah={265} mx={"auto"}>
|
||||||
|
<Image
|
||||||
|
style={{ maxHeight: 250, margin: "auto", padding: "5px" }}
|
||||||
|
alt="Foto"
|
||||||
|
height={250}
|
||||||
|
src={imgBG}
|
||||||
|
/>
|
||||||
|
</AspectRatio>
|
||||||
|
) : (
|
||||||
|
<Stack justify="center" align="center" h={"100%"}>
|
||||||
|
<IconUpload color="white" />
|
||||||
|
<Text fz={"xs"} c={"white"}>
|
||||||
|
Upload Background
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</ComponentGlobal_BoxUploadImage>
|
||||||
|
|
||||||
|
<Center>
|
||||||
|
<FileButton
|
||||||
|
onChange={async (files: any | null) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const buffer = URL.createObjectURL(
|
||||||
|
new Blob([new Uint8Array(await files.arrayBuffer())])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (files.size > MAX_SIZE) {
|
||||||
|
ComponentGlobal_NotifikasiPeringatan(
|
||||||
|
PemberitahuanMaksimalFile
|
||||||
|
);
|
||||||
|
onSetImgBG(null);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backgroundProfileId != "") {
|
||||||
|
const deleteFotoBg = await funGlobal_DeleteFileById({
|
||||||
|
fileId: backgroundProfileId,
|
||||||
|
dirId: DIRECTORY_ID.profile_background,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deleteFotoBg.success) {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed delete background:" +
|
||||||
|
deleteFotoBg.message
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteFotoBg.success) {
|
||||||
|
onSetBackgroundProfileId("");
|
||||||
|
onSetImgBG(null);
|
||||||
|
|
||||||
|
const uploadBackground = await funGlobal_UploadToStorage({
|
||||||
|
file: files,
|
||||||
|
dirId: DIRECTORY_ID.profile_background,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadBackground.success) {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed upload background:" +
|
||||||
|
uploadBackground.message
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadBackground.success) {
|
||||||
|
onSetBackgroundProfileId(uploadBackground.data.id);
|
||||||
|
onSetImgBG(buffer);
|
||||||
|
} else {
|
||||||
|
ComponentGlobal_NotifikasiPeringatan(
|
||||||
|
"Gagal upload background profile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const uploadBackground = await funGlobal_UploadToStorage({
|
||||||
|
file: files,
|
||||||
|
dirId: DIRECTORY_ID.profile_background,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (uploadBackground.success) {
|
||||||
|
onSetBackgroundProfileId(uploadBackground.data.id);
|
||||||
|
onSetImgBG(buffer);
|
||||||
|
} else {
|
||||||
|
ComponentGlobal_NotifikasiPeringatan(
|
||||||
|
"Gagal upload background profile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clientLogger.error("Client error upload background:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
accept="image/png,image/jpeg"
|
||||||
|
>
|
||||||
|
{(props) => (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
radius={"xl"}
|
||||||
|
leftIcon={<IconCamera />}
|
||||||
|
bg={MainColor.yellow}
|
||||||
|
color="yellow"
|
||||||
|
c={"black"}
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</FileButton>
|
||||||
|
</Center>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
216
src/app_modules/katalog/profile/create/view_upload_foto.tsx
Normal file
216
src/app_modules/katalog/profile/create/view_upload_foto.tsx
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import { DIRECTORY_ID } from "@/app/lib";
|
||||||
|
import { MainColor } from "@/app_modules/_global/color";
|
||||||
|
import { ComponentGlobal_BoxInformation } from "@/app_modules/_global/component";
|
||||||
|
import {
|
||||||
|
funGlobal_DeleteFileById,
|
||||||
|
funGlobal_UploadToStorage,
|
||||||
|
} from "@/app_modules/_global/fun";
|
||||||
|
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 { clientLogger } from "@/util/clientLogger";
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
FileButton,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
} from "@mantine/core";
|
||||||
|
import { IconCamera } from "@tabler/icons-react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
export default function Profile_ViewUploadFoto({
|
||||||
|
imgPP,
|
||||||
|
onSetImgPP,
|
||||||
|
fotoProfileId,
|
||||||
|
onSetFotoProfileId,
|
||||||
|
}: {
|
||||||
|
imgPP: string | null | undefined;
|
||||||
|
onSetImgPP: (img: string | null) => void;
|
||||||
|
fotoProfileId: string;
|
||||||
|
onSetFotoProfileId: (id: string) => void;
|
||||||
|
}) {
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box>
|
||||||
|
<Stack spacing={"lg"}>
|
||||||
|
<ComponentGlobal_BoxInformation informasi="Upload foto profile anda dengan ukuran maksimal file 3 MB." />
|
||||||
|
<Center>
|
||||||
|
{isLoading ? (
|
||||||
|
<Paper shadow="lg" radius={"100%"}>
|
||||||
|
<Avatar
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
size={150}
|
||||||
|
radius={"100%"}
|
||||||
|
sx={{
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderColor: MainColor.darkblue,
|
||||||
|
borderWidth: "0.5px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Center>
|
||||||
|
<Loader color="cyan" size="xl" />
|
||||||
|
</Center>
|
||||||
|
</Avatar>
|
||||||
|
</Paper>
|
||||||
|
) : imgPP != undefined || imgPP != null ? (
|
||||||
|
<Paper shadow="lg" radius={"100%"}>
|
||||||
|
<Avatar
|
||||||
|
color={"cyan"}
|
||||||
|
sx={{
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderColor: "gray",
|
||||||
|
borderWidth: "0.5px",
|
||||||
|
}}
|
||||||
|
src={imgPP}
|
||||||
|
size={150}
|
||||||
|
radius={"100%"}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
) : (
|
||||||
|
<Paper shadow="lg" radius={"100%"}>
|
||||||
|
<Avatar
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
size={150}
|
||||||
|
radius={"100%"}
|
||||||
|
sx={{
|
||||||
|
borderStyle: "solid",
|
||||||
|
borderColor: MainColor.darkblue,
|
||||||
|
borderWidth: "0.5px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Center>
|
||||||
|
|
||||||
|
<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
|
||||||
|
);
|
||||||
|
onSetImgPP(null);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fotoProfileId != "") {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const deleteFotoProfile = await funGlobal_DeleteFileById({
|
||||||
|
fileId: fotoProfileId,
|
||||||
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deleteFotoProfile.success) {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed delete photo profile:" +
|
||||||
|
deleteFotoProfile.message
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteFotoProfile.success) {
|
||||||
|
onSetFotoProfileId("");
|
||||||
|
onSetImgPP(null);
|
||||||
|
|
||||||
|
const uploadPhoto = await funGlobal_UploadToStorage({
|
||||||
|
file: files,
|
||||||
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadPhoto.success) {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed upload photo profile::" +
|
||||||
|
uploadPhoto.message
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadPhoto.success) {
|
||||||
|
clientLogger.info(
|
||||||
|
"Client success upload foto profile"
|
||||||
|
);
|
||||||
|
onSetFotoProfileId(uploadPhoto.data.id);
|
||||||
|
onSetImgPP(buffer);
|
||||||
|
} else {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed upload foto:",
|
||||||
|
uploadPhoto.message
|
||||||
|
);
|
||||||
|
ComponentGlobal_NotifikasiPeringatan(
|
||||||
|
"Gagal upload foto profile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clientLogger.error("Client error upload foto:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const uploadPhoto = await funGlobal_UploadToStorage({
|
||||||
|
file: files,
|
||||||
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (uploadPhoto.success) {
|
||||||
|
clientLogger.info("Client success upload foto profile");
|
||||||
|
onSetFotoProfileId(uploadPhoto.data.id);
|
||||||
|
onSetImgPP(buffer);
|
||||||
|
} else {
|
||||||
|
clientLogger.error(
|
||||||
|
"Client failed upload foto:",
|
||||||
|
uploadPhoto.message
|
||||||
|
);
|
||||||
|
ComponentGlobal_NotifikasiPeringatan(
|
||||||
|
"Gagal upload foto profile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clientLogger.error("Client error upload foto:", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
clientLogger.error("Client error upload foto:", error);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
accept="image/png,image/jpeg"
|
||||||
|
>
|
||||||
|
{(props) => (
|
||||||
|
<Button
|
||||||
|
{...props}
|
||||||
|
radius={"xl"}
|
||||||
|
leftIcon={<IconCamera />}
|
||||||
|
bg={MainColor.yellow}
|
||||||
|
color="yellow"
|
||||||
|
c={"black"}
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</FileButton>
|
||||||
|
</Center>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import prisma from "@/app/lib/prisma";
|
import prisma from "@/app/lib/prisma";
|
||||||
import { RouterHome } from "@/app/lib/router_hipmi/router_home";
|
import { RouterHome } from "@/app/lib/router_hipmi/router_home";
|
||||||
import { funGetUserIdByToken } from "@/app_modules/_global/fun/get";
|
import { funGetUserIdByToken } from "@/app_modules/_global/fun/get";
|
||||||
|
import backendLogger from "@/util/backendLogger";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { revalidatePath } from "next/cache";
|
import { revalidatePath } from "next/cache";
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ export default async function funCreateNewProfile({
|
|||||||
const userLoginId = await funGetUserIdByToken();
|
const userLoginId = await funGetUserIdByToken();
|
||||||
|
|
||||||
if (!userLoginId) {
|
if (!userLoginId) {
|
||||||
|
backendLogger.error("User tidak terautentikasi");
|
||||||
return { status: 400, message: "User tidak terautentikasi" }; // Validasi user login
|
return { status: 400, message: "User tidak terautentikasi" }; // Validasi user login
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ export default async function funCreateNewProfile({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!createProfile) {
|
if (!createProfile) {
|
||||||
|
backendLogger.error("Gagal membuat profile");
|
||||||
return { status: 400, message: "Gagal membuat profile" };
|
return { status: 400, message: "Gagal membuat profile" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +64,7 @@ export default async function funCreateNewProfile({
|
|||||||
message: "Berhasil",
|
message: "Berhasil",
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error creating profile:", error);
|
backendLogger.error("Terjadi kesalahan pada server", error);
|
||||||
return { status: 500, message: "Terjadi kesalahan pada server" };
|
return { status: 500, message: "Terjadi kesalahan pada server" };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ async function coba_ButtonFileUpload({
|
|||||||
formData.append("dirId", dirId);
|
formData.append("dirId", dirId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch("https://wibu-storage.wibudev.com/api/upload", {
|
const res = await fetch("https://wibu-storage.wibudev.com/api/image/upload", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData,
|
body: formData,
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ const middlewareConfig: MiddlewareConfig = {
|
|||||||
userPath: "/dev/home",
|
userPath: "/dev/home",
|
||||||
publicRoutes: [
|
publicRoutes: [
|
||||||
"/",
|
"/",
|
||||||
"/api/upload",
|
"/api/logs/*",
|
||||||
|
"/api/image/*",
|
||||||
"/api/job/*",
|
"/api/job/*",
|
||||||
"/api/validation",
|
"/api/validation",
|
||||||
"/api/auth/*",
|
"/api/auth/*",
|
||||||
|
|||||||
37
src/util/backendLogger.ts
Normal file
37
src/util/backendLogger.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// src/utils/backendLogger.ts
|
||||||
|
import winston from "winston";
|
||||||
|
import DailyRotateFile from "winston-daily-rotate-file";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const backendLogger = winston.createLogger({
|
||||||
|
level: "info",
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.timestamp(),
|
||||||
|
winston.format.json()
|
||||||
|
),
|
||||||
|
transports: [
|
||||||
|
// Error logs
|
||||||
|
new DailyRotateFile({
|
||||||
|
filename: path.join("logs/backend/error-%DATE%.log"),
|
||||||
|
datePattern: "YYYY-MM-DD",
|
||||||
|
level: "error",
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: "20m",
|
||||||
|
maxFiles: "14d",
|
||||||
|
}),
|
||||||
|
// Combined logs
|
||||||
|
new DailyRotateFile({
|
||||||
|
filename: path.join("logs/backend/combined-%DATE%.log"),
|
||||||
|
datePattern: "YYYY-MM-DD",
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: "20m",
|
||||||
|
maxFiles: "14d",
|
||||||
|
}),
|
||||||
|
// Console output in development
|
||||||
|
...(process.env.NODE_ENV !== "production"
|
||||||
|
? [new winston.transports.Console()]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default backendLogger;
|
||||||
87
src/util/clientLogger.ts
Normal file
87
src/util/clientLogger.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// src/utils/clientLogger.ts
|
||||||
|
interface LogEntry {
|
||||||
|
level: "info" | "warn" | "error";
|
||||||
|
message: string;
|
||||||
|
data?: any;
|
||||||
|
timestamp?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientLogger {
|
||||||
|
private queue: LogEntry[] = [];
|
||||||
|
private readonly maxQueueSize: number = 10;
|
||||||
|
private readonly apiEndpoint: string = "/api/logs";
|
||||||
|
private isSending: boolean = false;
|
||||||
|
|
||||||
|
private async sendLogs(): Promise<void> {
|
||||||
|
if (this.isSending || this.queue.length === 0) return;
|
||||||
|
|
||||||
|
this.isSending = true;
|
||||||
|
const logsToSend = [...this.queue];
|
||||||
|
this.queue = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.apiEndpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(logsToSend),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error("Failed to send logs:", response.statusText);
|
||||||
|
// Restore logs to queue if send failed
|
||||||
|
this.queue = [...logsToSend, ...this.queue];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error sending logs:", error);
|
||||||
|
// Restore logs to queue if send failed
|
||||||
|
this.queue = [...logsToSend, ...this.queue];
|
||||||
|
} finally {
|
||||||
|
this.isSending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private addToQueue(entry: LogEntry): void {
|
||||||
|
this.queue.push({
|
||||||
|
...entry,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.queue.length >= this.maxQueueSize) {
|
||||||
|
this.sendLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public info(message: string, data?: any): void {
|
||||||
|
this.addToQueue({ level: "info", message, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
public warn(message: string, data?: any): void {
|
||||||
|
this.addToQueue({ level: "warn", message, data });
|
||||||
|
// Send immediately for warnings
|
||||||
|
this.sendLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public error(message: string, error?: Error | any): void {
|
||||||
|
const errorData =
|
||||||
|
error instanceof Error
|
||||||
|
? {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
}
|
||||||
|
: error;
|
||||||
|
|
||||||
|
this.addToQueue({ level: "error", message, data: errorData });
|
||||||
|
// Send immediately for errors
|
||||||
|
this.sendLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush remaining logs (useful when page is about to unload)
|
||||||
|
public flush(): void {
|
||||||
|
this.sendLogs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const clientLogger = new ClientLogger();
|
||||||
101
src/util/frontend-logger.ts
Normal file
101
src/util/frontend-logger.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// utils/frontend-logger.ts
|
||||||
|
import winston from "winston";
|
||||||
|
import DailyRotateFile from "winston-daily-rotate-file";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
// Define log levels and their priorities
|
||||||
|
const levels = {
|
||||||
|
error: 0,
|
||||||
|
warn: 1,
|
||||||
|
info: 2,
|
||||||
|
debug: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define interface for log entries
|
||||||
|
export interface LogEntry {
|
||||||
|
level: keyof typeof levels;
|
||||||
|
message: string;
|
||||||
|
data?: any;
|
||||||
|
timestamp?: string;
|
||||||
|
userAgent?: string;
|
||||||
|
ip?: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom format for log entries
|
||||||
|
const logFormat = winston.format.combine(
|
||||||
|
winston.format.timestamp({
|
||||||
|
format: "YYYY-MM-DD HH:mm:ss",
|
||||||
|
}),
|
||||||
|
winston.format.metadata({ fillExcept: ["message", "level", "timestamp"] }),
|
||||||
|
winston.format.json()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create the logger instance
|
||||||
|
const frontendLogger: winston.Logger = winston.createLogger({
|
||||||
|
levels,
|
||||||
|
level: process.env.LOG_LEVEL || "info",
|
||||||
|
format: logFormat,
|
||||||
|
transports: [
|
||||||
|
// Daily Rotate File for errors
|
||||||
|
new DailyRotateFile({
|
||||||
|
filename: path.join(process.cwd(), "logs/frontend/error-%DATE%.log"),
|
||||||
|
datePattern: "YYYY-MM-DD",
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: "20m",
|
||||||
|
maxFiles: "14d",
|
||||||
|
level: "error",
|
||||||
|
format: logFormat,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Daily Rotate File for all logs
|
||||||
|
new DailyRotateFile({
|
||||||
|
filename: path.join(process.cwd(), "logs/frontend/combined-%DATE%.log"),
|
||||||
|
datePattern: "YYYY-MM-DD",
|
||||||
|
zippedArchive: true,
|
||||||
|
maxSize: "20m",
|
||||||
|
maxFiles: "14d",
|
||||||
|
format: logFormat,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
// Handle errors from the logger itself
|
||||||
|
exitOnError: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add console transport in development
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
frontendLogger.add(
|
||||||
|
new winston.transports.Console({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.colorize(),
|
||||||
|
winston.format.simple()
|
||||||
|
),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions for type-safe logging
|
||||||
|
export function logError(message: string, data?: any) {
|
||||||
|
frontendLogger.error(message, { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logWarn(message: string, data?: any) {
|
||||||
|
frontendLogger.warn(message, { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logInfo(message: string, data?: any) {
|
||||||
|
frontendLogger.info(message, { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function logDebug(message: string, data?: any) {
|
||||||
|
frontendLogger.debug(message, { data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function for dynamic logging
|
||||||
|
export function log(entry: LogEntry) {
|
||||||
|
const { level, message, ...metadata } = entry;
|
||||||
|
frontendLogger[level](message, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the logger instance as default
|
||||||
|
export default frontendLogger;
|
||||||
Reference in New Issue
Block a user