diff --git a/biome.json b/biome.json index ca67c14..3c13c1b 100644 --- a/biome.json +++ b/biome.json @@ -18,6 +18,14 @@ "recommended": true } }, + "overrides": [ + { + "includes": ["src/routeTree.gen.ts", "dist/**", "node_modules/**"], + "linter": { "enabled": false }, + "formatter": { "enabled": false }, + "assist": { "enabled": false } + } + ], "javascript": { "formatter": { "quoteStyle": "double" diff --git a/src/api/index.tsx b/src/api/index.tsx new file mode 100644 index 0000000..b7cb97d --- /dev/null +++ b/src/api/index.tsx @@ -0,0 +1,36 @@ +import { cors } from "@elysiajs/cors"; +import { swagger } from "@elysiajs/swagger"; +import Elysia from "elysia"; +import { apiMiddleware } from "../middleware/apiMiddleware"; +import { auth } from "../utils/auth"; +import { apikey } from "./apikey"; + +const isProduction = process.env.NODE_ENV === "production"; + +const api = new Elysia({ + prefix: "/api", +}) + .use(cors()) + .all("/auth/*", ({ request }) => auth.handler(request)) + .get("/session", async ({ request }) => { + const data = await auth.api.getSession({ headers: request.headers }); + return { data }; + }) + .use(apiMiddleware) + .use(apikey); + +if (!isProduction) { + api.use( + swagger({ + path: "/docs", + documentation: { + info: { + title: "Bun + React API", + version: "1.0.0", + }, + }, + }), + ); +} + +export default api; diff --git a/src/frontend.tsx b/src/frontend.tsx index b632040..49c4b49 100644 --- a/src/frontend.tsx +++ b/src/frontend.tsx @@ -8,12 +8,12 @@ /** biome-ignore-all lint/suspicious/noAssignInExpressions: { - if (!e.codeInfo) return; - - const url = VITE_PUBLIC_URL; - fetch(`${url}/__open-in-editor`, { - + onClickElement={(e) => { + if (!e.codeInfo) return; + + const url = VITE_PUBLIC_URL; + fetch(`${url}/__open-in-editor`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ diff --git a/src/index.css b/src/index.css index 09eaa7e..17daf26 100644 --- a/src/index.css +++ b/src/index.css @@ -182,6 +182,6 @@ code { *, ::before, ::after { - animation: none !important; + animation: none; } } diff --git a/src/index.ts b/src/index.ts index 14010a6..344c617 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,42 +1,13 @@ /** biome-ignore-all lint/suspicious/noExplicitAny: penjelasannya */ + import fs from "node:fs"; import path from "node:path"; -import { cors } from "@elysiajs/cors"; -import { swagger } from "@elysiajs/swagger"; import { Elysia } from "elysia"; -import { apikey } from "./api/apikey"; -import { apiMiddleware } from "./middleware/apiMiddleware"; -import { auth } from "./utils/auth"; +import api from "./api"; import { openInEditor } from "./utils/open-in-editor"; const isProduction = process.env.NODE_ENV === "production"; -const api = new Elysia({ - prefix: "/api", -}) - .use(cors()) - .all("/auth/*", ({ request }) => auth.handler(request)) - .get("/session", async ({ request }) => { - const data = await auth.api.getSession({ headers: request.headers }); - return { data }; - }) - .use(apiMiddleware) - .use(apikey); - -if (!isProduction) { - api.use( - swagger({ - path: "/docs", - documentation: { - info: { - title: "Bun + React API", - version: "1.0.0", - }, - }, - }), - ); -} - const app = new Elysia().use(api); if (!isProduction) { @@ -158,8 +129,11 @@ if (!isProduction) { const pathname = url.pathname; // 1. Try exact match in dist - let filePath = path.join("dist", pathname === "/" ? "index.html" : pathname); - + let filePath = path.join( + "dist", + pathname === "/" ? "index.html" : pathname, + ); + // 2. If not found and looks like an asset (has extension), try root of dist if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { if (pathname.includes(".") && !pathname.endsWith("/")) { @@ -191,4 +165,4 @@ console.log( `🚀 Server running at http://localhost:3000 in ${isProduction ? "production" : "development"} mode`, ); -export type ApiApp = typeof app; \ No newline at end of file +export type ApiApp = typeof app; diff --git a/src/routes/dashboard/route.tsx b/src/routes/dashboard/route.tsx index c509c77..8fc6615 100644 --- a/src/routes/dashboard/route.tsx +++ b/src/routes/dashboard/route.tsx @@ -1,5 +1,4 @@ import { - ActionIcon, AppShell, Avatar, Box, @@ -31,8 +30,8 @@ import { useNavigate, } from "@tanstack/react-router"; import { useSnapshot } from "valtio"; -import { authStore } from "../../store/auth"; import { authClient } from "@/utils/auth-client"; +import { authStore } from "../../store/auth"; export const Route = createFileRoute("/dashboard")({ component: DashboardLayout, diff --git a/src/routes/profile.tsx b/src/routes/profile.tsx index c8766c6..240e9f3 100644 --- a/src/routes/profile.tsx +++ b/src/routes/profile.tsx @@ -1,5 +1,3 @@ -import { protectedRouteMiddleware } from "@/middleware/authMiddleware"; -import { authClient } from "@/utils/auth-client"; import { ActionIcon, Avatar, @@ -13,11 +11,11 @@ import { Grid, Group, Paper, + rem, Stack, Text, Title, Tooltip, - rem, } from "@mantine/core"; import { modals } from "@mantine/modals"; import { @@ -34,6 +32,8 @@ import { import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; import { useSnapshot } from "valtio"; +import { protectedRouteMiddleware } from "@/middleware/authMiddleware"; +import { authClient } from "@/utils/auth-client"; import { authStore } from "../store/auth"; export const Route = createFileRoute("/profile")({ @@ -62,7 +62,8 @@ function Profile() { size: "sm", children: ( - Apakah Anda yakin ingin keluar dari akun Anda? Anda harus masuk kembali untuk mengakses data Anda. + Apakah Anda yakin ingin keluar dari akun Anda? Anda harus masuk + kembali untuk mengakses data Anda. ), labels: { confirm: "Keluar", cancel: "Batal" }, @@ -78,8 +79,20 @@ function Profile() { } }; - const InfoField = ({ icon: Icon, label, value, copyable = false, id = "" }: any) => ( - + const InfoField = ({ + icon: Icon, + label, + value, + copyable = false, + id = "", + }: any) => ( + @@ -89,18 +102,32 @@ function Profile() { {label} - + {value || "N/A"} {copyable && value && ( - + copyToClipboard(value, id)} > - {copied === id ? : } + {copied === id ? ( + + ) : ( + + )} )} @@ -116,8 +143,12 @@ function Profile() { {/* Header Section */} - Profil Saya - Kelola informasi akun dan pengaturan keamanan Anda + + Profil Saya + + + Kelola informasi akun dan pengaturan keamanan Anda + {snap.user?.role === "admin" && ( @@ -144,24 +175,47 @@ function Profile() { {/* Profile Overview Card */} - - + + {snap.user?.name?.charAt(0).toUpperCase()} - {snap.user?.name} + + {snap.user?.name} + - {snap.user?.email} - • - + + {snap.user?.email} + + + • + + {snap.user?.role || "user"} @@ -173,19 +227,41 @@ function Profile() { - Informasi Identitas + + Informasi Identitas + - + - + - + - + @@ -193,40 +269,73 @@ function Profile() { - Keamanan & Sesi - + + Keamanan & Sesi + + - Sesi Saat Ini + + Sesi Saat Ini + - Aktif Sekarang - ID: {snap.session?.id?.substring(0, 8)}... + + Aktif Sekarang + + + ID: {snap.session?.id?.substring(0, 8)}... + - + - Session Token + + Session Token + - - {snap.session?.token ? `${snap.session.token.substring(0, 32)}...` : "N/A"} - - snap.session?.token && copyToClipboard(snap.session.token, "token")} + - {copied === "token" ? : } + {snap.session?.token + ? `${snap.session.token.substring(0, 32)}...` + : "N/A"} + + + snap.session?.token && + copyToClipboard(snap.session.token, "token") + } + > + {copied === "token" ? ( + + ) : ( + + )} - diff --git a/src/utils/env.ts b/src/utils/env.ts index b224c93..7cf12dd 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -5,7 +5,7 @@ export const getEnv = (key: string, defaultValue = ""): string => { // 1. Try Vite's import.meta.env try { - if (typeof import.meta.env !== "undefined" && import.meta.env[key]) { + if (import.meta.env?.[key]) { return import.meta.env[key]; } } catch {} @@ -20,11 +20,14 @@ export const getEnv = (key: string, defaultValue = ""): string => { return defaultValue; }; -export const VITE_PUBLIC_URL = getEnv("VITE_PUBLIC_URL", "http://localhost:3000"); +export const VITE_PUBLIC_URL = getEnv( + "VITE_PUBLIC_URL", + "http://localhost:3000", +); export const IS_DEV = (() => { try { - return typeof import.meta.env !== "undefined" && import.meta.env.DEV; + return import.meta.env?.DEV; } catch { return false; } diff --git a/src/utils/open-in-editor.ts b/src/utils/open-in-editor.ts index aae7856..13cc7b2 100644 --- a/src/utils/open-in-editor.ts +++ b/src/utils/open-in-editor.ts @@ -1,9 +1,9 @@ // open-in-editor.ts // DEV utility: open source file in local editor -import { spawn } from "child_process"; -import fs from "fs"; -import path from "path"; +import { spawn } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; /* ------------------------------------------------------- * Types