diff --git a/client/package-lock.json b/client/package-lock.json index 4163d30..eddae0c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16,6 +16,7 @@ "@types/react": "^18.0.6", "@types/react-dom": "^18.0.2", "axios": "^0.26.1", + "cors": "^2.8.5", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.3.0", @@ -5402,6 +5403,18 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", @@ -19826,6 +19839,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", diff --git a/client/package.json b/client/package.json index b09883d..e829220 100644 --- a/client/package.json +++ b/client/package.json @@ -11,6 +11,7 @@ "@types/react": "^18.0.6", "@types/react-dom": "^18.0.2", "axios": "^0.26.1", + "cors": "^2.8.5", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router-dom": "^6.3.0", diff --git a/client/src/pages/account.tsx b/client/src/pages/account.tsx index 0fb33ff..a8c79f2 100644 --- a/client/src/pages/account.tsx +++ b/client/src/pages/account.tsx @@ -5,23 +5,24 @@ import {SAPIBase} from "../tools/api"; import "./css/account.css"; const AccountPage = () => { - const [ SAPIKEY, setSAPIKEY ] = React.useState(""); + const [ ID, setID ] = React.useState(""); + const [ PW, setPW ] = React.useState(""); const [ NBalance, setNBalance ] = React.useState("Not Authorized"); const [ NTransaction, setNTransaction ] = React.useState(0); const getAccountInformation = () => { const asyncFun = async() => { interface IAPIResponse { balance: number }; - const { data } = await axios.post(SAPIBase + '/account/getInfo', { credential: SAPIKEY }); + const { data } = await axios.post(SAPIBase + '/account/getInfo', { id:ID, pw:PW }); setNBalance(data.balance); } - asyncFun().catch((e) => window.alert(`AN ERROR OCCURED: ${e}`)); + asyncFun().catch((e) => alert(`AN ERROR OCCURED: ${e}`)); } const performTransaction = ( amount: number ) => { const asyncFun = async() => { interface IAPIResponse { success: boolean, balance: number, msg: string }; - const { data } = await axios.post(SAPIBase + '/account/transaction', { credential: SAPIKEY, amount: NBalance }); + const { data } = await axios.post(SAPIBase + '/account/transaction', { id:ID, pw:PW, amount: amount }); setNTransaction(0); if (!data.success) { window.alert('Transaction Failed:' + data.msg); @@ -39,7 +40,8 @@ const AccountPage = () => {

Account

- Enter API Key: setSAPIKEY(e.target.value)}/> + Enter ID: setID(e.target.value)}/> + Enter PW: setPW(e.target.value)}/>
diff --git a/client/src/pages/feed.tsx b/client/src/pages/feed.tsx index b64c53b..1aa1835 100644 --- a/client/src/pages/feed.tsx +++ b/client/src/pages/feed.tsx @@ -4,11 +4,11 @@ import { SAPIBase } from "../tools/api"; import Header from "../components/header"; import "./css/feed.css"; -interface IAPIResponse { id: number, title: string, content: string } +interface IAPIResponse { id: number, title: string, content: string, toggle:boolean } const FeedPage = (props: {}) => { const [ LAPIResponse, setLAPIResponse ] = React.useState([]); - const [ NPostCount, setNPostCount ] = React.useState(0); + const [ NPostCount, setNPostCount ] = React.useState(1); // 처음에 example 들어있음 const [ SNewPostTitle, setSNewPostTitle ] = React.useState(""); const [ SNewPostContent, setSNewPostContent ] = React.useState(""); @@ -17,7 +17,6 @@ const FeedPage = (props: {}) => { const asyncFun = async () => { const { data } = await axios.get( SAPIBase + `/feed/getFeed?count=${ NPostCount }`); console.log(data); - // const data = [ { id: 0, title: "test1", content: "Example body" }, { id: 1, title: "test2", content: "Example body" }, { id: 2, title: "test3", content: "Example body" } ].slice(0, NPostCount); if (BComponentExited) return; setLAPIResponse(data); }; @@ -27,7 +26,7 @@ const FeedPage = (props: {}) => { const createNewPost = () => { const asyncFun = async () => { - await axios.post( SAPIBase + '/feed/addFeed', { title: SNewPostTitle, content: SNewPostContent } ); + await axios.post( SAPIBase + '/feed/addFeed', { title: SNewPostTitle, content: SNewPostContent, toggle:false } ); setNPostCount(NPostCount + 1); setSNewPostTitle(""); setSNewPostContent(""); @@ -44,6 +43,38 @@ const FeedPage = (props: {}) => { asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); } + const modifyPost = (id: string, altTitle: string, altContent: string) => { + const asyncFun = async() => { + await axios.post( SAPIBase + '/feed/modifyFeed', { id: id, title: altTitle, content: altContent} ); + setLAPIResponse(() => ( + LAPIResponse.map((value) => { + const match = (value.id === parseInt(id)); + if (match) { + return {id: value.id, title: altTitle, content: altContent, toggle:value.toggle}; + } + return value; + }))); + + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + + const togglePost = (id: string) => { + const asyncFun = async () => { + // One can set X-HTTP-Method header to DELETE to specify deletion as well + await axios.post( SAPIBase + '/feed/toggleFeed', { id: id } ); + setLAPIResponse(() => ( + LAPIResponse.map((value) => { + const match = (value.id === parseInt(id)); + if (match) { + return {id: value.id, title: value.title, content: value.content, toggle:!value.toggle}; + } + return value; + }))); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + return (
@@ -57,9 +88,21 @@ const FeedPage = (props: {}) => {
{ LAPIResponse.map( (val, i) =>
+

togglePost(`${val.id}`)}>{val.toggle?"★":"☆"}

{/* ?"★":"☆" */}
deletePost(`${val.id}`)}>ⓧ

{ val.title }

{ val.content }

+ +
) }
diff --git a/seminar/src/index.js b/seminar/src/index.js index b668b83..18a9c63 100644 --- a/seminar/src/index.js +++ b/seminar/src/index.js @@ -3,6 +3,7 @@ const cors = require('cors'); const statusRouter = require('./routes/status'); const feedRouter = require('./routes/feed'); +const accountRouter = require('./routes/account'); const app = express(); const port = 8080; @@ -23,6 +24,7 @@ app.use(cors(corsOptions)); app.use('/status', statusRouter); app.use('/feed', feedRouter); +app.use('/account', accountRouter); app.listen(port, () => { console.log(`Example App Listening @ http://localhost:${ port }`); diff --git a/seminar/src/middleware/auth.js b/seminar/src/middleware/auth.js new file mode 100644 index 0000000..8e0cd7f --- /dev/null +++ b/seminar/src/middleware/auth.js @@ -0,0 +1,13 @@ +const authMiddleware = (req, res, next) => { + key=process.env.API_KEY || process.env.USER; + if (req.body.id === key && req.body.pw === key) { + console.log("[AUTH-MIDDLEWARE] Authorized User"); + next(); + } + else { + console.log("[AUTH-MIDDLEWARE] Not Authorized User"); + res.status(401).json({ error: "Not Authorized" }); + } +} + +module.exports = authMiddleware; \ No newline at end of file diff --git a/seminar/src/routes/account.js b/seminar/src/routes/account.js new file mode 100644 index 0000000..51572f8 --- /dev/null +++ b/seminar/src/routes/account.js @@ -0,0 +1,50 @@ +const express = require('express'); +const authMiddleware = require('../middleware/auth'); + +const router = express.Router(); + +class BankDB { + static _inst_; + static getInst = () => { + if ( !BankDB._inst_ ) BankDB._inst_ = new BankDB(); + return BankDB._inst_; + } + + #total = 10000; + + constructor() { console.log("[Bank-DB] DB Init Completed"); } + + getBalance = () => { + return { success: true, data: this.#total }; + } + + transaction = ( amount ) => { + this.#total += amount; + return { success: true, data: this.#total }; + } +} + +const bankDBInst = BankDB.getInst(); + +router.post('/getInfo', authMiddleware, (req, res) => { + try { + const { success, data } = bankDBInst.getBalance(); + if (success) return res.status(200).json({ balance: data }); + else return res.status(500).json({ error: data }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}); + +router.post('/transaction', authMiddleware, (req, res) => { + try { + const { amount } = req.body; + const { success, data } = bankDBInst.transaction( parseInt(amount) ); + if (success) res.status(200).json({ success: true, balance: data, msg: "Transaction success" }); + else res.status(500).json({ error: data }) + } catch (e) { + return res.status(500).json({ error: e }); + } +}) + +module.exports = router; \ No newline at end of file diff --git a/seminar/src/routes/feed.js b/seminar/src/routes/feed.js index f9c19d9..88b7805 100644 --- a/seminar/src/routes/feed.js +++ b/seminar/src/routes/feed.js @@ -9,7 +9,7 @@ class FeedDB { return FeedDB._inst_; } - #id = 1; #itemCount = 1; #LDataDB = [{ id: 0, title: "test1", content: "Example body" }]; + #id = 1; #itemCount = 1; #LDataDB = [{ id: 0, title: "test1", content: "Example body", toggle: false }]; constructor() { console.log("[Feed-DB] DB Init Completed"); } @@ -21,7 +21,7 @@ class FeedDB { insertItem = ( item ) => { const { title, content } = item; - this.#LDataDB.push({ id: this.#id, title, content }); + this.#LDataDB.push({ id: this.#id, title, content, toggle:false }); this.#id++; this.#itemCount++; return true; } @@ -36,6 +36,34 @@ class FeedDB { if (BItemDeleted) id--; return BItemDeleted; } + + modifyItem = (item) => { + const { id, title, content } = item; + let BItemModified = false; + + this.#LDataDB = this.#LDataDB.map((value) => { + const match = (value.id === parseInt(id)); + if (match) { + BItemModified=true; + return {id: value.id, title, content, toggle: value.toggle}; + } + return value; + }); + return BItemModified; + } + + toggleItem = (id) => { + let BItemToggled = false; + this.#LDataDB = this.#LDataDB.map((value) => { + const match = (value.id === id); + if (match) { + BItemToggled=true; + return {id: value.id, title: value.title, content:value.content, toggle: !value.toggle}; + } + return value; + }); + return BItemToggled; + } } const feedDBInst = FeedDB.getInst(); @@ -62,6 +90,7 @@ router.post('/addFeed', (req, res) => { } }); + router.post('/deleteFeed', (req, res) => { try { const { id } = req.body; @@ -73,4 +102,26 @@ router.post('/deleteFeed', (req, res) => { } }) +router.post('/modifyFeed', (req, res) => { + try { + const { id, title, content } = req.body; + const modifyResult=feedDBInst.modifyItem({id, title,content}); + if (!modifyResult) return res.status(500).json({ error: "Failed to modify" }) + else return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}) + +router.post('/toggleFeed', (req, res) => { + try{ + const {id} = req.body; + const toggleResult = feedDBInst.toggleItem(parseInt(id)); + if (!toggleResult) return res.status(500).json({ error: "No item deleted" }) + else return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({error: e }); + } +}) + module.exports = router; \ No newline at end of file