auto commit
This commit is contained in:
parent
c07c2a537f
commit
35e2a31442
@ -1,13 +1,10 @@
|
|||||||
// src/app/(admin)/layout.tsx
|
// src/app/(admin)/layout.tsx
|
||||||
|
import React from "react";
|
||||||
import AdminGuard from "@/components/auth/AdminGuard";
|
import AdminGuard from "@/components/auth/AdminGuard";
|
||||||
import { SideNav } from "@/components/layout/SideNav";
|
import { SideNav } from "@/components/layout/SideNav";
|
||||||
import { TopNav } from "@/components/layout/TopNav";
|
import { TopNav } from "@/components/layout/TopNav";
|
||||||
|
|
||||||
export default function AdminLayout({
|
const AdminLayout = ({ children }: { children: React.ReactNode }) => {
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<AdminGuard>
|
<AdminGuard>
|
||||||
<div className="h-screen flex">
|
<div className="h-screen flex">
|
||||||
@ -29,4 +26,6 @@ export default function AdminLayout({
|
|||||||
</div>
|
</div>
|
||||||
</AdminGuard>
|
</AdminGuard>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default AdminLayout;
|
||||||
|
@ -22,6 +22,7 @@ import { AxiosError } from "axios";
|
|||||||
import { UserForm } from "./components/UserForm";
|
import { UserForm } from "./components/UserForm";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { User } from "@/types/user";
|
import { User } from "@/types/user";
|
||||||
|
import AdminGuard from "@/components/auth/AdminGuard";
|
||||||
|
|
||||||
const AccountsPage = () => {
|
const AccountsPage = () => {
|
||||||
const { token, user } = useAuthStore();
|
const { token, user } = useAuthStore();
|
||||||
@ -225,6 +226,7 @@ const AccountsPage = () => {
|
|||||||
if (isLoading) return <div>Loading...</div>;
|
if (isLoading) return <div>Loading...</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<AdminGuard requiredPermissions={["users:manage"]}>
|
||||||
<div className="container mx-auto py-6">
|
<div className="container mx-auto py-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
@ -290,6 +292,7 @@ const AccountsPage = () => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
|
</AdminGuard>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,52 @@
|
|||||||
|
// // src/components/auth/AdminGuard.tsx
|
||||||
|
// "use client";
|
||||||
|
|
||||||
|
// import { usePermissions } from "@/hooks/usePermissions";
|
||||||
|
// import { ReactNode } from "react";
|
||||||
|
|
||||||
|
// interface AdminGuardProps {
|
||||||
|
// children: ReactNode;
|
||||||
|
// requiredPermissions?: string[]; // 필요한 권한 목록
|
||||||
|
// requireAll?: boolean; // true면 모든 권한 필요, false면 하나라도 있으면 됨
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default function AdminGuard({
|
||||||
|
// children,
|
||||||
|
// requiredPermissions = [],
|
||||||
|
// requireAll = false,
|
||||||
|
// }: AdminGuardProps) {
|
||||||
|
// const { hasAllPermissions, hasAnyPermission } = usePermissions();
|
||||||
|
|
||||||
|
// const hasAccess =
|
||||||
|
// requiredPermissions.length === 0
|
||||||
|
// ? true
|
||||||
|
// : requireAll
|
||||||
|
// ? hasAllPermissions(requiredPermissions)
|
||||||
|
// : hasAnyPermission(requiredPermissions);
|
||||||
|
|
||||||
|
// if (!hasAccess) {
|
||||||
|
// return (
|
||||||
|
// <div className="flex items-center justify-center min-h-[200px] text-gray-500">
|
||||||
|
// 접근 권한이 없습니다.
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return <>{children}</>;
|
||||||
|
// }
|
||||||
|
|
||||||
// src/components/auth/AdminGuard.tsx
|
// src/components/auth/AdminGuard.tsx
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import { usePermissions } from "@/hooks/usePermissions";
|
import { usePermissions } from "@/hooks/usePermissions";
|
||||||
import { ReactNode } from "react";
|
import { useAuth } from "@/hooks/useAuth";
|
||||||
|
|
||||||
interface AdminGuardProps {
|
interface AdminGuardProps {
|
||||||
children: ReactNode;
|
children: React.ReactNode;
|
||||||
requiredPermissions?: string[]; // 필요한 권한 목록
|
requiredPermissions?: string[];
|
||||||
requireAll?: boolean; // true면 모든 권한 필요, false면 하나라도 있으면 됨
|
requireAll?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AdminGuard({
|
export default function AdminGuard({
|
||||||
@ -15,8 +54,18 @@ export default function AdminGuard({
|
|||||||
requiredPermissions = [],
|
requiredPermissions = [],
|
||||||
requireAll = false,
|
requireAll = false,
|
||||||
}: AdminGuardProps) {
|
}: AdminGuardProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
const { user } = useAuth();
|
||||||
const { hasAllPermissions, hasAnyPermission } = usePermissions();
|
const { hasAllPermissions, hasAnyPermission } = usePermissions();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 사용자가 없거나 로그인하지 않은 경우
|
||||||
|
if (!user) {
|
||||||
|
router.push("/login");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 권한 체크
|
||||||
const hasAccess =
|
const hasAccess =
|
||||||
requiredPermissions.length === 0
|
requiredPermissions.length === 0
|
||||||
? true
|
? true
|
||||||
@ -25,19 +74,32 @@ export default function AdminGuard({
|
|||||||
: hasAnyPermission(requiredPermissions);
|
: hasAnyPermission(requiredPermissions);
|
||||||
|
|
||||||
if (!hasAccess) {
|
if (!hasAccess) {
|
||||||
|
router.push("/dashboard/overview");
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
user,
|
||||||
|
router,
|
||||||
|
requiredPermissions,
|
||||||
|
requireAll,
|
||||||
|
hasAllPermissions,
|
||||||
|
hasAnyPermission,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 권한 체크
|
||||||
|
const hasAccess =
|
||||||
|
requiredPermissions.length === 0
|
||||||
|
? true
|
||||||
|
: requireAll
|
||||||
|
? hasAllPermissions(requiredPermissions)
|
||||||
|
: hasAnyPermission(requiredPermissions);
|
||||||
|
|
||||||
|
if (!user || !hasAccess) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-[200px] text-gray-500">
|
<div className="flex items-center justify-center min-h-[200px] text-gray-500">
|
||||||
접근 권한이 없습니다.
|
로딩 중...
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용 예시:
|
|
||||||
/*
|
|
||||||
<AdminGuard requiredPermissions={['users:manage']}>
|
|
||||||
<UserManagementPanel />
|
|
||||||
</AdminGuard>
|
|
||||||
*/
|
|
||||||
|
@ -37,7 +37,7 @@ export function useAuth() {
|
|||||||
const logout = () => {
|
const logout = () => {
|
||||||
clearAuth();
|
clearAuth();
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
router.push("/");
|
router.push("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
return { user, token, login, logout };
|
return { user, token, login, logout };
|
||||||
|
@ -1,39 +1,70 @@
|
|||||||
|
// // src/hooks/usePermissions.ts
|
||||||
|
// import { useAuth } from "./useAuth";
|
||||||
|
|
||||||
|
// export function usePermissions() {
|
||||||
|
// const { user } = useAuth();
|
||||||
|
// const permissions = user?.permissions || {};
|
||||||
|
|
||||||
|
// const hasPermission = (permission: string): boolean => {
|
||||||
|
// return !!permissions[permission];
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const hasAnyPermission = (requiredPermissions: string[]): boolean => {
|
||||||
|
// return requiredPermissions.some((permission) => hasPermission(permission));
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const hasAllPermissions = (requiredPermissions: string[]): boolean => {
|
||||||
|
// return requiredPermissions.every((permission) => hasPermission(permission));
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// permissions,
|
||||||
|
// hasPermission,
|
||||||
|
// hasAnyPermission,
|
||||||
|
// hasAllPermissions,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
// src/hooks/usePermissions.ts
|
// src/hooks/usePermissions.ts
|
||||||
import { useAuth } from "./useAuth";
|
import { useAuth } from "./useAuth";
|
||||||
|
import { PATH_PERMISSIONS } from "@/config/permissions";
|
||||||
|
|
||||||
export function usePermissions() {
|
export function usePermissions() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const permissions = user?.permissions || {};
|
const permissions = user?.permissions || {};
|
||||||
|
|
||||||
const hasPermission = (permission: string): boolean => {
|
const hasPermission = (permission: string): boolean => {
|
||||||
|
if (!user) return false;
|
||||||
|
|
||||||
|
// 슈퍼 관리자는 모든 권한을 가짐
|
||||||
|
if (user.role === "super_admin") return true;
|
||||||
|
|
||||||
|
// 회사 관리자는 회사 관련 모든 권한을 가짐
|
||||||
|
if (user.role === "company_admin") {
|
||||||
|
if (
|
||||||
|
permission.startsWith("company:") ||
|
||||||
|
permission.startsWith("branches:") ||
|
||||||
|
permission.startsWith("users:") ||
|
||||||
|
permission.startsWith("departments:")
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return !!permissions[permission];
|
return !!permissions[permission];
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasAnyPermission = (requiredPermissions: string[]): boolean => {
|
const hasPathPermission = (path: string): boolean => {
|
||||||
|
const requiredPermissions = PATH_PERMISSIONS[path];
|
||||||
|
if (!requiredPermissions) return true;
|
||||||
return requiredPermissions.some((permission) => hasPermission(permission));
|
return requiredPermissions.some((permission) => hasPermission(permission));
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasAllPermissions = (requiredPermissions: string[]): boolean => {
|
|
||||||
return requiredPermissions.every((permission) => hasPermission(permission));
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
permissions,
|
permissions,
|
||||||
hasPermission,
|
hasPermission,
|
||||||
hasAnyPermission,
|
hasPathPermission,
|
||||||
hasAllPermissions,
|
hasAnyPermission: (perms: string[]) => perms.some(hasPermission),
|
||||||
|
hasAllPermissions: (perms: string[]) => perms.every(hasPermission),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용 예시:
|
|
||||||
/*
|
|
||||||
function MyComponent() {
|
|
||||||
const { hasPermission } = usePermissions();
|
|
||||||
|
|
||||||
if (!hasPermission('departments:view')) {
|
|
||||||
return <div>접근 권한이 없습니다.</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div>부서 목록...</div>;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
role: 'admin' | 'user';
|
role: "super_admin" | "company_admin" | "branch_admin" | "user";
|
||||||
// ...
|
permissions: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EnergyUsage {
|
export interface EnergyUsage {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
value: number;
|
value: number;
|
||||||
type: 'electricity' | 'gas' | 'water' | 'steam';
|
type: "electricity" | "gas" | "water" | "steam";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
// ...
|
Loading…
Reference in New Issue
Block a user