markdown editor
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
|
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
|
||||||
import MainNavigation from "./components/MainNavigation";
|
import MainNavigation from "./components/Navigations/MainNavigation";
|
||||||
import SideNavigation from "./components/SideNavigation";
|
import SideNavigation from "./components/Navigations/SideNavigation";
|
||||||
import MarkdownContent from "./components/MarkdownContent";
|
import MarkdownContent from "./components/MarkdownContent";
|
||||||
|
import MarkdownEditor from "./components/Markdowns/MarkdownEditor";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
import Callback from "./Callback";
|
import Callback from "./Callback";
|
||||||
import {config} from "./confs/appConfig";
|
import {config} from "./confs/appConfig";
|
||||||
@@ -20,6 +21,8 @@ const App = () => {
|
|||||||
<Route path="/markdown/:id" element={<MarkdownContent />} />
|
<Route path="/markdown/:id" element={<MarkdownContent />} />
|
||||||
<Route path="/callback" element={<Callback />} />
|
<Route path="/callback" element={<Callback />} />
|
||||||
<Route path="/test" element={<h1>TEST</h1>}></Route>
|
<Route path="/test" element={<h1>TEST</h1>}></Route>
|
||||||
|
<Route path="/markdown/create" element={<MarkdownEditor />}></Route>
|
||||||
|
<Route path="/markdown/edit/:id" element={<MarkdownEditor />}></Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ export const AuthContext = createContext({
|
|||||||
user: null,
|
user: null,
|
||||||
login: () => {},
|
login: () => {},
|
||||||
logout: () => {},
|
logout: () => {},
|
||||||
|
roles: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const AuthProvider = ({ children }) => {
|
const AuthProvider = ({ children }) => {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
|
const [roles, setRoles] = useState([]);
|
||||||
const userManager = new UserManager(appConfig.oidcConfig);
|
const userManager = new UserManager(appConfig.oidcConfig);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -19,11 +21,16 @@ const AuthProvider = ({ children }) => {
|
|||||||
if (user && !user.expired) {
|
if (user && !user.expired) {
|
||||||
setUser(user);
|
setUser(user);
|
||||||
localStorage.setItem("accessToken", user.access_token);
|
localStorage.setItem("accessToken", user.access_token);
|
||||||
|
const clientRoles = user?.profile?.resource_access?.[appConfig.kc_client_id]?.roles || [];
|
||||||
|
setRoles(clientRoles);
|
||||||
} else if (user && user.expired) {
|
} else if (user && user.expired) {
|
||||||
userManager.signinSilent()
|
userManager.signinSilent()
|
||||||
.then((newUser) => {
|
.then((newUser) => {
|
||||||
setUser(newUser);
|
setUser(newUser);
|
||||||
localStorage.setItem("accessToken", newUser.access_token);
|
localStorage.setItem("accessToken", newUser.access_token);
|
||||||
|
const clientRoles =
|
||||||
|
newUser?.profile?.resource_access?.[appConfig.kc_client_id]?.roles || [];
|
||||||
|
setRoles(clientRoles);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -33,7 +40,10 @@ const AuthProvider = ({ children }) => {
|
|||||||
}, [userManager]);
|
}, [userManager]);
|
||||||
|
|
||||||
const login = () => {
|
const login = () => {
|
||||||
userManager.signinRedirect().catch((err) => {
|
userManager
|
||||||
|
.signinRedirect()
|
||||||
|
.catch(
|
||||||
|
(err) => {
|
||||||
console.log(appConfig);
|
console.log(appConfig);
|
||||||
console.log(err);
|
console.log(err);
|
||||||
});
|
});
|
||||||
@@ -41,7 +51,7 @@ const AuthProvider = ({ children }) => {
|
|||||||
const logout = () => userManager.signoutRedirect();
|
const logout = () => userManager.signoutRedirect();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ user, login, logout }}>
|
<AuthContext.Provider value={{ user, roles, login, logout }}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
97
src/components/Markdowns/MarkdownEditor.js
Normal file
97
src/components/Markdowns/MarkdownEditor.js
Normal 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;
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import React, {useContext} from "react";
|
import React, {useContext} from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import "./MainNavigation.css";
|
import "./MainNavigation.css";
|
||||||
import {AuthContext} from "../AuthProvider";
|
import {AuthContext} from "../../AuthProvider";
|
||||||
|
|
||||||
const MainNavigation = () => {
|
const MainNavigation = () => {
|
||||||
const { user, login, logout } = useContext(AuthContext);
|
const { user, login, logout } = useContext(AuthContext);
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import "./SideNavigation.css";
|
import "./SideNavigation.css";
|
||||||
import {fetchWithCache} from "../utils/fetchWithCache";
|
import {fetchWithCache} from "../../utils/fetchWithCache";
|
||||||
|
import PermissionGuard from "../PermissionGuard";
|
||||||
|
|
||||||
const SideNavigation = () => {
|
const SideNavigation = () => {
|
||||||
const [markdowns, setMarkdowns] = useState([]);
|
const [markdowns, setMarkdowns] = useState([]);
|
||||||
@@ -40,6 +41,12 @@ const SideNavigation = () => {
|
|||||||
function renderTree(node, basePath = "") {
|
function renderTree(node, basePath = "") {
|
||||||
return (
|
return (
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>
|
||||||
|
<PermissionGuard rolesRequired={["admin", "creator"]} >
|
||||||
|
<Link to="/markdown/create">Create New Markdown</Link>
|
||||||
|
</PermissionGuard>
|
||||||
|
|
||||||
|
</li>
|
||||||
{Object.entries(node).map(([key, value]) => {
|
{Object.entries(node).map(([key, value]) => {
|
||||||
if (value.markdown) {
|
if (value.markdown) {
|
||||||
return (
|
return (
|
||||||
15
src/components/PermissionGuard.js
Normal file
15
src/components/PermissionGuard.js
Normal 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;
|
||||||
@@ -20,10 +20,9 @@ const config = async () => {
|
|||||||
redirect_uri: `${appConfig.serverHost}/callback`,
|
redirect_uri: `${appConfig.serverHost}/callback`,
|
||||||
post_logout_redirect_uri: appConfig.serverHost,
|
post_logout_redirect_uri: appConfig.serverHost,
|
||||||
response_type: "code",
|
response_type: "code",
|
||||||
scope: "openid profile email",
|
scope: "openid profile email roles",
|
||||||
|
|
||||||
};
|
};
|
||||||
console.log(appConfig);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user