diff --git a/client.mjs b/client.mjs index a05a777..89904df 100644 --- a/client.mjs +++ b/client.mjs @@ -1,186 +1,237 @@ export class Client { - /** - * Должен возвращать имя пользователя или null - * если пользователь не залогинен - * - * @return {Promise} username - * */ - async getUser() { - throw new Error("Not implemented"); - } - - /** - * Должен логинить пользователя с именем username - * и возвращать его имя - * - * @param {string} username - * @return {Promise} username - * */ - async loginUser(username) { - throw new Error("Not implemented"); - } - - /** - * Должен разлогинивать текущего пользователя - * - * @return {void} - * */ - async logoutUser() { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о компании - * - * @typedef {Object} Headquarters - * @property {string} address - * @property {string} city - * @property {string} state - * - * @typedef {Object} About - * @property {string} founder - * @property {string} founded - * @property {number} employees - * @property {string} ceo - * @property {string} coo - * @property {string} cto - * @property {number} valuation - * @property {Headquarters} headquarters - * @property {string} summary - * @return {Promise} - * */ - async getInfo() { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о всех событиях - * - * @typedef {Object} EventBrief - * @property {number} id - * @property {string} title - * - * @return {Promise} - * */ - async getHistory() { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о запрошенном событии - * - * @typedef {Object} EventFull - * @property {number} id - * @property {string} title - * @property {string} event_date_utc - * @property {string} details - * @property {Object.} links - * - * @param {number} id - * @return {Promise} - * */ - async getHistoryEvent(id) { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о всех ракетах - * - * @typedef {Object} RocketBrief - * @property {number} rocket_id - * @property {string} rocket_name - * - * @return {Promise} - * */ - async getRockets() { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о запрошенной ракете - * - * @typedef {Object} RocketFull - * @property {number} rocket_id - * @property {string} rocket_name - * @property {string} first_flight - * @property {string} description - * @property {string} wikipedia - * @property {string[]} flickr_images - * Смотри источник данных: - * @property {Object} height - * @property {Object} diameter - * @property {Object} mass - * @property {Object} engines - * @property {Object} first_stage - * @property {Object} second_stage - * - * @param {string} id - * @return {Promise} - * */ - async getRocket(id) { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о машине в космосе - * - * @typedef {Object} Roadster - * @property {string} name - * @property {string} launch_date_utc - * @property {string} details - * @property {number} earth_distance_km - * @property {number} mars_distance_km - * @property {string} wikipedia - * - * @return {Promise} - * */ - async getRoadster() { - throw new Error("Not implemented"); - } - - /** - * Должен возвращать информацию о всех посланных на Марс предметах - * - * @typedef {Object} Item - * @property {!string} id - * @property {!string} name - * @property {!string} phone - * @property {?number} weight - * @property {?string} color - * @property {?boolean} important - * - * @return {Promise} - * */ - async getSentToMars() { - throw new Error("Not implemented"); - } - - /** - * Должен посылать на марс переданный предмет и - * возвращать информацию о всех посланных на Марс предметах - * - * @typedef {Object} ItemToSend - * @property {!string} name - * @property {!string} phone - * @property {?number} weight - * @property {?string} color - * @property {?boolean} important - * - * @param {ItemToSend} item - * @return {Promise} - * */ - async sendToMars(item) { - throw new Error("Not implemented"); - } - - /** - * Должен отменять отправку на марс переданного предмета и - * возвращать информацию о всех посланных на Марс предметах - * - * @param {Item} item - * @return {Promise} - * */ - async cancelSendingToMars(item) { - throw new Error("Not implemented"); - } -} + /** + * Должен возвращать имя пользователя или null + * если пользователь не залогинен + * + * @return {Promise} username + * */ + async getUser() { + const response = await fetch('/api/user/'); + return Promise.resolve(response.text()); + } + + /** + * Должен логинить пользователя с именем username + * и возвращать его имя + * + * @param {string} username + * @return {Promise} username + * */ + async loginUser(username) { + const response = await fetch(`/api/login/?username=${username}`, { + method: 'post', + }); + return Promise.resolve(response.text()); + } + + /** + * Должен разлогинивать текущего пользователя + * + * @return {void} + * */ + async logoutUser() { + await fetch(`/api/logout/`, { + method: 'post', + }); + } + + /** + * Должен возвращать информацию о компании + * + * @typedef {Object} Headquarters + * @property {string} address + * @property {string} city + * @property {string} state + * + * @typedef {Object} About + * @property {string} founder + * @property {string} founded + * @property {number} employees + * @property {string} ceo + * @property {string} coo + * @property {string} cto + * @property {number} valuation + * @property {Headquarters} headquarters + * @property {string} summary + * @return {Promise} + * */ + async getInfo() { + const response = await fetch('/api/info/'); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о всех событиях + * + * @typedef {Object} EventBrief + * @property {number} id + * @property {string} title + * + * @return {Promise} + * */ + async getHistory() { + const response = await fetch('/api/history/'); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о запрошенном событии + * + * @typedef {Object} EventFull + * @property {number} id + * @property {string} title + * @property {string} event_date_utc + * @property {string} details + * @property {Object.} links + * + * @param {number} id + * @return {Promise} + * */ + async getHistoryEvent(id) { + const response = await fetch(`/api/history/event/?id=${id}`); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о всех ракетах + * + * @typedef {Object} RocketBrief + * @property {number} rocket_id + * @property {string} rocket_name + * + * @return {Promise} + * */ + async getRockets() { + const response = await fetch(`/api/rockets/`); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о запрошенной ракете + * + * @typedef {Object} RocketFull + * @property {number} rocket_id + * @property {string} rocket_name + * @property {string} first_flight + * @property {string} description + * @property {string} wikipedia + * @property {string[]} flickr_images + * Смотри источник данных: + * @property {Object} height + * @property {Object} diameter + * @property {Object} mass + * @property {Object} engines + * @property {Object} first_stage + * @property {Object} second_stage + * + * @param {string} id + * @return {Promise} + * */ + async getRocket(id) { + const response = await fetch(`/api/rocket/?id=${id}`); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о машине в космосе + * + * @typedef {Object} Roadster + * @property {string} name + * @property {string} launch_date_utc + * @property {string} details + * @property {number} earth_distance_km + * @property {number} mars_distance_km + * @property {string} wikipedia + * + * @return {Promise} + * */ + async getRoadster() { + const response = await fetch(`/api/roadster/`); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен возвращать информацию о всех посланных на Марс предметах + * + * @typedef {Object} Item + * @property {!string} id + * @property {!string} name + * @property {!string} phone + * @property {?number} weight + * @property {?string} color + * @property {?boolean} important + * + * @return {Promise} + * */ + async getSentToMars() { + const response = await fetch(`/api/sendToMars/`); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен посылать на марс переданный предмет и + * возвращать информацию о всех посланных на Марс предметах + * + * @typedef {Object} ItemToSend + * @property {!string} name + * @property {!string} phone + * @property {?number} weight + * @property {?string} color + * @property {?boolean} important + * + * @param {ItemToSend} item + * @return {Promise} + * */ + async sendToMars(item) { + const response = await fetch(`/api/sendToMars/send/`, { + method: 'post', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + item: item + }), + }); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + + /** + * Должен отменять отправку на марс переданного предмета и + * возвращать информацию о всех посланных на Марс предметах + * + * @param {Item} item + * @return {Promise} + * */ + async cancelSendingToMars(item) { + const response = await fetch(`/api/sendToMars/cancel/`, { + method: 'delete', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + id: item.id, + }), + }); + if (response.ok) { + return Promise.resolve(response.json()); + } + } + } \ No newline at end of file diff --git a/server.mjs b/server.mjs index 75d9cbe..53a0f3c 100644 --- a/server.mjs +++ b/server.mjs @@ -7,21 +7,127 @@ import bodyParser from "body-parser"; import fetch from "node-fetch"; const rootDir = process.cwd(); -const port = 3000; +const port = 1400; const app = express(); +const items = new Map([]); +let id = 0; + +app.use(express.static('spa/build')); +app.use(bodyParser.json()); +app.use(cookieParser()); app.get("/client.mjs", (_, res) => { - res.header("Cache-Control", "private, no-cache, no-store, must-revalidate"); - res.sendFile(path.join(rootDir, "client.mjs"), { - maxAge: -1, - cacheControl: false, + res.header("Cache-Control", "private, no-cache, no-store, must-revalidate"); + res.sendFile(path.join(rootDir, "client.mjs"), { + maxAge: -1, + cacheControl: false, + }); +}); + +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(201).end(); +}); + +app.get('/api/info', async (req, res) => { + const info = await fetch('https://api.spacexdata.com/v3/info'); + if (info.ok) { + const json = await info.json(); + res.json(json); + } else { + res.status(info.status).end(); + } +}); + +app.get('/api/history', async (req, res) => { + const info = await fetch('https://api.spacexdata.com/v3/history'); + if (info.ok) { + const json = await info.json(); + res.json(json); + } else { + res.status(info.status).end(); + } +}); + +app.get('/api/history/event/', async (req, res) => { + const info = await fetch(`https://api.spacexdata.com/v3/history/${req.query.id}`); + if (info.ok) { + const json = await info.json(); + res.json(json); + } else { + res.status(info.status).end(); + } }); -app.get("/", (_, res) => { - res.send(":)"); +app.get('/api/rockets', async (req, res) => { + const info = await fetch('https://api.spacexdata.com/v3/rockets'); + if (info.ok) { + const json = await info.json(); + res.json(json); + } else { + res.status(info.status).end(); + } }); -app.listen(port, () => { - console.log(`App listening on port ${port}`); +app.get('/api/roadster', async (req, res) => { + const info = await fetch('https://api.spacexdata.com/v3/roadster'); + if (info.ok) { + const json = await info.json(); + res.json(json); + } else { + res.status(info.status).end(); + } }); + +app.get("/api/sendToMars", (req, res) => { + res.json(getResponse()); +}); + +app.post("/api/sendToMars/send", (req, res) => { + const item = req.body; + item.id = id; + items.set(id, item); + id++; + res.json(getResponse()); + res.status(200); +}); + +app.delete("/api/sendToMars/cancel", function (req, res) { + items.delete(req.body.id); + res.json(getResponse()); + res.status(200); +}); + +app.get("/*", (_, res) => { + 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}( https://localhost:${port} )`); +}); + +function getResponse() { + const response = []; + items.forEach((value) => { + response.push(value) + }); + return response; +} \ No newline at end of file