Skip to content
Open

1-9 #36

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

Код приложения лежит в папке `spa`, собранная версия уже лежит в папке `spa/build`. Для выполнения задания трогать код приложения не потребуется, но если захочешь что-то поменять, не забудь установить зависимости (`npm install` в папке `spa`) и собрать новую версию приложения (`npm run build`).

0. Поставь зависимости и запусти сервер.
-0. Поставь зависимости и запусти сервер.

- Для этого перейди в директорию задачи и выполни команду `npm install`.
- После установки зависимостей, выполни команду `npm run start`.
- После запуска, перейди по адресу [localhost:3000](http://localhost:3000)

1. Сделай так, чтобы сервер смог отдавать статические файлы из директории `spa/build`. В express для этого есть middleware `express.static`. Подробнее можно прочитать [здесь](https://expressjs.com/en/starter/static-files.html)
-1. Сделай так, чтобы сервер смог отдавать статические файлы из директории `spa/build`. В express для этого есть middleware `express.static`. Подробнее можно прочитать [здесь](https://expressjs.com/en/starter/static-files.html)

2. Сделай так, чтобы при заходе на любой неизвестный адрес, сервер возвращал файл `spa/build/index.html`. В этом помогут специальные символы [в путях](https://expressjs.com/en/guide/routing.html#route-paths)

Expand Down
61 changes: 48 additions & 13 deletions client.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export class Client {
* @return {Promise<string | null>} username
* */
async getUser() {
throw new Error("Not implemented");
const response = await fetch("/api/user");
return response.text();
}

/**
Expand All @@ -17,7 +18,8 @@ export class Client {
* @return {Promise<string | null>} username
* */
async loginUser(username) {
throw new Error("Not implemented");
await fetch(`/api/login?username=${username}`);
return new Promise((resolve) => resolve(username));
}

/**
Expand All @@ -26,7 +28,7 @@ export class Client {
* @return {void}
* */
async logoutUser() {
throw new Error("Not implemented");
await fetch(`/api/logout`);
}

/**
Expand All @@ -50,7 +52,9 @@ export class Client {
* @return {Promise<About>}
* */
async getInfo() {
throw new Error("Not implemented");
const response = await fetch('/api/info');
if (response.ok)
return response.json();
}

/**
Expand All @@ -63,7 +67,9 @@ export class Client {
* @return {Promise<EventBrief[]>}
* */
async getHistory() {
throw new Error("Not implemented");
const response = await fetch('/api/history');
if (response.ok)
return response.json();
}

/**
Expand All @@ -80,7 +86,9 @@ export class Client {
* @return {Promise<EventFull>}
* */
async getHistoryEvent(id) {
throw new Error("Not implemented");
const response = await fetch('/api/history/event/?id=${id}');
if (response.ok)
return response.json();
}

/**
Expand All @@ -93,7 +101,9 @@ export class Client {
* @return {Promise<RocketBrief[]>}
* */
async getRockets() {
throw new Error("Not implemented");
const response = await fetch('/api/rockets');
if (response.ok)
return response.json();
}

/**
Expand All @@ -118,7 +128,9 @@ export class Client {
* @return {Promise<RocketFull>}
* */
async getRocket(id) {
throw new Error("Not implemented");
const response = await fetch('/api/rockets?id=${id}');
if (response.ok)
return response.json();
}

/**
Expand All @@ -135,7 +147,9 @@ export class Client {
* @return {Promise<Roadster>}
* */
async getRoadster() {
throw new Error("Not implemented");
const response = await fetch('/api/roadster');
if (response.ok)
return response.json();
}

/**
Expand All @@ -152,7 +166,10 @@ export class Client {
* @return {Promise<Item[]>}
* */
async getSentToMars() {
throw new Error("Not implemented");
const response = await fetch("/api/item");
if (response.ok) {
return new Promise(resolve => resolve(response.json()));
}
}

/**
Expand All @@ -170,7 +187,16 @@ export class Client {
* @return {Promise<Item[]>}
* */
async sendToMars(item) {
throw new Error("Not implemented");
const response = await fetch("/api/item", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(item),
});
if (response.ok) {
return new Promise(resolve => resolve(response.json()));
}
}

/**
Expand All @@ -181,6 +207,15 @@ export class Client {
* @return {Promise<Item[]>}
* */
async cancelSendingToMars(item) {
throw new Error("Not implemented");
const response = await fetch("/api/item", {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(item),
});
if (response.ok) {
return new Promise(resolve => resolve(response.json()));
}
}
}
}
146 changes: 137 additions & 9 deletions server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,147 @@ import fetch from "node-fetch";
const rootDir = process.cwd();
const port = 3000;
const app = express();
const items = new Map([]);
let id = 1;

const checkRedirect = function (req, res, next) {
if (!req.cookies.username && req.path !== "/login") {
res.redirect("/login");
}
next();
};

app.use(express.static('spa/build'));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(checkRedirect);

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.get("/api/login", (req, res) => {
const userName = req.query.username;
res.cookie("username", userName, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
});
res.send(userName);
});

app.get("/login", (_, res) => {
res.sendFile(path.join(rootDir, 'spa/build/index.html'));
});

app.get("/", (_, res) => {
res.send(":)");
app.get("/api/logout", (_, res) => {
res.clearCookie("username");
res.sendStatus(201);
});

app.listen(port, () => {
console.log(`App listening on port ${port}`);
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('/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.get('/api/rockets', async (req, res) => {
const info = await fetch(`https://api.spacexdata.com/v3/rockets/${req.query.id}`);
if (info.ok) {
const json = await info.json();
res.json(json);
} else {
res.status(info.status).end();
}
});

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/item", (req, res) => {
res.json(getData());
});

app.post("/api/item", (req, res) => {
const item = req.body;
item.id = id;
items.set(id, item);
id++;
res.json(getData());
res.status(200);
});

app.delete("/api/item", function (req, res) {
items.delete(req.body.id);
res.json(getData());
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}`);
});

function getData() {
const result = [];
items.forEach((value) => {
result.push(value)
});
return result;
}