Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vmstarchenko #4

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules/
*.log
.#*
tmp*
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,32 @@ Middleware-функции могут выполнять такие задачи:

####Реализуйте набор middleware-функций

- _Middleware авторизации_.
Она должна проверять значение авторизационной куки (`authorize`),
в случае если оно неизвестно или отсутствует — отдавать 403 ошибку.
Если значение известно — передавать следующую функцию в стэке middleware.
- _Middleware авторизации_.
Она должна проверять значение авторизационной куки (`authorize`),
в случае если оно неизвестно или отсутствует — отдавать 403 ошибку.
Если значение известно — передавать следующую функцию в стэке middleware.
Например, если пришли кука `"authorize": "random authorize string"`, то считаем, что пользователь авторизован, а
если кука вообще не пришла или пришла как пустая строка, то нет

- _Middleware логирования времени исполнения_.
Замеряет время работы в миллисекундах (до тысячных) всех остальных middleware в стэке,
- _Middleware логирования времени исполнения_.
Замеряет время работы в миллисекундах (до тысячных) всех остальных middleware в стэке,
логирует это время в консоль, и устанавливает хедер со временем работы `X-Time`.
Например, по результатам работы, сервер должен ответить заголовком `X-Time: 112.432`

- _Middleware логирования запросов_.
- _Middleware логирования запросов_.
Логирует в консоль все урлы и методы вызовов к веб-приложению, устанавливает в ответ хедер `X-Request-Url`
Например, если был вызван метод `GET` для урла `/apiUrl`, то должен установиться заголовок `X-Request-Url: "GET /apiUrl"`

- _Middleware обработки ошибок_.
- _Middleware обработки ошибок_.
Обрабатывает все ошибки, произошедшие в предыдущих middleware, так же обрабатывает все некорректные запросы к серверу,
выводит их в консоль `console.error` и возвращает пустой ответ с хедером `X-Request-Error: "error message"`
выводит их в консоль `console.error` и возвращает пустой ответ с хедером `X-Request-Error: "error message"`
Если был некорректный вызов API, например `GET /incorrect` (без `/v1/`), то
возвращать ошибку `X-Request-Error: "Unknown request"`

####Реализуйте трансформирующий поток

Реализуйте два трансформирующих потока которые принимают строку и производят транслитерацию:

1. Ожидает строку на русском, делает _прямую_ (из кириллических символов в латинские) трансформацию
2. Ожидает строку на английском, делает _обратную_ (из латинских символов в кириллические) трансформацию

Expand Down Expand Up @@ -95,11 +95,11 @@ Middleware-функции могут выполнять такие задачи:

####Реализуйте веб-приложение

Реализуйте с помощью Express веб-приложение которое предоставляет доступ к файловой системе.
Приложение должно использовать все middleware из предыдущего задания и на лету производить транслитерацию.
Реализуйте с помощью Express веб-приложение которое предоставляет доступ к файловой системе.
Приложение должно использовать все middleware из предыдущего задания и на лету производить транслитерацию.
При старте приложение должно монтироваться к какой-либо директории на локальной файловой системе.

Если полученный URL ссылается на директорию, нужно отдать список файлов в директории.
Если полученный URL ссылается на директорию, нужно отдать список файлов в директории.
URL указывается относительно текущей дериктории

```
Expand All @@ -118,7 +118,7 @@ URL указывается относительно текущей дерикт
Если полученный URL ссылается на файл, нужно отдать JSON с транслитерированным содержимым файла.
Язык файла мы определяем по первому определяемому символу, по которому можно определить язык.
Например, для файла "12asd" - первые символы латинские, производим обратную транслитерацию.
В случае, если используются кириллические символы, нужно провести прямую транслитерацию;
В случае, если используются кириллические символы, нужно провести прямую транслитерацию;
в случае если латинские — обратную. В иных случаях нужно ответить ошибкой.

```
Expand All @@ -136,8 +136,8 @@ URL указывается относительно текущей дерикт

####Задания со звездочкой*

