diff --git a/client.mjs b/client.mjs index a05a777..1ffb519 100644 --- a/client.mjs +++ b/client.mjs @@ -6,7 +6,8 @@ export class Client { * @return {Promise} username * */ async getUser() { - throw new Error("Not implemented"); + const respond = await fetch('/api/user/'); + return respond.text(); } /** @@ -17,7 +18,10 @@ export class Client { * @return {Promise} username * */ async loginUser(username) { - throw new Error("Not implemented"); + const respond = await fetch(`/api/login/?username=${username}`, { + method: 'post', + }); + return respond.text(); } /** @@ -26,7 +30,9 @@ export class Client { * @return {void} * */ async logoutUser() { - throw new Error("Not implemented"); + await fetch(`/api/logout/`, { + method: 'post', + }); } /** @@ -50,7 +56,10 @@ export class Client { * @return {Promise} * */ async getInfo() { - throw new Error("Not implemented"); + const respond = await fetch('/api/info/'); + if (respond.ok) { + return respond.json(); + } } /** @@ -63,7 +72,10 @@ export class Client { * @return {Promise} * */ async getHistory() { - throw new Error("Not implemented"); + const respond = await fetch('/api/history/'); + if (respond.ok) { + return respond.json(); + } } /** @@ -80,7 +92,10 @@ export class Client { * @return {Promise} * */ async getHistoryEvent(id) { - throw new Error("Not implemented"); + const respond = await fetch(`/api/history/event/?id=${id}`); + if (respond.ok) { + return respond.json(); + } } /** @@ -93,7 +108,10 @@ export class Client { * @return {Promise} * */ async getRockets() { - throw new Error("Not implemented"); + const respond = await fetch(`/api/rockets/`); + if (respond.ok) { + return respond.json(); + } } /** @@ -118,7 +136,10 @@ export class Client { * @return {Promise} * */ async getRocket(id) { - throw new Error("Not implemented"); + const respond = await fetch(`/api/rocket/?id=${id}`); + if (respond.ok) { + return respond.json(); + } } /** @@ -135,7 +156,10 @@ export class Client { * @return {Promise} * */ async getRoadster() { - throw new Error("Not implemented"); + const respond = await fetch(`/api/roadster/`); + if (respond.ok) { + return respond.json(); + } } /** @@ -152,7 +176,10 @@ export class Client { * @return {Promise} * */ async getSentToMars() { - throw new Error("Not implemented"); + const respond = await fetch(`/api/dispatch/`); + if (respond.ok) { + return respond.json(); + } } /** @@ -170,7 +197,19 @@ export class Client { * @return {Promise} * */ async sendToMars(item) { - throw new Error("Not implemented"); + const respond = await fetch(`/api/dispatch/send/`, { + method: 'post', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + item: item + }), + }); + if (respond.ok) { + return respond.json(); + } } /** @@ -181,6 +220,18 @@ export class Client { * @return {Promise} * */ async cancelSendingToMars(item) { - throw new Error("Not implemented"); + const respond = await fetch(`/api/dispatch/cancel/`, { + method: 'delete', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: item.id, + }), + }); + if (respond.ok) { + return respond.json(); + } } } diff --git a/server.mjs b/server.mjs index 75d9cbe..477fbd8 100644 --- a/server.mjs +++ b/server.mjs @@ -9,6 +9,39 @@ import fetch from "node-fetch"; const rootDir = process.cwd(); const port = 3000; const app = express(); +const mars = new Mars(); + +class Mars { + #sentThings = []; + #currentId = 0; + + constructor() { + } + + send(item) { + this.#sentThings.push({ + ...item, + id: this.#currentId++, + }); + } + + cancel(id) { + this.#sentThings.splice(this.#sentThings.indexOf(this.#sentThings.find(e => e.id === id)), 1); + } + + getDispatchedThings() { + return this.#sentThings.slice(); + } +} + +function cookieViewer(req, res, next) { + req.haveCookies = Object.keys(req.cookies).length > 0; + next(); +} + +app.use(cookieParser()); +app.use(express.static(path.join(rootDir, 'spa/build'))); +app.use(bodyParser.json()) app.get("/client.mjs", (_, res) => { res.header("Cache-Control", "private, no-cache, no-store, must-revalidate"); @@ -18,10 +51,165 @@ app.get("/client.mjs", (_, res) => { }); }); -app.get("/", (_, res) => { - res.send(":)"); +app.get("/login", (_, res) => { + res.sendFile(path.join(rootDir, 'spa/build/index.html')) +}); + +app.get('/api/user/', (req, res) => { + const username = req.cookies.username; + res.send(username); +}); + +app.post('/api/login/', (req, res) => { + const username = req.query.username; + res.cookie('username', username, { + httpOnly: true, + secure: true, + sameSite: 'Strict', + }); + res.send(username); +}); + +app.post('/api/logout/', (req, res) => { + res.clearCookie('username'); + res.status(200).end(); +}); + +app.get('/api/info/', async (req, res) => { + const resAPI = await fetch('https://api.spacexdata.com/v4/company'); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json({ + founder: json.founder, + founded: json.founded, + employees: json.employees, + ceo: json.ceo, + coo: json.coo, + cto: json.cto, + valuation: json.valuation, + headquarters: json.headquarters, + summary: json.summary, + }); + } else { + res.status(resAPI.status).end(); + } +}); + +app.get('/api/history/', async (req, res) => { + const resAPI = await fetch('https://api.spacexdata.com/v4/history'); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json(json.map(e => { + return { + id: e.id, + title: e.title, + } + })); + } else { + res.status(resAPI.status).end(); + } +}); + +app.get('/api/history/event/', async (req, res) => { + const resAPI = await fetch(`https://api.spacexdata.com/v4/history/${req.query.id}`); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json({ + details: json.details, + event_date_utc: json.event_date_utc, + id: json.id, + links: json.links, + title: json.title, + }); + } else { + res.status(resAPI.status).end(); + } +}); + +app.get('/api/rockets/', async (req, res) => { + const resAPI = await fetch(`https://api.spacexdata.com/v4/rockets`); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json(json.map(e => { + return { + rocket_id: e.id, + rocket_name: e.name, + }; + })); + } else { + res.status(resAPI.status).end(); + } +}); + +app.get('/api/rocket/', async (req, res) => { + const resAPI = await fetch(`https://api.spacexdata.com/v4/rockets/${req.query.id}`); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json({ + rocket_id: json.id, + rocket_name: json.name, + first_flight: json.first_flight, + description: json.description, + wikipedia: json.wikipedia, + flickr_images: json.flickr_images, + height: json.height, + diameter: json.diameter, + mass: json.mass, + engines: json.engines, + first_stage: json.first_stage, + second_stage: json.second_stage, + }); + } else { + res.status(resAPI.status).end(); + } +}); + +app.get('/api/roadster/', async (req, res) => { + const resAPI = await fetch(`https://api.spacexdata.com/v4/roadster`); + if (resAPI.ok) { + const json = await resAPI.json(); + res.json({ + name: json.name, + launch_date_utc: json.launch_date_utc, + details: json.details, + earth_distance_km: json.earth_distance_km, + mars_distance_km: json.mars_distance_km, + wikipedia: json.wikipedia, + }); + } else { + res.status(resAPI.status).end(); + } }); -app.listen(port, () => { - console.log(`App listening on port ${port}`); +app.get('/api/dispatch/', (req, res) => { + res.json(mars.getDispatchedThings()) }); + +app.post('/api/dispatch/send/', (req, res) => { + mars.send(req.body.item) + res.json(mars.getDispatchedThings()); +}); + +app.delete('/api/dispatch/cancel/', (req, res) => { + mars.cancel(req.body.id) + res.json(mars.getDispatchedThings()); +}); + +app.use(cookieViewer); + +app.get("/*", (req, res) => { + if (!req.haveCookies) { + res.redirect('/login'); + } else { + res.sendFile(path.join(rootDir, 'spa/build/index.html')); + } +}); + +https + .createServer({ + key: fs.readFileSync('./certs/server.key'), + cert: fs.readFileSync('./certs/server.cert') + }, app) + .listen(port, () => { + console.log(`App listening on port ${port}. Go to https://localhost:3000/`); + });