diff --git a/src/main/front/package-lock.json b/src/main/front/package-lock.json
index 9f70161..a573458 100644
--- a/src/main/front/package-lock.json
+++ b/src/main/front/package-lock.json
@@ -13,6 +13,7 @@
"@mui/icons-material": "^6.1.2",
"@mui/material": "^6.1.2",
"@react-oauth/google": "^0.12.1",
+ "date-fns": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-infinite-scroller": "^1.2.6",
@@ -2271,6 +2272,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
diff --git a/src/main/front/package.json b/src/main/front/package.json
index dbe4a31..2af35e2 100644
--- a/src/main/front/package.json
+++ b/src/main/front/package.json
@@ -15,6 +15,7 @@
"@mui/icons-material": "^6.1.2",
"@mui/material": "^6.1.2",
"@react-oauth/google": "^0.12.1",
+ "date-fns": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-infinite-scroller": "^1.2.6",
diff --git a/src/main/front/src/admin/Files/NoExtFile.jsx b/src/main/front/src/admin/Files/NoExtFile.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/front/src/admin/Files/index.jsx b/src/main/front/src/admin/Files/index.jsx
new file mode 100644
index 0000000..9542382
--- /dev/null
+++ b/src/main/front/src/admin/Files/index.jsx
@@ -0,0 +1,71 @@
+import * as React from "react";
+import PropTypes from "prop-types";
+import Tabs from "@mui/material/Tabs";
+import Tab from "@mui/material/Tab";
+import Box from "@mui/material/Box";
+
+function CustomTabPanel(props) {
+ const { children, value, index, ...other } = props;
+
+ return (
+
+ {value === index && {children}}
+
+ );
+}
+
+CustomTabPanel.propTypes = {
+ children: PropTypes.node,
+ index: PropTypes.number.isRequired,
+ value: PropTypes.number.isRequired,
+};
+
+function a11yProps(index) {
+ return {
+ id: `simple-tab-${index}`,
+ "aria-controls": `simple-tabpanel-${index}`,
+ };
+}
+
+export default function AdminFilesComp() {
+ const [value, setValue] = React.useState(1);
+
+ const handleChange = (event, newValue) => {
+ setValue(newValue);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Item One
+
+
+ Item Two
+
+
+ Item Three
+
+
+ );
+}
diff --git a/src/main/front/src/components/MainDrawer.jsx b/src/main/front/src/components/MainDrawer.jsx
index 0359ff8..e5f44b8 100644
--- a/src/main/front/src/components/MainDrawer.jsx
+++ b/src/main/front/src/components/MainDrawer.jsx
@@ -17,6 +17,7 @@ import {
Badge,
BottomNavigation,
BottomNavigationAction,
+ Divider,
Paper,
Tooltip,
} from "@mui/material";
@@ -131,7 +132,7 @@ export default function MainDrawer() {
null}>
*/}
-
+
{MENUS.map((menu, index) => (
))}
+
,
},
+ ...ADMINMENU.map((menu) => ({
+ path: `/admin/${menu.id}`,
+ element: ,
+ })),
]);
const theme = createTheme({
diff --git a/src/main/front/src/pages/admin/AdminFeed.jsx b/src/main/front/src/pages/admin/AdminFeed.jsx
new file mode 100644
index 0000000..9b76cdb
--- /dev/null
+++ b/src/main/front/src/pages/admin/AdminFeed.jsx
@@ -0,0 +1,43 @@
+import InfiniteScroll from "react-infinite-scroller";
+import AdminPage from "./AdminPage";
+import FeedCard from "../../components/FeedCard";
+import useLoadData from "../../hooks/useLoadData";
+import { Box, Paper } from "@mui/material";
+
+function AdminFeed() {
+ const [allFeeds, hasMore, loadData] = useLoadData();
+
+ return (
+
+ (
+
+ ))}
+ >
+ {allFeeds.map((item) => (
+ //
+
+
+
+
+ Hello!
+
+ ))}
+
+
+ );
+}
+
+export default AdminFeed;
diff --git a/src/main/front/src/pages/admin/AdminFiles.jsx b/src/main/front/src/pages/admin/AdminFiles.jsx
new file mode 100644
index 0000000..8c10974
--- /dev/null
+++ b/src/main/front/src/pages/admin/AdminFiles.jsx
@@ -0,0 +1,16 @@
+import InfiniteScroll from "react-infinite-scroller";
+import AdminPage from "./AdminPage";
+import FeedCard from "../../components/FeedCard";
+import useLoadData from "../../hooks/useLoadData";
+import { Box, Paper } from "@mui/material";
+import AdminFilesComp from "../../admin/Files";
+
+function AdminFiles() {
+ return (
+
+
+
+ );
+}
+
+export default AdminFiles;
diff --git a/src/main/front/src/pages/admin/AdminPage.jsx b/src/main/front/src/pages/admin/AdminPage.jsx
new file mode 100644
index 0000000..755a089
--- /dev/null
+++ b/src/main/front/src/pages/admin/AdminPage.jsx
@@ -0,0 +1,205 @@
+import * as React from "react";
+import PropTypes from "prop-types";
+import AppBar from "@mui/material/AppBar";
+import Box from "@mui/material/Box";
+import CssBaseline from "@mui/material/CssBaseline";
+import Divider from "@mui/material/Divider";
+import Drawer from "@mui/material/Drawer";
+import IconButton from "@mui/material/IconButton";
+import List from "@mui/material/List";
+import ListItem from "@mui/material/ListItem";
+import ListItemButton from "@mui/material/ListItemButton";
+import ListItemIcon from "@mui/material/ListItemIcon";
+import ListItemText from "@mui/material/ListItemText";
+import MenuIcon from "@mui/icons-material/Menu";
+import Toolbar from "@mui/material/Toolbar";
+import Typography from "@mui/material/Typography";
+import ExitToAppIcon from "@mui/icons-material/ExitToApp";
+
+import { ADMINMENU } from ".";
+import { Link, useLocation } from "react-router-dom";
+import { Button, Tooltip } from "@mui/material";
+
+const drawerWidth = 220;
+
+function AdminPage(props) {
+ const { window } = props;
+ const [mobileOpen, setMobileOpen] = React.useState(false);
+ const [isClosing, setIsClosing] = React.useState(false);
+
+ const location = useLocation();
+ const currentMenuIndex = ADMINMENU.findIndex(
+ (menu) => menu.id === location.pathname.split("/").reverse()[0]
+ );
+ console.log(location.pathname, currentMenuIndex);
+
+ const handleDrawerClose = () => {
+ setIsClosing(true);
+ setMobileOpen(false);
+ };
+
+ const handleDrawerTransitionEnd = () => {
+ setIsClosing(false);
+ };
+
+ const handleDrawerToggle = () => {
+ if (!isClosing) {
+ setMobileOpen(!mobileOpen);
+ }
+ };
+
+ const drawer = (
+
+
+
+
+ {ADMINMENU.map((item, index) => (
+
+ theme.palette.primary.main,
+ fontWeight: "bold",
+ },
+ ]}
+ >
+ theme.palette.primary.main,
+ },
+ ]}
+ >
+ {item.icon}
+
+
+
+
+ ))}
+
+
+ );
+
+ // Remove this const when copying and pasting into your project.
+ const container =
+ window !== undefined ? () => window().document.body : undefined;
+
+ return (
+
+
+
+
+
+
+
+
+ {currentMenuIndex >= 0
+ ? ADMINMENU[currentMenuIndex].title
+ : props.title}
+
+
+
+
+
+
+
+
+
+ {/* The implementation can be swapped with js to avoid SEO duplication of links. */}
+
+ {drawer}
+
+
+ {drawer}
+
+
+
+
+ {props.children}
+
+
+ );
+}
+
+AdminPage.propTypes = {
+ /**
+ * Injected by the documentation to work in an iframe.
+ * Remove this when copying and pasting into your project.
+ */
+ window: PropTypes.func,
+};
+
+export default AdminPage;
diff --git a/src/main/front/src/pages/admin/UsersTable.jsx b/src/main/front/src/pages/admin/UsersTable.jsx
new file mode 100644
index 0000000..d271c34
--- /dev/null
+++ b/src/main/front/src/pages/admin/UsersTable.jsx
@@ -0,0 +1,306 @@
+import Paper from "@mui/material/Paper";
+import Table from "@mui/material/Table";
+import TableBody from "@mui/material/TableBody";
+import TableCell from "@mui/material/TableCell";
+import TableContainer from "@mui/material/TableContainer";
+import TableHead from "@mui/material/TableHead";
+import TablePagination from "@mui/material/TablePagination";
+import TableRow from "@mui/material/TableRow";
+import { useEffect, useMemo, useState } from "react";
+import { useFetchBe } from "../../tools/api";
+import { visuallyHidden } from "@mui/utils";
+
+import { formatDistanceToNow, parseISO } from "date-fns";
+import { ko } from "date-fns/locale";
+import {
+ Box,
+ Checkbox,
+ TableSortLabel,
+ Tooltip,
+ Typography,
+} from "@mui/material";
+import AdminPage from "./AdminPage";
+
+const columns = [
+ {
+ id: "name",
+ label: "Name",
+ minWidth: 170,
+ format: (value, row) => (
+
+ {value}
+
+ {row.id}
+
+
+ ),
+ },
+ {
+ id: "createdAt",
+ label: "Created At",
+ minWidth: 170,
+ format: (value) => (
+
+
+ {formatDistanceToNow(parseISO(value), {
+ addSuffix: true,
+ locale: ko,
+ })}
+
+
+ ),
+ },
+ {
+ id: "lastLoginTime",
+ label: "Last Login",
+ minWidth: 170,
+ format: (value) => (
+
+
+ {formatDistanceToNow(parseISO(value), {
+ addSuffix: true,
+ locale: ko,
+ })}
+
+
+ ),
+ },
+ {
+ id: "lastReadTime",
+ label: "Last Read",
+ minWidth: 170,
+ format: (value) => (
+
+
+ {value &&
+ formatDistanceToNow(parseISO(value), {
+ addSuffix: true,
+ locale: ko,
+ })}
+
+
+ ),
+ },
+];
+
+function descendingComparator(a, b, orderBy) {
+ if (!a[orderBy]) return 1;
+ if (!b[orderBy]) return -1;
+ if (b[orderBy] < a[orderBy]) {
+ return -1;
+ }
+ if (b[orderBy] > a[orderBy]) {
+ return 1;
+ }
+ return 0;
+}
+
+function getComparator(order, orderBy) {
+ return order === "desc"
+ ? (a, b) => descendingComparator(a, b, orderBy)
+ : (a, b) => -descendingComparator(a, b, orderBy);
+}
+
+function EnhancedTableHead(props) {
+ const {
+ onSelectAllClick,
+ order,
+ orderBy,
+ numSelected,
+ rowCount,
+ onRequestSort,
+ } = props;
+ const createSortHandler = (property) => (event) => {
+ onRequestSort(event, property);
+ };
+
+ return (
+
+
+ {/*
+ 0 && numSelected < rowCount}
+ checked={rowCount > 0 && numSelected === rowCount}
+ onChange={onSelectAllClick}
+ inputProps={{
+ "aria-label": "select all desserts",
+ }}
+ />
+ */}
+ {columns.map((headCell) => (
+
+
+ {headCell.label}
+ {orderBy === headCell.id ? (
+
+ {order === "desc" ? "sorted descending" : "sorted ascending"}
+
+ ) : null}
+
+
+ ))}
+
+
+ );
+}
+
+export default function UsersTable() {
+ const [allData, setAllData] = useState([]);
+ const [page, setPage] = useState(0);
+ const [rowsPerPage, setRowsPerPage] = useState(25);
+
+ const [order, setOrder] = useState("desc");
+ const [orderBy, setOrderBy] = useState("lastReadTime");
+ const [selected, setSelected] = useState([]);
+ const [dense, setDense] = useState(false);
+
+ const handleRequestSort = (event, property) => {
+ const isAsc = orderBy === property && order === "asc";
+ setOrder(isAsc ? "desc" : "asc");
+ setOrderBy(property);
+ };
+
+ const handleSelectAllClick = (event) => {
+ if (event.target.checked) {
+ const newSelected = allData.map((n) => n.id);
+ setSelected(newSelected);
+ return;
+ }
+ setSelected([]);
+ };
+
+ const handleClick = (event, id) => {
+ const selectedIndex = selected.indexOf(id);
+ let newSelected = [];
+
+ if (selectedIndex === -1) {
+ newSelected = newSelected.concat(selected, id);
+ } else if (selectedIndex === 0) {
+ newSelected = newSelected.concat(selected.slice(1));
+ } else if (selectedIndex === selected.length - 1) {
+ newSelected = newSelected.concat(selected.slice(0, -1));
+ } else if (selectedIndex > 0) {
+ newSelected = newSelected.concat(
+ selected.slice(0, selectedIndex),
+ selected.slice(selectedIndex + 1)
+ );
+ }
+ setSelected(newSelected);
+ };
+
+ const handleChangePage = (event, newPage) => {
+ setPage(newPage);
+ };
+
+ const handleChangeRowsPerPage = (event) => {
+ setRowsPerPage(parseInt(event.target.value, 10));
+ setPage(0);
+ };
+
+ const handleChangeDense = (event) => {
+ setDense(event.target.checked);
+ };
+
+ // Avoid a layout jump when reaching the last page with empty allData.
+ const emptyRows =
+ page > 0 ? Math.max(0, (1 + page) * rowsPerPage - allData.length) : 0;
+
+ const visibleRows = useMemo(
+ () =>
+ [...allData]
+ .sort(getComparator(order, orderBy))
+ .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage),
+ [order, orderBy, page, rowsPerPage, allData]
+ );
+
+ const fetchBe = useFetchBe();
+
+ useEffect(() => {
+ // Get Admin Info
+ fetchBe("/admin/users").then((doc) => setAllData(doc));
+ }, []);
+
+ return (
+
+
+
+
+ {/*
+
+ {columns.map((column) => (
+
+ {column.label}
+
+ ))}
+
+ */}
+
+
+ {visibleRows
+ // .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
+ .map((row) => {
+ return (
+
+ {columns.map((column) => {
+ // console.log(row, column.id);
+ return (
+
+ {column.format
+ ? column.format(row[column.id], row)
+ : row[column.id]}
+
+ );
+ })}
+
+ );
+ })}
+ {emptyRows > 0 && (
+
+
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/main/front/src/pages/admin/index.jsx b/src/main/front/src/pages/admin/index.jsx
new file mode 100644
index 0000000..0099a57
--- /dev/null
+++ b/src/main/front/src/pages/admin/index.jsx
@@ -0,0 +1,28 @@
+import PeopleIcon from "@mui/icons-material/People";
+import FeedIcon from "@mui/icons-material/Feed";
+import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile";
+
+import UsersTable from "./UsersTable";
+import AdminFeed from "./AdminFeed";
+import AdminFiles from "./AdminFiles";
+
+export const ADMINMENU = [
+ {
+ title: "이용자 관리",
+ icon: ,
+ id: "users",
+ comp: UsersTable,
+ },
+ {
+ title: "게시글 관리",
+ icon: ,
+ id: "posts",
+ comp: AdminFeed,
+ },
+ {
+ title: "파일 관리",
+ icon: ,
+ id: "files",
+ comp: AdminFiles,
+ },
+];
diff --git a/src/main/java/com/thc/realspr/controller/TbadminController.java b/src/main/java/com/thc/realspr/controller/TbadminController.java
new file mode 100644
index 0000000..ac9b7ba
--- /dev/null
+++ b/src/main/java/com/thc/realspr/controller/TbadminController.java
@@ -0,0 +1,31 @@
+package com.thc.realspr.controller;
+
+import com.thc.realspr.dto.TbadminDto;
+import com.thc.realspr.service.TbadminService;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RequestMapping("/api/admin")
+@RestController
+public class TbadminController {
+ private final TbadminService tbadminService;
+
+ public TbadminController(TbadminService tbadminService) {
+ this.tbadminService = tbadminService;
+ }
+
+ @GetMapping("/users")
+ public List adminGetUser(@RequestParam Map param, HttpServletRequest request) {
+ String reqUserId = request.getAttribute("reqUserId").toString();
+ return tbadminService.adminGetUser(reqUserId, param);
+ }
+
+ @GetMapping("/firebasefilelist")
+ public List adminGetFirebaseStorageList(@RequestParam Map param, HttpServletRequest request) {
+ String reqUserId = request.getAttribute("reqUserId").toString();
+ return tbadminService.adminGetFirebaseStorageList(reqUserId);
+ }
+}
diff --git a/src/main/java/com/thc/realspr/domain/TbUserPerm.java b/src/main/java/com/thc/realspr/domain/TbUserPerm.java
new file mode 100644
index 0000000..cf6fee2
--- /dev/null
+++ b/src/main/java/com/thc/realspr/domain/TbUserPerm.java
@@ -0,0 +1,41 @@
+package com.thc.realspr.domain;
+
+import com.thc.realspr.id.UserPermId;
+import com.thc.realspr.id.UserSubjectId;
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import java.time.LocalDateTime;
+
+@Getter
+@Entity
+@EntityListeners(AuditingEntityListener.class) // For Audit
+public class TbUserPerm {
+ @EmbeddedId UserPermId id;
+ @Setter @Column(nullable = false) private String deleted; // 삭제여부
+
+ @CreatedDate
+ private LocalDateTime createdDate;
+ @LastModifiedDate
+ private LocalDateTime modifiedDate;
+
+ protected TbUserPerm() {
+ }
+
+ private TbUserPerm(String userId, String permission) {
+ this.id = new UserPermId(userId, permission);
+ }
+
+ public static TbUserPerm of(String userId, String permission) {
+ return new TbUserPerm(userId, permission);
+ }
+
+ @PrePersist
+ public void onPrePersist() {
+ this.deleted = "N";
+ }
+}
diff --git a/src/main/java/com/thc/realspr/dto/TbadminDto.java b/src/main/java/com/thc/realspr/dto/TbadminDto.java
new file mode 100644
index 0000000..e8415a2
--- /dev/null
+++ b/src/main/java/com/thc/realspr/dto/TbadminDto.java
@@ -0,0 +1,25 @@
+package com.thc.realspr.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+public class TbadminDto {
+ @Schema
+ @Getter
+ @Setter
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class UserDetail {
+ private String id;
+ private String name;
+ private LocalDateTime createdAt;
+ private LocalDateTime lastLoginTime;
+ private LocalDateTime lastReadTime;
+
+ }
+}
diff --git a/src/main/java/com/thc/realspr/id/UserPermId.java b/src/main/java/com/thc/realspr/id/UserPermId.java
new file mode 100644
index 0000000..f042cfa
--- /dev/null
+++ b/src/main/java/com/thc/realspr/id/UserPermId.java
@@ -0,0 +1,17 @@
+package com.thc.realspr.id;
+
+import jakarta.persistence.Embeddable;
+import lombok.*;
+
+import java.io.Serializable;
+
+@Embeddable
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode
+public class UserPermId implements Serializable {
+ private String userId;
+ private String permission;
+}
diff --git a/src/main/java/com/thc/realspr/mapper/TbadminMapper.java b/src/main/java/com/thc/realspr/mapper/TbadminMapper.java
new file mode 100644
index 0000000..7f63ff7
--- /dev/null
+++ b/src/main/java/com/thc/realspr/mapper/TbadminMapper.java
@@ -0,0 +1,9 @@
+package com.thc.realspr.mapper;
+
+import com.thc.realspr.dto.TbadminDto;
+
+import java.util.List;
+
+public interface TbadminMapper {
+ List allUsers();
+}
diff --git a/src/main/java/com/thc/realspr/repository/TbUserPermRepository.java b/src/main/java/com/thc/realspr/repository/TbUserPermRepository.java
new file mode 100644
index 0000000..4f6490a
--- /dev/null
+++ b/src/main/java/com/thc/realspr/repository/TbUserPermRepository.java
@@ -0,0 +1,16 @@
+package com.thc.realspr.repository;
+
+import com.thc.realspr.domain.TbUserLike;
+import com.thc.realspr.domain.TbUserPerm;
+import com.thc.realspr.id.UserPermId;
+import com.thc.realspr.id.UserSubjectId;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface TbUserPermRepository extends JpaRepository {
+ Optional findById(UserPermId userPermId);
+}
diff --git a/src/main/java/com/thc/realspr/service/FirebaseService.java b/src/main/java/com/thc/realspr/service/FirebaseService.java
index 0d4049c..c613cc8 100644
--- a/src/main/java/com/thc/realspr/service/FirebaseService.java
+++ b/src/main/java/com/thc/realspr/service/FirebaseService.java
@@ -1,5 +1,6 @@
package com.thc.realspr.service;
+import com.google.cloud.storage.Bucket;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@@ -8,6 +9,8 @@
import com.google.cloud.storage.Storage;
import com.google.firebase.cloud.StorageClient;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -33,4 +36,23 @@ public CompletableFuture getSignedUrlAsync(String path) {
String url = getSignedUrl(path); // Assume this is the existing method to get signed URL
return CompletableFuture.completedFuture(url);
}
+
+ public List listAllFiles(String folderName) {
+ Storage storage = StorageClient.getInstance().bucket().getStorage();
+ Bucket bucket = StorageClient.getInstance().bucket();
+
+ List fileList = new ArrayList<>();
+// for (Blob blob : bucket.list().iterateAll()) {
+ for (Blob blob : bucket.list(Storage.BlobListOption.prefix(folderName + "/")).iterateAll()) {
+ fileList.add(blob.getName());
+ }
+
+ return fileList;
+ }
+
+ @Async
+ public CompletableFuture> listAllFilesAsync(String folderName) {
+ List files = listAllFiles(folderName);
+ return CompletableFuture.completedFuture(files);
+ }
}
diff --git a/src/main/java/com/thc/realspr/service/TbadminService.java b/src/main/java/com/thc/realspr/service/TbadminService.java
new file mode 100644
index 0000000..cc6e8ad
--- /dev/null
+++ b/src/main/java/com/thc/realspr/service/TbadminService.java
@@ -0,0 +1,14 @@
+package com.thc.realspr.service;
+
+import com.thc.realspr.dto.TbadminDto;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public interface TbadminService {
+ public List adminGetUser(String userId, Map param);
+
+ public List adminGetFirebaseStorageList(String userId);
+}
diff --git a/src/main/java/com/thc/realspr/service/impl/TbadminServiceImpl.java b/src/main/java/com/thc/realspr/service/impl/TbadminServiceImpl.java
new file mode 100644
index 0000000..460c03c
--- /dev/null
+++ b/src/main/java/com/thc/realspr/service/impl/TbadminServiceImpl.java
@@ -0,0 +1,41 @@
+package com.thc.realspr.service.impl;
+
+import com.thc.realspr.dto.TbadminDto;
+import com.thc.realspr.exception.NoAuthorizationException;
+import com.thc.realspr.id.UserPermId;
+import com.thc.realspr.mapper.TbadminMapper;
+import com.thc.realspr.repository.TbUserPermRepository;
+import com.thc.realspr.service.FirebaseService;
+import com.thc.realspr.service.TbadminService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Service
+public class TbadminServiceImpl implements TbadminService {
+ private final TbadminMapper tbadminMapper;
+ private final TbUserPermRepository tbUserPermRepository;
+ private final FirebaseService firebaseService;
+
+ public TbadminServiceImpl(TbadminMapper tbadminMapper, TbUserPermRepository tbUserPermRepository, FirebaseService firebaseService) {
+ this.tbadminMapper = tbadminMapper;
+ this.tbUserPermRepository = tbUserPermRepository;
+ this.firebaseService = firebaseService;
+ }
+
+ public List adminGetUser(String userId, Map param) {
+
+ if (tbUserPermRepository.findById(new UserPermId(userId, "adminGetUser")).isEmpty())
+ throw new NoAuthorizationException("No Admin Permission");
+ return tbadminMapper.allUsers();
+ }
+
+ public List adminGetFirebaseStorageList(String userId) {
+
+ if (tbUserPermRepository.findById(new UserPermId(userId, "adminFirebaseFiles")).isEmpty())
+ throw new NoAuthorizationException("No Admin Permission");
+ return firebaseService.listAllFiles("KaFile");
+ }
+
+}
diff --git a/src/main/resources/mapper/TbadminMapper.xml b/src/main/resources/mapper/TbadminMapper.xml
new file mode 100644
index 0000000..dcdabcd
--- /dev/null
+++ b/src/main/resources/mapper/TbadminMapper.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
\ No newline at end of file