- Расширьте приложение возможностью монтироваться не только к локальной файловой системе,
но и к удаленным FTP- и WebDAV-серверам. Напишите в комментарии к коду небольшую инструкцию как переключаться,
- Расширьте приложение возможностью монтироваться не только к локальной файловой системе,
но и к удаленным FTP- и WebDAV-серверам. Напишите в комментарии к коду небольшую инструкцию как переключаться,
как пользоваться и как проверить что возвращаются нужные файлы. Если нужно будет светить логин-пароль, то
можете завести фейковый аккаунт
- Реализуйте трансформирующий поток, осуществляющий преобразование бинарных данных в Base64.
Expand Down
138 changes: 130 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,135 @@
'use strict';
;(function() {
"use strict";

const express = require('express');
const app = express();
// import
const express = require('express');
const fs = require('fs');

const PORT = process.env.PORT || 4000;
// custom import
const TransformStream = require('./transformStream');

app.listen(PORT, function () {
const app = express();

const PORT = process.env.PORT || 4000;

app.listen(PORT, function () {
console.log(`App is listen on ${PORT}`);
});
});

// log request time
app.use(function(req, res, next) {
req.requestTime = new Date().getTime();
next();
});

// add cookies
app.use(function (req, res, next) {
let cookiesString = (req.headers.cookie) ? req.headers.cookie : "",
cookies = {}, _;
let cookiesList = cookiesString.split(';');
for (let i=0, size=cookiesList.length; i < size; ++i) {
_ = cookiesList[i].split('=');
cookies[_[0]] = _[1];
}
req.cookies = cookies;
next();
});

// check authorized
app.use(function (req, res, next) {
if (isAuthorized(req)) {
next();
return;
}
res.sendStatus(403);
});

// log url
app.use(function (req, res, next) {
res.setHeader('X-Request-Url', req.method+' '+req.url);
console.log('X-Request-Url:', req.method+' '+req.url);
next();
});

// log time
app.use(function (req, res, next) {
let date = new Date();
let time = date.getTime() - req.requestTime + 1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А зачем ты прибавляешь единицу, чтобы тесты прошли:) Тебе нужно использовать другой механизм измерения времени выполнения запроса, который довал бы тебе точность до тысячных

res.setHeader('X-Time', time);
console.log('X-Time=' + time);
next();
});

// IMPORTANT. Это строка должна возвращать инстанс сервера

// views

app.get(/\/v1\/(.*)/, function (req, res) {
let path = simplifyPath(req.params[0]); // 0
// warn access
if (path === undefined) {
throw new Error();
}
path = './' + path;

if (fs.lstatSync(path).isDirectory()) { // is dir

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Синхронно, конечно не очень здорово, но ладно

fs.readdir(path, function (err, items) {
if (err) {
throw new Error();
}
items.push('.');
items.push('..');
let context = {
content: items,
};
res.send(context);
});
} else {

let stream = fs.createReadStream(path),
tstream = new TransformStream();

res.setHeader('Content-Type', 'application/json');
stream.pipe(tstream).pipe(res);

// stream.on('error', function(err) {res.end(err);});
}
});

app.get(/\/.*/, function (req, res) {
throw new Error();
// res.send('Test');
});

// log error
app.use(function (error, req, res, next) {
res.setHeader('X-Request-Error', "Unknown request");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На самом деле ты тут должен устанавливать в хедер то, что пришло тебе в error. У тебя же могут быть разные ошибки в приложении возникнуть, а ты будешь всегда одну и туже выставлять.
Если ты внимательно слушал лекцию, то если ты вызовешь next с параметром, то это означает что произошла ошибка и тебя сразу перекинет в мидлвару обработки ошибок

res.status(503).end();
});

// add path check
function simplifyPath(path) {
let newPath = [];
path = path.split('/');
let depth = 0;
for (let i=0, size=path.length; i < size; ++i) {
if (path[i] === '..') {
if (depth <= 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Хоть на это теста и нет, но тем не менее, это у тебя не будет работать, если запрос будет следующего вида ././../ т.е. в newPath будет что-то лежать, но ../ уведет тебя на уровень выше. Можешь попробовать доработать собственную версию, а можешь воспользоваться встроенным модулем path

return undefined;
depth -= 1;
newPath.pop();
} else {
depth += 1;
newPath.push(path[i]);
}
}
return newPath.join('/');
}

function isAuthorized(request) {
return !!(request && request.cookies && request.cookies.authorize);
}

module.exports = app;

// IMPORTANT. Это строка должна возвращать инстанс сервера
module.exports = app;
})();
144 changes: 72 additions & 72 deletions test/helpers/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,87 @@ const fs = require('fs');
const fsExtra = require('fs-extra');

module.exports = {
rndDir: () => {
let resp = {
create: () => {
return new Promise((resolve, reject) => {
fs.mkdtemp('./tmp-', (err, folder) => {
if (err) {
return reject(err);
}
resp.dirName = folder;
resolve();
});
});
},
rndDir: () => {
let resp = {
create: () => {
return new Promise((resolve, reject) => {
fs.mkdtemp('./tmp-', (err, folder) => {
if (err) {
return reject(err);
}
resp.dirName = folder;
resolve();
});
});
},

remove: () => {
return new Promise((resolve, reject) => {
if (!resp.dirName) {
return reject('no dir');
}
fsExtra.remove(resp.dirName, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
},
remove: () => {
return new Promise((resolve, reject) => {
if (!resp.dirName) {
return reject('no dir');
}
fsExtra.remove(resp.dirName, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
},

dirName: null
};
dirName: null
};

return resp;
},
return resp;
},

rndFile: (dir, text) => {
let filename = '/' + rndName();
let resp = {
create: () => {
return new Promise((resolve, reject) => {
fs.writeFile(dir + filename, text || 'test', (err) => {
if (err) {
return reject(err);
}
rndFile: (dir, text) => {
let filename = '/' + rndName();
let resp = {
create: () => {
return new Promise((resolve, reject) => {
fs.writeFile(dir + filename, text || 'test', (err) => {
if (err) {
return reject(err);
}

resp.filename = dir + filename;
resolve();
});
});
},
remove: () => {
return new Promise((resolve, reject) => {
if (!resp.filename) {
return reject('no file');
}
fsExtra.remove(resp.filename, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
},
filename: null
};
resp.filename = dir + filename;
resolve();
});
});
},
remove: () => {
return new Promise((resolve, reject) => {
if (!resp.filename) {
return reject('no file');
}
fsExtra.remove(resp.filename, (err) => {
if (err) {
return reject(err);
}
resolve();
});
});
},
filename: null
};

return resp;
return resp;

},
},

fileParser: (res, callback) => {
res.setEncoding('utf-8');
res.data = '';
res.on('data', function (chunk) {
res.data += chunk;
});
res.on('end', function () {
callback(null, JSON.parse(res.data));
});
}
fileParser: (res, callback) => {
res.setEncoding('utf-8');
res.data = '';
res.on('data', function (chunk) {
res.data += chunk;
});
res.on('end', function () {
callback(null, JSON.parse(res.data));
});
}
};

function rndName() {
return 'file' + String(Math.random()).slice(2);
return 'file' + String(Math.random()).slice(2);
}
Loading