Compare commits

..

1 Commits

Author SHA1 Message Date
413896c54b markdown editor 2024-12-04 16:53:35 +00:00
9 changed files with 155 additions and 24 deletions

View File

@@ -1,8 +1,9 @@
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import MainNavigation from "./components/MainNavigation";
import SideNavigation from "./components/SideNavigation";
import MainNavigation from "./components/Navigations/MainNavigation";
import SideNavigation from "./components/Navigations/SideNavigation";
import MarkdownContent from "./components/MarkdownContent";
import MarkdownEditor from "./components/Markdowns/MarkdownEditor";
import "./App.css";
import Callback from "./Callback";
import {config} from "./confs/appConfig";
@@ -20,6 +21,8 @@ const App = () => {
<Route path="/markdown/:id" element={<MarkdownContent />} />
<Route path="/callback" element={<Callback />} />
<Route path="/test" element={<h1>TEST</h1>}></Route>
<Route path="/markdown/create" element={<MarkdownEditor />}></Route>
<Route path="/markdown/edit/:id" element={<MarkdownEditor />}></Route>
</Routes>
</main>
</div>

View File

@@ -7,33 +7,43 @@ export const AuthContext = createContext({
user: null,
login: () => {},
logout: () => {},
roles: [],
});
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [roles, setRoles] = useState([]);
const userManager = new UserManager(appConfig.oidcConfig);
useEffect(() => {
userManager.getUser()
.then((user) => {
if (user && !user.expired) {
setUser(user);
localStorage.setItem("accessToken", user.access_token);
} else if (user && user.expired) {
userManager.signinSilent()
.then((newUser) => {
setUser(newUser);
localStorage.setItem("accessToken", newUser.access_token);
})
.catch((err) => {
console.error(err);
})
}
});
}, [userManager]);
if (user && !user.expired) {
setUser(user);
localStorage.setItem("accessToken", user.access_token);
const clientRoles = user?.profile?.resource_access?.[appConfig.kc_client_id]?.roles || [];
setRoles(clientRoles);
} else if (user && user.expired) {
userManager.signinSilent()
.then((newUser) => {
setUser(newUser);
localStorage.setItem("accessToken", newUser.access_token);
const clientRoles =
newUser?.profile?.resource_access?.[appConfig.kc_client_id]?.roles || [];
setRoles(clientRoles);
})
.catch((err) => {
console.error(err);
})
}
});
}, [userManager]);
const login = () => {
userManager.signinRedirect().catch((err) => {
userManager
.signinRedirect()
.catch(
(err) => {
console.log(appConfig);
console.log(err);
});
@@ -41,7 +51,7 @@ const AuthProvider = ({ children }) => {
const logout = () => userManager.signoutRedirect();
return (
<AuthContext.Provider value={{ user, login, logout }}>
<AuthContext.Provider value={{ user, roles, login, logout }}>
{children}
</AuthContext.Provider>
);

View File

@@ -0,0 +1,97 @@
import React, {useContext, useEffect, useState} from "react";
import {AuthContext} from "../../AuthProvider";
import {useNavigate, useParams} from "react-router-dom";
import {fetchWithCache} from "../../utils/fetchWithCache";
const MarkdownEditor = () => {
const {roles} = useContext(AuthContext);
const navigate = useNavigate();
const {id} = useParams()
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [path, setPath] = useState("");
useEffect(() => {
if(id){
fetchWithCache(`/api/markdown/${id}`)
.then((data) => {
setTitle(data.title);
setContent(data.content);
setPath(data.path);
})
.catch((err) => {console.error("failed to load markdown", err)});
}
}, [id]);
const handleSave = () => {
const url = id ? `/api/markdown/${id}` : "/api/markdown";
const method = id ? "PUT" : "POST";
fetch (url, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("accessToken")}`,
},
body: JSON.stringify({title, content, path}),
})
.then((res) => {
if (res.ok) {
navigate("/");
}else {
return res.json().then((data) => {
throw new Error(data.error || "Failed to save markdown");
})
}
})
.catch((err) => {console.error("failed to load markdown", err)});
};
const hasPermission = roles.includes("admin") || roles.includes("creator");
if(! hasPermission)
return (
<div> Can not crete(Permission Denied)</div>
);
return (
<div>
<h2>{id ? "Edit Markdown" : "Create Markdown"}</h2>
<form>
<div>
<label>
Title:
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</label>
</div>
<div>
<label>
Path:
<input
type="text"
value={path}
onChange={(e) => setPath(e.target.value)}
/>
</label>
</div>
<div>
<label>
Content:
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</label>
</div>
<button type="button" onClick={handleSave}>
Save
</button>
</form>
</div>
);
}
export default MarkdownEditor;

View File

@@ -3,7 +3,7 @@
import React, {useContext} from "react";
import { Link } from "react-router-dom";
import "./MainNavigation.css";
import {AuthContext} from "../AuthProvider";
import {AuthContext} from "../../AuthProvider";
const MainNavigation = () => {
const { user, login, logout } = useContext(AuthContext);

View File

@@ -3,7 +3,8 @@
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import "./SideNavigation.css";
import {fetchWithCache} from "../utils/fetchWithCache";
import {fetchWithCache} from "../../utils/fetchWithCache";
import PermissionGuard from "../PermissionGuard";
const SideNavigation = () => {
const [markdowns, setMarkdowns] = useState([]);
@@ -40,6 +41,12 @@ const SideNavigation = () => {
function renderTree(node, basePath = "") {
return (
<ul>
<li>
<PermissionGuard rolesRequired={["admin", "creator"]} >
<Link to="/markdown/create">Create New Markdown</Link>
</PermissionGuard>
</li>
{Object.entries(node).map(([key, value]) => {
if (value.markdown) {
return (
@@ -63,7 +70,7 @@ const SideNavigation = () => {
return (
<nav className="side-navigation">
<h3>Markdown Directory</h3>
<h3>Markdown Directory</h3>
{tree ? renderTree(tree) : <p>Loading...</p>}
</nav>
);

View File

@@ -0,0 +1,15 @@
import {useContext} from "react";
import {AuthContext} from "../AuthProvider";
const PermissionGuard = ({rolesRequired, children}) => {
const { roles = [] } = useContext(AuthContext);
const hasPermission = rolesRequired.some((role) => roles.includes(role));
if (!hasPermission) {
console.log("F");
return null;
}
return children;
}
export default PermissionGuard;

View File

@@ -20,10 +20,9 @@ const config = async () => {
redirect_uri: `${appConfig.serverHost}/callback`,
post_logout_redirect_uri: appConfig.serverHost,
response_type: "code",
scope: "openid profile email",
scope: "openid profile email roles",
};
console.log(appConfig);
};