From e4b89cb9122596ba2bf981ec6161e2c46ac0a1f7 Mon Sep 17 00:00:00 2001 From: wwwildcat Date: Sun, 29 Sep 2019 01:49:43 +0300 Subject: [PATCH 1/3] format + small fixes --- callbacks/addRepository.js | 50 ++++++------ callbacks/deleteRepository.js | 44 +++++------ callbacks/getCommits.js | 144 +++++++++++++++++----------------- callbacks/getDiff.js | 68 ++++++++-------- callbacks/getFile.js | 45 ++++++----- callbacks/getRepos.js | 18 ++--- callbacks/getRepository.js | 92 +++++++++++++--------- callbacks/getRepositoryAll.js | 58 ++++++++++++++ package-lock.json | 4 +- package.json | 6 +- 10 files changed, 305 insertions(+), 224 deletions(-) create mode 100644 callbacks/getRepositoryAll.js diff --git a/callbacks/addRepository.js b/callbacks/addRepository.js index fed1951..c493a31 100644 --- a/callbacks/addRepository.js +++ b/callbacks/addRepository.js @@ -2,32 +2,32 @@ const fs = require('fs'); const path = require('path'); const {execFile} = require('child_process'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка POST /api/repos + { url: ‘repo-url’ } module.exports = function(request, response) { - let pathToRepo = pathToRepos; - let url = request.query.url.replace(/.*(?=:\/\/)/, 'git'); - let params = ['clone', url]; - let repoTitle = request.query.url.match(/(?<=\/)[^/]*\/?$/)[0].match(/[^/]*/)[0]; - if(request.params['repositoryId']) { - params.push(request.params['repositoryId']); - repoTitle = request.params['repositoryId']; - } - pathToRepo = path.join(pathToRepos, repoTitle); - fs.access(pathToRepo, (err) => {//Проверка существования локального репозитория с таким же названием - if (!err) { - response.send(repoTitle + ' already exists'); - } - else { - execFile('git', params, {cwd: pathToRepos}, (err, out) => { - if (err) { - response.status(404).send(request.query.url + ' not found'); - } - else { - response.send(repoTitle + ' has been added succesfully'); - } - }); - } - }); + let pathToRepo = pathToRepos; + const url = request.body.url.replace(/.*(?=:\/\/)/, 'git'); + let params = ['clone', url]; + let repoTitle = request.body.url.match(/(?<=\/)[^/]*\/?$/)[0].match(/[^/]*/)[0]; + if(request.params['repositoryId']) { + params.push(request.params['repositoryId']); + repoTitle = request.params['repositoryId']; + } + pathToRepo = path.join(pathToRepos, repoTitle); + fs.access(pathToRepo, (err) => { //Проверка существования локального репозитория с таким же названием + if (!err) { + response.send(repoTitle + ' already exists'); + } + else { + execFile('git', params, {cwd: pathToRepos}, (err) => { + if (err) { + response.status(404).send(request.body.url + ' not found'); + } + else { + response.send(repoTitle + ' has been added succesfully'); + } + }); + } + }); }; \ No newline at end of file diff --git a/callbacks/deleteRepository.js b/callbacks/deleteRepository.js index 7172345..808454e 100644 --- a/callbacks/deleteRepository.js +++ b/callbacks/deleteRepository.js @@ -1,31 +1,31 @@ const fs = require('fs'); const path = require('path'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Вспомогательная функция для удаления непустой директории -let deleteDir = function(pathToDir) { - fs.readdirSync(pathToDir).forEach(item => { - let pathToItem = path.resolve(pathToDir, item); - if (fs.statSync(pathToItem).isFile()) { - fs.unlinkSync(pathToItem); - } - else { - deleteDir(pathToItem); - } - }); - fs.rmdirSync(pathToDir); +const deleteDir = function(pathToDir) { + fs.readdirSync(pathToDir).forEach(item => { + const pathToItem = path.resolve(pathToDir, item); + if (fs.statSync(pathToItem).isFile()) { + fs.unlinkSync(pathToItem); + } + else { + deleteDir(pathToItem); + } + }); + fs.rmdirSync(pathToDir); }; //Ручка DELETE /api/repos/:repositoryId module.exports = function(request, response) { - let pathToRepo = path.join(pathToRepos, request.params['repositoryId']); - fs.access(pathToRepo, err => {//Проверка пути к репозиторию - if(err) { - response.status(404).send(pathToRepo + ' not found'); - } - else { - deleteDir(pathToRepo); - response.send(request.params['repositoryId'] + ' has been deleted succesfully'); - } - }); + const pathToRepo = path.join(pathToRepos, request.params['repositoryId']); + fs.access(pathToRepo, err => { //Проверка пути к репозиторию + if(err) { + response.status(404).send(pathToRepo + ' not found'); + } + else { + deleteDir(pathToRepo); + response.send(request.params['repositoryId'] + ' has been deleted succesfully'); + } + }); }; \ No newline at end of file diff --git a/callbacks/getCommits.js b/callbacks/getCommits.js index 0b31238..87056ed 100644 --- a/callbacks/getCommits.js +++ b/callbacks/getCommits.js @@ -2,79 +2,79 @@ const fs = require('fs'); const path = require('path'); const {spawn} = require('child_process'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка GET /api/repos/:repositoryId/commits/:commitHash module.exports = function(request, response) { - let pathToRepo = path.join(pathToRepos, request.params['repositoryId']); - fs.access(pathToRepo, err => {//Проверка пути к репозиторию - if(err) { - response.status(404).send(pathToRepo + ' not found'); - } - else { - let out = ''; - const gitLog = spawn('git', ['log', request.params['commitHash']], {cwd: pathToRepo}); - gitLog.stdout.on('data', chunk => { - out += chunk.toString(); - }); - gitLog.on('close', code => { - if(!out) {//Проверка существования ветки или хэша коммита - response.status(404).send(request.params['commitHash'] + ' not found'); - } - else { - let commits = out.split(/\n\n(?=\S)/); - let commitsJSON = commits.map(commit => ({ - "SHA-1": commit.match(/[a-f0-9]{40}/)[0], - "author": commit.match(/(?<=Author:).*/)[0].trim(), - "date": commit.match(/(?<=Date:).*/)[0].trim(), - "message": commit.match(/(?<=\n\n).*/)[0].trim() - })); - //Бонусная ручка (выдача коммитов в заданном диапазоне) - if (request.query.from && request.query.to) { - let from = Number(request.query.from); - let to = Number(request.query.to); - let start = 1; - let end = commitsJSON.length; - if ((from > end && to > end) || (from < start && to < start)) { - response.status(404).send('No such commits'); - } - //Определение границ диапазона - else if (from >= start && from <= end) { - if (to >= start && to <= end) { - end = to; - } - else if (to < start) { - end = start; - } - start = from; - } - else if (from > end) { - start = end; - if (to >= start && to <= end) { - end = to; - } - else { - end = 1; - } - } - else if (to <= end) { - end = to; - } - //Преобразование и выдача массива коммитов - if (start <= end) { - response.json(commitsJSON.slice(start - 1, end)); - } - else { - response.json(commitsJSON.slice(end - 1, start).reverse()); - } - } - //Выдача полного списка коммитов - else { - response.json(commitsJSON); - } - } - }); - } - - }); + const pathToRepo = path.join(pathToRepos, request.params['repositoryId']); + fs.access(pathToRepo, err => { //Проверка пути к репозиторию + if(err) { + response.status(404).send(pathToRepo + ' not found'); + } + else { + let out = ''; + const gitLog = spawn('git', ['log', request.params['commitHash']], {cwd: pathToRepo}); + gitLog.stdout.on('data', chunk => { + out += chunk.toString(); + }); + gitLog.on('close', () => { + if(!out) { //Проверка существования ветки или хэша коммита + response.status(404).send(request.params['commitHash'] + ' not found'); + } + else { + const commits = out.split(/\n\n(?=\S)/); + const commitsJSON = commits.map(commit => ({ + 'SHA-1': commit.match(/[a-f0-9]{40}/)[0], + 'author': commit.match(/(?<=Author:).*/)[0].trim(), + 'date': commit.match(/(?<=Date:).*/)[0].trim(), + 'message': commit.match(/(?<=\n\n).*/)[0].trim() + })); + //Бонусная ручка (выдача коммитов в заданном диапазоне) + if (request.query.from && request.query.to) { + const from = Number(request.query.from); + const to = Number(request.query.to); + let start = 1; + let end = commitsJSON.length; + if ((from > end && to > end) || (from < start && to < start)) { + response.status(404).send('No such commits'); + } + //Определение границ диапазона + else if (from >= start && from <= end) { + if (to >= start && to <= end) { + end = to; + } + else if (to < start) { + end = start; + } + start = from; + } + else if (from > end) { + start = end; + if (to >= start && to <= end) { + end = to; + } + else { + end = 1; + } + } + else if (to <= end) { + end = to; + } + //Преобразование и выдача массива коммитов + if (start <= end) { + response.json(commitsJSON.slice(start - 1, end)); + } + else { + response.json(commitsJSON.slice(end - 1, start).reverse()); + } + } + //Выдача полного списка коммитов + else { + response.json(commitsJSON); + } + } + }); + } + + }); }; \ No newline at end of file diff --git a/callbacks/getDiff.js b/callbacks/getDiff.js index cdfed0c..1b676f6 100644 --- a/callbacks/getDiff.js +++ b/callbacks/getDiff.js @@ -2,41 +2,41 @@ const fs = require('fs'); const path = require('path'); const {spawn} = require('child_process'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка GET /api/repos/:repositoryId/commits/:commitHash/diff module.exports = function(request, response) { - let pathToRepo = path.join(pathToRepos, request.params['repositoryId']); - fs.access(pathToRepo, err => {//Проверка пути к репозиторию - if(err) { - response.status(404).send(pathToRepo + ' not found'); - } - else { - let out = ''; - const gitDiff = spawn('git', ['diff', request.params['commitHash'] + '~', request.params['commitHash']], {cwd: pathToRepo}); - gitDiff.stdout.on('data', chunk => { - out += chunk.toString(); - }); - gitDiff.on('close', code => { - if(!out) {//Проверка существования ветки или хэша коммита - response.status(404).send(request.params['commitHash'] + ' not found'); - } - else { - let modifiedFiles = out.split(/\s(?=diff --git)/); - let diffJSON = modifiedFiles.map(file => { - let changeHunks = file.match(/@@.*/s)[0].split(/\s(?=@@.*@@)/); - let changeHunksJSON = changeHunks.map(hunk => ({ - "range": hunk.match(/(?<=@@)[^@]*/)[0].trim(), - "lines": hunk.match(/(?<=@@.*@@).*/s)[0] - })); - return { - "pathToFile": file.match(/(?<=\+\+\+ b\/).*/)[0], - "changeHunks": changeHunksJSON - } - }); - response.json(diffJSON); - } - }); - } - }); + const pathToRepo = path.join(pathToRepos, request.params['repositoryId']); + fs.access(pathToRepo, err => { //Проверка пути к репозиторию + if(err) { + response.status(404).send(pathToRepo + ' not found'); + } + else { + let out = ''; + const gitDiff = spawn('git', ['diff', request.params['commitHash'] + '~', request.params['commitHash']], {cwd: pathToRepo}); + gitDiff.stdout.on('data', chunk => { + out += chunk.toString(); + }); + gitDiff.on('close', () => { + if(!out) { //Проверка существования ветки или хэша коммита + response.status(404).send(request.params['commitHash'] + ' not found'); + } + else { + const modifiedFiles = out.split(/\s(?=diff --git)/); + const diffJSON = modifiedFiles.map(file => { + const changeHunks = file.match(/@@.*/s)[0].split(/\s(?=@@.*@@)/); + const changeHunksJSON = changeHunks.map(hunk => ({ + 'range': hunk.match(/(?<=@@)[^@]*/)[0].trim(), + 'lines': hunk.match(/(?<=@@.*@@).*/s)[0] + })); + return { + 'pathToFile': file.match(/(?<=\+\+\+ b\/).*/)[0], + 'changeHunks': changeHunksJSON + }; + }); + response.json(diffJSON); + } + }); + } + }); }; \ No newline at end of file diff --git a/callbacks/getFile.js b/callbacks/getFile.js index fa73dd4..56e5a22 100644 --- a/callbacks/getFile.js +++ b/callbacks/getFile.js @@ -2,30 +2,29 @@ const fs = require('fs'); const path = require('path'); const {spawn} = require('child_process'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка GET /api/repos/:repositoryId/blob/:commitHash/:pathToFile module.exports = function(request, response) { - let pathToFile = path.join(pathToRepos, request.params['repositoryId']); - fs.access(pathToFile, err => {//Проверка пути к файлу - if(err) { - response.status(404).send(pathToFile + ' not found'); - } - else { - let out = ''; - const gitBlob = spawn('git', ['show', request.params['commitHash'] + ':' + request.params['pathToFile']], {cwd: pathToFile}); - gitBlob.stdout.on('data', chunk => { - out += chunk.toString(); - }); - gitBlob.on('close', code => { - if(!out) {//Проверка существования ветки или хэша коммита - response.status(404).send(request.params['commitHash'] + ' not found'); - } - else { - let binaryContent = Buffer.from(out, 'binary'); - response.json(binaryContent); - } - }); - } - }); + const pathToFile = path.join(pathToRepos, request.params['repositoryId']); + fs.access(pathToFile, err => { //Проверка пути к файлу + if(err) { + response.status(404).send(pathToFile + ' not found'); + } + else { + let out = ''; + const gitBlob = spawn('git', ['show', request.params['commitHash'] + ':' + request.params['pathToFile']], {cwd: pathToFile}); + gitBlob.stdout.on('data', chunk => { + out += chunk.toString(); + }); + gitBlob.on('close', () => { + if(!out) { //Проверка существования ветки или хэша коммита + response.status(404).send(request.params['commitHash'] + ' not found'); + } + else { + response.send(out); + } + }); + } + }); }; \ No newline at end of file diff --git a/callbacks/getRepos.js b/callbacks/getRepos.js index e16fbb6..63eaa85 100644 --- a/callbacks/getRepos.js +++ b/callbacks/getRepos.js @@ -1,16 +1,16 @@ const fs = require('fs'); const path = require('path'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка GET /api/repos module.exports = function (request, response) { - let repos = null; - fs.readdir(pathToRepos, (err, items) => { - if (err) { - response.status(404).send(pathToRepos + ' not found'); - }//Оставить только директории - repos = items.filter(item => fs.statSync(path.resolve(pathToRepos, item)).isDirectory()); - response.json(repos); - }); + let repos = null; + fs.readdir(pathToRepos, (err, items) => { + if (err) { + response.status(404).send(pathToRepos + ' not found'); + } //Оставить только директории + repos = items.filter(item => fs.statSync(path.resolve(pathToRepos, item)).isDirectory()); + response.json(repos); + }); }; \ No newline at end of file diff --git a/callbacks/getRepository.js b/callbacks/getRepository.js index 1c3e090..33e31c2 100644 --- a/callbacks/getRepository.js +++ b/callbacks/getRepository.js @@ -2,43 +2,63 @@ const fs = require('fs'); const path = require('path'); const {spawn} = require('child_process'); -let pathToRepos = process.argv[2]; +const pathToRepos = process.argv[2]; //Ручка GET /api/repos/:repositoryId(/tree/:commitHash/:path) module.exports = function(request, response) { - let pathToRepo = path.join(pathToRepos, request.params['repositoryId']); - let commitHash = 'master'; - let params = ['ls-tree', commitHash]; - if (request.params['commitHash']) {//Проверка наличия в запросе ветки или хэша коммита - commitHash = request.params['commitHash']; - if (request.params['path']) {//Проверка наличия в запросе дальнейшего пути - params[1] += ':' + request.params['path']; - } - } - fs.access(pathToRepo, err => {//Проверка пути к репозиторию - if(err) { - response.status(404).send(pathToRepo + ' not found'); - } - else { - let out = ''; - const gitTree = spawn('git', params, {cwd: pathToRepo}); - gitTree.stdout.on('data', chunk => { - out += chunk.toString(); - }); - gitTree.on('close', code => { - if(!out) {//Проверка существования ветки или хэша коммита - response.status(404).send(commitHash + ' not found'); - } - else { - let objects = out.split(/\n./); - let objectsJSON = objects.map(obj => ({ - "name": obj.match(/(?<=\t).*/)[0], - "type": obj.match(/(?<=\s)\S*/)[0], - "SHA-1": obj.match(/[a-f0-9]{40}/)[0] - })); - response.json(objectsJSON); - } - }); - } - }); + const pathToRepo = path.join(pathToRepos, request.params['repositoryId']); + let commitHash = 'master'; + let params = ['ls-tree', '--name-only', commitHash]; + if (request.params['commitHash']) { //Проверка наличия в запросе ветки или хэша коммита + commitHash = request.params['commitHash']; + if (request.params['path']) { //Проверка наличия в запросе дальнейшего пути + params[2] += ':' + request.params['path']; + } + } + fs.access(pathToRepo, err => { //Проверка пути к репозиторию + if(err) { + response.status(404).send(pathToRepo + ' not found'); + } + else { + let out = ''; + const gitTree = spawn('git', params, {cwd: pathToRepo}); + gitTree.stdout.on('data', chunk => { + out += chunk.toString(); + }); + gitTree.on('close', () => { + if(!out) { //Проверка существования ветки или хэша коммита + response.status(404).send(commitHash + ' not found'); + } + else { + const names = out.split(/\n/); + names.pop(); //Удаление последнего пустого элемента + const pathToDir = request.params['path'] ? path.join(pathToRepo, request.params['path']) : pathToRepo; //Новый рабочий каталог для дочернего процесса + let promises = []; + names.forEach(name => { //Создание промисов на каждый элемент массива, запрашивающих дополнительные данные + const promise = new Promise (function(resolve) { + let out = ''; + const commitInfo = spawn ('git', ['log', '-1', name], {cwd: pathToDir}); + commitInfo.stdout.on('data', chunk => { + out += chunk.toString(); + }); + commitInfo.on('close', () => { + const object = { + 'name': name, + 'hash': out.match(/(?<=commit )\S{6}/)[0], + 'message': out.match(/(?<=\n\n).*/)[0].trim(), + 'commiter': out.match(/(?<=Author:).*(?=<)/)[0].trim(), + 'date': out.match(/(?<=Date:).*(?=\+)/)[0].trim(), + }; + resolve(object); + + }); + }); + promises.push(promise); + }); + const outerPromise = Promise.all(promises); + outerPromise.then(value => response.json(value)); //Получение данных из промисов + } + }); + } + }); }; \ No newline at end of file diff --git a/callbacks/getRepositoryAll.js b/callbacks/getRepositoryAll.js new file mode 100644 index 0000000..60c11b9 --- /dev/null +++ b/callbacks/getRepositoryAll.js @@ -0,0 +1,58 @@ +const fs = require('fs'); +const path = require('path'); +const {spawn} = require('child_process'); + +const pathToRepos = process.argv[2]; + +//Ручка GET /api/repos/:repositoryId/all +module.exports = function(request, response) { + const pathToRepo = path.join(pathToRepos, request.params['repositoryId']); + const commitHash = 'master'; + const params = ['ls-tree', '-t', '-r', '--name-only', commitHash]; + fs.access(pathToRepo, err => { //Проверка пути к репозиторию + if(err) { + response.status(404).send(pathToRepo + ' not found'); + } + else { + let out = ''; + const gitTree = spawn('git', params, {cwd: pathToRepo}); + gitTree.stdout.on('data', chunk => { + out += chunk.toString(); + }); + gitTree.on('close', () => { + if(!out) { //Проверка существования ветки или хэша коммита + response.status(404).send(commitHash + ' not found'); + } + else { + const names = out.split(/\n/); + names.pop(); //Удаление последнего пустого элемента + let promises = []; + names.forEach(name => { //Создание промисов на каждый элемент массива, запрашивающих дополнительные данные + const promise = new Promise (function(resolve) { + let out = ''; + const commitInfo = spawn ('git', ['log', '-1', name], {cwd: pathToRepo}); + commitInfo.stdout.on('data', chunk => { + out += chunk.toString(); + }); + commitInfo.on('close', () => { + const object = { + 'name': name, + 'shortName': name.split('/').reverse()[0], + 'hash': out.match(/(?<=commit )\S{6}/)[0], + 'message': out.match(/(?<=\n\n).*/)[0].trim(), + 'commiter': out.match(/(?<=Author:).*(?=<)/)[0].trim(), + 'date': out.match(/(?<=Date:).*(?=\+)/)[0].trim(), + }; + resolve(object); + + }); + }); + promises.push(promise); + }); + const outerPromise = Promise.all(promises); + outerPromise.then(value => response.json(value)); //Получение данных из промисов + } + }); + } + }); +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 39be1bd..1d91caa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,8 @@ { - "requires": true, + "name": "shri_git_server", + "version": "0.0.1", "lockfileVersion": 1, + "requires": true, "dependencies": { "accepts": { "version": "1.3.7", diff --git a/package.json b/package.json index d90d9ca..744882d 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,14 @@ "description": "The homework for Yandex Web Development School", "main": "index.js", "dependencies": { + "body-parser": "^1.19.0", "express": "^4.17.1" }, "devDependencies": {}, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "start": "node index.js" }, - "author": "", + "author": "wwwildcat", + "repository": "https://github.com/wwwildcat/shri-task-nodejs", "license": "ISC" } From 0f22574801be08a8776b45506b9381e56c9429ec Mon Sep 17 00:00:00 2001 From: wwwildcat Date: Sun, 29 Sep 2019 01:51:15 +0300 Subject: [PATCH 2/3] add redux and search --- README.md | 19 ++++++ index.js | 30 +++++++- pages/html/arcadia_branches.html | 4 +- pages/html/arcadia_files.html | 79 +++++++++++---------- pages/html/arcanum.html | 2 +- pages/html/ya-make_details.html | 4 +- pages/html/ya-make_history.html | 4 +- pages/images/icons/search.svg | 1 + pages/redux/actions.js | 24 +++++++ pages/redux/middleware.js | 25 +++++++ pages/redux/reducer.js | 53 +++++++++++++++ pages/redux/store.js | 35 ++++++++++ pages/redux/view.js | 113 +++++++++++++++++++++++++++++++ pages/search.js | 20 ++++++ pages/styles.css | 79 +++++++++++++++------ 15 files changed, 430 insertions(+), 62 deletions(-) create mode 100644 README.md create mode 100644 pages/images/icons/search.svg create mode 100644 pages/redux/actions.js create mode 100644 pages/redux/middleware.js create mode 100644 pages/redux/reducer.js create mode 100644 pages/redux/store.js create mode 100644 pages/redux/view.js create mode 100644 pages/search.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..3de8ba1 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +## Сервер + +Установка: `npm install` + +Запуск: `npm start` + путь до папки с репозиториями + +## Страницы с версткой и поиск по файлам + +[http://localhost:3000/1-1](http://localhost:3000/1-1) - экраны 1.1 - 1.3 + поиск по файлам на redux + +[http://localhost:3000/1-4](http://localhost:3000/1-4) - 1.4 + +[http://localhost:3000/1-5](http://localhost:3000/1-5) - 1.5 + +[http://localhost:3000/1-6](http://localhost:3000/1-6) - 1.6 + +[http://localhost:3000/2-1](http://localhost:3000/2-1) - 2.1 + +[http://localhost:3000/3-1](http://localhost:3000/3-1) - 3.1 \ No newline at end of file diff --git a/index.js b/index.js index 2c92041..dfcd14f 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ const express = require('express'); +const bodyParser = require('body-parser'); const app = express(); //Коллбэки @@ -6,11 +7,35 @@ const getRepos = require('./callbacks/getRepos.js'); const getCommits = require('./callbacks/getCommits.js'); const getDiff = require('./callbacks/getDiff.js'); const getRepository = require('./callbacks/getRepository.js'); +const getRepositoryAll = require('./callbacks/getRepositoryAll.js'); const getFile = require('./callbacks/getFile.js'); const deleteRepository = require('./callbacks/deleteRepository.js'); const addRepository = require('./callbacks/addRepository.js'); //Запросы +app.use(express.static(__dirname + '/pages')); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); +//Статические страницы +app.get('/1-1', function (request, response) { + response.sendFile(__dirname + '/pages/html/arcadia_files.html'); +}); +app.get('/1-4', function (request, response) { + response.sendFile(__dirname + '/pages/html/arcanum.html'); +}); +app.get('/1-5', function (request, response) { + response.sendFile(__dirname + '/pages/html/ya-make_details.html'); +}); +app.get('/1-6', function (request, response) { + response.sendFile(__dirname + '/pages/html/ya-make_history.html'); +}); +app.get('/2-1', function (request, response) { + response.sendFile(__dirname + '/pages/html/arcadia_branches.html'); +}); +app.get('/3-1', function (request, response) { + response.sendFile(__dirname + '/pages/html/commits.html'); +}); +//Данные с сервера app.get('/api/repos', getRepos); app.get('/api/repos/:repositoryId/commits/:commitHash', getCommits); @@ -19,6 +44,8 @@ app.get('/api/repos/:repositoryId/commits/:commitHash/diff', getDiff); app.get('/api/repos/:repositoryId', getRepository); +app.get('/api/repos/:repositoryId/all', getRepositoryAll); //Возвращает содержимое всех папок и подпапок репозитория (для поиска по файлам) + app.get('/api/repos/:repositoryId/tree/:commitHash/:path([^/]*)?', getRepository); app.get('/api/repos/:repositoryId/blob/:commitHash/:pathToFile([^/]*)?', getFile); @@ -28,7 +55,8 @@ app.delete('/api/repos/:repositoryId', deleteRepository); app.post('/api/repos(/:repositoryId)?', addRepository); app.use(function(request, response,) { - response.status(404).send('URL not found'); + response.status(404).send('URL not found'); }); + app.listen(3000); \ No newline at end of file diff --git a/pages/html/arcadia_branches.html b/pages/html/arcadia_branches.html index ec71798..efed03d 100644 --- a/pages/html/arcadia_branches.html +++ b/pages/html/arcadia_branches.html @@ -23,7 +23,7 @@
arcadia
-
arcadia
+
arcadia
trunk @@ -58,7 +58,7 @@
Last commit c4d248 on 20 Oct 2017, 12:24 by robot-srch-releaser
- FILES + FILES BRANCHES
diff --git a/pages/html/arcadia_files.html b/pages/html/arcadia_files.html index 34a96b2..b7dd0cc 100644 --- a/pages/html/arcadia_files.html +++ b/pages/html/arcadia_files.html @@ -6,6 +6,7 @@ +
@@ -22,44 +23,54 @@
arcadia
-
-
arcadia
-
- trunk - -
    -
  • -
    Trunk
    -
    Last commit 4 s ago
    -
  • -
    -
  • -
    users/rudskoy/DEVTOOLS-43865
    -
    Last commit 1 min ago
    -
  • -
  • -
    users/rudskoy/DEVTOOLS-37948
    -
    Last commit at 16:25
    -
  • -
  • -
    users/rudskoy/DEVTOOLS-94877
    -
    Last commit yesterday, 14:50
    -
  • -
  • -
    users/rudskoy/DEVTOOLS-87450
    -
    Last commit on Jan 11, 12:01
    -
  • -
  • -
    users/rudskoy/DEVTOOLS-27073
    -
    Last commit on Dec 29, 2017
    -
  • -
+
+
+
arcadia
+
+ trunk + +
    +
  • +
    Trunk
    +
    Last commit 4 s ago
    +
  • +
    +
  • +
    users/rudskoy/DEVTOOLS-43865
    +
    Last commit 1 min ago
    +
  • +
  • +
    users/rudskoy/DEVTOOLS-37948
    +
    Last commit at 16:25
    +
  • +
  • +
    users/rudskoy/DEVTOOLS-94877
    +
    Last commit yesterday, 14:50
    +
  • +
  • +
    users/rudskoy/DEVTOOLS-87450
    +
    Last commit on Jan 11, 12:01
    +
  • +
  • +
    users/rudskoy/DEVTOOLS-27073
    +
    Last commit on Dec 29, 2017
    +
  • +
+
+
Last commit c4d248 on 20 Oct 2017, 12:24 by robot-srch-releaser
+
+ -
Last commit c4d248 on 20 Oct 2017, 12:24 by robot-srch-releaser
FILES - BRANCHES + BRANCHES
diff --git a/pages/html/arcanum.html b/pages/html/arcanum.html index 1558e2c..4755827 100644 --- a/pages/html/arcanum.html +++ b/pages/html/arcanum.html @@ -23,7 +23,7 @@
arcadia / trunk / arcadia / arcanum
-
arcanum
+
arcanum
trunk diff --git a/pages/html/ya-make_details.html b/pages/html/ya-make_details.html index 907e108..48e8435 100644 --- a/pages/html/ya-make_details.html +++ b/pages/html/ya-make_details.html @@ -23,7 +23,7 @@
arcadia / trunk / arcadia / arcanum / ya.make
-
ya.make
+
ya.make
trunk @@ -59,7 +59,7 @@
diff --git a/pages/html/ya-make_history.html b/pages/html/ya-make_history.html index bc0dfa8..76814e4 100644 --- a/pages/html/ya-make_history.html +++ b/pages/html/ya-make_history.html @@ -23,7 +23,7 @@
arcadia / trunk / arcadia / arcanum / ya.make
-
ya.make
+
ya.make
trunk @@ -58,7 +58,7 @@
Last commit r3248813 on 20 Oct 2017, 12:24 by robot-srch-releaser
diff --git a/pages/images/icons/search.svg b/pages/images/icons/search.svg new file mode 100644 index 0000000..8533bb3 --- /dev/null +++ b/pages/images/icons/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pages/redux/actions.js b/pages/redux/actions.js new file mode 100644 index 0000000..cf16c6a --- /dev/null +++ b/pages/redux/actions.js @@ -0,0 +1,24 @@ +export function receiveListOfRepositories(json) { + return { + type: 'RECEIVE_LIST_OF_REPOSITORIES', + content: json + }; +} +export function receiveRepositoryContent(json) { + return { + type: 'RECEIVE_REPOSITORY_CONTENT', + content: json + }; +} +export function receiveRepositoryContentAll(json) { + return { + type: 'RECEIVE_REPOSITORY_CONTENT_ALL', + content: json + }; +} +export function submitSearchForm(inputValue) { + return { + type: 'SUBMIT_SEARCH_FORM', + content: inputValue + }; +} \ No newline at end of file diff --git a/pages/redux/middleware.js b/pages/redux/middleware.js new file mode 100644 index 0000000..63e917a --- /dev/null +++ b/pages/redux/middleware.js @@ -0,0 +1,25 @@ +import {receiveListOfRepositories, receiveRepositoryContent, receiveRepositoryContentAll} from './actions.js'; +//Получение списка репозиториев +export function getListOfRepositories () { + return function (dispatch) { + return fetch('http://localhost:3000/api/repos') + .then(response => response.json()) + .then(json => dispatch(receiveListOfRepositories(json))); + }; +} +//Получение содержимого корневой папки репозитория +export function getRepositoryContent (repositoryId) { + return function (dispatch) { + return fetch(`http://localhost:3000/api/repos/${repositoryId}`) + .then(response => response.json()) + .then(json => dispatch(receiveRepositoryContent(json))); + }; +} +//Получение содержимого всех папок и подпапок репозитория +export function getRepositoryContentAll (repositoryId) { + return function (dispatch) { + return fetch(`http://localhost:3000/api/repos/${repositoryId}/all`) + .then(response => response.json()) + .then(json => dispatch(receiveRepositoryContentAll(json))); + }; +} \ No newline at end of file diff --git a/pages/redux/reducer.js b/pages/redux/reducer.js new file mode 100644 index 0000000..f747f17 --- /dev/null +++ b/pages/redux/reducer.js @@ -0,0 +1,53 @@ +//Типы Action +const Types = { + RECEIVE_LIST_OF_REPOSITORIES: 'RECEIVE_LIST_OF_REPOSITORIES', + RECEIVE_REPOSITORY_CONTENT: 'RECEIVE_REPOSITORY_CONTENT', + RECEIVE_REPOSITORY_CONTENT_ALL: 'RECEIVE_REPOSITORY_CONTENT_ALL', + SUBMIT_SEARCH_FORM: 'SUBMIT_SEARCH_FORM', + DEFAULT: 'default' +}; +//Reducer +export function reducer (state, action) { + if (action.type == Types.RECEIVE_LIST_OF_REPOSITORIES) {//Получение списка репозиториев + const data = action.content; + let newState = { + allRepositories: data, + currentRepository: data[1], + viewFiles: 'root' + }; + return Object.assign({}, state, newState); + } + else if (action.type == Types.RECEIVE_REPOSITORY_CONTENT) {//Получение содержимого корневой папки репозитория + const data = action.content; + let newState = {}; + const repositoryID = state.currentRepository; + newState[repositoryID] = { + rootFiles: data, + rootFilesFilter: data + }; + return Object.assign({}, state, newState); + } + else if (action.type == Types.RECEIVE_REPOSITORY_CONTENT_ALL) {//Получение содержимого всех папок и подпапок репозитория + const data = action.content; + let newState = {}; + const repositoryID = state.currentRepository; + newState[repositoryID] = state[repositoryID]; + newState[repositoryID].allFiles = data; + newState[repositoryID].allFilesFilter = data; + return Object.assign({}, state, newState); + } + else if (action.type == Types.SUBMIT_SEARCH_FORM) {//Поиск файлов, содержащих в названии ключевое слово + let searchInput = action.content.toLowerCase(); + let newState = {}; + const repositoryID = state.currentRepository; + newState[repositoryID] = state[repositoryID]; + let allFilesFilter = state[repositoryID].allFiles.filter(file => file.shortName.toLowerCase().indexOf(searchInput) !== -1); + newState[repositoryID].allFilesFilter = allFilesFilter; + newState.viewFiles = 'all'; + return Object.assign({}, state, newState); + } + else { + return state; + } +} + diff --git a/pages/redux/store.js b/pages/redux/store.js new file mode 100644 index 0000000..cb1211f --- /dev/null +++ b/pages/redux/store.js @@ -0,0 +1,35 @@ +export class Store { + constructor (reducer) { + this._reducer = reducer; + this._state = undefined; + this._listeners = []; + this.dispatch = this.dispatch.bind(this); + this.dispatch ({ + type: 'default' + }); + } + + getState() { + return this._state; + } + + subscribe(callback) { + this._listeners.push(callback); + } + + unsubscribe(callback) { + const index = this._listeners.indexOf(callback); + this._listeners.splice(index, 1); + } + + dispatch(action) { + this._state = this._reducer(this._state, action); + this._notifyListeners(); + } + + _notifyListeners () { + this._listeners.forEach(listener => { + listener(this._state); + }); + } +} \ No newline at end of file diff --git a/pages/redux/view.js b/pages/redux/view.js new file mode 100644 index 0000000..28a5891 --- /dev/null +++ b/pages/redux/view.js @@ -0,0 +1,113 @@ +import {submitSearchForm} from './actions.js'; + +// View общий +class View { + constructor(elem, store) { + this._elem = elem; + this._store = store; + this._prepareRender(store.getState()); + this._store.subscribe(this._prepareRender.bind(this)); + this._unsubscribe = this._store.unsubscribe; + } + + _prepareRender(state) { + this._elem.innerHTML = this.render(state); + } + + render() {} + + destroy() { + this._elem.innerHTML = ''; + this._unsubscribe(this._prepareRender.bind(this)); + } +} +// View формы поиска +export class FormView extends View { + constructor(elem, store) { + super(elem, store); + this._onSubmit = this._onSubmit.bind(this); + this._elem.addEventListener('submit', this._onSubmit); + } + + _onSubmit(event) { + event.preventDefault(); + this._store.dispatch(submitSearchForm(event.target.searchInput.value)); + } + + render() { + return `
+ + +
`; + } + + destroy() { + super.destroy(); + this._elem.removeEventListener('submit', this._onSubmit); + } +} +// View таблицы +export class TableView extends View { + constructor(elem, store) { + super(elem, store); + } + + render(state) { + if (state) { + const repositoryID = state.currentRepository; + let resultHTML = `
+
Name
+
Last commit
+
Commit message
+
Commiter
+
Updated
+
`; + if (state.viewFiles === 'root') { //Отображается содержимое корневой папки репозитория + if (state[repositoryID] && state[repositoryID].rootFilesFilter) { + state[repositoryID].rootFilesFilter.forEach(obj => { + resultHTML += `
+
+ + ${obj.name} +
+ +
${obj.message}
+
${obj.commiter}
+
${obj.date}
+
+
`; + }); + } + return resultHTML; + } + else if (state.viewFiles === 'all') { //Отображаются результаты поиска по всем папкам и подпапкам репозитория + if (state[repositoryID] && state[repositoryID].allFilesFilter) { + state[repositoryID].allFilesFilter.forEach(obj => { + resultHTML += `
+
+ + ${obj.name} +
+ +
${obj.message}
+
${obj.commiter}
+
${obj.date}
+
+
`; + }); + } + return resultHTML; + } + } + } + + destroy() { + super.destroy(); + } +} \ No newline at end of file diff --git a/pages/search.js b/pages/search.js new file mode 100644 index 0000000..0e39f9d --- /dev/null +++ b/pages/search.js @@ -0,0 +1,20 @@ +import {Store} from './redux/store.js'; +import {TableView, FormView} from './redux/view.js'; +import {reducer} from './redux/reducer.js'; +import {getListOfRepositories, getRepositoryContent, getRepositoryContentAll} from './redux/middleware.js'; + +let store = new Store(reducer); +//Получение данных с сервера с помощью middleware +getListOfRepositories()(store.dispatch) + .then(() => { + getRepositoryContent(store.getState().allRepositories[1])(store.dispatch) + .then(() => { + getRepositoryContentAll(store.getState().allRepositories[1])(store.dispatch); + }); + }); +//Отображение элементов +const table = document.querySelector('.Table'); +const searchForm = document.querySelector('.Search-form'); + +let tableView = new TableView (table, store); +let formView = new FormView (searchForm, store); diff --git a/pages/styles.css b/pages/styles.css index 960efbd..7f932c3 100644 --- a/pages/styles.css +++ b/pages/styles.css @@ -233,6 +233,13 @@ a { height: 12px; background: url('images/icons/menu.svg') 50% 50% no-repeat; } + +.Icon-search { + display: inline-block; + width: 12px; + height: 12px; + background: url('images/icons/search.svg') 50% 50% no-repeat; +} /**** Смысловые блоки и элементы ****/ /* Шапка */ .Header { @@ -343,6 +350,12 @@ a { padding: 0 32px; } +.Main-flexContainer { + display: flex; + justify-content: space-between; + margin-top: 12px; +} + .Main-content { margin-top: 16px; } @@ -352,9 +365,18 @@ a { } @media (max-width: 360px) { + .Main { + padding: 0; + } + .Main-content_variable { margin-top: 12px; } + + .Main-flexContainer { + flex-direction: column; + margin: 10px 16px 0; + } } /* Путь к текущему файлу/папке */ .Path { @@ -364,12 +386,15 @@ a { word-wrap: break-word; border-bottom: solid 1px #e5e5e5; } -/* Текущие репозиторий и ветка */ -.Current { - margin-top: 12px; -} -.Current-fileName { +@media (max-width: 360px) { + .Path { + margin: 0 16px; + padding-bottom: 8px; + } +} +/* Текущие репозиторий и ветка */ +.Current-objectName { display: inline-block; margin-bottom: 10px; } @@ -382,20 +407,7 @@ a { } @media (max-width: 360px) { - .Main { - padding: 0; - } - - .Path { - margin: 0 16px; - padding-bottom: 8px; - } - - .Current { - margin: 10px 16px 0; - } - - .Current-fileName { + .Current-objectName { margin-bottom: 4px; } @@ -404,6 +416,33 @@ a { margin-bottom: 4px; } } + +/* Поле поиска */ +.Search { + margin-top: 10px; +} + +.Search-input { + border: solid 1px #e5e5e5; + padding: 3px; +} + +.Search-button { + width: 23px; + height: 23px; + padding: 3px; + background: #e5e5e5; + border: solid 1px #e5e5e5; + border-radius: 4px; + cursor: pointer; +} + +@media (max-width: 360px) { + .Search { + margin-top: 8px; + } +} + /* Раскрывающийся список веток */ .BranchList_closed { display: none; @@ -548,7 +587,7 @@ a { border-bottom: solid 1px #f2f2f2; } -.Table-cell:last-child { +.Table-cell:nth-child(5) { text-align: right; } From e808710615e7090ebc51091982c9f48dfef2861f Mon Sep 17 00:00:00 2001 From: wwwildcat <52196493+wwwildcat@users.noreply.github.com> Date: Wed, 9 Oct 2019 02:23:10 +0300 Subject: [PATCH 3/3] edit readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3de8ba1..d6fe00c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Установка: `npm install` -Запуск: `npm start` + путь до папки с репозиториями +Запуск: `npm start /home/user/my-repos`, где `/home/user/my-repos` - путь до папки с репозиториями ## Страницы с версткой и поиск по файлам @@ -16,4 +16,4 @@ [http://localhost:3000/2-1](http://localhost:3000/2-1) - 2.1 -[http://localhost:3000/3-1](http://localhost:3000/3-1) - 3.1 \ No newline at end of file +[http://localhost:3000/3-1](http://localhost:3000/3-1) - 3.1